Enable lexer and parser to handle UTF-8 config file #72

This commit is contained in:
Thomas Jensen 2023-04-07 21:10:00 +02:00
parent 4ff37eb5ba
commit 8a7bb8039f
No known key found for this signature in database
GPG Key ID: A4ACEE270D0FB7DB
5 changed files with 450 additions and 327 deletions

1
.gitattributes vendored
View File

@ -25,5 +25,4 @@ tools/VERIFICATION.txt text eol=crlf
/test/*.txt text eol=lf
# some files which are currently in ISO-8859-15 encoding
/src/lexer.l text working-tree-encoding=ISO_8859-15
/test/111_manual_encoding_iso.txt text working-tree-encoding=ISO_8859-15

View File

@ -19,7 +19,7 @@
*/
#include "config.h"
#include "bxstring.h"
typedef struct {
int yyerrcnt;
@ -33,8 +33,8 @@ typedef struct {
/*
* Valid characters to be used as string delimiters. Note that the
* following list must correspond to the SDELIM definition below.
* Valid characters to be used as string delimiters.
* The following list must correspond to the SDELIM definition below.
*/
#define LEX_SDELIM "\"~'`!@%&*=:;<>?/|.\\"
#define LEX_SDELIM_RECOMMENDED "\"~'!|"
@ -50,7 +50,7 @@ typedef struct {
* @param yyscanner pointer to the scanner data block
* @param configfile the path to the config file we are reading
*/
void inflate_inbuf(void *yyscanner, const char *configfile);
void inflate_inbuf(void *yyscanner, const bxstr_t *configfile);
}
@ -58,12 +58,14 @@ void inflate_inbuf(void *yyscanner, const char *configfile);
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <unitypes.h>
#include "boxes.h"
#include "shape.h"
#include "tools.h"
#include "parsing.h"
#include "parser.h"
#include "unicode.h"
#define LEX_MAX_WARN 3 /* number of lex errors per design */
@ -99,8 +101,23 @@ static int change_string_delimiters(pass_to_flex *extra, char *delim_expr);
%x DELIMSPEC
%x PARENT
/*
* The following paragraph contains patterns to recognize UTF-8 characters from a byte stream, based on
* - https://stackoverflow.com/a/10253320/1005481 by Zack Weinberg (under CC-BY-SA 3.0 license)
* - https://www.w3.org/2005/03/23-lex-U by Eric Prud'hommeaux, W3C (under the W3C Document License)
*/
PBOM \xEF\xBB\xBF
U2A [\xC2-\xDF][\x80-\xBF]
U2B \xE0[\xA0-\xBF][\x80-\xBF]
U3A [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}
U3B \xED[\x80-\x9F][\x80-\xBF]
U4A \xF0[\x90-\xBF][\x80-\xBF]{2}
U4B [\xF1-\xF3][\x80-\xBF]{3}
U4C \xF4[\x80-\x8F][\x80-\xBF]{2}
UTF_8 {U2A}|{U2B}|{U3A}|{U3B}|{U4A}|{U4B}|{U4C}
PWORD [a-zA-ZäöüÄÖÜ][a-zA-Z0-9\-_üäöÜÄÖß]*
PWORD (?:[a-zA-Z]|{UTF_8})(?:[a-zA-Z0-9_-]|{UTF_8})*
PASCII_ID [a-zA-Z][a-zA-Z0-9_-]*
PWHITE [ \t\r\n]
SDELIM [\"~\'`!@\%\&\*=:;<>\?/|\.\\]
PPARENT parent
@ -116,16 +133,17 @@ PFILENAME [^\r\n]+
*/
<INITIAL,BOX,DELIMSPEC,ELASTIC,SHAPES>{PWHITE} /* ignore whitespace */
<INITIAL,BOX,DELIMSPEC,ELASTIC,SHAPES>{PWHITE}|{PBOM} /* ignore whitespace and a byte order mark */
<DELIMSPEC>[^ \t\r\n]+ {
/*
* String delimiter spec - like WORD, but allow any character
*/
yylval->s = (char *) strdup (yytext);
yylval->s = bxs_from_ascii("IGNORED");
char *str = (char *) strdup(yytext);
BEGIN(BOX);
report_state("YDELIMS", yytext, "INITIAL");
if (change_string_delimiters(yyextra, yylval->s) != 0) {
report_state("YDELIMS", str, "INITIAL");
if (change_string_delimiters(yyextra, str) != 0) {
return YUNREC;
}
return YDELIMSPEC;
@ -137,7 +155,6 @@ PFILENAME [^\r\n]+
* Strings -- first match everything starting from a potential string delimiter until the end of the line. We
* will give back what we don't need and also detect unterminated strings. Strings always end on the same line.
*/
char *p;
int rest_len = yyleng - 1; /* length of string pointed to by p */
int qcnt = 0; /* esc char count in current string */
@ -145,27 +162,33 @@ PFILENAME [^\r\n]+
REJECT; /* that was not our delimiter */
}
yylval->s = (char *) strdup (yytext + 1);
if (yylval->s == NULL) {
char *str = (char *) strdup(yytext + 1);
if (str == NULL) {
perror (PROJECT);
exit (EXIT_FAILURE);
}
p = yylval->s;
char *p = str;
while (*p) {
if (*p == yyextra->sesc) {
memmove (p, p+1, rest_len); /* incl. '\0' */
++qcnt;
--rest_len;
if (*p == '\0')
if (*p == '\0') {
break;
}
}
else if (*p == yyextra->sdel) {
*p = '\0';
yyless ((p-yylval->s)+2+qcnt); /* string plus quotes */
yyless ((p - str) + 2 + qcnt); /* string plus quotes */
#ifdef LEXER_DEBUG
fprintf (stderr, " STRING: \"%s\"\n", yylval->s);
fprintf (stderr, " STRING: \"%s\"\n", str);
#endif
uint32_t *utf8 = u32_strconv_from_arg(str, CONFIG_FILE_ENCODING);
yylval->s = bxs_from_unicode(utf8);
BFREE(utf8);
BFREE(str);
return STRING;
}
--rest_len;
@ -174,6 +197,7 @@ PFILENAME [^\r\n]+
if ((yyextra->yyerrcnt)++ < 5) {
yyerror(NULL, "Unterminated String -- %s", yytext);
}
BFREE(str);
return YUNREC;
}
@ -185,17 +209,15 @@ PFILENAME [^\r\n]+
}
<PARENT>{PFILENAME} {
char *p = yytext;
while (*p == ' ' || *p == '\t') {
++p;
}
yylval->s = (char *) strdup (p);
p = yylval->s + strlen(yylval->s) - 1;
while ((*p == ' ' || *p == '\t') && p >= yylval->s) {
*p-- = '\0';
}
uint32_t *utf8 = u32_strconv_from_arg(yytext, CONFIG_FILE_ENCODING);
bxstr_t *bxstr = bxs_from_unicode(utf8);
yylval->s = bxs_trim(bxstr);
BFREE(utf8);
bxs_free(bxstr);
BEGIN(INITIAL);
report_state("FILENAM", yylval->s, "INITIAL");
report_state("FILENAM", bxs_to_output(yylval->s), "INITIAL");
return FILENAME;
}
@ -225,27 +247,37 @@ PFILENAME [^\r\n]+
--p; /* skip trailing whitespace */
p -= 2; /* almost skip "ends" statement */
*p = '\0'; /* p now points to 'n' */
yylval->s = (char *) strdup (yytext);
if (yylval->s == NULL) {
char *sample = (char *) strdup(yytext);
if (sample == NULL) {
perror (PROJECT);
exit (EXIT_FAILURE);
}
*p-- = 'n';
len = p - yytext; /* yyless(n): push back all but the first n */
yyless (len); /* allow him to return YENDSAMPLE */
yyless (len); /* allow the lexer to return YENDSAMPLE */
yylval->s[len] = '\n'; /* replace 'e' with newline */
btrim (yylval->s, &len);
sample[len] = '\n'; /* replace 'e' with newline */
btrim(sample, &len);
if (len > 0) {
strcat (yylval->s, "\n"); /* memory was allocated with strdup */
uint32_t *utf8 = u32_strconv_from_arg(sample, CONFIG_FILE_ENCODING);
uint32_t *nl = u32_strconv_from_arg("\n", CONFIG_FILE_ENCODING);
bxstr_t *bxstr = bxs_from_unicode(utf8);
bxstr_t *bxstr2 = bxs_rtrim(bxstr);
bxs_free(bxstr);
bxstr = bxs_strcat(bxstr2, nl);
BFREE(nl);
BFREE(utf8);
BFREE(sample);
bxs_free(bxstr2);
yylval->s = bxstr;
return STRING;
}
else {
if ((yyextra->yyerrcnt)++ < 5) {
yyerror(NULL, "SAMPLE block must not be empty");
}
BFREE (yylval->s);
BFREE(sample);
return YUNREC;
}
}
@ -339,8 +371,8 @@ PFILENAME [^\r\n]+
#ifdef LEXER_DEBUG
fprintf (stderr, "KEYWORD: %s\n", yytext);
#endif
yylval->s = (char *) strdup (yytext);
if (yylval->s == NULL) {
yylval->ascii = strdup(yytext);
if (yylval->ascii == NULL) {
perror (PROJECT);
exit (EXIT_FAILURE);
}
@ -357,19 +389,35 @@ PFILENAME [^\r\n]+
return YCHGDEL;
}
<INITIAL,BOX>{PASCII_ID} {
/*
* a free-floating word which is not a string, i.e. it does not have delimiting characters (ASCII version)
*/
yylval->ascii = strdup(yytext);
if (yylval->ascii == NULL) {
perror (PROJECT);
exit (EXIT_FAILURE);
}
#ifdef LEXER_DEBUG
fprintf (stderr, "ASCIIID: %s\n", yylval->ascii);
#endif
return ASCII_ID;
}
<INITIAL,BOX>{PWORD} {
/*
* a free-floating word which is not a string, i.e. it does not have delimiting characters
*/
#ifdef LEXER_DEBUG
fprintf (stderr, " WORD: %s\n", yytext);
#endif
yylval->s = (char *) strdup (yytext);
uint32_t *utf8 = u32_strconv_from_arg(yytext, CONFIG_FILE_ENCODING);
yylval->s = bxs_from_unicode(utf8);
if (yylval->s == NULL) {
perror (PROJECT);
exit (EXIT_FAILURE);
}
#ifdef LEXER_DEBUG
fprintf (stderr, " WORD: %s\n", u32_strconv_to_output(utf8));
#endif
BFREE(utf8);
return WORD;
}
@ -411,14 +459,17 @@ PFILENAME [^\r\n]+
%%
void inflate_inbuf(void *yyscanner, const char *configfile)
void inflate_inbuf(void *yyscanner, const bxstr_t *configfile)
{
struct stat sinf;
if (stat(configfile, &sinf)) {
char *utf8 = u32_strconv_to_arg(configfile->memory, "UTF-8");
if (stat(utf8, &sinf)) {
perror (PROJECT);
BFREE(utf8);
exit (EXIT_FAILURE);
}
BFREE(utf8);
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);

View File

@ -18,21 +18,25 @@
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unictype.h>
#include <unistr.h>
#include "discovery.h"
#include "tools.h"
#include "regulex.h"
#include "unicode.h"
#include "query.h"
#include "parsecode.h"
#include "parsing.h"
#include "parser.h"
#include "lex.yy.h"
#include "query.h"
#include "regulex.h"
#include "tools.h"
#include "unicode.h"
#include "parser.h"
#include "lex.yy.h"
static pcre2_code *eol_pattern = NULL;
@ -53,19 +57,19 @@ static int check_sizes(pass_to_bison *bison_args)
int i, j, k;
#ifdef PARSER_DEBUG
fprintf (stderr, " Parser: check_sizes()\n");
fprintf(stderr, " Parser: check_sizes()\n");
#endif
for (i=0; i<NUM_SIDES; ++i) {
for (i = 0; i < NUM_SIDES; ++i) {
if (i == 0 || i == 2) {
/*
* horizontal
*/
for (j=0; j<SHAPES_PER_SIDE-1; ++j) {
for (j = 0; j < SHAPES_PER_SIDE - 1; ++j) {
if (curdes.shape[sides[i][j]].height == 0) {
continue;
}
for (k=j+1; k<SHAPES_PER_SIDE; ++k) {
for (k = j + 1; k < SHAPES_PER_SIDE; ++k) {
if (curdes.shape[sides[i][k]].height == 0) {
continue;
}
@ -83,11 +87,11 @@ static int check_sizes(pass_to_bison *bison_args)
/*
* vertical
*/
for (j=0; j<SHAPES_PER_SIDE-1; ++j) {
for (j = 0; j < SHAPES_PER_SIDE - 1; ++j) {
if (curdes.shape[sides[i][j]].width == 0) {
continue;
}
for (k=j+1; k<SHAPES_PER_SIDE; ++k) {
for (k = j + 1; k < SHAPES_PER_SIDE; ++k) {
if (curdes.shape[sides[i][k]].width == 0) {
continue;
}
@ -103,7 +107,7 @@ static int check_sizes(pass_to_bison *bison_args)
}
}
return 0; /* all clear */
return 0; /* all clear */
}
@ -121,17 +125,17 @@ static int corner_check(pass_to_bison *bison_args)
int c;
#ifdef PARSER_DEBUG
fprintf (stderr, " Parser: corner_check()\n");
fprintf(stderr, " Parser: corner_check()\n");
#endif
for (c=0; c<NUM_CORNERS; ++c) {
for (c = 0; c < NUM_CORNERS; ++c) {
if (curdes.shape[corners[c]].elastic) {
yyerror(bison_args, "Corners may not be elastic (%s)", shape_name[corners[c]]);
return 1;
}
}
return 0; /* all clear */
return 0; /* all clear */
}
@ -141,16 +145,16 @@ static shape_t non_existent_elastics(pass_to_bison *bison_args)
shape_t i;
#ifdef PARSER_DEBUG
fprintf (stderr, " Parser: non_existent_elastics()\n");
fprintf(stderr, " Parser: non_existent_elastics()\n");
#endif
for (i=0; i<NUM_SHAPES; ++i) {
if (curdes.shape[i].elastic && isempty(curdes.shape+i)) {
for (i = 0; i < NUM_SHAPES; ++i) {
if (curdes.shape[i].elastic && isempty(curdes.shape + i)) {
return i;
}
}
return (shape_t) NUM_SHAPES; /* all elastic shapes exist */
return (shape_t) NUM_SHAPES; /* all elastic shapes exist */
}
@ -160,21 +164,21 @@ static int insufficient_elasticity(pass_to_bison *bison_args)
int i, j, ef;
#ifdef PARSER_DEBUG
fprintf (stderr, " Parser: insufficient_elasticity()\n");
fprintf(stderr, " Parser: insufficient_elasticity()\n");
#endif
for (i=0; i<NUM_SIDES; ++i) {
for (j=1,ef=0; j<4; ++j) {
for (i = 0; i < NUM_SIDES; ++i) {
for (j = 1, ef = 0; j < 4; ++j) {
if (curdes.shape[sides[i][j]].elastic) {
++ef;
}
}
if (ef != 1 && ef != 2) {
return 1; /* error */
return 1; /* error */
}
}
return 0; /* all clear */
return 0; /* all clear */
}
@ -184,19 +188,20 @@ static int adjoining_elastics(pass_to_bison *bison_args)
int i, j, ef;
#ifdef PARSER_DEBUG
fprintf (stderr, " Parser: adjoining_elastics()\n");
fprintf(stderr, " Parser: adjoining_elastics()\n");
#endif
for (i=0; i<NUM_SIDES; ++i) {
for (i = 0; i < NUM_SIDES; ++i) {
ef = 0;
for (j=1; j<4; ++j) {
if (isempty(curdes.shape+sides[i][j])) {
for (j = 1; j < 4; ++j) {
if (isempty(curdes.shape + sides[i][j])) {
continue;
}
if (curdes.shape[sides[i][j]].elastic) {
if (ef) {
return 1; /* error detected */
} else {
return 1; /* error detected */
}
else {
ef = 1;
}
}
@ -206,7 +211,7 @@ static int adjoining_elastics(pass_to_bison *bison_args)
}
}
return 0; /* all clear */
return 0; /* all clear */
}
@ -217,8 +222,7 @@ int perform_se_check(pass_to_bison *bison_args)
s_rc = non_existent_elastics(bison_args);
if (s_rc != NUM_SHAPES) {
yyerror(bison_args, "Shape %s has been specified as elastic but doesn't exist",
shape_name[s_rc]);
yyerror(bison_args, "Shape %s has been specified as elastic but doesn't exist", shape_name[s_rc]);
return 1;
}
@ -244,7 +248,7 @@ int perform_se_check(pass_to_bison *bison_args)
static void init_design(pass_to_bison *bison_args, design_t *design)
{
memset (design, 0, sizeof(design_t));
memset(design, 0, sizeof(design_t));
design->aliases = (char **) calloc(1, sizeof(char *));
design->indentmode = DEF_INDENTMODE;
design->defined_in = bison_args->config_file;
@ -259,23 +263,23 @@ static void init_design(pass_to_bison *bison_args, design_t *design)
*/
void recover(pass_to_bison *bison_args)
{
bison_args->num_mandatory = 0;
bison_args->time_for_se_check = 0;
bison_args->num_shapespec = 0;
bison_args->num_mandatory = 0;
bison_args->time_for_se_check = 0;
bison_args->num_shapespec = 0;
/*
* Clear current design
*/
BFREE (curdes.name);
BFREE (curdes.author);
BFREE (curdes.aliases);
BFREE (curdes.designer);
BFREE (curdes.created);
BFREE (curdes.revision);
BFREE (curdes.revdate);
BFREE (curdes.sample);
BFREE (curdes.tags);
init_design(bison_args, &(curdes));
/*
* Clear current design
*/
BFREE(curdes.name);
BFREE(curdes.author);
BFREE(curdes.aliases);
BFREE(curdes.designer);
BFREE(curdes.created);
BFREE(curdes.revision);
BFREE(curdes.revdate);
BFREE(curdes.sample);
BFREE(curdes.tags);
init_design(bison_args, &(curdes));
}
@ -370,23 +374,23 @@ static int alias_exists_in_child_configs(pass_to_bison *bison_args, char *alias)
static int tag_add(pass_to_bison *bison_args, char *tag)
static int tag_add(pass_to_bison *bison_args, bxstr_t *tag)
{
int rc = RC_SUCCESS;
if (tag_is_valid(tag)) {
if (!array_contains0(curdes.tags, tag)) {
if (is_ascii_id(tag, 1)) {
if (!array_contains0(curdes.tags, tag->ascii)) {
size_t num_tags = array_count0(curdes.tags);
curdes.tags = (char **) realloc(curdes.tags, (num_tags + 2) * sizeof(char *));
curdes.tags[num_tags] = tag;
curdes.tags[num_tags] = (char *) strdup(tag->ascii);
curdes.tags[num_tags + 1] = NULL;
}
else {
yyerror(bison_args, "duplicate tag -- %s", tag);
yyerror(bison_args, "duplicate tag -- %s", bxs_to_output(tag));
rc = RC_ERROR;
}
}
else {
yyerror(bison_args, "invalid tag -- %s", tag);
yyerror(bison_args, "invalid tag -- %s", bxs_to_output(tag));
rc = RC_ERROR;
}
return rc;
@ -394,51 +398,41 @@ static int tag_add(pass_to_bison *bison_args, char *tag)
static char *tag_next_comma(char *s)
{
char *comma = strchr(s, ',');
if (comma == NULL) {
comma = s + strlen(s);
}
return comma;
}
static int tag_split_add(pass_to_bison *bison_args, char *tag)
static int tag_split_add(pass_to_bison *bison_args, bxstr_t *tag)
{
int rc = RC_SUCCESS;
char *s = tag;
char *c = NULL;
uint32_t *c = NULL;
int vis_start = 0;
int cursor = -1;
do {
c = tag_next_comma(s);
char *single_tag = trimdup(s, c - 1);
c = bxs_strchr(tag, to_utf32(','), &cursor);
bxstr_t *single_tag = bxs_trimdup(tag, vis_start, c != NULL ? (size_t) cursor : tag->num_chars_visible);
int rc_add = tag_add(bison_args, single_tag);
bxs_free(single_tag);
if (rc_add != 0) {
rc = rc_add;
}
s = c + 1;
} while (*c != '\0');
vis_start = cursor + 1;
} while (c != NULL);
return rc;
}
/**
* Add tag to list of current design's tag after validity checking.
* @param bison_args the parser state
* @param tag a single tag, or a comma-separated list of tags
* @return error code, 0 on success, anything else on failure
*/
int tag_record(pass_to_bison *bison_args, char *tag)
int tag_record(pass_to_bison *bison_args, bxstr_t *tag)
{
int rc = RC_SUCCESS;
if (strchr(tag, ',') != NULL) {
if (tag->num_chars_invisible > 0) {
yyerror(bison_args, "invalid tag");
return RC_ERROR;
}
if (bxs_strchr(tag, to_utf32(','), NULL) != NULL) {
rc = tag_split_add(bison_args, tag);
}
else {
char *trimmed = trimdup(tag, tag + strlen(tag) - 1);
bxstr_t *trimmed = bxs_trimdup(tag, 0, tag->num_chars_visible);
rc = tag_add(bison_args, trimmed);
bxs_free(trimmed);
}
return rc;
}
@ -454,12 +448,12 @@ int tag_record(pass_to_bison *bison_args, char *tag)
*/
int action_finalize_shapes(pass_to_bison *bison_args)
{
int i,j;
shape_t fshape; /* found shape */
int fside; /* first side */
int sc; /* side counter */
int side; /* effective side */
int rc; /* received return code */
int i, j;
shape_t fshape; /* found shape */
int fside; /* first side */
int sc; /* side counter */
int side; /* effective side */
int rc; /* received return code */
/*
* At least one shape must be specified
@ -474,28 +468,29 @@ int action_finalize_shapes(pass_to_bison *bison_args)
* as necessary, starting at any side which already includes at
* least one shape in order to ensure correct measurements.
*/
fshape = findshape (curdes.shape, NUM_SHAPES);
fshape = findshape(curdes.shape, NUM_SHAPES);
if (fshape == NUM_SHAPES) {
yyerror(bison_args, "internal error");
return RC_ABORT;
}
fside = on_side (fshape, 0);
fside = on_side(fshape, 0);
if (fside == NUM_SIDES) {
yyerror(bison_args, "internal error");
return RC_ABORT;
}
for (sc=0,side=fside; sc<NUM_SIDES; ++sc,side=(side+1)%NUM_SIDES) {
shape_t nshape; /* next shape */
sentry_t *c; /* corner to be processed */
c = curdes.shape + sides[side][SHAPES_PER_SIDE-1];
for (sc = 0, side = fside; sc < NUM_SIDES; ++sc, side = (side + 1) % NUM_SIDES) {
shape_t nshape; /* next shape */
sentry_t *c; /* corner to be processed */
c = curdes.shape + sides[side][SHAPES_PER_SIDE - 1];
if (isempty(c)) {
nshape = findshape (c, SHAPES_PER_SIDE);
nshape = findshape(c, SHAPES_PER_SIDE);
if (side == BLEF || side == BRIG) {
if (nshape == SHAPES_PER_SIDE) {
c->height = 1;
} else {
}
else {
c->height = c[nshape].height;
}
c->width = curdes.shape[fshape].width;
@ -503,36 +498,38 @@ int action_finalize_shapes(pass_to_bison *bison_args)
else {
if (nshape == SHAPES_PER_SIDE) {
c->width = 1;
} else {
}
else {
c->width = c[nshape].width;
}
c->height = curdes.shape[fshape].height;
}
c->elastic = 0;
rc = genshape (c->width, c->height, &(c->chars));
rc = genshape(c->width, c->height, &(c->chars), &(c->mbcs));
if (rc) {
return RC_ABORT;
}
}
fshape = sides[side][SHAPES_PER_SIDE-1];
fshape = sides[side][SHAPES_PER_SIDE - 1];
}
/*
* For all sides whose side shapes have not been defined, generate
* an elastic middle side shape.
*/
for (side=0; side<NUM_SIDES; ++side) {
for (side = 0; side < NUM_SIDES; ++side) {
int found = 0;
for (i=1; i<SHAPES_PER_SIDE-1; ++i) {
if (isempty (curdes.shape + sides[side][i])) {
for (i = 1; i < SHAPES_PER_SIDE - 1; ++i) {
if (isempty(curdes.shape + sides[side][i])) {
continue;
} else {
}
else {
found = 1;
}
}
if (!found) {
sentry_t *c = curdes.shape + sides[side][SHAPES_PER_SIDE/2];
sentry_t *c = curdes.shape + sides[side][SHAPES_PER_SIDE / 2];
if (side == BLEF || side == BRIG) {
c->width = curdes.shape[sides[side][0]].width;
c->height = 1;
@ -542,7 +539,7 @@ int action_finalize_shapes(pass_to_bison *bison_args)
c->height = curdes.shape[sides[side][0]].height;
}
c->elastic = 1;
rc = genshape (c->width, c->height, &(c->chars));
rc = genshape(c->width, c->height, &(c->chars), &(c->mbcs));
if (rc) {
return RC_ABORT;
}
@ -563,10 +560,10 @@ int action_finalize_shapes(pass_to_bison *bison_args)
/*
* Compute minimum height/width of a box of current design
*/
for (i=0; i<NUM_SIDES; ++i) {
for (i = 0; i < NUM_SIDES; ++i) {
size_t c = 0;
if (i % 2) { /* vertical sides */
for (j=0; j<SHAPES_PER_SIDE; ++j) {
if (i % 2) { /* vertical sides */
for (j = 0; j < SHAPES_PER_SIDE; ++j) {
if (!isempty(curdes.shape + sides[i][j])) {
c += curdes.shape[sides[i][j]].height;
}
@ -575,8 +572,8 @@ int action_finalize_shapes(pass_to_bison *bison_args)
curdes.minheight = c;
}
}
else { /* horizontal sides */
for (j=0; j<SHAPES_PER_SIDE; ++j) {
else { /* horizontal sides */
for (j = 0; j < SHAPES_PER_SIDE; ++j) {
if (!isempty(curdes.shape + sides[i][j])) {
c += curdes.shape[sides[i][j]].width;
}
@ -590,7 +587,7 @@ int action_finalize_shapes(pass_to_bison *bison_args)
/*
* Compute height of highest shape in design
*/
for (i=0; i<NUM_SHAPES; ++i) {
for (i = 0; i < NUM_SHAPES; ++i) {
if (isempty(curdes.shape + i)) {
continue;
}
@ -599,10 +596,9 @@ int action_finalize_shapes(pass_to_bison *bison_args)
}
}
#ifdef PARSER_DEBUG
fprintf (stderr, " Parser: Minimum box dimensions: width %d height %d\n",
fprintf(stderr, " Parser: Minimum box dimensions: width %d height %d\n",
(int) curdes.minwidth, (int) curdes.minheight);
fprintf (stderr, " Parser: Maximum shape height: %d\n",
(int) curdes.maxshapeheight);
fprintf(stderr, " Parser: Maximum shape height: %d\n", (int) curdes.maxshapeheight);
#endif
return RC_SUCCESS;
}
@ -631,45 +627,42 @@ int action_start_parsing_design(pass_to_bison *bison_args, char *design_name)
if (!design_needed(bison_args)) {
bison_args->speeding = 1;
#ifdef PARSER_DEBUG
fprintf (stderr, " Parser: Skipping to next design (lexer doesn't know!)\n");
fprintf(stderr, " Parser: Skipping to next design (lexer doesn't know!)\n");
#endif
return RC_ERROR; /* trigger the parser's `error` rule, which will skip to the next design */
return RC_ERROR; /* trigger the parser's `error` rule, which will skip to the next design */
}
return RC_SUCCESS;
}
/**
* Rule action called when a parent config file specification is encountered.
* @param bison_args the parser state
* @param filepath the path to the parent config file
* @return 0: success;
* 1: YYERROR must be invoked
*/
int action_parent_config(pass_to_bison *bison_args, char *filepath)
int action_parent_config(pass_to_bison *bison_args, bxstr_t *filepath)
{
#ifdef PARSER_DEBUG
fprintf (stderr, " Parser: parent config file specified: [%s]\n", filepath);
fprintf(stderr, " Parser: parent config file specified: [%s]\n", bxs_to_output(filepath));
#endif
if (filepath == NULL || filepath[0] == '\0') {
if (bxs_is_empty(filepath)) {
bison_args->skipping = 1;
yyerror(bison_args, "parent reference is empty");
return RC_ERROR;
}
else if (strcasecmp(filepath, ":global:") == 0) { /* special token */
else if (strcasecmp(filepath->ascii, ":global:") == 0) { /* special token */
filepath = discover_config_file(1);
if (filepath == NULL) {
bison_args->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");
return RC_ERROR;
}
}
else if (!bxs_valid_in_filename(filepath, NULL)) {
yyerror(bison_args, "parent reference contains invalid characters: %s", bxs_to_output(filepath));
return RC_ERROR;
}
else {
FILE *f = fopen(filepath, "r");
FILE *f = bx_fopens(filepath, "r");
if (f == NULL) {
bison_args->skipping = 1;
yyerror(bison_args, "parent config file not found: %s", filepath);
yyerror(bison_args, "parent config file not found: %s", bxs_to_output(filepath));
return RC_ERROR;
}
else {
@ -677,46 +670,36 @@ int action_parent_config(pass_to_bison *bison_args, char *filepath)
}
}
#ifdef PARSER_DEBUG
fprintf (stderr, " Parser: parent config file path resolved: [%s]\n", filepath);
fprintf(stderr, " Parser: parent config file path resolved: [%s]\n", bxs_to_output(filepath));
#endif
int is_new = !array_contains(bison_args->parent_configs, bison_args->num_parent_configs, filepath);
if (!is_new) {
#ifdef PARSER_DEBUG
fprintf (stderr, " Parser: duplicate parent / cycle: [%s]\n", filepath);
#endif
}
else {
bison_args->parent_configs = realloc(bison_args->parent_configs,
(bison_args->num_parent_configs + 1) * sizeof(char *));
int is_new = !array_contains_bxs(bison_args->parent_configs, bison_args->num_parent_configs, filepath);
if (is_new) {
bison_args->parent_configs
= realloc(bison_args->parent_configs, (bison_args->num_parent_configs + 1) * sizeof(bxstr_t *));
bison_args->parent_configs[bison_args->num_parent_configs] = filepath;
++(bison_args->num_parent_configs);
}
else {
#ifdef PARSER_DEBUG
fprintf(stderr, " Parser: duplicate parent / cycle: [%s]\n", bxs_to_output(filepath));
#endif
}
return RC_SUCCESS;
}
/**
* Rule action called when a design has been parsed completely and can be checked and added.
* @param bison_args the parser state
* @param design_primary_name the primary name of the design as specified at BOX statement
* @param name_at_end the primary name of the design as specified at END statement
* @return 0: success;
* 1: YYERROR must be invoked
* 2: YYABORT must be invoked
* 3: YYACCEPT must be invoked
*/
int action_add_design(pass_to_bison *bison_args, char *design_primary_name, char *name_at_end)
{
design_t *tmp;
char *p;
#ifdef PARSER_DEBUG
fprintf (stderr, "--------- ADDING DESIGN \"%s\".\n", design_primary_name);
fprintf(stderr, "--------- ADDING DESIGN \"%s\".\n", design_primary_name);
#endif
if (strcasecmp (design_primary_name, name_at_end)) {
if (strcasecmp(design_primary_name, name_at_end)) {
yyerror(bison_args, "box design name differs at BOX and END");
return RC_ERROR;
}
@ -731,7 +714,7 @@ int action_add_design(pass_to_bison *bison_args, char *design_primary_name, char
p = design_primary_name;
while (*p) {
if (*p < 32 || *p > 126) {
if (*p < 32 || *p > 126) { // CHECK this check may be unnecessary due to lexer's ASCII_ID
yyerror(bison_args, "box design name must consist of printable standard ASCII characters.");
return RC_ERROR;
}
@ -756,7 +739,7 @@ int action_add_design(pass_to_bison *bison_args, char *design_primary_name, char
++(bison_args->design_idx);
tmp = (design_t *) realloc(bison_args->designs, (bison_args->design_idx + 1) * sizeof(design_t));
if (tmp == NULL) {
perror (PROJECT);
perror(PROJECT);
return RC_ABORT;
}
bison_args->designs = tmp;
@ -767,63 +750,77 @@ int action_add_design(pass_to_bison *bison_args, char *design_primary_name, char
/**
* Rule action called when a keyword entry is encountered in a box design.
* @param bison_args the parser state
* @param keyword the keyword
* @param value the associated value
* @return 0: success;
* 1: YYERROR must be invoked
* 2: YYABORT must be invoked
*/
int action_record_keyword(pass_to_bison *bison_args, char *keyword, char *value)
static int is_semantic_version(char *version)
{
pcre2_code *version_pattern /* CHECK cache compiled pattern to speed up config file parsing */
= compile_pattern("^(0|[1-9]\\d*)(?:\\.(0|[1-9]\\d*))?(?:\\.(0|[1-9]\\d*))?(?:[+-][a-zA-Z0-9\\.+-]+)?$");
int result = regex_match(version_pattern, version);
pcre2_code_free(version_pattern);
return result;
}
int action_record_keyword(pass_to_bison *bison_args, char *keyword, bxstr_t *value)
{
#ifdef PARSER_DEBUG
fprintf (stderr, " Parser: entry rule fulfilled [%s = %s]\n", keyword, value);
fprintf(stderr, " Parser: entry rule fulfilled [%s = %s]\n", keyword, bxs_to_output(value));
#endif
if (strcasecmp (keyword, "author") == 0) {
curdes.author = (char *) strdup (value);
size_t error_pos = 0;
if (!bxs_valid_in_kv_string(value, &error_pos)) {
yyerror(bison_args, "invalid character in string value at position %d", (int) error_pos);
return RC_ERROR;
}
if (strcasecmp(keyword, "author") == 0) {
curdes.author = bxs_strdup(value);
if (curdes.author == NULL) {
perror (PROJECT);
perror(PROJECT);
return RC_ABORT;
}
}
else if (strcasecmp (keyword, "designer") == 0) {
curdes.designer = (char *) strdup (value);
else if (strcasecmp(keyword, "designer") == 0) {
curdes.designer = bxs_strdup(value);
if (curdes.designer == NULL) {
perror (PROJECT);
perror(PROJECT);
return RC_ABORT;
}
}
else if (strcasecmp (keyword, "revision") == 0) {
curdes.revision = (char *) strdup (value);
if (curdes.revision == NULL) {
perror (PROJECT);
return RC_ABORT;
else if (strcasecmp(keyword, "revision") == 0) {
if (is_semantic_version(value->ascii)) {
curdes.revision = (char *) strdup(value->ascii);
if (curdes.revision == NULL) {
perror(PROJECT);
return RC_ABORT;
}
}
else {
yyerror(bison_args, "revision is not a version number in line %d of %s", __LINE__, __FILE__);
return RC_ERROR;
}
}
else if (strcasecmp (keyword, "created") == 0) {
curdes.created = (char *) strdup (value);
else if (strcasecmp(keyword, "created") == 0) {
curdes.created = bxs_strdup(value);
if (curdes.created == NULL) {
perror (PROJECT);
perror(PROJECT);
return RC_ABORT;
}
}
else if (strcasecmp (keyword, "revdate") == 0) {
curdes.revdate = (char *) strdup (value);
else if (strcasecmp(keyword, "revdate") == 0) {
curdes.revdate = bxs_strdup(value);
if (curdes.revdate == NULL) {
perror (PROJECT);
perror(PROJECT);
return RC_ABORT;
}
}
else if (strcasecmp (keyword, "tags") == 0) {
tag_record(bison_args, value); /* discard return code (we print warnings, but tolerate the problem) */
else if (strcasecmp(keyword, "tags") == 0) {
tag_record(bison_args, value); /* discard return code (we print warnings, but tolerate the problem) */
}
else if (strcasecmp (keyword, "indent") == 0) {
if (strcasecmp (value, "text") == 0 ||
strcasecmp (value, "box") == 0 ||
strcasecmp (value, "none") == 0) {
curdes.indentmode = value[0];
else if (strcasecmp(keyword, "indent") == 0) {
char *val = value->ascii;
if (strcasecmp(val, "text") == 0 || strcasecmp(val, "box") == 0 || strcasecmp(val, "none") == 0) {
curdes.indentmode = val[0];
}
else {
yyerror(bison_args, "indent keyword must be followed by \"text\", \"box\", or \"none\"");
@ -883,9 +880,9 @@ int action_padding_entry(pass_to_bison *bison_args, char *area, int value)
int action_init_parser(pass_to_bison *bison_args)
{
bison_args->designs = (design_t *) calloc (1, sizeof(design_t));
bison_args->designs = (design_t *) calloc(1, sizeof(design_t));
if (bison_args->designs == NULL) {
perror (PROJECT);
perror(PROJECT);
return RC_ABORT;
}
bison_args->num_designs = 1;
@ -903,7 +900,7 @@ int action_add_alias(pass_to_bison *bison_args, char *alias_name)
}
if (alias_exists_in_child_configs(bison_args, alias_name)) {
#ifdef PARSER_DEBUG
fprintf (stderr, " Parser: alias already used by child config, dropping: %s\n", alias_name);
fprintf(stderr, " Parser: alias already used by child config, dropping: %s\n", alias_name);
#endif
}
else {
@ -917,22 +914,40 @@ int action_add_alias(pass_to_bison *bison_args, char *alias_name)
static char *adjust_eols(char *sample)
static bxstr_t *adjust_eols(uint32_t *sample)
{
if (eol_pattern == NULL) {
eol_pattern = compile_pattern("(?(?=\r)(\r\n?)|(\n))");
}
uint32_t *u32_sample = u32_strconv_from_input(sample);
uint32_t *replaced = regex_replace(eol_pattern, opt.eol, u32_sample, strlen(sample), 1);
char *result = u32_strconv_to_output(replaced);
uint32_t *replaced = regex_replace(eol_pattern, opt.eol, sample, u32_strlen(sample), 1);
bxstr_t *result = bxs_from_unicode(replaced);
BFREE(replaced);
BFREE(u32_sample);
return result;
}
int action_sample_block(pass_to_bison *bison_args, char *sample)
static uint32_t *find_first_nonblank_line(bxstr_t *sample)
{
uint32_t *result = sample->memory;
uint32_t *p = sample->memory;
while (*p != char_nul) {
while (uc_is_blank(*p) || *p == char_cr) {
p++;
}
if (*p == char_newline) {
result = ++p;
}
else {
break;
}
}
return result;
}
int action_sample_block(pass_to_bison *bison_args, bxstr_t *sample)
{
#ifdef PARSER_DEBUG
fprintf(stderr, " Parser: SAMPLE block rule satisfied\n");
@ -943,20 +958,59 @@ int action_sample_block(pass_to_bison *bison_args, char *sample)
return RC_ERROR;
}
char *p = sample;
while ((*p == '\r' || *p == '\n') && *p != '\0') {
p++;
}
char *line = adjust_eols(p);
uint32_t *p = find_first_nonblank_line(sample);
bxstr_t *line = adjust_eols(p);
if (line == NULL) {
perror(PROJECT);
return RC_ABORT;
}
if (bxs_is_empty(line)) {
yyerror(bison_args, "SAMPLE block must not be empty");
return RC_ERROR;
}
size_t error_pos = 0;
if (!bxs_valid_in_sample(line, &error_pos)) {
yyerror(bison_args, "invalid character in SAMPLE block at position %d", (int) error_pos);
return RC_ERROR;
}
curdes.sample = line;
++(bison_args->num_mandatory);
return RC_SUCCESS;
}
/*EOF*/ /* vim: set cindent sw=4: */
int action_add_regex_rule(pass_to_bison *bison_args, char *type, reprule_t **rule_list, size_t *rule_list_len,
bxstr_t *search, bxstr_t *replace, char mode)
{
size_t n = *rule_list_len;
UNUSED(type); /* used only in PARSER_DEBUG mode */
#ifdef PARSER_DEBUG
fprintf(stderr, "Adding %s rule: \"%s\" with \"%s\" (%c)\n",
strcmp(type, "rep") == 0 ? "replacement" : "reversion",
bxs_to_output(search), bxs_to_output(replace), mode);
#endif
*rule_list = (reprule_t *) realloc(*rule_list, (n + 1) * sizeof(reprule_t));
if (*rule_list == NULL) {
perror(PROJECT);
return RC_ABORT;
}
memset((*rule_list) + n, 0, sizeof(reprule_t));
(*rule_list)[n].search = bxs_strdup(search);
(*rule_list)[n].repstr = bxs_strdup(replace);
if ((*rule_list)[n].search == NULL || (*rule_list)[n].repstr == NULL) {
perror(PROJECT);
return RC_ABORT;
}
(*rule_list)[n].line = yyget_lineno(bison_args->lexer_state);
(*rule_list)[n].mode = mode;
*rule_list_len = n + 1;
return RC_SUCCESS;
}
/* vim: set cindent sw=4: */

View File

@ -71,7 +71,7 @@ int action_start_parsing_design(pass_to_bison *bison_args, char *design_name);
* @return 0: success;
* 1: YYERROR must be invoked
*/
int action_parent_config(pass_to_bison *bison_args, char *filepath);
int action_parent_config(pass_to_bison *bison_args, bxstr_t *filepath);
/**
@ -80,12 +80,13 @@ int action_parent_config(pass_to_bison *bison_args, char *filepath);
* @param design_primary_name the primary name of the design as specified at BOX statement
* @param name_at_end the primary name of the design as specified at END statement
* @return 0: success;
* 1: YYERROR must be invoked
* 2: YYABORT must be invoked
* 1: YYERROR must be invoked;
* 2: YYABORT must be invoked;
* 3: YYACCEPT must be invoked
*/
int action_add_design(pass_to_bison *bison_args, char *design_primary_name, char *name_at_end);
/**
* Rule action called when a keyword entry is encountered in a box design.
* @param bison_args the parser state
@ -95,7 +96,7 @@ int action_add_design(pass_to_bison *bison_args, char *design_primary_name, char
* 1: YYERROR must be invoked
* 2: YYABORT must be invoked
*/
int action_record_keyword(pass_to_bison *bison_args, char *keyword, char *value);
int action_record_keyword(pass_to_bison *bison_args, char *keyword, bxstr_t *value);
/**
@ -118,6 +119,23 @@ int action_padding_entry(pass_to_bison *bison_args, char *area, int value);
int action_add_alias(pass_to_bison *bison_args, char *alias_name);
/**
* Rule action to add a `replace` or `reverse` rule to the current design definition.
* @param bison_args the parser state
* @param type the rule type (either `rep` or `rev`)
* @param rule_list pointer to the list to add to
* @param rule_list_len pointer to the number of elements in `rule_list`
* @param search the search pattern of the rule
* @param replace the replacement text of the rule
* @param mode the replacement mode (`g` or `o` for `global` or `once`)
* @return 0: success;
* 1: YYERROR must be invoked
* 2: YYABORT must be invoked
*/
int action_add_regex_rule(pass_to_bison *bison_args, char *type, reprule_t **rule_list, size_t *rule_list_len,
bxstr_t *search, bxstr_t *replace, char mode);
/**
* (shape-elastic check)
*/
@ -132,12 +150,12 @@ void recover(pass_to_bison *bison_args);
/**
* Add tag to list of current design's tag after validity checking.
* Add tag to list of current design's tags after validity checking.
* @param bison_args the parser state
* @param tag a single tag, or a comma-separated list of tags
* @return error code, 0 on success, anything else on failure
*/
int tag_record(pass_to_bison *bison_args, char *tag);
int tag_record(pass_to_bison *bison_args, bxstr_t *tag);
/**
@ -148,7 +166,7 @@ int tag_record(pass_to_bison *bison_args, char *tag);
* 1: YYERROR must be invoked
* 2: YYABORT must be invoked
*/
int action_sample_block(pass_to_bison *bison_args, char *sample);
int action_sample_block(pass_to_bison *bison_args, bxstr_t *sample);
#endif

View File

@ -20,6 +20,7 @@
#include "config.h"
#include "boxes.h"
#include "bxstring.h"
/** all the arguments which we pass to the bison parser */
@ -40,7 +41,7 @@ typedef struct {
size_t num_child_configs;
/** the path to the config file we are parsing */
char *config_file;
bxstr_t *config_file;
int num_mandatory;
@ -56,7 +57,7 @@ typedef struct {
int speeding;
/** names of config files specified via "parent" */
char **parent_configs;
bxstr_t **parent_configs;
/** number of parent config files (size of parent_configs array) */
size_t num_parent_configs;
@ -72,6 +73,7 @@ typedef struct {
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <unictype.h>
#include "shape.h"
#include "tools.h"
@ -79,6 +81,7 @@ typedef struct {
#include "parser.h"
#include "lex.yy.h"
#include "parsecode.h"
#include "unicode.h"
/** required for bison-flex bridge */
@ -113,7 +116,8 @@ typedef struct {
%parse-param {pass_to_bison *bison_args}
%union {
char *s;
bxstr_t *s;
char *ascii;
char c;
shape_t shape;
sentry_t sentry;
@ -122,8 +126,9 @@ typedef struct {
%token YPARENT YSHAPES YELASTIC YPADDING YSAMPLE YENDSAMPLE YBOX YEND YUNREC
%token YREPLACE YREVERSE YTO YWITH YCHGDEL YTAGS
%token <s> KEYWORD
%token <ascii> KEYWORD
%token <s> WORD
%token <ascii> ASCII_ID
%token <s> STRING
%token <s> FILENAME
%token <shape> SHAPE
@ -166,7 +171,8 @@ config_file
BFREE (bison_args->designs);
bison_args->num_designs = 0;
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);
fprintf(stderr, "%s: no valid data in config file -- %s\n", PROJECT,
bxs_to_output(bison_args->config_file));
YYABORT;
}
YYACCEPT;
@ -209,22 +215,22 @@ design_or_error: design | error
}
alias: WORD
alias: ASCII_ID
{
invoke_action(action_add_alias(bison_args, $1));
}
alias_list: alias | alias_list ',' alias;
design_id: WORD | WORD ',' alias_list;
design_id: ASCII_ID | ASCII_ID ',' alias_list;
design: YBOX design_id
{
invoke_action(action_start_parsing_design(bison_args, $<s>2));
invoke_action(action_start_parsing_design(bison_args, $<ascii>2));
}
layout YEND WORD
layout YEND ASCII_ID
{
invoke_action(action_add_design(bison_args, $<s>2, $6));
invoke_action(action_add_design(bison_args, $<ascii>2, $6));
}
;
@ -247,18 +253,17 @@ 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);
/*
* Called when PARENT appears as a key inside a box design. That's a user mistake, but not an error.
*/
bxstr_t *filename = $2;
if (filename->memory[0] != filename->memory[filename->num_chars - 1] || uc_is_alnum(filename->memory[0])) {
yyerror(bison_args, "string expected");
YYERROR;
}
else {
#ifdef PARSER_DEBUG
fprintf (stderr, " Parser: Discarding entry [%s = %s].\n", "parent", filename);
fprintf (stderr, " Parser: Discarding entry [%s = %s].\n", "parent", bxs_to_output(filename));
#endif
}
}
@ -270,10 +275,10 @@ entry: KEYWORD STRING
| YTAGS '(' tag_list ')' | YTAGS tag_entry
| WORD STRING
| WORD STRING | ASCII_ID STRING
{
#ifdef PARSER_DEBUG
fprintf (stderr, " Parser: Discarding entry [%s = %s].\n", $1, $2);
fprintf (stderr, " Parser: Discarding entry [%s = %s].\n", $1, bxs_to_output($2));
#endif
}
;
@ -301,53 +306,12 @@ block: YSAMPLE STRING YENDSAMPLE
| YREPLACE rflag STRING YWITH STRING
{
int a = curdes.anz_reprules;
#ifdef PARSER_DEBUG
fprintf (stderr, "Adding replacement rule: \"%s\" with \"%s\" (%c)\n",
$3, $5, $2);
#endif
curdes.reprules = (reprule_t *) realloc (curdes.reprules, (a+1) * sizeof(reprule_t));
if (curdes.reprules == NULL) {
perror (PROJECT);
YYABORT;
}
memset (&(curdes.reprules[a]), 0, sizeof(reprule_t));
curdes.reprules[a].search = (char *) strdup ($3);
curdes.reprules[a].repstr = (char *) strdup ($5);
if (curdes.reprules[a].search == NULL || curdes.reprules[a].repstr == NULL) {
perror (PROJECT);
YYABORT;
}
curdes.reprules[a].line = yyget_lineno(scanner);
curdes.reprules[a].mode = $2;
curdes.anz_reprules = a + 1;
invoke_action(action_add_regex_rule(bison_args, "rep", &curdes.reprules, &curdes.anz_reprules, $3, $5, $2));
}
| YREVERSE rflag STRING YTO STRING
{
int a = curdes.anz_revrules;
#ifdef PARSER_DEBUG
fprintf (stderr, "Adding reversion rule: \"%s\" to \"%s\" (%c)\n", $3, $5, $2);
#endif
curdes.revrules = (reprule_t *) realloc (curdes.revrules, (a+1) * sizeof(reprule_t));
if (curdes.revrules == NULL) {
perror (PROJECT);
YYABORT;
}
memset (&(curdes.revrules[a]), 0, sizeof(reprule_t));
curdes.revrules[a].search = (char *) strdup ($3);
curdes.revrules[a].repstr = (char *) strdup ($5);
if (curdes.revrules[a].search == NULL || curdes.revrules[a].repstr == NULL) {
perror (PROJECT);
YYABORT;
}
curdes.revrules[a].line = yyget_lineno(scanner);
curdes.revrules[a].mode = $2;
curdes.anz_revrules = a + 1;
invoke_action(action_add_regex_rule(bison_args, "rev", &curdes.revrules, &curdes.anz_revrules, $3, $5, $2));
}
| YPADDING '{' wlist '}'
@ -426,9 +390,8 @@ shape_def: '(' shape_lines ')'
shape_lines: shape_lines ',' STRING
{
sentry_t rval = $1;
size_t slen = strlen ($3);
char **tmp;
sentry_t rval = $1; // TODO move this to parsecode.c
size_t slen = $3->num_columns;
#ifdef PARSER_DEBUG
fprintf (stderr, "Extending a shape entry\n");
@ -439,18 +402,38 @@ shape_lines: shape_lines ',' STRING
YYERROR;
}
size_t error_pos = 0;
if (!bxs_valid_in_shape($3, &error_pos)) {
yyerror(bison_args, "invalid character in shape line at position %d", (int) error_pos);
YYERROR;
}
rval.height++;
tmp = (char **) realloc (rval.chars, rval.height*sizeof(char*));
char **tmp = (char **) realloc(rval.chars, rval.height * sizeof(char *));
if (tmp == NULL) {
perror (PROJECT": shape_lines11");
YYABORT;
}
rval.chars = tmp;
rval.chars[rval.height-1] = (char *) strdup ($3);
rval.chars[rval.height - 1] = (char *) strdup ($3->ascii);
if (rval.chars[rval.height-1] == NULL) {
perror (PROJECT": shape_lines12");
YYABORT;
}
bxstr_t **mtmp = (bxstr_t **) realloc(rval.mbcs, rval.height * sizeof(bxstr_t *));
if (mtmp == NULL) {
perror (PROJECT": shape_lines13");
YYABORT;
}
rval.mbcs = mtmp;
rval.mbcs[rval.height - 1] = bxs_strdup($3);
if (rval.mbcs[rval.height - 1] == NULL) {
perror (PROJECT": shape_lines14");
YYABORT;
}
$$ = rval;
}
@ -462,18 +445,36 @@ shape_lines: shape_lines ',' STRING
fprintf (stderr, "Initializing a shape entry with first line\n");
#endif
rval.width = strlen ($1);
size_t error_pos = 0;
if (!bxs_valid_in_shape($1, &error_pos)) {
yyerror(bison_args, "invalid character in shape line at position %d", (int) error_pos);
YYERROR;
}
rval.width = $1->num_columns;
rval.height = 1;
rval.chars = (char **) malloc (sizeof(char*));
if (rval.chars == NULL) {
perror (PROJECT": shape_lines21");
YYABORT;
}
rval.chars[0] = (char *) strdup ($1);
rval.chars[0] = (char *) strdup ($1->ascii);
if (rval.chars[0] == NULL) {
perror (PROJECT": shape_lines22");
YYABORT;
}
rval.mbcs = (bxstr_t **) malloc(sizeof(bxstr_t *));
if (rval.mbcs == NULL) {
perror (PROJECT": shape_lines23");
YYABORT;
}
rval.mbcs[0] = bxs_strdup($1);
if (rval.mbcs[0] == NULL) {
perror (PROJECT": shape_lines24");
YYABORT;
}
$$ = rval;
}
;
@ -481,7 +482,7 @@ shape_lines: shape_lines ',' STRING
wlist: wlist wlist_entry | wlist_entry;
wlist_entry: WORD YNUMBER
wlist_entry: ASCII_ID YNUMBER
{
invoke_action(action_padding_entry(bison_args, $1, $2));
}