mirror of
https://github.com/ascii-boxes/boxes.git
synced 2024-12-12 18:01:14 +01:00
316 lines
9.8 KiB
C
316 lines
9.8 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/>.
|
|
*
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
*/
|
|
|
|
/*
|
|
* Invocation of the configuration file parser
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
|
|
#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: */
|