[BACK]Return to roff.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / mandoc

Annotation of src/usr.bin/mandoc/roff.c, Revision 1.15

1.15    ! schwarze    1: /*     $Id: roff.c,v 1.14 2010/10/26 22:28:57 schwarze Exp $ */
1.1       schwarze    2: /*
                      3:  * Copyright (c) 2010 Kristaps Dzonsons <kristaps@bsd.lv>
1.8       schwarze    4:  * Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org>
1.1       schwarze    5:  *
                      6:  * Permission to use, copy, modify, and distribute this software for any
                      7:  * purpose with or without fee is hereby granted, provided that the above
                      8:  * copyright notice and this permission notice appear in all copies.
                      9:  *
                     10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     11:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     12:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     13:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     14:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     15:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     16:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     17:  */
                     18: #ifdef HAVE_CONFIG_H
                     19: #include "config.h"
                     20: #endif
                     21:
                     22: #include <assert.h>
1.6       schwarze   23: #include <errno.h>
1.3       schwarze   24: #include <ctype.h>
1.6       schwarze   25: #include <limits.h>
1.1       schwarze   26: #include <stdlib.h>
                     27: #include <string.h>
1.2       schwarze   28: #include <stdio.h>
1.1       schwarze   29:
                     30: #include "mandoc.h"
                     31: #include "roff.h"
1.8       schwarze   32: #include "libmandoc.h"
1.1       schwarze   33:
1.2       schwarze   34: #define        RSTACK_MAX      128
                     35:
                     36: #define        ROFF_CTL(c) \
                     37:        ('.' == (c) || '\'' == (c))
                     38:
1.1       schwarze   39: enum   rofft {
1.2       schwarze   40:        ROFF_am,
                     41:        ROFF_ami,
                     42:        ROFF_am1,
1.1       schwarze   43:        ROFF_de,
                     44:        ROFF_dei,
1.2       schwarze   45:        ROFF_de1,
                     46:        ROFF_ds,
                     47:        ROFF_el,
                     48:        ROFF_ie,
                     49:        ROFF_if,
1.1       schwarze   50:        ROFF_ig,
1.14      schwarze   51:        ROFF_nr,
1.2       schwarze   52:        ROFF_rm,
1.14      schwarze   53:        ROFF_so,
1.2       schwarze   54:        ROFF_tr,
                     55:        ROFF_cblock,
1.13      schwarze   56:        ROFF_ccond, /* FIXME: remove this. */
1.1       schwarze   57:        ROFF_MAX
                     58: };
                     59:
1.2       schwarze   60: enum   roffrule {
                     61:        ROFFRULE_ALLOW,
                     62:        ROFFRULE_DENY
                     63: };
                     64:
1.8       schwarze   65:
                     66: struct roffstr {
                     67:        char            *name; /* key of symbol */
                     68:        char            *string; /* current value */
                     69:        struct roffstr  *next; /* next in list */
                     70: };
                     71:
1.1       schwarze   72: struct roff {
                     73:        struct roffnode *last; /* leaf of stack */
                     74:        mandocmsg        msg; /* err/warn/fatal messages */
                     75:        void            *data; /* privdata for messages */
1.2       schwarze   76:        enum roffrule    rstack[RSTACK_MAX]; /* stack of !`ie' rules */
                     77:        int              rstackpos; /* position in rstack */
1.6       schwarze   78:        struct regset   *regs; /* read/writable registers */
1.8       schwarze   79:        struct roffstr  *first_string;
1.1       schwarze   80: };
                     81:
                     82: struct roffnode {
                     83:        enum rofft       tok; /* type of node */
                     84:        struct roffnode *parent; /* up one in stack */
                     85:        int              line; /* parse line */
                     86:        int              col; /* parse col */
1.2       schwarze   87:        char            *end; /* end-rules: custom token */
                     88:        int              endspan; /* end-rules: next-line or infty */
                     89:        enum roffrule    rule; /* current evaluation rule */
1.1       schwarze   90: };
                     91:
                     92: #define        ROFF_ARGS        struct roff *r, /* parse ctx */ \
                     93:                         enum rofft tok, /* tok of macro */ \
                     94:                         char **bufp, /* input buffer */ \
                     95:                         size_t *szp, /* size of input buffer */ \
                     96:                         int ln, /* parse line */ \
1.2       schwarze   97:                         int ppos, /* original pos in buffer */ \
                     98:                         int pos, /* current pos in buffer */ \
                     99:                         int *offs /* reset offset of buffer data */
1.1       schwarze  100:
                    101: typedef        enum rofferr (*roffproc)(ROFF_ARGS);
                    102:
                    103: struct roffmac {
                    104:        const char      *name; /* macro name */
1.2       schwarze  105:        roffproc         proc; /* process new macro */
                    106:        roffproc         text; /* process as child text of macro */
                    107:        roffproc         sub; /* process as child of macro */
                    108:        int              flags;
                    109: #define        ROFFMAC_STRUCT  (1 << 0) /* always interpret */
1.3       schwarze  110:        struct roffmac  *next;
1.1       schwarze  111: };
                    112:
1.2       schwarze  113: static enum rofferr     roff_block(ROFF_ARGS);
                    114: static enum rofferr     roff_block_text(ROFF_ARGS);
                    115: static enum rofferr     roff_block_sub(ROFF_ARGS);
                    116: static enum rofferr     roff_cblock(ROFF_ARGS);
                    117: static enum rofferr     roff_ccond(ROFF_ARGS);
                    118: static enum rofferr     roff_cond(ROFF_ARGS);
                    119: static enum rofferr     roff_cond_text(ROFF_ARGS);
                    120: static enum rofferr     roff_cond_sub(ROFF_ARGS);
1.7       schwarze  121: static enum rofferr     roff_ds(ROFF_ARGS);
1.8       schwarze  122: static enum roffrule    roff_evalcond(const char *, int *);
                    123: static void             roff_freestr(struct roff *);
                    124: static const char      *roff_getstrn(const struct roff *,
                    125:                                const char *, size_t);
1.6       schwarze  126: static enum rofferr     roff_line(ROFF_ARGS);
                    127: static enum rofferr     roff_nr(ROFF_ARGS);
1.9       schwarze  128: static int              roff_res(struct roff *,
                    129:                                char **, size_t *, int);
1.8       schwarze  130: static void             roff_setstr(struct roff *,
                    131:                                const char *, const char *);
1.14      schwarze  132: static enum rofferr     roff_so(ROFF_ARGS);
1.12      schwarze  133: static char            *roff_strdup(const char *);
1.1       schwarze  134:
1.3       schwarze  135: /* See roff_hash_find() */
                    136:
                    137: #define        ASCII_HI         126
                    138: #define        ASCII_LO         33
                    139: #define        HASHWIDTH       (ASCII_HI - ASCII_LO + 1)
                    140:
                    141: static struct roffmac  *hash[HASHWIDTH];
                    142:
                    143: static struct roffmac   roffs[ROFF_MAX] = {
                    144:        { "am", roff_block, roff_block_text, roff_block_sub, 0, NULL },
                    145:        { "ami", roff_block, roff_block_text, roff_block_sub, 0, NULL },
                    146:        { "am1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
                    147:        { "de", roff_block, roff_block_text, roff_block_sub, 0, NULL },
                    148:        { "dei", roff_block, roff_block_text, roff_block_sub, 0, NULL },
                    149:        { "de1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
1.7       schwarze  150:        { "ds", roff_ds, NULL, NULL, 0, NULL },
1.3       schwarze  151:        { "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
                    152:        { "ie", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
                    153:        { "if", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
                    154:        { "ig", roff_block, roff_block_text, roff_block_sub, 0, NULL },
1.14      schwarze  155:        { "nr", roff_nr, NULL, NULL, 0, NULL },
1.3       schwarze  156:        { "rm", roff_line, NULL, NULL, 0, NULL },
1.14      schwarze  157:        { "so", roff_so, NULL, NULL, 0, NULL },
1.3       schwarze  158:        { "tr", roff_line, NULL, NULL, 0, NULL },
                    159:        { ".", roff_cblock, NULL, NULL, 0, NULL },
                    160:        { "\\}", roff_ccond, NULL, NULL, 0, NULL },
1.1       schwarze  161: };
                    162:
                    163: static void             roff_free1(struct roff *);
                    164: static enum rofft       roff_hash_find(const char *);
1.3       schwarze  165: static void             roff_hash_init(void);
1.2       schwarze  166: static void             roffnode_cleanscope(struct roff *);
1.11      schwarze  167: static void             roffnode_push(struct roff *,
1.1       schwarze  168:                                enum rofft, int, int);
                    169: static void             roffnode_pop(struct roff *);
                    170: static enum rofft       roff_parse(const char *, int *);
1.6       schwarze  171: static int              roff_parse_nat(const char *, unsigned int *);
1.1       schwarze  172:
1.3       schwarze  173: /* See roff_hash_find() */
                    174: #define        ROFF_HASH(p)    (p[0] - ASCII_LO)
                    175:
                    176: static void
                    177: roff_hash_init(void)
                    178: {
                    179:        struct roffmac   *n;
                    180:        int               buc, i;
                    181:
                    182:        for (i = 0; i < (int)ROFF_MAX; i++) {
                    183:                assert(roffs[i].name[0] >= ASCII_LO);
                    184:                assert(roffs[i].name[0] <= ASCII_HI);
                    185:
                    186:                buc = ROFF_HASH(roffs[i].name);
                    187:
                    188:                if (NULL != (n = hash[buc])) {
                    189:                        for ( ; n->next; n = n->next)
                    190:                                /* Do nothing. */ ;
                    191:                        n->next = &roffs[i];
                    192:                } else
                    193:                        hash[buc] = &roffs[i];
                    194:        }
                    195: }
                    196:
1.1       schwarze  197:
                    198: /*
                    199:  * Look up a roff token by its name.  Returns ROFF_MAX if no macro by
                    200:  * the nil-terminated string name could be found.
                    201:  */
                    202: static enum rofft
                    203: roff_hash_find(const char *p)
                    204: {
1.3       schwarze  205:        int              buc;
                    206:        struct roffmac  *n;
1.1       schwarze  207:
1.3       schwarze  208:        /*
                    209:         * libroff has an extremely simple hashtable, for the time
                    210:         * being, which simply keys on the first character, which must
                    211:         * be printable, then walks a chain.  It works well enough until
                    212:         * optimised.
                    213:         */
                    214:
                    215:        if (p[0] < ASCII_LO || p[0] > ASCII_HI)
                    216:                return(ROFF_MAX);
                    217:
                    218:        buc = ROFF_HASH(p);
                    219:
                    220:        if (NULL == (n = hash[buc]))
                    221:                return(ROFF_MAX);
                    222:        for ( ; n; n = n->next)
                    223:                if (0 == strcmp(n->name, p))
                    224:                        return((enum rofft)(n - roffs));
1.1       schwarze  225:
                    226:        return(ROFF_MAX);
                    227: }
                    228:
                    229:
                    230: /*
                    231:  * Pop the current node off of the stack of roff instructions currently
                    232:  * pending.
                    233:  */
                    234: static void
                    235: roffnode_pop(struct roff *r)
                    236: {
                    237:        struct roffnode *p;
                    238:
1.2       schwarze  239:        assert(r->last);
                    240:        p = r->last;
                    241:
                    242:        if (ROFF_el == p->tok)
                    243:                if (r->rstackpos > -1)
                    244:                        r->rstackpos--;
                    245:
                    246:        r->last = r->last->parent;
                    247:        if (p->end)
                    248:                free(p->end);
1.1       schwarze  249:        free(p);
                    250: }
                    251:
                    252:
                    253: /*
                    254:  * Push a roff node onto the instruction stack.  This must later be
                    255:  * removed with roffnode_pop().
                    256:  */
1.11      schwarze  257: static void
1.1       schwarze  258: roffnode_push(struct roff *r, enum rofft tok, int line, int col)
                    259: {
                    260:        struct roffnode *p;
                    261:
1.11      schwarze  262:        p = mandoc_calloc(1, sizeof(struct roffnode));
1.1       schwarze  263:        p->tok = tok;
                    264:        p->parent = r->last;
                    265:        p->line = line;
                    266:        p->col = col;
1.2       schwarze  267:        p->rule = p->parent ? p->parent->rule : ROFFRULE_DENY;
1.1       schwarze  268:
                    269:        r->last = p;
                    270: }
                    271:
                    272:
                    273: static void
                    274: roff_free1(struct roff *r)
                    275: {
                    276:
                    277:        while (r->last)
                    278:                roffnode_pop(r);
1.8       schwarze  279:        roff_freestr(r);
1.1       schwarze  280: }
                    281:
                    282:
                    283: void
                    284: roff_reset(struct roff *r)
                    285: {
                    286:
                    287:        roff_free1(r);
                    288: }
                    289:
                    290:
                    291: void
                    292: roff_free(struct roff *r)
                    293: {
                    294:
                    295:        roff_free1(r);
                    296:        free(r);
                    297: }
                    298:
                    299:
                    300: struct roff *
1.11      schwarze  301: roff_alloc(struct regset *regs, void *data, const mandocmsg msg)
1.1       schwarze  302: {
                    303:        struct roff     *r;
                    304:
1.11      schwarze  305:        r = mandoc_calloc(1, sizeof(struct roff));
1.6       schwarze  306:        r->regs = regs;
1.1       schwarze  307:        r->msg = msg;
                    308:        r->data = data;
1.2       schwarze  309:        r->rstackpos = -1;
1.3       schwarze  310:
                    311:        roff_hash_init();
1.1       schwarze  312:        return(r);
                    313: }
                    314:
                    315:
1.8       schwarze  316: /*
                    317:  * Pre-filter each and every line for reserved words (one beginning with
                    318:  * `\*', e.g., `\*(ab').  These must be handled before the actual line
                    319:  * is processed.
                    320:  */
                    321: static int
1.9       schwarze  322: roff_res(struct roff *r, char **bufp, size_t *szp, int pos)
1.8       schwarze  323: {
                    324:        const char      *cp, *cpp, *st, *res;
                    325:        int              i, maxl;
                    326:        size_t           nsz;
                    327:        char            *n;
                    328:
1.9       schwarze  329:        /* LINTED */
1.8       schwarze  330:        for (cp = &(*bufp)[pos]; (cpp = strstr(cp, "\\*")); cp++) {
                    331:                cp = cpp + 2;
                    332:                switch (*cp) {
                    333:                case ('('):
                    334:                        cp++;
                    335:                        maxl = 2;
                    336:                        break;
                    337:                case ('['):
                    338:                        cp++;
                    339:                        maxl = 0;
                    340:                        break;
                    341:                default:
                    342:                        maxl = 1;
                    343:                        break;
                    344:                }
                    345:
                    346:                st = cp;
                    347:
                    348:                for (i = 0; 0 == maxl || i < maxl; i++, cp++) {
                    349:                        if ('\0' == *cp)
                    350:                                return(1); /* Error. */
                    351:                        if (0 == maxl && ']' == *cp)
                    352:                                break;
                    353:                }
                    354:
                    355:                res = roff_getstrn(r, st, (size_t)i);
                    356:
                    357:                if (NULL == res) {
                    358:                        cp -= maxl ? 1 : 0;
                    359:                        continue;
                    360:                }
                    361:
                    362:                nsz = *szp + strlen(res) + 1;
                    363:                n = mandoc_malloc(nsz);
                    364:
                    365:                *n = '\0';
                    366:
                    367:                strlcat(n, *bufp, (size_t)(cpp - *bufp + 1));
                    368:                strlcat(n, res, nsz);
                    369:                strlcat(n, cp + (maxl ? 0 : 1), nsz);
                    370:
                    371:                free(*bufp);
                    372:
                    373:                *bufp = n;
                    374:                *szp = nsz;
                    375:                return(0);
                    376:        }
                    377:
                    378:        return(1);
                    379: }
                    380:
                    381:
1.1       schwarze  382: enum rofferr
1.6       schwarze  383: roff_parseln(struct roff *r, int ln, char **bufp,
                    384:                size_t *szp, int pos, int *offs)
1.1       schwarze  385: {
                    386:        enum rofft       t;
                    387:        int              ppos;
                    388:
1.2       schwarze  389:        /*
1.8       schwarze  390:         * Run the reserved-word filter only if we have some reserved
                    391:         * words to fill in.
                    392:         */
                    393:
1.9       schwarze  394:        if (r->first_string && ! roff_res(r, bufp, szp, pos))
1.8       schwarze  395:                return(ROFF_RERUN);
                    396:
                    397:        /*
1.2       schwarze  398:         * First, if a scope is open and we're not a macro, pass the
                    399:         * text through the macro's filter.  If a scope isn't open and
                    400:         * we're not a macro, just let it through.
                    401:         */
                    402:
                    403:        if (r->last && ! ROFF_CTL((*bufp)[pos])) {
                    404:                t = r->last->tok;
                    405:                assert(roffs[t].text);
                    406:                return((*roffs[t].text)
1.8       schwarze  407:                                (r, t, bufp, szp,
                    408:                                 ln, pos, pos, offs));
1.2       schwarze  409:        } else if ( ! ROFF_CTL((*bufp)[pos]))
                    410:                return(ROFF_CONT);
                    411:
                    412:        /*
                    413:         * If a scope is open, go to the child handler for that macro,
                    414:         * as it may want to preprocess before doing anything with it.
                    415:         */
                    416:
                    417:        if (r->last) {
1.1       schwarze  418:                t = r->last->tok;
                    419:                assert(roffs[t].sub);
1.2       schwarze  420:                return((*roffs[t].sub)
1.8       schwarze  421:                                (r, t, bufp, szp,
                    422:                                 ln, pos, pos, offs));
1.2       schwarze  423:        }
                    424:
                    425:        /*
                    426:         * Lastly, as we've no scope open, try to look up and execute
                    427:         * the new macro.  If no macro is found, simply return and let
                    428:         * the compilers handle it.
                    429:         */
                    430:
                    431:        ppos = pos;
                    432:        if (ROFF_MAX == (t = roff_parse(*bufp, &pos)))
1.1       schwarze  433:                return(ROFF_CONT);
                    434:
1.2       schwarze  435:        assert(roffs[t].proc);
                    436:        return((*roffs[t].proc)
1.8       schwarze  437:                        (r, t, bufp, szp,
                    438:                         ln, ppos, pos, offs));
1.2       schwarze  439: }
                    440:
1.1       schwarze  441:
1.2       schwarze  442: int
                    443: roff_endparse(struct roff *r)
                    444: {
1.1       schwarze  445:
1.2       schwarze  446:        if (NULL == r->last)
                    447:                return(1);
                    448:        return((*r->msg)(MANDOCERR_SCOPEEXIT, r->data, r->last->line,
                    449:                                r->last->col, NULL));
1.1       schwarze  450: }
                    451:
                    452:
                    453: /*
                    454:  * Parse a roff node's type from the input buffer.  This must be in the
                    455:  * form of ".foo xxx" in the usual way.
                    456:  */
                    457: static enum rofft
                    458: roff_parse(const char *buf, int *pos)
                    459: {
                    460:        int              j;
                    461:        char             mac[5];
                    462:        enum rofft       t;
                    463:
1.2       schwarze  464:        assert(ROFF_CTL(buf[*pos]));
                    465:        (*pos)++;
1.1       schwarze  466:
                    467:        while (buf[*pos] && (' ' == buf[*pos] || '\t' == buf[*pos]))
                    468:                (*pos)++;
                    469:
                    470:        if ('\0' == buf[*pos])
                    471:                return(ROFF_MAX);
                    472:
                    473:        for (j = 0; j < 4; j++, (*pos)++)
                    474:                if ('\0' == (mac[j] = buf[*pos]))
                    475:                        break;
1.2       schwarze  476:                else if (' ' == buf[*pos] || (j && '\\' == buf[*pos]))
1.1       schwarze  477:                        break;
                    478:
                    479:        if (j == 4 || j < 1)
                    480:                return(ROFF_MAX);
                    481:
                    482:        mac[j] = '\0';
                    483:
                    484:        if (ROFF_MAX == (t = roff_hash_find(mac)))
                    485:                return(t);
                    486:
                    487:        while (buf[*pos] && ' ' == buf[*pos])
                    488:                (*pos)++;
                    489:
                    490:        return(t);
                    491: }
                    492:
                    493:
1.6       schwarze  494: static int
                    495: roff_parse_nat(const char *buf, unsigned int *res)
                    496: {
                    497:        char            *ep;
                    498:        long             lval;
                    499:
                    500:        errno = 0;
                    501:        lval = strtol(buf, &ep, 10);
                    502:        if (buf[0] == '\0' || *ep != '\0')
                    503:                return(0);
                    504:        if ((errno == ERANGE &&
                    505:                        (lval == LONG_MAX || lval == LONG_MIN)) ||
                    506:                        (lval > INT_MAX || lval < 0))
                    507:                return(0);
                    508:
                    509:        *res = (unsigned int)lval;
                    510:        return(1);
                    511: }
                    512:
                    513:
1.1       schwarze  514: /* ARGSUSED */
                    515: static enum rofferr
1.2       schwarze  516: roff_cblock(ROFF_ARGS)
1.1       schwarze  517: {
                    518:
1.2       schwarze  519:        /*
                    520:         * A block-close `..' should only be invoked as a child of an
                    521:         * ignore macro, otherwise raise a warning and just ignore it.
                    522:         */
                    523:
                    524:        if (NULL == r->last) {
                    525:                if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
                    526:                        return(ROFF_ERR);
                    527:                return(ROFF_IGN);
                    528:        }
1.1       schwarze  529:
1.2       schwarze  530:        switch (r->last->tok) {
                    531:        case (ROFF_am):
                    532:                /* FALLTHROUGH */
                    533:        case (ROFF_ami):
                    534:                /* FALLTHROUGH */
                    535:        case (ROFF_am1):
                    536:                /* FALLTHROUGH */
                    537:        case (ROFF_de):
                    538:                /* FALLTHROUGH */
                    539:        case (ROFF_dei):
                    540:                /* FALLTHROUGH */
                    541:        case (ROFF_de1):
                    542:                /* FALLTHROUGH */
                    543:        case (ROFF_ig):
                    544:                break;
                    545:        default:
                    546:                if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
                    547:                        return(ROFF_ERR);
1.1       schwarze  548:                return(ROFF_IGN);
1.2       schwarze  549:        }
                    550:
                    551:        if ((*bufp)[pos])
                    552:                if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
                    553:                        return(ROFF_ERR);
                    554:
                    555:        roffnode_pop(r);
                    556:        roffnode_cleanscope(r);
                    557:        return(ROFF_IGN);
                    558:
                    559: }
1.1       schwarze  560:
                    561:
1.2       schwarze  562: static void
                    563: roffnode_cleanscope(struct roff *r)
                    564: {
1.1       schwarze  565:
1.2       schwarze  566:        while (r->last) {
                    567:                if (--r->last->endspan < 0)
                    568:                        break;
                    569:                roffnode_pop(r);
                    570:        }
                    571: }
1.1       schwarze  572:
                    573:
1.2       schwarze  574: /* ARGSUSED */
                    575: static enum rofferr
                    576: roff_ccond(ROFF_ARGS)
                    577: {
1.1       schwarze  578:
1.2       schwarze  579:        if (NULL == r->last) {
                    580:                if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
                    581:                        return(ROFF_ERR);
1.1       schwarze  582:                return(ROFF_IGN);
1.2       schwarze  583:        }
1.1       schwarze  584:
1.2       schwarze  585:        switch (r->last->tok) {
                    586:        case (ROFF_el):
                    587:                /* FALLTHROUGH */
                    588:        case (ROFF_ie):
                    589:                /* FALLTHROUGH */
                    590:        case (ROFF_if):
                    591:                break;
                    592:        default:
                    593:                if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
                    594:                        return(ROFF_ERR);
                    595:                return(ROFF_IGN);
                    596:        }
1.1       schwarze  597:
1.2       schwarze  598:        if (r->last->endspan > -1) {
                    599:                if ( ! (*r->msg)(MANDOCERR_NOSCOPE, r->data, ln, ppos, NULL))
                    600:                        return(ROFF_ERR);
1.1       schwarze  601:                return(ROFF_IGN);
1.2       schwarze  602:        }
                    603:
                    604:        if ((*bufp)[pos])
                    605:                if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
                    606:                        return(ROFF_ERR);
1.1       schwarze  607:
1.2       schwarze  608:        roffnode_pop(r);
                    609:        roffnode_cleanscope(r);
1.1       schwarze  610:        return(ROFF_IGN);
                    611: }
                    612:
                    613:
                    614: /* ARGSUSED */
                    615: static enum rofferr
1.2       schwarze  616: roff_block(ROFF_ARGS)
1.1       schwarze  617: {
1.2       schwarze  618:        int             sv;
                    619:        size_t          sz;
                    620:
                    621:        if (ROFF_ig != tok && '\0' == (*bufp)[pos]) {
                    622:                if ( ! (*r->msg)(MANDOCERR_NOARGS, r->data, ln, ppos, NULL))
                    623:                        return(ROFF_ERR);
                    624:                return(ROFF_IGN);
                    625:        } else if (ROFF_ig != tok) {
                    626:                while ((*bufp)[pos] && ' ' != (*bufp)[pos])
                    627:                        pos++;
                    628:                while (' ' == (*bufp)[pos])
                    629:                        pos++;
                    630:        }
                    631:
1.11      schwarze  632:        roffnode_push(r, tok, ln, ppos);
1.2       schwarze  633:
                    634:        if ('\0' == (*bufp)[pos])
                    635:                return(ROFF_IGN);
1.1       schwarze  636:
1.2       schwarze  637:        sv = pos;
                    638:        while ((*bufp)[pos] && ' ' != (*bufp)[pos] &&
                    639:                        '\t' != (*bufp)[pos])
                    640:                pos++;
                    641:
                    642:        /*
                    643:         * Note: groff does NOT like escape characters in the input.
                    644:         * Instead of detecting this, we're just going to let it fly and
                    645:         * to hell with it.
                    646:         */
                    647:
                    648:        assert(pos > sv);
                    649:        sz = (size_t)(pos - sv);
                    650:
                    651:        if (1 == sz && '.' == (*bufp)[sv])
                    652:                return(ROFF_IGN);
                    653:
1.11      schwarze  654:        r->last->end = mandoc_malloc(sz + 1);
1.2       schwarze  655:
                    656:        memcpy(r->last->end, *bufp + sv, sz);
                    657:        r->last->end[(int)sz] = '\0';
                    658:
                    659:        if ((*bufp)[pos])
                    660:                if ( ! (*r->msg)(MANDOCERR_ARGSLOST, r->data, ln, pos, NULL))
                    661:                        return(ROFF_ERR);
1.1       schwarze  662:
                    663:        return(ROFF_IGN);
                    664: }
                    665:
                    666:
                    667: /* ARGSUSED */
                    668: static enum rofferr
1.2       schwarze  669: roff_block_sub(ROFF_ARGS)
1.1       schwarze  670: {
1.2       schwarze  671:        enum rofft      t;
                    672:        int             i, j;
                    673:
                    674:        /*
                    675:         * First check whether a custom macro exists at this level.  If
                    676:         * it does, then check against it.  This is some of groff's
                    677:         * stranger behaviours.  If we encountered a custom end-scope
                    678:         * tag and that tag also happens to be a "real" macro, then we
                    679:         * need to try interpreting it again as a real macro.  If it's
                    680:         * not, then return ignore.  Else continue.
                    681:         */
                    682:
                    683:        if (r->last->end) {
                    684:                i = pos + 1;
                    685:                while (' ' == (*bufp)[i] || '\t' == (*bufp)[i])
                    686:                        i++;
                    687:
                    688:                for (j = 0; r->last->end[j]; j++, i++)
                    689:                        if ((*bufp)[i] != r->last->end[j])
                    690:                                break;
1.1       schwarze  691:
1.2       schwarze  692:                if ('\0' == r->last->end[j] &&
                    693:                                ('\0' == (*bufp)[i] ||
                    694:                                 ' ' == (*bufp)[i] ||
                    695:                                 '\t' == (*bufp)[i])) {
                    696:                        roffnode_pop(r);
                    697:                        roffnode_cleanscope(r);
1.1       schwarze  698:
1.2       schwarze  699:                        if (ROFF_MAX != roff_parse(*bufp, &pos))
                    700:                                return(ROFF_RERUN);
                    701:                        return(ROFF_IGN);
                    702:                }
1.1       schwarze  703:        }
                    704:
1.2       schwarze  705:        /*
                    706:         * If we have no custom end-query or lookup failed, then try
                    707:         * pulling it out of the hashtable.
                    708:         */
1.1       schwarze  709:
1.2       schwarze  710:        ppos = pos;
                    711:        t = roff_parse(*bufp, &pos);
1.1       schwarze  712:
1.2       schwarze  713:        /* If we're not a comment-end, then throw it away. */
                    714:        if (ROFF_cblock != t)
1.1       schwarze  715:                return(ROFF_IGN);
                    716:
1.2       schwarze  717:        assert(roffs[t].proc);
1.6       schwarze  718:        return((*roffs[t].proc)(r, t, bufp, szp,
                    719:                                ln, ppos, pos, offs));
1.2       schwarze  720: }
                    721:
                    722:
                    723: /* ARGSUSED */
                    724: static enum rofferr
                    725: roff_block_text(ROFF_ARGS)
                    726: {
                    727:
                    728:        return(ROFF_IGN);
                    729: }
                    730:
                    731:
                    732: /* ARGSUSED */
                    733: static enum rofferr
                    734: roff_cond_sub(ROFF_ARGS)
                    735: {
                    736:        enum rofft       t;
                    737:        enum roffrule    rr;
                    738:
                    739:        ppos = pos;
                    740:        rr = r->last->rule;
                    741:
1.5       schwarze  742:        /*
                    743:         * Clean out scope.  If we've closed ourselves, then don't
                    744:         * continue.
                    745:         */
                    746:
                    747:        roffnode_cleanscope(r);
                    748:
1.12      schwarze  749:        if (ROFF_MAX == (t = roff_parse(*bufp, &pos))) {
                    750:                if ('\\' == (*bufp)[pos] && '}' == (*bufp)[pos + 1])
                    751:                        return(roff_ccond
                    752:                                (r, ROFF_ccond, bufp, szp,
                    753:                                 ln, pos, pos + 2, offs));
1.2       schwarze  754:                return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
1.12      schwarze  755:        }
1.2       schwarze  756:
                    757:        /*
                    758:         * A denied conditional must evaluate its children if and only
                    759:         * if they're either structurally required (such as loops and
                    760:         * conditionals) or a closing macro.
                    761:         */
                    762:        if (ROFFRULE_DENY == rr)
                    763:                if ( ! (ROFFMAC_STRUCT & roffs[t].flags))
                    764:                        if (ROFF_ccond != t)
                    765:                                return(ROFF_IGN);
                    766:
                    767:        assert(roffs[t].proc);
1.6       schwarze  768:        return((*roffs[t].proc)(r, t, bufp, szp,
                    769:                                ln, ppos, pos, offs));
1.2       schwarze  770: }
                    771:
                    772:
                    773: /* ARGSUSED */
                    774: static enum rofferr
                    775: roff_cond_text(ROFF_ARGS)
                    776: {
                    777:        char            *ep, *st;
                    778:        enum roffrule    rr;
                    779:
                    780:        rr = r->last->rule;
1.1       schwarze  781:
                    782:        /*
1.2       schwarze  783:         * We display the value of the text if out current evaluation
                    784:         * scope permits us to do so.
1.1       schwarze  785:         */
1.13      schwarze  786:
                    787:        /* FIXME: use roff_ccond? */
1.1       schwarze  788:
1.2       schwarze  789:        st = &(*bufp)[pos];
                    790:        if (NULL == (ep = strstr(st, "\\}"))) {
                    791:                roffnode_cleanscope(r);
                    792:                return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
                    793:        }
                    794:
1.4       schwarze  795:        if (ep == st || (ep > st && '\\' != *(ep - 1)))
1.2       schwarze  796:                roffnode_pop(r);
                    797:
                    798:        roffnode_cleanscope(r);
                    799:        return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
                    800: }
                    801:
                    802:
1.5       schwarze  803: static enum roffrule
                    804: roff_evalcond(const char *v, int *pos)
                    805: {
                    806:
                    807:        switch (v[*pos]) {
                    808:        case ('n'):
                    809:                (*pos)++;
                    810:                return(ROFFRULE_ALLOW);
                    811:        case ('e'):
                    812:                /* FALLTHROUGH */
                    813:        case ('o'):
                    814:                /* FALLTHROUGH */
                    815:        case ('t'):
                    816:                (*pos)++;
                    817:                return(ROFFRULE_DENY);
                    818:        default:
                    819:                break;
                    820:        }
                    821:
                    822:        while (v[*pos] && ' ' != v[*pos])
                    823:                (*pos)++;
                    824:        return(ROFFRULE_DENY);
                    825: }
                    826:
                    827:
1.2       schwarze  828: /* ARGSUSED */
                    829: static enum rofferr
1.6       schwarze  830: roff_line(ROFF_ARGS)
                    831: {
                    832:
                    833:        return(ROFF_IGN);
                    834: }
                    835:
                    836:
                    837: /* ARGSUSED */
                    838: static enum rofferr
1.2       schwarze  839: roff_cond(ROFF_ARGS)
                    840: {
                    841:        int              sv;
1.5       schwarze  842:        enum roffrule    rule;
1.2       schwarze  843:
                    844:        /* Stack overflow! */
                    845:
                    846:        if (ROFF_ie == tok && r->rstackpos == RSTACK_MAX - 1) {
1.1       schwarze  847:                (*r->msg)(MANDOCERR_MEM, r->data, ln, ppos, NULL);
                    848:                return(ROFF_ERR);
                    849:        }
                    850:
1.5       schwarze  851:        /* First, evaluate the conditional. */
1.2       schwarze  852:
1.5       schwarze  853:        if (ROFF_el == tok) {
                    854:                /*
                    855:                 * An `.el' will get the value of the current rstack
                    856:                 * entry set in prior `ie' calls or defaults to DENY.
                    857:                 */
                    858:                if (r->rstackpos < 0)
                    859:                        rule = ROFFRULE_DENY;
                    860:                else
                    861:                        rule = r->rstack[r->rstackpos];
                    862:        } else
                    863:                rule = roff_evalcond(*bufp, &pos);
1.2       schwarze  864:
                    865:        sv = pos;
1.5       schwarze  866:
1.2       schwarze  867:        while (' ' == (*bufp)[pos])
                    868:                pos++;
                    869:
                    870:        /*
                    871:         * Roff is weird.  If we have just white-space after the
                    872:         * conditional, it's considered the BODY and we exit without
                    873:         * really doing anything.  Warn about this.  It's probably
                    874:         * wrong.
                    875:         */
1.5       schwarze  876:
1.2       schwarze  877:        if ('\0' == (*bufp)[pos] && sv != pos) {
1.5       schwarze  878:                if ((*r->msg)(MANDOCERR_NOARGS, r->data, ln, ppos, NULL))
                    879:                        return(ROFF_IGN);
                    880:                return(ROFF_ERR);
1.2       schwarze  881:        }
                    882:
1.11      schwarze  883:        roffnode_push(r, tok, ln, ppos);
1.2       schwarze  884:
1.5       schwarze  885:        r->last->rule = rule;
1.2       schwarze  886:
                    887:        if (ROFF_ie == tok) {
                    888:                /*
                    889:                 * An if-else will put the NEGATION of the current
                    890:                 * evaluated conditional into the stack.
                    891:                 */
                    892:                r->rstackpos++;
                    893:                if (ROFFRULE_DENY == r->last->rule)
                    894:                        r->rstack[r->rstackpos] = ROFFRULE_ALLOW;
                    895:                else
                    896:                        r->rstack[r->rstackpos] = ROFFRULE_DENY;
                    897:        }
1.5       schwarze  898:
                    899:        /* If the parent has false as its rule, then so do we. */
                    900:
1.2       schwarze  901:        if (r->last->parent && ROFFRULE_DENY == r->last->parent->rule)
                    902:                r->last->rule = ROFFRULE_DENY;
1.5       schwarze  903:
                    904:        /*
                    905:         * Determine scope.  If we're invoked with "\{" trailing the
                    906:         * conditional, then we're in a multiline scope.  Else our scope
                    907:         * expires on the next line.
                    908:         */
1.2       schwarze  909:
                    910:        r->last->endspan = 1;
                    911:
                    912:        if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) {
                    913:                r->last->endspan = -1;
                    914:                pos += 2;
                    915:        }
                    916:
                    917:        /*
                    918:         * If there are no arguments on the line, the next-line scope is
                    919:         * assumed.
                    920:         */
                    921:
                    922:        if ('\0' == (*bufp)[pos])
                    923:                return(ROFF_IGN);
                    924:
                    925:        /* Otherwise re-run the roff parser after recalculating. */
1.1       schwarze  926:
1.2       schwarze  927:        *offs = pos;
                    928:        return(ROFF_RERUN);
1.1       schwarze  929: }
                    930:
                    931:
1.2       schwarze  932: /* ARGSUSED */
                    933: static enum rofferr
1.7       schwarze  934: roff_ds(ROFF_ARGS)
                    935: {
1.10      schwarze  936:        char            *name, *string;
                    937:
                    938:        /*
                    939:         * A symbol is named by the first word following the macro
                    940:         * invocation up to a space.  Its value is anything after the
                    941:         * name's trailing whitespace and optional double-quote.  Thus,
                    942:         *
                    943:         *  [.ds foo "bar  "     ]
                    944:         *
                    945:         * will have `bar  "     ' as its value.
                    946:         */
1.7       schwarze  947:
                    948:        name = *bufp + pos;
                    949:        if ('\0' == *name)
                    950:                return(ROFF_IGN);
                    951:
                    952:        string = name;
1.10      schwarze  953:        /* Read until end of name. */
1.7       schwarze  954:        while (*string && ' ' != *string)
                    955:                string++;
1.10      schwarze  956:
                    957:        /* Nil-terminate name. */
1.7       schwarze  958:        if (*string)
1.10      schwarze  959:                *(string++) = '\0';
                    960:
                    961:        /* Read past spaces. */
                    962:        while (*string && ' ' == *string)
                    963:                string++;
                    964:
                    965:        /* Read passed initial double-quote. */
1.7       schwarze  966:        if (*string && '"' == *string)
                    967:                string++;
                    968:
1.10      schwarze  969:        /* The rest is the value. */
1.8       schwarze  970:        roff_setstr(r, name, string);
1.7       schwarze  971:        return(ROFF_IGN);
                    972: }
                    973:
                    974:
                    975: /* ARGSUSED */
                    976: static enum rofferr
1.6       schwarze  977: roff_nr(ROFF_ARGS)
1.1       schwarze  978: {
1.6       schwarze  979:        const char      *key, *val;
                    980:        struct reg      *rg;
                    981:
                    982:        key = &(*bufp)[pos];
                    983:        rg = r->regs->regs;
                    984:
                    985:        /* Parse register request. */
                    986:        while ((*bufp)[pos] && ' ' != (*bufp)[pos])
                    987:                pos++;
                    988:
                    989:        /*
                    990:         * Set our nil terminator.  Because this line is going to be
                    991:         * ignored anyway, we can munge it as we please.
                    992:         */
                    993:        if ((*bufp)[pos])
                    994:                (*bufp)[pos++] = '\0';
                    995:
                    996:        /* Skip whitespace to register token. */
                    997:        while ((*bufp)[pos] && ' ' == (*bufp)[pos])
                    998:                pos++;
                    999:
                   1000:        val = &(*bufp)[pos];
                   1001:
                   1002:        /* Process register token. */
                   1003:
                   1004:        if (0 == strcmp(key, "nS")) {
                   1005:                rg[(int)REG_nS].set = 1;
                   1006:                if ( ! roff_parse_nat(val, &rg[(int)REG_nS].v.u))
                   1007:                        rg[(int)REG_nS].v.u = 0;
                   1008:        }
1.1       schwarze 1009:
1.2       schwarze 1010:        return(ROFF_IGN);
1.14      schwarze 1011: }
                   1012:
                   1013:
                   1014: /* ARGSUSED */
                   1015: static enum rofferr
                   1016: roff_so(ROFF_ARGS)
                   1017: {
                   1018:        char *name;
1.15    ! schwarze 1019:
        !          1020:        (*r->msg)(MANDOCERR_SO, r->data, ln, ppos, NULL);
1.14      schwarze 1021:
                   1022:        name = *bufp + pos;
                   1023:        if ('/' == *name || strstr(name, "../") || strstr(name, "/..")) {
                   1024:                (*r->msg)(MANDOCERR_SOPATH, r->data, ln, pos, NULL);
                   1025:                return(ROFF_ERR);
                   1026:        }
                   1027:
                   1028:        *offs = pos;
                   1029:        return(ROFF_SO);
1.7       schwarze 1030: }
                   1031:
                   1032:
1.12      schwarze 1033: static char *
                   1034: roff_strdup(const char *name)
                   1035: {
                   1036:        char            *namecopy, *sv;
                   1037:
                   1038:        /*
                   1039:         * This isn't a nice simple mandoc_strdup() because we must
                   1040:         * handle roff's stupid double-escape rule.
                   1041:         */
                   1042:        sv = namecopy = mandoc_malloc(strlen(name) + 1);
                   1043:        while (*name) {
                   1044:                if ('\\' == *name && '\\' == *(name + 1))
                   1045:                        name++;
                   1046:                *namecopy++ = *name++;
                   1047:        }
                   1048:
                   1049:        *namecopy = '\0';
                   1050:        return(sv);
                   1051: }
                   1052:
                   1053:
1.8       schwarze 1054: static void
                   1055: roff_setstr(struct roff *r, const char *name, const char *string)
1.7       schwarze 1056: {
                   1057:        struct roffstr   *n;
                   1058:        char             *namecopy;
                   1059:
1.8       schwarze 1060:        n = r->first_string;
1.7       schwarze 1061:        while (n && strcmp(name, n->name))
                   1062:                n = n->next;
1.8       schwarze 1063:
                   1064:        if (NULL == n) {
                   1065:                namecopy = mandoc_strdup(name);
                   1066:                n = mandoc_malloc(sizeof(struct roffstr));
                   1067:                n->name = namecopy;
                   1068:                n->next = r->first_string;
                   1069:                r->first_string = n;
                   1070:        } else
1.7       schwarze 1071:                free(n->string);
1.8       schwarze 1072:
1.12      schwarze 1073:        /* Don't use mandoc_strdup: clean out double-escapes. */
                   1074:        n->string = string ? roff_strdup(string) : NULL;
1.7       schwarze 1075: }
                   1076:
                   1077:
1.8       schwarze 1078: static const char *
                   1079: roff_getstrn(const struct roff *r, const char *name, size_t len)
1.7       schwarze 1080: {
1.8       schwarze 1081:        const struct roffstr *n;
1.7       schwarze 1082:
1.8       schwarze 1083:        n = r->first_string;
1.10      schwarze 1084:        while (n && (strncmp(name, n->name, len) || '\0' != n->name[(int)len]))
1.7       schwarze 1085:                n = n->next;
1.8       schwarze 1086:
                   1087:        return(n ? n->string : NULL);
1.7       schwarze 1088: }
                   1089:
1.8       schwarze 1090:
                   1091: static void
                   1092: roff_freestr(struct roff *r)
1.7       schwarze 1093: {
                   1094:        struct roffstr   *n, *nn;
                   1095:
1.8       schwarze 1096:        for (n = r->first_string; n; n = nn) {
1.7       schwarze 1097:                free(n->name);
                   1098:                free(n->string);
                   1099:                nn = n->next;
                   1100:                free(n);
                   1101:        }
1.8       schwarze 1102:
                   1103:        r->first_string = NULL;
1.1       schwarze 1104: }