mirror of
https://github.com/ascii-boxes/boxes.git
synced 2024-12-12 09:51:10 +01:00
Enable querying of the design list by tag #23
This commit is contained in:
parent
95673257d9
commit
f147c8f6fc
148
src/boxes.c
148
src/boxes.c
@ -45,6 +45,13 @@
|
|||||||
#include "unicode.h"
|
#include "unicode.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char *tag;
|
||||||
|
size_t count;
|
||||||
|
} tagstats_t;
|
||||||
|
|
||||||
|
|
||||||
/* _\|/_
|
/* _\|/_
|
||||||
(o o)
|
(o o)
|
||||||
+----oOO-{_}-OOo------------------------------------------------------------+
|
+----oOO-{_}-OOo------------------------------------------------------------+
|
||||||
@ -130,7 +137,7 @@ static void usage_long(FILE *st)
|
|||||||
|
|
||||||
static int validate_tag(char *tag)
|
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 1;
|
||||||
}
|
}
|
||||||
return tag_is_valid(tag);
|
return tag_is_valid(tag);
|
||||||
@ -143,6 +150,7 @@ static char **parse_tag_query(char *optarg)
|
|||||||
char **result = NULL;
|
char **result = NULL;
|
||||||
char *dup = strdup(optarg); /* required because strtok() modifies its input */
|
char *dup = strdup(optarg); /* required because strtok() modifies its input */
|
||||||
|
|
||||||
|
int contains_positive_element = 0;
|
||||||
size_t num_expr = 0;
|
size_t num_expr = 0;
|
||||||
for (char *q = strtok(dup, ","); q != NULL; q = strtok(NULL, ","))
|
for (char *q = strtok(dup, ","); q != NULL; q = strtok(NULL, ","))
|
||||||
{
|
{
|
||||||
@ -152,15 +160,24 @@ static char **parse_tag_query(char *optarg)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trimmed[0] == '+' || trimmed[0] == '-') {
|
if (trimmed[0] != '-') {
|
||||||
if (!validate_tag(trimmed + 1)) {
|
contains_positive_element = 1;
|
||||||
fprintf(stderr, "%s: not a tag -- %s\n", PROJECT, trimmed + 1);
|
}
|
||||||
return NULL;
|
|
||||||
}
|
char *raw_tag = (trimmed[0] == '+' || trimmed[0] == '-') ? (trimmed + 1) : trimmed;
|
||||||
} else if (!validate_tag(trimmed)) {
|
if (!validate_tag(raw_tag)) {
|
||||||
fprintf(stderr, "%s: not a tag -- %s\n", PROJECT, trimmed);
|
fprintf(stderr, "%s: not a tag -- %s\n", PROJECT, raw_tag);
|
||||||
return NULL;
|
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;
|
++num_expr;
|
||||||
result = (char **) realloc(result, (num_expr + 1) * sizeof(char *));
|
result = (char **) realloc(result, (num_expr + 1) * sizeof(char *));
|
||||||
@ -172,6 +189,23 @@ static char **parse_tag_query(char *optarg)
|
|||||||
result[num_expr] = NULL;
|
result[num_expr] = NULL;
|
||||||
}
|
}
|
||||||
BFREE(dup);
|
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;
|
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)
|
static int filter_by_tag(char **tags)
|
||||||
{
|
{
|
||||||
// TODO
|
#ifdef DEBUG
|
||||||
return 1; // TODO or 0 if the tags don't match
|
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
|
* Display all shapes
|
||||||
*/
|
*/
|
||||||
if (opt.query != NULL) {
|
if (query_is_undoc()) {
|
||||||
fprintf(opt.outfile, "Sample:\n%s\n", d->sample);
|
fprintf(opt.outfile, "Sample:\n%s\n", d->sample);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -1036,6 +1149,8 @@ static int list_styles()
|
|||||||
}
|
}
|
||||||
qsort(list, anz_designs, sizeof(design_t *), style_sort);
|
qsort(list, anz_designs, sizeof(design_t *), style_sort);
|
||||||
|
|
||||||
|
tagstats_t *tagstats = NULL;
|
||||||
|
size_t num_tags = 0;
|
||||||
if (opt.query == NULL) {
|
if (opt.query == NULL) {
|
||||||
print_design_list_header();
|
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);
|
fprintf(opt.outfile, "%s:\n\n%s\n\n", all_names, list[i]->sample);
|
||||||
}
|
}
|
||||||
BFREE(all_names);
|
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;
|
return 0;
|
||||||
|
@ -145,6 +145,13 @@ typedef struct { /* Command line options: */
|
|||||||
|
|
||||||
extern opt_t opt;
|
extern opt_t opt;
|
||||||
|
|
||||||
|
#define QUERY_ALL "(all)"
|
||||||
|
#define QUERY_UNDOC "(undoc)"
|
||||||
|
|
||||||
|
/** Check if -q "(undoc)" was specified. */
|
||||||
|
int query_is_undoc();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
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 */
|
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 */
|
||||||
|
@ -1034,7 +1034,7 @@ int output_box(const sentry_t *thebox)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* add info line for web ui if requested with -q */
|
/* 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));
|
fprintf(opt.outfile, "%d ", (int) (thebox[BTOP].height + vfill1_save - skip_start));
|
||||||
for (j = 0; j < input.anz_lines; j++) {
|
for (j = 0; j < input.anz_lines; j++) {
|
||||||
fprintf(opt.outfile, "%d%s",
|
fprintf(opt.outfile, "%d%s",
|
||||||
|
@ -745,9 +745,7 @@ layout: layout entry | layout block | entry | block ;
|
|||||||
|
|
||||||
tag_entry: STRING
|
tag_entry: STRING
|
||||||
{
|
{
|
||||||
if (tag_record(bison_args, $1) != 0) {
|
tag_record(bison_args, $1); /* discard return code (we print warnings, but tolerate the problem) */
|
||||||
YYABORT;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tag_list: tag_entry | tag_list ',' tag_entry;
|
tag_list: tag_entry | tag_list ',' tag_entry;
|
||||||
@ -794,9 +792,7 @@ entry: KEYWORD STRING
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (strcasecmp ($1, "tags") == 0) {
|
else if (strcasecmp ($1, "tags") == 0) {
|
||||||
if (tag_record(bison_args, $2) != 0) {
|
tag_record(bison_args, $2); /* discard return code (we print warnings, but tolerate the problem) */
|
||||||
YYABORT;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (strcasecmp ($1, "indent") == 0) {
|
else if (strcasecmp ($1, "indent") == 0) {
|
||||||
if (strcasecmp ($2, "text") == 0 ||
|
if (strcasecmp ($2, "text") == 0 ||
|
||||||
|
6
test/151_tag_query_no_results.txt
Normal file
6
test/151_tag_query_no_results.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
:ARGS
|
||||||
|
-f 14x_tag_query.cfg -q non-existent
|
||||||
|
:INPUT
|
||||||
|
:OUTPUT-FILTER
|
||||||
|
:EXPECTED
|
||||||
|
:EOF
|
9
test/163_tag_query_minus3.txt
Normal file
9
test/163_tag_query_minus3.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
:ARGS
|
||||||
|
-f 14x_tag_query.cfg -q -tag1,tag2
|
||||||
|
:INPUT
|
||||||
|
:OUTPUT-FILTER
|
||||||
|
:EXPECTED
|
||||||
|
designB
|
||||||
|
aliasB1 (alias)
|
||||||
|
aliasB2 (alias)
|
||||||
|
:EOF
|
7
test/164_tag_query_empty.txt
Normal file
7
test/164_tag_query_empty.txt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
:ARGS
|
||||||
|
-q ,,
|
||||||
|
:INPUT
|
||||||
|
:OUTPUT-FILTER
|
||||||
|
:EXPECTED-ERROR 1
|
||||||
|
boxes: empty tag query -- ,,
|
||||||
|
:EOF
|
Loading…
Reference in New Issue
Block a user