Print this page
5051 import mdocml-1.12.3
Reviewed by: Yuri Pankov <yuri.pankov@nexenta.com>
Approved by: TBD

Split Close
Expand all
Collapse all
          --- old/usr/src/cmd/mandoc/roff.c
          +++ new/usr/src/cmd/mandoc/roff.c
   1      -/*      $Id: roff.c,v 1.172 2011/10/24 21:41:45 schwarze Exp $ */
        1 +/*      $Id: roff.c,v 1.189 2013/12/30 18:44:06 schwarze Exp $ */
   2    2  /*
   3      - * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
   4      - * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
        3 + * Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
        4 + * Copyright (c) 2010, 2011, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
   5    5   *
   6    6   * Permission to use, copy, modify, and distribute this software for any
   7    7   * purpose with or without fee is hereby granted, provided that the above
   8    8   * copyright notice and this permission notice appear in all copies.
   9    9   *
  10   10   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
  11   11   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  12   12   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
  13   13   * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  14   14   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  15   15   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  16   16   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  17   17   */
  18   18  #ifdef HAVE_CONFIG_H
  19   19  #include "config.h"
  20   20  #endif
  21   21  
  22   22  #include <assert.h>
  23   23  #include <ctype.h>
       24 +#include <stdio.h>
  24   25  #include <stdlib.h>
  25   26  #include <string.h>
  26   27  
  27   28  #include "mandoc.h"
  28   29  #include "libroff.h"
  29   30  #include "libmandoc.h"
  30   31  
  31   32  /* Maximum number of nested if-else conditionals. */
  32   33  #define RSTACK_MAX      128
  33   34  
  34   35  /* Maximum number of string expansions per line, to break infinite loops. */
  35   36  #define EXPAND_LIMIT    1000
  36   37  
  37   38  enum    rofft {
  38   39          ROFF_ad,
  39   40          ROFF_am,
  40   41          ROFF_ami,
  41   42          ROFF_am1,
       43 +        ROFF_cc,
  42   44          ROFF_de,
  43   45          ROFF_dei,
  44   46          ROFF_de1,
  45   47          ROFF_ds,
  46   48          ROFF_el,
       49 +        ROFF_fam,
       50 +        ROFF_hw,
  47   51          ROFF_hy,
  48   52          ROFF_ie,
  49   53          ROFF_if,
  50   54          ROFF_ig,
  51   55          ROFF_it,
  52   56          ROFF_ne,
  53   57          ROFF_nh,
  54   58          ROFF_nr,
  55   59          ROFF_ns,
  56   60          ROFF_ps,
  57   61          ROFF_rm,
  58   62          ROFF_so,
  59   63          ROFF_ta,
  60   64          ROFF_tr,
       65 +        ROFF_Dd,
       66 +        ROFF_TH,
  61   67          ROFF_TS,
  62   68          ROFF_TE,
  63   69          ROFF_T_,
  64   70          ROFF_EQ,
  65   71          ROFF_EN,
  66   72          ROFF_cblock,
  67   73          ROFF_ccond,
  68   74          ROFF_USERDEF,
  69   75          ROFF_MAX
  70   76  };
  71   77  
  72   78  enum    roffrule {
  73      -        ROFFRULE_ALLOW,
  74      -        ROFFRULE_DENY
       79 +        ROFFRULE_DENY,
       80 +        ROFFRULE_ALLOW
  75   81  };
  76   82  
  77   83  /*
  78      - * A single register entity.  If "set" is zero, the value of the
  79      - * register should be the default one, which is per-register.
  80      - * Registers are assumed to be unsigned ints for now.
  81      - */
  82      -struct  reg {
  83      -        int              set; /* whether set or not */
  84      -        unsigned int     u; /* unsigned integer */
  85      -};
  86      -
  87      -/*
  88   84   * An incredibly-simple string buffer.
  89   85   */
  90   86  struct  roffstr {
  91   87          char            *p; /* nil-terminated buffer */
  92   88          size_t           sz; /* saved strlen(p) */
  93   89  };
  94   90  
  95   91  /*
  96   92   * A key-value roffstr pair as part of a singly-linked list.
  97   93   */
  98   94  struct  roffkv {
  99   95          struct roffstr   key;
 100   96          struct roffstr   val;
 101   97          struct roffkv   *next; /* next in list */
 102   98  };
 103   99  
      100 +/*
      101 + * A single number register as part of a singly-linked list.
      102 + */
      103 +struct  roffreg {
      104 +        struct roffstr   key;
      105 +        int              val;
      106 +        struct roffreg  *next;
      107 +};
      108 +
 104  109  struct  roff {
      110 +        enum mparset     parsetype; /* requested parse type */
 105  111          struct mparse   *parse; /* parse point */
 106  112          struct roffnode *last; /* leaf of stack */
 107  113          enum roffrule    rstack[RSTACK_MAX]; /* stack of !`ie' rules */
      114 +        char             control; /* control character */
 108  115          int              rstackpos; /* position in rstack */
 109      -        struct reg       regs[REG__MAX];
      116 +        struct roffreg  *regtab; /* number registers */
 110  117          struct roffkv   *strtab; /* user-defined strings & macros */
 111  118          struct roffkv   *xmbtab; /* multi-byte trans table (`tr') */
 112  119          struct roffstr  *xtab; /* single-byte trans table (`tr') */
 113  120          const char      *current_string; /* value of last called user macro */
 114  121          struct tbl_node *first_tbl; /* first table parsed */
 115  122          struct tbl_node *last_tbl; /* last table parsed */
 116  123          struct tbl_node *tbl; /* current table being parsed */
 117  124          struct eqn_node *last_eqn; /* last equation parsed */
 118  125          struct eqn_node *first_eqn; /* first equation parsed */
 119  126          struct eqn_node *eqn; /* current equation being parsed */
↓ open down ↓ 42 lines elided ↑ open up ↑
 162  169  static  enum rofft       roffhash_find(const char *, size_t);
 163  170  static  void             roffhash_init(void);
 164  171  static  void             roffnode_cleanscope(struct roff *);
 165  172  static  void             roffnode_pop(struct roff *);
 166  173  static  void             roffnode_push(struct roff *, enum rofft,
 167  174                                  const char *, int, int);
 168  175  static  enum rofferr     roff_block(ROFF_ARGS);
 169  176  static  enum rofferr     roff_block_text(ROFF_ARGS);
 170  177  static  enum rofferr     roff_block_sub(ROFF_ARGS);
 171  178  static  enum rofferr     roff_cblock(ROFF_ARGS);
      179 +static  enum rofferr     roff_cc(ROFF_ARGS);
 172  180  static  enum rofferr     roff_ccond(ROFF_ARGS);
 173  181  static  enum rofferr     roff_cond(ROFF_ARGS);
 174  182  static  enum rofferr     roff_cond_text(ROFF_ARGS);
 175  183  static  enum rofferr     roff_cond_sub(ROFF_ARGS);
 176  184  static  enum rofferr     roff_ds(ROFF_ARGS);
 177  185  static  enum roffrule    roff_evalcond(const char *, int *);
 178  186  static  void             roff_free1(struct roff *);
      187 +static  void             roff_freereg(struct roffreg *);
 179  188  static  void             roff_freestr(struct roffkv *);
 180  189  static  char            *roff_getname(struct roff *, char **, int, int);
      190 +static  int              roff_getnum(const char *, int *, int *);
      191 +static  int              roff_getop(const char *, int *, char *);
      192 +static  int              roff_getregn(const struct roff *,
      193 +                                const char *, size_t);
 181  194  static  const char      *roff_getstrn(const struct roff *, 
 182  195                                  const char *, size_t);
      196 +static  enum rofferr     roff_it(ROFF_ARGS);
 183  197  static  enum rofferr     roff_line_ignore(ROFF_ARGS);
 184  198  static  enum rofferr     roff_nr(ROFF_ARGS);
 185  199  static  void             roff_openeqn(struct roff *, const char *,
 186  200                                  int, int, const char *);
 187  201  static  enum rofft       roff_parse(struct roff *, const char *, int *);
 188      -static  enum rofferr     roff_parsetext(char *);
      202 +static  enum rofferr     roff_parsetext(char **, size_t *, int, int *);
 189  203  static  enum rofferr     roff_res(struct roff *, 
 190  204                                  char **, size_t *, int, int);
 191  205  static  enum rofferr     roff_rm(ROFF_ARGS);
 192  206  static  void             roff_setstr(struct roff *,
 193  207                                  const char *, const char *, int);
 194  208  static  void             roff_setstrn(struct roffkv **, const char *, 
 195  209                                  size_t, const char *, size_t, int);
 196  210  static  enum rofferr     roff_so(ROFF_ARGS);
 197  211  static  enum rofferr     roff_tr(ROFF_ARGS);
      212 +static  enum rofferr     roff_Dd(ROFF_ARGS);
      213 +static  enum rofferr     roff_TH(ROFF_ARGS);
 198  214  static  enum rofferr     roff_TE(ROFF_ARGS);
 199  215  static  enum rofferr     roff_TS(ROFF_ARGS);
 200  216  static  enum rofferr     roff_EQ(ROFF_ARGS);
 201  217  static  enum rofferr     roff_EN(ROFF_ARGS);
 202  218  static  enum rofferr     roff_T_(ROFF_ARGS);
 203  219  static  enum rofferr     roff_userdef(ROFF_ARGS);
 204  220  
 205  221  /* See roffhash_find() */
 206  222  
 207  223  #define ASCII_HI         126
 208  224  #define ASCII_LO         33
 209  225  #define HASHWIDTH       (ASCII_HI - ASCII_LO + 1)
 210  226  
 211  227  static  struct roffmac  *hash[HASHWIDTH];
 212  228  
 213  229  static  struct roffmac   roffs[ROFF_MAX] = {
 214  230          { "ad", roff_line_ignore, NULL, NULL, 0, NULL },
 215  231          { "am", roff_block, roff_block_text, roff_block_sub, 0, NULL },
 216  232          { "ami", roff_block, roff_block_text, roff_block_sub, 0, NULL },
 217  233          { "am1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
      234 +        { "cc", roff_cc, NULL, NULL, 0, NULL },
 218  235          { "de", roff_block, roff_block_text, roff_block_sub, 0, NULL },
 219  236          { "dei", roff_block, roff_block_text, roff_block_sub, 0, NULL },
 220  237          { "de1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
 221  238          { "ds", roff_ds, NULL, NULL, 0, NULL },
 222  239          { "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
      240 +        { "fam", roff_line_ignore, NULL, NULL, 0, NULL },
      241 +        { "hw", roff_line_ignore, NULL, NULL, 0, NULL },
 223  242          { "hy", roff_line_ignore, NULL, NULL, 0, NULL },
 224  243          { "ie", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
 225  244          { "if", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
 226  245          { "ig", roff_block, roff_block_text, roff_block_sub, 0, NULL },
 227      -        { "it", roff_line_ignore, NULL, NULL, 0, NULL },
      246 +        { "it", roff_it, NULL, NULL, 0, NULL },
 228  247          { "ne", roff_line_ignore, NULL, NULL, 0, NULL },
 229  248          { "nh", roff_line_ignore, NULL, NULL, 0, NULL },
 230  249          { "nr", roff_nr, NULL, NULL, 0, NULL },
 231  250          { "ns", roff_line_ignore, NULL, NULL, 0, NULL },
 232  251          { "ps", roff_line_ignore, NULL, NULL, 0, NULL },
 233  252          { "rm", roff_rm, NULL, NULL, 0, NULL },
 234  253          { "so", roff_so, NULL, NULL, 0, NULL },
 235  254          { "ta", roff_line_ignore, NULL, NULL, 0, NULL },
 236  255          { "tr", roff_tr, NULL, NULL, 0, NULL },
      256 +        { "Dd", roff_Dd, NULL, NULL, 0, NULL },
      257 +        { "TH", roff_TH, NULL, NULL, 0, NULL },
 237  258          { "TS", roff_TS, NULL, NULL, 0, NULL },
 238  259          { "TE", roff_TE, NULL, NULL, 0, NULL },
 239  260          { "T&", roff_T_, NULL, NULL, 0, NULL },
 240  261          { "EQ", roff_EQ, NULL, NULL, 0, NULL },
 241  262          { "EN", roff_EN, NULL, NULL, 0, NULL },
 242  263          { ".", roff_cblock, NULL, NULL, 0, NULL },
 243  264          { "\\}", roff_ccond, NULL, NULL, 0, NULL },
 244  265          { NULL, roff_userdef, NULL, NULL, 0, NULL },
 245  266  };
 246  267  
      268 +const   char *const __mdoc_reserved[] = {
      269 +        "Ac", "Ad", "An", "Ao", "Ap", "Aq", "Ar", "At",
      270 +        "Bc", "Bd", "Bf", "Bk", "Bl", "Bo", "Bq",
      271 +        "Brc", "Bro", "Brq", "Bsx", "Bt", "Bx",
      272 +        "Cd", "Cm", "Db", "Dc", "Dd", "Dl", "Do", "Dq",
      273 +        "Ds", "Dt", "Dv", "Dx", "D1",
      274 +        "Ec", "Ed", "Ef", "Ek", "El", "Em", "em",
      275 +        "En", "Eo", "Eq", "Er", "Es", "Ev", "Ex",
      276 +        "Fa", "Fc", "Fd", "Fl", "Fn", "Fo", "Fr", "Ft", "Fx",
      277 +        "Hf", "Ic", "In", "It", "Lb", "Li", "Lk", "Lp", "LP",
      278 +        "Me", "Ms", "Mt", "Nd", "Nm", "No", "Ns", "Nx",
      279 +        "Oc", "Oo", "Op", "Os", "Ot", "Ox",
      280 +        "Pa", "Pc", "Pf", "Po", "Pp", "PP", "pp", "Pq",
      281 +        "Qc", "Ql", "Qo", "Qq", "Or", "Rd", "Re", "Rs", "Rv",
      282 +        "Sc", "Sf", "Sh", "SH", "Sm", "So", "Sq",
      283 +        "Ss", "St", "Sx", "Sy",
      284 +        "Ta", "Tn", "Ud", "Ux", "Va", "Vt", "Xc", "Xo", "Xr",
      285 +        "%A", "%B", "%D", "%I", "%J", "%N", "%O",
      286 +        "%P", "%Q", "%R", "%T", "%U", "%V",
      287 +        NULL
      288 +};
      289 +
      290 +const   char *const __man_reserved[] = {
      291 +        "AT", "B", "BI", "BR", "BT", "DE", "DS", "DT",
      292 +        "EE", "EN", "EQ", "EX", "HF", "HP", "I", "IB", "IP", "IR",
      293 +        "LP", "ME", "MT", "OP", "P", "PD", "PP", "PT",
      294 +        "R", "RB", "RE", "RI", "RS", "SB", "SH", "SM", "SS", "SY",
      295 +        "TE", "TH", "TP", "TQ", "TS", "T&", "UC", "UE", "UR", "YS",
      296 +        NULL
      297 +};
      298 +
 247  299  /* Array of injected predefined strings. */
 248  300  #define PREDEFS_MAX      38
 249  301  static  const struct predef predefs[PREDEFS_MAX] = {
 250  302  #include "predefs.in"
 251  303  };
 252  304  
 253  305  /* See roffhash_find() */
 254  306  #define ROFF_HASH(p)    (p[0] - ASCII_LO)
 255  307  
      308 +static  int      roffit_lines;  /* number of lines to delay */
      309 +static  char    *roffit_macro;  /* nil-terminated macro line */
      310 +
 256  311  static void
 257  312  roffhash_init(void)
 258  313  {
 259  314          struct roffmac   *n;
 260  315          int               buc, i;
 261  316  
 262  317          for (i = 0; i < (int)ROFF_USERDEF; i++) {
 263  318                  assert(roffs[i].name[0] >= ASCII_LO);
 264  319                  assert(roffs[i].name[0] <= ASCII_HI);
 265  320  
↓ open down ↓ 78 lines elided ↑ open up ↑
 344  399          p->col = col;
 345  400          p->rule = p->parent ? p->parent->rule : ROFFRULE_DENY;
 346  401  
 347  402          r->last = p;
 348  403  }
 349  404  
 350  405  
 351  406  static void
 352  407  roff_free1(struct roff *r)
 353  408  {
 354      -        struct tbl_node *t;
      409 +        struct tbl_node *tbl;
 355  410          struct eqn_node *e;
 356  411          int              i;
 357  412  
 358      -        while (NULL != (t = r->first_tbl)) {
 359      -                r->first_tbl = t->next;
 360      -                tbl_free(t);
      413 +        while (NULL != (tbl = r->first_tbl)) {
      414 +                r->first_tbl = tbl->next;
      415 +                tbl_free(tbl);
 361  416          }
 362  417  
 363  418          r->first_tbl = r->last_tbl = r->tbl = NULL;
 364  419  
 365  420          while (NULL != (e = r->first_eqn)) {
 366  421                  r->first_eqn = e->next;
 367  422                  eqn_free(e);
 368  423          }
 369  424  
 370  425          r->first_eqn = r->last_eqn = r->eqn = NULL;
 371  426  
 372  427          while (r->last)
 373  428                  roffnode_pop(r);
 374  429  
 375  430          roff_freestr(r->strtab);
 376  431          roff_freestr(r->xmbtab);
 377  432  
 378  433          r->strtab = r->xmbtab = NULL;
 379  434  
      435 +        roff_freereg(r->regtab);
      436 +
      437 +        r->regtab = NULL;
      438 +
 380  439          if (r->xtab)
 381  440                  for (i = 0; i < 128; i++)
 382  441                          free(r->xtab[i].p);
 383  442  
 384  443          free(r->xtab);
 385  444          r->xtab = NULL;
 386  445  }
 387  446  
 388  447  void
 389  448  roff_reset(struct roff *r)
 390  449  {
 391  450          int              i;
 392  451  
 393  452          roff_free1(r);
 394  453  
 395      -        memset(&r->regs, 0, sizeof(struct reg) * REG__MAX);
      454 +        r->control = 0;
 396  455  
 397  456          for (i = 0; i < PREDEFS_MAX; i++) 
 398  457                  roff_setstr(r, predefs[i].name, predefs[i].str, 0);
 399  458  }
 400  459  
 401  460  
 402  461  void
 403  462  roff_free(struct roff *r)
 404  463  {
 405  464  
 406  465          roff_free1(r);
 407  466          free(r);
 408  467  }
 409  468  
 410  469  
 411  470  struct roff *
 412      -roff_alloc(struct mparse *parse)
      471 +roff_alloc(enum mparset type, struct mparse *parse)
 413  472  {
 414  473          struct roff     *r;
 415  474          int              i;
 416  475  
 417  476          r = mandoc_calloc(1, sizeof(struct roff));
      477 +        r->parsetype = type;
 418  478          r->parse = parse;
 419  479          r->rstackpos = -1;
 420  480          
 421  481          roffhash_init();
 422  482  
 423  483          for (i = 0; i < PREDEFS_MAX; i++) 
 424  484                  roff_setstr(r, predefs[i].name, predefs[i].str, 0);
 425  485  
 426  486          return(r);
 427  487  }
 428  488  
 429  489  /*
 430      - * Pre-filter each and every line for reserved words (one beginning with
 431      - * `\*', e.g., `\*(ab').  These must be handled before the actual line
 432      - * is processed. 
 433      - * This also checks the syntax of regular escapes.
      490 + * In the current line, expand user-defined strings ("\*")
      491 + * and references to number registers ("\n").
      492 + * Also check the syntax of other escape sequences.
 434  493   */
 435  494  static enum rofferr
 436  495  roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos)
 437  496  {
 438      -        enum mandoc_esc  esc;
      497 +        char             ubuf[12]; /* buffer to print the number */
 439  498          const char      *stesc; /* start of an escape sequence ('\\') */
 440  499          const char      *stnam; /* start of the name, after "[(*" */
 441  500          const char      *cp;    /* end of the name, e.g. before ']' */
 442  501          const char      *res;   /* the string to be substituted */
 443      -        int              i, maxl, expand_count;
 444      -        size_t           nsz;
 445      -        char            *n;
      502 +        char            *nbuf;  /* new buffer to copy bufp to */
      503 +        size_t           nsz;   /* size of the new buffer */
      504 +        size_t           maxl;  /* expected length of the escape name */
      505 +        size_t           naml;  /* actual length of the escape name */
      506 +        int              expand_count;  /* to avoid infinite loops */
 446  507  
 447  508          expand_count = 0;
 448  509  
 449  510  again:
 450  511          cp = *bufp + pos;
 451  512          while (NULL != (cp = strchr(cp, '\\'))) {
 452  513                  stesc = cp++;
 453  514  
 454  515                  /*
 455      -                 * The second character must be an asterisk.
      516 +                 * The second character must be an asterisk or an n.
 456  517                   * If it isn't, skip it anyway:  It is escaped,
 457  518                   * so it can't start another escape sequence.
 458  519                   */
 459  520  
 460  521                  if ('\0' == *cp)
 461  522                          return(ROFF_CONT);
 462  523  
 463      -                if ('*' != *cp) {
 464      -                        res = cp;
 465      -                        esc = mandoc_escape(&cp, NULL, NULL);
 466      -                        if (ESCAPE_ERROR != esc)
      524 +                switch (*cp) {
      525 +                case ('*'):
      526 +                        res = NULL;
      527 +                        break;
      528 +                case ('n'):
      529 +                        res = ubuf;
      530 +                        break;
      531 +                default:
      532 +                        if (ESCAPE_ERROR != mandoc_escape(&cp, NULL, NULL))
 467  533                                  continue;
 468      -                        cp = res;
 469  534                          mandoc_msg
 470  535                                  (MANDOCERR_BADESCAPE, r->parse, 
 471  536                                   ln, (int)(stesc - *bufp), NULL);
 472  537                          return(ROFF_CONT);
 473  538                  }
 474  539  
 475  540                  cp++;
 476  541  
 477  542                  /*
 478  543                   * The third character decides the length
 479      -                 * of the name of the string.
      544 +                 * of the name of the string or register.
 480  545                   * Save a pointer to the name.
 481  546                   */
 482  547  
 483  548                  switch (*cp) {
 484  549                  case ('\0'):
 485  550                          return(ROFF_CONT);
 486  551                  case ('('):
 487  552                          cp++;
 488  553                          maxl = 2;
 489  554                          break;
↓ open down ↓ 2 lines elided ↑ open up ↑
 492  557                          maxl = 0;
 493  558                          break;
 494  559                  default:
 495  560                          maxl = 1;
 496  561                          break;
 497  562                  }
 498  563                  stnam = cp;
 499  564  
 500  565                  /* Advance to the end of the name. */
 501  566  
 502      -                for (i = 0; 0 == maxl || i < maxl; i++, cp++) {
      567 +                for (naml = 0; 0 == maxl || naml < maxl; naml++, cp++) {
 503  568                          if ('\0' == *cp) {
 504  569                                  mandoc_msg
 505  570                                          (MANDOCERR_BADESCAPE, 
 506  571                                           r->parse, ln, 
 507  572                                           (int)(stesc - *bufp), NULL);
 508  573                                  return(ROFF_CONT);
 509  574                          }
 510  575                          if (0 == maxl && ']' == *cp)
 511  576                                  break;
 512  577                  }
 513  578  
 514  579                  /*
 515  580                   * Retrieve the replacement string; if it is
 516  581                   * undefined, resume searching for escapes.
 517  582                   */
 518  583  
 519      -                res = roff_getstrn(r, stnam, (size_t)i);
      584 +                if (NULL == res)
      585 +                        res = roff_getstrn(r, stnam, naml);
      586 +                else
      587 +                        snprintf(ubuf, sizeof(ubuf), "%d",
      588 +                            roff_getregn(r, stnam, naml));
 520  589  
 521  590                  if (NULL == res) {
 522  591                          mandoc_msg
 523  592                                  (MANDOCERR_BADESCAPE, r->parse, 
 524  593                                   ln, (int)(stesc - *bufp), NULL);
 525  594                          res = "";
 526  595                  }
 527  596  
 528  597                  /* Replace the escape sequence by the string. */
 529  598  
 530  599                  pos = stesc - *bufp;
 531  600  
 532  601                  nsz = *szp + strlen(res) + 1;
 533      -                n = mandoc_malloc(nsz);
      602 +                nbuf = mandoc_malloc(nsz);
 534  603  
 535      -                strlcpy(n, *bufp, (size_t)(stesc - *bufp + 1));
 536      -                strlcat(n, res, nsz);
 537      -                strlcat(n, cp + (maxl ? 0 : 1), nsz);
      604 +                strlcpy(nbuf, *bufp, (size_t)(stesc - *bufp + 1));
      605 +                strlcat(nbuf, res, nsz);
      606 +                strlcat(nbuf, cp + (maxl ? 0 : 1), nsz);
 538  607  
 539  608                  free(*bufp);
 540  609  
 541      -                *bufp = n;
      610 +                *bufp = nbuf;
 542  611                  *szp = nsz;
 543  612  
 544  613                  if (EXPAND_LIMIT >= ++expand_count)
 545  614                          goto again;
 546  615  
 547  616                  /* Just leave the string unexpanded. */
 548  617                  mandoc_msg(MANDOCERR_ROFFLOOP, r->parse, ln, pos, NULL);
 549  618                  return(ROFF_IGN);
 550  619          }
 551  620          return(ROFF_CONT);
 552  621  }
 553  622  
 554  623  /*
 555      - * Process text streams: convert all breakable hyphens into ASCII_HYPH.
      624 + * Process text streams:
      625 + * Convert all breakable hyphens into ASCII_HYPH.
      626 + * Decrement and spring input line trap.
 556  627   */
 557  628  static enum rofferr
 558      -roff_parsetext(char *p)
      629 +roff_parsetext(char **bufp, size_t *szp, int pos, int *offs)
 559  630  {
 560  631          size_t           sz;
 561  632          const char      *start;
      633 +        char            *p;
      634 +        int              isz;
 562  635          enum mandoc_esc  esc;
 563  636  
 564      -        start = p;
      637 +        start = p = *bufp + pos;
 565  638  
 566  639          while ('\0' != *p) {
 567  640                  sz = strcspn(p, "-\\");
 568  641                  p += sz;
 569  642  
 570  643                  if ('\0' == *p)
 571  644                          break;
 572  645  
 573  646                  if ('\\' == *p) {
 574  647                          /* Skip over escapes. */
 575  648                          p++;
 576      -                        esc = mandoc_escape
 577      -                                ((const char **)&p, NULL, NULL);
      649 +                        esc = mandoc_escape((const char **)&p, NULL, NULL);
 578  650                          if (ESCAPE_ERROR == esc)
 579  651                                  break;
 580  652                          continue;
 581  653                  } else if (p == start) {
 582  654                          p++;
 583  655                          continue;
 584  656                  }
 585  657  
 586  658                  if (isalpha((unsigned char)p[-1]) &&
 587  659                      isalpha((unsigned char)p[1]))
 588  660                          *p = ASCII_HYPH;
 589  661                  p++;
 590  662          }
 591  663  
      664 +        /* Spring the input line trap. */
      665 +        if (1 == roffit_lines) {
      666 +                isz = asprintf(&p, "%s\n.%s", *bufp, roffit_macro);
      667 +                if (-1 == isz) {
      668 +                        perror(NULL);
      669 +                        exit((int)MANDOCLEVEL_SYSERR);
      670 +                }
      671 +                free(*bufp);
      672 +                *bufp = p;
      673 +                *szp = isz + 1;
      674 +                *offs = 0;
      675 +                free(roffit_macro);
      676 +                roffit_lines = 0;
      677 +                return(ROFF_REPARSE);
      678 +        } else if (1 < roffit_lines)
      679 +                --roffit_lines;
 592  680          return(ROFF_CONT);
 593  681  }
 594  682  
 595  683  enum rofferr
 596  684  roff_parseln(struct roff *r, int ln, char **bufp, 
 597  685                  size_t *szp, int pos, int *offs)
 598  686  {
 599  687          enum rofft       t;
 600  688          enum rofferr     e;
 601  689          int              ppos, ctl;
↓ open down ↓ 2 lines elided ↑ open up ↑
 604  692           * Run the reserved-word filter only if we have some reserved
 605  693           * words to fill in.
 606  694           */
 607  695  
 608  696          e = roff_res(r, bufp, szp, ln, pos);
 609  697          if (ROFF_IGN == e)
 610  698                  return(e);
 611  699          assert(ROFF_CONT == e);
 612  700  
 613  701          ppos = pos;
 614      -        ctl = mandoc_getcontrol(*bufp, &pos);
      702 +        ctl = roff_getcontrol(r, *bufp, &pos);
 615  703  
 616  704          /*
 617  705           * First, if a scope is open and we're not a macro, pass the
 618  706           * text through the macro's filter.  If a scope isn't open and
 619  707           * we're not a macro, just let it through.
 620  708           * Finally, if there's an equation scope open, divert it into it
 621  709           * no matter our state.
 622  710           */
 623  711  
 624  712          if (r->last && ! ctl) {
 625  713                  t = r->last->tok;
 626  714                  assert(roffs[t].text);
 627  715                  e = (*roffs[t].text)
 628  716                          (r, t, bufp, szp, ln, pos, pos, offs);
 629  717                  assert(ROFF_IGN == e || ROFF_CONT == e);
 630  718                  if (ROFF_CONT != e)
 631  719                          return(e);
 632      -                if (r->eqn)
 633      -                        return(eqn_read(&r->eqn, ln, *bufp, pos, offs));
      720 +        }
      721 +        if (r->eqn)
      722 +                return(eqn_read(&r->eqn, ln, *bufp, ppos, offs));
      723 +        if ( ! ctl) {
 634  724                  if (r->tbl)
 635  725                          return(tbl_read(r->tbl, ln, *bufp, pos));
 636      -                return(roff_parsetext(*bufp + pos));
 637      -        } else if ( ! ctl) {
 638      -                if (r->eqn)
 639      -                        return(eqn_read(&r->eqn, ln, *bufp, pos, offs));
 640      -                if (r->tbl)
 641      -                        return(tbl_read(r->tbl, ln, *bufp, pos));
 642      -                return(roff_parsetext(*bufp + pos));
 643      -        } else if (r->eqn)
 644      -                return(eqn_read(&r->eqn, ln, *bufp, ppos, offs));
      726 +                return(roff_parsetext(bufp, szp, pos, offs));
      727 +        }
 645  728  
 646  729          /*
 647  730           * If a scope is open, go to the child handler for that macro,
 648  731           * as it may want to preprocess before doing anything with it.
 649  732           * Don't do so if an equation is open.
 650  733           */
 651  734  
 652  735          if (r->last) {
 653  736                  t = r->last->tok;
 654  737                  assert(roffs[t].sub);
↓ open down ↓ 116 lines elided ↑ open up ↑
 771  854          return(ROFF_IGN);
 772  855  
 773  856  }
 774  857  
 775  858  
 776  859  static void
 777  860  roffnode_cleanscope(struct roff *r)
 778  861  {
 779  862  
 780  863          while (r->last) {
 781      -                if (--r->last->endspan < 0)
      864 +                if (--r->last->endspan != 0)
 782  865                          break;
 783  866                  roffnode_pop(r);
 784  867          }
 785  868  }
 786  869  
 787  870  
 788  871  /* ARGSUSED */
 789  872  static enum rofferr
 790  873  roff_ccond(ROFF_ARGS)
 791  874  {
↓ open down ↓ 185 lines elided ↑ open up ↑
 977 1060  /* ARGSUSED */
 978 1061  static enum rofferr
 979 1062  roff_cond_sub(ROFF_ARGS)
 980 1063  {
 981 1064          enum rofft       t;
 982 1065          enum roffrule    rr;
 983 1066          char            *ep;
 984 1067  
 985 1068          rr = r->last->rule;
 986 1069          roffnode_cleanscope(r);
     1070 +        t = roff_parse(r, *bufp, &pos);
 987 1071  
 988 1072          /*
 989      -         * If the macro is unknown, first check if it contains a closing
 990      -         * delimiter `\}'.  If it does, close out our scope and return
 991      -         * the currently-scoped rule (ignore or continue).  Else, drop
 992      -         * into the currently-scoped rule.
     1073 +         * Fully handle known macros when they are structurally
     1074 +         * required or when the conditional evaluated to true.
 993 1075           */
 994 1076  
 995      -        if (ROFF_MAX == (t = roff_parse(r, *bufp, &pos))) {
 996      -                ep = &(*bufp)[pos];
 997      -                for ( ; NULL != (ep = strchr(ep, '\\')); ep++) {
 998      -                        ep++;
 999      -                        if ('}' != *ep)
1000      -                                continue;
     1077 +        if ((ROFF_MAX != t) &&
     1078 +            (ROFF_ccond == t || ROFFRULE_ALLOW == rr ||
     1079 +             ROFFMAC_STRUCT & roffs[t].flags)) {
     1080 +                assert(roffs[t].proc);
     1081 +                return((*roffs[t].proc)(r, t, bufp, szp,
     1082 +                                        ln, ppos, pos, offs));
     1083 +        }
1001 1084  
1002      -                        /*
1003      -                         * Make the \} go away.
1004      -                         * This is a little haphazard, as it's not quite
1005      -                         * clear how nroff does this.
1006      -                         * If we're at the end of line, then just chop
1007      -                         * off the \} and resize the buffer.
1008      -                         * If we aren't, then conver it to spaces.
1009      -                         */
     1085 +        /* Always check for the closing delimiter `\}'. */
1010 1086  
1011      -                        if ('\0' == *(ep + 1)) {
1012      -                                *--ep = '\0';
1013      -                                *szp -= 2;
1014      -                        } else
1015      -                                *(ep - 1) = *ep = ' ';
     1087 +        ep = &(*bufp)[pos];
     1088 +        while (NULL != (ep = strchr(ep, '\\'))) {
     1089 +                if ('}' != *(++ep))
     1090 +                        continue;
1016 1091  
1017      -                        roff_ccond(r, ROFF_ccond, bufp, szp, 
1018      -                                        ln, pos, pos + 2, offs);
1019      -                        break;
1020      -                }
1021      -                return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
1022      -        }
     1092 +                /*
     1093 +                 * If we're at the end of line, then just chop
     1094 +                 * off the \} and resize the buffer.
     1095 +                 * If we aren't, then convert it to spaces.
     1096 +                 */
1023 1097  
1024      -        /*
1025      -         * A denied conditional must evaluate its children if and only
1026      -         * if they're either structurally required (such as loops and
1027      -         * conditionals) or a closing macro.
1028      -         */
     1098 +                if ('\0' == *(ep + 1)) {
     1099 +                        *--ep = '\0';
     1100 +                        *szp -= 2;
     1101 +                } else
     1102 +                        *(ep - 1) = *ep = ' ';
1029 1103  
1030      -        if (ROFFRULE_DENY == rr)
1031      -                if ( ! (ROFFMAC_STRUCT & roffs[t].flags))
1032      -                        if (ROFF_ccond != t)
1033      -                                return(ROFF_IGN);
1034      -
1035      -        assert(roffs[t].proc);
1036      -        return((*roffs[t].proc)(r, t, bufp, szp, 
1037      -                                ln, ppos, pos, offs));
     1104 +                roff_ccond(r, ROFF_ccond, bufp, szp, 
     1105 +                                ln, pos, pos + 2, offs);
     1106 +                break;
     1107 +        }
     1108 +        return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
1038 1109  }
1039 1110  
1040 1111  /* ARGSUSED */
1041 1112  static enum rofferr
1042 1113  roff_cond_text(ROFF_ARGS)
1043 1114  {
1044 1115          char            *ep;
1045 1116          enum roffrule    rr;
1046 1117  
1047 1118          rr = r->last->rule;
↓ open down ↓ 4 lines elided ↑ open up ↑
1052 1123                  ep++;
1053 1124                  if ('}' != *ep)
1054 1125                          continue;
1055 1126                  *ep = '&';
1056 1127                  roff_ccond(r, ROFF_ccond, bufp, szp, 
1057 1128                                  ln, pos, pos + 2, offs);
1058 1129          }
1059 1130          return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
1060 1131  }
1061 1132  
     1133 +static int
     1134 +roff_getnum(const char *v, int *pos, int *res)
     1135 +{
     1136 +        int p, n;
     1137 +
     1138 +        p = *pos;
     1139 +        n = v[p] == '-';
     1140 +        if (n)
     1141 +                p++;
     1142 +
     1143 +        for (*res = 0; isdigit((unsigned char)v[p]); p++)
     1144 +                *res += 10 * *res + v[p] - '0';
     1145 +        if (p == *pos + n)
     1146 +                return 0;
     1147 +
     1148 +        if (n)
     1149 +                *res = -*res;
     1150 +
     1151 +        *pos = p;
     1152 +        return 1;
     1153 +}
     1154 +
     1155 +static int
     1156 +roff_getop(const char *v, int *pos, char *res)
     1157 +{
     1158 +        int e;
     1159 +
     1160 +        *res = v[*pos];
     1161 +        e = v[*pos + 1] == '=';
     1162 +
     1163 +        switch (*res) {
     1164 +        case '=':
     1165 +                break;
     1166 +        case '>':
     1167 +                if (e)
     1168 +                        *res = 'g';
     1169 +                break;
     1170 +        case '<':
     1171 +                if (e)
     1172 +                        *res = 'l';
     1173 +                break;
     1174 +        default:
     1175 +                return(0);
     1176 +        }
     1177 +
     1178 +        *pos += 1 + e;
     1179 +
     1180 +        return(*res);
     1181 +}
     1182 +
1062 1183  static enum roffrule
1063 1184  roff_evalcond(const char *v, int *pos)
1064 1185  {
     1186 +        int      not, lh, rh;
     1187 +        char     op;
1065 1188  
1066 1189          switch (v[*pos]) {
1067 1190          case ('n'):
1068 1191                  (*pos)++;
1069 1192                  return(ROFFRULE_ALLOW);
1070 1193          case ('e'):
1071 1194                  /* FALLTHROUGH */
1072 1195          case ('o'):
1073 1196                  /* FALLTHROUGH */
1074 1197          case ('t'):
1075 1198                  (*pos)++;
1076 1199                  return(ROFFRULE_DENY);
     1200 +        case ('!'):
     1201 +                (*pos)++;
     1202 +                not = 1;
     1203 +                break;
1077 1204          default:
     1205 +                not = 0;
1078 1206                  break;
1079 1207          }
1080 1208  
1081      -        while (v[*pos] && ' ' != v[*pos])
1082      -                (*pos)++;
1083      -        return(ROFFRULE_DENY);
     1209 +        if (!roff_getnum(v, pos, &lh))
     1210 +                return ROFFRULE_DENY;
     1211 +        if (!roff_getop(v, pos, &op)) {
     1212 +                if (lh < 0)
     1213 +                        lh = 0;
     1214 +                goto out;
     1215 +        }
     1216 +        if (!roff_getnum(v, pos, &rh))
     1217 +                return ROFFRULE_DENY;
     1218 +        switch (op) {
     1219 +        case 'g':
     1220 +                lh = lh >= rh;
     1221 +                break;
     1222 +        case 'l':
     1223 +                lh = lh <= rh;
     1224 +                break;
     1225 +        case '=':
     1226 +                lh = lh == rh;
     1227 +                break;
     1228 +        case '>':
     1229 +                lh = lh > rh;
     1230 +                break;
     1231 +        case '<':
     1232 +                lh = lh < rh;
     1233 +                break;
     1234 +        default:
     1235 +                return ROFFRULE_DENY;
     1236 +        }
     1237 +out:
     1238 +        if (not)
     1239 +                lh = !lh;
     1240 +        return lh ? ROFFRULE_ALLOW : ROFFRULE_DENY;
1084 1241  }
1085 1242  
1086 1243  /* ARGSUSED */
1087 1244  static enum rofferr
1088 1245  roff_line_ignore(ROFF_ARGS)
1089 1246  {
1090 1247  
1091      -        if (ROFF_it == tok)
1092      -                mandoc_msg(MANDOCERR_REQUEST, r->parse, ln, ppos, "it");
1093      -
1094 1248          return(ROFF_IGN);
1095 1249  }
1096 1250  
1097 1251  /* ARGSUSED */
1098 1252  static enum rofferr
1099 1253  roff_cond(ROFF_ARGS)
1100 1254  {
1101      -        int              sv;
1102      -        enum roffrule    rule;
1103 1255  
     1256 +        roffnode_push(r, tok, NULL, ln, ppos);
     1257 +
1104 1258          /* 
1105 1259           * An `.el' has no conditional body: it will consume the value
1106 1260           * of the current rstack entry set in prior `ie' calls or
1107 1261           * defaults to DENY.  
1108 1262           *
1109 1263           * If we're not an `el', however, then evaluate the conditional.
1110 1264           */
1111 1265  
1112      -        rule = ROFF_el == tok ?
     1266 +        r->last->rule = ROFF_el == tok ?
1113 1267                  (r->rstackpos < 0 ? 
1114 1268                   ROFFRULE_DENY : r->rstack[r->rstackpos--]) :
1115 1269                  roff_evalcond(*bufp, &pos);
1116 1270  
1117      -        sv = pos;
1118      -        while (' ' == (*bufp)[pos])
1119      -                pos++;
1120      -
1121 1271          /*
1122      -         * Roff is weird.  If we have just white-space after the
1123      -         * conditional, it's considered the BODY and we exit without
1124      -         * really doing anything.  Warn about this.  It's probably
1125      -         * wrong.
1126      -         */
1127      -
1128      -        if ('\0' == (*bufp)[pos] && sv != pos) {
1129      -                mandoc_msg(MANDOCERR_NOARGS, r->parse, ln, ppos, NULL);
1130      -                return(ROFF_IGN);
1131      -        }
1132      -
1133      -        roffnode_push(r, tok, NULL, ln, ppos);
1134      -
1135      -        r->last->rule = rule;
1136      -
1137      -        /*
1138 1272           * An if-else will put the NEGATION of the current evaluated
1139 1273           * conditional into the stack of rules.
1140 1274           */
1141 1275  
1142 1276          if (ROFF_ie == tok) {
1143 1277                  if (r->rstackpos == RSTACK_MAX - 1) {
1144 1278                          mandoc_msg(MANDOCERR_MEM, 
1145 1279                                  r->parse, ln, ppos, NULL);
1146 1280                          return(ROFF_ERR);
1147 1281                  }
↓ open down ↓ 1 lines elided ↑ open up ↑
1149 1283                          ROFFRULE_DENY == r->last->rule ?
1150 1284                          ROFFRULE_ALLOW : ROFFRULE_DENY;
1151 1285          }
1152 1286  
1153 1287          /* If the parent has false as its rule, then so do we. */
1154 1288  
1155 1289          if (r->last->parent && ROFFRULE_DENY == r->last->parent->rule)
1156 1290                  r->last->rule = ROFFRULE_DENY;
1157 1291  
1158 1292          /*
1159      -         * Determine scope.  If we're invoked with "\{" trailing the
1160      -         * conditional, then we're in a multiline scope.  Else our scope
1161      -         * expires on the next line.
     1293 +         * Determine scope.
     1294 +         * If there is nothing on the line after the conditional,
     1295 +         * not even whitespace, use next-line scope.
1162 1296           */
1163 1297  
1164      -        r->last->endspan = 1;
     1298 +        if ('\0' == (*bufp)[pos]) {
     1299 +                r->last->endspan = 2;
     1300 +                goto out;
     1301 +        }
1165 1302  
     1303 +        while (' ' == (*bufp)[pos])
     1304 +                pos++;
     1305 +
     1306 +        /* An opening brace requests multiline scope. */
     1307 +
1166 1308          if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) {
1167 1309                  r->last->endspan = -1;
1168 1310                  pos += 2;
     1311 +                goto out;
1169 1312          } 
1170 1313  
1171 1314          /*
1172      -         * If there are no arguments on the line, the next-line scope is
1173      -         * assumed.
     1315 +         * Anything else following the conditional causes
     1316 +         * single-line scope.  Warn if the scope contains
     1317 +         * nothing but trailing whitespace.
1174 1318           */
1175 1319  
1176 1320          if ('\0' == (*bufp)[pos])
1177      -                return(ROFF_IGN);
     1321 +                mandoc_msg(MANDOCERR_NOARGS, r->parse, ln, ppos, NULL);
1178 1322  
1179      -        /* Otherwise re-run the roff parser after recalculating. */
     1323 +        r->last->endspan = 1;
1180 1324  
     1325 +out:
1181 1326          *offs = pos;
1182 1327          return(ROFF_RERUN);
1183 1328  }
1184 1329  
1185 1330  
1186 1331  /* ARGSUSED */
1187 1332  static enum rofferr
1188 1333  roff_ds(ROFF_ARGS)
1189 1334  {
1190 1335          char            *name, *string;
↓ open down ↓ 15 lines elided ↑ open up ↑
1206 1351  
1207 1352          /* Read past initial double-quote. */
1208 1353          if ('"' == *string)
1209 1354                  string++;
1210 1355  
1211 1356          /* The rest is the value. */
1212 1357          roff_setstr(r, name, string, 0);
1213 1358          return(ROFF_IGN);
1214 1359  }
1215 1360  
     1361 +void
     1362 +roff_setreg(struct roff *r, const char *name, int val, char sign)
     1363 +{
     1364 +        struct roffreg  *reg;
     1365 +
     1366 +        /* Search for an existing register with the same name. */
     1367 +        reg = r->regtab;
     1368 +
     1369 +        while (reg && strcmp(name, reg->key.p))
     1370 +                reg = reg->next;
     1371 +
     1372 +        if (NULL == reg) {
     1373 +                /* Create a new register. */
     1374 +                reg = mandoc_malloc(sizeof(struct roffreg));
     1375 +                reg->key.p = mandoc_strdup(name);
     1376 +                reg->key.sz = strlen(name);
     1377 +                reg->val = 0;
     1378 +                reg->next = r->regtab;
     1379 +                r->regtab = reg;
     1380 +        }
     1381 +
     1382 +        if ('+' == sign)
     1383 +                reg->val += val;
     1384 +        else if ('-' == sign)
     1385 +                reg->val -= val;
     1386 +        else
     1387 +                reg->val = val;
     1388 +}
     1389 +
1216 1390  int
1217      -roff_regisset(const struct roff *r, enum regs reg)
     1391 +roff_getreg(const struct roff *r, const char *name)
1218 1392  {
     1393 +        struct roffreg  *reg;
1219 1394  
1220      -        return(r->regs[(int)reg].set);
     1395 +        for (reg = r->regtab; reg; reg = reg->next)
     1396 +                if (0 == strcmp(name, reg->key.p))
     1397 +                        return(reg->val);
     1398 +
     1399 +        return(0);
1221 1400  }
1222 1401  
1223      -unsigned int
1224      -roff_regget(const struct roff *r, enum regs reg)
     1402 +static int
     1403 +roff_getregn(const struct roff *r, const char *name, size_t len)
1225 1404  {
     1405 +        struct roffreg  *reg;
1226 1406  
1227      -        return(r->regs[(int)reg].u);
     1407 +        for (reg = r->regtab; reg; reg = reg->next)
     1408 +                if (len == reg->key.sz &&
     1409 +                    0 == strncmp(name, reg->key.p, len))
     1410 +                        return(reg->val);
     1411 +
     1412 +        return(0);
1228 1413  }
1229 1414  
1230      -void
1231      -roff_regunset(struct roff *r, enum regs reg)
     1415 +static void
     1416 +roff_freereg(struct roffreg *reg)
1232 1417  {
     1418 +        struct roffreg  *old_reg;
1233 1419  
1234      -        r->regs[(int)reg].set = 0;
     1420 +        while (NULL != reg) {
     1421 +                free(reg->key.p);
     1422 +                old_reg = reg;
     1423 +                reg = reg->next;
     1424 +                free(old_reg);
     1425 +        }
1235 1426  }
1236 1427  
1237 1428  /* ARGSUSED */
1238 1429  static enum rofferr
1239 1430  roff_nr(ROFF_ARGS)
1240 1431  {
1241 1432          const char      *key;
1242 1433          char            *val;
     1434 +        size_t           sz;
1243 1435          int              iv;
     1436 +        char             sign;
1244 1437  
1245 1438          val = *bufp + pos;
1246 1439          key = roff_getname(r, &val, ln, pos);
1247 1440  
1248      -        if (0 == strcmp(key, "nS")) {
1249      -                r->regs[(int)REG_nS].set = 1;
1250      -                if ((iv = mandoc_strntoi(val, strlen(val), 10)) >= 0)
1251      -                        r->regs[(int)REG_nS].u = (unsigned)iv;
1252      -                else
1253      -                        r->regs[(int)REG_nS].u = 0u;
1254      -        }
     1441 +        sign = *val;
     1442 +        if ('+' == sign || '-' == sign)
     1443 +                val++;
1255 1444  
     1445 +        sz = strspn(val, "0123456789");
     1446 +        iv = sz ? mandoc_strntoi(val, sz, 10) : 0;
     1447 +
     1448 +        roff_setreg(r, key, iv, sign);
     1449 +
1256 1450          return(ROFF_IGN);
1257 1451  }
1258 1452  
1259 1453  /* ARGSUSED */
1260 1454  static enum rofferr
1261 1455  roff_rm(ROFF_ARGS)
1262 1456  {
1263 1457          const char       *name;
1264 1458          char             *cp;
1265 1459  
↓ open down ↓ 1 lines elided ↑ open up ↑
1267 1461          while ('\0' != *cp) {
1268 1462                  name = roff_getname(r, &cp, ln, (int)(cp - *bufp));
1269 1463                  if ('\0' != *name)
1270 1464                          roff_setstr(r, name, NULL, 0);
1271 1465          }
1272 1466          return(ROFF_IGN);
1273 1467  }
1274 1468  
1275 1469  /* ARGSUSED */
1276 1470  static enum rofferr
     1471 +roff_it(ROFF_ARGS)
     1472 +{
     1473 +        char            *cp;
     1474 +        size_t           len;
     1475 +        int              iv;
     1476 +
     1477 +        /* Parse the number of lines. */
     1478 +        cp = *bufp + pos;
     1479 +        len = strcspn(cp, " \t");
     1480 +        cp[len] = '\0';
     1481 +        if ((iv = mandoc_strntoi(cp, len, 10)) <= 0) {
     1482 +                mandoc_msg(MANDOCERR_NUMERIC, r->parse,
     1483 +                                ln, ppos, *bufp + 1);
     1484 +                return(ROFF_IGN);
     1485 +        }
     1486 +        cp += len + 1;
     1487 +
     1488 +        /* Arm the input line trap. */
     1489 +        roffit_lines = iv;
     1490 +        roffit_macro = mandoc_strdup(cp);
     1491 +        return(ROFF_IGN);
     1492 +}
     1493 +
     1494 +/* ARGSUSED */
     1495 +static enum rofferr
     1496 +roff_Dd(ROFF_ARGS)
     1497 +{
     1498 +        const char *const       *cp;
     1499 +
     1500 +        if (MPARSE_MDOC != r->parsetype)
     1501 +                for (cp = __mdoc_reserved; *cp; cp++)
     1502 +                        roff_setstr(r, *cp, NULL, 0);
     1503 +
     1504 +        return(ROFF_CONT);
     1505 +}
     1506 +
     1507 +/* ARGSUSED */
     1508 +static enum rofferr
     1509 +roff_TH(ROFF_ARGS)
     1510 +{
     1511 +        const char *const       *cp;
     1512 +
     1513 +        if (MPARSE_MDOC != r->parsetype)
     1514 +                for (cp = __man_reserved; *cp; cp++)
     1515 +                        roff_setstr(r, *cp, NULL, 0);
     1516 +
     1517 +        return(ROFF_CONT);
     1518 +}
     1519 +
     1520 +/* ARGSUSED */
     1521 +static enum rofferr
1277 1522  roff_TE(ROFF_ARGS)
1278 1523  {
1279 1524  
1280 1525          if (NULL == r->tbl)
1281 1526                  mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
1282 1527          else
1283 1528                  tbl_end(&r->tbl);
1284 1529  
1285 1530          return(ROFF_IGN);
1286 1531  }
↓ open down ↓ 58 lines elided ↑ open up ↑
1345 1590  {
1346 1591  
1347 1592          mandoc_msg(MANDOCERR_NOSCOPE, r->parse, ln, ppos, NULL);
1348 1593          return(ROFF_IGN);
1349 1594  }
1350 1595  
1351 1596  /* ARGSUSED */
1352 1597  static enum rofferr
1353 1598  roff_TS(ROFF_ARGS)
1354 1599  {
1355      -        struct tbl_node *t;
     1600 +        struct tbl_node *tbl;
1356 1601  
1357 1602          if (r->tbl) {
1358 1603                  mandoc_msg(MANDOCERR_SCOPEBROKEN, r->parse, ln, ppos, NULL);
1359 1604                  tbl_end(&r->tbl);
1360 1605          }
1361 1606  
1362      -        t = tbl_alloc(ppos, ln, r->parse);
     1607 +        tbl = tbl_alloc(ppos, ln, r->parse);
1363 1608  
1364 1609          if (r->last_tbl)
1365      -                r->last_tbl->next = t;
     1610 +                r->last_tbl->next = tbl;
1366 1611          else
1367      -                r->first_tbl = r->last_tbl = t;
     1612 +                r->first_tbl = r->last_tbl = tbl;
1368 1613  
1369      -        r->tbl = r->last_tbl = t;
     1614 +        r->tbl = r->last_tbl = tbl;
1370 1615          return(ROFF_IGN);
1371 1616  }
1372 1617  
1373 1618  /* ARGSUSED */
1374 1619  static enum rofferr
     1620 +roff_cc(ROFF_ARGS)
     1621 +{
     1622 +        const char      *p;
     1623 +
     1624 +        p = *bufp + pos;
     1625 +
     1626 +        if ('\0' == *p || '.' == (r->control = *p++))
     1627 +                r->control = 0;
     1628 +
     1629 +        if ('\0' != *p)
     1630 +                mandoc_msg(MANDOCERR_ARGCOUNT, r->parse, ln, ppos, NULL);
     1631 +
     1632 +        return(ROFF_IGN);
     1633 +}
     1634 +
     1635 +/* ARGSUSED */
     1636 +static enum rofferr
1375 1637  roff_tr(ROFF_ARGS)
1376 1638  {
1377 1639          const char      *p, *first, *second;
1378 1640          size_t           fsz, ssz;
1379 1641          enum mandoc_esc  esc;
1380 1642  
1381 1643          p = *bufp + pos;
1382 1644  
1383 1645          if ('\0' == *p) {
1384 1646                  mandoc_msg(MANDOCERR_ARGCOUNT, r->parse, ln, ppos, NULL);
↓ open down ↓ 78 lines elided ↑ open up ↑
1463 1725  /* ARGSUSED */
1464 1726  static enum rofferr
1465 1727  roff_userdef(ROFF_ARGS)
1466 1728  {
1467 1729          const char       *arg[9];
1468 1730          char             *cp, *n1, *n2;
1469 1731          int               i;
1470 1732  
1471 1733          /*
1472 1734           * Collect pointers to macro argument strings
1473      -         * and null-terminate them.
     1735 +         * and NUL-terminate them.
1474 1736           */
1475 1737          cp = *bufp + pos;
1476 1738          for (i = 0; i < 9; i++)
1477 1739                  arg[i] = '\0' == *cp ? "" :
1478 1740                      mandoc_getarg(r->parse, &cp, ln, &pos);
1479 1741  
1480 1742          /*
1481 1743           * Expand macro arguments.
1482 1744           */
1483 1745          *szp = 0;
↓ open down ↓ 274 lines elided ↑ open up ↑
1758 2020                   * roff_res() was called.
1759 2021                   */
1760 2022                  sz = (int)(p - pp);
1761 2023                  res = mandoc_realloc(res, ssz + sz + 1);
1762 2024                  memcpy(res + ssz, pp, sz);
1763 2025                  ssz += sz;
1764 2026          }
1765 2027  
1766 2028          res[(int)ssz] = '\0';
1767 2029          return(res);
     2030 +}
     2031 +
     2032 +/*
     2033 + * Find out whether a line is a macro line or not.  
     2034 + * If it is, adjust the current position and return one; if it isn't,
     2035 + * return zero and don't change the current position.
     2036 + * If the control character has been set with `.cc', then let that grain
     2037 + * precedence.
     2038 + * This is slighly contrary to groff, where using the non-breaking
     2039 + * control character when `cc' has been invoked will cause the
     2040 + * non-breaking macro contents to be printed verbatim.
     2041 + */
     2042 +int
     2043 +roff_getcontrol(const struct roff *r, const char *cp, int *ppos)
     2044 +{
     2045 +        int             pos;
     2046 +
     2047 +        pos = *ppos;
     2048 +
     2049 +        if (0 != r->control && cp[pos] == r->control)
     2050 +                pos++;
     2051 +        else if (0 != r->control)
     2052 +                return(0);
     2053 +        else if ('\\' == cp[pos] && '.' == cp[pos + 1])
     2054 +                pos += 2;
     2055 +        else if ('.' == cp[pos] || '\'' == cp[pos])
     2056 +                pos++;
     2057 +        else
     2058 +                return(0);
     2059 +
     2060 +        while (' ' == cp[pos] || '\t' == cp[pos])
     2061 +                pos++;
     2062 +
     2063 +        *ppos = pos;
     2064 +        return(1);
1768 2065  }
    
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX