]> git.cworth.org Git - tar/blob - tests/argcv.c
Imported Upstream version 1.20
[tar] / tests / argcv.c
1 /* argcv.c - simple functions for parsing input based on whitespace
2    Copyright (C) 1999, 2000, 2001, 2007 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 3 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
33 static int
34 argcv_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
95 static char escape_transtab[] = "\\\\a\ab\bf\fn\nr\rt\t";
96
97 int
98 argcv_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
110 int
111 argcv_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
125 static int
126 xtonum (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
139 static size_t
140 escaped_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
166 static void
167 unescape_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
242 static void
243 escape_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
271 int
272 argcv_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  */
319 int
320 argcv_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
331 int
332 argcv_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
380 char *command = "set prompt=\"& \a\\\"\" \\x25\\0145\\098\\ta";
381
382 main(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