]> git.cameronkatri.com Git - mandoc.git/blobdiff - roff.c
Split mlinks_undupe() out of mpages_merge()
[mandoc.git] / roff.c
diff --git a/roff.c b/roff.c
index eb9d495edd11ae4d136d2d5d22a64bbbd577035f..e33216c40970f719a4f18da04d9bbf9e5f091be2 100644 (file)
--- a/roff.c
+++ b/roff.c
@@ -1,4 +1,4 @@
-/*     $Id: roff.c,v 1.180 2013/10/05 22:08:12 schwarze Exp $ */
+/*     $Id: roff.c,v 1.188 2013/12/25 00:50:05 schwarze Exp $ */
 /*
  * Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2010, 2011, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
@@ -46,6 +46,8 @@ enum  rofft {
        ROFF_de1,
        ROFF_ds,
        ROFF_el,
+       ROFF_fam,
+       ROFF_hw,
        ROFF_hy,
        ROFF_ie,
        ROFF_if,
@@ -74,8 +76,8 @@ enum  rofft {
 };
 
 enum   roffrule {
-       ROFFRULE_ALLOW,
-       ROFFRULE_DENY
+       ROFFRULE_DENY,
+       ROFFRULE_ALLOW
 };
 
 /*
@@ -97,11 +99,10 @@ struct      roffkv {
 
 /*
  * A single number register as part of a singly-linked list.
- * Registers are assumed to be unsigned ints for now.
  */
 struct roffreg {
        struct roffstr   key;
-       unsigned int     u;
+       int              val;
        struct roffreg  *next;
 };
 
@@ -186,6 +187,10 @@ static     void             roff_free1(struct roff *);
 static void             roff_freereg(struct roffreg *);
 static void             roff_freestr(struct roffkv *);
 static char            *roff_getname(struct roff *, char **, int, int);
+static int              roff_getnum(const char *, int *, int *);
+static int              roff_getop(const char *, int *, char *);
+static int              roff_getregn(const struct roff *,
+                               const char *, size_t);
 static const char      *roff_getstrn(const struct roff *, 
                                const char *, size_t);
 static enum rofferr     roff_it(ROFF_ARGS);
@@ -232,6 +237,8 @@ static      struct roffmac   roffs[ROFF_MAX] = {
        { "de1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
        { "ds", roff_ds, NULL, NULL, 0, NULL },
        { "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
+       { "fam", roff_line_ignore, NULL, NULL, 0, NULL },
+       { "hw", roff_line_ignore, NULL, NULL, 0, NULL },
        { "hy", roff_line_ignore, NULL, NULL, 0, NULL },
        { "ie", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
        { "if", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
@@ -480,22 +487,23 @@ roff_alloc(enum mparset type, struct mparse *parse)
 }
 
 /*
- * Pre-filter each and every line for reserved words (one beginning with
- * `\*', e.g., `\*(ab').  These must be handled before the actual line
- * is processed. 
- * This also checks the syntax of regular escapes.
+ * In the current line, expand user-defined strings ("\*")
+ * and references to number registers ("\n").
+ * Also check the syntax of other escape sequences.
  */
 static enum rofferr
 roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos)
 {
-       enum mandoc_esc  esc;
+       char             ubuf[12]; /* buffer to print the number */
        const char      *stesc; /* start of an escape sequence ('\\') */
        const char      *stnam; /* start of the name, after "[(*" */
        const char      *cp;    /* end of the name, e.g. before ']' */
        const char      *res;   /* the string to be substituted */
-       int              i, maxl, expand_count;
-       size_t           nsz;
-       char            *n;
+       char            *nbuf;  /* new buffer to copy bufp to */
+       size_t           nsz;   /* size of the new buffer */
+       size_t           maxl;  /* expected length of the escape name */
+       size_t           naml;  /* actual length of the escape name */
+       int              expand_count;  /* to avoid infinite loops */
 
        expand_count = 0;
 
@@ -505,7 +513,7 @@ again:
                stesc = cp++;
 
                /*
-                * The second character must be an asterisk.
+                * The second character must be an asterisk or an n.
                 * If it isn't, skip it anyway:  It is escaped,
                 * so it can't start another escape sequence.
                 */
@@ -513,12 +521,16 @@ again:
                if ('\0' == *cp)
                        return(ROFF_CONT);
 
-               if ('*' != *cp) {
-                       res = cp;
-                       esc = mandoc_escape(&cp, NULL, NULL);
-                       if (ESCAPE_ERROR != esc)
+               switch (*cp) {
+               case ('*'):
+                       res = NULL;
+                       break;
+               case ('n'):
+                       res = ubuf;
+                       break;
+               default:
+                       if (ESCAPE_ERROR != mandoc_escape(&cp, NULL, NULL))
                                continue;
-                       cp = res;
                        mandoc_msg
                                (MANDOCERR_BADESCAPE, r->parse, 
                                 ln, (int)(stesc - *bufp), NULL);
@@ -529,7 +541,7 @@ again:
 
                /*
                 * The third character decides the length
-                * of the name of the string.
+                * of the name of the string or register.
                 * Save a pointer to the name.
                 */
 
@@ -552,7 +564,7 @@ again:
 
                /* Advance to the end of the name. */
 
-               for (i = 0; 0 == maxl || i < maxl; i++, cp++) {
+               for (naml = 0; 0 == maxl || naml < maxl; naml++, cp++) {
                        if ('\0' == *cp) {
                                mandoc_msg
                                        (MANDOCERR_BADESCAPE, 
@@ -569,7 +581,11 @@ again:
                 * undefined, resume searching for escapes.
                 */
 
-               res = roff_getstrn(r, stnam, (size_t)i);
+               if (NULL == res)
+                       res = roff_getstrn(r, stnam, naml);
+               else
+                       snprintf(ubuf, sizeof(ubuf), "%d",
+                           roff_getregn(r, stnam, naml));
 
                if (NULL == res) {
                        mandoc_msg
@@ -583,15 +599,15 @@ again:
                pos = stesc - *bufp;
 
                nsz = *szp + strlen(res) + 1;
-               n = mandoc_malloc(nsz);
+               nbuf = mandoc_malloc(nsz);
 
-               strlcpy(n, *bufp, (size_t)(stesc - *bufp + 1));
-               strlcat(n, res, nsz);
-               strlcat(n, cp + (maxl ? 0 : 1), nsz);
+               strlcpy(nbuf, *bufp, (size_t)(stesc - *bufp + 1));
+               strlcat(nbuf, res, nsz);
+               strlcat(nbuf, cp + (maxl ? 0 : 1), nsz);
 
                free(*bufp);
 
-               *bufp = n;
+               *bufp = nbuf;
                *szp = nsz;
 
                if (EXPAND_LIMIT >= ++expand_count)
@@ -702,19 +718,14 @@ roff_parseln(struct roff *r, int ln, char **bufp,
                assert(ROFF_IGN == e || ROFF_CONT == e);
                if (ROFF_CONT != e)
                        return(e);
-               if (r->eqn)
-                       return(eqn_read(&r->eqn, ln, *bufp, pos, offs));
-               if (r->tbl)
-                       return(tbl_read(r->tbl, ln, *bufp, pos));
-               return(roff_parsetext(bufp, szp, pos, offs));
-       } else if ( ! ctl) {
-               if (r->eqn)
-                       return(eqn_read(&r->eqn, ln, *bufp, pos, offs));
+       }
+       if (r->eqn)
+               return(eqn_read(&r->eqn, ln, *bufp, ppos, offs));
+       if ( ! ctl) {
                if (r->tbl)
                        return(tbl_read(r->tbl, ln, *bufp, pos));
                return(roff_parsetext(bufp, szp, pos, offs));
-       } else if (r->eqn)
-               return(eqn_read(&r->eqn, ln, *bufp, ppos, offs));
+       }
 
        /*
         * If a scope is open, go to the child handler for that macro,
@@ -1120,9 +1131,61 @@ roff_cond_text(ROFF_ARGS)
        return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
 }
 
+static int
+roff_getnum(const char *v, int *pos, int *res)
+{
+       int p, n;
+
+       p = *pos;
+       n = v[p] == '-';
+       if (n)
+               p++;
+
+       for (*res = 0; isdigit((unsigned char)v[p]); p++)
+               *res += 10 * *res + v[p] - '0';
+       if (p == *pos + n)
+               return 0;
+
+       if (n)
+               *res = -*res;
+
+       *pos = p;
+       return 1;
+}
+
+static int
+roff_getop(const char *v, int *pos, char *res)
+{
+       int e;
+
+       *res = v[*pos];
+       e = v[*pos + 1] == '=';
+
+       switch (*res) {
+       case '=':
+               break;
+       case '>':
+               if (e)
+                       *res = 'g';
+               break;
+       case '<':
+               if (e)
+                       *res = 'l';
+               break;
+       default:
+               return(0);
+       }
+
+       *pos += 1 + e;
+
+       return(*res);
+}
+
 static enum roffrule
 roff_evalcond(const char *v, int *pos)
 {
+       int      not, lh, rh;
+       char     op;
 
        switch (v[*pos]) {
        case ('n'):
@@ -1135,13 +1198,47 @@ roff_evalcond(const char *v, int *pos)
        case ('t'):
                (*pos)++;
                return(ROFFRULE_DENY);
+       case ('!'):
+               (*pos)++;
+               not = 1;
+               break;
        default:
+               not = 0;
                break;
        }
 
-       while (v[*pos] && ' ' != v[*pos])
-               (*pos)++;
-       return(ROFFRULE_DENY);
+       if (!roff_getnum(v, pos, &lh))
+               return ROFFRULE_DENY;
+       if (!roff_getop(v, pos, &op)) {
+               if (lh < 0)
+                       lh = 0;
+               goto out;
+       }
+       if (!roff_getnum(v, pos, &rh))
+               return ROFFRULE_DENY;
+       switch (op) {
+       case 'g':
+               lh = lh >= rh;
+               break;
+       case 'l':
+               lh = lh <= rh;
+               break;
+       case '=':
+               lh = lh == rh;
+               break;
+       case '>':
+               lh = lh > rh;
+               break;
+       case '<':
+               lh = lh < rh;
+               break;
+       default:
+               return ROFFRULE_DENY;
+       }
+out:
+       if (not)
+               lh = !lh;
+       return lh ? ROFFRULE_ALLOW : ROFFRULE_DENY;
 }
 
 /* ARGSUSED */
@@ -1263,7 +1360,7 @@ roff_ds(ROFF_ARGS)
 }
 
 void
-roff_setreg(struct roff *r, const char *name, unsigned int val)
+roff_setreg(struct roff *r, const char *name, int val, char sign)
 {
        struct roffreg  *reg;
 
@@ -1278,21 +1375,40 @@ roff_setreg(struct roff *r, const char *name, unsigned int val)
                reg = mandoc_malloc(sizeof(struct roffreg));
                reg->key.p = mandoc_strdup(name);
                reg->key.sz = strlen(name);
+               reg->val = 0;
                reg->next = r->regtab;
                r->regtab = reg;
        }
 
-       reg->u = val;
+       if ('+' == sign)
+               reg->val += val;
+       else if ('-' == sign)
+               reg->val -= val;
+       else
+               reg->val = val;
 }
 
-unsigned int
+int
 roff_getreg(const struct roff *r, const char *name)
 {
        struct roffreg  *reg;
 
        for (reg = r->regtab; reg; reg = reg->next)
                if (0 == strcmp(name, reg->key.p))
-                       return(reg->u);
+                       return(reg->val);
+
+       return(0);
+}
+
+static int
+roff_getregn(const struct roff *r, const char *name, size_t len)
+{
+       struct roffreg  *reg;
+
+       for (reg = r->regtab; reg; reg = reg->next)
+               if (len == reg->key.sz &&
+                   0 == strncmp(name, reg->key.p, len))
+                       return(reg->val);
 
        return(0);
 }
@@ -1316,16 +1432,21 @@ roff_nr(ROFF_ARGS)
 {
        const char      *key;
        char            *val;
+       size_t           sz;
        int              iv;
+       char             sign;
 
        val = *bufp + pos;
        key = roff_getname(r, &val, ln, pos);
 
-       iv = mandoc_strntoi(val, strlen(val), 10);
-       if (0 > iv)
-               iv = 0;
+       sign = *val;
+       if ('+' == sign || '-' == sign)
+               val++;
+
+       sz = strspn(val, "0123456789");
+       iv = sz ? mandoc_strntoi(val, sz, 10) : 0;
 
-       roff_setreg(r, key, (unsigned)iv);
+       roff_setreg(r, key, iv, sign);
 
        return(ROFF_IGN);
 }
@@ -1612,7 +1733,7 @@ roff_userdef(ROFF_ARGS)
 
        /*
         * Collect pointers to macro argument strings
-        * and null-terminate them.
+        * and NUL-terminate them.
         */
        cp = *bufp + pos;
        for (i = 0; i < 9; i++)