d894f40575b614c9f586cbb9bae58882a403f4c8
[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.29 2006/01/14 04:37:19 paddy_s 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 split(str, token, toklen, sep)
51 char *str;      /* String to split */
52 char **token;   /* Array of token pointers */
53 int toklen;     /* Size of token[] */
54 char *sep;      /* Token separators - usually " " */
55 {
56     register char *pi, *po;
57     register int fld;
58     register size_t len;
59     static char *buf = (char *)0; /* XXX - static buffer */
60     int in_quotes;
61
62     assert(str && token && toklen > 0 && sep);
63
64     token[0] = str;
65
66     for (fld = 1; fld < toklen; fld++) token[fld] = (char *)0;
67
68     fld = 0;
69
70     if (*sep == '\0' || *str == '\0' || toklen == 1) return fld;
71
72     /* Calculate the length of the unquoted string. */
73
74     len = 0;
75     for (pi = str; *pi && *pi != '\n'; pi++) {
76         switch(*pi) {
77         case '\\':      /* had better not be trailing... */
78             pi++;
79             if (*pi >= '0' && *pi <= '3') pi = pi + 2;
80             len++;
81             break;
82         case '"':       /* just ignore "'s */
83             break;
84         default:
85             len++;
86         }
87     }
88
89     /* Allocate some space */
90
91     buf = newalloc(buf, len+1);
92
93     /* Copy it across and tokenise it */
94
95     in_quotes = 0;
96     po = buf;
97     token[++fld] = po;
98     for (pi = str; *pi && *pi != '\n'; pi++) {
99         if (*pi == '\\') {      /* escape */
100             pi++;
101             if (*pi >= '0' && *pi <= '3') {
102                 *po =       ((*pi++ - '0') << 6);
103                 *po = *po + ((*pi++ - '0') << 3);
104                 *po = *po + ((*pi   - '0')     );
105             }
106             else *po = *pi;
107             po++;
108         }
109         else if (*pi == '"') {  /* quotes */
110             in_quotes = !in_quotes;
111         }
112         else if (!in_quotes && strchr(sep, *pi)) {      /* separator */
113             *po = '\0'; /* end of token */
114             if (fld+1 >= toklen) return fld; /* too many tokens */
115             token[++fld] = po + 1;
116             po++;
117         }
118         else {
119             *po++ = *pi;        /* normal */
120         }
121     }
122     *po = '\0';
123
124     assert(po == buf + len);    /* Just checking! */
125
126     return fld;
127 }
128
129 /*
130 ** Quote all the funny characters in one token.
131 ** - squotef - formatted string with space separator
132 ** - quotef  - formatted string with specified separators
133 ** - squote  - fixed string with space separator
134 ** - quote   - fixed strings with specified separators
135 **/
136 printf_arglist_function(char *squotef, char *, format)
137 {
138         va_list argp;
139         char linebuf[16384];
140
141         /* Format the token */
142
143         arglist_start(argp, format);
144         vsnprintf(linebuf, sizeof(linebuf), format, argp);
145         arglist_end(argp);
146
147         return quote(" ", linebuf);
148 }
149
150 printf_arglist_function1(char *quotef, char *, sep, char *, format)
151 {
152         va_list argp;
153         char linebuf[16384];
154
155         /* Format the token */
156
157         arglist_start(argp, format);
158         vsnprintf(linebuf, sizeof(linebuf), format, argp);
159         arglist_end(argp);
160
161         return quote(sep, linebuf);
162 }
163
164 char *squote(str)
165 char *str;      /* the string to quote */
166 {
167         return quote(" ", str);
168 }
169
170 char *quote(sepchr, str)
171 char *sepchr;   /* separators that also need quoting */
172 char *str;      /* the string to quote */
173 {
174     register char *pi, *po;
175     register size_t len;
176     char *buf;
177     int sep, need_quotes;
178
179     /* Calculate the length of the quoted token. */
180
181     len = sep = 0;
182     for (pi = str; *pi; pi++) {
183         if (*pi < ' ' || *pi > '~')
184             len = len + 4;
185         else if (*pi == '\\' || *pi == '"')
186             len = len + 2;
187         else if (*sepchr && strchr(sepchr, *pi)) {
188             len = len + 1;
189             sep++;
190         }
191         else
192             len++;
193     }
194
195     need_quotes = (sep != 0);
196
197     if (need_quotes) len = len + 2;
198
199     /* Allocate some space */
200
201     buf = alloc(len+1);         /* trailing null */
202
203     /* Copy it across */
204
205     po = buf;
206
207     if (need_quotes) *po++ = '"';
208
209     for (pi = str; *pi; pi++) {
210         if (*pi < ' ' || *pi > '~') {
211             *po++ = '\\';
212             *po++ = ((*pi >> 6) & 07) + '0';
213             *po++ = ((*pi >> 3) & 07) + '0';
214             *po++ = ((*pi     ) & 07) + '0';
215         }
216         else if (*pi == '\\' || *pi == '"') {
217             *po++ = '\\';
218             *po++ = *pi;
219         }
220         else *po++ = *pi;
221     }
222
223     if (need_quotes) *po++ = '"';
224
225     *po = '\0';
226
227     assert(po - buf == len);    /* Just checking! */
228
229     return buf;
230 }
231
232 /* Quote a string so that it can be used as a regular expression */
233 char *rxquote(str)
234 char *str;      /* the string to quote */
235 {
236     char *pi, *po;
237     size_t len;
238     char *buf;
239
240     /* Calculate the length of the quoted token. */
241
242     len = 0;
243     for (pi = str; *pi; pi++) {
244         switch (*pi) {
245             /* regular expression meta-characters: */
246 #define META_CHARS \
247         case '\\': \
248         case '.': \
249         case '?': \
250         case '*': \
251         case '+': \
252         case '^': \
253         case '$': \
254         case '|': \
255         case '(': \
256         case ')': \
257         case '[': \
258         case ']': \
259         case '{': \
260         case '}' /* no colon */
261         META_CHARS:
262             len++;
263             /* fall through */
264         default:
265             len++;
266             break;
267         }
268     }
269
270     /* Allocate some space */
271
272     buf = alloc(len+1);         /* trailing null */
273
274     /* Copy it across */
275
276     po = buf;
277
278     for (pi = str; *pi; pi++) {
279         switch (*pi) {
280         META_CHARS:
281 #undef META_CHARS
282             *po++ = '\\';
283             /* fall through */
284         default:
285             *po++ = *pi;
286             break;
287         }
288     }
289
290     *po = '\0';
291
292     assert(po - buf == len);    /* Just checking! */
293
294     return buf;
295 }
296
297 #ifndef HAVE_SHQUOTE
298 /* Quote a string so that it can be safely passed to a shell */
299 char *shquote(str)
300 char *str;      /* the string to quote */
301 {
302     char *pi, *po;
303     size_t len;
304     char *buf;
305
306     /* Calculate the length of the quoted token. */
307
308     len = 0;
309     for (pi = str; *pi; pi++) {
310         switch (*pi) {
311             /* shell meta-characters: */
312 #define META_CHARS \
313         case '\\': \
314         case ' ': \
315         case '\t': \
316         case '\n': \
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 ']': \
334         case '{': \
335         case '}' /* no colon */
336         META_CHARS:
337             len++;
338             /* fall through */
339         default:
340             len++;
341             break;
342         }
343     }
344
345     /* Allocate some space */
346
347     buf = alloc(len+1);         /* trailing null */
348
349     /* Copy it across */
350
351     po = buf;
352
353     for (pi = str; *pi; pi++) {
354         switch (*pi) {
355         META_CHARS:
356 #undef META_CHARS
357             *po++ = '\\';
358             /* fall through */
359         default:
360             *po++ = *pi;
361             break;
362         }
363     }
364
365     *po = '\0';
366
367     assert(po - buf == len);    /* Just checking! */
368
369     return buf;
370 }
371 #endif
372
373 /* Table lookup.
374 */
375 int table_lookup(table, str)
376 table_t *table;
377 char *str;
378 {
379         while(table->word != (char *)0) {
380                 if (*table->word == *str &&
381                     strcmp(table->word, str) == 0) return table->value;
382                 table++;
383         }
384
385         return table->value;
386 }
387
388 /* Reverse table lookup.
389 */
390 char *table_lookup_r(table, val)
391 table_t *table;
392 int val;
393 {
394         while(table->word != (char *)0) {
395                 if (table->value == val) return table->word;
396                 table++;
397         }
398
399         return (char *)0;
400 }
401
402 #ifdef TEST
403
404 int main()
405 {
406         char *str = NULL;
407         char *t[20];
408         int r;
409         char *sr;
410         int i;
411
412         safe_fd(-1, 0);
413
414         set_pname("token test");
415
416         /* Don't die when child closes pipe */
417         signal(SIGPIPE, SIG_IGN);
418
419         erroutput_type = ERR_INTERACTIVE;
420
421         printf("Testing split() with \" \" token separator\n");
422         while(1) {
423                 printf("Input string: ");
424                 amfree(str);
425                 if ((str = agets(stdin)) == NULL) {
426                         printf("\n");
427                         break;
428                 }
429                 r = split(str, t, 20, " ");
430                 printf("%d token%s:\n", r, (r == 1) ? "" : "s");
431                 for (i=0; i <= r; i++) printf("tok[%d] = \"%s\"\n", i, t[i]);
432         }
433         amfree(str);
434         printf("\n");
435
436         printf("Testing quote()\n");
437         while(1) {
438                 printf("Input string: ");
439                 amfree(str);
440                 if ((str = agets(stdin)) == NULL) {
441                         printf("\n");
442                         break;
443                 }
444                 sr = squote(str);
445                 printf("Quoted   = \"%s\"\n", sr);
446                 strncpy(str,sr,sizeof(str)-1);
447                 str[sizeof(str)-1] = '\0';
448                 r = split(str, t, 20, " ");
449                 if (r != 1) printf("split()=%d!\n", r);
450                 printf("Unquoted = \"%s\"\n", t[1]);
451                 amfree(sr);
452         }
453         amfree(str);
454         return 0;
455 }
456
457 #endif