Imported Upstream version 2.5.1
[debian/amanda] / common-src / token.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1997-1998 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /*
27  * $Id: token.c,v 1.32 2006/07/19 17:41:15 martinea Exp $
28  *
29  * token bashing routines
30  */
31
32 /*
33 ** The quoting method used here was selected because it has the
34 ** property that quoting a string that doesn't contain funny
35 ** characters results in an unchanged string and it was easy to code.
36 ** There are probably other algorithms that are just as effective.
37 **/
38
39 #include "amanda.h"
40 #include "arglist.h"
41 #include "token.h"
42
43 /* Split a string up into tokens.
44 ** There is exactly one separator character between tokens.
45 ** XXX Won't work too well if separator is '\'!
46 **
47 ** Inspired by awk and a routine called splitter() that I snarfed from
48 ** the net ages ago (original author long forgotten).
49 */
50 int
51 split(
52     char *      str,    /* String to split */
53     char **     token,  /* Array of token pointers */
54     int         toklen, /* Size of token[] */
55     char *      sep)    /* Token separators - usually " " */
56 {
57     register char *pi, *po;
58     register int fld;
59     register size_t len;
60     static char *buf = (char *)0; /* XXX - static buffer */
61     int in_quotes;
62
63     assert(str && token && toklen > 0 && sep);
64
65     token[0] = str;
66
67     for (fld = 1; fld < toklen; fld++) token[fld] = (char *)0;
68
69     fld = 0;
70
71     if (*sep == '\0' || *str == '\0' || toklen == 1) return fld;
72
73     /* Calculate the length of the unquoted string. */
74     len = strlen(str);;
75
76     /* Allocate some space */
77
78     buf = newalloc(buf, len+1);
79
80     /* Copy it across and tokenise it */
81
82     in_quotes = 0;
83     po = buf;
84     token[++fld] = po;
85     for (pi = str; *pi && *pi != '\0'; pi++) {
86         if (*pi == '\n' && !in_quotes)
87             break;
88
89         if (!in_quotes && strchr(sep, *pi)) {
90             /*
91              * separator
92              * Advance to next field.
93              */
94             *po = '\0'; /* end of token */
95             if (fld+1 >= toklen) return fld; /* too many tokens */
96             token[++fld] = po + 1;
97             po++;
98             continue;
99         }
100
101         if (*pi == '"') {
102             /*
103              * Start or end of quote
104              * Emit quote in either case
105              */
106             in_quotes = !in_quotes;
107         } else if (in_quotes && *pi == '\\' && (*(pi + 1) == '"')) {
108             /*
109              * Quoted quote.
110              * emit '/' - default will pick up '"'
111              */
112             *po++ = *pi++;
113         }
114         *po++ = *pi;    /* Emit character */
115     }
116     *po = '\0';
117
118     assert(po == buf + len);    /* Just checking! */
119
120     return fld;
121 }
122
123 /*
124 ** Quote all the funny characters in one token.
125 ** - squotef - formatted string with space separator
126 ** - quotef  - formatted string with specified separators
127 ** - squote  - fixed string with space separator
128 ** - quote   - fixed strings with specified separators
129 **/
130 printf_arglist_function(char *squotef, char *, format)
131 {
132         va_list argp;
133         char linebuf[16384];
134
135         /* Format the token */
136
137         arglist_start(argp, format);
138         vsnprintf(linebuf, SIZEOF(linebuf), format, argp);
139         arglist_end(argp);
140
141         return quote(" ", linebuf);
142 }
143
144 printf_arglist_function1(char *quotef, char *, sep, char *, format)
145 {
146         va_list argp;
147         char linebuf[16384];
148
149         /* Format the token */
150
151         arglist_start(argp, format);
152         vsnprintf(linebuf, SIZEOF(linebuf), format, argp);
153         arglist_end(argp);
154
155         return quote(sep, linebuf);
156 }
157
158 char *squote(
159     char *      str)    /* the string to quote */
160 {
161         return quote(" ", str);
162 }
163
164 char *
165 quote(
166     char *      sepchr, /* separators that also need quoting */
167     char *      str)    /* the string to quote */
168 {
169     register char *pi, *po;
170     register size_t len;
171     char *buf;
172     int sep, need_quotes;
173
174     /* Calculate the length of the quoted token. */
175
176     sep = 0;
177     len = 0;
178     for (pi = str; *pi; pi++) {
179         if (*pi < ' ' || *pi > '~')
180             len = len + 4;
181         else if (*pi == '\\' || *pi == '"')
182             len = len + 2;
183         else if (*sepchr && strchr(sepchr, *pi)) {
184             len = len + 1;
185             sep++;
186         }
187         else
188             len++;
189     }
190
191     need_quotes = (sep != 0);
192
193     if (need_quotes) len = len + 2;
194
195     /* Allocate some space */
196
197     buf = alloc(len+1);         /* trailing null */
198
199     /* Copy it across */
200
201     po = buf;
202
203     if (need_quotes) *po++ = '"';
204
205     for (pi = str; *pi; pi++) {
206         if (*pi < ' ' || *pi > '~') {
207             *po++ = '\\';
208             *po++ = (char)(((*pi >> 6) & 07) + '0');
209             *po++ = (char)(((*pi >> 3) & 07) + '0');
210             *po++ = (char)(((*pi     ) & 07) + '0');
211         }
212         else if (*pi == '\\' || *pi == '"') {
213             *po++ = '\\';
214             *po++ = *pi;
215         }
216         else *po++ = *pi;
217     }
218
219     if (need_quotes) *po++ = '"';
220
221     *po = '\0';
222
223     assert(po == (buf + len));  /* Just checking! */
224
225     return buf;
226 }
227
228 /* Quote a string so that it can be used as a regular expression */
229 char *
230 rxquote(
231     char *      str)    /* the string to quote */
232 {
233     char *pi, *po;
234     size_t len;
235     char *buf;
236
237     /* Calculate the length of the quoted token. */
238
239     len = 0;
240     for (pi = str; *pi; pi++) {
241         switch (*pi) {
242             /* regular expression meta-characters: */
243 #define META_CHARS \
244         case '\\': \
245         case '.': \
246         case '?': \
247         case '*': \
248         case '+': \
249         case '^': \
250         case '$': \
251         case '|': \
252         case '(': \
253         case ')': \
254         case '[': \
255         case ']': \
256         case '{': \
257         case '}' /* no colon */
258         META_CHARS:
259             len++;
260             /* fall through */
261         default:
262             len++;
263             break;
264         }
265     }
266
267     /* Allocate some space */
268
269     buf = alloc(len+1);         /* trailing null */
270
271     /* Copy it across */
272
273     po = buf;
274
275     for (pi = str; *pi; pi++) {
276         switch (*pi) {
277         META_CHARS:
278 #undef META_CHARS
279             *po++ = '\\';
280             /* fall through */
281         default:
282             *po++ = *pi;
283             break;
284         }
285     }
286
287     *po = '\0';
288
289     assert(po == (buf + len));  /* Just checking! */
290
291     return buf;
292 }
293
294 #ifndef HAVE_SHQUOTE
295 /* Quote a string so that it can be safely passed to a shell */
296 char *
297 shquote(
298     char *      str)    /* the string to quote */
299 {
300     char *pi, *po;
301     size_t len;
302     char *buf;
303
304     /* Calculate the length of the quoted token. */
305
306     len = 0;
307     for (pi = str; *pi; pi++) {
308         switch (*pi) {
309             /* shell meta-characters: */
310 #define META_CHARS \
311         case '\\': \
312         case ' ': \
313         case '\t': \
314         case '\n': \
315         case '?': \
316         case '*': \
317         case '$': \
318         case '~': \
319         case '!': \
320         case ';': \
321         case '&': \
322         case '<': \
323         case '>': \
324         case '\'': \
325         case '\"': \
326         case '`': \
327         case '|': \
328         case '(': \
329         case ')': \
330         case '[': \
331         case ']': \
332         case '{': \
333         case '}' /* no colon */
334         META_CHARS:
335             len++;
336             /* fall through */
337         default:
338             len++;
339             break;
340         }
341     }
342
343     /* Allocate some space */
344
345     buf = alloc(len+1);         /* trailing null */
346
347     /* Copy it across */
348
349     po = buf;
350
351     for (pi = str; *pi; pi++) {
352         switch (*pi) {
353         META_CHARS:
354 #undef META_CHARS
355             *po++ = '\\';
356             /* fall through */
357         default:
358             *po++ = *pi;
359             break;
360         }
361     }
362
363     *po = '\0';
364
365     assert(po == (buf + len));  /* Just checking! */
366
367     return buf;
368 }
369 #endif
370
371 /* Table lookup.
372 */
373 int
374 table_lookup(
375     table_t *   table,
376     char *      str)
377 {
378         while(table->word != (char *)0) {
379                 if (*table->word == *str && strcmp(table->word, str) == 0) {
380                         return table->value;
381                 }
382                 table++;
383         }
384
385         return table->value;
386 }
387
388 /* Reverse table lookup.
389 */
390 char *
391 table_lookup_r(
392     /*@keep@*/  table_t *       table,
393                 int             val)
394 {
395         while(table->word != (char *)0) {
396                 if (table->value == val) {
397                         return table->word;
398                 }
399                 table++;
400         }
401
402         return (char *)0;
403 }
404
405 #ifdef TEST
406
407 int
408 main(
409     int         argc,
410     char **     argv)
411 {
412         char *str = NULL;
413         char *t[20];
414         int r;
415         char *sr;
416         int i;
417
418         safe_fd(-1, 0);
419
420         /* shut up compiler */
421         argc = argc;
422         argv = argv;
423
424         set_pname("token test");
425
426         dbopen(NULL);
427
428         /* Don't die when child closes pipe */
429         signal(SIGPIPE, SIG_IGN);
430
431         erroutput_type = ERR_INTERACTIVE;
432
433         printf("Testing split() with \" \" token separator\n");
434         while(1) {
435                 printf("Input string: ");
436                 amfree(str);
437                 if ((str = agets(stdin)) == NULL) {
438                         printf("\n");
439                         break;
440                 }
441                 r = split(str, t, 20, " ");
442                 printf("%d token%s:\n", r, (r == 1) ? "" : "s");
443                 for (i=0; i <= r; i++) printf("tok[%d] = \"%s\"\n", i, t[i]);
444         }
445         amfree(str);
446         printf("\n");
447
448         printf("Testing quote()\n");
449         while(1) {
450                 printf("Input string: ");
451                 amfree(str);
452                 if ((str = agets(stdin)) == NULL) {
453                         printf("\n");
454                         break;
455                 }
456                 sr = squote(str);
457                 printf("Quoted   = \"%s\"\n", sr);
458                 strncpy(str,sr,SIZEOF(str)-1);
459                 str[SIZEOF(str)-1] = '\0';
460                 r = split(str, t, 20, " ");
461                 if (r != 1) printf("split()=%d!\n", r);
462                 printf("Unquoted = \"%s\"\n", t[1]);
463                 amfree(sr);
464         }
465         amfree(str);
466         return 0;
467 }
468
469 #endif