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