Handle tags as a real list in memory and config file #23

The previous keyword-based syntax remains supported for
forwards compatibility reasons.
This commit is contained in:
Thomas Jensen 2021-04-02 15:45:04 +02:00
parent e360bfde4e
commit b51724f3b1
No known key found for this signature in database
GPG Key ID: A4ACEE270D0FB7DB
4 changed files with 140 additions and 16 deletions

View File

@ -164,8 +164,7 @@ static int process_commandline(int argc, char *argv[])
/*
* Intercept '--help' and '-?' cases first, as they are not supported by getopt()
*/
if (argc >= 2 && argv[1] != NULL
&& (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)) {
if (argc >= 2 && argv[1] != NULL && (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)) {
usage_long(stdout);
return 42;
}
@ -632,8 +631,8 @@ static int build_design(design_t **adesigns, const char *cld)
dp->padding[BLEF] = 1;
dp->defined_in = "(command line)";
dp->tags = (char *) calloc(10, sizeof(char));
strcpy(dp->tags, "transient");
dp->tags = (char **) calloc(2, sizeof(char *));
dp->tags[0] = "transient";
dp->shape[W].height = 1;
dp->shape[W].width = strlen(cld);
@ -901,8 +900,16 @@ static int list_styles()
empty_side(opt.design->shape, BTOP) &&
empty_side(opt.design->shape, BBOT) ? "no" : "yes");
fprintf(opt.outfile, "Tags: %s\n",
d->tags ? d->tags : "(none)");
fprintf(opt.outfile, "Tags: ");
size_t tidx = 0;
while(d->tags[tidx] != NULL) {
fprintf(opt.outfile, "%s%s", tidx > 0 ? ", " : "", d->tags[tidx]);
++tidx;
}
if (tidx == 0) {
fprintf(opt.outfile, "none");
}
fprintf(opt.outfile, "\n");
fprintf(opt.outfile, "Elastic Shapes: ");
sstart = 0;

View File

@ -105,7 +105,7 @@ typedef struct {
size_t minwidth;
size_t minheight;
int padding[ANZ_SIDES];
char *tags;
char **tags;
char *defined_in; /* path to config file where this was defined */
reprule_t *current_rule;

View File

@ -266,6 +266,12 @@ ends[ \t\r]*$ {
}
Tags {
#ifdef LEXER_DEBUG
fprintf (stderr, " YTAGS: %s\n", yytext);
#endif
return YTAGS;
}
Elastic {
BEGIN(ELASTIC);
@ -327,7 +333,7 @@ Once { yylval->c = 'o'; return YRXPFLAG; }
}
author|designer|tags|created|revision|revdate|indent {
author|designer|created|revision|revdate|indent {
/*
* general key words
*/

View File

@ -326,6 +326,7 @@ static void init_design(pass_to_bison *bison_args, design_t *design)
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 *));
}
@ -413,6 +414,109 @@ static int design_name_exists(pass_to_bison *bison_args, char *name)
return result;
}
static int tag_is_valid(char *tag)
{
const size_t len = strlen(tag);
return len > 0
&& strspn(tag, "abcdefghijklmnopqrstuvwxyz-0123456789") == len
&& strchr("abcdefghijklmnopqrstuvwxyz", tag[0]) != NULL
&& tag[len - 1] != '-'
&& strstr(tag, "--") == NULL;
}
static int tag_add(pass_to_bison *bison_args, char *tag)
{
int rc = 0;
if (tag_is_valid(tag)) {
if (!array_contains0(curdes.tags, tag)) {
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 + 1] = NULL;
}
else {
yyerror(bison_args, "duplicate tag -- %s", tag);
rc = 1;
}
}
else {
yyerror(bison_args, "invalid tag -- %s", tag);
rc = 2;
}
return rc;
}
static char *tag_next_comma(char *s)
{
char *comma = strchr(s, ',');
if (comma == NULL) {
comma = s + strlen(s);
}
return comma;
}
static char *tag_trim(char *s, char *e)
{
if (s > e || (s == e && *s == '\0')) {
return strdup("");
}
while (s <= e && (*s == ' ' || *s == '\t')) {
++s;
}
while (e > s && (*e == ' ' || *e == '\t')) {
--e;
}
return strndup(s, e - s + 1);
}
static int tag_split_add(pass_to_bison *bison_args, char *tag)
{
int rc = 0;
char *s = tag;
char *c = NULL;
do {
c = tag_next_comma(s);
char *single_tag = tag_trim(s, c - 1);
int rc_add = tag_add(bison_args, single_tag);
if (rc_add != 0) {
rc = rc_add;
}
s = c + 1;
} while (*c != '\0');
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
*/
static int tag_record(pass_to_bison *bison_args, char *tag)
{
int rc = 0;
if (strchr(tag, ',') != NULL) {
rc = tag_split_add(bison_args, tag);
}
else {
char *trimmed = tag_trim(tag, tag + strlen(tag) - 1);
rc = tag_add(bison_args, trimmed);
}
return rc;
}
}
@ -438,7 +542,7 @@ static int design_name_exists(pass_to_bison *bison_args, char *name)
}
%token YPARENT YSHAPES YELASTIC YPADDING YSAMPLE YENDSAMPLE YBOX YEND YUNREC
%token YREPLACE YREVERSE YTO YWITH YCHGDEL
%token YREPLACE YREVERSE YTO YWITH YCHGDEL YTAGS
%token <s> KEYWORD
%token <s> WORD
%token <s> STRING
@ -575,10 +679,7 @@ alias: WORD
yyerror(bison_args, "alias already in use -- %s", $1);
YYERROR;
}
size_t num_aliases = 0;
while (curdes.aliases[num_aliases] != NULL) {
++num_aliases;
}
size_t num_aliases = array_count0(curdes.aliases);
curdes.aliases = (char **) realloc(curdes.aliases, (num_aliases + 2) * sizeof(char *));
curdes.aliases[num_aliases] = strdup($1);
curdes.aliases[num_aliases + 1] = NULL;
@ -670,6 +771,16 @@ layout YEND WORD
layout: layout entry | layout block | entry | block ;
tag_entry: STRING
{
if (tag_record(bison_args, $1) != 0) {
YYABORT;
}
}
tag_list: tag_entry | tag_list ',' tag_entry;
entry: KEYWORD STRING
{
#ifdef PARSER_DEBUG
@ -711,9 +822,7 @@ entry: KEYWORD STRING
}
}
else if (strcasecmp ($1, "tags") == 0) {
curdes.tags = (char *) strdup ($2);
if (curdes.tags == NULL) {
perror (PROJECT);
if (tag_record(bison_args, $2) != 0) {
YYABORT;
}
}
@ -772,6 +881,8 @@ entry: KEYWORD STRING
chg_strdelims(bison_args, $2[0], $2[1]);
}
| YTAGS '(' tag_list ')' | YTAGS tag_entry
| WORD STRING
{
#ifdef PARSER_DEBUG