srclist.update
[gnulib.git] / lib / argz.c
1 /* argz.c -- argz implementation for non-glibc systems
2    Copyright (C) 2004 Free Software Foundation, Inc.
3    Originally by Gary V. Vaughan  <gary@gnu.org>
4
5    NOTE: The canonical source of this file is maintained with the
6    GNU Libtool package.  Report bugs to bug-libtool@gnu.org.
7
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2, or (at your option)
11    any later version.
12
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License along
19    with this program; if not, write to the Free Software Foundation,
20    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
21
22 #if defined(HAVE_CONFIG_H)
23 #  if defined(LTDL) && defined LT_CONFIG_H
24 #    include LT_CONFIG_H
25 #  else
26 #    include <config.h>
27 #  endif
28 #endif
29
30 #include <argz.h>
31
32 #include <assert.h>
33 #include <stddef.h>
34 #include <stdlib.h>
35 #include <sys/types.h>
36 #include <errno.h>
37
38 #if defined(HAVE_STRING_H)
39 #  include <string.h>
40 #elif defined(HAVE_STRINGS_H)
41 #  include <strings.h>
42 #endif
43 #if defined(HAVE_MEMORY_H)
44 #  include <memory.h>
45 #endif
46
47 #define EOS_CHAR '\0'
48
49 error_t
50 argz_append (char **pargz, size_t *pargz_len, const char *buf, size_t buf_len)
51 {
52   size_t argz_len;
53   char  *argz;
54
55   assert (pargz);
56   assert (pargz_len);
57   assert ((*pargz && *pargz_len) || (!*pargz && !*pargz_len));
58
59   /* If nothing needs to be appended, no more work is required.  */
60   if (buf_len == 0)
61     return 0;
62
63   /* Ensure there is enough room to append BUF_LEN.  */
64   argz_len = *pargz_len + buf_len;
65   argz = (char *) realloc (*pargz, argz_len);
66   if (!argz)
67     return ENOMEM;
68
69   /* Copy characters from BUF after terminating '\0' in ARGZ.  */
70   memcpy (argz + *pargz_len, buf, buf_len);
71
72   /* Assign new values.  */
73   *pargz = argz;
74   *pargz_len = argz_len;
75
76   return 0;
77 }
78
79
80 error_t
81 argz_create_sep (const char *str, int delim, char **pargz, size_t *pargz_len)
82 {
83   size_t argz_len;
84   char *argz = 0;
85
86   assert (str);
87   assert (pargz);
88   assert (pargz_len);
89
90   /* Make a copy of STR, but replacing each occurrence of
91      DELIM with '\0'.  */
92   argz_len = 1+ strlen (str);
93   if (argz_len)
94     {
95       const char *p;
96       char *q;
97
98       argz = (char *) malloc (argz_len);
99       if (!argz)
100         return ENOMEM;
101
102       for (p = str, q = argz; *p != EOS_CHAR; ++p)
103         {
104           if (*p == delim)
105             {
106               /* Ignore leading delimiters, and fold consecutive
107                  delimiters in STR into a single '\0' in ARGZ.  */
108               if ((q > argz) && (q[-1] != EOS_CHAR))
109                 *q++ = EOS_CHAR;
110               else
111                 --argz_len;
112             }
113           else
114             *q++ = *p;
115         }
116       /* Copy terminating EOS_CHAR.  */
117       *q = *p;
118     }
119
120   /* If ARGZ_LEN has shrunk to nothing, release ARGZ's memory.  */
121   if (!argz_len)
122     argz = (free (argz), (char *) 0);
123
124   /* Assign new values.  */
125   *pargz = argz;
126   *pargz_len = argz_len;
127
128   return 0;
129 }
130
131
132 error_t
133 argz_insert (char **pargz, size_t *pargz_len, char *before, const char *entry)
134 {
135   assert (pargz);
136   assert (pargz_len);
137   assert (entry && *entry);
138
139   /* No BEFORE address indicates ENTRY should be inserted after the
140      current last element.  */
141   if (!before)
142     return argz_append (pargz, pargz_len, entry, 1+ strlen (entry));
143
144   /* This probably indicates a programmer error, but to preserve
145      semantics, scan back to the start of an entry if BEFORE points
146      into the middle of it.  */
147   while ((before > *pargz) && (before[-1] != EOS_CHAR))
148     --before;
149
150   {
151     size_t entry_len    = 1+ strlen (entry);
152     size_t argz_len     = *pargz_len + entry_len;
153     size_t offset       = before - *pargz;
154     char   *argz        = (char *) realloc (*pargz, argz_len);
155
156     if (!argz)
157       return ENOMEM;
158
159     /* Make BEFORE point to the equivalent offset in ARGZ that it
160        used to have in *PARGZ incase realloc() moved the block.  */
161     before = argz + offset;
162
163     /* Move the ARGZ entries starting at BEFORE up into the new
164        space at the end -- making room to copy ENTRY into the
165        resulting gap.  */
166     memmove (before + entry_len, before, *pargz_len - offset);
167     memcpy  (before, entry, entry_len);
168
169     /* Assign new values.  */
170     *pargz = argz;
171     *pargz_len = argz_len;
172   }
173
174   return 0;
175 }
176
177
178 char *
179 argz_next (char *argz, size_t argz_len, const char *entry)
180 {
181   assert ((argz && argz_len) || (!argz && !argz_len));
182
183   if (entry)
184     {
185       /* Either ARGZ/ARGZ_LEN is empty, or ENTRY points into an address
186          within the ARGZ vector.  */
187       assert ((!argz && !argz_len)
188               || ((argz <= entry) && (entry < (argz + argz_len))));
189
190       /* Move to the char immediately after the terminating
191          '\0' of ENTRY.  */
192       entry = 1+ strchr (entry, EOS_CHAR);
193
194       /* Return either the new ENTRY, or else NULL if ARGZ is
195          exhausted.  */
196       return (entry >= argz + argz_len) ? 0 : (char *) entry;
197     }
198   else
199     {
200       /* This should probably be flagged as a programmer error,
201          since starting an argz_next loop with the iterator set
202          to ARGZ is safer.  To preserve semantics, handle the NULL
203          case by returning the start of ARGZ (if any).  */
204       if (argz_len > 0)
205         return argz;
206       else
207         return 0;
208     }
209 }
210
211
212 void
213 argz_stringify (char *argz, size_t argz_len, int sep)
214 {
215   assert ((argz && argz_len) || (!argz && !argz_len));
216
217   if (sep)
218     {
219       --argz_len;               /* don't stringify the terminating EOS */
220       while (--argz_len > 0)
221         {
222           if (argz[argz_len] == EOS_CHAR)
223             argz[argz_len] = sep;
224         }
225     }
226 }