Support multiple configuration files #5

Main implementation
This commit is contained in:
Thomas Jensen 2021-03-13 15:18:41 +01:00
parent 46aab3faa6
commit 97b62dd09a
No known key found for this signature in database
GPG Key ID: A4ACEE270D0FB7DB
5 changed files with 381 additions and 205 deletions

View File

@ -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 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 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 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 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 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 getopt.o: misc/getopt.c misc/getopt.h | check_dir

View File

@ -25,10 +25,20 @@
#include "config.h" #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 * 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. * 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 * @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 <string.h> #include <string.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include "shape.h" #include "shape.h"
#define FILE_LEXER_L #define FILE_LEXER_L
#include "boxes.h" #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 */ #define LEX_MAX_WARN 3 /* number of lex errors per design */
static int yyerrcnt = 0; static void report_state_char(char *symbol, char c, char *expected_state_str);
/* the currently active string delimiter character */
static char sdel = '\"';
/* the currently active string escape character */
static char sesc = '\\';
static void report_state(char *symbol, char *text, char *expected_state_str);
%} %}
%option 8bit %option 8bit
%option bison-bridge %option bison-bridge
%option case-insensitive %option case-insensitive
%option ecs %option ecs
%option extra-type="pass_to_flex *"
%option never-interactive %option never-interactive
%option nodefault %option nodefault
%option noinput %option noinput
@ -85,12 +93,12 @@ static char sesc = '\\';
%option yylineno %option yylineno
%x SAMPLE
%x SPEEDMODE
%x DELWORD %x DELWORD
%x PARENT %x PARENT
%s SHAPES %x SAMPLE
%x SPEEDMODE
%s ELASTIC %s ELASTIC
%s SHAPES
PWORD [a-zA-ZäöüÄÖÜ][a-zA-Z0-9\-_üäöÜÄÖß]* PWORD [a-zA-ZäöüÄÖÜ][a-zA-Z0-9\-_üäöÜÄÖß]*
@ -104,24 +112,22 @@ PFILENAME [^\r\n]+
%% %%
<DELWORD,SHAPES,ELASTIC,INITIAL>[ \r\t] /* ignore whitespace */ <INITIAL,DELWORD,ELASTIC,SHAPES>[ \r\t] /* ignore whitespace */
<DELWORD,SHAPES,ELASTIC,INITIAL>\n {} <INITIAL,DELWORD,ELASTIC,SHAPES>\n {}
<DELWORD>[^ \t\r\n]+ { <DELWORD>[^ \t\r\n]+ {
/* /*
* String delimiter spec - like WORD, but allow any character * 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); yylval->s = (char *) strdup (yytext);
if (yylval->s == NULL) { if (yylval->s == NULL) {
perror (PROJECT); perror (PROJECT);
exit (EXIT_FAILURE); exit (EXIT_FAILURE);
} }
BEGIN INITIAL; BEGIN(INITIAL);
report_state("YDELWOR", yytext, "INITIAL");
return YDELWORD; return YDELWORD;
} }
@ -136,7 +142,7 @@ PFILENAME [^\r\n]+
int rest_len = yyleng - 1; /* length of string pointed to by p */ int rest_len = yyleng - 1; /* length of string pointed to by p */
int qcnt = 0; /* esc char count in current string */ 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 */ REJECT; /* that was not our delimiter */
} }
@ -148,35 +154,34 @@ PFILENAME [^\r\n]+
p = yylval->s; p = yylval->s;
while (*p) { while (*p) {
if (*p == sesc) { if (*p == *(yyextra->sesc_ptr)) {
memmove (p, p+1, rest_len); /* incl. '\0' */ memmove (p, p+1, rest_len); /* incl. '\0' */
++qcnt; ++qcnt;
--rest_len; --rest_len;
if (*p == '\0') if (*p == '\0')
break; break;
} }
else if (*p == sdel) { else if (*p == *(yyextra->sdel_ptr)) {
*p = '\0'; *p = '\0';
yyless ((p-yylval->s)+2+qcnt); /* string plus quotes */ yyless ((p-yylval->s)+2+qcnt); /* string plus quotes */
#ifdef LEXER_DEBUG #ifdef LEXER_DEBUG
fprintf (stderr, "\n STRING: \"%s\"", yylval->s); fprintf (stderr, " STRING: \"%s\"\n", yylval->s);
#endif #endif
return STRING; return STRING;
} }
--rest_len; --rest_len;
++p; ++p;
} }
if (yyerrcnt++ < 5) if ((yyextra->yyerrcnt)++ < 5) {
yyerror(NULL, "Unterminated String -- %s", yytext); yyerror(NULL, "Unterminated String -- %s", yytext);
}
return YUNREC; return YUNREC;
} }
{PPARENT} { {PPARENT} {
#ifdef LEXER_DEBUG BEGIN(PARENT);
fprintf (stderr, "\nYPARENT: %s", yytext); report_state("YPARENT", yytext, "PARENT");
#endif
BEGIN PARENT;
return YPARENT; return YPARENT;
} }
@ -190,23 +195,20 @@ PFILENAME [^\r\n]+
while ((*p == ' ' || *p == '\t') && p >= yylval->s) { while ((*p == ' ' || *p == '\t') && p >= yylval->s) {
*p-- = '\0'; *p-- = '\0';
} }
#ifdef LEXER_DEBUG BEGIN(INITIAL);
fprintf (stderr, "\n STRING: \"%s\"", yylval->s); report_state("FILENAM", yylval->s, "INITIAL");
#endif return FILENAME;
BEGIN INITIAL;
return STRING;
} }
<PARENT>\n { <PARENT>\r?\n {
BEGIN INITIAL; /* This is triggered only when no parent filename was specified. */
BEGIN(INITIAL);
report_state(" NL", "", "INITIAL");
} }
Sample { Sample {
#ifdef LEXER_DEBUG BEGIN(SAMPLE);
fprintf (stderr, "\nYSAMPLE: %s -- STATE SAMPLE", yytext); report_state("YSAMPLE", yytext, "SAMPLE");
#endif
BEGIN SAMPLE;
return YSAMPLE; return YSAMPLE;
} }
@ -235,19 +237,18 @@ Sample {
len = p - yytext; /* yyless(n): push back all but the first n */ len = p - yytext; /* yyless(n): push back all but the first n */
yyless (len); /* allow him to return YENDSAMPLE */ 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); btrim (yylval->s, &len);
if (len > 0) { if (len > 0) {
strcat (yylval->s, "\n"); /* memory was allocated with strdup */ strcat (yylval->s, "\n"); /* memory was allocated with strdup */
#ifdef LEXER_DEBUG BEGIN(INITIAL);
fprintf (stderr, "\n STRING: \"%s\" -- STATE INITIAL", yylval->s); report_state(" STRING", yylval->s, "INITIAL");
#endif
BEGIN INITIAL;
return STRING; return STRING;
} }
else { else {
if (yyerrcnt++ < 5) if ((yyextra->yyerrcnt)++ < 5) {
yyerror(NULL, "SAMPLE block must not be empty"); yyerror(NULL, "SAMPLE block must not be empty");
}
BFREE (yylval->s); BFREE (yylval->s);
return YUNREC; return YUNREC;
} }
@ -259,7 +260,7 @@ Sample {
ends[ \t\r]*$ { ends[ \t\r]*$ {
#ifdef LEXER_DEBUG #ifdef LEXER_DEBUG
fprintf (stderr, "\nYENDSAM: %s", yytext); fprintf (stderr, "YENDSAM: %s\n", yytext);
#endif #endif
return YENDSAMPLE; return YENDSAMPLE;
} }
@ -267,26 +268,22 @@ ends[ \t\r]*$ {
Elastic { Elastic {
#ifdef LEXER_DEBUG BEGIN(ELASTIC);
fprintf (stderr, "\nYELASTC: %s -- STATE ELASTIC", yytext); report_state("YELASTC", yytext, "ELASTIC");
#endif
BEGIN ELASTIC;
return YELASTIC; return YELASTIC;
} }
Shapes { Shapes {
#ifdef LEXER_DEBUG BEGIN(SHAPES);
fprintf (stderr, "\nYSHAPES: %s -- STATE SHAPES", yytext); report_state("YSHAPES", yytext, "SHAPES");
#endif
BEGIN SHAPES;
return YSHAPES; return YSHAPES;
} }
{PBOX} { {PBOX} {
#ifdef LEXER_DEBUG #ifdef LEXER_DEBUG
fprintf (stderr, "\n YBOX: %s", yytext); fprintf (stderr, " YBOX: %s\n", yytext);
#endif #endif
yyerrcnt = 0; yyextra->yyerrcnt = 0;
return YBOX; return YBOX;
} }
@ -318,18 +315,14 @@ Once { yylval->c = 'o'; return YRXPFLAG; }
<SHAPES,ELASTIC>wnw { yylval->shape = WNW; return SHAPE; } <SHAPES,ELASTIC>wnw { yylval->shape = WNW; return SHAPE; }
<ELASTIC>\) { <ELASTIC>\) {
#ifdef LEXER_DEBUG BEGIN(INITIAL);
fprintf (stderr, "\n SYMBOL: \'%c\' -- STATE INITIAL", yytext[0]); report_state_char("SYMBOL", yytext[0], "INITIAL");
#endif
BEGIN INITIAL;
return yytext[0]; return yytext[0];
} }
<SHAPES>\} { <SHAPES>\} {
#ifdef LEXER_DEBUG BEGIN(INITIAL);
fprintf (stderr, "\n SYMBOL: \'%c\' -- STATE INITIAL", yytext[0]); report_state_char("SYMBOL", yytext[0], "INITIAL");
#endif
BEGIN INITIAL;
return yytext[0]; return yytext[0];
} }
@ -339,7 +332,7 @@ author|designer|tags|created|revision|revdate|indent {
* general key words * general key words
*/ */
#ifdef LEXER_DEBUG #ifdef LEXER_DEBUG
fprintf (stderr, "\nKEYWORD: %s", yytext); fprintf (stderr, "KEYWORD: %s\n", yytext);
#endif #endif
yylval->s = (char *) strdup (yytext); yylval->s = (char *) strdup (yytext);
if (yylval->s == NULL) { if (yylval->s == NULL) {
@ -354,17 +347,15 @@ Delimiter|Delim {
/* /*
* Change string delimiting characters * Change string delimiting characters
*/ */
#ifdef LEXER_DEBUG BEGIN(DELWORD);
fprintf (stderr, "\nYCHGDEL: %s -- STATE DELWORD", yytext); report_state("YCHGDEL", yytext, "DELWORD");
#endif
BEGIN DELWORD;
return YCHGDEL; return YCHGDEL;
} }
{PWORD} { {PWORD} {
#ifdef LEXER_DEBUG #ifdef LEXER_DEBUG
fprintf (stderr, "\n WORD: %s", yytext); fprintf (stderr, " WORD: %s\n", yytext);
#endif #endif
yylval->s = (char *) strdup (yytext); yylval->s = (char *) strdup (yytext);
if (yylval->s == NULL) { if (yylval->s == NULL) {
@ -377,7 +368,7 @@ Delimiter|Delim {
[\+-]?[0-9]+ { [\+-]?[0-9]+ {
#ifdef LEXER_DEBUG #ifdef LEXER_DEBUG
fprintf (stderr, "\nYNUMBER: %s", yytext); fprintf (stderr, "YNUMBER: %s\n", yytext);
#endif #endif
yylval->num = atoi (yytext); yylval->num = atoi (yytext);
return YNUMBER; return YNUMBER;
@ -386,7 +377,7 @@ Delimiter|Delim {
[,(){}] { [,(){}] {
#ifdef LEXER_DEBUG #ifdef LEXER_DEBUG
fprintf (stderr, "\n SYMBOL: \'%c\'", yytext[0]); fprintf (stderr, " SYMBOL: \'%c\'\n", yytext[0]);
#endif #endif
return yytext[0]; return yytext[0];
} }
@ -395,25 +386,23 @@ Delimiter|Delim {
#.*$ { #.*$ {
/* ignore comments */ /* ignore comments */
#ifdef LEXER_DEBUG #ifdef LEXER_DEBUG
fprintf (stderr, "\nCOMMENT: %s", yytext+1); fprintf (stderr, "COMMENT: %s\n", yytext+1);
#endif #endif
} }
. { . {
if (yyerrcnt++ < LEX_MAX_WARN) if ((yyextra->yyerrcnt)++ < LEX_MAX_WARN)
yyerror(NULL, "Unrecognized input char \'%s\'", yytext); yyerror(NULL, "Unrecognized input char \'%s\'", yytext);
return YUNREC; return YUNREC;
} }
<SPEEDMODE>{PBOX}{PWHITE}+{PWORD} { <SPEEDMODE>{PBOX}{PWHITE}+{PWORD}|{PPARENT} {
#ifdef LEXER_DEBUG /* end speedmode, but then give back the whole match so BOX or PARENT can be started properly */
fprintf (stderr, "\n STATUS: %s -- STATE INITIAL", yytext); yyless(0);
#endif BEGIN(INITIAL);
yyless (0); report_state(" STATUS", "", "INITIAL");
speeding = 0;
BEGIN INITIAL;
} }
<SPEEDMODE>\n {} <SPEEDMODE>\n {}
@ -424,11 +413,7 @@ Delimiter|Delim {
%% %%
/* TODO These functions should go away. The cast to (struct yyguts_t *) is a hack. void inflate_inbuf(void *yyscanner, const char *configfile)
* 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)
{ {
struct stat sinf; struct stat sinf;
@ -436,31 +421,42 @@ void inflate_inbuf(void *scanner, const char *configfile)
perror (PROJECT); perror (PROJECT);
exit (EXIT_FAILURE); exit (EXIT_FAILURE);
} }
struct yyguts_t *yyg = (struct yyguts_t *) scanner; struct yyguts_t *yyg = (struct yyguts_t *) yyscanner;
yy_delete_buffer(YY_CURRENT_BUFFER, scanner); yy_delete_buffer(YY_CURRENT_BUFFER, yyscanner);
yy_switch_to_buffer (yy_create_buffer(yyin, sinf.st_size+10, scanner), scanner); 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 struct yyguts_t *yyg = (struct yyguts_t *) yyscanner;
fprintf (stderr, "\n STATUS: begin_speedmode() -- STATE SPEEDMODE"); BEGIN(SPEEDMODE);
#endif report_state(" STATUS", "begin_speedmode()", "SPEEDMODE");
struct yyguts_t *yyg = (struct yyguts_t *) scanner;
BEGIN 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 #ifdef LEXER_DEBUG
fprintf (stderr, "\n STATUS: chg_strdelims ('%c', '%c')", asesc, asdel); lexerDebug = 1;
#endif #endif
sesc = asesc; if (lexerDebug) {
sdel = asdel; fprintf(stderr, "%7s: %s -- STATE %s\n", symbol, text, expected_state_str);
}
} }

View File

@ -25,7 +25,6 @@
#include "boxes.h" #include "boxes.h"
extern int speeding;
/** all the arguments which we pass to the bison parser */ /** all the arguments which we pass to the bison parser */
typedef struct { typedef struct {
@ -41,6 +40,31 @@ typedef struct {
/** the path to the config file we are parsing */ /** the path to the config file we are parsing */
char *config_file; 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 */ /** the flex scanner, which is explicitly passed to reentrant bison */
void *lexer_state; void *lexer_state;
} pass_to_bison; } pass_to_bison;
@ -62,13 +86,13 @@ typedef struct {
#include "parser.h" #include "parser.h"
#include "lex.yy.h" #include "lex.yy.h"
/* /*
* Valid characters to be used as string delimiters. Note that the * Valid characters to be used as string delimiters. Note that the
* following list must correspond to the DELIM definition in lexer.l. * following list must correspond to the DELIM definition in lexer.l.
*/ */
#define LEX_SDELIM "\"~'`!@%&*=:;<>?/|.\\" #define LEX_SDELIM "\"~'`!@%&*=:;<>?/|.\\"
/** required for bison-flex bridge */ /** required for bison-flex bridge */
#define scanner bison_args->lexer_state #define scanner bison_args->lexer_state
@ -76,14 +100,6 @@ typedef struct {
#define curdes (bison_args->designs[bison_args->design_idx]) #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) 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) static void recover(pass_to_bison *bison_args)
/* /*
* Reset parser to neutral state, so a new design can be parsed. * 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; bison_args->num_mandatory = 0;
time_for_se_check = 0; bison_args->time_for_se_check = 0;
num_shapespec = 0; bison_args->num_shapespec = 0;
chg_strdelims ('\\', '\"'); chg_strdelims(bison_args, '\\', '\"');
/* /*
* Clear current design * Clear current design
@ -358,7 +385,6 @@ static int design_needed (const char *name, const int design_idx)
|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|_*/ |__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|__|_*/
%define api.pure true %define api.pure true
%pure-parser
%lex-param {void *scanner} %lex-param {void *scanner}
%parse-param {pass_to_bison *bison_args} %parse-param {pass_to_bison *bison_args}
@ -375,6 +401,7 @@ static int design_needed (const char *name, const int design_idx)
%token <s> KEYWORD %token <s> KEYWORD
%token <s> WORD %token <s> WORD
%token <s> STRING %token <s> STRING
%token <s> FILENAME
%token <shape> SHAPE %token <shape> SHAPE
%token <num> YNUMBER %token <num> YNUMBER
%token <c> YRXPFLAG %token <c> YRXPFLAG
@ -410,6 +437,7 @@ first_rule:
YYABORT; YYABORT;
} }
bison_args->num_designs = 1; bison_args->num_designs = 1;
bison_args->designs->indentmode = DEF_INDENTMODE; bison_args->designs->indentmode = DEF_INDENTMODE;
bison_args->designs->defined_in = bison_args->config_file; bison_args->designs->defined_in = bison_args->config_file;
} }
@ -424,14 +452,11 @@ config_file
if (bison_args->design_idx == 0) { if (bison_args->design_idx == 0) {
BFREE (bison_args->designs); BFREE (bison_args->designs);
bison_args->num_designs = 0; bison_args->num_designs = 0;
if (opt.design_choice_by_user) { if (!opt.design_choice_by_user && bison_args->num_parent_configs == 0) {
fprintf (stderr, "%s: unknown box design -- %s\n", fprintf (stderr, "%s: no valid data in config file -- %s\n", PROJECT, bison_args->config_file);
PROJECT, (char *) opt.design); YYABORT;
} }
else { YYACCEPT;
yyerror(bison_args, "no valid designs found");
}
YYABORT;
} }
--(bison_args->design_idx); --(bison_args->design_idx);
@ -444,21 +469,21 @@ config_file
bison_args->designs = tmp; bison_args->designs = tmp;
} }
parent_def: YPARENT STRING parent_def: YPARENT FILENAME
{ {
char *filepath = $2; char *filepath = $2;
#ifdef PARSER_DEBUG #ifdef PARSER_DEBUG
fprintf (stderr, "parent config file specified: [%s]\n", filepath); fprintf (stderr, "parent config file specified: [%s]\n", filepath);
#endif #endif
if (filepath == NULL || filepath[0] == '\0') { if (filepath == NULL || filepath[0] == '\0') {
skipping = 1; bison_args->skipping = 1;
yyerror(bison_args, "parent reference is empty"); yyerror(bison_args, "parent reference is empty");
YYERROR; YYERROR;
} }
else if (strcasecmp(filepath, ":global:") == 0) { /* special token */ else if (strcasecmp(filepath, ":global:") == 0) { /* special token */
filepath = discover_config_file(1); filepath = discover_config_file(1);
if (filepath == NULL) { 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(bison_args, "parent reference to global config which cannot be found");
YYERROR; YYERROR;
} }
@ -466,7 +491,7 @@ parent_def: YPARENT STRING
else { else {
FILE *f = fopen(filepath, "r"); FILE *f = fopen(filepath, "r");
if (f == NULL) { if (f == NULL) {
skipping = 1; bison_args->skipping = 1;
yyerror(bison_args, "parent config file not found: %s", filepath); yyerror(bison_args, "parent config file not found: %s", filepath);
YYERROR; YYERROR;
} }
@ -478,16 +503,17 @@ parent_def: YPARENT STRING
fprintf (stderr, "parent config file path resolved: [%s]\n", filepath); fprintf (stderr, "parent config file path resolved: [%s]\n", filepath);
#endif #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) { if (!is_new) {
#ifdef PARSER_DEBUG #ifdef PARSER_DEBUG
fprintf (stderr, "duplicate parent / cycle: [%s]\n", filepath); fprintf (stderr, "duplicate parent / cycle: [%s]\n", filepath);
#endif #endif
} }
else { else {
parent_configs = realloc(parent_configs, (num_parent_configs + 1) * sizeof(char *)); bison_args->parent_configs = realloc(bison_args->parent_configs,
parent_configs[num_parent_configs] = filepath; (bison_args->num_parent_configs + 1) * sizeof(char *));
++num_parent_configs; 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 design_or_error: design | error
{ {
if (!speeding && !skipping) { if (!(bison_args->speeding) && !(bison_args->skipping)) {
recover(bison_args); recover(bison_args);
yyerror(bison_args, "skipping to next design"); yyerror(bison_args, "skipping to next design");
skipping = 1; bison_args->skipping = 1;
} }
} }
design: YBOX WORD design: YBOX WORD
{ {
chg_strdelims ('\\', '\"'); chg_strdelims(bison_args, '\\', '\"');
skipping = 0; bison_args->speeding = 0;
bison_args->skipping = 0;
if (!design_needed ($2, bison_args->design_idx)) { if (!design_needed ($2, bison_args->design_idx)) {
speeding = 1; bison_args->speeding = 1;
begin_speedmode(scanner); begin_speedmode(scanner);
YYERROR; YYERROR;
} }
@ -529,7 +556,7 @@ layout YEND WORD
yyerror(bison_args, "box design name differs at BOX and END"); yyerror(bison_args, "box design name differs at BOX and END");
YYERROR; YYERROR;
} }
if (num_mandatory < 3) { if (bison_args->num_mandatory < 3) {
yyerror(bison_args, "entries SAMPLE, SHAPES, and ELASTIC are mandatory"); yyerror(bison_args, "entries SAMPLE, SHAPES, and ELASTIC are mandatory");
YYERROR; YYERROR;
} }
@ -556,9 +583,9 @@ layout YEND WORD
perror (PROJECT); perror (PROJECT);
YYABORT; YYABORT;
} }
num_mandatory = 0; bison_args->num_mandatory = 0;
time_for_se_check = 0; bison_args->time_for_se_check = 0;
num_shapespec = 0; bison_args->num_shapespec = 0;
/* /*
* Check if we need to continue parsing. If not, return. * 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 | YCHGDEL YDELWORD
{ {
if (strlen($2) != 2) { if (strlen($2) != 2) {
@ -670,7 +715,7 @@ entry: KEYWORD STRING
($2)[1], LEX_SDELIM); ($2)[1], LEX_SDELIM);
YYERROR; YYERROR;
} }
chg_strdelims ($2[0], $2[1]); chg_strdelims(bison_args, $2[0], $2[1]);
} }
| WORD STRING | WORD STRING
@ -704,7 +749,7 @@ block: YSAMPLE STRING YENDSAMPLE
} }
curdes.sample = line; curdes.sample = line;
++num_mandatory; ++(bison_args->num_mandatory);
} }
| YSHAPES '{' slist '}' | YSHAPES '{' slist '}'
@ -719,7 +764,7 @@ block: YSAMPLE STRING YENDSAMPLE
/* /*
* At least one shape must be specified * 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(bison_args, "must specify at least one non-empty shape per design");
YYERROR; YYERROR;
} }
@ -808,8 +853,8 @@ block: YSAMPLE STRING YENDSAMPLE
YYERROR; YYERROR;
} }
++num_mandatory; ++(bison_args->num_mandatory);
if (++time_for_se_check > 1) { if (++(bison_args->time_for_se_check) > 1) {
if (perform_se_check(bison_args) != 0) { if (perform_se_check(bison_args) != 0) {
YYERROR; YYERROR;
} }
@ -863,8 +908,8 @@ block: YSAMPLE STRING YENDSAMPLE
| YELASTIC '(' elist ')' | YELASTIC '(' elist ')'
{ {
++num_mandatory; ++(bison_args->num_mandatory);
if (++time_for_se_check > 1) { if (++(bison_args->time_for_se_check) > 1) {
if (perform_se_check(bison_args) != 0) { if (perform_se_check(bison_args) != 0) {
YYERROR; YYERROR;
} }
@ -970,7 +1015,7 @@ slist_entry: SHAPE shape_def
if (isempty (curdes.shape + $1)) { if (isempty (curdes.shape + $1)) {
curdes.shape[$1] = $2; curdes.shape[$1] = $2;
if (!isdeepempty(&($2))) { if (!isdeepempty(&($2))) {
++num_shapespec; ++(bison_args->num_shapespec);
} }
} }
else { else {

View File

@ -24,73 +24,85 @@
#include "config.h" #include "config.h"
#include <stdarg.h> #include <stdarg.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <strings.h>
#include "boxes.h" #include "boxes.h"
#include "parsing.h" #include "parsing.h"
#include "parser.h" #include "parser.h"
#include "lex.yy.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 */ /** file handle of the config file currently being parsed */
static FILE *current_config_handle = NULL; 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 */ /** the arguments passed to our current invocation of the parser */
static pass_to_bison *current_bison_args = NULL; 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 bison_args the bison args that we set up in the calling function (contains config file path)
* @param config_file_path the new file name to set * @return 0 if successful (yyin is set)
* @return 0 if successful (yyin and current_config_file_name are set)
* != 0 on error (yyin is unmodified) * != 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) { 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; return 1;
} }
current_config_file_name = (char *) config_file_path;
yyset_in(current_config_handle, bison_args->lexer_state); 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; return 0;
} }
void print_design_list_header() void print_design_list_header()
{ {
char buf[42]; char buf[42];
sprintf(buf, "%d", anz_designs); sprintf(buf, "%d", anz_designs);
fprintf(opt.outfile, "%d Available Style%s in \"%s\":\n", fprintf(opt.outfile, "%d Available Style%s", anz_designs, anz_designs == 1 ? "" : "s");
anz_designs, anz_designs == 1 ? "" : "s", current_config_file_name); if (num_parent_configs > 0) {
fprintf(opt.outfile, "-----------------------%s", fprintf(opt.outfile, ":\n");
anz_designs == 1 ? "" : "-"); fprintf(opt.outfile, "-----------------%s", anz_designs == 1 ? "" : "-");
for (int i = strlen(current_config_file_name) + strlen(buf); i > 0; --i) { for (int i = strlen(buf); i > 0; --i) {
fprintf(opt.outfile, "-"); 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"); fprintf(opt.outfile, "\n\n");
} }
/** /**
* Print configuration file parser errors. * Print configuration file parser errors.
* @param bison_args pointer to the parser arguments * @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); va_start (ap, fmt);
pass_to_bison *bargs = bison_args ? bison_args : current_bison_args; pass_to_bison *bargs = bison_args ? bison_args : current_bison_args;
fprintf(stderr, "%s: %s: line %d: ", PROJECT, fprintf(stderr, "%s: %s: line %d: ", PROJECT, bargs->config_file, yyget_lineno(bargs->lexer_state));
current_config_file_name ? current_config_file_name : "(null)",
yyget_lineno(bargs->lexer_state));
vfprintf(stderr, fmt, ap); vfprintf(stderr, fmt, ap);
fputc('\n', stderr); 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; pass_to_bison bison_args;
bison_args.designs = NULL; bison_args.designs = NULL;
bison_args.num_designs = 0; bison_args.num_designs = 0;
bison_args.design_idx = 0; bison_args.design_idx = 0;
bison_args.config_file = (char *) config_file; 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; 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; current_bison_args = &bison_args;
yylex_init (&(bison_args.lexer_state)); 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) { if (rc) {
return NULL; return new_bison_args(config_file);
} }
inflate_inbuf(bison_args.lexer_state, config_file); inflate_inbuf(bison_args.lexer_state, config_file);
rc = yyparse(&bison_args); 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) { 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;
return bison_args.designs;
} }
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;
} }

View File

@ -28,20 +28,8 @@
#include "parser.h" #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. * Print the header for the design list output, which includes the file name(s).
* TODO what if more file names used?
*/ */
void print_design_list_header(); void print_design_list_header();