]> git.tuebingen.mpg.de Git - lopsub.git/blob - config_file.l
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