]> git.cameronkatri.com Git - mandoc.git/blobdiff - tbl_opts.c
do not crash when a tbl(7) cell uses roman font
[mandoc.git] / tbl_opts.c
index 7470e4e21b4b301a68be50a91d449f58783204e6..e3a8373702996cff79a4b22792e9acfb176a79f8 100644 (file)
@@ -1,6 +1,7 @@
-/*     $Id: tbl_opts.c,v 1.3 2010/12/29 01:16:57 kristaps Exp $ */
+/*     $Id: tbl_opts.c,v 1.24 2018/12/14 05:18:03 schwarze Exp $ */
 /*
- * Copyright (c) 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
+ * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
+ * Copyright (c) 2015 Ingo Schwarze <schwarze@openbsd.org>
  *
  * 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 "libroff.h"
+#include "tbl.h"
+#include "libmandoc.h"
+#include "tbl_int.h"
 
-enum   tbl_ident {
-       KEY_CENTRE = 0,
-       KEY_DELIM,
-       KEY_EXPAND,
-       KEY_BOX,
-       KEY_DBOX,
-       KEY_ALLBOX,
-       KEY_TAB,
-       KEY_LINESIZE,
-       KEY_NOKEEP,
-       KEY_DPOINT,
-       KEY_NOSPACE,
-       KEY_FRAME,
-       KEY_DFRAME,
-       KEY_MAX
-};
+#define        KEY_DPOINT      0
+#define        KEY_DELIM       1
+#define        KEY_LINESIZE    2
+#define        KEY_TAB         3
 
 struct tbl_phrase {
        const char      *name;
        int              key;
-       enum tbl_ident   ident;
 };
 
-/* Handle Commonwealth/American spellings. */
-#define        KEY_MAXKEYS      14
-
-/* Maximum length of key name string. */
-#define        KEY_MAXNAME      13
+static const struct tbl_phrase keys[] = {
+       {"decimalpoint", 0},
+       {"delim",        0},
+       {"linesize",     0},
+       {"tab",          0},
+       {"allbox",       TBL_OPT_ALLBOX | TBL_OPT_BOX},
+       {"box",          TBL_OPT_BOX},
+       {"frame",        TBL_OPT_BOX},
+       {"center",       TBL_OPT_CENTRE},
+       {"centre",       TBL_OPT_CENTRE},
+       {"doublebox",    TBL_OPT_DBOX},
+       {"doubleframe",  TBL_OPT_DBOX},
+       {"expand",       TBL_OPT_EXPAND},
+       {"nokeep",       TBL_OPT_NOKEEP},
+       {"nospaces",     TBL_OPT_NOSPACE},
+       {"nowarn",       TBL_OPT_NOWARN},
+};
 
-/* Maximum length of key number size. */
-#define        KEY_MAXNUMSZ     10
+#define KEY_MAXKEYS ((int)(sizeof(keys)/sizeof(keys[0])))
 
-static const struct tbl_phrase keys[KEY_MAXKEYS] = {
-       { "center",      TBL_OPT_CENTRE,        KEY_CENTRE},
-       { "centre",      TBL_OPT_CENTRE,        KEY_CENTRE},
-       { "delim",       0,                     KEY_DELIM},
-       { "expand",      TBL_OPT_EXPAND,        KEY_EXPAND},
-       { "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},
-       { "tab",         0,                     KEY_TAB},
-       { "linesize",    0,                     KEY_LINESIZE},
-       { "nokeep",      TBL_OPT_NOKEEP,        KEY_NOKEEP},
-       { "decimalpoint", 0,                    KEY_DPOINT},
-       { "nospaces",    TBL_OPT_NOSPACE,       KEY_NOSPACE},
-};
+static void     arg(struct tbl_node *, int, const char *, int *, int);
 
-static int              arg(struct tbl *, int, const char *, int *, int);
-static void             opt(struct tbl *, int, const char *, int *);
 
-static int
-arg(struct tbl *tbl, int ln, const char *p, int *pos, int key)
+static void
+arg(struct tbl_node *tbl, int ln, const char *p, int *pos, int key)
 {
-       int              i;
-       char             buf[KEY_MAXNUMSZ];
+       int              len, want;
 
-       while (isspace((unsigned char)p[*pos]))
+       while (p[*pos] == ' ' || p[*pos] == '\t')
                (*pos)++;
 
-       /* Arguments always begin with a parenthesis. */
+       /* Arguments are enclosed in parentheses. */
 
-       if ('(' != p[*pos]) {
-               TBL_MSG(tbl, MANDOCERR_TBL, ln, *pos);
-               return(0);
+       len = 0;
+       if (p[*pos] == '(') {
+               (*pos)++;
+               while (p[*pos + len] != ')')
+                       len++;
        }
 
-       (*pos)++;
-
-       /*
-        * 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):
-               if ('\0' == (tbl->delims[0] = p[(*pos)++])) {
-                       TBL_MSG(tbl, MANDOCERR_TBL, ln, *pos - 1);
-                       return(0);
-               } 
-
-               if ('\0' == (tbl->delims[1] = p[(*pos)++])) {
-                       TBL_MSG(tbl, MANDOCERR_TBL, ln, *pos - 1);
-                       return(0);
-               } 
+       case KEY_DELIM:
+               mandoc_msg(MANDOCERR_TBLOPT_EQN,
+                   ln, *pos, "%.*s", len, p + *pos);
+               want = 2;
+               break;
+       case KEY_TAB:
+               want = 1;
+               if (len == want)
+                       tbl->opts.tab = p[*pos];
+               break;
+       case KEY_LINESIZE:
+               want = 0;
+               break;
+       case KEY_DPOINT:
+               want = 1;
+               if (len == want)
+                       tbl->opts.decimal = p[*pos];
                break;
-       case (KEY_TAB):
-               if ('\0' != (tbl->tab = p[(*pos)++]))
-                       break;
-
-               TBL_MSG(tbl, MANDOCERR_TBL, ln, *pos - 1);
-               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->linesize = atoi(buf);
-                       break;
-               }
-
-               (*tbl->msg)(MANDOCERR_TBL, tbl->data, ln, *pos, NULL);
-               return(0);
-       case (KEY_DPOINT):
-               if ('\0' != (tbl->decimal = p[(*pos)++]))
-                       break;
-
-               TBL_MSG(tbl, MANDOCERR_TBL, ln, *pos - 1);
-               return(0);
        default:
                abort();
-               /* NOTREACHED */
        }
 
-       /* End with a close parenthesis. */
+       if (len == 0)
+               mandoc_msg(MANDOCERR_TBLOPT_NOARG, ln, *pos,
+                   "%s", keys[key].name);
+       else if (want && len != want)
+               mandoc_msg(MANDOCERR_TBLOPT_ARGSZ, ln, *pos,
+                   "%s want %d have %d", keys[key].name, want, len);
 
-       if (')' == p[(*pos)++])
-               return(1);
-
-       TBL_MSG(tbl, MANDOCERR_TBL, ln, *pos - 1);
-       return(0);
+       *pos += len;
+       if (p[*pos] == ')')
+               (*pos)++;
 }
 
-static void
-opt(struct tbl *tbl, int ln, const char *p, int *pos)
+/*
+ * Parse one line of options up to the semicolon.
+ * Each option can be preceded by blanks and/or commas,
+ * and some options are followed by arguments.
+ */
+void
+tbl_option(struct tbl_node *tbl, int ln, const char *p, int *offs)
 {
-       int              i, sv;
-       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.
-        */
+       int              i, pos, len;
 
-again: /*
-        * EBNF describing this section:
-        *
-        * options      ::= option_list [:space:]* [;][\n]
-        * option_list  ::= option option_tail
-        * option_tail  ::= [:space:]+ option_list |
-        *              ::= epsilon
-        * option       ::= [:alpha:]+ args
-        * args         ::= [:space:]* [(] [:alpha:]+ [)]
-        */
+       pos = *offs;
+       for (;;) {
+               while (p[pos] == ' ' || p[pos] == '\t' || p[pos] == ',')
+                       pos++;
 
-       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] = tolower(p[*pos]);
-               if ( ! isalpha((unsigned char)buf[i]))
-                       break;
-       }
+               if (p[pos] == ';') {
+                       *offs = pos + 1;
+                       return;
+               }
 
-       /* Exit if buffer is empty (or overrun). */
+               /* Parse one option name. */
 
-       if (KEY_MAXNAME == i || 0 == i) {
-               TBL_MSG(tbl, MANDOCERR_TBL, ln, *pos);
-               return;
-       }
+               len = 0;
+               while (isalpha((unsigned char)p[pos + len]))
+                       len++;
 
-       buf[i] = '\0';
+               if (len == 0) {
+                       mandoc_msg(MANDOCERR_TBLOPT_ALPHA,
+                           ln, pos, "%c", p[pos]);
+                       pos++;
+                       continue;
+               }
 
-       while (isspace((unsigned char)p[*pos]))
-               (*pos)++;
+               /* Look up the option name. */
 
-       /* 
-        * Look through all of the available keys to find one that
-        * matches the input.  FIXME: hashtable this.
-        */
+               i = 0;
+               while (i < KEY_MAXKEYS &&
+                   (strncasecmp(p + pos, keys[i].name, len) ||
+                    keys[i].name[len] != '\0'))
+                       i++;
 
-       for (i = 0; i < KEY_MAXKEYS; i++) {
-               if (strcmp(buf, keys[i].name))
+               if (i == KEY_MAXKEYS) {
+                       mandoc_msg(MANDOCERR_TBLOPT_BAD,
+                           ln, pos, "%.*s", len, p + pos);
+                       pos += len;
                        continue;
+               }
 
-               /*
-                * 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 |= keys[i].key;
-               else if ( ! arg(tbl, ln, p, pos, keys[i].ident))
-                       return;
+               /* Handle the option. */
 
-               break;
+               pos += len;
+               if (keys[i].key)
+                       tbl->opts.opts |= keys[i].key;
+               else
+                       arg(tbl, ln, p, &pos, i);
        }
-
-       /* 
-        * Allow us to recover from bad options by continuing to another
-        * parse sequence.
-        */
-
-       if (KEY_MAXKEYS == i)
-               TBL_MSG(tbl, MANDOCERR_TBLOPT, ln, sv);
-
-       /* Try again... */
-
-       goto again;
-}
-
-int
-tbl_option(struct tbl *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;
-       opt(tbl, ln, p, &pos);
-
-       /* Always succeed. */
-       return(1);
 }