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