]> git.cameronkatri.com Git - mandoc.git/blobdiff - tbl_opts.c
In mdoc(7) and man(7), if a width is given as a bare number without
[mandoc.git] / tbl_opts.c
index e98ba9469b7370c6c4fceb8c6dfe926f844ed197..10f33d417579cc87f7d45fe3db39b4a371c603c6 100644 (file)
@@ -1,6 +1,6 @@
-/*     $Id: tbl_opts.c,v 1.2 2010/12/28 13:47:38 kristaps Exp $ */
+/*     $Id: tbl_opts.c,v 1.14 2014/08/10 23:54:41 schwarze Exp $ */
 /*
- * Copyright (c) 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
+ * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
+#include "config.h"
+
+#include <sys/types.h>
+
+#include <ctype.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
+#include "mandoc.h"
+#include "libmandoc.h"
 #include "libroff.h"
 
 enum   tbl_ident {
@@ -46,13 +53,19 @@ struct      tbl_phrase {
 /* Handle Commonwealth/American spellings. */
 #define        KEY_MAXKEYS      14
 
+/* Maximum length of key name string. */
+#define        KEY_MAXNAME      13
+
+/* Maximum length of key number size. */
+#define        KEY_MAXNUMSZ     10
+
 static const struct tbl_phrase keys[KEY_MAXKEYS] = {
        { "center",      TBL_OPT_CENTRE,        KEY_CENTRE},
        { "centre",      TBL_OPT_CENTRE,        KEY_CENTRE},
-       { "delim",       0,                     KEY_DELIM},
+       { "delim",       0,                     KEY_DELIM},
        { "expand",      TBL_OPT_EXPAND,        KEY_EXPAND},
-       { "box",         TBL_OPT_BOX,           KEY_BOX},
-       { "doublebox",   TBL_OPT_DBOX,          KEY_DBOX},
+       { "box",         TBL_OPT_BOX,           KEY_BOX},
+       { "doublebox",   TBL_OPT_DBOX,          KEY_DBOX},
        { "allbox",      TBL_OPT_ALLBOX,        KEY_ALLBOX},
        { "frame",       TBL_OPT_BOX,           KEY_FRAME},
        { "doubleframe", TBL_OPT_DBOX,          KEY_DFRAME},
@@ -63,134 +76,196 @@ static    const struct tbl_phrase keys[KEY_MAXKEYS] = {
        { "nospaces",    TBL_OPT_NOSPACE,       KEY_NOSPACE},
 };
 
-static int              arg(struct tbl *, int, const char *, int *, int);
-static int              opt(struct tbl *, int, const char *, int *);
+static int              arg(struct tbl_node *, int,
+                               const char *, int *, enum tbl_ident);
+static void             opt(struct tbl_node *, int,
+                               const char *, int *);
+
 
 static int
-arg(struct tbl *tbl, int ln, const char *p, int *pos, int key)
+arg(struct tbl_node *tbl, int ln, const char *p, int *pos, enum tbl_ident key)
 {
-       int              sv;
+       int              i;
+       char             buf[KEY_MAXNUMSZ];
 
-again:
-       sv = *pos;
+       while (isspace((unsigned char)p[*pos]))
+               (*pos)++;
 
-       switch (tbl_next(tbl, p, pos)) {
-       case (TBL_TOK_OPENPAREN):
-               break;
-       case (TBL_TOK_SPACE):
-               /* FALLTHROUGH */
-       case (TBL_TOK_TAB):
-               goto again;
-       default:
+       /* Arguments always begin with a parenthesis. */
+
+       if ('(' != p[*pos]) {
+               mandoc_msg(MANDOCERR_TBL, tbl->parse,
+                   ln, *pos, NULL);
                return(0);
        }
 
-       sv = *pos;
+       (*pos)++;
 
-       switch (tbl_next(tbl, p, pos)) {
-       case (TBL_TOK__MAX):
-               break;
-       default:
-               return(0);
-       }
+       /*
+        * The arguments can be ANY value, so we can't just stop at the
+        * next close parenthesis (the argument can be a closed
+        * parenthesis itself).
+        */
 
        switch (key) {
-       case (KEY_DELIM):
-               /* FIXME: cache this value. */
-               if (2 != strlen(tbl->buf))
-                       return(0);
-               tbl->delims[0] = tbl->buf[0];
-               tbl->delims[1] = tbl->buf[1];
-               break;
-       case (KEY_TAB):
-               /* FIXME: cache this value. */
-               if (1 != strlen(tbl->buf))
+       case KEY_DELIM:
+               if ('\0' == p[(*pos)++]) {
+                       mandoc_msg(MANDOCERR_TBL, tbl->parse,
+                           ln, *pos - 1, NULL);
                        return(0);
-               tbl->tab = tbl->buf[0];
-               break;
-       case (KEY_LINESIZE):
-               if ((tbl->linesize = atoi(tbl->buf)) <= 0)
-                       return(0);
-               break;
-       case (KEY_DPOINT):
-               /* FIXME: cache this value. */
-               if (1 != strlen(tbl->buf))
+               }
+
+               if ('\0' == p[(*pos)++]) {
+                       mandoc_msg(MANDOCERR_TBL, tbl->parse,
+                           ln, *pos - 1, NULL);
                        return(0);
-               tbl->decimal = tbl->buf[0];
+               }
                break;
+       case KEY_TAB:
+               if ('\0' != (tbl->opts.tab = p[(*pos)++]))
+                       break;
+
+               mandoc_msg(MANDOCERR_TBL, tbl->parse,
+                   ln, *pos - 1, NULL);
+               return(0);
+       case KEY_LINESIZE:
+               for (i = 0; i < KEY_MAXNUMSZ && p[*pos]; i++, (*pos)++) {
+                       buf[i] = p[*pos];
+                       if ( ! isdigit((unsigned char)buf[i]))
+                               break;
+               }
+
+               if (i < KEY_MAXNUMSZ) {
+                       buf[i] = '\0';
+                       tbl->opts.linesize = atoi(buf);
+                       break;
+               }
+
+               mandoc_msg(MANDOCERR_TBL, tbl->parse, ln, *pos, NULL);
+               return(0);
+       case KEY_DPOINT:
+               if ('\0' != (tbl->opts.decimal = p[(*pos)++]))
+                       break;
+
+               mandoc_msg(MANDOCERR_TBL, tbl->parse,
+                   ln, *pos - 1, NULL);
+               return(0);
        default:
                abort();
+               /* NOTREACHED */
        }
 
-       sv = *pos;
+       /* End with a close parenthesis. */
 
-       switch (tbl_next(tbl, p, pos)) {
-       case (TBL_TOK_CLOSEPAREN):
-               break;
-       default:
-               return(0);
-       }
+       if (')' == p[(*pos)++])
+               return(1);
 
-       return(1);
+       mandoc_msg(MANDOCERR_TBL, tbl->parse, ln, *pos - 1, NULL);
+       return(0);
 }
 
-
-static int
-opt(struct tbl *tbl, int ln, const char *p, int *pos)
+static void
+opt(struct tbl_node *tbl, int ln, const char *p, int *pos)
 {
        int              i, sv;
-
-again:
-       sv = *pos;
+       char             buf[KEY_MAXNAME];
 
        /*
+        * Parse individual options from the stream as surrounded by
+        * this goto.  Each pass through the routine parses out a single
+        * option and registers it.  Option arguments are processed in
+        * the arg() function.
+        */
+
+again: /*
         * EBNF describing this section:
         *
         * options      ::= option_list [:space:]* [;][\n]
         * option_list  ::= option option_tail
         * option_tail  ::= [:space:]+ option_list |
-        *              ::= epsilon
+        *              ::= epsilon
         * option       ::= [:alpha:]+ args
         * args         ::= [:space:]* [(] [:alpha:]+ [)]
         */
 
-       switch (tbl_next(tbl, p, pos)) {
-       case (TBL_TOK__MAX):
-               break;
-       case (TBL_TOK_SPACE):
-               /* FALLTHROUGH */
-       case (TBL_TOK_TAB):
-               goto again;
-       case (TBL_TOK_SEMICOLON):
-               tbl->part = TBL_PART_LAYOUT;
-               return(1);
-       default:
-               return(0);
+       while (isspace((unsigned char)p[*pos]))
+               (*pos)++;
+
+       /* Safe exit point. */
+
+       if (';' == p[*pos])
+               return;
+
+       /* Copy up to first non-alpha character. */
+
+       for (sv = *pos, i = 0; i < KEY_MAXNAME; i++, (*pos)++) {
+               buf[i] = (char)tolower((unsigned char)p[*pos]);
+               if ( ! isalpha((unsigned char)buf[i]))
+                       break;
        }
 
+       /* Exit if buffer is empty (or overrun). */
+
+       if (KEY_MAXNAME == i || 0 == i) {
+               mandoc_msg(MANDOCERR_TBL, tbl->parse, ln, *pos, NULL);
+               return;
+       }
+
+       buf[i] = '\0';
+
+       while (isspace((unsigned char)p[*pos]))
+               (*pos)++;
+
+       /*
+        * Look through all of the available keys to find one that
+        * matches the input.  FIXME: hashtable this.
+        */
+
        for (i = 0; i < KEY_MAXKEYS; i++) {
-               /* FIXME: hashtable this? */
-               if (strcasecmp(tbl->buf, keys[i].name))
+               if (strcmp(buf, keys[i].name))
                        continue;
-               if (keys[i].key) 
-                       tbl->opts |= keys[i].key;
+
+               /*
+                * Note: this is more difficult to recover from, as we
+                * can be anywhere in the option sequence and it's
+                * harder to jump to the next.  Meanwhile, just bail out
+                * of the sequence altogether.
+                */
+
+               if (keys[i].key)
+                       tbl->opts.opts |= keys[i].key;
                else if ( ! arg(tbl, ln, p, pos, keys[i].ident))
-                       return(0);
+                       return;
 
                break;
        }
 
+       /*
+        * Allow us to recover from bad options by continuing to another
+        * parse sequence.
+        */
+
        if (KEY_MAXKEYS == i)
-               return(0);
+               mandoc_msg(MANDOCERR_TBLOPT, tbl->parse, ln, sv, NULL);
 
-       return(opt(tbl, ln, p, pos));
+       goto again;
+       /* NOTREACHED */
 }
 
 int
-tbl_option(struct tbl *tbl, int ln, const char *p)
+tbl_option(struct tbl_node *tbl, int ln, const char *p)
 {
        int              pos;
 
+       /*
+        * Table options are always on just one line, so automatically
+        * switch into the next input mode here.
+        */
+       tbl->part = TBL_PART_LAYOUT;
+
        pos = 0;
-       return(opt(tbl, ln, p, &pos));
+       opt(tbl, ln, p, &pos);
+
+       /* Always succeed. */
+       return(1);
 }