1 #include "tokenize.h"
2 
3 #include <stdlib.h>
4 #include <string.h>
5 
6 #define LOG_MODULE "tokenize"
7 #define LOG_ENABLE_DBG 0
8 #include "log.h"
9 
10 static bool
push_argv(char *** argv,size_t * size,char * arg,size_t * argc)11 push_argv(char ***argv, size_t *size, char *arg, size_t *argc)
12 {
13     if (arg != NULL && arg[0] == '%')
14         return true;
15 
16     if (*argc >= *size) {
17         size_t new_size = *size > 0 ? 2 * *size : 10;
18         char **new_argv = realloc(*argv, new_size * sizeof(new_argv[0]));
19 
20         if (new_argv == NULL)
21             return false;
22 
23         *argv = new_argv;
24         *size = new_size;
25     }
26 
27     (*argv)[(*argc)++] = arg;
28     return true;
29 }
30 
31 bool
tokenize_cmdline(char * cmdline,char *** argv)32 tokenize_cmdline(char *cmdline, char ***argv)
33 {
34     *argv = NULL;
35     size_t argv_size = 0;
36 
37     bool first_token_is_quoted = cmdline[0] == '"' || cmdline[0] == '\'';
38     char delim = first_token_is_quoted ? cmdline[0] : ' ';
39 
40     char *p = first_token_is_quoted ? &cmdline[1] : &cmdline[0];
41     char *search_start = p;
42 
43     size_t idx = 0;
44     while (*p != '\0') {
45         char *end = strchr(search_start, delim);
46         if (end == NULL) {
47             if (delim != ' ') {
48                 LOG_ERR("unterminated %s quote", delim == '"' ? "double" : "single");
49                 free(*argv);
50                 *argv = NULL;
51                 return false;
52             }
53 
54             if (!push_argv(argv, &argv_size, p, &idx) ||
55                 !push_argv(argv, &argv_size, NULL, &idx))
56             {
57                 goto err;
58             } else
59                 return true;
60         }
61 
62         if (end > p && *(end - 1) == '\\') {
63             /* Escaped quote, remove one level of escaping and
64              * continue searching for "our" closing quote */
65             memmove(end - 1, end, strlen(end));
66             end[strlen(end) - 1] = '\0';
67             search_start = end;
68             continue;
69         }
70 
71         *end = '\0';
72 
73         if (!push_argv(argv, &argv_size, p, &idx))
74             goto err;
75 
76         p = end + 1;
77         while (*p == delim)
78             p++;
79 
80         while (*p == ' ')
81             p++;
82 
83         if (*p == '"' || *p == '\'') {
84             delim = *p;
85             p++;
86         } else
87             delim = ' ';
88         search_start = p;
89     }
90 
91     if (!push_argv(argv, &argv_size, NULL, &idx))
92         goto err;
93 
94     return true;
95 
96 err:
97     free(*argv);
98     return false;
99 }
100