parser.c

changeset 0
86b7f6f9c5c0
child 1
6e704fc09528
equal deleted inserted replaced
-1:000000000000 0:86b7f6f9c5c0
1 /*
2 * libtu/parser.c
3 *
4 * Copyright (c) Tuomo Valkonen 1999-2000.
5 *
6 * This file is distributed under the terms of the "Artistic License".
7 * See the included file LICENSE for details.
8 */
9
10 #include <string.h>
11
12 #include "include/parser.h"
13 #include "include/misc.h"
14 #include "include/output.h"
15
16 #define MAX_TOKENS 32
17 #define MAX_NEST 16
18
19
20 enum{
21 P_EOF,
22 P_OK,
23 P_ERROR,
24 P_BEG_SECT,
25 P_END_SECT
26 };
27
28
29 static bool check_args(const Tokenizer *tokz, Token *tokens, int ntokens,
30 const char *fmt);
31
32
33 /* */
34
35
36 static int read_statement(const ConfOpt **opt_ret, Token *tokens,
37 int *ntok_ret, Tokenizer *tokz,
38 const ConfOpt *options)
39 {
40 int ntokens=0;
41 Token *tok=NULL;
42 const ConfOpt *opt=NULL;
43 int had_comma=0;
44 int retval=P_OK;
45 int e=0;
46 int e_line=0;
47
48 while(1){
49 if(ntokens==MAX_TOKENS-1){
50 e=E_TOKZ_TOKEN_LIMIT;
51 goto ret_err;
52 }
53
54 tok=&tokens[ntokens];
55
56 if(!tokz_get_token(tokz, tok))
57 goto ret_err;
58
59 ntokens++;
60
61 if(!TOK_IS_OP(tok)){
62 if(ntokens==1 && !had_comma){
63 if(!TOK_IS_IDENT(tok)){
64 e=E_TOKZ_IDENTIFIER_EXPECTED;
65 goto ret_err;
66 }
67
68 /* find the option */
69 for(opt=options; opt->optname; opt++){
70 if(strcmp(opt->optname, TOK_IDENT_VAL(tok))==0)
71 break;
72 }
73
74 if(!opt->optname){
75 e=E_TOKZ_UNKNOWN_OPTION;
76 e_line=tok->line;
77 retval=P_ERROR;
78 }
79
80 had_comma=2;
81 }else{
82 if(!had_comma){
83 e=E_TOKZ_SYNTAX;
84 goto ret_err;
85 }
86
87 had_comma=0;
88 }
89 continue;
90 }
91
92 /* It is an operator */
93
94 switch(TOK_OP_VAL(tok)){
95 case OP_SCOLON:
96 if(opt){
97 if(had_comma || opt->opts){
98 e=E_TOKZ_SYNTAX;
99 goto ret_err;
100 }
101 goto ret_success;
102 }
103 break;
104
105 case OP_NEXTLINE:
106 if(had_comma==1){
107 e=E_TOKZ_SYNTAX;
108 e_line=tok->line-1;
109 goto ret_err2;
110 }
111
112 if(opt && !opt->opts)
113 goto ret_success;
114 break;
115
116 case OP_EOF:
117 if(had_comma==1){
118 e=E_TOKZ_UNEXPECTED_EOF;
119 goto ret_err;
120 }
121
122 if(opt && opt->opts){
123 e=E_TOKZ_UNEXPECTED_EOF;
124 goto ret_err;
125 }
126
127 retval=P_EOF;
128 goto ret_success;
129
130 case OP_R_BRC:
131 if(had_comma==1){
132 e=E_TOKZ_SYNTAX;
133 goto ret_err;
134 }
135
136 if(opt && opt->opts){
137 e=E_TOKZ_SYNTAX;
138 goto ret_err;
139 }
140
141 retval=P_END_SECT;
142 goto ret_success;
143
144 case OP_L_BRC:
145 if(had_comma==1 || !opt || !opt->opts){
146 e=E_TOKZ_SYNTAX;
147 goto ret_err;
148 }
149
150 retval=P_BEG_SECT;
151 goto ret_success;
152
153 case OP_COMMA:
154 if(had_comma){
155 e=E_TOKZ_SYNTAX;
156 goto ret_err;
157 }
158 had_comma=1;
159 break;
160
161 default:
162 e=E_TOKZ_SYNTAX;
163 goto ret_err;
164 }
165
166 ntokens--;
167 }
168
169 ret_err:
170 e_line=tok->line;
171 ret_err2:
172 retval=P_ERROR;
173
174 ret_success:
175 if(retval==P_ERROR && e!=0)
176 tokz_warn_error(tokz, e_line, e);
177
178 *opt_ret=opt;
179 *ntok_ret=ntokens;
180
181 return retval;
182 }
183
184
185 /* */
186
187
188 static bool call_end_sect(Tokenizer *tokz, const ConfOpt *options)
189 {
190 bool retval=TRUE;
191
192 while(options->optname){
193 if(strcmp(options->optname, "#end")==0){
194 retval=options->fn(tokz, 0, NULL);
195 break;
196 }
197 options++;
198 }
199
200 return retval;
201 }
202
203
204 static void call_cancel_sect(Tokenizer *tokz, const ConfOpt *options)
205 {
206 while(options->optname){
207 if(strcmp(options->optname, "#cancel")==0){
208 options->fn(tokz, 0, NULL);
209 break;
210 }
211 options++;
212 }
213 }
214
215
216 /* */
217
218
219 /* Does the parsing work
220 */
221 bool parse_config_tokz(Tokenizer *tokz, const ConfOpt *options)
222 {
223 Token tokens[MAX_TOKENS];
224 bool alloced_optstack=FALSE;
225 int i, t, ntokens;
226 int init_nest_lvl;
227 bool had_error=FALSE;
228
229 /* Allocate tokz->optstack if it does not yet exist (if it does,
230 * we have been called from an option handler)
231 */
232 if(!tokz->optstack){
233 tokz->optstack=ALLOC_N(const ConfOpt*, MAX_NEST);
234 if(!tokz->optstack){
235 warn_err();
236 return FALSE;
237 }
238
239 memset(tokz->optstack, 0, sizeof(ConfOpt*)*MAX_NEST);
240 init_nest_lvl=tokz->nest_lvl=0;
241 alloced_optstack=TRUE;
242 }else{
243 init_nest_lvl=tokz->nest_lvl;
244 }
245
246 tokz->optstack[init_nest_lvl]=options;
247
248 for(i=0;i<MAX_TOKENS;i++)
249 tok_init(&tokens[i]);
250
251
252 /* The loop
253 */
254 while(1){
255 t=read_statement(&options, tokens, &ntokens,
256 tokz, tokz->optstack[tokz->nest_lvl]);
257
258 had_error=(t==P_ERROR);
259
260 /* Check that arguments are ok */
261 if(!had_error && options)
262 had_error=!check_args(tokz, tokens, ntokens, options->argfmt);
263
264 if(tokz->flags&TOKZ_PARSER_INDENT_MODE)
265 verbose_indent(tokz->nest_lvl);
266
267 /* New section? */
268 if(t==P_BEG_SECT){
269 if(tokz->nest_lvl==MAX_NEST-1){
270 tokz_warn_error(tokz, tokz->line, E_TOKZ_MAX_NEST);
271 had_error=TRUE;
272 while(ntokens--)
273 tok_free(&tokens[ntokens]);
274 break;
275 }else{
276 tokz->optstack[++tokz->nest_lvl]=options->opts;
277 }
278 }
279
280 /* call the handler */
281 if(!had_error && options && options->fn)
282 had_error=!options->fn(tokz, ntokens-1, tokens);
283
284 /* free the tokens */
285 while(ntokens--)
286 tok_free(&tokens[ntokens]);
287
288 switch(t){
289 case P_BEG_SECT:
290 if(!had_error)
291 continue;
292 /* #cancel handler should not be called when
293 * error occured in section begin handler */
294 tokz->nest_lvl--;
295 break;
296
297 case P_EOF:
298 if(tokz->nest_lvl>0){
299 tokz_warn_error(tokz, 0, E_TOKZ_UNEXPECTED_EOF);
300 had_error=TRUE;
301 }else if(!had_error){
302 had_error=!call_end_sect(tokz, tokz->optstack[0]);
303 }
304 break;
305
306 case P_END_SECT:
307 if(tokz->nest_lvl==0){
308 tokz_warn_error(tokz, tokz->line, E_TOKZ_SYNTAX);
309 had_error=TRUE;
310 break;
311 }
312
313 if(!had_error)
314 had_error=!call_end_sect(tokz, tokz->optstack[tokz->nest_lvl]);
315
316 tokz->nest_lvl--;
317
318 if(tokz->nest_lvl<init_nest_lvl)
319 break;
320
321 /* fall thru */
322
323 default:
324 if(!had_error)
325 continue;
326 }
327 break;
328 }
329
330 /* On error, call all the #cancel-handlers */
331 while(had_error && tokz->nest_lvl>=init_nest_lvl){
332 call_cancel_sect(tokz, tokz->optstack[tokz->nest_lvl]);
333 tokz->nest_lvl--;
334 }
335
336 /* Free optstack if it was alloced by this call */
337 if(alloced_optstack){
338 free(tokz->optstack);
339 tokz->optstack=NULL;
340 tokz->nest_lvl=0;
341 }
342
343 if(tokz->flags&TOKZ_PARSER_INDENT_MODE)
344 verbose_indent(init_nest_lvl);
345
346 return !had_error;
347 }
348
349
350 /* */
351
352
353 bool parse_config(const char *fname, const ConfOpt *options)
354 {
355 Tokenizer *tokz;
356 bool ret;
357
358 tokz=tokz_open(fname);
359
360 if(tokz==NULL)
361 return FALSE;
362
363 ret=parse_config_tokz(tokz, options);
364
365 tokz_close(tokz);
366
367 return ret;
368 }
369
370
371 bool parse_config_file(FILE *file, const ConfOpt *options)
372 {
373 Tokenizer *tokz;
374 bool ret;
375
376 tokz=tokz_open_file(file);
377
378 if(tokz==NULL)
379 return FALSE;
380
381 ret=parse_config_tokz(tokz, options);
382
383 tokz_close(tokz);
384
385 return ret;
386 }
387
388
389 /*
390 * Argument validity checking stuff
391 */
392
393
394 static bool arg_match(Token *tok, char c)
395 {
396 static const char chs[]={0, 'l', 'd', 'c', 's', 'i', 0, 0};
397 char c2;
398
399 if(c=='.' || c=='*')
400 return TRUE;
401
402 c2=chs[tok->type];
403
404 if(c2==c)
405 return TRUE;
406
407 if(c2=='c' && c=='l'){
408 TOK_SET_LONG(tok, TOK_CHAR_VAL(tok));
409 return TRUE;
410 }
411
412 if(c2=='l' && c=='c'){
413 TOK_SET_CHAR(tok, TOK_LONG_VAL(tok));
414 return TRUE;
415 }
416
417 if(c2=='l' && c=='d'){
418 TOK_SET_DOUBLE(tok, TOK_LONG_VAL(tok));
419 return TRUE;
420 }
421
422 return FALSE;
423 }
424
425
426 static bool check_argument(const char **pret, Token *tok, const char *p)
427 {
428 int mode=0;
429
430 if(*p=='*'){
431 *pret=p;
432 return TRUE;
433 }else if(*p=='?'){
434 mode=1;
435 p++;
436 }else if(*p==':'){
437 mode=2;
438 p++;
439 }else if(*p=='+'){
440 *pret=p;
441 return arg_match(tok, *(p-1));
442 }
443
444 while(*p!='\0'){
445 if(arg_match(tok, *p)){
446 p++;
447 while(mode==2 && *p==':'){
448 if(*++p=='\0')
449 break; /* invalid argument format string it is... */
450 p++;
451 }
452 *pret=p;
453 return TRUE;
454 }
455
456 if(mode==0)
457 break;
458
459 p++;
460
461 if(mode==1){
462 *pret=p;
463 return TRUE;
464 }
465
466 if(*p!=':')
467 break;
468 p++;
469 }
470
471 *pret=p;
472 return FALSE;
473 }
474
475
476 static bool args_at_end(const char *p)
477 {
478 if(p==NULL)
479 return TRUE;
480
481 while(*p!='\0'){
482 if(*p=='*' || *p=='+')
483 p++;
484 else if(*p=='?')
485 p+=2;
486 else
487 return FALSE;
488 }
489
490 return TRUE;
491 }
492
493
494 static bool check_args(const Tokenizer *tokz, Token *tokens, int ntokens,
495 const char *fmt)
496 {
497 int i;
498
499 if(fmt==NULL)
500 return ntokens==2;
501
502 for(i=1; i<ntokens-1; i++){
503 if(!check_argument(&fmt, &tokens[i], fmt)){
504 tokz_warn_error(tokz, tokens[i].line,
505 *fmt!='\0' ? E_TOKZ_INVALID_ARGUMENT
506 : E_TOKZ_TOO_MANY_ARGS);
507 return FALSE;
508 }
509 }
510
511 if(!args_at_end(fmt)){
512 tokz_warn_error(tokz, tokens[i].line, E_TOKZ_TOO_FEW_ARGS);
513 return FALSE;
514 }
515
516 return TRUE;
517 }
518

mercurial