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