varnish-cache/lib/libvarnish/vav.c
0
/*-
1
 * Copyright (c) 2006 Verdens Gang AS
2
 * Copyright (c) 2006-2011 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
6
 *
7
 * SPDX-License-Identifier: BSD-2-Clause
8
 *
9
 * Redistribution and use in source and binary forms, with or without
10
 * modification, are permitted provided that the following conditions
11
 * are met:
12
 * 1. Redistributions of source code must retain the above copyright
13
 *    notice, this list of conditions and the following disclaimer.
14
 * 2. Redistributions in binary form must reproduce the above copyright
15
 *    notice, this list of conditions and the following disclaimer in the
16
 *    documentation and/or other materials provided with the distribution.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
22
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28
 * SUCH DAMAGE.
29
 *
30
 * const char **VAV_Parse(const char *s, int *argc, int flag)
31
 *      Parse a command like line into an argv[]
32
 *      Index zero contains NULL or an error message
33
 *      (The error message is a static const char* and does not
34
 *      need saving or copying.)
35
 *      "double quotes" and backslash substitution is handled.
36
 *
37
 * void VAV_Free(const char **argv)
38
 *      Free the result of VAV_Parse()
39
 *
40
 */
41
42
#include "config.h"
43
44
#include <ctype.h>
45
#include <stdint.h>
46
#include <stdio.h>
47
#include <stdlib.h>
48
#include <string.h>
49
50
#include "vdef.h"
51
52
#include "vas.h"
53
#include "vav.h"
54
#include "vnum.h"
55
56
static int
57 114002
vav_backslash_txt(const char *s, const char *e, char *res)
58
{
59
        int r, l, i;
60
        const char *p;
61
        char c;
62
63 114002
        AN(s);
64 114002
        if (e == NULL)
65 110562
                e = strchr(s, '\0');
66
67 114002
        l = pdiff(s, e);
68 114002
        if (l < 2)
69 40
                return (0);
70
71 113962
        assert(*s == '\\');
72 113962
        r = c = 0;
73 113962
        switch (s[1]) {
74
        case 'n':
75 53861
                c = '\n';
76 53861
                r = 2;
77 53861
                break;
78
        case 'r':
79 41941
                c = '\r';
80 41941
                r = 2;
81 41941
                break;
82
        case 't':
83 920
                c = '\t';
84 920
                r = 2;
85 920
                break;
86
        case '"':
87 5600
                c = '"';
88 5600
                r = 2;
89 5600
                break;
90
        case '\\':
91 1280
                c = '\\';
92 1280
                r = 2;
93 1280
                break;
94
        case '0': case '1': case '2': case '3':
95
        case '4': case '5': case '6': case '7':
96 720
                for (r = 1; r < 4 && r < l; r++) {
97 560
                        if (!isdigit(s[r]))
98 40
                                break;
99 520
                        if (s[r] - '0' > 7)
100 0
                                break;
101 520
                        c <<= 3;        /*lint !e701 signed left shift */
102 520
                        c |= s[r] - '0';
103 520
                }
104 200
                break;
105
        case 'x':
106 9520
                if (l >= 4 && (i = VNUM_hex(s + 2, s + 4, &p)) >= 0 &&
107 9440
                    p == s + 4) {
108 9400
                        AZ(i & ~0xff);
109 9400
                        c = i;  /*lint !e734 loss of precision */
110 9400
                        r = 4;
111 9400
                }
112 9520
                break;
113
        default:
114 640
                break;
115
        }
116 113962
        if (res != NULL)
117 112202
                *res = c;
118 113962
        return (r);
119 114002
}
120
121
int
122 110562
VAV_BackSlash(const char *s, char *res)
123
{
124
125 110562
        return (vav_backslash_txt(s, NULL, res));
126
}
127
128
char *
129 18801913
VAV_BackSlashDecode(const char *s, const char *e)
130
{
131
        const char *q;
132
        char *p, *r;
133
        int i;
134
135 18801913
        if (e == NULL)
136 0
                e = strchr(s, '\0');
137 18801913
        assert(e != NULL);
138 18801913
        p = calloc(1, (e - s) + 1L);
139 18801913
        if (p == NULL)
140 0
                return (p);
141 170893892
        for (r = p, q = s; q < e; ) {
142 152091979
                if (*q != '\\') {
143 152090339
                        *r++ = *q++;
144 152090339
                        continue;
145
                }
146 1640
                i = vav_backslash_txt(q, e, r);
147 1640
                if (i == 0) {
148 0
                        free(p);
149 0
                        errno = EINVAL;
150 0
                        return (NULL);
151
                }
152 1640
                q += i;
153 1640
                r++;
154
        }
155 18801913
        *r = '\0';
156 18801913
        return (p);
157 18801913
}
158
159
static char err_invalid_backslash[] = "Invalid backslash sequence";
160
static char err_invalid_quote[] = "Invalid '\"'";
161
static char err_missing_quote[] = "Missing '\"'";
162
static char err_missing_separator[] = "Missing separator between arguments";
163
164
char **
165 4982480
VAV_ParseTxt(const char *b, const char *e, int *argc, int flag)
166
{
167
        char **argv;
168
        const char *p, *sep;
169
        int nargv, largv;
170
        int i, quote;
171
172 4982480
        AN(b);
173 4982480
        if (e == NULL)
174 4512913
                e = strchr(b, '\0');
175 4982480
        sep = NULL;
176 4982480
        quote = 0;
177 4982480
        nargv = 1;
178 4982480
        largv = 16;
179 4982480
        argv = calloc(largv, sizeof *argv);
180 4982480
        if (argv == NULL)
181 0
                return (NULL);
182
183 27020920
        while (b < e) {
184 22038960
                if (isspace(*b)) {
185 3230606
                        b++;
186 3230606
                        continue;
187
                }
188 18808394
                if (sep != NULL && isspace(*sep) &&
189 12683796
                    *b == ',' && (flag & ARGV_COMMA)) {
190 40
                        sep = NULL;
191 40
                        b++;
192 40
                        continue;
193
                }
194 18808354
                if (sep != NULL && *sep == '"' &&
195 38640
                    *b == ',' && (flag & ARGV_COMMA)) {
196 40
                        sep = NULL;
197 40
                        b++;
198 40
                        continue;
199
                }
200 18808274
                if (sep != NULL && *sep == '"' && *b == '"' && (b - sep) < 2) {
201 40
                        argv[0] = err_missing_separator;
202 40
                        return (argv);
203
                }
204 18808234
                sep = NULL;
205 18808234
                if ((flag & ARGV_COMMENT) && *b == '#')
206 40
                        break;
207 18808194
                if (*b == '"' && !(flag & ARGV_NOESC)) {
208 76400
                        p = ++b;
209 76400
                        quote = 1;
210 76400
                } else {
211 18731794
                        p = b;
212 18731794
                        quote = 0;
213
                }
214 170931923
                while (b < e) {
215 166411353
                        if (*b == '\\' && !(flag & ARGV_NOESC)) {
216 1800
                                i = vav_backslash_txt(b, e, NULL);
217 1800
                                if (i == 0) {
218 160
                                        argv[0] = err_invalid_backslash;
219 160
                                        return (argv);
220
                                }
221 1640
                                b += i;
222 1640
                                continue;
223
                        }
224 166409553
                        if (!quote) {
225 166001555
                                if (isspace(*b)) {
226 13107624
                                        sep = b;
227 13107624
                                        break;
228
                                }
229 152893931
                                if ((flag & ARGV_COMMA) && *b == ',') {
230 1103560
                                        sep = b;
231 1103560
                                        break;
232
                                }
233 151790371
                                if (!(flag & ARGV_NOESC) && *b == '"') {
234 80
                                        argv[0] = err_invalid_quote;
235 80
                                        return (argv);
236
                                }
237 151790291
                                b++;
238 151790291
                                continue;
239
                        }
240 407998
                        if (*b == '"' && !(flag & ARGV_NOESC)) {
241 76200
                                sep = b;
242 76200
                                quote = 0;
243 76200
                                break;
244
                        }
245 331798
                        b++;
246
                }
247 18807954
                if (sep == NULL && quote) {
248 200
                        argv[0] = err_missing_quote;
249 200
                        return (argv);
250
                }
251
                /*
252
                 * Ensure slots for 1 new arg plus 1 trailing arg,
253
                 * vcli_serve.c depends on this extra slot.
254
                 */
255 18807754
                if (nargv + 2 >= largv) {
256 40840
                        argv = realloc(argv, sizeof (*argv) * (largv += largv));
257 40840
                        assert(argv != NULL);
258 40840
                }
259 18807754
                if (flag & ARGV_NOESC) {
260 5840
                        argv[nargv] = malloc(1L + (b - p));
261 5840
                        assert(argv[nargv] != NULL);
262 5840
                        memcpy(argv[nargv], p, b - p);
263 5840
                        argv[nargv][b - p] = '\0';
264 5840
                } else {
265 18801914
                        argv[nargv] = VAV_BackSlashDecode(p, b);
266 18801914
                        assert(argv[nargv] != NULL);
267
                }
268 18807754
                nargv++;
269 18807754
                if (b < e)
270 14287384
                        b++;
271
        }
272 4982000
        if (sep != NULL && *sep == ',') {
273 40
                argv[nargv] = strdup("");
274 40
                assert(argv[nargv] != NULL);
275 40
                nargv++;
276 40
        }
277 4982000
        argv[nargv] = NULL;
278 4982000
        if (argc != NULL)
279 4703319
                *argc = nargv;
280 4982000
        return (argv);
281 4982480
}
282
283
char **
284 4512913
VAV_Parse(const char *s, int *argc, int flag)
285
{
286
287 4512913
        return (VAV_ParseTxt(s, NULL, argc, flag));
288
}
289
290
void
291 4832007
VAV_Free(char **argv)
292
{
293
        int i;
294
295 23195450
        for (i = 1; argv[i] != NULL; i++)
296 18363443
                free(argv[i]);
297 4832007
        free(argv);
298 4832007
}
299
300
#ifdef TESTPROG
301
302
#include <printf.h>
303
304
static void
305
VAV_Print(char **argv)
306
{
307
        int i;
308
309
        printf("---- %p\n", argv);
310
        if (argv[0] != NULL)
311
                printf("err %V\n", argv[0]);
312
        for (i = 1; argv[i] != NULL; i++)
313
                printf("%3d %V\n", i, argv[i]);
314
}
315
316
static void
317
Test(const char *str)
318
{
319
        char **av;
320
321
        printf("Test: <%V>\n", str);
322
        av = VAV_Parse(str, NULL, 0);
323
        VAV_Print(av);
324
}
325
326
#if defined __linux__
327
int
328
printf_v(FILE *stream, const struct printf_info *info,
329
    const void *const *args)
330
{
331
        const char *v = *((char **)args[0]);
332
        return (fprintf(stream, "%*s",
333
            info->left ? -info->width : info->width, v));
334
}
335
336
int
337
printf_v_info(const struct printf_info *info, size_t n, int *argtypes,
338
    int *size)
339
{
340
        if (n > 0)
341
                argtypes[0] = PA_STRING;
342
        return (1);
343
}
344
#endif
345
346
int
347
main(int argc, char **argv)
348
{
349
        char buf[BUFSIZ];
350
351
        (void)argc;
352
        (void)argv;
353
354
#if defined __FreeBSD__
355
        register_printf_render_std("V");
356
#elif defined __linux__
357
        register_printf_specifier('V', printf_v, printf_v_info);
358
#else
359
#error Unsupported platform
360
#endif
361
362
        while (fgets(buf, sizeof buf, stdin))
363
                Test(buf);
364
365
        return (0);
366
}
367
#endif
368
369
#ifdef TEST_DRIVER
370
#  include <stdio.h>
371
372
struct test_case {
373
        int             flag;
374
        const char      *str;
375
        const char      **argv;
376
};
377
378
static const struct test_case *tests[] = {
379
#define TEST_PASS(flag, str, ...)                                       \
380
        &(const struct test_case){flag, str,                            \
381
            (const char **)&(const void *[]){NULL, __VA_ARGS__, NULL}}
382
#define TEST_FAIL(flag, str, err)                                       \
383
        &(const struct test_case){flag, str,                            \
384
            (const char **)&(const void *[]){err_ ## err, NULL}}
385
#define K ARGV_COMMENT
386
#define C ARGV_COMMA
387
#define N ARGV_NOESC
388
        TEST_PASS(K|C|N, "", NULL),
389
        TEST_PASS(0    , "foo", "foo"),
390
        TEST_PASS(0    , "foo bar", "foo", "bar"),
391
        TEST_PASS(  C  , "foo bar", "foo", "bar"),
392
        TEST_PASS(  C  , "foo,bar", "foo", "bar"),
393
        TEST_PASS(0    , "  foo  bar  ", "foo", "bar"),
394
        TEST_PASS(  C  , "  foo  ,  bar  ", "foo", "bar"),
395
        TEST_PASS(  C  , "foo bar ", "foo", "bar"),
396
        TEST_PASS(  C  , "foo,bar,", "foo", "bar", ""),
397
        TEST_PASS(0    , "foo \"bar baz\"", "foo", "bar baz"),
398
        TEST_PASS(0    , "foo #bar", "foo", "#bar"),
399
        TEST_PASS(K    , "foo #bar", "foo"),
400
        TEST_PASS(0    , "foo#bar", "foo#bar"),
401
        TEST_PASS(K    , "foo#bar", "foo#bar"),
402
        TEST_PASS(    N, "\\", "\\"),
403
        TEST_FAIL(0    , "\\", invalid_backslash),
404
        TEST_FAIL(0    , "\\x", invalid_backslash),
405
        TEST_FAIL(0    , "\\x2", invalid_backslash),
406
        TEST_FAIL(0    , "\\x2O", invalid_backslash),
407
        TEST_PASS(0    , "\\x20", " "),
408
        TEST_FAIL(0    , "\"foo", missing_quote),
409
        TEST_PASS(    N, "foo\"bar", "foo\"bar"),
410
        TEST_FAIL(0    , "foo\"bar", invalid_quote),
411
        TEST_FAIL(0    , "foo\"bar", invalid_quote),
412
        TEST_PASS(0    , "\"foo\" \"bar\"", "foo", "bar"),
413
        TEST_PASS(  C  , "\"foo\",\"bar\"", "foo", "bar"),
414
        TEST_PASS(    N, "\"foo\"\"bar\"", "\"foo\"\"bar\""),
415
        TEST_FAIL(0    , "\"foo\"\"bar\"", missing_separator),
416
        NULL
417
#undef N
418
#undef C
419
#undef K
420
#undef TEST_FAIL
421
#undef TEST_PASS
422
};
423
424
static char **
425 1120
test_run(const struct test_case *tc, int *ret)
426
{
427
        const char *exp, *act;
428
        char **argv, *tmp;
429
        int argc, i;
430
431 1120
        i = strlen(tc->str);
432 1120
        if (i == 0) {
433 40
                argv = VAV_Parse(tc->str, &argc, tc->flag);
434 40
        } else {
435 1080
                tmp = malloc(i); /* sanitizer-friendly */
436 1080
                AN(tmp);
437 1080
                memcpy(tmp, tc->str, i);
438 1080
                argv = VAV_ParseTxt(tmp, tmp + i, &argc, tc->flag);
439 1080
                free(tmp);
440
        }
441 1120
        AN(argv);
442
443 1120
        if (tc->argv[0] != argv[0]) {
444 0
                exp = tc->argv[0] != NULL ? tc->argv[0] : "success";
445 0
                act = argv[0] != NULL ? argv[0] : "success";
446 0
                printf(
447
                    "ERROR: Parsing string <%s> with flags %x, "
448
                    "expected <%s> got <%s>.\n",
449 0
                    tc->str, tc->flag, exp, act);
450 0
                *ret = 1;
451 0
                return (argv);
452
        }
453
454 2360
        for (i = 1; i < argc && tc->argv[i] != NULL && argv[i] != NULL; i++) {
455 1240
                if (!strcmp(tc->argv[i], argv[i]))
456 1240
                        continue;
457 0
                printf(
458
                    "ERROR: Parsing string <%s> with flags %x, "
459
                    "expected <%s> for argv[%d] got <%s>.\n",
460 0
                    tc->str, tc->flag, tc->argv[i], i, argv[i]);
461 0
                *ret = 1;
462 0
                return (argv);
463
        }
464
465 1120
        if (tc->argv[0] == NULL && (tc->argv[i] != NULL || argv[i] != NULL)) {
466 0
                act = i < argc ? "less" : "more";
467 0
                printf(
468
                    "ERROR: Parsing string <%s> with flags %x, "
469
                    "got %s arguments (%d) than expected.\n",
470 0
                    tc->str, tc->flag, act, argc);
471 0
                *ret = 1;
472 0
                return (argv);
473
        }
474
475 1120
        exp = tc->argv[0] == NULL ? "PASS" : "FAIL";
476 1120
        printf("%s: <%s> with flags %x as expected.\n", exp, tc->str, tc->flag);
477 1120
        return (argv);
478 1120
}
479
480
int
481 40
main(int argc, char **argv)
482
{
483
        const struct test_case **tc;
484 40
        int ret = 0, fail = 0;
485
486 40
        (void)argc;
487 40
        (void)argv;
488
489 1160
        for (tc = tests; *tc != NULL; ret = 0, tc++) {
490 1120
                argv = test_run(*tc, &ret);
491 1120
                VAV_Free(argv);
492 1120
                if (ret)
493 0
                        fail = 1;
494 1120
        }
495 40
        return (fail);
496
}
497
#endif /* TEST_DRIVER */