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