/* * 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 . * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* * Invocation of the configuration file parser */ #include "config.h" #include #include #include #include #include #include "boxes.h" #include "bxstring.h" #include "parsing.h" #include "tools.h" #include "parser.h" #include "lex.yy.h" /** file handle of the config file currently being parsed */ static FILE *current_config_handle = NULL; /** 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 bxstr_t *first_config_file = NULL; /** all parent configs encountered across all parsed config files */ static bxstr_t **parent_configs = NULL; /** total number of parent configs (the size of the `parent_configs` array) */ static size_t num_parent_configs = 0; /** * Set yyin to the config file to be used. * * @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) { current_config_handle = bx_fopens(bison_args->config_file, "r"); if (current_config_handle == NULL) { fprintf(stderr, "%s: Couldn't open config file '%s' for input\n", PROJECT, bxs_to_output(bison_args->config_file)); return 1; } yyset_in(current_config_handle, bison_args->lexer_state); return 0; } void print_design_list_header() { char buf[42]; sprintf(buf, "%d", num_designs); fprintf(opt.outfile, "%d Available Style%s", num_designs, num_designs == 1 ? "" : "s"); if (num_parent_configs > 0) { fprintf(opt.outfile, ":%s", opt.eol); fprintf(opt.outfile, "-----------------%s", num_designs == 1 ? "" : "-"); for (int i = strlen(buf); i > 0; --i) { fprintf(opt.outfile, "-"); } fprintf(opt.outfile, "%s%s", opt.eol, opt.eol); fprintf(opt.outfile, "Configuration Files:%s", opt.eol); fprintf(opt.outfile, " - %s%s", bxs_to_output(first_config_file), opt.eol); for (size_t i = 0; i < num_parent_configs; i++) { fprintf(opt.outfile, " - %s (parent)%s", bxs_to_output(parent_configs[i]), opt.eol); } } else { fprintf(opt.outfile, " in \"%s\":%s", bxs_to_output(first_config_file), opt.eol); fprintf(opt.outfile, "-----------------------%s", num_designs == 1 ? "" : "-"); for (int i = first_config_file->num_columns + strlen(buf); i > 0; --i) { fprintf(opt.outfile, "-"); } } fprintf(opt.outfile, "%s%s", opt.eol, opt.eol); } /** * Print configuration file parser errors. * @param bison_args pointer to the parser arguments * @param fmt a format string for `vfprintf()`, followed by the arguments * @return 0 */ int yyerror(pass_to_bison *bison_args, const char *fmt, ...) { va_list ap; va_start (ap, fmt); pass_to_bison *bargs = bison_args ? bison_args : current_bison_args; fprintf(stderr, "%s: %s: line %d: ", PROJECT, bxs_to_output(bargs->config_file), yyget_lineno(bargs->lexer_state)); vfprintf(stderr, fmt, ap); fputc('\n', stderr); va_end (ap); return 0; } static pass_to_bison new_bison_args(bxstr_t *config_file) { pass_to_bison bison_args; bison_args.designs = NULL; bison_args.num_designs = 0; bison_args.design_idx = 0; bison_args.config_file = config_file; 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_flex flex_extra_data; flex_extra_data.yyerrcnt = 0; flex_extra_data.sdel = '\"'; flex_extra_data.sesc = '\\'; return flex_extra_data; } static pass_to_bison parse_config_file(bxstr_t *config_file, design_t *child_configs, size_t num_child_configs) { #ifdef DEBUG fprintf (stderr, "Parsing Config File %s ...\n", bxs_to_output(config_file)); #endif pass_to_bison bison_args = new_bison_args(config_file); bison_args.child_configs = child_configs; bison_args.num_child_configs = num_child_configs; pass_to_flex flex_extra_data = new_flex_extra_data(); current_bison_args = &bison_args; yylex_init (&(bison_args.lexer_state)); yylex_init_extra(&flex_extra_data, &(bison_args.lexer_state)); int rc = open_yy_config_file(&bison_args); if (rc) { return new_bison_args(config_file); } inflate_inbuf(bison_args.lexer_state, config_file); rc = yyparse(&bison_args); yylex_destroy(bison_args.lexer_state); if (current_config_handle != NULL) { fclose(current_config_handle); current_config_handle = NULL; } if (rc) { #ifdef DEBUG fprintf (stderr, "yyparse() returned %d\n", rc); #endif return new_bison_args(config_file); } return bison_args; } static int designs_contain(design_t *designs, size_t num_designs, design_t adesign) { 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; } 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--) { bxstr_t *parent_file = bison_args->parent_configs[parent_idx]; int is_new = !array_contains_bxs(parent_configs, num_parent_configs, parent_file); if (is_new) { parent_configs = (bxstr_t **) realloc(parent_configs, (num_parent_configs + 1) * sizeof(bxstr_t *)); 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(bxstr_t *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; bxstr_t *config_file = p_first_config_file; do { pass_to_bison bison_args = parse_config_file(config_file, result, *r_num_designs); ++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; } fflush(stderr); /* ensure order of error msgs and normal output for test cases */ return result; } /*EOF*/ /* vim: set sw=4: */