Add filtering by length, and retaining only longest/shortest matches.
authormdw <mdw>
Fri, 25 Mar 2005 13:21:14 +0000 (13:21 +0000)
committermdw <mdw>
Fri, 25 Mar 2005 13:21:14 +0000 (13:21 +0000)
Also return nonzero and print a message if no matches were found.

Makefile.am
anag.c
anag.h
longest.c [new file with mode: 0644]
util.c

index d3c2d5d..06c94ad 100644 (file)
@@ -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 (file)
--- 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 (file)
--- 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 (file)
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 (file)
--- 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 -------------------------------------------------*/