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);