From 97b62dd09a07377ade0b8676ca83b78389e42364 Mon Sep 17 00:00:00 2001 From: Thomas Jensen Date: Sat, 13 Mar 2021 15:18:41 +0100 Subject: [PATCH] Support multiple configuration files #5 Main implementation --- src/Makefile | 2 +- src/lexer.l | 200 +++++++++++++++++++++---------------------- src/parser.y | 139 ++++++++++++++++++++---------- src/parsing.c | 231 +++++++++++++++++++++++++++++++++++++++++--------- src/parsing.h | 14 +-- 5 files changed, 381 insertions(+), 205 deletions(-) diff --git a/src/Makefile b/src/Makefile index 164b9f4..6e403fd 100644 --- a/src/Makefile +++ b/src/Makefile @@ -94,7 +94,7 @@ tools.o: tools.c tools.h boxes.h shape.h unicode.h config.h | check_dir unicode.o: unicode.c unicode.h boxes.h tools.h config.h | check_dir shape.o: shape.c shape.h boxes.h tools.h config.h | check_dir generate.o: generate.c generate.h boxes.h shape.h tools.h unicode.h config.h | check_dir -parsing.o: parsing.c parsing.h boxes.h config.h | check_dir +parsing.o: parsing.c parsing.h parser.h lex.yy.h boxes.h tools.h config.h | check_dir remove.o: remove.c remove.h boxes.h shape.h tools.h unicode.h config.h | check_dir regulex.o: regulex.c regulex.h boxes.h tools.h unicode.h config.h | check_dir getopt.o: misc/getopt.c misc/getopt.h | check_dir diff --git a/src/lexer.l b/src/lexer.l index 951eb6e..4519c99 100644 --- a/src/lexer.l +++ b/src/lexer.l @@ -25,10 +25,20 @@ #include "config.h" -void begin_speedmode(); + +typedef struct { + int yyerrcnt; + + /** pointer to the currently active string delimiter character in bison_args */ + char *sdel_ptr; + + /** pointer to the currently active string escape character in bison_args */ + char *sesc_ptr; +} pass_to_flex; -void chg_strdelims (const char asdel, const char asesc); + +void begin_speedmode(void *yyscanner); /** @@ -38,10 +48,10 @@ void chg_strdelims (const char asdel, const char asesc); * dynamically enlarge its input buffer to accomodate larger tokens. Thus, we simply set the buffer size to the * input file size plus 10 bytes margin-of-error. * - * @param scanner pointer to the scanner data block + * @param yyscanner pointer to the scanner data block * @param configfile the path to the config file we are reading */ -void inflate_inbuf(void *scanner, const char *configfile); +void inflate_inbuf(void *yyscanner, const char *configfile); } @@ -49,6 +59,7 @@ void inflate_inbuf(void *scanner, const char *configfile); #include #include #include + #include "shape.h" #define FILE_LEXER_L #include "boxes.h" @@ -60,21 +71,18 @@ void inflate_inbuf(void *scanner, const char *configfile); #define LEX_MAX_WARN 3 /* number of lex errors per design */ -static int yyerrcnt = 0; - -/* the currently active string delimiter character */ -static char sdel = '\"'; - -/* the currently active string escape character */ -static char sesc = '\\'; +static void report_state_char(char *symbol, char c, char *expected_state_str); +static void report_state(char *symbol, char *text, char *expected_state_str); %} + %option 8bit %option bison-bridge %option case-insensitive %option ecs +%option extra-type="pass_to_flex *" %option never-interactive %option nodefault %option noinput @@ -85,12 +93,12 @@ static char sesc = '\\'; %option yylineno -%x SAMPLE -%x SPEEDMODE %x DELWORD %x PARENT -%s SHAPES +%x SAMPLE +%x SPEEDMODE %s ELASTIC +%s SHAPES PWORD [a-zA-ZäöüÄÖÜ][a-zA-Z0-9\-_üäöÜÄÖß]* @@ -104,24 +112,22 @@ PFILENAME [^\r\n]+ %% -[ \r\t] /* ignore whitespace */ +[ \r\t] /* ignore whitespace */ -\n {} +\n {} [^ \t\r\n]+ { /* * String delimiter spec - like WORD, but allow any character */ - #ifdef LEXER_DEBUG - fprintf (stderr, "\nYDELWOR: %s -- STATE INITIAL", yytext); - #endif yylval->s = (char *) strdup (yytext); if (yylval->s == NULL) { perror (PROJECT); exit (EXIT_FAILURE); } - BEGIN INITIAL; + BEGIN(INITIAL); + report_state("YDELWOR", yytext, "INITIAL"); return YDELWORD; } @@ -136,7 +142,7 @@ PFILENAME [^\r\n]+ int rest_len = yyleng - 1; /* length of string pointed to by p */ int qcnt = 0; /* esc char count in current string */ - if (yytext[0] != sdel) { + if (yytext[0] != *(yyextra->sdel_ptr)) { REJECT; /* that was not our delimiter */ } @@ -148,35 +154,34 @@ PFILENAME [^\r\n]+ p = yylval->s; while (*p) { - if (*p == sesc) { + if (*p == *(yyextra->sesc_ptr)) { memmove (p, p+1, rest_len); /* incl. '\0' */ ++qcnt; --rest_len; if (*p == '\0') break; } - else if (*p == sdel) { + else if (*p == *(yyextra->sdel_ptr)) { *p = '\0'; yyless ((p-yylval->s)+2+qcnt); /* string plus quotes */ #ifdef LEXER_DEBUG - fprintf (stderr, "\n STRING: \"%s\"", yylval->s); + fprintf (stderr, " STRING: \"%s\"\n", yylval->s); #endif return STRING; } --rest_len; ++p; } - if (yyerrcnt++ < 5) + if ((yyextra->yyerrcnt)++ < 5) { yyerror(NULL, "Unterminated String -- %s", yytext); + } return YUNREC; } {PPARENT} { - #ifdef LEXER_DEBUG - fprintf (stderr, "\nYPARENT: %s", yytext); - #endif - BEGIN PARENT; + BEGIN(PARENT); + report_state("YPARENT", yytext, "PARENT"); return YPARENT; } @@ -190,23 +195,20 @@ PFILENAME [^\r\n]+ while ((*p == ' ' || *p == '\t') && p >= yylval->s) { *p-- = '\0'; } - #ifdef LEXER_DEBUG - fprintf (stderr, "\n STRING: \"%s\"", yylval->s); - #endif - BEGIN INITIAL; - return STRING; + BEGIN(INITIAL); + report_state("FILENAM", yylval->s, "INITIAL"); + return FILENAME; } -\n { - BEGIN INITIAL; +\r?\n { + /* This is triggered only when no parent filename was specified. */ + BEGIN(INITIAL); + report_state(" NL", "", "INITIAL"); } - Sample { - #ifdef LEXER_DEBUG - fprintf (stderr, "\nYSAMPLE: %s -- STATE SAMPLE", yytext); - #endif - BEGIN SAMPLE; + BEGIN(SAMPLE); + report_state("YSAMPLE", yytext, "SAMPLE"); return YSAMPLE; } @@ -235,19 +237,18 @@ Sample { len = p - yytext; /* yyless(n): push back all but the first n */ yyless (len); /* allow him to return YENDSAMPLE */ - yylval->s[len] = '\n'; /* replace 'e' with newline */ + yylval->s[len] = '\n'; /* replace 'e' with newline */ btrim (yylval->s, &len); if (len > 0) { - strcat (yylval->s, "\n"); /* memory was allocated with strdup */ - #ifdef LEXER_DEBUG - fprintf (stderr, "\n STRING: \"%s\" -- STATE INITIAL", yylval->s); - #endif - BEGIN INITIAL; + strcat (yylval->s, "\n"); /* memory was allocated with strdup */ + BEGIN(INITIAL); + report_state(" STRING", yylval->s, "INITIAL"); return STRING; } else { - if (yyerrcnt++ < 5) + if ((yyextra->yyerrcnt)++ < 5) { yyerror(NULL, "SAMPLE block must not be empty"); + } BFREE (yylval->s); return YUNREC; } @@ -259,7 +260,7 @@ Sample { ends[ \t\r]*$ { #ifdef LEXER_DEBUG - fprintf (stderr, "\nYENDSAM: %s", yytext); + fprintf (stderr, "YENDSAM: %s\n", yytext); #endif return YENDSAMPLE; } @@ -267,26 +268,22 @@ ends[ \t\r]*$ { Elastic { - #ifdef LEXER_DEBUG - fprintf (stderr, "\nYELASTC: %s -- STATE ELASTIC", yytext); - #endif - BEGIN ELASTIC; + BEGIN(ELASTIC); + report_state("YELASTC", yytext, "ELASTIC"); return YELASTIC; } Shapes { - #ifdef LEXER_DEBUG - fprintf (stderr, "\nYSHAPES: %s -- STATE SHAPES", yytext); - #endif - BEGIN SHAPES; + BEGIN(SHAPES); + report_state("YSHAPES", yytext, "SHAPES"); return YSHAPES; } {PBOX} { #ifdef LEXER_DEBUG - fprintf (stderr, "\n YBOX: %s", yytext); + fprintf (stderr, " YBOX: %s\n", yytext); #endif - yyerrcnt = 0; + yyextra->yyerrcnt = 0; return YBOX; } @@ -318,18 +315,14 @@ Once { yylval->c = 'o'; return YRXPFLAG; } wnw { yylval->shape = WNW; return SHAPE; } \) { - #ifdef LEXER_DEBUG - fprintf (stderr, "\n SYMBOL: \'%c\' -- STATE INITIAL", yytext[0]); - #endif - BEGIN INITIAL; + BEGIN(INITIAL); + report_state_char("SYMBOL", yytext[0], "INITIAL"); return yytext[0]; } \} { - #ifdef LEXER_DEBUG - fprintf (stderr, "\n SYMBOL: \'%c\' -- STATE INITIAL", yytext[0]); - #endif - BEGIN INITIAL; + BEGIN(INITIAL); + report_state_char("SYMBOL", yytext[0], "INITIAL"); return yytext[0]; } @@ -339,7 +332,7 @@ author|designer|tags|created|revision|revdate|indent { * general key words */ #ifdef LEXER_DEBUG - fprintf (stderr, "\nKEYWORD: %s", yytext); + fprintf (stderr, "KEYWORD: %s\n", yytext); #endif yylval->s = (char *) strdup (yytext); if (yylval->s == NULL) { @@ -354,17 +347,15 @@ Delimiter|Delim { /* * Change string delimiting characters */ - #ifdef LEXER_DEBUG - fprintf (stderr, "\nYCHGDEL: %s -- STATE DELWORD", yytext); - #endif - BEGIN DELWORD; + BEGIN(DELWORD); + report_state("YCHGDEL", yytext, "DELWORD"); return YCHGDEL; } {PWORD} { #ifdef LEXER_DEBUG - fprintf (stderr, "\n WORD: %s", yytext); + fprintf (stderr, " WORD: %s\n", yytext); #endif yylval->s = (char *) strdup (yytext); if (yylval->s == NULL) { @@ -377,7 +368,7 @@ Delimiter|Delim { [\+-]?[0-9]+ { #ifdef LEXER_DEBUG - fprintf (stderr, "\nYNUMBER: %s", yytext); + fprintf (stderr, "YNUMBER: %s\n", yytext); #endif yylval->num = atoi (yytext); return YNUMBER; @@ -386,7 +377,7 @@ Delimiter|Delim { [,(){}] { #ifdef LEXER_DEBUG - fprintf (stderr, "\n SYMBOL: \'%c\'", yytext[0]); + fprintf (stderr, " SYMBOL: \'%c\'\n", yytext[0]); #endif return yytext[0]; } @@ -395,25 +386,23 @@ Delimiter|Delim { #.*$ { /* ignore comments */ #ifdef LEXER_DEBUG - fprintf (stderr, "\nCOMMENT: %s", yytext+1); + fprintf (stderr, "COMMENT: %s\n", yytext+1); #endif } . { - if (yyerrcnt++ < LEX_MAX_WARN) + if ((yyextra->yyerrcnt)++ < LEX_MAX_WARN) yyerror(NULL, "Unrecognized input char \'%s\'", yytext); return YUNREC; } -{PBOX}{PWHITE}+{PWORD} { - #ifdef LEXER_DEBUG - fprintf (stderr, "\n STATUS: %s -- STATE INITIAL", yytext); - #endif - yyless (0); - speeding = 0; - BEGIN INITIAL; +{PBOX}{PWHITE}+{PWORD}|{PPARENT} { + /* end speedmode, but then give back the whole match so BOX or PARENT can be started properly */ + yyless(0); + BEGIN(INITIAL); + report_state(" STATUS", "", "INITIAL"); } \n {} @@ -424,11 +413,7 @@ Delimiter|Delim { %% -/* TODO These functions should go away. The cast to (struct yyguts_t *) is a hack. - * And they use global variables which is not reentrant at all. Use extra data instead. - * Maybe inflate_inbuf() can be moved to parsing.c? */ - -void inflate_inbuf(void *scanner, const char *configfile) +void inflate_inbuf(void *yyscanner, const char *configfile) { struct stat sinf; @@ -436,31 +421,42 @@ void inflate_inbuf(void *scanner, const char *configfile) perror (PROJECT); exit (EXIT_FAILURE); } - struct yyguts_t *yyg = (struct yyguts_t *) scanner; - yy_delete_buffer(YY_CURRENT_BUFFER, scanner); - yy_switch_to_buffer (yy_create_buffer(yyin, sinf.st_size+10, scanner), scanner); + struct yyguts_t *yyg = (struct yyguts_t *) yyscanner; + yy_delete_buffer(YY_CURRENT_BUFFER, yyscanner); + yy_switch_to_buffer (yy_create_buffer(yyin, sinf.st_size+10, yyscanner), yyscanner); + BEGIN(INITIAL); } -void begin_speedmode(void *scanner) +void begin_speedmode(void *yyscanner) { - #ifdef LEXER_DEBUG - fprintf (stderr, "\n STATUS: begin_speedmode() -- STATE SPEEDMODE"); - #endif - struct yyguts_t *yyg = (struct yyguts_t *) scanner; - BEGIN SPEEDMODE; + struct yyguts_t *yyg = (struct yyguts_t *) yyscanner; + BEGIN(SPEEDMODE); + report_state(" STATUS", "begin_speedmode()", "SPEEDMODE"); } -void chg_strdelims (const char asesc, const char asdel) +static void report_state_char(char *symbol, char c, char *expected_state_str) { + char *s = (char *) malloc(4); + sprintf(s, "'%c'", c); + report_state(symbol, s, expected_state_str); + BFREE(s); +} + + + +static void report_state(char *symbol, char *text, char *expected_state_str) +{ + int lexerDebug = 0; #ifdef LEXER_DEBUG - fprintf (stderr, "\n STATUS: chg_strdelims ('%c', '%c')", asesc, asdel); + lexerDebug = 1; #endif - sesc = asesc; - sdel = asdel; + if (lexerDebug) { + fprintf(stderr, "%7s: %s -- STATE %s\n", symbol, text, expected_state_str); + } } diff --git a/src/parser.y b/src/parser.y index 5d2a539..dedcf22 100644 --- a/src/parser.y +++ b/src/parser.y @@ -25,7 +25,6 @@ #include "boxes.h" -extern int speeding; /** all the arguments which we pass to the bison parser */ typedef struct { @@ -41,6 +40,31 @@ typedef struct { /** the path to the config file we are parsing */ char *config_file; + /** the currently active string delimiter character */ + char sdel; + + /** the currently active string escape character */ + char sesc; + + int num_mandatory; + + int time_for_se_check; + + /** number of user-specified shapes */ + int num_shapespec; + + /** used to limit "skipping" msgs */ + int skipping; + + /** true if we're skipping designs, but no error */ + int speeding; + + /** names of config files specified via "parent" */ + char **parent_configs; + + /** number of parent config files (size of parent_configs array) */ + size_t num_parent_configs; + /** the flex scanner, which is explicitly passed to reentrant bison */ void *lexer_state; } pass_to_bison; @@ -62,13 +86,13 @@ typedef struct { #include "parser.h" #include "lex.yy.h" + /* * Valid characters to be used as string delimiters. Note that the * following list must correspond to the DELIM definition in lexer.l. */ #define LEX_SDELIM "\"~'`!@%&*=:;<>?/|.\\" - /** required for bison-flex bridge */ #define scanner bison_args->lexer_state @@ -76,14 +100,6 @@ typedef struct { #define curdes (bison_args->designs[bison_args->design_idx]) -static int num_mandatory = 0; -static int time_for_se_check = 0; -static int num_shapespec = 0; /* number of user-specified shapes */ - -int speeding = 0; /* true if we're skipping designs, but no error */ -static int skipping = 0; /* used to limit "skipping" msgs */ - - static int check_sizes(pass_to_bison *bison_args) /* @@ -293,6 +309,17 @@ static int perform_se_check(pass_to_bison *bison_args) +static void chg_strdelims (pass_to_bison *bison_args, const char asesc, const char asdel) +{ + #ifdef PARSER_DEBUG + fprintf (stderr, " STATUS: chg_strdelims ('%c', '%c') - This changes lexer behavior!\n", asesc, asdel); + #endif + bison_args->sesc = asesc; + bison_args->sdel = asdel; +} + + + static void recover(pass_to_bison *bison_args) /* * Reset parser to neutral state, so a new design can be parsed. @@ -300,10 +327,10 @@ static void recover(pass_to_bison *bison_args) * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ { - num_mandatory = 0; - time_for_se_check = 0; - num_shapespec = 0; - chg_strdelims ('\\', '\"'); + bison_args->num_mandatory = 0; + bison_args->time_for_se_check = 0; + bison_args->num_shapespec = 0; + chg_strdelims(bison_args, '\\', '\"'); /* * Clear current design @@ -358,7 +385,6 @@ static int design_needed (const char *name, const int design_idx) |__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|_*/ %define api.pure true -%pure-parser %lex-param {void *scanner} %parse-param {pass_to_bison *bison_args} @@ -375,6 +401,7 @@ static int design_needed (const char *name, const int design_idx) %token KEYWORD %token WORD %token STRING +%token FILENAME %token SHAPE %token YNUMBER %token YRXPFLAG @@ -410,6 +437,7 @@ first_rule: YYABORT; } bison_args->num_designs = 1; + bison_args->designs->indentmode = DEF_INDENTMODE; bison_args->designs->defined_in = bison_args->config_file; } @@ -424,14 +452,11 @@ config_file if (bison_args->design_idx == 0) { BFREE (bison_args->designs); bison_args->num_designs = 0; - if (opt.design_choice_by_user) { - fprintf (stderr, "%s: unknown box design -- %s\n", - PROJECT, (char *) opt.design); + if (!opt.design_choice_by_user && bison_args->num_parent_configs == 0) { + fprintf (stderr, "%s: no valid data in config file -- %s\n", PROJECT, bison_args->config_file); + YYABORT; } - else { - yyerror(bison_args, "no valid designs found"); - } - YYABORT; + YYACCEPT; } --(bison_args->design_idx); @@ -444,21 +469,21 @@ config_file bison_args->designs = tmp; } -parent_def: YPARENT STRING +parent_def: YPARENT FILENAME { char *filepath = $2; #ifdef PARSER_DEBUG fprintf (stderr, "parent config file specified: [%s]\n", filepath); #endif if (filepath == NULL || filepath[0] == '\0') { - skipping = 1; + bison_args->skipping = 1; yyerror(bison_args, "parent reference is empty"); YYERROR; } else if (strcasecmp(filepath, ":global:") == 0) { /* special token */ filepath = discover_config_file(1); if (filepath == NULL) { - skipping = 1; /* prevent redundant "skipping to next design" message */ + bison_args->skipping = 1; /* prevent redundant "skipping to next design" message */ yyerror(bison_args, "parent reference to global config which cannot be found"); YYERROR; } @@ -466,7 +491,7 @@ parent_def: YPARENT STRING else { FILE *f = fopen(filepath, "r"); if (f == NULL) { - skipping = 1; + bison_args->skipping = 1; yyerror(bison_args, "parent config file not found: %s", filepath); YYERROR; } @@ -478,16 +503,17 @@ parent_def: YPARENT STRING fprintf (stderr, "parent config file path resolved: [%s]\n", filepath); #endif - int is_new = !array_contains(parent_configs, num_parent_configs, filepath); + int is_new = !array_contains(bison_args->parent_configs, bison_args->num_parent_configs, filepath); if (!is_new) { #ifdef PARSER_DEBUG fprintf (stderr, "duplicate parent / cycle: [%s]\n", filepath); #endif } else { - parent_configs = realloc(parent_configs, (num_parent_configs + 1) * sizeof(char *)); - parent_configs[num_parent_configs] = filepath; - ++num_parent_configs; + bison_args->parent_configs = realloc(bison_args->parent_configs, + (bison_args->num_parent_configs + 1) * sizeof(char *)); + bison_args->parent_configs[bison_args->num_parent_configs] = filepath; + ++(bison_args->num_parent_configs); } } @@ -496,20 +522,21 @@ config_file: config_file design_or_error | design_or_error | config_file parent_ design_or_error: design | error { - if (!speeding && !skipping) { + if (!(bison_args->speeding) && !(bison_args->skipping)) { recover(bison_args); yyerror(bison_args, "skipping to next design"); - skipping = 1; + bison_args->skipping = 1; } } design: YBOX WORD { - chg_strdelims ('\\', '\"'); - skipping = 0; + chg_strdelims(bison_args, '\\', '\"'); + bison_args->speeding = 0; + bison_args->skipping = 0; if (!design_needed ($2, bison_args->design_idx)) { - speeding = 1; + bison_args->speeding = 1; begin_speedmode(scanner); YYERROR; } @@ -529,7 +556,7 @@ layout YEND WORD yyerror(bison_args, "box design name differs at BOX and END"); YYERROR; } - if (num_mandatory < 3) { + if (bison_args->num_mandatory < 3) { yyerror(bison_args, "entries SAMPLE, SHAPES, and ELASTIC are mandatory"); YYERROR; } @@ -556,9 +583,9 @@ layout YEND WORD perror (PROJECT); YYABORT; } - num_mandatory = 0; - time_for_se_check = 0; - num_shapespec = 0; + bison_args->num_mandatory = 0; + bison_args->time_for_se_check = 0; + bison_args->num_shapespec = 0; /* * Check if we need to continue parsing. If not, return. @@ -655,6 +682,24 @@ entry: KEYWORD STRING } } +| YPARENT FILENAME + { + char *filename = $2; + if (filename[0] != filename[strlen(filename) - 1] + || (filename[0] >= 'a' && filename[0] <= 'z') + || (filename[0] >= 'A' && filename[0] <= 'Z') + || (filename[0] >= '0' && filename[0] <= '9')) + { + yyerror(bison_args, "string expected", filename); + YYERROR; + } + else { + #ifdef PARSER_DEBUG + fprintf (stderr, "%s: Discarding entry [%s = %s].\n", PROJECT, "parent", filename); + #endif + } + } + | YCHGDEL YDELWORD { if (strlen($2) != 2) { @@ -670,7 +715,7 @@ entry: KEYWORD STRING ($2)[1], LEX_SDELIM); YYERROR; } - chg_strdelims ($2[0], $2[1]); + chg_strdelims(bison_args, $2[0], $2[1]); } | WORD STRING @@ -704,7 +749,7 @@ block: YSAMPLE STRING YENDSAMPLE } curdes.sample = line; - ++num_mandatory; + ++(bison_args->num_mandatory); } | YSHAPES '{' slist '}' @@ -719,7 +764,7 @@ block: YSAMPLE STRING YENDSAMPLE /* * At least one shape must be specified */ - if (num_shapespec < 1) { + if (bison_args->num_shapespec < 1) { yyerror(bison_args, "must specify at least one non-empty shape per design"); YYERROR; } @@ -808,8 +853,8 @@ block: YSAMPLE STRING YENDSAMPLE YYERROR; } - ++num_mandatory; - if (++time_for_se_check > 1) { + ++(bison_args->num_mandatory); + if (++(bison_args->time_for_se_check) > 1) { if (perform_se_check(bison_args) != 0) { YYERROR; } @@ -863,8 +908,8 @@ block: YSAMPLE STRING YENDSAMPLE | YELASTIC '(' elist ')' { - ++num_mandatory; - if (++time_for_se_check > 1) { + ++(bison_args->num_mandatory); + if (++(bison_args->time_for_se_check) > 1) { if (perform_se_check(bison_args) != 0) { YYERROR; } @@ -970,7 +1015,7 @@ slist_entry: SHAPE shape_def if (isempty (curdes.shape + $1)) { curdes.shape[$1] = $2; if (!isdeepempty(&($2))) { - ++num_shapespec; + ++(bison_args->num_shapespec); } } else { diff --git a/src/parsing.c b/src/parsing.c index 78edf69..c76db70 100644 --- a/src/parsing.c +++ b/src/parsing.c @@ -24,73 +24,85 @@ #include "config.h" #include +#include #include +#include #include "boxes.h" #include "parsing.h" #include "parser.h" #include "lex.yy.h" +#include "tools.h" -/* file name of the config file currently being parsed */ -char *current_config_file_name = NULL; - /** file handle of the config file currently being parsed */ static FILE *current_config_handle = NULL; -/** names of config files specified via "parent" */ -char **parent_configs = NULL; - -/** number of parent config files (size of parent_configs array) */ -size_t num_parent_configs = 0; - /** the arguments passed to our current invocation of the parser */ static pass_to_bison *current_bison_args = NULL; +/** the name of the initially specified config file */ +static const char *first_config_file = NULL; + +/** all parent configs encountered across all parsed config files */ +static char **parent_configs = NULL; + +/** total number of parent configs (the size of the `parent_configs` array) */ +static size_t num_parent_configs = 0; /** - * Set yyin and current_config_file_name to the config file to be used. + * Set yyin to the config file to be used. * - * @param bison_args the bison args that we set up in the calling function - * @param config_file_path the new file name to set - * @return 0 if successful (yyin and current_config_file_name are set) + * @param bison_args the bison args that we set up in the calling function (contains config file path) + * @return 0 if successful (yyin is set) * != 0 on error (yyin is unmodified) */ -static int open_yy_config_file(pass_to_bison *bison_args, const char *config_file_path) +static int open_yy_config_file(pass_to_bison *bison_args) { - current_config_handle = fopen(config_file_path, "r"); + current_config_handle = fopen(bison_args->config_file, "r"); if (current_config_handle == NULL) { - fprintf(stderr, "%s: Couldn't open config file '%s' for input\n", PROJECT, config_file_path); + fprintf(stderr, "%s: Couldn't open config file '%s' for input\n", PROJECT, bison_args->config_file); return 1; } - current_config_file_name = (char *) config_file_path; yyset_in(current_config_handle, bison_args->lexer_state); - // TODO to reset parser, ‘YY_FLUSH_BUFFER’ and BEGIN INITIAL after each change to yyin. - // --> should be ok, because we delete the whole buffer at the beginning, but BEGIN INITIAL may make sense return 0; } + void print_design_list_header() { char buf[42]; sprintf(buf, "%d", anz_designs); - fprintf(opt.outfile, "%d Available Style%s in \"%s\":\n", - anz_designs, anz_designs == 1 ? "" : "s", current_config_file_name); - fprintf(opt.outfile, "-----------------------%s", - anz_designs == 1 ? "" : "-"); - for (int i = strlen(current_config_file_name) + strlen(buf); i > 0; --i) { - fprintf(opt.outfile, "-"); + fprintf(opt.outfile, "%d Available Style%s", anz_designs, anz_designs == 1 ? "" : "s"); + if (num_parent_configs > 0) { + fprintf(opt.outfile, ":\n"); + fprintf(opt.outfile, "-----------------%s", anz_designs == 1 ? "" : "-"); + for (int i = strlen(buf); i > 0; --i) { + fprintf(opt.outfile, "-"); + } + fprintf(opt.outfile, "\n\n"); + fprintf(opt.outfile, "Configuration Files:\n"); + fprintf(opt.outfile, " - %s\n", first_config_file); + for (size_t i = 0; i < num_parent_configs; i++) { + fprintf(opt.outfile, " - %s (parent)\n", parent_configs[i]); + } + } + else { + fprintf(opt.outfile, " in \"%s\":\n", first_config_file); + fprintf(opt.outfile, "-----------------------%s", anz_designs == 1 ? "" : "-"); + for (int i = strlen(first_config_file) + strlen(buf); i > 0; --i) { + fprintf(opt.outfile, "-"); + } } fprintf(opt.outfile, "\n\n"); } - /** * Print configuration file parser errors. * @param bison_args pointer to the parser arguments @@ -104,9 +116,7 @@ int yyerror(pass_to_bison *bison_args, const char *fmt, ...) va_start (ap, fmt); pass_to_bison *bargs = bison_args ? bison_args : current_bison_args; - fprintf(stderr, "%s: %s: line %d: ", PROJECT, - current_config_file_name ? current_config_file_name : "(null)", - yyget_lineno(bargs->lexer_state)); + fprintf(stderr, "%s: %s: line %d: ", PROJECT, bargs->config_file, yyget_lineno(bargs->lexer_state)); vfprintf(stderr, fmt, ap); fputc('\n', stderr); @@ -117,24 +127,54 @@ int yyerror(pass_to_bison *bison_args, const char *fmt, ...) -static design_t *parse_config_file(const char *config_file, size_t *r_num_designs) +static pass_to_bison new_bison_args(const char *config_file) { - #ifdef DEBUG - fprintf (stderr, "Parsing Config File %s ...\n", config_file); - #endif - pass_to_bison bison_args; bison_args.designs = NULL; bison_args.num_designs = 0; bison_args.design_idx = 0; bison_args.config_file = (char *) config_file; + bison_args.sdel = '\"'; /* sdel is shared by flex and bison */ + bison_args.sesc = '\\'; /* sesc is shared by flex and bison */ + bison_args.num_mandatory = 0; + bison_args.time_for_se_check = 0; + bison_args.num_shapespec = 0; + bison_args.skipping = 0; + bison_args.speeding = 0; + bison_args.parent_configs = NULL; + bison_args.num_parent_configs = 0; bison_args.lexer_state = NULL; + return bison_args; +} + + + +static pass_to_flex new_flex_extra_data(pass_to_bison *bison_args) +{ + pass_to_flex flex_extra_data; + flex_extra_data.yyerrcnt = 0; + flex_extra_data.sdel_ptr = &(bison_args->sdel); + flex_extra_data.sesc_ptr = &(bison_args->sesc); + return flex_extra_data; +} + + + +static pass_to_bison parse_config_file(const char *config_file) +{ + #ifdef DEBUG + fprintf (stderr, "Parsing Config File %s ...\n", config_file); + #endif + + pass_to_bison bison_args = new_bison_args(config_file); + pass_to_flex flex_extra_data = new_flex_extra_data(&bison_args); current_bison_args = &bison_args; yylex_init (&(bison_args.lexer_state)); - int rc = open_yy_config_file(&bison_args, config_file); + yylex_init_extra(&flex_extra_data, &(bison_args.lexer_state)); + int rc = open_yy_config_file(&bison_args); if (rc) { - return NULL; + return new_bison_args(config_file); } inflate_inbuf(bison_args.lexer_state, config_file); rc = yyparse(&bison_args); @@ -146,19 +186,126 @@ static design_t *parse_config_file(const char *config_file, size_t *r_num_design } if (rc) { - return NULL; + #ifdef DEBUG + fprintf (stderr, "yyparse() returned %d\n", rc); + #endif + return new_bison_args(config_file); } - *r_num_designs = bison_args.num_designs; - return bison_args.designs; + return bison_args; } -design_t *parse_config_files(const char *first_config_file, size_t *r_num_designs) +static int designs_contain(design_t *designs, size_t num_designs, design_t adesign) { - // TODO much code + int result = adesign.name == NULL; /* broken records count as "present", so we don't copy them */ + if (designs != NULL && num_designs > 0 && adesign.name != NULL) { + for (size_t d = 0; d < num_designs; d++) { + if (strcasecmp(designs[d].name, adesign.name) == 0) { + result = 1; + break; + } + } + } + return result; +} - return parse_config_file(first_config_file, r_num_designs); + + +static int record_parent_config_files(pass_to_bison *bison_args) +{ + if (bison_args->num_parent_configs > 0) { + for (int parent_idx = bison_args->num_parent_configs - 1; parent_idx >= 0; parent_idx--) { + char *parent_file = bison_args->parent_configs[parent_idx]; + int is_new = !array_contains(parent_configs, num_parent_configs, parent_file); + if (is_new) { + parent_configs = (char **) realloc(parent_configs, (num_parent_configs + 1) * sizeof(char *)); + if (parent_configs == NULL) { + return 1; + } + parent_configs[num_parent_configs] = parent_file; + ++num_parent_configs; + } + } + } + BFREE(bison_args->parent_configs); + return 0; +} + + + +static int copy_designs(pass_to_bison *bison_args, design_t **r_result, size_t *r_num_designs) +{ + if (bison_args->num_designs > 0) { + if (*r_result == NULL) { + *r_result = bison_args->designs; + *r_num_designs = bison_args->num_designs; + } + else { + for (size_t d = 0; d < bison_args->num_designs; d++) { + if (!designs_contain(*r_result, *r_num_designs, bison_args->designs[d])) { + *r_result = (design_t *) realloc(*r_result, (*r_num_designs + 1) * sizeof(design_t)); + if (*r_result == NULL) { + perror(PROJECT); + return 1; + } + memcpy(*r_result + *r_num_designs, bison_args->designs + d, sizeof(design_t)); + (*r_num_designs)++; + } + } + BFREE(bison_args->designs); + } + } + return 0; +} + + + +design_t *parse_config_files(const char *p_first_config_file, size_t *r_num_designs) +{ + size_t parents_parsed = -1; /** how many parent config files have already been parsed */ + + design_t *result = NULL; + *r_num_designs = 0; + + first_config_file = p_first_config_file; + const char *config_file = p_first_config_file; + do { + pass_to_bison bison_args = parse_config_file(config_file); + ++parents_parsed; + #ifdef DEBUG + fprintf (stderr, "bison_args returned: " + ".num_parent_configs=%d, .parent_configs=%p, .num_designs=%d, .designs=%p\n", + (int) bison_args.num_parent_configs, bison_args.parent_configs, + (int) bison_args.num_designs, bison_args.designs); + #endif + + if (record_parent_config_files(&bison_args) != 0) { + perror(PROJECT); + return NULL; + } + if (copy_designs(&bison_args, &result, r_num_designs) != 0) { + return NULL; + } + + #ifdef DEBUG + fprintf (stderr, "design count raised to: %d\n", (int) *r_num_designs); + #endif + if (parents_parsed < num_parent_configs) { + config_file = parent_configs[parents_parsed]; + } + } while (parents_parsed < num_parent_configs); + + if (*r_num_designs == 0) { + if (opt.design_choice_by_user) { + fprintf (stderr, "%s: unknown box design -- %s\n", PROJECT, (char *) opt.design); + } + else { + fprintf (stderr, "%s: no valid designs found\n", PROJECT); + } + return NULL; + } + return result; } diff --git a/src/parsing.h b/src/parsing.h index 13d2743..47af1b1 100644 --- a/src/parsing.h +++ b/src/parsing.h @@ -28,20 +28,8 @@ #include "parser.h" -/** file name of config file used */ -extern char *config_file_name; - -/** names of config files specified via "parent" */ -extern char **parent_configs; - -/** number of parent config files (size of parent_configs array) */ -extern size_t num_parent_configs; - - - /** - * Print the header for the design list output, which includes the file name. - * TODO what if more file names used? + * Print the header for the design list output, which includes the file name(s). */ void print_design_list_header();