]> git.cameronkatri.com Git - mandoc.git/blobdiff - roff.c
when indenting, extend the right margin accordingly, when needed;
[mandoc.git] / roff.c
diff --git a/roff.c b/roff.c
index eb9d495edd11ae4d136d2d5d22a64bbbd577035f..dce31f85a470d7b2b416bc969c529a7d722766df 100644 (file)
--- a/roff.c
+++ b/roff.c
@@ -1,7 +1,7 @@
-/*     $Id: roff.c,v 1.180 2013/10/05 22:08:12 schwarze Exp $ */
+/*     $Id: roff.c,v 1.194 2014/02/14 23:24:26 schwarze Exp $ */
 /*
  * Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2010, 2011, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010-2014 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
@@ -40,12 +40,16 @@ enum        rofft {
        ROFF_am,
        ROFF_ami,
        ROFF_am1,
+       ROFF_as,
        ROFF_cc,
+       ROFF_ce,
        ROFF_de,
        ROFF_dei,
        ROFF_de1,
        ROFF_ds,
        ROFF_el,
+       ROFF_fam,
+       ROFF_hw,
        ROFF_hy,
        ROFF_ie,
        ROFF_if,
@@ -74,8 +78,8 @@ enum  rofft {
 };
 
 enum   roffrule {
-       ROFFRULE_ALLOW,
-       ROFFRULE_DENY
+       ROFFRULE_DENY,
+       ROFFRULE_ALLOW
 };
 
 /*
@@ -97,17 +101,17 @@ 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;
 };
 
 struct roff {
        enum mparset     parsetype; /* requested parse type */
        struct mparse   *parse; /* parse point */
+       int              quick; /* skip standard macro deletion */
        struct roffnode *last; /* leaf of stack */
        enum roffrule    rstack[RSTACK_MAX]; /* stack of !`ie' rules */
        char             control; /* control character */
@@ -186,6 +190,11 @@ 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 int              roff_getregro(const char *name);
 static const char      *roff_getstrn(const struct roff *, 
                                const char *, size_t);
 static enum rofferr     roff_it(ROFF_ARGS);
@@ -226,12 +235,16 @@ static    struct roffmac   roffs[ROFF_MAX] = {
        { "am", roff_block, roff_block_text, roff_block_sub, 0, NULL },
        { "ami", roff_block, roff_block_text, roff_block_sub, 0, NULL },
        { "am1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
+       { "as", roff_ds, NULL, NULL, 0, NULL },
        { "cc", roff_cc, NULL, NULL, 0, NULL },
+       { "ce", roff_line_ignore, NULL, NULL, 0, NULL },
        { "de", roff_block, roff_block_text, roff_block_sub, 0, NULL },
        { "dei", roff_block, roff_block_text, roff_block_sub, 0, NULL },
        { "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 },
@@ -440,14 +453,9 @@ roff_free1(struct roff *r)
 void
 roff_reset(struct roff *r)
 {
-       int              i;
 
        roff_free1(r);
-
        r->control = 0;
-
-       for (i = 0; i < PREDEFS_MAX; i++) 
-               roff_setstr(r, predefs[i].name, predefs[i].str, 0);
 }
 
 
@@ -461,41 +469,39 @@ roff_free(struct roff *r)
 
 
 struct roff *
-roff_alloc(enum mparset type, struct mparse *parse)
+roff_alloc(enum mparset type, struct mparse *parse, int quick)
 {
        struct roff     *r;
-       int              i;
 
        r = mandoc_calloc(1, sizeof(struct roff));
        r->parsetype = type;
        r->parse = parse;
+       r->quick = quick;
        r->rstackpos = -1;
        
        roffhash_init();
 
-       for (i = 0; i < PREDEFS_MAX; i++) 
-               roff_setstr(r, predefs[i].name, predefs[i].str, 0);
-
        return(r);
 }
 
 /*
- * 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 +511,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 +519,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 +539,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 +562,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 +579,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 +597,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)
@@ -630,8 +644,7 @@ roff_parsetext(char **bufp, size_t *szp, int pos, int *offs)
                if ('\\' == *p) {
                        /* Skip over escapes. */
                        p++;
-                       esc = mandoc_escape
-                               ((const char const **)&p, NULL, NULL);
+                       esc = mandoc_escape((const char **)&p, NULL, NULL);
                        if (ESCAPE_ERROR == esc)
                                break;
                        continue;
@@ -702,19 +715,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,
@@ -935,7 +943,7 @@ roff_block(ROFF_ARGS)
        /*
         * At the beginning of a `de' macro, clear the existing string
         * with the same name, if there is one.  New content will be
-        * added from roff_block_text() in multiline mode.
+        * appended from roff_block_text() in multiline mode.
         */
 
        if (ROFF_de == tok)
@@ -1025,7 +1033,7 @@ roff_block_sub(ROFF_ARGS)
         */
        if (ROFF_cblock != t) {
                if (ROFF_de == tok)
-                       roff_setstr(r, r->last->name, *bufp + ppos, 1);
+                       roff_setstr(r, r->last->name, *bufp + ppos, 2);
                return(ROFF_IGN);
        }
 
@@ -1041,7 +1049,7 @@ roff_block_text(ROFF_ARGS)
 {
 
        if (ROFF_de == tok)
-               roff_setstr(r, r->last->name, *bufp + pos, 1);
+               roff_setstr(r, r->last->name, *bufp + pos, 2);
 
        return(ROFF_IGN);
 }
@@ -1120,9 +1128,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 +1195,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 */
@@ -1258,12 +1352,12 @@ roff_ds(ROFF_ARGS)
                string++;
 
        /* The rest is the value. */
-       roff_setstr(r, name, string, 0);
+       roff_setstr(r, name, string, ROFF_as == tok);
        return(ROFF_IGN);
 }
 
 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 +1372,82 @@ 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;
+}
+
+/*
+ * Handle some predefined read-only number registers.
+ * For now, return -1 if the requested register is not predefined;
+ * in case a predefined read-only register having the value -1
+ * were to turn up, another special value would have to be chosen.
+ */
+static int
+roff_getregro(const char *name)
+{
+
+       switch (*name) {
+       case ('A'):  /* ASCII approximation mode is always off. */
+               return(0);
+       case ('g'):  /* Groff compatibility mode is always on. */
+               return(1);
+       case ('H'):  /* Fixed horizontal resolution. */
+               return (24);
+       case ('j'):  /* Always adjust left margin only. */
+               return(0);
+       case ('T'):  /* Some output device is always defined. */
+               return(1);
+       case ('V'):  /* Fixed vertical resolution. */
+               return (40);
+       default:
+               return (-1);
+       }
 }
 
-unsigned int
+int
 roff_getreg(const struct roff *r, const char *name)
 {
        struct roffreg  *reg;
+       int              val;
+
+       if ('.' == name[0] && '\0' != name[1] && '\0' == name[2]) {
+               val = roff_getregro(name + 1);
+               if (-1 != val)
+                       return (val);
+       }
 
        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;
+       int              val;
+
+       if ('.' == name[0] && 2 == len) {
+               val = roff_getregro(name + 1);
+               if (-1 != val)
+                       return (val);
+       }
+
+       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 +1471,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++;
 
-       roff_setreg(r, key, (unsigned)iv);
+       sz = strspn(val, "0123456789");
+       iv = sz ? mandoc_strntoi(val, sz, 10) : 0;
+
+       roff_setreg(r, key, iv, sign);
 
        return(ROFF_IGN);
 }
@@ -1377,7 +1537,7 @@ roff_Dd(ROFF_ARGS)
 {
        const char *const       *cp;
 
-       if (MPARSE_MDOC != r->parsetype)
+       if (0 == r->quick && MPARSE_MDOC != r->parsetype)
                for (cp = __mdoc_reserved; *cp; cp++)
                        roff_setstr(r, *cp, NULL, 0);
 
@@ -1390,7 +1550,7 @@ roff_TH(ROFF_ARGS)
 {
        const char *const       *cp;
 
-       if (MPARSE_MDOC != r->parsetype)
+       if (0 == r->quick && MPARSE_MDOC != r->parsetype)
                for (cp = __man_reserved; *cp; cp++)
                        roff_setstr(r, *cp, NULL, 0);
 
@@ -1612,7 +1772,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++)
@@ -1692,22 +1852,23 @@ roff_getname(struct roff *r, char **cpp, int ln, int pos)
 
 /*
  * Store *string into the user-defined string called *name.
- * In multiline mode, append to an existing entry and append '\n';
- * else replace the existing entry, if there is one.
  * To clear an existing entry, call with (*r, *name, NULL, 0).
+ * append == 0: replace mode
+ * append == 1: single-line append mode
+ * append == 2: multiline append mode, append '\n' after each call
  */
 static void
 roff_setstr(struct roff *r, const char *name, const char *string,
-       int multiline)
+       int append)
 {
 
        roff_setstrn(&r->strtab, name, strlen(name), string,
-                       string ? strlen(string) : 0, multiline);
+                       string ? strlen(string) : 0, append);
 }
 
 static void
 roff_setstrn(struct roffkv **r, const char *name, size_t namesz,
-               const char *string, size_t stringsz, int multiline)
+               const char *string, size_t stringsz, int append)
 {
        struct roffkv   *n;
        char            *c;
@@ -1729,8 +1890,7 @@ roff_setstrn(struct roffkv **r, const char *name, size_t namesz,
                n->val.sz = 0;
                n->next = *r;
                *r = n;
-       } else if (0 == multiline) {
-               /* In multiline mode, append; else replace. */
+       } else if (0 == append) {
                free(n->val.p);
                n->val.p = NULL;
                n->val.sz = 0;
@@ -1743,7 +1903,7 @@ roff_setstrn(struct roffkv **r, const char *name, size_t namesz,
         * One additional byte for the '\n' in multiline mode,
         * and one for the terminating '\0'.
         */
-       newch = stringsz + (multiline ? 2u : 1u);
+       newch = stringsz + (1 < append ? 2u : 1u);
 
        if (NULL == n->val.p) {
                n->val.p = mandoc_malloc(newch);
@@ -1770,7 +1930,7 @@ roff_setstrn(struct roffkv **r, const char *name, size_t namesz,
        }
 
        /* Append terminating bytes. */
-       if (multiline)
+       if (1 < append)
                *c++ = '\n';
 
        *c = '\0';
@@ -1781,12 +1941,18 @@ static const char *
 roff_getstrn(const struct roff *r, const char *name, size_t len)
 {
        const struct roffkv *n;
+       int i;
 
        for (n = r->strtab; n; n = n->next)
                if (0 == strncmp(name, n->key.p, len) && 
                                '\0' == n->key.p[(int)len])
                        return(n->val.p);
 
+       for (i = 0; i < PREDEFS_MAX; i++)
+               if (0 == strncmp(name, predefs[i].name, len) &&
+                               '\0' == predefs[i].name[(int)len])
+                       return(predefs[i].str);
+
        return(NULL);
 }