Avoid warning about yywrap being redefined.
[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