(root)/
texinfo-7.1/
gnulib/
lib/
argz.c
       1  /* Functions for dealing with '\0' separated arg vectors.
       2     Copyright (C) 1995-1998, 2000-2002, 2006, 2008-2023 Free Software
       3     Foundation, Inc.
       4     This file is part of the GNU C Library.
       5  
       6     This file is free software: you can redistribute it and/or modify
       7     it under the terms of the GNU Lesser General Public License as
       8     published by the Free Software Foundation; either version 2.1 of the
       9     License, or (at your option) any later version.
      10  
      11     This file is distributed in the hope that it will be useful,
      12     but WITHOUT ANY WARRANTY; without even the implied warranty of
      13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14     GNU Lesser General Public License for more details.
      15  
      16     You should have received a copy of the GNU Lesser General Public License
      17     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <config.h>
      20  
      21  #include <argz.h>
      22  #include <errno.h>
      23  #include <stdlib.h>
      24  #include <string.h>
      25  
      26  
      27  
      28  /* Add BUF, of length BUF_LEN to the argz vector in ARGZ & ARGZ_LEN.  */
      29  error_t
      30  argz_append (char **argz, size_t *argz_len, const char *buf, size_t buf_len)
      31  {
      32    size_t new_argz_len = *argz_len + buf_len;
      33    char *new_argz = realloc (*argz, new_argz_len);
      34    if (new_argz)
      35      {
      36        memcpy (new_argz + *argz_len, buf, buf_len);
      37        *argz = new_argz;
      38        *argz_len = new_argz_len;
      39        return 0;
      40      }
      41    else
      42      return ENOMEM;
      43  }
      44  
      45  /* Add STR to the argz vector in ARGZ & ARGZ_LEN.  This should be moved into
      46     argz.c in libshouldbelibc.  */
      47  error_t
      48  argz_add (char **argz, size_t *argz_len, const char *str)
      49  {
      50    return argz_append (argz, argz_len, str, strlen (str) + 1);
      51  }
      52  
      53  
      54  
      55  error_t
      56  argz_add_sep (char **argz, size_t *argz_len, const char *string, int delim)
      57  {
      58    size_t nlen = strlen (string) + 1;
      59  
      60    if (nlen > 1)
      61      {
      62        const char *rp;
      63        char *wp;
      64  
      65        *argz = (char *) realloc (*argz, *argz_len + nlen);
      66        if (*argz == NULL)
      67          return ENOMEM;
      68  
      69        wp = *argz + *argz_len;
      70        rp = string;
      71        do
      72          if (*rp == delim)
      73            {
      74              if (wp > *argz && wp[-1] != '\0')
      75                *wp++ = '\0';
      76              else
      77                --nlen;
      78            }
      79          else
      80            *wp++ = *rp;
      81        while (*rp++ != '\0');
      82  
      83        *argz_len += nlen;
      84      }
      85  
      86    return 0;
      87  }
      88  
      89  
      90  
      91  error_t
      92  argz_create_sep (const char *string, int delim, char **argz, size_t *len)
      93  {
      94    size_t nlen = strlen (string) + 1;
      95  
      96    if (nlen > 1)
      97      {
      98        const char *rp;
      99        char *wp;
     100  
     101        *argz = (char *) malloc (nlen);
     102        if (*argz == NULL)
     103          return ENOMEM;
     104  
     105        rp = string;
     106        wp = *argz;
     107        do
     108          if (*rp == delim)
     109            {
     110              if (wp > *argz && wp[-1] != '\0')
     111                *wp++ = '\0';
     112              else
     113                --nlen;
     114            }
     115          else
     116            *wp++ = *rp;
     117        while (*rp++ != '\0');
     118  
     119        if (nlen == 0)
     120          {
     121            free (*argz);
     122            *argz = NULL;
     123            *len = 0;
     124          }
     125  
     126        *len = nlen;
     127      }
     128    else
     129      {
     130        *argz = NULL;
     131        *len = 0;
     132      }
     133  
     134    return 0;
     135  }
     136  
     137  
     138  /* Insert ENTRY into ARGZ & ARGZ_LEN before BEFORE, which should be an
     139     existing entry in ARGZ; if BEFORE is NULL, ENTRY is appended to the end.
     140     Since ARGZ's first entry is the same as ARGZ, argz_insert (ARGZ, ARGZ_LEN,
     141     ARGZ, ENTRY) will insert ENTRY at the beginning of ARGZ.  If BEFORE is not
     142     in ARGZ, EINVAL is returned, else if memory can't be allocated for the new
     143     ARGZ, ENOMEM is returned, else 0.  */
     144  error_t
     145  argz_insert (char **argz, size_t *argz_len, char *before, const char *entry)
     146  {
     147    if (! before)
     148      return argz_add (argz, argz_len, entry);
     149  
     150    if (before < *argz || before >= *argz + *argz_len)
     151      return EINVAL;
     152  
     153    if (before > *argz)
     154      /* Make sure before is actually the beginning of an entry.  */
     155      while (before[-1])
     156        before--;
     157  
     158    {
     159      size_t after_before = *argz_len - (before - *argz);
     160      size_t entry_len = strlen  (entry) + 1;
     161      size_t new_argz_len = *argz_len + entry_len;
     162      char *new_argz = realloc (*argz, new_argz_len);
     163  
     164      if (new_argz)
     165        {
     166          before = new_argz + (before - *argz);
     167          memmove (before + entry_len, before, after_before);
     168          memmove (before, entry, entry_len);
     169          *argz = new_argz;
     170          *argz_len = new_argz_len;
     171          return 0;
     172        }
     173      else
     174        return ENOMEM;
     175    }
     176  }
     177  
     178  
     179  char *
     180  argz_next (const char *argz, size_t argz_len, const char *entry)
     181  {
     182    if (entry)
     183      {
     184        if (entry < argz + argz_len)
     185          entry = strchr (entry, '\0') + 1;
     186  
     187        return entry >= argz + argz_len ? NULL : (char *) entry;
     188      }
     189    else
     190      if (argz_len > 0)
     191        return (char *) argz;
     192      else
     193        return NULL;
     194  }
     195  
     196  
     197  /* Make '\0' separated arg vector ARGZ printable by converting all the '\0's
     198     except the last into the character SEP.  */
     199  void
     200  argz_stringify (char *argz, size_t len, int sep)
     201  {
     202    if (len > 0)
     203      while (1)
     204        {
     205          size_t part_len = strnlen (argz, len);
     206          argz += part_len;
     207          len -= part_len;
     208          if (len-- <= 1)         /* includes final '\0' we want to stop at */
     209            break;
     210          *argz++ = sep;
     211        }
     212  }
     213  
     214  
     215  /* Returns the number of strings in ARGZ.  */
     216  size_t
     217  argz_count (const char *argz, size_t len)
     218  {
     219    size_t count = 0;
     220    while (len > 0)
     221      {
     222        size_t part_len = strlen (argz);
     223        argz += part_len + 1;
     224        len -= part_len + 1;
     225        count++;
     226      }
     227    return count;
     228  }
     229  
     230  
     231  /* Puts pointers to each string in ARGZ, plus a terminating 0 element, into
     232     ARGV, which must be large enough to hold them all.  */
     233  void
     234  argz_extract (const char *argz, size_t len, char **argv)
     235  {
     236    while (len > 0)
     237      {
     238        size_t part_len = strlen (argz);
     239        *argv++ = (char *) argz;
     240        argz += part_len + 1;
     241        len -= part_len + 1;
     242      }
     243    *argv = 0;
     244  }
     245  
     246  
     247  /* Make a '\0' separated arg vector from a unix argv vector, returning it in
     248     ARGZ, and the total length in LEN.  If a memory allocation error occurs,
     249     ENOMEM is returned, otherwise 0.  */
     250  error_t
     251  argz_create (char *const argv[], char **argz, size_t *len)
     252  {
     253    int argc;
     254    size_t tlen = 0;
     255    char *const *ap;
     256    char *p;
     257  
     258    for (argc = 0; argv[argc] != NULL; ++argc)
     259      tlen += strlen (argv[argc]) + 1;
     260  
     261    if (tlen == 0)
     262      *argz = NULL;
     263    else
     264      {
     265        *argz = malloc (tlen);
     266        if (*argz == NULL)
     267          return ENOMEM;
     268  
     269        for (p = *argz, ap = argv; *ap; ++ap, ++p)
     270          p = stpcpy (p, *ap);
     271      }
     272    *len = tlen;
     273  
     274    return 0;
     275  }
     276  
     277  
     278  /* Delete ENTRY from ARGZ & ARGZ_LEN, if any.  */
     279  void
     280  argz_delete (char **argz, size_t *argz_len, char *entry)
     281  {
     282    if (entry)
     283      /* Get rid of the old value for NAME.  */
     284      {
     285        size_t entry_len = strlen (entry) + 1;
     286        *argz_len -= entry_len;
     287        memmove (entry, entry + entry_len, *argz_len - (entry - *argz));
     288        if (*argz_len == 0)
     289          {
     290            free (*argz);
     291            *argz = 0;
     292          }
     293      }
     294  }
     295  
     296  
     297  /* Append BUF, of length BUF_LEN to *TO, of length *TO_LEN, reallocating and
     298     updating *TO & *TO_LEN appropriately.  If an allocation error occurs,
     299     *TO's old value is freed, and *TO is set to 0.  */
     300  static void
     301  str_append (char **to, size_t *to_len, const char *buf, const size_t buf_len)
     302  {
     303    size_t new_len = *to_len + buf_len;
     304    char *new_to = realloc (*to, new_len + 1);
     305  
     306    if (new_to)
     307      {
     308        *((char *) mempcpy (new_to + *to_len, buf, buf_len)) = '\0';
     309        *to = new_to;
     310        *to_len = new_len;
     311      }
     312    else
     313      {
     314        free (*to);
     315        *to = 0;
     316      }
     317  }
     318  
     319  /* Replace any occurrences of the string STR in ARGZ with WITH, reallocating
     320     ARGZ as necessary.  If REPLACE_COUNT is non-zero, *REPLACE_COUNT will be
     321     incremented by number of replacements performed.  */
     322  error_t
     323  argz_replace (char **argz, size_t *argz_len, const char *str, const char *with,
     324                  unsigned *replace_count)
     325  {
     326    error_t err = 0;
     327  
     328    if (str && *str)
     329      {
     330        char *arg = 0;
     331        char *src = *argz;
     332        size_t src_len = *argz_len;
     333        char *dst = 0;
     334        size_t dst_len = 0;
     335        int delayed_copy = 1;     /* True while we've avoided copying anything.  */
     336        size_t str_len = strlen (str), with_len = strlen (with);
     337  
     338        while (!err && (arg = argz_next (src, src_len, arg)))
     339          {
     340            char *match = strstr (arg, str);
     341            if (match)
     342              {
     343                char *from = match + str_len;
     344                size_t to_len = match - arg;
     345                char *to = strndup (arg, to_len);
     346  
     347                while (to && from)
     348                  {
     349                    str_append (&to, &to_len, with, with_len);
     350                    if (to)
     351                      {
     352                        match = strstr (from, str);
     353                        if (match)
     354                          {
     355                            str_append (&to, &to_len, from, match - from);
     356                            from = match + str_len;
     357                          }
     358                        else
     359                          {
     360                            str_append (&to, &to_len, from, strlen (from));
     361                            from = 0;
     362                          }
     363                      }
     364                  }
     365  
     366                if (to)
     367                  {
     368                    if (delayed_copy)
     369                      /* We avoided copying SRC to DST until we found a match;
     370                         now that we've done so, copy everything from the start
     371                         of SRC.  */
     372                      {
     373                        if (arg > src)
     374                          err = argz_append (&dst, &dst_len, src, (arg - src));
     375                        delayed_copy = 0;
     376                      }
     377                    if (! err)
     378                      err = argz_add (&dst, &dst_len, to);
     379                    free (to);
     380                  }
     381                else
     382                  err = ENOMEM;
     383  
     384                if (replace_count)
     385                  (*replace_count)++;
     386              }
     387            else if (! delayed_copy)
     388              err = argz_add (&dst, &dst_len, arg);
     389          }
     390  
     391        if (! err)
     392          {
     393            if (! delayed_copy)
     394              /* We never found any instances of str.  */
     395              {
     396                free (src);
     397                *argz = dst;
     398                *argz_len = dst_len;
     399              }
     400          }
     401        else if (dst_len > 0)
     402          free (dst);
     403      }
     404  
     405    return err;
     406  }