--- /dev/null
+Makefile.in
+configure
+aclocal.m4
+build
+stamp-h.in
+config.h.in
--- /dev/null
+COPYING
+install-sh
+mkinstalldirs
+missing
--- /dev/null
+;;; -*-emacs-lisp-*-
+
+(setq skel-alist
+ (append
+ '((author . "Mark Wooding")
+ (full-title . "Anag: a simple wordgame helper")
+ (program . "Anag"))
+ skel-alist))
+
--- /dev/null
+## -*-makefile-*-
+##
+## $Id: Makefile.am,v 1.1 2001/02/04 17:14:42 mdw Exp $
+##
+## Makefile for Anag
+##
+## (c) 2001 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.
+
+##----- Revision history ----------------------------------------------------
+##
+## $Log: Makefile.am,v $
+## Revision 1.1 2001/02/04 17:14:42 mdw
+## Initial checkin
+##
+
+AUTOMAKE_OPTIONS = foreign
+bin_PROGRAMS = anag
+anag_SOURCES = anag.c anag.h wildcard.c anagram.c trackword.c util.c
+
+##----- That's all, folks ---------------------------------------------------
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: acconfig.h,v 1.1 2001/02/04 17:14:42 mdw Exp $
+ *
+ * Configuration header for Anag
+ *
+ * (c) 1999 Straylight/Edgeware
+ */
+
+/*----- 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.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: acconfig.h,v $
+ * Revision 1.1 2001/02/04 17:14:42 mdw
+ * Initial checkin
+ *
+ */
+
+#ifndef ACCONFIG_H
+#define ACCONFIG_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Autoconfiguration data --------------------------------------------*/
+@TOP@
+
+/* Package and version number. */
+#define PACKAGE "anag"
+#define VERSION "1.0.0"
+
+/* Define this to be your default wordlist location. */
+#define DICTIONARY "/usr/dict/words"
+
+@BOTTOM@
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: anag.c,v 1.1 2001/02/04 17:14:42 mdw Exp $
+ *
+ * Main driver for anag
+ *
+ * (c) 2001 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.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: anag.c,v $
+ * Revision 1.1 2001/02/04 17:14:42 mdw
+ * Initial checkin
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "anag.h"
+
+/*----- Static variables --------------------------------------------------*/
+
+static const char *file = DICTIONARY;
+
+/*----- Help text functions -----------------------------------------------*/
+
+static void usage(FILE *fp)
+{
+ pquis(fp, "Usage: $ [-f file] expression\n");
+}
+
+static void version(FILE *fp)
+{
+ pquis(fp, "$, version " VERSION "\n");
+}
+
+static void help(FILE *fp)
+{
+ version(fp);
+ fputc('\n', fp);
+ usage(fp);
+ fputs("\n\
+Searches a wordlist, printing all of the words which match an expression.\n\
+The basic tests in the expression are:\n\
+\n\
+-anagram WORD matches a full-length anagram\n\
+-subgram WORD matches words which only use letters in WORD\n\
+-wildcard PATTERN matches with wildcards `*' and `?'\n\
+-trackword WORD matches words which can be found in a trackword\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\
+you like), and grouped using parentheses `(' and `)'.\n\
+", fp);
+}
+
+/*----- The options parser ------------------------------------------------*/
+
+/* --- Options table structure --- */
+
+struct opt {
+ const char *name;
+ unsigned nargs;
+ unsigned f;
+ unsigned tag;
+};
+
+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_EOF
+};
+
+#define OF_SHORT 1u
+
+static const struct opt opttab[] = {
+
+ /* --- Options -- don't form part of the language --- */
+
+ { "help", 0, OF_SHORT, O_HELP },
+ { "version", 0, OF_SHORT, O_VERSION },
+ { "usage", 0, OF_SHORT, O_USAGE },
+ { "file", 1, OF_SHORT, O_FILE },
+
+ /* --- Operators -- provide the basic structure of the language --- *
+ *
+ * These are also given magical names by the parser.
+ */
+
+ { "and", 0, OF_SHORT, O_AND },
+ { "or", 0, OF_SHORT, O_OR },
+ { "not", 0, OF_SHORT, O_NOT },
+
+ /* --- Actual matching oeprations -- do something useful --- */
+
+ { "anagram", 1, 0, O_ANAG },
+ { "subgram", 1, 0, O_SUBG },
+ { "wildcard", 1, 0, O_WILD },
+ { "trackword", 1, 0, O_TRACK },
+
+ /* --- End marker --- */
+
+ { 0, 0, 0, 0 }
+};
+
+static int ac;
+static const char *const *av;
+static int ai;
+
+/* --- @nextopt@ --- *
+ *
+ * Arguments: @const char ***arg@ = where to store the arg pointer
+ *
+ * Returns: The tag of the next option.
+ *
+ * Use: Scans the next option off the command line. If the option
+ * doesn't form part of the language, it's processed internally,
+ * and you'll never see it from here. On exit, the @arg@
+ * pointer is set to contain the address of the option scanned,
+ * followed by its arguments if any. You're expected to know
+ * how many arguments there are for your option.
+ */
+
+static unsigned nextopt(const char *const **arg)
+{
+ for (;;) {
+ const struct opt *o, *oo;
+ size_t sz;
+ const char *p;
+
+ /* --- Pick the next option off the front --- */
+
+ *arg = av + ai;
+ if (ai >= ac)
+ return (O_EOF);
+ p = av[ai++];
+
+ /* --- Cope with various forms of magic --- */
+
+ if (p[0] != '-') {
+ if (!p[1]) switch (*p) {
+ case '&': return (O_AND);
+ case '|': return (O_OR);
+ case '!': return (O_NOT);
+ case '(': return (O_LPAREN);
+ case ')': return (O_RPAREN);
+ }
+ goto bad;
+ }
+
+ /* --- Now cope with other sorts of weirdies --- *
+ *
+ * By the end of this, a leading `-' or `--' will have been stripped.
+ */
+
+ p++;
+ if (!*p)
+ goto bad;
+ if (*p == '-')
+ p++;
+ if (!*p) {
+ if (ai < ac)
+ die("syntax error near `--': rubbish at end of line");
+ return (O_EOF);
+ }
+
+ /* --- Now look the word up in my table --- */
+
+ sz = strlen(p);
+ oo = 0;
+ for (o = opttab; o->name; o++) {
+ if (strncmp(p, o->name, sz) == 0) {
+ if (strlen(o->name) == sz || ((o->f & OF_SHORT) && sz == 1)) {
+ oo = o;
+ break;
+ }
+ if (oo) {
+ die("ambiguous option name `-%s' (could match `-%s' or `-%s')",
+ p, oo->name, o->name);
+ }
+ oo = o;
+ }
+ }
+ if (!oo)
+ die("unrecognized option name `-%s'", p);
+
+ /* --- Sort out the arguments --- */
+
+ if (ai + oo->nargs > ac)
+ die("too few arguments for `-%s' (need %u)", oo->name, oo->nargs);
+ ai += oo->nargs;
+
+ /* --- Now process the option --- */
+
+ switch (oo->tag) {
+ case O_HELP:
+ help(stdout);
+ exit(0);
+ case O_VERSION:
+ version(stdout);
+ exit(0);
+ case O_USAGE:
+ usage(stdout);
+ exit(0);
+ case O_FILE:
+ file = (*arg)[1];
+ break;
+ default:
+ return (oo->tag);
+ }
+ bad:
+ die("syntax error near `%s': unknown token type", av[ai - 1]);
+ }
+}
+
+/*----- Node types for operators ------------------------------------------*/
+
+/* --- Node structures --- */
+
+typedef struct node_bin {
+ node n;
+ node *left;
+ node *right;
+} node_bin;
+
+typedef struct node_un {
+ node n;
+ node *arg;
+} node_un;
+
+/* --- Node functions --- */
+
+static int n_or(node *nn, const char *p, size_t sz)
+{
+ node_bin *n = (node_bin *)nn;
+ return (n->left->func(n->left, p, sz) || n->right->func(n->right, p, sz));
+}
+
+static int n_and(node *nn, const char *p, size_t sz)
+{
+ node_bin *n = (node_bin *)nn;
+ return (n->left->func(n->left, p, sz) && n->right->func(n->right, p, sz));
+}
+
+static int n_not(node *nn, const char *p, size_t sz)
+{
+ node_un *n = (node_un *)nn;
+ return (!n->arg->func(n->arg, p, sz));
+}
+
+/*----- Parser for the expression syntax ----------------------------------*/
+
+/* --- A parser context --- */
+
+typedef struct p_ctx {
+ unsigned t;
+ const char *const *a;
+} p_ctx;
+
+/* --- Parser structure --- *
+ *
+ * This is a simple recursive descent parser. The context retains
+ * information about the current token. Each function is passed the address
+ * of a node pointer to fill in. This simplifies the binary operator code
+ * somewhat, relative to returning pointers to node trees.
+ */
+
+static void p_expr(p_ctx *p, node **/*nn*/);
+
+static void p_next(p_ctx *p)
+{
+ static const char *const eof[] = { "<end>", 0 };
+ p->t = nextopt(&p->a);
+ if (p->t == O_EOF)
+ p->a = eof;
+}
+
+static void p_factor(p_ctx *p, node **nn)
+{
+ node_un *n;
+ if (p->t == O_LPAREN) {
+ p_next(p);
+ p_expr(p, nn);
+ if (p->t != O_RPAREN)
+ die("syntax error near `%s': missing `('", *p->a);
+ p_next(p);
+ } else if (p->t == O_NOT) {
+ n = xmalloc(sizeof(node_un));
+ n->n.func = n_not;
+ *nn = &n->n;
+ p_next(p);
+ p_factor(p, &n->arg);
+ } else {
+ switch (p->t) {
+ case O_ANAG: *nn = anagram(p->a + 1); break;
+ case O_SUBG: *nn = subgram(p->a + 1); break;
+ case O_WILD: *nn = wildcard(p->a + 1); break;
+ case O_TRACK: *nn = trackword(p->a + 1); break;
+ default: die("syntax error near `%s': unexpected token", *p->a);
+ }
+ p_next(p);
+ }
+}
+
+static void p_term(p_ctx *p, node **nn)
+{
+ node_bin *n;
+ for (;;) {
+ p_factor(p, nn);
+ switch (p->t) {
+ case O_AND:
+ p_next(p);
+ default:
+ break;
+ case O_LPAREN:
+ case O_RPAREN:
+ case O_OR:
+ case O_EOF:
+ return;
+ }
+ n = xmalloc(sizeof(node_bin));
+ n->left = *nn;
+ n->n.func = n_and;
+ *nn = &n->n;
+ nn = &n->right;
+ }
+}
+
+static void p_expr(p_ctx *p, node **nn)
+{
+ node_bin *n;
+ for (;;) {
+ p_term(p, nn);
+ if (p->t != O_OR)
+ break;
+ p_next(p);
+ n = xmalloc(sizeof(node_bin));
+ n->left = *nn;
+ n->n.func = n_or;
+ *nn = &n->n;
+ nn = &n->right;
+ }
+}
+
+/* --- @p_argv@ --- *
+ *
+ * Arguments: @int argc@ = number of command-line arguments
+ * @const char *const argv[]@ = vectoor of arguments
+ *
+ * Returns: A compiled node, parsed from the arguments.
+ *
+ * Use: Does the donkey-work of parsing a command-line.
+ */
+
+static node *p_argv(int argc, const char *const argv[])
+{
+ p_ctx p;
+ node *n;
+
+ av = argv;
+ ac = argc;
+ ai = 1;
+ p_next(&p);
+ p_expr(&p, &n);
+ if (p.t != O_EOF) {
+ die("syntax error near `%s': rubbish at end of line (too many `)'s?)",
+ *p.a);
+ }
+ return (n);
+}
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @main@ --- *
+ *
+ * Arguments: @int argc@ = number of command-line arguments
+ * @char *argv[]@ = vector of argument words
+ *
+ * Returns: Zero on success, nonzero on failure.
+ *
+ * Use: Picks entries from a word list which match particular
+ * expressions. This might be of assistance to word-game types.
+ */
+
+int main(int argc, char *argv[])
+{
+ node *n;
+ FILE *fp;
+ dstr d = DSTR_INIT;
+ char *p, *q, *l;
+
+ ego(argv[0]);
+ n = p_argv(argc, (const char *const *)argv);
+
+ if ((fp = fopen(file, "r")) == 0)
+ die("error opening `%s': %s", file, strerror(errno));
+ for (;;) {
+ dstr_reset(&d);
+ if (dstr_putline(&d, fp) < 0)
+ break;
+ l = d.buf + d.len;
+ for (p = q = d.buf; p < l; p++) {
+ if (!isalnum((unsigned char)*p))
+ continue;
+ *q++ = tolower((unsigned char)*p);
+ }
+ *q = 0;
+ d.len = q - d.buf;
+ if (n->func(n, d.buf, d.len)) {
+ fwrite(d.buf, 1, d.len, stdout);
+ fputc('\n', stdout);
+ }
+ }
+ if (!feof(fp))
+ die("error reading `%s': %s", file, strerror(errno));
+ fclose(fp);
+ return (0);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: anag.h,v 1.1 2001/02/04 17:14:42 mdw Exp $
+ *
+ * External definitions for Anag
+ *
+ * (c) 2001 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.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: anag.h,v $
+ * Revision 1.1 2001/02/04 17:14:42 mdw
+ * Initial checkin
+ *
+ */
+
+#ifndef ANAG_H
+#define ANAG_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "config.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef struct node {
+ int (*func)(struct node */*n*/, const char */*p*/, size_t /*sz*/);
+} node;
+
+typedef struct dstr {
+ char *buf;
+ size_t len;
+ size_t sz;
+} dstr;
+
+#define DSTR_INIT { 0, 0, 0 }
+
+/*----- Node types --------------------------------------------------------*/
+
+extern node *anagram(const char *const */*av*/);
+extern node *subgram(const char *const */*av*/);
+extern node *wildcard(const char *const */*av*/);
+extern node *trackword(const char *const */*av*/);
+
+/*----- Error reporting ---------------------------------------------------*/
+
+/* --- @ego@ --- *
+ *
+ * Arguments: @const char *p@ = pointer to program name
+ *
+ * Returns: ---
+ *
+ * Use: Stores what the program's name is.
+ */
+
+extern void ego(const char */*p*/);
+
+/* --- @pquis@ --- *
+ *
+ * Arguments: @FILE *fp@ = output stream to write on
+ * @const char *p@ = pointer to string to write
+ *
+ * Returns: Zero if everything worked, EOF if not.
+ *
+ * Use: Writes the string @p@ to the output stream @fp@. Occurrences
+ * of the character `$' in @p@ are replaced by the program name
+ * as reported by @quis@. A `$$' is replaced by a single `$'
+ * sign.
+ */
+
+extern int pquis(FILE */*fp*/, const char */*p*/);
+
+/* --- @die@ --- *
+ *
+ * Arguments: @const char *f@ = a @printf@-style format string
+ * @...@ = other arguments
+ *
+ * Returns: Never.
+ *
+ * Use: Reports an error and exits.
+ */
+
+extern void die(const char */*f*/, ...);
+
+/*----- Memory allocation -------------------------------------------------*/
+
+/* --- @xmalloc@ --- *
+ *
+ * Arguments: @size_t sz@ = size of block to allocate
+ *
+ * Returns: Pointer to allocated block.
+ *
+ * Use: Allocates memory. If there's not enough memory, the
+ * program exits.
+ */
+
+extern void *xmalloc(size_t /*sz*/);
+
+/* --- @xrealloc@ --- *
+ *
+ * Arguments: @void *p@ = a pointer to allocated memory
+ * @size_t sz@ = new size of block wanted
+ *
+ * Returns: Pointer to resized block.
+ *
+ * Use: Resizes an allocated block. If there's not enough memory,
+ * the program exits.
+ */
+
+extern void *xrealloc(void */*p*/, size_t /*sz*/);
+
+/*----- Dynamic string handling -------------------------------------------*/
+
+/* --- @dstr_destroy@ --- *
+ *
+ * Arguments: @dstr *d@ = pointer to a dynamic string block
+ *
+ * Returns: ---
+ *
+ * Use: Reclaims the space used by a dynamic string.
+ */
+
+extern void dstr_destroy(dstr */*d*/);
+
+/* --- @dstr_reset@ --- *
+ *
+ * Arguments: @dstr *d@ = pointer to a dynamic string block
+ *
+ * Returns: ---
+ *
+ * Use: Resets a string so that new data gets put at the beginning.
+ */
+
+extern void dstr_reset(dstr */*d*/);
+
+/* --- @dstr_ensure@ --- *
+ *
+ * Arguments: @dstr *d@ = pointer to a dynamic string block
+ * @size_t sz@ = amount of free space to ensure
+ *
+ * Returns: ---
+ *
+ * Use: Ensures that at least @sz@ bytes are available in the
+ * given string.
+ */
+
+extern void dstr_ensure(dstr */*d*/, size_t /*sz*/);
+
+/* --- @dstr_putline@ --- *
+ *
+ * Arguments: @dstr *d@ = pointer to a dynamic string block
+ * @FILE *fp@ = a stream to read from
+ *
+ * Returns: The number of characters read into the buffer, or @EOF@ if
+ * end-of-file was reached before any characters were read.
+ *
+ * Use: Appends the next line from the given input stream to the
+ * string. A trailing newline is not added; a trailing null
+ * byte is appended, as for @dstr_putz@.
+ */
+
+extern int dstr_putline(dstr */*d*/, FILE */*fp*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: anagram.c,v 1.1 2001/02/04 17:14:42 mdw Exp $
+ *
+ * Matches anagrams and subgrams
+ *
+ * (c) 2001 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.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: anagram.c,v $
+ * Revision 1.1 2001/02/04 17:14:42 mdw
+ * Initial checkin
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "anag.h"
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef struct node_gram {
+ node n;
+ size_t sz;
+ unsigned f[UCHAR_MAX + 1];
+} node_gram;
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @compile@ --- *
+ *
+ * Arguments: @node_gram *n@ = pointer to node to fill in
+ * @const char *p@ = pointer to string to compile from
+ *
+ * Returns: The length of the string.
+ *
+ * Use: Fills in an anagram node with letter frequencies.
+ */
+
+static size_t compile(node_gram *n, const char *p)
+{
+ size_t len = 0;
+ unsigned i;
+ memset(n->f, 0, sizeof(n->f));
+ while (*p) {
+ i = (unsigned char)*p++;
+ n->f[i]++;
+ len++;
+ }
+ return (len);
+}
+
+/* --- Node matcher --- */
+
+static int n_gram(node *nn, const char *p, size_t sz)
+{
+ node_gram *n = (node_gram *)nn;
+ unsigned f[UCHAR_MAX];
+ const char *q;
+ unsigned i;
+
+ if (n->sz && sz != n->sz)
+ return (0);
+ memcpy(f, n->f, sizeof(f));
+ q = p + sz;
+ while (p < q) {
+ i = (unsigned char)*p++;
+ if (!f[i])
+ return (0);
+ f[i]--;
+ }
+ return (1);
+}
+
+/* --- Node creation --- */
+
+node *anagram(const char *const *av)
+{
+ node_gram *n = xmalloc(sizeof(*n));
+ n->n.func = n_gram;
+ n->sz = compile(n, av[0]);
+ return (&n->n);
+}
+
+node *subgram(const char *const *av)
+{
+ node_gram *n = xmalloc(sizeof(*n));
+ n->n.func = n_gram;
+ n->sz = 0;
+ compile(n, av[0]);
+ return (&n->n);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+dnl -*-fundamental-*-
+dnl
+dnl $Id: configure.in,v 1.1 2001/02/04 17:14:42 mdw Exp $
+dnl
+dnl Configuration script
+dnl
+dnl (c) 2001 Mark Wooding
+dnl
+
+dnl ----- Licensing notice --------------------------------------------------
+dnl
+dnl This file is part of Anag: a simple wordgame helper.
+dnl
+dnl Anag is free software; you can redistribute it and/or modify
+dnl it under the terms of the GNU General Public License as published by
+dnl the Free Software Foundation; either version 2 of the License, or
+dnl (at your option) any later version.
+dnl
+dnl Anag is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+dnl GNU General Public License for more details.
+dnl
+dnl You should have received a copy of the GNU General Public License
+dnl along with Anag; if not, write to the Free Software Foundation,
+dnl Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+dnl ----- Revision history --------------------------------------------------
+dnl
+dnl $Log: configure.in,v $
+dnl Revision 1.1 2001/02/04 17:14:42 mdw
+dnl Initial checkin
+dnl
+
+AC_INIT(anag.c)
+AM_INIT_AUTOMAKE(anag, 1.0.0)
+AM_CONFIG_HEADER(config.h)
+AC_PROG_CC
+mdw_GCC_FLAGS
+AC_ARG_WITH(dictionary,
+[ --with-dictionary=DICT set default word list to be DICT],
+[AC_DEFINE_UNQUOTED(DICTIONARY, "$withval")],
+[AC_DEFINE(DICTIONARY, "/usr/dict/words")])
+AC_OUTPUT(Makefile)
+
+dnl ----- That's all, folks -------------------------------------------------
--- /dev/null
+#! /bin/sh
+
+set -e
+mklinks
+mkaclocal
+autoheader
+autoconf
+automake
+mkdir build
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: trackword.c,v 1.1 2001/02/04 17:14:42 mdw Exp $
+ *
+ * Matches trackwords
+ *
+ * (c) 2001 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.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: trackword.c,v $
+ * Revision 1.1 2001/02/04 17:14:42 mdw
+ * Initial checkin
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "anag.h"
+
+/*----- Data structures ---------------------------------------------------*/
+
+enum { N, NE, E, SE, S, SW, W, NW, NDIR };
+
+typedef struct tcell {
+ struct tcell *hop[NDIR];
+ char ch;
+ unsigned char f;
+} tcell;
+
+typedef struct node_track {
+ node n;
+ unsigned x;
+ unsigned y;
+ tcell *c;
+} node_track;
+
+/*----- Utility functions -------------------------------------------------*/
+
+/* --- @isqrt@ --- *
+ *
+ * Arguments: @long a@ = the target value
+ *
+ * Returns: %$\lfloor \sqrt{a} \rfloor$%
+ *
+ * Use: Computes an integer square root. This algorithm is
+ * Newton-Raphson with integers. I've stolen this more-or-less
+ * wholesale fom the multiprecision integer square-root function
+ * in Catacomb.
+ */
+
+long isqrt(long a)
+{
+ long i, q;
+
+ /* --- Initial guess is half the input length --- */
+
+ for (i = q = a; q; q >>= 2)
+ i >>= 1;
+
+ /* --- Do the main series iteration --- */
+
+ for (;;) {
+ q = i * i - a;
+ if (!q || (q < 0 && -q <= 2 * i))
+ return (i);
+ q /= 2 * i;
+ if (!q)
+ i--;
+ else
+ i -= q;
+ }
+}
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @track@ --- *
+ *
+ * Arguments: @tcell *c@ = pointer to start cell
+ * @const char *p@ = string to match
+ *
+ * Returns: Nonzero for a match, zero if no match was found.
+ *
+ * Use: Searches for a match in a trackword.
+ */
+
+static int track(tcell *c, const char *p)
+{
+ int i;
+
+ if (*p++ != c->ch)
+ return (0);
+ if (!*p)
+ return (1);
+ c->f = 1;
+ for (i = 0; i < NDIR; i++) {
+ if (!c->hop[i] || c->hop[i]->f)
+ continue;
+ if (track(c->hop[i], p)) {
+ c->f = 0;
+ return (1);
+ }
+ }
+ c->f = 0;
+ return (0);
+}
+
+/* --- Node matcher --- */
+
+static int n_track(node *nn, const char *p, size_t sz)
+{
+ node_track *n = (node_track *)nn;
+ tcell *c;
+
+ if (!*p)
+ return (1);
+ for (c = n->c; c->ch; c++) {
+ if (track(c, p))
+ return (1);
+ }
+ return (0);
+}
+
+/* --- Node creation --- */
+
+node *trackword(const char *const *av)
+{
+ node_track *n;
+ unsigned x, y;
+ unsigned i, j;
+ const char *p = av[0], *q, *l;
+ size_t sz = strlen(p);
+ tcell *c;
+
+ /* --- Work out the dimensions --- */
+
+ x = strcspn(p, "/");
+ if (!p[x]) {
+ x = isqrt(sz);
+ if (x * x != sz)
+ die("bad trackword `%s': not square, and no line markers", p);
+ y = x;
+ }
+
+ if (!x)
+ die("bad trackword `%s': no columns", p);
+
+ y = 0;
+ l = p + sz;
+ q = p;
+ for (;;) {
+ if (*q == '/')
+ q++;
+ if (!*q)
+ break;
+ if (l - q < x)
+ die("bad trackword `%s': inconsistent line lengths", p);
+ q += x;
+ y++;
+ }
+
+ if (!y)
+ die("bad trackword `%s': no rows", p);
+
+ /* --- Build the match node --- */
+
+ n = xmalloc(sizeof(*n));
+ n->n.func = n_track;
+ n->x = x; n->y = y;
+ n->c = xmalloc((x * y + 1) * sizeof(tcell));
+
+ q = p;
+ c = n->c;
+ for (j = 0; j < y; j++) {
+ if (*q == '/')
+ q++;
+ for (i = 0; i < x; i++) {
+ c->ch = *q++;
+ c->f = 0;
+
+#define NOK (j > 0)
+#define SOK (j < y - 1)
+#define WOK (i > 0)
+#define EOK (i < x - 1)
+
+ c->hop[N] = NOK ? c - x : 0;
+ c->hop[S] = SOK ? c + x : 0;
+ c->hop[W] = WOK ? c - 1 : 0;
+ c->hop[E] = EOK ? c + 1 : 0;
+
+ c->hop[NW] = NOK && WOK ? c - x - 1 : 0;
+ c->hop[NE] = NOK && EOK ? c - x + 1 : 0;
+ c->hop[SW] = SOK && WOK ? c + x - 1 : 0;
+ c->hop[SE] = SOK && EOK ? c + x + 1 : 0;
+
+#undef NOK
+#undef SOK
+#undef WOK
+#undef EOK
+
+ c++;
+ }
+ }
+ c->ch = 0;
+
+ /* --- Done --- */
+
+ c = n->c;
+ for (j = 0; j < y; j++) {
+ for (i = 0; i < x; i++) {
+ printf("%c ", c->ch);
+ c++;
+ }
+ fputc('\n', stdout);
+ }
+
+ return (&n->n);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: util.c,v 1.1 2001/02/04 17:14:42 mdw Exp $
+ *
+ * Various useful utilities, stolen from mLib
+ *
+ * (c) 2001 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.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: util.c,v $
+ * Revision 1.1 2001/02/04 17:14:42 mdw
+ * Initial checkin
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "anag.h"
+
+/*----- Static variables --------------------------------------------------*/
+
+static const char *quis = "<unset>";
+
+/*----- Error reporting ---------------------------------------------------*/
+
+/* --- @ego@ --- *
+ *
+ * Arguments: @const char *p@ = pointer to program name
+ *
+ * Returns: ---
+ *
+ * Use: Stores what the program's name is.
+ */
+
+#ifndef PATHSEP
+# if defined(__riscos)
+# define PATHSEP '.'
+# elif defined(__unix) || defined(unix)
+# define PATHSEP '/'
+# else
+# define PATHSEP '\\'
+# endif
+#endif
+
+void ego(const char *p)
+{
+ const char *q = p;
+ while (*q) {
+ if (*q++ == PATHSEP)
+ p = q;
+ }
+ if (*p == '-')
+ p++;
+ quis = p;
+}
+
+#undef PATHSEP
+
+/* --- @pquis@ --- *
+ *
+ * Arguments: @FILE *fp@ = output stream to write on
+ * @const char *p@ = pointer to string to write
+ *
+ * Returns: Zero if everything worked, EOF if not.
+ *
+ * Use: Writes the string @p@ to the output stream @fp@. Occurrences
+ * of the character `$' in @p@ are replaced by the program name
+ * as reported by @quis@. A `$$' is replaced by a single `$'
+ * sign.
+ */
+
+int pquis(FILE *fp, const char *p)
+{
+ size_t sz;
+
+ while (*p) {
+ sz = strcspn(p, "$");
+ if (sz) {
+ if (fwrite(p, 1, sz, fp) < sz)
+ return (EOF);
+ p += sz;
+ }
+ if (*p == '$') {
+ p++;
+ if (*p == '$') {
+ if (fputc('$', fp) == EOF)
+ return (EOF);
+ p++;
+ } else {
+ if (fputs(quis, fp) == EOF)
+ return (EOF);
+ }
+ }
+ }
+ return (0);
+}
+
+/* --- @die@ --- *
+ *
+ * Arguments: @const char *f@ = a @printf@-style format string
+ * @...@ = other arguments
+ *
+ * Returns: Never.
+ *
+ * Use: Reports an error and exits.
+ */
+
+void die(const char *f, ...)
+{
+ va_list ap;
+ va_start(ap, f);
+ fprintf(stderr, "%s: ", quis);
+ vfprintf(stderr, f, ap);
+ va_end(ap);
+ putc('\n', stderr);
+ exit(EXIT_FAILURE);
+}
+
+/*----- Memory allocation -------------------------------------------------*/
+
+/* --- @xmalloc@ --- *
+ *
+ * Arguments: @size_t sz@ = size of block to allocate
+ *
+ * Returns: Pointer to allocated block.
+ *
+ * Use: Allocates memory. If there's not enough memory, the
+ * program exits.
+ */
+
+void *xmalloc(size_t sz)
+{
+ void *p = malloc(sz);
+ if (!p)
+ die("not enough memory");
+ return (p);
+}
+
+/* --- @xrealloc@ --- *
+ *
+ * Arguments: @void *p@ = a pointer to allocated memory
+ * @size_t sz@ = new size of block wanted
+ *
+ * Returns: Pointer to resized block.
+ *
+ * Use: Resizes an allocated block. If there's not enough memory,
+ * the program exits.
+ */
+
+void *xrealloc(void *p, size_t sz)
+{
+ p = realloc(p, sz);
+ if (!p)
+ die("not enough memory");
+ return (p);
+}
+
+/*----- Dynamic string handling -------------------------------------------*/
+
+#define DSTR_INITSZ 64
+
+/* --- @dstr_destroy@ --- *
+ *
+ * Arguments: @dstr *d@ = pointer to a dynamic string block
+ *
+ * Returns: ---
+ *
+ * Use: Reclaims the space used by a dynamic string.
+ */
+
+void dstr_destroy(dstr *d) { free(d->buf); d->len = 0; d->sz = 0; }
+
+/* --- @dstr_reset@ --- *
+ *
+ * Arguments: @dstr *d@ = pointer to a dynamic string block
+ *
+ * Returns: ---
+ *
+ * Use: Resets a string so that new data gets put at the beginning.
+ */
+
+void dstr_reset(dstr *d) { d->len = 0; }
+
+/* --- @dstr_ensure@ --- *
+ *
+ * Arguments: @dstr *d@ = pointer to a dynamic string block
+ * @size_t sz@ = amount of free space to ensure
+ *
+ * Returns: ---
+ *
+ * Use: Ensures that at least @sz@ bytes are available in the
+ * given string.
+ */
+
+void dstr_ensure(dstr *d, size_t sz)
+{
+ size_t rq = d->len + sz;
+ size_t nsz;
+
+ /* --- If we have enough space, just leave it --- */
+
+ if (rq <= d->sz)
+ return;
+
+ /* --- Grow the buffer --- */
+
+ nsz = d->sz;
+
+ if (nsz == 0)
+ nsz = (DSTR_INITSZ >> 1);
+ do nsz <<= 1; while (nsz < rq);
+
+ if (d->buf)
+ d->buf = xrealloc(d->buf, nsz);
+ else
+ d->buf = xmalloc(nsz);
+ d->sz = nsz;
+}
+
+/* --- @dstr_putline@ --- *
+ *
+ * Arguments: @dstr *d@ = pointer to a dynamic string block
+ * @FILE *fp@ = a stream to read from
+ *
+ * Returns: The number of characters read into the buffer, or @EOF@ if
+ * end-of-file was reached before any characters were read.
+ *
+ * Use: Appends the next line from the given input stream to the
+ * string. A trailing newline is not added; a trailing null
+ * byte is appended, as for @dstr_putz@.
+ */
+
+int dstr_putline(dstr *d, FILE *fp)
+{
+ size_t left = d->sz - d->len;
+ size_t off = d->len;
+ int rd = 0;
+ int ch;
+
+ for (;;) {
+
+ /* --- Read the next byte --- */
+
+ ch = getc(fp);
+
+ /* --- End-of-file when no characters read is special --- */
+
+ if (ch == EOF && !rd)
+ return (EOF);
+
+ /* --- Make sure there's some buffer space --- */
+
+ if (!left) {
+ d->len = off;
+ dstr_ensure(d, 1);
+ left = d->sz - off;
+ }
+
+ /* --- End-of-file or newline ends the loop --- */
+
+ if (ch == EOF || ch == '\n') {
+ d->buf[off] = 0;
+ d->len = off;
+ return rd;
+ }
+
+ /* --- Append the character and continue --- */
+
+ d->buf[off++] = ch;
+ left--; rd++;
+ }
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * $Id: wildcard.c,v 1.1 2001/02/04 17:14:42 mdw Exp $
+ *
+ * Matches wildcard patterns
+ *
+ * (c) 2001 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.
+ */
+
+/*----- Revision history --------------------------------------------------*
+ *
+ * $Log: wildcard.c,v $
+ * Revision 1.1 2001/02/04 17:14:42 mdw
+ * Initial checkin
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "anag.h"
+
+/*----- Data structures ---------------------------------------------------*/
+
+typedef struct node_wild {
+ node n;
+ const char *p;
+} node_wild;
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @match@ --- *
+ *
+ * Arguments: @const char *p@ = pointer to pattern string
+ * @const char *s@ = string to compare with
+ *
+ * Returns: Nonzero if the pattern matches the string.
+ *
+ * Use: Does simple wildcard matching. This is quite nasty and more
+ * than a little slow. Supports metacharacters `*', `?' and
+ * '['.
+ */
+
+static int match(const char *p, const char *s)
+{
+ for (;;) {
+ char pch = *p++, pche, sch;
+ int sense;
+
+ switch (pch) {
+ case '?':
+ if (!*s)
+ return (0);
+ s++;
+ break;
+ case '*':
+ if (!*p)
+ return (1);
+ while (*s) {
+ if (match(p, s))
+ return (1);
+ s++;
+ }
+ return (0);
+ case '[':
+ if (!*s)
+ return (0);
+ sch = *s++;
+ pch = *p++;
+ sense = 1;
+ if (pch == '^' || pch == '!') {
+ sense = !sense;
+ pch = *p++;
+ }
+ if (pch == ']') {
+ if (*p == '-' && p[1] && p[1] != ']') {
+ pche = p[1];
+ p += 2;
+ if (pch <= sch && sch <= pche)
+ goto class_match;
+ } else if (pch == sch)
+ goto class_match;
+ pch = *p++;
+ }
+ for (;; pch = *p++) {
+ if (!pch || pch == ']')
+ goto class_nomatch;
+ if (*p == '-' && p[1] && p[1] != ']') {
+ pche = p[1];
+ p += 2;
+ if (pch <= sch && sch <= pche)
+ goto class_match;
+ } else if (pch == sch)
+ goto class_match;
+ }
+ class_match:
+ if (!sense)
+ return (0);
+ for (;;) {
+ pch = *p++;
+ if (!pch)
+ return (0);
+ if (pch == ']')
+ break;
+ if (*p == '-' && p[1] && p[1] != ']')
+ p += 2;
+ }
+ break;
+ class_nomatch:
+ if (sense)
+ return (0);
+ break;
+ case '\\':
+ pch = *p++;
+ default:
+ if (pch != *s)
+ return (0);
+ if (!pch)
+ return (1);
+ s++;
+ break;
+ }
+ }
+}
+
+/* --- Node matcher --- */
+
+static int n_wild(node *nn, const char *p, size_t sz)
+{
+ node_wild *n = (node_wild *)nn;
+ return (match(n->p, p));
+}
+
+/* --- Node creation --- */
+
+node *wildcard(const char *const *av)
+{
+ node_wild *n = xmalloc(sizeof(*n));
+ n->n.func = n_wild;
+ n->p = av[0];
+ return (&n->n);
+}
+
+/*----- That's all, folks -------------------------------------------------*/