source: trunk/essentials/app-arch/tar/tests/argcv.c

Last change on this file was 3342, checked in by bird, 18 years ago

tar 1.16.1

File size: 7.3 KB
Line 
1/* argcv.c - simple functions for parsing input based on whitespace
2 Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General Public
15 License along with this library; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
17
18#include <ctype.h>
19
20#include <argcv.h>
21
22/*
23 * takes a string and splits it into several strings, breaking at ' '
24 * command is the string to split
25 * the number of strings is placed into argc
26 * the split strings are put into argv
27 * returns 0 on success, nonzero on failure
28 */
29
30#define isws(c) ((c)==' '||(c)=='\t'||(c)=='\n')
31#define isdelim(c,delim) ((c)=='"'||strchr(delim,(c))!=NULL)
32
33static int
34argcv_scan (int len, const char *command, const char *delim, const char* cmnt,
35 int *start, int *end, int *save)
36{
37 int i = 0;
38
39 for (;;)
40 {
41 i = *save;
42
43 if (i >= len)
44 return i + 1;
45
46 /* Skip initial whitespace */
47 while (i < len && isws (command[i]))
48 i++;
49 *start = i;
50
51 switch (command[i])
52 {
53 case '"':
54 case '\'':
55 while (++i < len
56 && (command[i] != command[*start]
57 || command[i-1] == '\\'))
58 ;
59 if (i < len) /* found matching quote */
60 break;
61 /*FALLTHRU*/ default:
62 if (isdelim (command[i], delim))
63 break;
64 /* Skip until next whitespace character or end of line. Honor
65 escaped whitespace. */
66 while (++i < len &&
67 !((isws (command[i]) && command[i-1] != '\\')
68 || isdelim (command[i], delim)));
69 i--;
70 break;
71 }
72
73 *end = i;
74 *save = i + 1;
75
76 /* If we have a token, and it starts with a comment character, skip
77 to the newline and restart the token search. */
78 if (*save <= len)
79 {
80 if (cmnt && strchr (cmnt, command[*start]) != NULL)
81 {
82 i = *save;
83 while (i < len && command[i] != '\n')
84 i++;
85
86 *save = i;
87 continue;
88 }
89 }
90 break;
91 }
92 return *save;
93}
94
95static char escape_transtab[] = "\\\\a\ab\bf\fn\nr\rt\t";
96
97int
98argcv_unescape_char (int c)
99{
100 char *p;
101
102 for (p = escape_transtab; *p; p += 2)
103 {
104 if (*p == c)
105 return p[1];
106 }
107 return c;
108}
109
110int
111argcv_escape_char (int c)
112{
113 char *p;
114
115 for (p = escape_transtab + sizeof(escape_transtab) - 2;
116 p > escape_transtab; p -= 2)
117 {
118 if (*p == c)
119 return p[-1];
120 }
121 return -1;
122}
123
124
125static int
126xtonum (const char *src, int base, size_t cnt)
127{
128 int val;
129 char *p;
130 char tmp[4]; /* At most three characters + zero */
131
132 /* Notice: No use to check `cnt'. It should be either 2 or 3 */
133 memcpy (tmp, src, cnt);
134 tmp[cnt] = 0;
135 val = strtoul (tmp, &p, base);
136 return (*p == 0) ? val : -1;
137}
138
139static size_t
140escaped_length (const char *str, int *quote)
141{
142 size_t len = 0;
143
144 for (; *str; str++)
145 {
146 if (*str == ' ')
147 {
148 len++;
149 *quote = 1;
150 }
151 else if (*str == '"')
152 {
153 len += 2;
154 *quote = 1;
155 }
156 else if (isprint (*str))
157 len++;
158 else if (argcv_escape_char (*str) != -1)
159 len += 2;
160 else
161 len += 4;
162 }
163 return len;
164}
165
166static void
167unescape_copy (char *dst, const char *src, size_t n)
168{
169 int c;
170
171 while (n > 0)
172 {
173 n--;
174 if (*src == '\\')
175 {
176 switch (*++src)
177 {
178 case 'x':
179 case 'X':
180 ++src;
181 --n;
182 if (n == 0)
183 {
184 *dst++ = '\\';
185 *dst++ = src[-1];
186 }
187 else
188 {
189 c = xtonum(src, 16, 2);
190 if (c == -1)
191 {
192 *dst++ = '\\';
193 *dst++ = src[-1];
194 }
195 else
196 {
197 *dst++ = c;
198 src += 2;
199 n -= 2;
200 }
201 }
202 break;
203
204 case '0':
205 ++src;
206 --n;
207 if (n == 0)
208 {
209 *dst++ = '\\';
210 *dst++ = src[-1];
211 }
212 else
213 {
214 c = xtonum(src, 8, 3);
215 if (c == -1)
216 {
217 *dst++ = '\\';
218 *dst++ = src[-1];
219 }
220 else
221 {
222 *dst++ = c;
223 src += 3;
224 n -= 3;
225 }
226 }
227 break;
228
229 default:
230 *dst++ = argcv_unescape_char (*src++);
231 n--;
232 }
233 }
234 else
235 {
236 *dst++ = *src++;
237 }
238 }
239 *dst = 0;
240}
241
242static void
243escape_copy (char *dst, const char *src)
244{
245 for (; *src; src++)
246 {
247 if (*src == '"')
248 {
249 *dst++ = '\\';
250 *dst++ = '"';
251 }
252 else if (*src != '\t' && isprint(*src))
253 *dst++ = *src;
254 else
255 {
256 int c = argcv_escape_char (*src);
257 *dst++ = '\\';
258 if (c != -1)
259 *dst++ = c;
260 else
261 {
262 char tmp[4];
263 snprintf (tmp, sizeof tmp, "%03o", *(unsigned char*)src);
264 memcpy (dst, tmp, 3);
265 dst += 3;
266 }
267 }
268 }
269}
270
271int
272argcv_get (const char *command, const char *delim, const char* cmnt,
273 int *argc, char ***argv)
274{
275 int len = strlen (command);
276 int i = 0;
277 int start, end, save;
278
279 *argv = NULL;
280
281 /* Count number of arguments */
282 *argc = 0;
283 save = 0;
284
285 while (argcv_scan (len, command, delim, cmnt, &start, &end, &save) <= len)
286 (*argc)++;
287
288 *argv = calloc ((*argc + 1), sizeof (char *));
289
290 i = 0;
291 save = 0;
292 for (i = 0; i < *argc; i++)
293 {
294 int n;
295 argcv_scan (len, command, delim, cmnt, &start, &end, &save);
296
297 if ((command[start] == '"' || command[end] == '\'')
298 && command[end] == command[start])
299 {
300 start++;
301 end--;
302 }
303 n = end - start + 1;
304 (*argv)[i] = calloc (n+1, sizeof (char));
305 if ((*argv)[i] == NULL)
306 return 1;
307 unescape_copy ((*argv)[i], &command[start], n);
308 (*argv)[i][n] = 0;
309 }
310 (*argv)[i] = NULL;
311 return 0;
312}
313
314/*
315 * frees all elements of an argv array
316 * argc is the number of elements
317 * argv is the array
318 */
319int
320argcv_free (int argc, char **argv)
321{
322 while (--argc >= 0)
323 if (argv[argc])
324 free (argv[argc]);
325 free (argv);
326 return 1;
327}
328
329/* Take a argv an make string separated by ' '. */
330
331int
332argcv_string (int argc, char **argv, char **pstring)
333{
334 size_t i, j, len;
335 char *buffer;
336
337 /* No need. */
338 if (pstring == NULL)
339 return 1;
340
341 buffer = malloc (1);
342 if (buffer == NULL)
343 return 1;
344 *buffer = '\0';
345
346 for (len = i = j = 0; i < argc; i++)
347 {
348 int quote = 0;
349 int toklen;
350
351 toklen = escaped_length (argv[i], &quote);
352
353 len += toklen + 2;
354 if (quote)
355 len += 2;
356
357 buffer = realloc (buffer, len);
358 if (buffer == NULL)
359 return 1;
360
361 if (i != 0)
362 buffer[j++] = ' ';
363 if (quote)
364 buffer[j++] = '"';
365 escape_copy (buffer + j, argv[i]);
366 j += toklen;
367 if (quote)
368 buffer[j++] = '"';
369 }
370
371 for (; j > 0 && isspace (buffer[j-1]); j--)
372 ;
373 buffer[j] = 0;
374 if (pstring)
375 *pstring = buffer;
376 return 0;
377}
378
379#if 0
380char *command = "set prompt=\"& \a\\\"\" \\x25\\0145\\098\\ta";
381
382main(int xargc, char **xargv)
383{
384 int i, argc;
385 char **argv;
386 char *s;
387
388 argcv_get (xargv[1] ? xargv[1]:command, "=", "#", &argc, &argv);
389 printf ("%d args:\n", argc);
390 for (i = 0; i < argc; i++)
391 printf ("%s\n", argv[i]);
392 printf ("===\n");
393 argcv_string (argc, argv, &s);
394 printf ("%s\n", s);
395}
396#endif
Note: See TracBrowser for help on using the repository browser.