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