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