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