need terminating */ for argz.c; what about others?
[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 /* Provide our wierdo HAVE_CONFIG_H rvalue for other clients.  */
23 #if !defined(LTDL) && defined(HAVE_CONFIG_H)
24 #  define HAVE_CONFIG_H <config.h>
25 #endif
26
27 #if defined(HAVE_CONFIG_H)
28 #  include HAVE_CONFIG_H
29 #endif
30
31 #include <argz.h>
32
33 #include <assert.h>
34 #include <stddef.h>
35 #include <stdlib.h>
36 #include <sys/types.h>
37 #include <errno.h>
38
39 #if defined(HAVE_STRING_H)
40 #  include <string.h>
41 #elif defined(HAVE_STRINGS_H)
42 #  include <strings.h>
43 #endif
44 #if defined(HAVE_MEMORY_H)
45 #  include <memory.h>
46 #endif
47
48 #define EOS_CHAR '\0'
49
50 error_t
51 argz_append (char **pargz, size_t *pargz_len, const char *buf, size_t buf_len)
52 {
53   size_t argz_len;
54   char  *argz;
55
56   assert (pargz);
57   assert (pargz_len);
58   assert ((*pargz && *pargz_len) || (!*pargz && !*pargz_len));
59
60   /* If nothing needs to be appended, no more work is required.  */
61   if (buf_len == 0)
62     return 0;
63
64   /* Ensure there is enough room to append BUF_LEN.  */
65   argz_len = *pargz_len + buf_len;
66   argz = (char *) realloc (*pargz, argz_len);
67   if (!argz)
68     return ENOMEM;
69
70   /* Copy characters from BUF after terminating '\0' in ARGZ.  */
71   memcpy (argz + *pargz_len, buf, buf_len);
72
73   /* Assign new values.  */
74   *pargz = argz;
75   *pargz_len = argz_len;
76
77   return 0;
78 }
79
80
81 error_t
82 argz_create_sep (const char *str, int delim, char **pargz, size_t *pargz_len)
83 {
84   size_t argz_len;
85   char *argz = 0;
86
87   assert (str);
88   assert (pargz);
89   assert (pargz_len);
90
91   /* Make a copy of STR, but replacing each occurrence of
92      DELIM with '\0'.  */
93   argz_len = 1+ strlen (str);
94   if (argz_len)
95     {
96       const char *p;
97       char *q;
98
99       argz = (char *) malloc (argz_len);
100       if (!argz)
101         return ENOMEM;
102
103       for (p = str, q = argz; *p != EOS_CHAR; ++p)
104         {
105           if (*p == delim)
106             {
107               /* Ignore leading delimiters, and fold consecutive
108                  delimiters in STR into a single '\0' in ARGZ.  */
109               if ((q > argz) && (q[-1] != EOS_CHAR))
110                 *q++ = EOS_CHAR;
111               else
112                 --argz_len;
113             }
114           else
115             *q++ = *p;
116         }
117       /* Copy terminating EOS_CHAR.  */
118       *q = *p;
119     }
120
121   /* If ARGZ_LEN has shrunk to nothing, release ARGZ's memory.  */
122   if (!argz_len)
123     argz = (free (argz), (char *) 0);
124
125   /* Assign new values.  */
126   *pargz = argz;
127   *pargz_len = argz_len;
128
129   return 0;
130 }
131
132
133 error_t
134 argz_insert (char **pargz, size_t *pargz_len, char *before, const char *entry)
135 {
136   assert (pargz);
137   assert (pargz_len);
138   assert (entry && *entry);
139
140   /* No BEFORE address indicates ENTRY should be inserted after the
141      current last element.  */
142   if (!before)
143     return argz_append (pargz, pargz_len, entry, 1+ strlen (entry));
144
145   /* This probably indicates a programmer error, but to preserve
146      semantics, scan back to the start of an entry if BEFORE points
147      into the middle of it.  */
148   while ((before > *pargz) && (before[-1] != EOS_CHAR))
149     --before;
150
151   {
152     size_t entry_len    = 1+ strlen (entry);
153     size_t argz_len     = *pargz_len + entry_len;
154     size_t offset       = before - *pargz;
155     char   *argz        = (char *) realloc (*pargz, argz_len);
156
157     if (!argz)
158       return ENOMEM;
159
160     /* Make BEFORE point to the equivalent offset in ARGZ that it
161        used to have in *PARGZ incase realloc() moved the block.  */
162     before = argz + offset;
163
164     /* Move the ARGZ entries starting at BEFORE up into the new
165        space at the end -- making room to copy ENTRY into the
166        resulting gap.  */
167     memmove (before + entry_len, before, *pargz_len - offset);
168     memcpy  (before, entry, entry_len);
169
170     /* Assign new values.  */
171     *pargz = argz;
172     *pargz_len = argz_len;
173   }
174
175   return 0;
176 }
177
178
179 char *
180 argz_next (char *argz, size_t argz_len, const char *entry)
181 {
182   assert ((argz && argz_len) || (!argz && !argz_len));
183
184   if (entry)
185     {
186       /* Either ARGZ/ARGZ_LEN is empty, or ENTRY points into an address
187          within the ARGZ vector.  */
188       assert ((!argz && !argz_len)
189               || ((argz <= entry) && (entry < (argz + argz_len))));
190
191       /* Move to the char immediately after the terminating
192          '\0' of ENTRY.  */
193       entry = 1+ strchr (entry, EOS_CHAR);
194
195       /* Return either the new ENTRY, or else NULL if ARGZ is
196          exhausted.  */
197       return (entry >= argz + argz_len) ? 0 : (char *) entry;
198     }
199   else
200     {
201       /* This should probably be flagged as a programmer error,
202          since starting an argz_next loop with the iterator set
203          to ARGZ is safer.  To preserve semantics, handle the NULL
204          case by returning the start of ARGZ (if any).  */
205       if (argz_len > 0)
206         return argz;
207       else
208         return 0;
209     }
210 }
211
212
213 void
214 argz_stringify (char *argz, size_t argz_len, int sep)
215 {
216   assert ((argz && argz_len) || (!argz && !argz_len));
217
218   if (sep)
219     {
220       --argz_len;               /* don't stringify the terminating EOS */
221       while (--argz_len > 0)
222         {
223           if (argz[argz_len] == EOS_CHAR)
224             argz[argz_len] = sep;
225         }
226     }
227 }