build: Activate ONESHELL.
[lopsub.git] / config_file.l
1 /*
2 * Copyright (C) 2016 Andre Noll <maan@tuebingen.mpg.de>
3 *
4 * Licensed under the LGPL v3, see http://www.gnu.org/licenses/lgpl-3.0.html
5 */
6
7 /* We don't want symbols to clash with those of other flex users. */
8 %option prefix="lls_yy"
9
10 /* Generate a scanner which is safe to use in multithreaded programs. */
11 %option reentrant
12
13 /* To maintain state in a reentrant way. */
14 %option extra-type="struct lls_yy_config_file_extra *"
15
16 %option stack
17 %option never-interactive
18 %option yylineno
19
20 %x SC_ARG
21 %s SC_SCANNING
22
23 IDENTIFIER [a-zA-Z]+[a-zA-Z0-9_-]*
24 EQUALS [[:space:]]*=[[:space:]]*
25 OPTION [a-zA-Z]+[a-zA-Z0-9_-]*
26
27 %{
28 #include <stdlib.h>
29 #include <ctype.h>
30 #include <assert.h>
31 #include <stdbool.h>
32 #include <inttypes.h>
33 #include "lopsub-internal.h"
34 #include "lopsub.h"
35
36 struct lls_yy_config_file_extra {
37 int argc;
38 char **argv;
39 const char *subcmd;
40 };
41
42 static int add_option(yyscan_t yyscanner);
43 static int parse_arg(char **result, yyscan_t yyscanner);
44 static int yywrap(yyscan_t yyscanner) {return 1;}
45 %}
46
47 %%
48
49 /* skip comments and whitespace */
50 ^[[:space:]]*#.*\n ;
51 [[:space:]]|\n+ ;
52
53 <INITIAL,SC_SCANNING>\[[[:space:]]*{IDENTIFIER}[[:space:]]*\][[:space:]]*\n {
54 int i, j;
55 const char *subcmd = yyget_extra(yyscanner)->subcmd;
56
57 assert(yytext[0] == '[');
58 if (!subcmd)
59 return 0;
60 for (i = 1; i < yyleng; i++)
61 if (!isspace(yytext[i]))
62 break;
63 for (j = i; j < yyleng; j++)
64 if (yytext[j] == ']' || isspace(yytext[j]))
65 break;
66 assert(j < yyleng);
67 yytext[j] = '\0';
68 if (strcmp(yytext + i, subcmd))
69 BEGIN(INITIAL);
70 else
71 BEGIN(SC_SCANNING);
72 }
73
74 <SC_SCANNING>{OPTION}[[:space:]]*\n add_option(yyscanner);
75
76 <SC_SCANNING>{OPTION}({EQUALS}|[[:space:]]+) {
77 int ret = add_option(yyscanner);
78
79 if (ret < 0)
80 return ret;
81 BEGIN(SC_ARG);
82 }
83
84 <SC_ARG>.*\n {
85 struct lls_yy_config_file_extra *extra = yyget_extra(yyscanner);
86 const char *opt = extra->argv[extra->argc - 1];
87 char *arg, *result;
88 size_t opt_len = strlen(opt), arg_len;
89 int ret = parse_arg(&arg, yyscanner);
90
91 if (ret < 0)
92 return ret;
93 arg_len = ret;
94 result = malloc(opt_len + arg_len + 2);
95 if (!result)
96 return -E_LLS_NOMEM;
97 strcpy(result, opt);
98 result[opt_len] = '=';
99 strcpy(result + opt_len + 1, arg);
100 free(arg);
101 result[opt_len + arg_len + 1] = '\0';
102 free(extra->argv[extra->argc - 1]);
103 extra->argv[extra->argc - 1] = result;
104 BEGIN(SC_SCANNING);
105 }
106
107 <INITIAL>.*\n {}
108
109 /* This rule runs iff none of the above patterns matched */
110 . {return -1;}
111 %%
112
113 #include <sys/types.h>
114 #include <sys/stat.h>
115 #include <fcntl.h>
116 #include <stdio.h>
117
118 static int expand_result(yyscan_t yyscanner)
119 {
120 struct lls_yy_config_file_extra *extra = yyget_extra(yyscanner);
121 int nargc = extra->argc + 1;
122 char **nrargv = realloc(extra->argv, (nargc + 1) * sizeof(char *));
123
124 if (!nrargv)
125 return -E_LLS_NOMEM;
126 extra->argc = nargc;
127 extra->argv = nrargv;
128 extra->argv[extra->argc] = NULL;
129 return 1;
130 }
131
132 static int add_option(yyscan_t yyscanner)
133 {
134 struct lls_yy_config_file_extra *extra = yyget_extra(yyscanner);
135 int ret;
136 unsigned n;
137 int len = yyget_leng(yyscanner);
138 char *narg, *text = yyget_text(yyscanner);
139
140 for (n = 0; n < len; n++)
141 if (!isalnum(text[n]) && !(text[n] == '-'))
142 break;
143 assert(n > 0);
144 ret = expand_result(yyscanner);
145 if (ret < 0)
146 return ret;
147 narg = malloc(n + 2 + 1);
148 if (!narg)
149 return -E_LLS_NOMEM;
150 narg[0] = narg[1] = '-';
151 memcpy(narg + 2, text, n);
152 narg[n + 2] = '\0';
153 extra->argv[extra->argc - 1] = narg;
154 return 1;
155 }
156
157 static int parse_arg(char **result, yyscan_t yyscanner)
158 {
159 bool backslash = false, quote = false;
160 const char *in;
161 char *out;
162 int ret;
163 int len = yyget_leng(yyscanner);
164 char *text = yyget_text(yyscanner);
165
166 *result = malloc(len + 1);
167 if (!*result)
168 return -E_LLS_NOMEM;
169 for (in = text, out = *result; *in; in++) {
170 if (*in == '\\') {
171 if (!backslash) {
172 backslash = true;
173 continue;
174 }
175 } else if (*in == 'n' || *in == 't') {
176 if (backslash) { /* \n or \t */
177 *out++ = (*in == 'n')? '\n' : '\t';
178 backslash = false;
179 continue;
180 }
181 } else if (*in == '"') {
182 if (!backslash) {
183 quote = !quote;
184 continue;
185 }
186 } else if (isspace(*in)) {
187 if (!backslash && !quote)
188 break;
189 }
190 /* copy the character */
191 *out++ = *in;
192 backslash = false;
193 }
194 ret = -E_LLS_TRAILING_BACKSLASH;
195 if (backslash)
196 goto fail;
197 ret = -E_LLS_UNMATCHED_QUOTE;
198 if (quote)
199 goto fail;
200 /* look at first non-space character */
201 for (; *in; in++) {
202 if (isspace(*in))
203 continue;
204 if (*in == '#')
205 break;
206 ret = -E_LLS_TRAILING_GARBAGE;
207 goto fail;
208 }
209 /* success */
210 *out = '\0';
211 return out - *result;
212 fail:
213 assert(ret < 0);
214 free(*result);
215 *result = NULL;
216 return ret;
217 }
218
219 void lls_free_argv(char **argv)
220 {
221 int i;
222
223 if (!argv)
224 return;
225 for (i = 0; argv[i]; i++)
226 free(argv[i]);
227 free(argv);
228 }
229
230 int lls_convert_config(const char *buf, size_t nbytes, const char *subcmd,
231 char ***result, char **errctx)
232 {
233 int ret;
234 YY_BUFFER_STATE yybs;
235 yyscan_t yyscanner;
236 struct lls_yy_config_file_extra extra;
237
238 *result = NULL;
239 if (errctx)
240 *errctx = NULL;
241
242 extra.argc = 1;
243 extra.argv = malloc((extra.argc + 1) * sizeof(char *));
244 if (!extra.argv)
245 return -E_LLS_NOMEM;
246 extra.argv[0] = strdup(__FUNCTION__);
247 if (!extra.argv[0]) {
248 free(extra.argv);
249 return -E_LLS_NOMEM;
250 }
251 extra.argv[1] = NULL;
252 extra.subcmd = subcmd;
253
254 ret = yylex_init_extra(&extra, &yyscanner);
255 if (ret != 0) {
256 ret = -E_LLS_NOMEM;
257 goto free_argv;
258 }
259 yy_push_state(subcmd? INITIAL : SC_SCANNING, yyscanner);
260 yybs = yy_scan_bytes(buf, nbytes, yyscanner);
261 if (!yybs) {
262 ret = -E_LLS_YY_SCAN;
263 goto destroy;
264 }
265 ret = yylex(yyscanner);
266 if (ret < 0) {
267 ret = -E_LLS_YY_LEX;
268 if (errctx) {
269 *errctx = malloc(100);
270 if (*errctx)
271 sprintf(*errctx, "error at line %d",
272 yyget_lineno(yyscanner));
273 }
274 }
275 yy_delete_buffer(yybs, yyscanner);
276 destroy:
277 yylex_destroy(yyscanner);
278 free_argv:
279 if (ret < 0)
280 lls_free_argv(extra.argv);
281 else {
282 *result = extra.argv;
283 ret = extra.argc;
284 }
285 return ret;
286 }
287
288 #if 0
289 int main(void)
290 {
291 char buf[100 * 1024];
292 int ret, len, i, argc;
293 char **argv;
294
295 ret = read(STDIN_FILENO, buf, sizeof(buf));
296 if (ret <= 0)
297 exit(EXIT_FAILURE);
298 len = ret;
299 ret = lls_convert_config(buf, len, NULL, &argv, NULL);
300 if (ret < 0)
301 exit(EXIT_FAILURE);
302 argc = ret;
303 for (i = 0; i < argc; i++)
304 printf("argv[%d]: %s\n", i, rargv[i]);
305 return EXIT_SUCCESS;
306 }
307 #endif