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

View File

@ -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 <string.h>
#include <sys/stat.h>
#include <unistd.h>
#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]+
%%
<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]+ {
/*
* 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;
}
<PARENT>\n {
BEGIN INITIAL;
<PARENT>\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; }
<SHAPES,ELASTIC>wnw { yylval->shape = WNW; return SHAPE; }
<ELASTIC>\) {
#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];
}
<SHAPES>\} {
#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;
}
<SPEEDMODE>{PBOX}{PWHITE}+{PWORD} {
#ifdef LEXER_DEBUG
fprintf (stderr, "\n STATUS: %s -- STATE INITIAL", yytext);
#endif
yyless (0);
speeding = 0;
BEGIN INITIAL;
<SPEEDMODE>{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");
}
<SPEEDMODE>\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);
}
}

View File

@ -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 <s> KEYWORD
%token <s> WORD
%token <s> STRING
%token <s> FILENAME
%token <shape> SHAPE
%token <num> YNUMBER
%token <c> 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 {

View File

@ -24,73 +24,85 @@
#include "config.h"
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#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;
}

View File

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