Enable querying of the design list by tag #23

This commit is contained in:
Thomas Jensen 2021-04-03 14:13:33 +02:00
parent 95673257d9
commit f147c8f6fc
No known key found for this signature in database
GPG Key ID: A4ACEE270D0FB7DB
7 changed files with 168 additions and 19 deletions

View File

@ -45,6 +45,13 @@
#include "unicode.h"
typedef struct {
char *tag;
size_t count;
} tagstats_t;
/* _\|/_
(o o)
+----oOO-{_}-OOo------------------------------------------------------------+
@ -130,7 +137,7 @@ static void usage_long(FILE *st)
static int validate_tag(char *tag)
{
if (strcmp(tag, "(all)") == 0 || strcmp(tag, "(undoc)") == 0) {
if (strcmp(tag, QUERY_ALL) == 0 || strcmp(tag, QUERY_UNDOC) == 0) {
return 1;
}
return tag_is_valid(tag);
@ -143,6 +150,7 @@ static char **parse_tag_query(char *optarg)
char **result = NULL;
char *dup = strdup(optarg); /* required because strtok() modifies its input */
int contains_positive_element = 0;
size_t num_expr = 0;
for (char *q = strtok(dup, ","); q != NULL; q = strtok(NULL, ","))
{
@ -152,15 +160,24 @@ static char **parse_tag_query(char *optarg)
continue;
}
if (trimmed[0] == '+' || trimmed[0] == '-') {
if (!validate_tag(trimmed + 1)) {
fprintf(stderr, "%s: not a tag -- %s\n", PROJECT, trimmed + 1);
return NULL;
}
} else if (!validate_tag(trimmed)) {
fprintf(stderr, "%s: not a tag -- %s\n", PROJECT, trimmed);
if (trimmed[0] != '-') {
contains_positive_element = 1;
}
char *raw_tag = (trimmed[0] == '+' || trimmed[0] == '-') ? (trimmed + 1) : trimmed;
if (!validate_tag(raw_tag)) {
fprintf(stderr, "%s: not a tag -- %s\n", PROJECT, raw_tag);
return NULL;
}
if (result != NULL) {
for (size_t i = 0; result[i] != NULL; ++i) {
char *restag = (result[i][0] == '+' || result[i][0] == '-') ? (result[i] + 1) : result[i];
if (strcasecmp(restag, raw_tag) == 0) {
fprintf(stderr, "%s: duplicate query expression -- %s\n", PROJECT, trimmed);
return NULL;
}
}
}
++num_expr;
result = (char **) realloc(result, (num_expr + 1) * sizeof(char *));
@ -172,6 +189,23 @@ static char **parse_tag_query(char *optarg)
result[num_expr] = NULL;
}
BFREE(dup);
if (num_expr == 0) {
fprintf(stderr, "%s: empty tag query -- %s\n", PROJECT, optarg);
return NULL;
}
if (!contains_positive_element) {
++num_expr;
result = (char **) realloc(result, (num_expr + 1) * sizeof(char *));
if (result == NULL) {
perror(PROJECT);
}
else {
result[num_expr - 1] = QUERY_ALL;
result[num_expr] = NULL;
}
}
return result;
}
@ -827,10 +861,89 @@ static char *names(design_t *design)
int query_is_undoc()
{
return opt.query != NULL && strcmp(opt.query[0], QUERY_UNDOC) == 0 && opt.query[1] == NULL;
}
static int filter_by_tag(char **tags)
{
// TODO
return 1; // TODO or 0 if the tags don't match
#ifdef DEBUG
fprintf(stderr, "filter_by_tag(");
for (size_t tidx = 0; tags[tidx] != NULL; ++tidx) {
fprintf(stderr, "%s%s", tidx > 0 ? ", " : "", tags[tidx]);
}
#endif
int result = array_contains0(opt.query, QUERY_ALL);
if (opt.query != NULL) {
for (size_t qidx = 0; opt.query[qidx] != NULL; ++qidx) {
if (opt.query[qidx][0] == '+') {
result = array_contains0(tags, opt.query[qidx] + 1);
if (!result) {
break;
}
}
else if (opt.query[qidx][0] == '-') {
if (array_contains0(tags, opt.query[qidx] + 1)) {
result = 0;
break;
}
}
else if (array_contains0(tags, opt.query[qidx])) {
result = 1;
}
}
}
#ifdef DEBUG
fprintf(stderr, ") -> %d\n", result);
#endif
return result;
}
static void count_tag(char *tag, tagstats_t **tagstats, size_t *num_tags)
{
if (*tagstats != NULL) {
for (size_t i = 0; i < (*num_tags); ++i) {
if (strcasecmp((*tagstats)[i].tag, tag) == 0) {
++((*tagstats)[i].count);
return;
}
}
}
++(*num_tags);
*tagstats = realloc(*tagstats, (*num_tags) * sizeof(tagstats_t));
(*tagstats)[(*num_tags) - 1].tag = tag;
(*tagstats)[(*num_tags) - 1].count = 1;
}
static int tagstats_sort(const void *p1, const void *p2)
{
return strcasecmp(((tagstats_t *) p1)->tag, ((tagstats_t *) p2)->tag);
}
static void print_tags(tagstats_t *tagstats, size_t num_tags)
{
if (tagstats != NULL) {
qsort(tagstats, num_tags, sizeof(tagstats_t), tagstats_sort);
for (size_t tidx = 0; tidx < num_tags; ++tidx) {
if (tidx > 0) {
fprintf(opt.outfile, " | ");
}
fprintf(opt.outfile, "%s (%d)", tagstats[tidx].tag, (int) tagstats[tidx].count);
}
fprintf(opt.outfile, "\n");
}
}
@ -997,7 +1110,7 @@ static int list_styles()
/*
* Display all shapes
*/
if (opt.query != NULL) {
if (query_is_undoc()) {
fprintf(opt.outfile, "Sample:\n%s\n", d->sample);
}
else {
@ -1036,6 +1149,8 @@ static int list_styles()
}
qsort(list, anz_designs, sizeof(design_t *), style_sort);
tagstats_t *tagstats = NULL;
size_t num_tags = 0;
if (opt.query == NULL) {
print_design_list_header();
}
@ -1065,9 +1180,18 @@ static int list_styles()
fprintf(opt.outfile, "%s:\n\n%s\n\n", all_names, list[i]->sample);
}
BFREE(all_names);
for (size_t tidx = 0; list[i]->tags[tidx] != NULL; ++tidx) {
count_tag(list[i]->tags[tidx], &tagstats, &num_tags);
}
}
}
BFREE (list);
BFREE(list);
if (opt.query == NULL) {
print_tags(tagstats, num_tags);
BFREE(tagstats);
}
}
return 0;

View File

@ -145,6 +145,13 @@ typedef struct { /* Command line options: */
extern opt_t opt;
#define QUERY_ALL "(all)"
#define QUERY_UNDOC "(undoc)"
/** Check if -q "(undoc)" was specified. */
int query_is_undoc();
typedef struct {
size_t len; /* length of visible text in columns (visible character positions in a text terminal), which is the same as the length of the 'text' field */

View File

@ -1034,7 +1034,7 @@ int output_box(const sentry_t *thebox)
}
/* add info line for web ui if requested with -q */
if (opt.query != NULL) {
if (query_is_undoc()) {
fprintf(opt.outfile, "%d ", (int) (thebox[BTOP].height + vfill1_save - skip_start));
for (j = 0; j < input.anz_lines; j++) {
fprintf(opt.outfile, "%d%s",

View File

@ -745,9 +745,7 @@ layout: layout entry | layout block | entry | block ;
tag_entry: STRING
{
if (tag_record(bison_args, $1) != 0) {
YYABORT;
}
tag_record(bison_args, $1); /* discard return code (we print warnings, but tolerate the problem) */
}
tag_list: tag_entry | tag_list ',' tag_entry;
@ -794,9 +792,7 @@ entry: KEYWORD STRING
}
}
else if (strcasecmp ($1, "tags") == 0) {
if (tag_record(bison_args, $2) != 0) {
YYABORT;
}
tag_record(bison_args, $2); /* discard return code (we print warnings, but tolerate the problem) */
}
else if (strcasecmp ($1, "indent") == 0) {
if (strcasecmp ($2, "text") == 0 ||

View File

@ -0,0 +1,6 @@
:ARGS
-f 14x_tag_query.cfg -q non-existent
:INPUT
:OUTPUT-FILTER
:EXPECTED
:EOF

View File

@ -0,0 +1,9 @@
:ARGS
-f 14x_tag_query.cfg -q -tag1,tag2
:INPUT
:OUTPUT-FILTER
:EXPECTED
designB
aliasB1 (alias)
aliasB2 (alias)
:EOF

View File

@ -0,0 +1,7 @@
:ARGS
-q ,,
:INPUT
:OUTPUT-FILTER
:EXPECTED-ERROR 1
boxes: empty tag query -- ,,
:EOF