Blame gnulib/lib/argz.c

Packit Service a2ae7a
/* Functions for dealing with '\0' separated arg vectors.
Packit Service a2ae7a
   Copyright (C) 1995-1998, 2000-2002, 2006, 2008-2019 Free Software
Packit Service a2ae7a
   Foundation, Inc.
Packit Service a2ae7a
   This file is part of the GNU C Library.
Packit Service a2ae7a
Packit Service a2ae7a
   This program is free software; you can redistribute it and/or modify
Packit Service a2ae7a
   it under the terms of the GNU Lesser General Public License as published by
Packit Service a2ae7a
   the Free Software Foundation; either version 2.1, or (at your option)
Packit Service a2ae7a
   any later version.
Packit Service a2ae7a
Packit Service a2ae7a
   This program is distributed in the hope that it will be useful,
Packit Service a2ae7a
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service a2ae7a
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit Service a2ae7a
   GNU Lesser General Public License for more details.
Packit Service a2ae7a
Packit Service a2ae7a
   You should have received a copy of the GNU Lesser General Public License along
Packit Service a2ae7a
   with this program; if not, see <https://www.gnu.org/licenses/>.  */
Packit Service a2ae7a
Packit Service a2ae7a
#include <config.h>
Packit Service a2ae7a
Packit Service a2ae7a
#include <argz.h>
Packit Service a2ae7a
#include <errno.h>
Packit Service a2ae7a
#include <stdlib.h>
Packit Service a2ae7a
#include <string.h>
Packit Service a2ae7a
Packit Service a2ae7a
Packit Service a2ae7a
Packit Service a2ae7a
/* Add BUF, of length BUF_LEN to the argz vector in ARGZ & ARGZ_LEN.  */
Packit Service a2ae7a
error_t
Packit Service a2ae7a
argz_append (char **argz, size_t *argz_len, const char *buf, size_t buf_len)
Packit Service a2ae7a
{
Packit Service a2ae7a
  size_t new_argz_len = *argz_len + buf_len;
Packit Service a2ae7a
  char *new_argz = realloc (*argz, new_argz_len);
Packit Service a2ae7a
  if (new_argz)
Packit Service a2ae7a
    {
Packit Service a2ae7a
      memcpy (new_argz + *argz_len, buf, buf_len);
Packit Service a2ae7a
      *argz = new_argz;
Packit Service a2ae7a
      *argz_len = new_argz_len;
Packit Service a2ae7a
      return 0;
Packit Service a2ae7a
    }
Packit Service a2ae7a
  else
Packit Service a2ae7a
    return ENOMEM;
Packit Service a2ae7a
}
Packit Service a2ae7a
Packit Service a2ae7a
/* Add STR to the argz vector in ARGZ & ARGZ_LEN.  This should be moved into
Packit Service a2ae7a
   argz.c in libshouldbelibc.  */
Packit Service a2ae7a
error_t
Packit Service a2ae7a
argz_add (char **argz, size_t *argz_len, const char *str)
Packit Service a2ae7a
{
Packit Service a2ae7a
  return argz_append (argz, argz_len, str, strlen (str) + 1);
Packit Service a2ae7a
}
Packit Service a2ae7a
Packit Service a2ae7a
Packit Service a2ae7a
Packit Service a2ae7a
error_t
Packit Service a2ae7a
argz_add_sep (char **argz, size_t *argz_len, const char *string, int delim)
Packit Service a2ae7a
{
Packit Service a2ae7a
  size_t nlen = strlen (string) + 1;
Packit Service a2ae7a
Packit Service a2ae7a
  if (nlen > 1)
Packit Service a2ae7a
    {
Packit Service a2ae7a
      const char *rp;
Packit Service a2ae7a
      char *wp;
Packit Service a2ae7a
Packit Service a2ae7a
      *argz = (char *) realloc (*argz, *argz_len + nlen);
Packit Service a2ae7a
      if (*argz == NULL)
Packit Service a2ae7a
        return ENOMEM;
Packit Service a2ae7a
Packit Service a2ae7a
      wp = *argz + *argz_len;
Packit Service a2ae7a
      rp = string;
Packit Service a2ae7a
      do
Packit Service a2ae7a
        if (*rp == delim)
Packit Service a2ae7a
          {
Packit Service a2ae7a
            if (wp > *argz && wp[-1] != '\0')
Packit Service a2ae7a
              *wp++ = '\0';
Packit Service a2ae7a
            else
Packit Service a2ae7a
              --nlen;
Packit Service a2ae7a
          }
Packit Service a2ae7a
        else
Packit Service a2ae7a
          *wp++ = *rp;
Packit Service a2ae7a
      while (*rp++ != '\0');
Packit Service a2ae7a
Packit Service a2ae7a
      *argz_len += nlen;
Packit Service a2ae7a
    }
Packit Service a2ae7a
Packit Service a2ae7a
  return 0;
Packit Service a2ae7a
}
Packit Service a2ae7a
Packit Service a2ae7a
Packit Service a2ae7a
Packit Service a2ae7a
error_t
Packit Service a2ae7a
argz_create_sep (const char *string, int delim, char **argz, size_t *len)
Packit Service a2ae7a
{
Packit Service a2ae7a
  size_t nlen = strlen (string) + 1;
Packit Service a2ae7a
Packit Service a2ae7a
  if (nlen > 1)
Packit Service a2ae7a
    {
Packit Service a2ae7a
      const char *rp;
Packit Service a2ae7a
      char *wp;
Packit Service a2ae7a
Packit Service a2ae7a
      *argz = (char *) malloc (nlen);
Packit Service a2ae7a
      if (*argz == NULL)
Packit Service a2ae7a
        return ENOMEM;
Packit Service a2ae7a
Packit Service a2ae7a
      rp = string;
Packit Service a2ae7a
      wp = *argz;
Packit Service a2ae7a
      do
Packit Service a2ae7a
        if (*rp == delim)
Packit Service a2ae7a
          {
Packit Service a2ae7a
            if (wp > *argz && wp[-1] != '\0')
Packit Service a2ae7a
              *wp++ = '\0';
Packit Service a2ae7a
            else
Packit Service a2ae7a
              --nlen;
Packit Service a2ae7a
          }
Packit Service a2ae7a
        else
Packit Service a2ae7a
          *wp++ = *rp;
Packit Service a2ae7a
      while (*rp++ != '\0');
Packit Service a2ae7a
Packit Service a2ae7a
      if (nlen == 0)
Packit Service a2ae7a
        {
Packit Service a2ae7a
          free (*argz);
Packit Service a2ae7a
          *argz = NULL;
Packit Service a2ae7a
          *len = 0;
Packit Service a2ae7a
        }
Packit Service a2ae7a
Packit Service a2ae7a
      *len = nlen;
Packit Service a2ae7a
    }
Packit Service a2ae7a
  else
Packit Service a2ae7a
    {
Packit Service a2ae7a
      *argz = NULL;
Packit Service a2ae7a
      *len = 0;
Packit Service a2ae7a
    }
Packit Service a2ae7a
Packit Service a2ae7a
  return 0;
Packit Service a2ae7a
}
Packit Service a2ae7a
Packit Service a2ae7a
Packit Service a2ae7a
/* Insert ENTRY into ARGZ & ARGZ_LEN before BEFORE, which should be an
Packit Service a2ae7a
   existing entry in ARGZ; if BEFORE is NULL, ENTRY is appended to the end.
Packit Service a2ae7a
   Since ARGZ's first entry is the same as ARGZ, argz_insert (ARGZ, ARGZ_LEN,
Packit Service a2ae7a
   ARGZ, ENTRY) will insert ENTRY at the beginning of ARGZ.  If BEFORE is not
Packit Service a2ae7a
   in ARGZ, EINVAL is returned, else if memory can't be allocated for the new
Packit Service a2ae7a
   ARGZ, ENOMEM is returned, else 0.  */
Packit Service a2ae7a
error_t
Packit Service a2ae7a
argz_insert (char **argz, size_t *argz_len, char *before, const char *entry)
Packit Service a2ae7a
{
Packit Service a2ae7a
  if (! before)
Packit Service a2ae7a
    return argz_add (argz, argz_len, entry);
Packit Service a2ae7a
Packit Service a2ae7a
  if (before < *argz || before >= *argz + *argz_len)
Packit Service a2ae7a
    return EINVAL;
Packit Service a2ae7a
Packit Service a2ae7a
  if (before > *argz)
Packit Service a2ae7a
    /* Make sure before is actually the beginning of an entry.  */
Packit Service a2ae7a
    while (before[-1])
Packit Service a2ae7a
      before--;
Packit Service a2ae7a
Packit Service a2ae7a
  {
Packit Service a2ae7a
    size_t after_before = *argz_len - (before - *argz);
Packit Service a2ae7a
    size_t entry_len = strlen  (entry) + 1;
Packit Service a2ae7a
    size_t new_argz_len = *argz_len + entry_len;
Packit Service a2ae7a
    char *new_argz = realloc (*argz, new_argz_len);
Packit Service a2ae7a
Packit Service a2ae7a
    if (new_argz)
Packit Service a2ae7a
      {
Packit Service a2ae7a
        before = new_argz + (before - *argz);
Packit Service a2ae7a
        memmove (before + entry_len, before, after_before);
Packit Service a2ae7a
        memmove (before, entry, entry_len);
Packit Service a2ae7a
        *argz = new_argz;
Packit Service a2ae7a
        *argz_len = new_argz_len;
Packit Service a2ae7a
        return 0;
Packit Service a2ae7a
      }
Packit Service a2ae7a
    else
Packit Service a2ae7a
      return ENOMEM;
Packit Service a2ae7a
  }
Packit Service a2ae7a
}
Packit Service a2ae7a
Packit Service a2ae7a
Packit Service a2ae7a
char *
Packit Service a2ae7a
argz_next (const char *argz, size_t argz_len, const char *entry)
Packit Service a2ae7a
{
Packit Service a2ae7a
  if (entry)
Packit Service a2ae7a
    {
Packit Service a2ae7a
      if (entry < argz + argz_len)
Packit Service a2ae7a
        entry = strchr (entry, '\0') + 1;
Packit Service a2ae7a
Packit Service a2ae7a
      return entry >= argz + argz_len ? NULL : (char *) entry;
Packit Service a2ae7a
    }
Packit Service a2ae7a
  else
Packit Service a2ae7a
    if (argz_len > 0)
Packit Service a2ae7a
      return (char *) argz;
Packit Service a2ae7a
    else
Packit Service a2ae7a
      return NULL;
Packit Service a2ae7a
}
Packit Service a2ae7a
Packit Service a2ae7a
Packit Service a2ae7a
/* Make '\0' separated arg vector ARGZ printable by converting all the '\0's
Packit Service a2ae7a
   except the last into the character SEP.  */
Packit Service a2ae7a
void
Packit Service a2ae7a
argz_stringify (char *argz, size_t len, int sep)
Packit Service a2ae7a
{
Packit Service a2ae7a
  if (len > 0)
Packit Service a2ae7a
    while (1)
Packit Service a2ae7a
      {
Packit Service a2ae7a
        size_t part_len = strnlen (argz, len);
Packit Service a2ae7a
        argz += part_len;
Packit Service a2ae7a
        len -= part_len;
Packit Service a2ae7a
        if (len-- <= 1)         /* includes final '\0' we want to stop at */
Packit Service a2ae7a
          break;
Packit Service a2ae7a
        *argz++ = sep;
Packit Service a2ae7a
      }
Packit Service a2ae7a
}
Packit Service a2ae7a
Packit Service a2ae7a
Packit Service a2ae7a
/* Returns the number of strings in ARGZ.  */
Packit Service a2ae7a
size_t
Packit Service a2ae7a
argz_count (const char *argz, size_t len)
Packit Service a2ae7a
{
Packit Service a2ae7a
  size_t count = 0;
Packit Service a2ae7a
  while (len > 0)
Packit Service a2ae7a
    {
Packit Service a2ae7a
      size_t part_len = strlen (argz);
Packit Service a2ae7a
      argz += part_len + 1;
Packit Service a2ae7a
      len -= part_len + 1;
Packit Service a2ae7a
      count++;
Packit Service a2ae7a
    }
Packit Service a2ae7a
  return count;
Packit Service a2ae7a
}
Packit Service a2ae7a
Packit Service a2ae7a
Packit Service a2ae7a
/* Puts pointers to each string in ARGZ, plus a terminating 0 element, into
Packit Service a2ae7a
   ARGV, which must be large enough to hold them all.  */
Packit Service a2ae7a
void
Packit Service a2ae7a
argz_extract (const char *argz, size_t len, char **argv)
Packit Service a2ae7a
{
Packit Service a2ae7a
  while (len > 0)
Packit Service a2ae7a
    {
Packit Service a2ae7a
      size_t part_len = strlen (argz);
Packit Service a2ae7a
      *argv++ = (char *) argz;
Packit Service a2ae7a
      argz += part_len + 1;
Packit Service a2ae7a
      len -= part_len + 1;
Packit Service a2ae7a
    }
Packit Service a2ae7a
  *argv = 0;
Packit Service a2ae7a
}
Packit Service a2ae7a
Packit Service a2ae7a
Packit Service a2ae7a
/* Make a '\0' separated arg vector from a unix argv vector, returning it in
Packit Service a2ae7a
   ARGZ, and the total length in LEN.  If a memory allocation error occurs,
Packit Service a2ae7a
   ENOMEM is returned, otherwise 0.  */
Packit Service a2ae7a
error_t
Packit Service a2ae7a
argz_create (char *const argv[], char **argz, size_t *len)
Packit Service a2ae7a
{
Packit Service a2ae7a
  int argc;
Packit Service a2ae7a
  size_t tlen = 0;
Packit Service a2ae7a
  char *const *ap;
Packit Service a2ae7a
  char *p;
Packit Service a2ae7a
Packit Service a2ae7a
  for (argc = 0; argv[argc] != NULL; ++argc)
Packit Service a2ae7a
    tlen += strlen (argv[argc]) + 1;
Packit Service a2ae7a
Packit Service a2ae7a
  if (tlen == 0)
Packit Service a2ae7a
    *argz = NULL;
Packit Service a2ae7a
  else
Packit Service a2ae7a
    {
Packit Service a2ae7a
      *argz = malloc (tlen);
Packit Service a2ae7a
      if (*argz == NULL)
Packit Service a2ae7a
        return ENOMEM;
Packit Service a2ae7a
Packit Service a2ae7a
      for (p = *argz, ap = argv; *ap; ++ap, ++p)
Packit Service a2ae7a
        p = stpcpy (p, *ap);
Packit Service a2ae7a
    }
Packit Service a2ae7a
  *len = tlen;
Packit Service a2ae7a
Packit Service a2ae7a
  return 0;
Packit Service a2ae7a
}
Packit Service a2ae7a
Packit Service a2ae7a
Packit Service a2ae7a
/* Delete ENTRY from ARGZ & ARGZ_LEN, if any.  */
Packit Service a2ae7a
void
Packit Service a2ae7a
argz_delete (char **argz, size_t *argz_len, char *entry)
Packit Service a2ae7a
{
Packit Service a2ae7a
  if (entry)
Packit Service a2ae7a
    /* Get rid of the old value for NAME.  */
Packit Service a2ae7a
    {
Packit Service a2ae7a
      size_t entry_len = strlen (entry) + 1;
Packit Service a2ae7a
      *argz_len -= entry_len;
Packit Service a2ae7a
      memmove (entry, entry + entry_len, *argz_len - (entry - *argz));
Packit Service a2ae7a
      if (*argz_len == 0)
Packit Service a2ae7a
        {
Packit Service a2ae7a
          free (*argz);
Packit Service a2ae7a
          *argz = 0;
Packit Service a2ae7a
        }
Packit Service a2ae7a
    }
Packit Service a2ae7a
}
Packit Service a2ae7a
Packit Service a2ae7a
Packit Service a2ae7a
/* Append BUF, of length BUF_LEN to *TO, of length *TO_LEN, reallocating and
Packit Service a2ae7a
   updating *TO & *TO_LEN appropriately.  If an allocation error occurs,
Packit Service a2ae7a
   *TO's old value is freed, and *TO is set to 0.  */
Packit Service a2ae7a
static void
Packit Service a2ae7a
str_append (char **to, size_t *to_len, const char *buf, const size_t buf_len)
Packit Service a2ae7a
{
Packit Service a2ae7a
  size_t new_len = *to_len + buf_len;
Packit Service a2ae7a
  char *new_to = realloc (*to, new_len + 1);
Packit Service a2ae7a
Packit Service a2ae7a
  if (new_to)
Packit Service a2ae7a
    {
Packit Service a2ae7a
      *((char *) mempcpy (new_to + *to_len, buf, buf_len)) = '\0';
Packit Service a2ae7a
      *to = new_to;
Packit Service a2ae7a
      *to_len = new_len;
Packit Service a2ae7a
    }
Packit Service a2ae7a
  else
Packit Service a2ae7a
    {
Packit Service a2ae7a
      free (*to);
Packit Service a2ae7a
      *to = 0;
Packit Service a2ae7a
    }
Packit Service a2ae7a
}
Packit Service a2ae7a
Packit Service a2ae7a
/* Replace any occurrences of the string STR in ARGZ with WITH, reallocating
Packit Service a2ae7a
   ARGZ as necessary.  If REPLACE_COUNT is non-zero, *REPLACE_COUNT will be
Packit Service a2ae7a
   incremented by number of replacements performed.  */
Packit Service a2ae7a
error_t
Packit Service a2ae7a
argz_replace (char **argz, size_t *argz_len, const char *str, const char *with,
Packit Service a2ae7a
                unsigned *replace_count)
Packit Service a2ae7a
{
Packit Service a2ae7a
  error_t err = 0;
Packit Service a2ae7a
Packit Service a2ae7a
  if (str && *str)
Packit Service a2ae7a
    {
Packit Service a2ae7a
      char *arg = 0;
Packit Service a2ae7a
      char *src = *argz;
Packit Service a2ae7a
      size_t src_len = *argz_len;
Packit Service a2ae7a
      char *dst = 0;
Packit Service a2ae7a
      size_t dst_len = 0;
Packit Service a2ae7a
      int delayed_copy = 1;     /* True while we've avoided copying anything.  */
Packit Service a2ae7a
      size_t str_len = strlen (str), with_len = strlen (with);
Packit Service a2ae7a
Packit Service a2ae7a
      while (!err && (arg = argz_next (src, src_len, arg)))
Packit Service a2ae7a
        {
Packit Service a2ae7a
          char *match = strstr (arg, str);
Packit Service a2ae7a
          if (match)
Packit Service a2ae7a
            {
Packit Service a2ae7a
              char *from = match + str_len;
Packit Service a2ae7a
              size_t to_len = match - arg;
Packit Service a2ae7a
              char *to = strndup (arg, to_len);
Packit Service a2ae7a
Packit Service a2ae7a
              while (to && from)
Packit Service a2ae7a
                {
Packit Service a2ae7a
                  str_append (&to, &to_len, with, with_len);
Packit Service a2ae7a
                  if (to)
Packit Service a2ae7a
                    {
Packit Service a2ae7a
                      match = strstr (from, str);
Packit Service a2ae7a
                      if (match)
Packit Service a2ae7a
                        {
Packit Service a2ae7a
                          str_append (&to, &to_len, from, match - from);
Packit Service a2ae7a
                          from = match + str_len;
Packit Service a2ae7a
                        }
Packit Service a2ae7a
                      else
Packit Service a2ae7a
                        {
Packit Service a2ae7a
                          str_append (&to, &to_len, from, strlen (from));
Packit Service a2ae7a
                          from = 0;
Packit Service a2ae7a
                        }
Packit Service a2ae7a
                    }
Packit Service a2ae7a
                }
Packit Service a2ae7a
Packit Service a2ae7a
              if (to)
Packit Service a2ae7a
                {
Packit Service a2ae7a
                  if (delayed_copy)
Packit Service a2ae7a
                    /* We avoided copying SRC to DST until we found a match;
Packit Service a2ae7a
                       now that we've done so, copy everything from the start
Packit Service a2ae7a
                       of SRC.  */
Packit Service a2ae7a
                    {
Packit Service a2ae7a
                      if (arg > src)
Packit Service a2ae7a
                        err = argz_append (&dst, &dst_len, src, (arg - src));
Packit Service a2ae7a
                      delayed_copy = 0;
Packit Service a2ae7a
                    }
Packit Service a2ae7a
                  if (! err)
Packit Service a2ae7a
                    err = argz_add (&dst, &dst_len, to);
Packit Service a2ae7a
                  free (to);
Packit Service a2ae7a
                }
Packit Service a2ae7a
              else
Packit Service a2ae7a
                err = ENOMEM;
Packit Service a2ae7a
Packit Service a2ae7a
              if (replace_count)
Packit Service a2ae7a
                (*replace_count)++;
Packit Service a2ae7a
            }
Packit Service a2ae7a
          else if (! delayed_copy)
Packit Service a2ae7a
            err = argz_add (&dst, &dst_len, arg);
Packit Service a2ae7a
        }
Packit Service a2ae7a
Packit Service a2ae7a
      if (! err)
Packit Service a2ae7a
        {
Packit Service a2ae7a
          if (! delayed_copy)
Packit Service a2ae7a
            /* We never found any instances of str.  */
Packit Service a2ae7a
            {
Packit Service a2ae7a
              free (src);
Packit Service a2ae7a
              *argz = dst;
Packit Service a2ae7a
              *argz_len = dst_len;
Packit Service a2ae7a
            }
Packit Service a2ae7a
        }
Packit Service a2ae7a
      else if (dst_len > 0)
Packit Service a2ae7a
        free (dst);
Packit Service a2ae7a
    }
Packit Service a2ae7a
Packit Service a2ae7a
  return err;
Packit Service a2ae7a
}