boxes/src/parsecode.c

1068 lines
29 KiB
C

/*
* boxes - Command line filter to draw/remove ASCII boxes around text
* Copyright (c) 1999-2024 Thomas Jensen and the boxes contributors
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
* License, version 3, as published by the Free Software Foundation.
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <https://www.gnu.org/licenses/>.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
/*
* C code used exclusively by the parser.
*/
#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 "parsecode.h"
#include "parsing.h"
#include "query.h"
#include "regulex.h"
#include "shape.h"
#include "tools.h"
#include "unicode.h"
#include "parser.h"
#include "lex.yy.h"
static pcre2_code *eol_pattern = NULL;
static int check_sizes(pass_to_bison *bison_args)
/*
* For the author's convenience, it is required that shapes on one side
* have equal width (vertical sides) and height (horizontal sides).
*
* RETURNS: == 0 no problem detected
* != 0 on error (prints error message, too)
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
{
int i, j, k;
#ifdef PARSER_DEBUG
fprintf(stderr, " Parser: check_sizes()\n");
#endif
for (i = 0; i < NUM_SIDES; ++i) {
if (i == 0 || i == 2) {
/*
* horizontal
*/
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) {
if (curdes.shape[sides[i][k]].height == 0) {
continue;
}
if (curdes.shape[sides[i][j]].height != curdes.shape[sides[i][k]].height) {
yyerror(bison_args, "All shapes on horizontal sides must be of "
"equal height (%s: %d, %s: %d)\n",
shape_name[sides[i][j]], curdes.shape[sides[i][j]].height,
shape_name[sides[i][k]], curdes.shape[sides[i][k]].height);
return 1;
}
}
}
}
else {
/*
* vertical
*/
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) {
if (curdes.shape[sides[i][k]].width == 0) {
continue;
}
if (curdes.shape[sides[i][j]].width != curdes.shape[sides[i][k]].width) {
yyerror(bison_args, "All shapes on vertical sides must be of "
"equal width (%s: %d, %s: %d)\n",
shape_name[sides[i][j]], curdes.shape[sides[i][j]].width,
shape_name[sides[i][k]], curdes.shape[sides[i][k]].width);
return 1;
}
}
}
}
}
return 0; /* all clear */
}
static int corner_check(pass_to_bison *bison_args)
/*
* Check that no corners are elastic.
*
* RETURNS: == 0 no problem detected
* != 0 on error
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
{
int c;
#ifdef PARSER_DEBUG
fprintf(stderr, " Parser: corner_check()\n");
#endif
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 */
}
static shape_t non_existent_elastics(pass_to_bison *bison_args)
{
shape_t i;
#ifdef PARSER_DEBUG
fprintf(stderr, " Parser: non_existent_elastics()\n");
#endif
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 */
}
static int insufficient_elasticity(pass_to_bison *bison_args)
{
int i, j, ef;
#ifdef PARSER_DEBUG
fprintf(stderr, " Parser: insufficient_elasticity()\n");
#endif
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 0; /* all clear */
}
static int adjoining_elastics(pass_to_bison *bison_args)
{
int i, j, ef;
#ifdef PARSER_DEBUG
fprintf(stderr, " Parser: adjoining_elastics()\n");
#endif
for (i = 0; i < NUM_SIDES; ++i) {
ef = 0;
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 {
ef = 1;
}
}
else {
ef = 0;
}
}
}
return 0; /* all clear */
}
int perform_se_check(pass_to_bison *bison_args)
{
shape_t s_rc;
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]);
return 1;
}
if (corner_check(bison_args)) {
/* Error message printed in check func */
return 1;
}
if (insufficient_elasticity(bison_args)) {
yyerror(bison_args, "There must be exactly one or two elastic shapes per side");
return 1;
}
if (adjoining_elastics(bison_args)) {
yyerror(bison_args, "Two adjoining shapes may not be elastic");
return 1;
}
return 0;
}
static void init_design(pass_to_bison *bison_args, design_t *design)
{
memset(design, 0, sizeof(design_t));
design->aliases = (char **) calloc(1, sizeof(char *));
design->indentmode = DEF_INDENTMODE;
design->defined_in = bison_args->config_file;
design->tags = (char **) calloc(1, sizeof(char *));
}
/**
* Reset parser to neutral state, so a new design can be parsed.
* @param bison_args the parser state
*/
void recover(pass_to_bison *bison_args)
{
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.sample);
BFREE(curdes.tags);
init_design(bison_args, &(curdes));
}
static int design_has_alias(design_t *design, char *alias)
{
int result = 0;
for (size_t aidx = 0; design->aliases[aidx] != NULL; ++aidx) {
if (strcasecmp(alias, design->aliases[aidx]) == 0) {
result = 1;
break;
}
}
return result;
}
static int design_has_name(design_t *design, char *name)
{
int result = 0;
if (strcasecmp(name, design->name) == 0) {
result = 1;
}
else {
result = design_has_alias(design, name);
}
return result;
}
static int full_parse_required()
{
int result = 0;
if (!opt.design_choice_by_user) {
result = opt.r || opt.l || (opt.query != NULL && !opt.qundoc);
}
#ifdef DEBUG
fprintf(stderr, " Parser: full_parse_required() -> %s\n", result ? "true" : "false");
#endif
return result;
}
/**
* Determine if the design currently being parsed is one that we will need.
* @param bison_args the bison state
* @return result flag
*/
static int design_needed(pass_to_bison *bison_args)
{
if (opt.design_choice_by_user) {
return design_has_name(&(curdes), (char *) opt.design);
}
else {
if (full_parse_required() || bison_args->design_idx == 0) {
return 1;
}
}
return 0;
}
static int design_name_exists(pass_to_bison *bison_args, char *name)
{
int result = 0;
for (int i = 0; i < bison_args->design_idx; ++i) {
if (design_has_name(bison_args->designs + i, name)) {
result = 1;
break;
}
}
return result;
}
static int alias_exists_in_child_configs(pass_to_bison *bison_args, char *alias)
{
int result = 0;
for (size_t i = 0; i < bison_args->num_child_configs; ++i) {
if (design_has_alias(bison_args->child_configs + i, alias)) {
result = 1;
break;
}
}
return result;
}
static int tag_add(pass_to_bison *bison_args, bxstr_t *tag)
{
int rc = RC_SUCCESS;
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] = (char *) strdup(tag->ascii);
curdes.tags[num_tags + 1] = NULL;
}
else {
yyerror(bison_args, "duplicate tag -- %s", bxs_to_output(tag));
rc = RC_ERROR;
}
}
else {
yyerror(bison_args, "invalid tag -- %s", bxs_to_output(tag));
rc = RC_ERROR;
}
return rc;
}
static int tag_split_add(pass_to_bison *bison_args, bxstr_t *tag)
{
int rc = RC_SUCCESS;
uint32_t *c = NULL;
int vis_start = 0;
int cursor = -1;
do {
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;
}
vis_start = cursor + 1;
} while (c != NULL);
return rc;
}
int tag_record(pass_to_bison *bison_args, bxstr_t *tag)
{
int rc = RC_SUCCESS;
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 {
bxstr_t *trimmed = bxs_trimdup(tag, 0, tag->num_chars_visible);
rc = tag_add(bison_args, trimmed);
bxs_free(trimmed);
}
return rc;
}
/**
* Rule action called when a shape list was fully parsed.
* @param bison_args the parser state
* @return 0: success;
* 1: YYERROR must be invoked;
* 2: YYABORT must be invoked
*/
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 */
/*
* At least one shape must be specified
*/
if (bison_args->num_shapespec < 1) {
yyerror(bison_args, "must specify at least one non-empty shape per design");
return RC_ERROR;
}
/*
* Ensure that all corners have been specified. Generate corners
* 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);
if (fshape == NUM_SHAPES) {
yyerror(bison_args, "internal error");
return RC_ABORT;
}
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];
if (isempty(c)) {
nshape = findshape(c, SHAPES_PER_SIDE);
if (side == BLEF || side == BRIG) {
if (nshape == SHAPES_PER_SIDE) {
c->height = 1;
}
else {
c->height = c[nshape].height;
}
c->width = curdes.shape[fshape].width;
}
else {
if (nshape == SHAPES_PER_SIDE) {
c->width = 1;
}
else {
c->width = c[nshape].width;
}
c->height = curdes.shape[fshape].height;
}
c->elastic = 0;
rc = genshape(c->width, c->height, &(c->chars), &(c->mbcs));
if (rc) {
return RC_ABORT;
}
}
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) {
int found = 0;
for (i = 1; i < SHAPES_PER_SIDE - 1; ++i) {
if (isempty(curdes.shape + sides[side][i])) {
continue;
}
else {
found = 1;
}
}
if (!found) {
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;
}
else {
c->width = 1;
c->height = curdes.shape[sides[side][0]].height;
}
c->elastic = 1;
rc = genshape(c->width, c->height, &(c->chars), &(c->mbcs));
if (rc) {
return RC_ABORT;
}
}
}
if (check_sizes(bison_args)) {
return RC_ERROR;
}
++(bison_args->num_mandatory);
if (++(bison_args->time_for_se_check) > 1) {
if (perform_se_check(bison_args) != 0) {
return RC_ERROR;
}
}
/*
* Compute minimum height/width of a box of current design
*/
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 (!isempty(curdes.shape + sides[i][j])) {
c += curdes.shape[sides[i][j]].height;
}
}
if (c > curdes.minheight) {
curdes.minheight = c;
}
}
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;
}
}
if (c > curdes.minwidth) {
curdes.minwidth = c;
}
}
}
/*
* Compute height of highest shape in design
*/
for (i = 0; i < NUM_SHAPES; ++i) {
if (isempty(curdes.shape + i)) {
continue;
}
if (curdes.shape[i].height > curdes.maxshapeheight) {
curdes.maxshapeheight = curdes.shape[i].height;
}
}
#ifdef PARSER_DEBUG
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);
#endif
/*
* Set name of each shape
*/
for (i = 0; i < NUM_SHAPES; ++i) {
curdes.shape[i].name = i;
}
return RC_SUCCESS;
}
/**
* Rule action called when a new box design is starting to be parsed.
* @param bison_args the parser state
* @param design_name the primary name of the design
* @return 0: success;
* 1: YYERROR must be invoked;
* 2: YYABORT must be invoked
*/
int action_start_parsing_design(pass_to_bison *bison_args, char *design_name)
{
bison_args->speeding = 0;
bison_args->skipping = 0;
curdes.name = (char *) strdup(design_name);
if (curdes.name == NULL) {
perror(PROJECT);
return RC_ABORT;
}
if (!design_needed(bison_args)) {
bison_args->speeding = 1;
#ifdef PARSER_DEBUG
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_SUCCESS;
}
int action_parent_config(pass_to_bison *bison_args, bxstr_t *filepath)
{
#ifdef PARSER_DEBUG
fprintf(stderr, " Parser: parent config file specified: [%s]\n", bxs_to_output(filepath));
#endif
if (bxs_is_empty(filepath)) {
bison_args->skipping = 1;
yyerror(bison_args, "parent reference is empty");
return RC_ERROR;
}
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 */
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 = bx_fopens(filepath, "r");
if (f == NULL) {
bison_args->skipping = 1;
yyerror(bison_args, "parent config file not found: %s", bxs_to_output(filepath));
return RC_ERROR;
}
else {
fclose(f);
}
}
#ifdef PARSER_DEBUG
fprintf(stderr, " Parser: parent config file path resolved: [%s]\n", bxs_to_output(filepath));
#endif
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;
}
int action_add_design(pass_to_bison *bison_args, char *design_primary_name, char *name_at_end)
{
design_t *tmp;
#ifdef PARSER_DEBUG
fprintf(stderr, "--------- ADDING DESIGN \"%s\".\n", design_primary_name);
#endif
if (strcasecmp(design_primary_name, name_at_end)) {
yyerror(bison_args, "box design name differs at BOX and END");
return RC_ERROR;
}
if (bison_args->num_mandatory < 3) {
yyerror(bison_args, "entries SAMPLE, SHAPES, and ELASTIC are mandatory");
return RC_ERROR;
}
if (design_name_exists(bison_args, design_primary_name)) {
yyerror(bison_args, "duplicate box design name -- %s", design_primary_name);
return RC_ERROR;
}
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.
*/
if (!full_parse_required()) {
bison_args->num_designs = bison_args->design_idx + 1;
return RC_ACCEPT;
}
/*
* Allocate space for next design
*/
++(bison_args->design_idx);
tmp = (design_t *) realloc(bison_args->designs, (bison_args->design_idx + 1) * sizeof(design_t));
if (tmp == NULL) {
perror(PROJECT);
return RC_ABORT;
}
bison_args->designs = tmp;
init_design(bison_args, &(curdes));
return RC_SUCCESS;
}
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, bxs_to_output(value));
#endif
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);
return RC_ABORT;
}
}
else if (strcasecmp(keyword, "designer") == 0) {
curdes.designer = bxs_strdup(value);
if (curdes.designer == NULL) {
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, "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\"");
return RC_ERROR;
}
}
else {
yyerror(bison_args, "internal parser error (unrecognized: %s) in line %d of %s.", keyword, __LINE__, __FILE__);
return RC_ERROR;
}
return RC_SUCCESS;
}
int action_padding_entry(pass_to_bison *bison_args, char *area, int value)
{
if (value < 0) {
yyerror(bison_args, "padding must be a positive integer (%s %d) (ignored)", area, value);
}
else {
size_t len1 = strlen(area);
if (len1 <= 3 && strncasecmp("all", area, len1) == 0) {
curdes.padding[BTOP] = value;
curdes.padding[BBOT] = value;
curdes.padding[BLEF] = value;
curdes.padding[BRIG] = value;
}
else if (len1 <= 10 && strncasecmp("horizontal", area, len1) == 0) {
curdes.padding[BRIG] = value;
curdes.padding[BLEF] = value;
}
else if (len1 <= 8 && strncasecmp("vertical", area, len1) == 0) {
curdes.padding[BTOP] = value;
curdes.padding[BBOT] = value;
}
else if (len1 <= 3 && strncasecmp("top", area, len1) == 0) {
curdes.padding[BTOP] = value;
}
else if (len1 <= 5 && strncasecmp("right", area, len1) == 0) {
curdes.padding[BRIG] = value;
}
else if (len1 <= 4 && strncasecmp("left", area, len1) == 0) {
curdes.padding[BLEF] = value;
}
else if (len1 <= 6 && strncasecmp("bottom", area, len1) == 0) {
curdes.padding[BBOT] = value;
}
else {
yyerror(bison_args, "invalid padding area %s (ignored)", area);
}
}
return RC_SUCCESS;
}
int action_init_parser(pass_to_bison *bison_args)
{
bison_args->designs = (design_t *) calloc(1, sizeof(design_t));
if (bison_args->designs == NULL) {
perror(PROJECT);
return RC_ABORT;
}
bison_args->num_designs = 1;
init_design(bison_args, bison_args->designs);
return RC_SUCCESS;
}
int action_add_alias(pass_to_bison *bison_args, char *alias_name)
{
if (design_name_exists(bison_args, alias_name)) {
yyerror(bison_args, "alias already in use -- %s", alias_name);
return RC_ERROR;
}
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);
#endif
}
else {
size_t num_aliases = array_count0(curdes.aliases);
curdes.aliases = (char **) realloc(curdes.aliases, (num_aliases + 2) * sizeof(char *));
curdes.aliases[num_aliases] = strdup(alias_name);
curdes.aliases[num_aliases + 1] = NULL;
}
return RC_SUCCESS;
}
static bxstr_t *adjust_eols(uint32_t *sample)
{
if (eol_pattern == NULL) {
eol_pattern = compile_pattern("(?:(\r\n?)|(\n))");
}
uint32_t *replaced = regex_replace(eol_pattern, opt.eol, sample, u32_strlen(sample), 1);
bxstr_t *result = bxs_from_unicode(replaced);
BFREE(replaced);
return result;
}
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");
#endif
if (curdes.sample) {
yyerror(bison_args, "duplicate SAMPLE block");
return RC_ERROR;
}
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;
}
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;
}
int action_first_shape_line(pass_to_bison *bison_args, bxstr_t *line, sentry_t *shape)
{
sentry_t rval = SENTRY_INITIALIZER;
#ifdef PARSER_DEBUG
fprintf(stderr, "Initializing a shape entry with first line\n");
#endif
size_t error_pos = 0;
if (!bxs_valid_in_shape(line, &error_pos)) {
yyerror(bison_args, "invalid character in shape line at position %d", (int) error_pos);
return RC_ERROR;
}
rval.width = line->num_columns;
rval.height = 1;
rval.chars = (char **) malloc(sizeof(char *));
if (rval.chars == NULL) {
perror(PROJECT ": shape_lines21");
return RC_ABORT;
}
rval.chars[0] = (char *) strdup(line->ascii);
if (rval.chars[0] == NULL) {
perror(PROJECT ": shape_lines22");
return RC_ABORT;
}
rval.mbcs = (bxstr_t **) malloc(sizeof(bxstr_t *));
if (rval.mbcs == NULL) {
perror(PROJECT ": shape_lines23");
return RC_ABORT;
}
rval.mbcs[0] = bxs_strdup(line);
if (rval.mbcs[0] == NULL) {
perror(PROJECT ": shape_lines24");
return RC_ABORT;
}
memcpy(shape, &rval, sizeof(sentry_t));
return RC_SUCCESS;
}
int action_add_shape_line(pass_to_bison *bison_args, sentry_t *shape, bxstr_t *line)
{
#ifdef PARSER_DEBUG
fprintf(stderr, "Extending a shape entry\n");
#endif
size_t slen = line->num_columns;
if (slen != shape->width) {
yyerror(bison_args, "all elements of a shape spec must be of equal length");
return RC_ERROR;
}
size_t error_pos = 0;
if (!bxs_valid_in_shape(line, &error_pos)) {
yyerror(bison_args, "invalid character in shape line at position %d", (int) error_pos);
return RC_ERROR;
}
shape->height++;
char **tmp = (char **) realloc(shape->chars, shape->height * sizeof(char *));
if (tmp == NULL) {
perror(PROJECT ": shape_lines11");
return RC_ABORT;
}
shape->chars = tmp;
shape->chars[shape->height - 1] = (char *) strdup(line->ascii);
if (shape->chars[shape->height - 1] == NULL) {
perror(PROJECT ": shape_lines12");
return RC_ABORT;
}
bxstr_t **mtmp = (bxstr_t **) realloc(shape->mbcs, shape->height * sizeof(bxstr_t *));
if (mtmp == NULL) {
perror(PROJECT ": shape_lines13");
return RC_ABORT;
}
shape->mbcs = mtmp;
shape->mbcs[shape->height - 1] = bxs_strdup(line);
if (shape->mbcs[shape->height - 1] == NULL) {
perror(PROJECT ": shape_lines14");
return RC_ABORT;
}
return RC_SUCCESS;
}
/* vim: set cindent sw=4: */