4 * Copyright (c) Tuomo Valkonen 1999-2002.
6 * You may distribute and modify this library under the terms of either
7 * the Clarified Artistic License or the GNU LGPL, version 2.1 or later.
17 #define MAX_TOKENS 256
35 static bool opt_include(Tokenizer
*tokz
, int n
, Token
*toks
);
38 static ConfOpt common_opts
[]={
39 {"include", "s", opt_include
, NULL
},
40 {NULL
, NULL
, NULL
, NULL
}
47 static int read_statement(Tokenizer
*tokz
, Token
*tokens
, int *ntok_ret
)
51 int had_comma
=0; /* 0 - no, 1 - yes, 2 - not had, not expected */
58 if(!tokz_get_token(tokz
, tok
)){
63 if(ntokens
==MAX_TOKENS
-1){
65 tokz_warn_error(tokz
, tok
->line
, e
);
66 if(!(tokz
->flags
&TOKZ_ERROR_TOLERANT
))
73 if(ntokens
==1 && !had_comma
){
85 /* It is an operator */
88 switch(TOK_OP_VAL(tok
)){
90 retval
=(ntokens
==0 ? P_NONE
: P_STMT_NS
);
94 retval
=(ntokens
==0 ? P_NONE
: P_STMT
);
98 retval
=(ntokens
==0 ? P_BEG_SECT
: P_STMT_SECT
);
105 tokz_unget_token(tokz
, tok
);
111 retval
=(ntokens
==0 ? P_EOF
: P_STMT_NS
);
114 e
=E_TOKZ_UNEXPECTED_EOF
;
137 tokz_warn_error(tokz
, tok
->line
, e
);
139 if(!(tokz
->flags
&TOKZ_ERROR_TOLERANT
) || retval
!=0)
153 static bool find_beg_sect(Tokenizer
*tokz
)
157 while(tokz_get_token(tokz
, &tok
)){
159 if(TOK_OP_VAL(&tok
)==OP_NEXTLINE
)
162 if(TOK_OP_VAL(&tok
)==OP_SCOLON
)
165 if(TOK_OP_VAL(&tok
)==OP_L_BRC
)
169 tokz_unget_token(tokz
, &tok
);
179 static const ConfOpt
* lookup_option(const ConfOpt
*opts
, const char *name
)
181 while(opts
->optname
!=NULL
){
182 if(strcmp(opts
->optname
, name
)==0)
190 static bool call_end_sect(Tokenizer
*tokz
, const ConfOpt
*opts
)
192 opts
=lookup_option(opts
, "#end");
194 return opts
->fn(tokz
, 0, NULL
);
200 static bool call_cancel_sect(Tokenizer
*tokz
, const ConfOpt
*opts
)
202 opts
=lookup_option(opts
, "#cancel");
204 return opts
->fn(tokz
, 0, NULL
);
213 bool parse_config_tokz(Tokenizer
*tokz
, const ConfOpt
*options
)
215 Token tokens
[MAX_TOKENS
];
216 bool alloced_optstack
=FALSE
;
221 bool is_default
=FALSE
;
223 /* Allocate tokz->optstack if it does not yet exist (if it does,
224 * we have been called from an option handler)
227 tokz
->optstack
=ALLOC_N(const ConfOpt
*, MAX_NEST
);
233 memset(tokz
->optstack
, 0, sizeof(ConfOpt
*)*MAX_NEST
);
234 init_nest_lvl
=tokz
->nest_lvl
=0;
235 alloced_optstack
=TRUE
;
237 init_nest_lvl
=tokz
->nest_lvl
;
240 tokz
->optstack
[init_nest_lvl
]=options
;
242 for(i
=0; i
<MAX_TOKENS
; i
++)
243 tok_init(&tokens
[i
]);
249 /* free the tokens */
251 tok_free(&tokens
[ntokens
]);
253 /* read the tokens */
254 t
=read_statement(tokz
, tokens
, &ntokens
);
266 else if(tokz
->flags
&TOKZ_PARSER_INDENT_MODE
)
267 verbose_indent(tokz
->nest_lvl
);
269 if(!TOK_IS_IDENT(tokens
+0)){
271 tokz_warn_error(tokz
, tokens
->line
,
272 E_TOKZ_IDENTIFIER_EXPECTED
);
276 if(find_beg_sect(tokz
))
283 /* Got the statement and its type */
285 options
=lookup_option(tokz
->optstack
[tokz
->nest_lvl
],
286 TOK_IDENT_VAL(tokens
+0));
288 options
=lookup_option(common_opts
, TOK_IDENT_VAL(tokens
+0));
289 if(options
==NULL
&& (tokz
->flags
&TOKZ_DEFAULT_OPTION
)){
290 options
=lookup_option(tokz
->optstack
[tokz
->nest_lvl
], "#default");
291 is_default
=(options
!=NULL
);
296 tokz_warn_error(tokz
, tokens
->line
, E_TOKZ_UNKNOWN_OPTION
);
297 }else if(!is_default
) {
298 had_error
=!check_args(tokz
, tokens
, ntokens
, options
->argfmt
);
304 /* Found the option and arguments are ok */
306 if(options
->opts
!=NULL
){
309 tokz_warn_error(tokz
, tokz
->line
, E_TOKZ_LBRACE_EXPECTED
);
310 }else if(tokz
->nest_lvl
==MAX_NEST
-1){
311 tokz_warn_error(tokz
, tokz
->line
, E_TOKZ_MAX_NEST
);
315 tokz
->optstack
[tokz
->nest_lvl
]=options
->opts
;
317 }else if(t
==P_STMT_SECT
){
319 tokz_warn_error(tokz
, tokz
->line
, E_TOKZ_SYNTAX
);
322 if(!had_error
&& options
->fn
!=NULL
){
323 had_error
=!options
->fn(tokz
, ntokens
, tokens
);
324 if(t
==P_STMT_SECT
&& had_error
)
332 }else if(tokz
->nest_lvl
>0 || errornest
>0){
333 tokz_warn_error(tokz
, 0, E_TOKZ_UNEXPECTED_EOF
);
341 tokz_warn_error(tokz
, tokz
->line
, E_TOKZ_SYNTAX
);
345 if(tokz
->nest_lvl
+errornest
==0){
346 tokz_warn_error(tokz
, tokz
->line
, E_TOKZ_SYNTAX
);
356 had_error
=!call_end_sect(tokz
, tokz
->optstack
[tokz
->nest_lvl
]);
360 if(tokz
->nest_lvl
<init_nest_lvl
)
370 if(!(tokz
->flags
&TOKZ_ERROR_TOLERANT
))
375 /* free the tokens */
377 tok_free(&tokens
[ntokens
]);
379 while(tokz
->nest_lvl
>=init_nest_lvl
){
380 if(tokz
->flags
&TOKZ_ERROR_TOLERANT
|| !had_error
)
381 call_end_sect(tokz
, tokz
->optstack
[tokz
->nest_lvl
]);
383 call_cancel_sect(tokz
, tokz
->optstack
[tokz
->nest_lvl
]);
387 /* Free optstack if it was alloced by this call */
388 if(alloced_optstack
){
389 free(tokz
->optstack
);
394 if(tokz
->flags
&TOKZ_PARSER_INDENT_MODE
)
395 verbose_indent(init_nest_lvl
);
404 bool parse_config(const char *fname
, const ConfOpt
*options
, int flags
)
409 tokz
=tokz_open(fname
);
414 tokz
->flags
|=flags
&~TOKZ_READ_COMMENTS
;
416 ret
=parse_config_tokz(tokz
, options
);
424 bool parse_config_file(FILE *file
, const ConfOpt
*options
, int flags
)
429 tokz
=tokz_open_file(file
, NULL
);
434 tokz
->flags
|=flags
&~TOKZ_READ_COMMENTS
;
436 ret
=parse_config_tokz(tokz
, options
);
445 * Argument validity checking stuff
449 static int arg_match(Token
*tok
, char c
, bool si
)
460 if(c2
=='i' && c
=='s'){
461 TOK_SET_IDENT(tok
, TOK_STRING_VAL(tok
));
465 if(c2
=='s' && c
=='i'){
466 TOK_SET_STRING(tok
, TOK_IDENT_VAL(tok
));
471 if(c2
=='c' && c
=='l'){
472 TOK_SET_LONG(tok
, TOK_CHAR_VAL(tok
));
476 if(c2
=='l' && c
=='c'){
477 TOK_SET_CHAR(tok
, TOK_LONG_VAL(tok
));
481 if(c2
=='l' && c
=='d'){
482 TOK_SET_DOUBLE(tok
, TOK_LONG_VAL(tok
));
488 TOK_SET_BOOL(tok
, TOK_LONG_VAL(tok
));
491 if(strcmp(TOK_IDENT_VAL(tok
), "TRUE")==0){
493 TOK_SET_BOOL(tok
, TRUE
);
495 }else if(strcmp(TOK_IDENT_VAL(tok
), "FALSE")==0){
497 TOK_SET_BOOL(tok
, FALSE
);
503 return E_TOKZ_INVALID_ARGUMENT
;
507 static int check_argument(const char **pret
, Token
*tok
, const char *p
,
511 int e
=E_TOKZ_TOO_MANY_ARGS
;
527 return arg_match(tok
, *(p
-1), si
);
531 e
=arg_match(tok
, *p
, si
);
534 while(mode
==2 && *p
==':'){
536 break; /* Invalid argument format string, though... */
556 e
=E_TOKZ_TOO_MANY_ARGS
;
564 static bool args_at_end(const char *p
)
570 if(*p
=='*' || *p
=='+')
582 bool do_check_args(const Tokenizer
*tokz
, Token
*tokens
, int ntokens
,
583 const char *fmt
, bool si
)
590 tokz_warn_error(tokz
, tokens
[0].line
, E_TOKZ_TOO_MANY_ARGS
);
594 for(i
=1; i
<ntokens
; i
++){
595 e
=check_argument(&fmt
, &tokens
[i
], fmt
, si
);
597 tokz_warn_error(tokz
, tokens
[i
].line
, e
);
602 if(!args_at_end(fmt
)){
603 tokz_warn_error(tokz
, tokens
[i
].line
, E_TOKZ_TOO_FEW_ARGS
);
611 bool check_args(const Tokenizer
*tokz
, Token
*tokens
, int ntokens
,
614 return do_check_args(tokz
, tokens
, ntokens
, fmt
, FALSE
);
618 bool check_args_loose(const Tokenizer
*tokz
, Token
*tokens
, int ntokens
,
621 return do_check_args(tokz
, tokens
, ntokens
, fmt
, TRUE
);
628 static bool try_include(Tokenizer
*tokz
, const char *fname
)
637 if(!tokz_pushf_file(tokz
, f
, fname
)){
646 static bool try_include_dir(Tokenizer
*tokz
, const char *dir
, int dlen
,
652 tmpname
=scatn(dir
, dlen
, file
, -1);
659 retval
=try_include(tokz
, tmpname
);
667 static bool opt_include(Tokenizer
*tokz
, int n
, Token
*toks
)
669 const char *fname
=TOK_STRING_VAL(toks
+1);
670 const char *lastndx
=NULL
;
674 if(fname
[0]!='/' && tokz
->name
!=NULL
)
675 lastndx
=strrchr(tokz
->name
, '/');
678 retval
=try_include(tokz
, fname
);
680 retval
=try_include_dir(tokz
, tokz
->name
, lastndx
-tokz
->name
+1, fname
);
687 if(tokz
->includepaths
!=NULL
){
688 while(tokz
->includepaths
[i
]!=NULL
){
689 if(try_include_dir(tokz
, tokz
->includepaths
[i
], -1, fname
))
695 warn_obj(fname
, "%s", strerror(e
));
701 extern void tokz_set_includepaths(Tokenizer
*tokz
, char **paths
)
703 tokz
->includepaths
=paths
;
708 ConfOpt libtu_dummy_confopts
[]={
714 bool parse_config_tokz_skip_section(Tokenizer
*tokz
)
716 return parse_config_tokz(tokz
, libtu_dummy_confopts
);