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