Print this page
9718 update mandoc to 1.14.4

@@ -1,9 +1,9 @@
-/*      $Id: roff.c,v 1.324 2017/07/14 17:16:16 schwarze Exp $ */
+/*      $Id: roff.c,v 1.329 2018/08/01 15:40:17 schwarze Exp $ */
 /*
  * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2010-2015, 2017 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010-2015, 2017, 2018 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
  * copyright notice and this permission notice appear in all copies.
  *

@@ -44,10 +44,11 @@
 #define ROFFDEF_PRE     (1 << 2)  /* Predefined. */
 #define ROFFDEF_REN     (1 << 3)  /* Renamed standard macro. */
 #define ROFFDEF_STD     (1 << 4)  /* mdoc(7) or man(7) macro. */
 #define ROFFDEF_ANY     (ROFFDEF_USER | ROFFDEF_PRE | \
                          ROFFDEF_REN | ROFFDEF_STD)
+#define ROFFDEF_UNDEF   (1 << 5)  /* Completely undefined. */
 
 /* --- data types --------------------------------------------------------- */
 
 /*
  * An incredibly-simple string buffer.

@@ -70,10 +71,11 @@
  * A single number register as part of a singly-linked list.
  */
 struct  roffreg {
         struct roffstr   key;
         int              val;
+        int              step;
         struct roffreg  *next;
 };
 
 /*
  * Association of request and macro names with token IDs.

@@ -179,15 +181,15 @@
 static  void             roff_freereg(struct roffreg *);
 static  void             roff_freestr(struct roffkv *);
 static  size_t           roff_getname(struct roff *, char **, int, int);
 static  int              roff_getnum(const char *, int *, int *, int);
 static  int              roff_getop(const char *, int *, char *);
-static  int              roff_getregn(const struct roff *,
-                                const char *, size_t);
+static  int              roff_getregn(struct roff *,
+                                const char *, size_t, char);
 static  int              roff_getregro(const struct roff *,
                                 const char *name);
-static  const char      *roff_getstrn(const struct roff *,
+static  const char      *roff_getstrn(struct roff *,
                                 const char *, size_t, int *);
 static  int              roff_hasregn(const struct roff *,
                                 const char *, size_t);
 static  enum rofferr     roff_insec(ROFF_ARGS);
 static  enum rofferr     roff_it(ROFF_ARGS);

@@ -204,10 +206,12 @@
 static  enum rofferr     roff_renamed(ROFF_ARGS);
 static  enum rofferr     roff_res(struct roff *, struct buf *, int, int);
 static  enum rofferr     roff_rm(ROFF_ARGS);
 static  enum rofferr     roff_rn(ROFF_ARGS);
 static  enum rofferr     roff_rr(ROFF_ARGS);
+static  void             roff_setregn(struct roff *, const char *,
+                                size_t, int, char, int);
 static  void             roff_setstr(struct roff *,
                                 const char *, const char *, int);
 static  void             roff_setstrn(struct roffkv **, const char *,
                                 size_t, const char *, size_t, int);
 static  enum rofferr     roff_so(ROFF_ARGS);

@@ -756,11 +760,11 @@
 {
         struct roff     *r;
 
         r = mandoc_calloc(1, sizeof(struct roff));
         r->parse = parse;
-        r->reqtab = roffhash_alloc(0, ROFF_USERDEF);
+        r->reqtab = roffhash_alloc(0, ROFF_RENAMED);
         r->options = options;
         r->format = options & (MPARSE_MDOC | MPARSE_MAN);
         r->rstackpos = -1;
         r->escape = '\\';
         return r;

@@ -1116,12 +1120,14 @@
  */
 static enum rofferr
 roff_res(struct roff *r, struct buf *buf, int ln, int pos)
 {
         char             ubuf[24]; /* buffer to print the number */
+        struct roff_node *n;    /* used for header comments */
         const char      *start; /* start of the string to process */
         char            *stesc; /* start of an escape sequence ('\\') */
+        char            *ep;    /* end of comment string */
         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 */
         char            *nbuf;  /* new buffer to copy buf->buf to */
         size_t           maxl;  /* expected length of the escape name */

@@ -1132,10 +1138,11 @@
         int              npos;  /* position in numeric expression */
         int              arg_complete; /* argument not interrupted by eol */
         int              done;  /* no more input available */
         int              deftype; /* type of definition to paste */
         int              rcsid; /* kind of RCS id seen */
+        char             sign;  /* increment number register */
         char             term;  /* character terminating the escape */
 
         /* Search forward for comments. */
 
         done = 0;

@@ -1166,18 +1173,39 @@
                         r->man->meta.rcsids |= rcsid;
                 }
 
                 /* Handle trailing whitespace. */
 
-                cp = strchr(stesc--, '\0') - 1;
-                if (*cp == '\n') {
+                ep = strchr(stesc--, '\0') - 1;
+                if (*ep == '\n') {
                         done = 1;
-                        cp--;
+                        ep--;
                 }
-                if (*cp == ' ' || *cp == '\t')
+                if (*ep == ' ' || *ep == '\t')
                         mandoc_msg(MANDOCERR_SPACE_EOL, r->parse,
-                            ln, cp - buf->buf, NULL);
+                            ln, ep - buf->buf, NULL);
+
+                /*
+                 * Save comments preceding the title macro
+                 * in the syntax tree.
+                 */
+
+                if (r->format == 0) {
+                        while (*ep == ' ' || *ep == '\t')
+                                ep--;
+                        ep[1] = '\0';
+                        n = roff_node_alloc(r->man,
+                            ln, stesc + 1 - buf->buf,
+                            ROFFT_COMMENT, TOKEN_NONE);
+                        n->string = mandoc_strdup(stesc + 2);
+                        roff_node_append(r->man, n);
+                        n->flags |= NODE_VALID | NODE_ENDED;
+                        r->man->next = ROFF_NEXT_SIBLING;
+                }
+
+                /* Discard comments. */
+
                 while (stesc > start && stesc[-1] == ' ')
                         stesc--;
                 *stesc = '\0';
                 break;
         }

@@ -1242,10 +1270,13 @@
                 case 'B':
                 case 'w':
                         term = cp[1];
                         /* FALLTHROUGH */
                 case 'n':
+                        sign = cp[1];
+                        if (sign == '+' || sign == '-')
+                                cp++;
                         res = ubuf;
                         break;
                 default:
                         esc = mandoc_escape(&cp, &stnam, &inaml);
                         if (esc == ESCAPE_ERROR ||

@@ -1346,11 +1377,11 @@
                         ubuf[1] = '\0';
                         break;
                 case 'n':
                         if (arg_complete)
                                 (void)snprintf(ubuf, sizeof(ubuf), "%d",
-                                    roff_getregn(r, stnam, naml));
+                                    roff_getregn(r, stnam, naml, sign));
                         else
                                 ubuf[0] = '\0';
                         break;
                 case 'w':
                         /* use even incomplete args */

@@ -1637,10 +1668,15 @@
                 t = roffhash_find(r->reqtab, mac, maclen);
                 break;
         }
         if (t != TOKEN_NONE)
                 *pos = cp - buf;
+        else if (deftype == ROFFDEF_UNDEF) {
+                /* Using an undefined macro defines it to be empty. */
+                roff_setstrn(&r->strtab, mac, maclen, "", 0, 0);
+                roff_setstrn(&r->rentab, mac, maclen, NULL, 0, 0);
+        }
         return t;
 }
 
 /* --- handling of request blocks ----------------------------------------- */
 

@@ -2513,24 +2549,33 @@
 /* --- register management ------------------------------------------------ */
 
 void
 roff_setreg(struct roff *r, const char *name, int val, char sign)
 {
+        roff_setregn(r, name, strlen(name), val, sign, INT_MIN);
+}
+
+static void
+roff_setregn(struct roff *r, const char *name, size_t len,
+    int val, char sign, int step)
+{
         struct roffreg  *reg;
 
         /* Search for an existing register with the same name. */
         reg = r->regtab;
 
-        while (reg && strcmp(name, reg->key.p))
+        while (reg != NULL && (reg->key.sz != len ||
+            strncmp(reg->key.p, name, len) != 0))
                 reg = reg->next;
 
         if (NULL == reg) {
                 /* Create a new register. */
                 reg = mandoc_malloc(sizeof(struct roffreg));
-                reg->key.p = mandoc_strdup(name);
-                reg->key.sz = strlen(name);
+                reg->key.p = mandoc_strndup(name, len);
+                reg->key.sz = len;
                 reg->val = 0;
+                reg->step = 0;
                 reg->next = r->regtab;
                 r->regtab = reg;
         }
 
         if ('+' == sign)

@@ -2537,10 +2582,12 @@
                 reg->val += val;
         else if ('-' == sign)
                 reg->val -= val;
         else
                 reg->val = val;
+        if (step != INT_MIN)
+                reg->step = step;
 }
 
 /*
  * Handle some predefined read-only number registers.
  * For now, return -1 if the requested register is not predefined;

@@ -2570,30 +2617,17 @@
                 return -1;
         }
 }
 
 int
-roff_getreg(const struct roff *r, const char *name)
+roff_getreg(struct roff *r, const char *name)
 {
-        struct roffreg  *reg;
-        int              val;
-
-        if ('.' == name[0] && '\0' != name[1] && '\0' == name[2]) {
-                val = roff_getregro(r, name + 1);
-                if (-1 != val)
-                        return val;
-        }
-
-        for (reg = r->regtab; reg; reg = reg->next)
-                if (0 == strcmp(name, reg->key.p))
-                        return reg->val;
-
-        return 0;
+        return roff_getregn(r, name, strlen(name), '\0');
 }
 
 static int
-roff_getregn(const struct roff *r, const char *name, size_t len)
+roff_getregn(struct roff *r, const char *name, size_t len, char sign)
 {
         struct roffreg  *reg;
         int              val;
 
         if ('.' == name[0] && 2 == len) {

@@ -2600,15 +2634,28 @@
                 val = roff_getregro(r, name + 1);
                 if (-1 != val)
                         return val;
         }
 
-        for (reg = r->regtab; reg; reg = reg->next)
+        for (reg = r->regtab; reg; reg = reg->next) {
                 if (len == reg->key.sz &&
-                    0 == strncmp(name, reg->key.p, len))
+                    0 == strncmp(name, reg->key.p, len)) {
+                        switch (sign) {
+                        case '+':
+                                reg->val += reg->step;
+                                break;
+                        case '-':
+                                reg->val -= reg->step;
+                                break;
+                        default:
+                                break;
+                        }
                         return reg->val;
+                }
+        }
 
+        roff_setregn(r, name, len, 0, '\0', INT_MIN);
         return 0;
 }
 
 static int
 roff_hasregn(const struct roff *r, const char *name, size_t len)

@@ -2644,31 +2691,38 @@
 }
 
 static enum rofferr
 roff_nr(ROFF_ARGS)
 {
-        char            *key, *val;
+        char            *key, *val, *step;
         size_t           keysz;
-        int              iv;
+        int              iv, is, len;
         char             sign;
 
         key = val = buf->buf + pos;
         if (*key == '\0')
                 return ROFF_IGN;
 
         keysz = roff_getname(r, &val, ln, pos);
         if (key[keysz] == '\\')
                 return ROFF_IGN;
-        key[keysz] = '\0';
 
         sign = *val;
         if (sign == '+' || sign == '-')
                 val++;
 
-        if (roff_evalnum(r, ln, val, NULL, &iv, ROFFNUM_SCALE))
-                roff_setreg(r, key, iv, sign);
+        len = 0;
+        if (roff_evalnum(r, ln, val, &len, &iv, ROFFNUM_SCALE) == 0)
+                return ROFF_IGN;
 
+        step = val + len;
+        while (isspace((unsigned char)*step))
+                step++;
+        if (roff_evalnum(r, ln, step, NULL, &is, 0) == 0)
+                is = INT_MIN;
+
+        roff_setregn(r, key, keysz, iv, sign, is);
         return ROFF_IGN;
 }
 
 static enum rofferr
 roff_rr(ROFF_ARGS)

@@ -2789,10 +2843,11 @@
         if (tbl_end(r->tbl) == 0) {
                 r->tbl = NULL;
                 free(buf->buf);
                 buf->buf = mandoc_strdup(".sp");
                 buf->sz = 4;
+                *offs = 0;
                 return ROFF_REPARSE;
         }
         r->tbl = NULL;
         return ROFF_IGN;
 }

@@ -3308,10 +3363,11 @@
                 else if (++expand_count > EXPAND_LIMIT) {
                         mandoc_msg(MANDOCERR_ROFFLOOP, r->parse,
                             ln, (int)(cp - n1), NULL);
                         free(buf->buf);
                         buf->buf = n1;
+                        *offs = 0;
                         return ROFF_IGN;
                 }
 
                 /*
                  * Determine the size of the expanded argument,

@@ -3402,10 +3458,11 @@
 
         buf->sz = mandoc_asprintf(&nbuf, ".%s%s%s", r->current_string,
             buf->buf[pos] == '\0' ? "" : " ", buf->buf + pos) + 1;
         free(buf->buf);
         buf->buf = nbuf;
+        *offs = 0;
         return ROFF_CONT;
 }
 
 static size_t
 roff_getname(struct roff *r, char **cpp, int ln, int pos)

@@ -3535,66 +3592,99 @@
         *c = '\0';
         n->val.sz = (int)(c - n->val.p);
 }
 
 static const char *
-roff_getstrn(const struct roff *r, const char *name, size_t len,
+roff_getstrn(struct roff *r, const char *name, size_t len,
     int *deftype)
 {
         const struct roffkv     *n;
-        int                      i;
+        int                      found, i;
         enum roff_tok            tok;
 
-        if (*deftype & ROFFDEF_USER) {
+        found = 0;
                 for (n = r->strtab; n != NULL; n = n->next) {
-                        if (strncmp(name, n->key.p, len) == 0 &&
-                            n->key.p[len] == '\0' &&
-                            n->val.p != NULL) {
+                if (strncmp(name, n->key.p, len) != 0 ||
+                    n->key.p[len] != '\0' || n->val.p == NULL)
+                        continue;
+                if (*deftype & ROFFDEF_USER) {
                                 *deftype = ROFFDEF_USER;
                                 return n->val.p;
+                } else {
+                        found = 1;
+                        break;
                         }
                 }
-        }
-        if (*deftype & ROFFDEF_PRE) {
-                for (i = 0; i < PREDEFS_MAX; i++) {
-                        if (strncmp(name, predefs[i].name, len) == 0 &&
-                            predefs[i].name[len] == '\0') {
-                                *deftype = ROFFDEF_PRE;
-                                return predefs[i].str;
-                        }
-                }
-        }
-        if (*deftype & ROFFDEF_REN) {
                 for (n = r->rentab; n != NULL; n = n->next) {
-                        if (strncmp(name, n->key.p, len) == 0 &&
-                            n->key.p[len] == '\0' &&
-                            n->val.p != NULL) {
+                if (strncmp(name, n->key.p, len) != 0 ||
+                    n->key.p[len] != '\0' || n->val.p == NULL)
+                        continue;
+                if (*deftype & ROFFDEF_REN) {
                                 *deftype = ROFFDEF_REN;
                                 return n->val.p;
+                } else {
+                        found = 1;
+                        break;
                         }
                 }
+        for (i = 0; i < PREDEFS_MAX; i++) {
+                if (strncmp(name, predefs[i].name, len) != 0 ||
+                    predefs[i].name[len] != '\0')
+                        continue;
+                if (*deftype & ROFFDEF_PRE) {
+                        *deftype = ROFFDEF_PRE;
+                        return predefs[i].str;
+                } else {
+                        found = 1;
+                        break;
         }
-        if (*deftype & ROFFDEF_STD) {
+        }
                 if (r->man->macroset != MACROSET_MAN) {
                         for (tok = MDOC_Dd; tok < MDOC_MAX; tok++) {
-                                if (strncmp(name, roff_name[tok], len) == 0 &&
-                                    roff_name[tok][len] == '\0') {
+                        if (strncmp(name, roff_name[tok], len) != 0 ||
+                            roff_name[tok][len] != '\0')
+                                continue;
+                        if (*deftype & ROFFDEF_STD) {
                                         *deftype = ROFFDEF_STD;
                                         return NULL;
+                        } else {
+                                found = 1;
+                                break;
                                 }
                         }
                 }
                 if (r->man->macroset != MACROSET_MDOC) {
                         for (tok = MAN_TH; tok < MAN_MAX; tok++) {
-                                if (strncmp(name, roff_name[tok], len) == 0 &&
-                                    roff_name[tok][len] == '\0') {
+                        if (strncmp(name, roff_name[tok], len) != 0 ||
+                            roff_name[tok][len] != '\0')
+                                continue;
+                        if (*deftype & ROFFDEF_STD) {
                                         *deftype = ROFFDEF_STD;
                                         return NULL;
+                        } else {
+                                found = 1;
+                                break;
                                 }
                         }
                 }
+
+        if (found == 0 && *deftype != ROFFDEF_ANY) {
+                if (*deftype & ROFFDEF_REN) {
+                        /*
+                         * This might still be a request,
+                         * so do not treat it as undefined yet.
+                         */
+                        *deftype = ROFFDEF_UNDEF;
+                        return NULL;
         }
+
+                /* Using an undefined string defines it to be empty. */
+
+                roff_setstrn(&r->strtab, name, len, "", 0, 0);
+                roff_setstrn(&r->rentab, name, len, NULL, 0, 0);
+        }
+
         *deftype = 0;
         return NULL;
 }
 
 static void