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