From 94ed9b3061c85d2dcee290c0e207a5c7fc7c8371 Mon Sep 17 00:00:00 2001 Message-Id: <94ed9b3061c85d2dcee290c0e207a5c7fc7c8371.1713944629.git.mdw@distorted.org.uk> From: Mark Wooding Date: Fri, 25 Mar 2005 13:21:14 +0000 Subject: [PATCH] Add filtering by length, and retaining only longest/shortest matches. Also return nonzero and print a message if no matches were found. Organization: Straylight/Edgeware From: mdw --- Makefile.am | 2 +- anag.c | 116 +++++++++++++++++++++++++++++++++++++++++++++--- anag.h | 26 ++++++++++- longest.c | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++++ util.c | 4 +- 5 files changed, 262 insertions(+), 10 deletions(-) create mode 100644 longest.c diff --git a/Makefile.am b/Makefile.am index d3c2d5d..06c94ad 100644 --- a/Makefile.am +++ b/Makefile.am @@ -40,7 +40,7 @@ java_DATA = @JARFILES@ anag_SOURCES = \ anag.c anag.h util.c \ - wildcard.c anagram.c mono.c trackword.c regexp.c pcre.c + wildcard.c anagram.c mono.c trackword.c regexp.c pcre.c longest.c anag.jar: AnagGUI.class jar cfm anag.jar $(srcdir)/anag.manifest Anag*.class Whinge*.class diff --git a/anag.c b/anag.c index 616461c..d4413bd 100644 --- a/anag.c +++ b/anag.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: anag.c,v 1.8 2004/04/08 01:36:19 mdw Exp $ + * $Id$ * * Main driver for anag * @@ -80,6 +80,9 @@ The basic tests in the expression are:\n\ " #endif "\ +-length [+|-]N matches if length is [at least|at most] N\n\ +-longest output longest matches found here\n\ +-shortest output shortest matches found here\n\ \n\ These simple tests can be combined using the operators `-a', `-o' and `-n'\n\ (for `and', `or' and `not'; they may also be written `&', `|' and `!' if\n\ @@ -102,7 +105,8 @@ enum { O_HELP, O_VERSION, O_USAGE, O_FILE, O_AND, O_OR, O_NOT, O_LPAREN, O_RPAREN, - O_ANAG, O_SUBG, O_WILD, O_TRACK, O_REGEXP, O_PCRE, O_MONO, + O_ANAG, O_SUBG, O_WILD, O_TRACK, O_REGEXP, O_PCRE, O_MONO, O_LENGTH, + O_LONGEST, O_SHORTEST, O_EOF }; @@ -126,7 +130,7 @@ static const struct opt opttab[] = { { "or", 0, OF_SHORT, O_OR }, { "not", 0, OF_SHORT, O_NOT }, - /* --- Actual matching oeprations -- do something useful --- */ + /* --- Actual matching operations -- do something useful --- */ { "anagram", 1, 0, O_ANAG }, { "subgram", 1, 0, O_SUBG }, @@ -139,6 +143,9 @@ static const struct opt opttab[] = { #ifdef HAVE_PCRE { "pcre", 1, 0, O_PCRE }, #endif + { "length", 1, 0, O_LENGTH }, + { "longest", 0, 0, O_LONGEST }, + { "shortest", 0, 0, O_SHORTEST }, /* --- End marker --- */ @@ -291,6 +298,59 @@ static int n_not(node *nn, const char *p, size_t sz) return (!n->arg->func(n->arg, p, sz)); } +/*----- Other simple node types -------------------------------------------*/ + +enum { LESS = -1, EQUAL = 0, GREATER = 1 }; + +typedef struct node_numeric { + node n; + int dir; + int i; +} node_numeric; + +static void parse_numeric(const char *p, int *dir, int *i) +{ + long l; + const char *pp = p; + char *q; + + switch (*pp) { + case '-': *dir = LESS; pp++; break; + case '+': *dir = GREATER; pp++; break; + default: *dir = EQUAL; break; + } + errno = 0; + l = strtol(pp, &q, 0); + if (*q || errno || l < INT_MIN || l > INT_MAX) + die("bad numeric parameter `%s'", p); + *i = l; +} + +static node *make_numeric(const char *const *av, + int (*func)(struct node *, const char *, size_t)) +{ + node_numeric *n = xmalloc(sizeof(*n)); + parse_numeric(av[0], &n->dir, &n->i); + n->n.func = func; + return (&n->n); +} + +static int cmp_numeric(int x, int dir, int n) +{ + switch (dir) { + case LESS: return (x <= n); + case EQUAL: return (x == n); + case GREATER: return (x >= n); + } + abort(); +} + +static int n_length(node *nn, const char *p, size_t sz) +{ + node_numeric *n = (node_numeric *)nn; + return (cmp_numeric(sz, n->dir, n->i)); +} + /*----- Parser for the expression syntax ----------------------------------*/ /* --- A parser context --- */ @@ -346,6 +406,9 @@ static void p_factor(p_ctx *p, node **nn) case O_PCRE: *nn = pcrenode(p->a + 1); break; #endif case O_MONO: *nn = mono(p->a + 1); break; + case O_LENGTH: *nn = make_numeric(p->a + 1, n_length); break; + case O_LONGEST: *nn = longest(p->a + 1); break; + case O_SHORTEST: *nn = shortest(p->a + 1); break; default: die("syntax error near `%s': unexpected token", *p->a); } p_next(p); @@ -423,6 +486,38 @@ static node *p_argv(int argc, const char *const argv[]) return (n); } +/*----- At-end stuff ------------------------------------------------------*/ + +/* --- @atend_register@ --- * + * + * Arguments: @int (*func)(void *)@ = function to call + * @void *p@ = handle to pass to it + * + * Returns: --- + * + * Use: Adds a function to the list of things to do at the end of the + * program. The function should return nonzero if it produced + * any output. + */ + +typedef struct atend { + struct atend *next; + int (*func)(void */*p*/); + void *p; +} atend; + +static atend *aa_head = 0, **aa_tail = &aa_head; + +void atend_register(int (*func)(void */*p*/), void *p) +{ + atend *a = xmalloc(sizeof(*a)); + a->next = 0; + a->func = func; + a->p = p; + *aa_tail = a; + aa_tail = &a->next; +} + /*----- Main code ---------------------------------------------------------*/ /* --- @main@ --- * @@ -441,7 +536,9 @@ int main(int argc, char *argv[]) node *n; FILE *fp; dstr d = DSTR_INIT; + int ok = 0; char *p, *q, *l; + atend *a; ego(argv[0]); n = p_argv(argc, (const char *const *)argv); @@ -463,12 +560,19 @@ int main(int argc, char *argv[]) if (n->func(n, d.buf, d.len)) { fwrite(d.buf, 1, d.len, stdout); fputc('\n', stdout); + ok = 1; } } - if (!feof(fp)) + if (ferror(fp) || fclose(fp)) die("error reading `%s': %s", file, strerror(errno)); - fclose(fp); - return (0); + for (a = aa_head; a; a = a->next) { + if (a->func(a->p)) + ok = 1; + } + if (fflush(stdout) || ferror(stdout) || fclose(stdout)) + die("error writing output: %s", strerror(errno)); + if (!ok) pquis(stderr, "$: no matches found\n"); + return (ok ? EX_OK : EX_NONE); } /*----- That's all, folks -------------------------------------------------*/ diff --git a/anag.h b/anag.h index e233159..f9eb5a3 100644 --- a/anag.h +++ b/anag.h @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: anag.h,v 1.5 2004/04/08 01:36:19 mdw Exp $ + * $Id$ * * External definitions for Anag * @@ -69,6 +69,14 @@ extern node *trackword(const char *const */*av*/); extern node *mono(const char *const */*av*/); extern node *regexp(const char *const */*av*/); extern node *pcrenode(const char *const */*av*/); +extern node *longest(const char *const */*av*/); +extern node *shortest(const char *const */*av*/); + +/*----- Exit codes --------------------------------------------------------*/ + +#define EX_OK 0 +#define EX_NONE 1 +#define EX_FAIL 127 /*----- Error reporting ---------------------------------------------------*/ @@ -189,6 +197,22 @@ extern void dstr_ensure(dstr */*d*/, size_t /*sz*/); extern int dstr_putline(dstr */*d*/, FILE */*fp*/); +/*----- Infrastructure ----------------------------------------------------*/ + +/* --- @atend_register@ --- * + * + * Arguments: @int (*func)(void *)@ = function to call + * @void *p@ = handle to pass to it + * + * Returns: --- + * + * Use: Adds a function to the list of things to do at the end of the + * program. The function should return nonzero if it produced + * any output. + */ + +extern void atend_register(int (*/*func*/)(void */*p*/), void */*p*/); + /*----- That's all, folks -------------------------------------------------*/ #ifdef __cplusplus diff --git a/longest.c b/longest.c new file mode 100644 index 0000000..30371bc --- /dev/null +++ b/longest.c @@ -0,0 +1,124 @@ +/* -*-c-*- + * + * $Id$ + * + * Remember the longest word seen so far + * + * (c) 2005 Mark Wooding + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Anag: a simple wordgame helper. + * + * Anag is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Anag 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 Anag; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Header files ------------------------------------------------------*/ + +#include "anag.h" + +/*----- Data structures ---------------------------------------------------*/ + +typedef struct link { + struct link *next; + char *p; +} link; + +typedef struct node_longest { + node n; + size_t len; + link *l, **lt; +} node_longest; + +/*----- Main code ---------------------------------------------------------*/ + +static void empty(node_longest *n) +{ + link *l, *ll; + + for (l = n->l; l; l = ll) { + ll = l->next; + free(l->p); + free(l); + } + n->l = 0; + n->lt = &n->l; +} + +static void insert(node_longest *n, const char *p, size_t sz) +{ + link *l; + + n->len = sz; + l = xmalloc(sizeof(*l)); + l->p = xmalloc(sz + 1); + memcpy(l->p, p, sz + 1); + l->next = 0; + *n->lt = l; + n->lt = &l->next; +} + +static int aa_output(void *p) +{ + node_longest *n = p; + link *l; + + if (!n->l) return (0); + for (l = n->l; l; l = l->next) + puts(l->p); + return (1); +} + +static int n_longest(node *nn, const char *p, size_t sz) +{ + node_longest *n = (node_longest *)nn; + if (sz < n->len) + return (0); + if (sz > n->len) + empty(n); + insert(n, p, sz); + return (0); +} + +static int n_shortest(node *nn, const char *p, size_t sz) +{ + node_longest *n = (node_longest *)nn; + if (n->len && sz > n->len) + return (0); + if (!n->len || sz < n->len) + empty(n); + insert(n, p, sz); + return (0); +} + +static node *mklongest(int (*nf)(node *, const char *, size_t), + int (*aa)(void *)) +{ + node_longest *n = xmalloc(sizeof(*n)); + n->n.func = nf; + n->len = 0; + n->l = 0; + n->lt = &n->l; + atend_register(aa, n); + return (&n->n); +} + +node *longest(const char *const *av) + { return mklongest(n_longest, aa_output); } +node *shortest(const char *const *av) + { return mklongest(n_shortest, aa_output); } + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/util.c b/util.c index d35f4af..2adb4a9 100644 --- a/util.c +++ b/util.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: util.c,v 1.2 2004/04/08 01:36:19 mdw Exp $ + * $Id$ * * Various useful utilities, stolen from mLib * @@ -126,7 +126,7 @@ void die(const char *f, ...) vfprintf(stderr, f, ap); va_end(ap); putc('\n', stderr); - exit(EXIT_FAILURE); + exit(EX_FAIL); } /*----- Memory allocation -------------------------------------------------*/ -- [mdw]