]> git.cameronkatri.com Git - mandoc.git/blobdiff - roff.c
Smarten BADCHAR check to allow ASCII_HYPH.
[mandoc.git] / roff.c
diff --git a/roff.c b/roff.c
index 9226af3f9bc24bdeecb7cf3bc66dbead9de868ec..d57d2457ceb6f5f5c9f7e93901314edce9643bb6 100644 (file)
--- a/roff.c
+++ b/roff.c
@@ -1,4 +1,4 @@
-/*     $Id: roff.c,v 1.80 2010/05/17 00:37:26 kristaps Exp $ */
+/*     $Id: roff.c,v 1.84 2010/05/24 23:54:18 schwarze Exp $ */
 /*
  * Copyright (c) 2010 Kristaps Dzonsons <kristaps@bsd.lv>
  *
@@ -26,6 +26,8 @@
 #include "mandoc.h"
 #include "roff.h"
 
+#define        RSTACK_MAX      128
+
 #define        ROFF_CTL(c) \
        ('.' == (c) || '\'' == (c))
 
@@ -36,26 +38,29 @@ enum        rofft {
        ROFF_de,
        ROFF_dei,
        ROFF_de1,
+       ROFF_ds,
+       ROFF_el,
+       ROFF_ie,
        ROFF_if,
        ROFF_ig,
+       ROFF_rm,
+       ROFF_tr,
        ROFF_cblock,
        ROFF_ccond,
-#if 0
-       ROFF_ie,
-       ROFF_el,
-#endif
        ROFF_MAX
 };
 
+enum   roffrule {
+       ROFFRULE_ALLOW,
+       ROFFRULE_DENY
+};
+
 struct roff {
        struct roffnode *last; /* leaf of stack */
        mandocmsg        msg; /* err/warn/fatal messages */
        void            *data; /* privdata for messages */
-};
-
-enum   roffrule {
-       ROFFRULE_ALLOW,
-       ROFFRULE_DENY
+       enum roffrule    rstack[RSTACK_MAX]; /* stack of !`ie' rules */
+       int              rstackpos; /* position in rstack */
 };
 
 struct roffnode {
@@ -65,7 +70,7 @@ struct        roffnode {
        int              col; /* parse col */
        char            *end; /* end-rules: custom token */
        int              endspan; /* end-rules: next-line or infty */
-       enum roffrule    rule;
+       enum roffrule    rule; /* current evaluation rule */
 };
 
 #define        ROFF_ARGS        struct roff *r, /* parse ctx */ \
@@ -93,9 +98,10 @@ static       enum rofferr     roff_block_text(ROFF_ARGS);
 static enum rofferr     roff_block_sub(ROFF_ARGS);
 static enum rofferr     roff_cblock(ROFF_ARGS);
 static enum rofferr     roff_ccond(ROFF_ARGS);
-static enum rofferr     roff_if(ROFF_ARGS);
-static enum rofferr     roff_if_text(ROFF_ARGS);
-static enum rofferr     roff_if_sub(ROFF_ARGS);
+static enum rofferr     roff_cond(ROFF_ARGS);
+static enum rofferr     roff_cond_text(ROFF_ARGS);
+static enum rofferr     roff_cond_sub(ROFF_ARGS);
+static enum rofferr     roff_line(ROFF_ARGS);
 
 const  struct roffmac   roffs[ROFF_MAX] = {
        { "am", roff_block, roff_block_text, roff_block_sub, 0 },
@@ -104,8 +110,13 @@ const      struct roffmac   roffs[ROFF_MAX] = {
        { "de", roff_block, roff_block_text, roff_block_sub, 0 },
        { "dei", roff_block, roff_block_text, roff_block_sub, 0 },
        { "de1", roff_block, roff_block_text, roff_block_sub, 0 },
-       { "if", roff_if, roff_if_text, roff_if_sub, ROFFMAC_STRUCT },
+       { "ds", roff_line, NULL, NULL, 0 },
+       { "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT },
+       { "ie", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT },
+       { "if", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT },
        { "ig", roff_block, roff_block_text, roff_block_sub, 0 },
+       { "rm", roff_line, NULL, NULL, 0 },
+       { "tr", roff_line, NULL, NULL, 0 },
        { ".", roff_cblock, NULL, NULL, 0 },
        { "\\}", roff_ccond, NULL, NULL, 0 },
 };
@@ -149,6 +160,11 @@ roffnode_pop(struct roff *r)
 
        assert(r->last);
        p = r->last; 
+
+       if (ROFF_el == p->tok)
+               if (r->rstackpos > -1)
+                       r->rstackpos--;
+
        r->last = r->last->parent;
        if (p->end)
                free(p->end);
@@ -219,6 +235,7 @@ roff_alloc(const mandocmsg msg, void *data)
 
        r->msg = msg;
        r->data = data;
+       r->rstackpos = -1;
        return(r);
 }
 
@@ -306,7 +323,7 @@ roff_parse(const char *buf, int *pos)
        for (j = 0; j < 4; j++, (*pos)++)
                if ('\0' == (mac[j] = buf[*pos]))
                        break;
-               else if (' ' == buf[*pos])
+               else if (' ' == buf[*pos] || (j && '\\' == buf[*pos]))
                        break;
 
        if (j == 4 || j < 1)
@@ -340,7 +357,22 @@ roff_cblock(ROFF_ARGS)
                return(ROFF_IGN);
        }
 
-       if (ROFF_ig != r->last->tok) {
+       switch (r->last->tok) {
+       case (ROFF_am):
+               /* FALLTHROUGH */
+       case (ROFF_ami):
+               /* FALLTHROUGH */
+       case (ROFF_am1):
+               /* FALLTHROUGH */
+       case (ROFF_de):
+               /* FALLTHROUGH */
+       case (ROFF_dei):
+               /* FALLTHROUGH */
+       case (ROFF_de1):
+               /* FALLTHROUGH */
+       case (ROFF_ig):
+               break;
+       default:
                if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
                        return(ROFF_ERR);
                return(ROFF_IGN);
@@ -380,7 +412,14 @@ roff_ccond(ROFF_ARGS)
                return(ROFF_IGN);
        }
 
-       if (ROFF_if != r->last->tok) {
+       switch (r->last->tok) {
+       case (ROFF_el):
+               /* FALLTHROUGH */
+       case (ROFF_ie):
+               /* FALLTHROUGH */
+       case (ROFF_if):
+               break;
+       default:
                if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
                        return(ROFF_ERR);
                return(ROFF_IGN);
@@ -461,36 +500,6 @@ roff_block(ROFF_ARGS)
 }
 
 
-/* ARGSUSED */
-static enum rofferr
-roff_if_sub(ROFF_ARGS)
-{
-       enum rofft       t;
-       enum roffrule    rr;
-
-       ppos = pos;
-       rr = r->last->rule;
-       roffnode_cleanscope(r);
-
-       if (ROFF_MAX == (t = roff_parse(*bufp, &pos)))
-               return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
-
-       /*
-        * A denied conditional must evaluate its children if and only
-        * if they're either structurally required (such as loops and
-        * conditionals) or a closing macro.
-        */
-       if (ROFFRULE_DENY == rr)
-               if ( ! (ROFFMAC_STRUCT & roffs[t].flags))
-                       if (ROFF_ccond != t)
-                               return(ROFF_IGN);
-
-       assert(roffs[t].proc);
-       return((*roffs[t].proc)
-                       (r, t, bufp, szp, ln, ppos, pos, offs));
-}
-
-
 /* ARGSUSED */
 static enum rofferr
 roff_block_sub(ROFF_ARGS)
@@ -558,39 +567,91 @@ roff_block_text(ROFF_ARGS)
 
 /* ARGSUSED */
 static enum rofferr
-roff_if_text(ROFF_ARGS)
+roff_cond_sub(ROFF_ARGS)
+{
+       enum rofft       t;
+       enum roffrule    rr;
+
+       ppos = pos;
+       rr = r->last->rule;
+
+       roff_cond_text(r, tok, bufp, szp, ln, ppos, pos, offs);
+
+       if (ROFF_MAX == (t = roff_parse(*bufp, &pos)))
+               return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
+
+       /*
+        * A denied conditional must evaluate its children if and only
+        * if they're either structurally required (such as loops and
+        * conditionals) or a closing macro.
+        */
+       if (ROFFRULE_DENY == rr)
+               if ( ! (ROFFMAC_STRUCT & roffs[t].flags))
+                       if (ROFF_ccond != t)
+                               return(ROFF_IGN);
+
+       assert(roffs[t].proc);
+       return((*roffs[t].proc)
+                       (r, t, bufp, szp, ln, ppos, pos, offs));
+}
+
+
+/* ARGSUSED */
+static enum rofferr
+roff_cond_text(ROFF_ARGS)
 {
        char            *ep, *st;
+       enum roffrule    rr;
+
+       rr = r->last->rule;
+
+       /*
+        * We display the value of the text if out current evaluation
+        * scope permits us to do so.
+        */
 
        st = &(*bufp)[pos];
        if (NULL == (ep = strstr(st, "\\}"))) {
                roffnode_cleanscope(r);
-               return(ROFF_IGN);
+               return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
        }
 
-       if (ep > st && '\\' != *(ep - 1))
+       if (ep > st && '\\' != *(ep - 1)) {
+               ep = '\0';
                roffnode_pop(r);
+       }
 
        roffnode_cleanscope(r);
-       return(ROFF_IGN);
+       return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
 }
 
 
 /* ARGSUSED */
 static enum rofferr
-roff_if(ROFF_ARGS)
+roff_cond(ROFF_ARGS)
 {
+       int              cpos;  /* position of the condition */
        int              sv;
 
-       /*
-        * Read ahead past the conditional.
-        * FIXME: this does not work, as conditionals don't end on
-        * whitespace, but are parsed according to a formal grammar.
-        * It's good enough for now, however.
-        */
+       /* Stack overflow! */
 
-       while ((*bufp)[pos] && ' ' != (*bufp)[pos])
-               pos++;
+       if (ROFF_ie == tok && r->rstackpos == RSTACK_MAX - 1) {
+               (*r->msg)(MANDOCERR_MEM, r->data, ln, ppos, NULL);
+               return(ROFF_ERR);
+       }
+
+       cpos = pos;
+
+       if (ROFF_if == tok || ROFF_ie == tok) {
+               /*
+                * Read ahead past the conditional.  FIXME: this does
+                * not work, as conditionals don't end on whitespace,
+                * but are parsed according to a formal grammar.  It's
+                * good enough for now, however.
+                */
+               while ((*bufp)[pos] && ' ' != (*bufp)[pos])
+                       pos++;
+       }
 
        sv = pos;
        while (' ' == (*bufp)[pos])
@@ -602,7 +663,6 @@ roff_if(ROFF_ARGS)
         * really doing anything.  Warn about this.  It's probably
         * wrong.
         */
-
        if ('\0' == (*bufp)[pos] && sv != pos) {
                if ( ! (*r->msg)(MANDOCERR_NOARGS, r->data, ln, ppos, NULL))
                        return(ROFF_ERR);
@@ -612,7 +672,34 @@ roff_if(ROFF_ARGS)
        if ( ! roffnode_push(r, tok, ln, ppos))
                return(ROFF_ERR);
 
-       /* Don't evaluate: just assume NO. */
+       /* XXX: Implement more conditionals. */
+
+       if (ROFF_if == tok || ROFF_ie == tok)
+               r->last->rule = 'n' == (*bufp)[cpos] ?
+                   ROFFRULE_ALLOW : ROFFRULE_DENY;
+       else if (ROFF_el == tok) {
+               /* 
+                * An `.el' will get the value of the current rstack
+                * entry set in prior `ie' calls or defaults to DENY.
+                */
+               if (r->rstackpos < 0)
+                       r->last->rule = ROFFRULE_DENY;
+               else
+                       r->last->rule = r->rstack[r->rstackpos];
+       }
+       if (ROFF_ie == tok) {
+               /*
+                * An if-else will put the NEGATION of the current
+                * evaluated conditional into the stack.
+                */
+               r->rstackpos++;
+               if (ROFFRULE_DENY == r->last->rule)
+                       r->rstack[r->rstackpos] = ROFFRULE_ALLOW;
+               else
+                       r->rstack[r->rstackpos] = ROFFRULE_DENY;
+       }
+       if (r->last->parent && ROFFRULE_DENY == r->last->parent->rule)
+               r->last->rule = ROFFRULE_DENY;
 
        r->last->endspan = 1;
 
@@ -634,3 +721,12 @@ roff_if(ROFF_ARGS)
        *offs = pos;
        return(ROFF_RERUN);
 }
+
+
+/* ARGSUSED */
+static enum rofferr
+roff_line(ROFF_ARGS)
+{
+
+       return(ROFF_IGN);
+}