Extract tag query functionality into its own, new 'query' module #78

This commit is contained in:
Thomas Jensen 2021-04-16 21:22:45 +02:00
parent 4d0dbcd59b
commit 82bc084bad
No known key found for this signature in database
GPG Key ID: A4ACEE270D0FB7DB
8 changed files with 257 additions and 151 deletions

View File

@ -28,11 +28,11 @@ GEN_HDR = parser.h boxes.h lex.yy.h
GEN_SRC = parser.c lex.yy.c
GEN_FILES = $(GEN_SRC) $(GEN_HDR)
ORIG_HDRCL = boxes.in.h config.h
ORIG_HDR = $(ORIG_HDRCL) cmdline.h discovery.h generate.h input.h list.h parsecode.h parsing.h regulex.h remove.h \
shape.h tools.h unicode.h
ORIG_HDR = $(ORIG_HDRCL) cmdline.h discovery.h generate.h input.h list.h parsecode.h parsing.h query.h regulex.h \
remove.h shape.h tools.h unicode.h
ORIG_GEN = lexer.l parser.y
ORIG_NORM = boxes.c cmdline.c discovery.c generate.c input.c list.c parsecode.c parsing.c regulex.c remove.c shape.c \
tools.c unicode.c
ORIG_NORM = boxes.c cmdline.c discovery.c generate.c input.c list.c parsecode.c parsing.c query.c regulex.c remove.c \
shape.c tools.c unicode.c
ORIG_SRC = $(ORIG_GEN) $(ORIG_NORM)
ORIG_FILES = $(ORIG_SRC) $(ORIG_HDR)
@ -95,10 +95,11 @@ generate.o: generate.c generate.h boxes.h shape.h tools.h unicode.h config.h |
getopt.o: misc/getopt.c misc/getopt.h | check_dir
input.o: input.c boxes.h input.h regulex.h tools.h unicode.h config.h | check_dir
lex.yy.o: lex.yy.c parser.h boxes.h parsing.h tools.h shape.h config.h | check_dir
list.o: list.c list.h boxes.h parsing.h tools.h config.h | check_dir
parsecode.o: parsecode.c parser.h boxes.h tools.h lex.yy.h regulex.h unicode.h config.h | check_dir
list.o: list.c list.h boxes.h parsing.h query.h tools.h config.h | check_dir
parsecode.o: parsecode.c parser.h boxes.h tools.h lex.yy.h query.h regulex.h unicode.h config.h | check_dir
parser.o: parser.c boxes.h lex.yy.h parser.h parsing.h tools.h shape.h discovery.h config.h | check_dir
parsing.o: parsing.c parsing.h parser.h lex.yy.h boxes.h tools.h config.h | check_dir
query.o: query.c query.h boxes.h list.h tools.h config.h | check_dir
regulex.o: regulex.c regulex.h boxes.h tools.h unicode.h config.h | check_dir
remove.o: remove.c remove.h boxes.h shape.h tools.h unicode.h config.h | check_dir
shape.o: shape.c shape.h boxes.h tools.h config.h | check_dir

View File

@ -31,6 +31,7 @@
#include "input.h"
#include "list.h"
#include "parsing.h"
#include "query.h"
#include "remove.h"
#include "tools.h"
#include "unicode.h"
@ -158,71 +159,6 @@ static int build_design(design_t **adesigns, const char *cld)
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)
{
#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 int query_by_tag()
{
design_t **list = sort_designs_by_name(); /* temp list for sorting */
if (list == NULL) {
return 1;
}
for (int i = 0; i < num_designs; ++i) {
if (filter_by_tag(list[i]->tags)) {
fprintf(opt.outfile, "%s%s", list[i]->name, opt.eol);
for (size_t aidx = 0; list[i]->aliases[aidx] != NULL; ++aidx) {
fprintf(opt.outfile, "%s (alias)%s", list[i]->aliases[aidx], opt.eol);
}
}
}
BFREE(list);
return 0;
}
/* _\|/_
(o o)
+----oOO-{_}-OOo------------------------------------------------------------+

View File

@ -134,13 +134,6 @@ 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

@ -37,6 +37,7 @@
#include "boxes.h"
#include "discovery.h"
#include "query.h"
#include "tools.h"
#include "cmdline.h"
@ -109,16 +110,6 @@ void usage_long(FILE *st)
static int validate_tag(char *tag)
{
if (strcmp(tag, QUERY_ALL) == 0 || strcmp(tag, QUERY_UNDOC) == 0) {
return 1;
}
return tag_is_valid(tag);
}
static opt_t *create_new_opt()
{
opt_t *result = (opt_t *) calloc(1, sizeof(opt_t));
@ -427,70 +418,11 @@ static int padding(opt_t *result, char *optarg)
* @param optarg the argument to `-q` on the command line
* @returns 0 on success, anything else on error
*/
static int query_by_tag(opt_t *result, char *optarg)
static int query(opt_t *result, char *optarg)
{
char **query = 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, ","))
{
char *trimmed = trimdup(q, q + strlen(q) - 1);
if (strlen(trimmed) == 0) {
BFREE(trimmed);
continue;
}
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 1;
}
if (query != NULL) {
for (size_t i = 0; query[i] != NULL; ++i) {
char *restag = (query[i][0] == '+' || query[i][0] == '-') ? (query[i] + 1) : query[i];
if (strcasecmp(restag, raw_tag) == 0) {
fprintf(stderr, "%s: duplicate query expression -- %s\n", PROJECT, trimmed);
return 1;
}
}
}
++num_expr;
query = (char **) realloc(query, (num_expr + 1) * sizeof(char *));
if (query == NULL) {
perror(PROJECT);
break;
}
query[num_expr - 1] = trimmed;
query[num_expr] = NULL;
}
BFREE(dup);
if (num_expr == 0) {
fprintf(stderr, "%s: empty tag query -- %s\n", PROJECT, optarg);
return 1;
}
if (!contains_positive_element) {
++num_expr;
query = (char **) realloc(query, (num_expr + 1) * sizeof(char *));
if (query == NULL) {
perror(PROJECT);
}
else {
query[num_expr - 1] = QUERY_ALL;
query[num_expr] = NULL;
}
}
char **query = parse_query(optarg);
result->query = query;
return 0;
return query != NULL ? 0 : 1;
}
@ -771,7 +703,7 @@ opt_t *process_commandline(int argc, char *argv[])
break;
case 'q':
if (query_by_tag(result, optarg) != 0) {
if (query(result, optarg) != 0) {
return NULL;
}
break;

View File

@ -29,6 +29,7 @@
#include "boxes.h"
#include "parsing.h"
#include "query.h"
#include "tools.h"
#include "list.h"

View File

@ -32,6 +32,7 @@
#include "tools.h"
#include "regulex.h"
#include "unicode.h"
#include "query.h"
#include "parsecode.h"
#include "parsing.h"
#include "parser.h"

189
src/query.c Normal file
View File

@ -0,0 +1,189 @@
/*
* boxes - Command line filter to draw/remove ASCII boxes around text
* Copyright (c) 1999-2021 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 2, 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, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
/*
* Functions related to querying the design list by tag.
*/
#include "config.h"
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include "boxes.h"
#include "list.h"
#include "tools.h"
#include "query.h"
#define QUERY_ALL "(all)"
#define QUERY_UNDOC "(undoc)"
static int validate_tag(char *tag)
{
if (strcmp(tag, QUERY_ALL) == 0 || strcmp(tag, QUERY_UNDOC) == 0) {
return 1;
}
return tag_is_valid(tag);
}
char **parse_query(char *optarg)
{
/* CAUTION: This function cannot use `opt`, because it is involved in its construction. */
char **query = 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, ","))
{
char *trimmed = trimdup(q, q + strlen(q) - 1);
if (strlen(trimmed) == 0) {
BFREE(trimmed);
continue;
}
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);
BFREE(trimmed);
BFREE(query);
return NULL;
}
if (query != NULL) {
for (size_t i = 0; query[i] != NULL; ++i) {
char *restag = (query[i][0] == '+' || query[i][0] == '-') ? (query[i] + 1) : query[i];
if (strcasecmp(restag, raw_tag) == 0) {
fprintf(stderr, "%s: duplicate query expression -- %s\n", PROJECT, trimmed);
BFREE(trimmed);
BFREE(query);
return NULL;
}
}
}
++num_expr;
query = (char **) realloc(query, (num_expr + 1) * sizeof(char *));
if (query == NULL) {
perror(PROJECT);
break;
}
query[num_expr - 1] = trimmed;
query[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;
query = (char **) realloc(query, (num_expr + 1) * sizeof(char *));
if (query == NULL) {
perror(PROJECT);
}
else {
query[num_expr - 1] = QUERY_ALL;
query[num_expr] = NULL;
}
}
return query;
}
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)
{
#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;
}
int query_by_tag()
{
design_t **list = sort_designs_by_name(); /* temp list for sorting */
if (list == NULL) {
return 1;
}
for (int i = 0; i < num_designs; ++i) {
if (filter_by_tag(list[i]->tags)) {
fprintf(opt.outfile, "%s%s", list[i]->name, opt.eol);
for (size_t aidx = 0; list[i]->aliases[aidx] != NULL; ++aidx) {
fprintf(opt.outfile, "%s (alias)%s", list[i]->aliases[aidx], opt.eol);
}
}
}
BFREE(list);
return 0;
}
/*EOF*/ /* vim: set sw=4: */

53
src/query.h Normal file
View File

@ -0,0 +1,53 @@
/*
* boxes - Command line filter to draw/remove ASCII boxes around text
* Copyright (c) 1999-2021 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 2, 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, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
/*
* Functions related to querying the design list by tag.
*/
#ifndef QUERY_H
#define QUERY_H
/**
* Parse the tag query specified with the `-q` command line option.
* @param optarg the argument to `-q` on the command line
* @returns the parsed query which can be assigned to the global `opt` struct later, or `NULL` on error
*/
char **parse_query(char *optarg);
/**
* Check if -q "(undoc)" was specified.
* @returns flag
*/
int query_is_undoc();
/**
* Perform the tag query based on the global design list and the query from the global `opt` struct.
* @returns 0 if successful; anything else on error (then the program should exit)
*/
int query_by_tag();
#endif
/*EOF*/ /* vim: set cindent sw=4: */