[BACK]Return to roff.c CVS log [TXT][DIR] Up to [cvsweb.bsd.lv] / mandoc

Annotation of mandoc/roff.c, Revision 1.53

1.53    ! kristaps    1: /* $Id: roff.c,v 1.52 2008/12/08 16:29:57 kristaps Exp $ */
1.1       kristaps    2: /*
                      3:  * Copyright (c) 2008 Kristaps Dzonsons <kristaps@kth.se>
                      4:  *
                      5:  * Permission to use, copy, modify, and distribute this software for any
                      6:  * purpose with or without fee is hereby granted, provided that the
                      7:  * above copyright notice and this permission notice appear in all
                      8:  * copies.
                      9:  *
                     10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
                     11:  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
                     12:  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
                     13:  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
                     14:  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
                     15:  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
                     16:  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
                     17:  * PERFORMANCE OF THIS SOFTWARE.
                     18:  */
1.30      kristaps   19: #include <sys/param.h>
1.33      kristaps   20: #include <sys/types.h>
1.30      kristaps   21:
1.1       kristaps   22: #include <assert.h>
                     23: #include <ctype.h>
                     24: #include <err.h>
1.12      kristaps   25: #include <stdarg.h>
1.1       kristaps   26: #include <stdlib.h>
                     27: #include <stdio.h>
                     28: #include <string.h>
                     29: #include <time.h>
                     30:
                     31: #include "libmdocml.h"
                     32: #include "private.h"
1.43      kristaps   33: #include "roff.h"
1.1       kristaps   34:
1.33      kristaps   35: /* FIXME: First letters of quoted-text interpreted in rofffindtok. */
                     36: /* FIXME: `No' not implemented. */
1.27      kristaps   37: /* TODO: warn if Pp occurs before/after Sh etc. (see mdoc.samples). */
                     38: /* TODO: warn about empty lists. */
                     39: /* TODO: (warn) some sections need specific elements. */
                     40: /* TODO: (warn) NAME section has particular order. */
                     41: /* TODO: unify empty-content tags a la <br />. */
                     42: /* TODO: macros with a set number of arguments? */
1.36      kristaps   43: /* TODO: validate Dt macro arguments. */
1.43      kristaps   44: /* FIXME: Bl -diag supposed to ignore callable children. */
                     45: /* FIXME: Nm has newline when used in NAME section. */
1.1       kristaps   46:
                     47: struct roffnode {
1.5       kristaps   48:        int               tok;                  /* Token id. */
                     49:        struct roffnode  *parent;               /* Parent (or NULL). */
1.1       kristaps   50: };
                     51:
                     52: struct rofftree {
1.5       kristaps   53:        struct roffnode  *last;                 /* Last parsed node. */
1.32      kristaps   54:        char             *cur;                  /* Line start. */
1.30      kristaps   55:        struct tm         tm;                   /* `Dd' results. */
1.42      kristaps   56:        char              name[64];             /* `Nm' results. */
1.5       kristaps   57:        char              os[64];               /* `Os' results. */
                     58:        char              title[64];            /* `Dt' results. */
1.51      kristaps   59:        enum roffmsec     section;
1.5       kristaps   60:        char              volume[64];           /* `Dt' results. */
1.1       kristaps   61:        int               state;
1.5       kristaps   62: #define        ROFF_PRELUDE     (1 << 1)               /* In roff prelude. */
                     63: #define        ROFF_PRELUDE_Os  (1 << 2)               /* `Os' is parsed. */
                     64: #define        ROFF_PRELUDE_Dt  (1 << 3)               /* `Dt' is parsed. */
                     65: #define        ROFF_PRELUDE_Dd  (1 << 4)               /* `Dd' is parsed. */
                     66: #define        ROFF_BODY        (1 << 5)               /* In roff body. */
1.32      kristaps   67:        struct roffcb     cb;                   /* Callbacks. */
                     68:        void             *arg;                  /* Callbacks' arg. */
1.52      kristaps   69:        int               csec;                 /* Current section. */
                     70:        int               asec;                 /* Thus-far sections. */
1.1       kristaps   71: };
                     72:
1.4       kristaps   73: static struct roffnode  *roffnode_new(int, struct rofftree *);
1.15      kristaps   74: static void              roffnode_free(struct rofftree *);
1.12      kristaps   75: static void              roff_warn(const struct rofftree *,
                     76:                                const char *, char *, ...);
                     77: static void              roff_err(const struct rofftree *,
                     78:                                const char *, char *, ...);
1.31      kristaps   79: static int               roffpurgepunct(struct rofftree *, char **);
1.7       kristaps   80: static int               roffscan(int, const int *);
1.1       kristaps   81: static int               rofffindtok(const char *);
                     82: static int               rofffindarg(const char *);
1.2       kristaps   83: static int               rofffindcallable(const char *);
1.51      kristaps   84: static int               roffismsec(const char *);
1.52      kristaps   85: static int               roffissec(const char **);
1.51      kristaps   86: static int               roffispunct(const char *);
1.53    ! kristaps   87: static int               roffisatt(const char *);
1.52      kristaps   88: static int               roffchecksec(struct rofftree *,
                     89:                                const char *, int);
1.10      kristaps   90: static int               roffargs(const struct rofftree *,
                     91:                                int, char *, char **);
1.5       kristaps   92: static int               roffargok(int, int);
1.12      kristaps   93: static int               roffnextopt(const struct rofftree *,
1.18      kristaps   94:                                int, char ***, char **);
1.27      kristaps   95: static int               roffparseopts(struct rofftree *, int,
                     96:                                char ***, int *, char **);
1.33      kristaps   97: static int               roffcall(struct rofftree *, int, char **);
1.51      kristaps   98: static int               roffexit(struct rofftree *, int);
1.17      kristaps   99: static int               roffparse(struct rofftree *, char *);
1.37      kristaps  100: static int               textparse(struct rofftree *, char *);
                    101: static int               roffdata(struct rofftree *, int, char *);
1.44      kristaps  102: static int               roffspecial(struct rofftree *, int,
1.47      kristaps  103:                                const char *, const int *,
                    104:                                const char **, size_t, char **);
1.42      kristaps  105: static int               roffsetname(struct rofftree *, char **);
1.1       kristaps  106:
1.35      kristaps  107: #ifdef __linux__
                    108: extern size_t            strlcat(char *, const char *, size_t);
                    109: extern size_t            strlcpy(char *, const char *, size_t);
1.33      kristaps  110: extern int               vsnprintf(char *, size_t,
                    111:                                const char *, va_list);
                    112: extern char             *strptime(const char *, const char *,
                    113:                                struct tm *);
                    114: #endif
                    115:
1.1       kristaps  116: int
                    117: roff_free(struct rofftree *tree, int flush)
                    118: {
1.16      kristaps  119:        int              error, t;
1.15      kristaps  120:        struct roffnode *n;
1.1       kristaps  121:
1.17      kristaps  122:        error = 0;
                    123:
1.16      kristaps  124:        if ( ! flush)
                    125:                goto end;
1.1       kristaps  126:
1.16      kristaps  127:        error = 1;
1.1       kristaps  128:
1.16      kristaps  129:        if (ROFF_PRELUDE & tree->state) {
1.28      kristaps  130:                roff_err(tree, NULL, "prelude never finished");
1.16      kristaps  131:                goto end;
1.52      kristaps  132:        } else if ( ! (ROFFSec_NAME & tree->asec)) {
                    133:                roff_err(tree, NULL, "missing `NAME' section");
                    134:                goto end;
                    135:        } else if ( ! (ROFFSec_NMASK & tree->asec))
                    136:                roff_warn(tree, NULL, "missing suggested `NAME', "
                    137:                                "`SYNOPSIS', `DESCRIPTION' sections");
1.16      kristaps  138:
1.28      kristaps  139:        for (n = tree->last; n; n = n->parent) {
1.16      kristaps  140:                if (0 != tokens[n->tok].ctx)
1.17      kristaps  141:                        continue;
1.28      kristaps  142:                roff_err(tree, NULL, "closing explicit scope `%s'",
1.16      kristaps  143:                                toknames[n->tok]);
                    144:                goto end;
                    145:        }
                    146:
                    147:        while (tree->last) {
                    148:                t = tree->last->tok;
1.51      kristaps  149:                if ( ! roffexit(tree, t))
1.16      kristaps  150:                        goto end;
1.1       kristaps  151:        }
                    152:
1.30      kristaps  153:        if ( ! (*tree->cb.rofftail)(tree->arg))
                    154:                goto end;
                    155:
1.16      kristaps  156:        error = 0;
                    157:
                    158: end:
                    159:
1.15      kristaps  160:        while (tree->last)
                    161:                roffnode_free(tree);
                    162:
1.1       kristaps  163:        free(tree);
1.17      kristaps  164:
1.1       kristaps  165:        return(error ? 0 : 1);
                    166: }
                    167:
                    168:
                    169: struct rofftree *
1.15      kristaps  170: roff_alloc(const struct roffcb *cb, void *args)
1.1       kristaps  171: {
                    172:        struct rofftree *tree;
                    173:
1.17      kristaps  174:        assert(args);
                    175:        assert(cb);
                    176:
1.12      kristaps  177:        if (NULL == (tree = calloc(1, sizeof(struct rofftree))))
                    178:                err(1, "calloc");
1.1       kristaps  179:
                    180:        tree->state = ROFF_PRELUDE;
1.15      kristaps  181:        tree->arg = args;
1.51      kristaps  182:        tree->section = ROFF_MSEC_MAX;
1.15      kristaps  183:
                    184:        (void)memcpy(&tree->cb, cb, sizeof(struct roffcb));
1.1       kristaps  185:
                    186:        return(tree);
                    187: }
                    188:
                    189:
                    190: int
1.17      kristaps  191: roff_engine(struct rofftree *tree, char *buf)
1.1       kristaps  192: {
                    193:
1.17      kristaps  194:        tree->cur = buf;
                    195:        assert(buf);
1.12      kristaps  196:
1.17      kristaps  197:        if (0 == *buf) {
1.29      kristaps  198:                roff_err(tree, buf, "blank line");
1.1       kristaps  199:                return(0);
                    200:        } else if ('.' != *buf)
1.17      kristaps  201:                return(textparse(tree, buf));
1.1       kristaps  202:
1.17      kristaps  203:        return(roffparse(tree, buf));
1.1       kristaps  204: }
                    205:
                    206:
                    207: static int
1.37      kristaps  208: textparse(struct rofftree *tree, char *buf)
1.1       kristaps  209: {
1.37      kristaps  210:        char            *bufp;
                    211:
                    212:        /* TODO: literal parsing. */
1.17      kristaps  213:
1.30      kristaps  214:        if ( ! (ROFF_BODY & tree->state)) {
                    215:                roff_err(tree, buf, "data not in body");
                    216:                return(0);
                    217:        }
1.37      kristaps  218:
                    219:        /* LINTED */
                    220:        while (*buf) {
                    221:                while (*buf && isspace(*buf))
                    222:                        buf++;
                    223:
                    224:                if (0 == *buf)
                    225:                        break;
                    226:
                    227:                bufp = buf++;
                    228:
                    229:                while (*buf && ! isspace(*buf))
                    230:                        buf++;
                    231:
                    232:                if (0 != *buf) {
                    233:                        *buf++ = 0;
                    234:                        if ( ! roffdata(tree, 1, bufp))
                    235:                                return(0);
                    236:                        continue;
                    237:                }
                    238:
                    239:                if ( ! roffdata(tree, 1, bufp))
                    240:                        return(0);
                    241:                break;
                    242:        }
                    243:
                    244:        return(1);
1.1       kristaps  245: }
                    246:
                    247:
                    248: static int
1.10      kristaps  249: roffargs(const struct rofftree *tree,
                    250:                int tok, char *buf, char **argv)
1.1       kristaps  251: {
                    252:        int              i;
1.12      kristaps  253:        char            *p;
1.1       kristaps  254:
                    255:        assert(tok >= 0 && tok < ROFF_MAX);
                    256:        assert('.' == *buf);
                    257:
1.12      kristaps  258:        p = buf;
                    259:
1.40      kristaps  260:        /*
                    261:         * This is an ugly little loop.  It parses a line into
                    262:         * space-delimited tokens.  If a quote mark is encountered, a
                    263:         * token is alloted the entire quoted text.  If whitespace is
                    264:         * escaped, it's included in the prior alloted token.
                    265:         */
                    266:
1.1       kristaps  267:        /* LINTED */
1.37      kristaps  268:        for (i = 0; *buf && i < ROFF_MAXLINEARG; i++) {
1.10      kristaps  269:                if ('\"' == *buf) {
                    270:                        argv[i] = ++buf;
                    271:                        while (*buf && '\"' != *buf)
                    272:                                buf++;
                    273:                        if (0 == *buf) {
1.13      kristaps  274:                                roff_err(tree, argv[i], "unclosed "
1.12      kristaps  275:                                                "quote in argument "
                    276:                                                "list for `%s'",
                    277:                                                toknames[tok]);
1.10      kristaps  278:                                return(0);
                    279:                        }
                    280:                } else {
                    281:                        argv[i] = buf++;
1.40      kristaps  282:                        while (*buf) {
                    283:                                if ( ! isspace(*buf)) {
                    284:                                        buf++;
                    285:                                        continue;
                    286:                                }
                    287:                                if (*(buf - 1) == '\\') {
                    288:                                        buf++;
                    289:                                        continue;
                    290:                                }
                    291:                                break;
                    292:                        }
1.10      kristaps  293:                        if (0 == *buf)
                    294:                                continue;
1.1       kristaps  295:                }
                    296:                *buf++ = 0;
                    297:                while (*buf && isspace(*buf))
                    298:                        buf++;
                    299:        }
1.40      kristaps  300:
1.1       kristaps  301:        assert(i > 0);
1.37      kristaps  302:        if (ROFF_MAXLINEARG == i && *buf) {
1.13      kristaps  303:                roff_err(tree, p, "too many arguments for `%s'", toknames
1.12      kristaps  304:                                [tok]);
1.10      kristaps  305:                return(0);
                    306:        }
                    307:
                    308:        argv[i] = NULL;
                    309:        return(1);
1.1       kristaps  310: }
                    311:
                    312:
1.6       kristaps  313: static int
                    314: roffscan(int tok, const int *tokv)
                    315: {
1.7       kristaps  316:
1.6       kristaps  317:        if (NULL == tokv)
                    318:                return(1);
                    319:
1.7       kristaps  320:        for ( ; ROFF_MAX != *tokv; tokv++)
1.6       kristaps  321:                if (tok == *tokv)
                    322:                        return(1);
                    323:
                    324:        return(0);
                    325: }
                    326:
                    327:
1.1       kristaps  328: static int
1.17      kristaps  329: roffparse(struct rofftree *tree, char *buf)
1.1       kristaps  330: {
                    331:        int               tok, t;
1.6       kristaps  332:        struct roffnode  *n;
1.37      kristaps  333:        char             *argv[ROFF_MAXLINEARG];
1.18      kristaps  334:        char            **argvp;
1.1       kristaps  335:
1.25      kristaps  336:        if (0 != *buf && 0 != *(buf + 1) && 0 != *(buf + 2))
                    337:                if (0 == strncmp(buf, ".\\\"", 3))
                    338:                        return(1);
                    339:
1.6       kristaps  340:        if (ROFF_MAX == (tok = rofffindtok(buf + 1))) {
1.52      kristaps  341:                roff_err(tree, buf, "bogus line macro");
1.6       kristaps  342:                return(0);
1.51      kristaps  343:        } else if ( ! roffargs(tree, tok, buf, argv))
1.1       kristaps  344:                return(0);
1.12      kristaps  345:
1.18      kristaps  346:        argvp = (char **)argv;
1.6       kristaps  347:
                    348:        /*
1.12      kristaps  349:         * Prelude macros break some assumptions, so branch now.
1.6       kristaps  350:         */
1.1       kristaps  351:
1.6       kristaps  352:        if (ROFF_PRELUDE & tree->state) {
                    353:                assert(NULL == tree->last);
1.51      kristaps  354:                return(roffcall(tree, tok, argvp));
1.28      kristaps  355:        }
1.6       kristaps  356:
                    357:        assert(ROFF_BODY & tree->state);
                    358:
                    359:        /*
                    360:         * First check that our possible parents and parent's possible
                    361:         * children are satisfied.
                    362:         */
                    363:
1.28      kristaps  364:        if (tree->last && ! roffscan
                    365:                        (tree->last->tok, tokens[tok].parents)) {
1.15      kristaps  366:                roff_err(tree, *argvp, "`%s' has invalid parent `%s'",
                    367:                                toknames[tok],
                    368:                                toknames[tree->last->tok]);
1.6       kristaps  369:                return(0);
                    370:        }
                    371:
1.28      kristaps  372:        if (tree->last && ! roffscan
                    373:                        (tok, tokens[tree->last->tok].children)) {
1.18      kristaps  374:                roff_err(tree, *argvp, "`%s' is invalid child of `%s'",
                    375:                                toknames[tok],
                    376:                                toknames[tree->last->tok]);
1.1       kristaps  377:                return(0);
                    378:        }
                    379:
                    380:        /*
1.6       kristaps  381:         * Branch if we're not a layout token.
1.1       kristaps  382:         */
                    383:
1.6       kristaps  384:        if (ROFF_LAYOUT != tokens[tok].type)
1.51      kristaps  385:                return(roffcall(tree, tok, argvp));
1.6       kristaps  386:        if (0 == tokens[tok].ctx)
1.51      kristaps  387:                return(roffcall(tree, tok, argvp));
1.1       kristaps  388:
1.12      kristaps  389:        /*
                    390:         * First consider implicit-end tags, like as follows:
                    391:         *      .Sh SECTION 1
                    392:         *      .Sh SECTION 2
                    393:         * In this, we want to close the scope of the NAME section.  If
                    394:         * there's an intermediary implicit-end tag, such as
                    395:         *      .Sh SECTION 1
                    396:         *      .Ss Subsection 1
                    397:         *      .Sh SECTION 2
                    398:         * then it must be closed as well.
                    399:         */
                    400:
1.6       kristaps  401:        if (tok == tokens[tok].ctx) {
1.12      kristaps  402:                /*
                    403:                 * First search up to the point where we must close.
                    404:                 * If one doesn't exist, then we can open a new scope.
                    405:                 */
                    406:
1.6       kristaps  407:                for (n = tree->last; n; n = n->parent) {
                    408:                        assert(0 == tokens[n->tok].ctx ||
                    409:                                        n->tok == tokens[n->tok].ctx);
                    410:                        if (n->tok == tok)
                    411:                                break;
1.10      kristaps  412:                        if (ROFF_SHALLOW & tokens[tok].flags) {
                    413:                                n = NULL;
                    414:                                break;
                    415:                        }
1.28      kristaps  416:                        if (tokens[n->tok].ctx == n->tok)
                    417:                                continue;
                    418:                        roff_err(tree, *argv, "`%s' breaks `%s' scope",
                    419:                                        toknames[tok], toknames[n->tok]);
                    420:                        return(0);
1.6       kristaps  421:                }
1.10      kristaps  422:
                    423:                /*
                    424:                 * Create a new scope, as no previous one exists to
                    425:                 * close out.
                    426:                 */
1.12      kristaps  427:
                    428:                if (NULL == n)
1.51      kristaps  429:                        return(roffcall(tree, tok, argvp));
1.10      kristaps  430:
                    431:                /*
1.12      kristaps  432:                 * Close out all intermediary scoped blocks, then hang
                    433:                 * the current scope from our predecessor's parent.
1.10      kristaps  434:                 */
                    435:
1.1       kristaps  436:                do {
                    437:                        t = tree->last->tok;
1.51      kristaps  438:                        if ( ! roffexit(tree, t))
1.1       kristaps  439:                                return(0);
                    440:                } while (t != tok);
1.6       kristaps  441:
1.51      kristaps  442:                return(roffcall(tree, tok, argvp));
1.1       kristaps  443:        }
                    444:
1.12      kristaps  445:        /*
                    446:         * Now consider explicit-end tags, where we want to close back
                    447:         * to a specific tag.  Example:
                    448:         *      .Bl
                    449:         *      .It Item.
                    450:         *      .El
                    451:         * In this, the `El' tag closes out the scope of `Bl'.
                    452:         */
                    453:
1.6       kristaps  454:        assert(tok != tokens[tok].ctx && 0 != tokens[tok].ctx);
                    455:
1.32      kristaps  456:        /* LINTED */
1.28      kristaps  457:        for (n = tree->last; n; n = n->parent)
                    458:                if (n->tok != tokens[tok].ctx) {
                    459:                        if (n->tok == tokens[n->tok].ctx)
                    460:                                continue;
                    461:                        roff_err(tree, *argv, "`%s' breaks `%s' scope",
                    462:                                        toknames[tok], toknames[n->tok]);
                    463:                        return(0);
                    464:                } else
                    465:                        break;
                    466:
                    467:
                    468:        if (NULL == n) {
                    469:                roff_err(tree, *argv, "`%s' has no starting tag `%s'",
                    470:                                toknames[tok],
                    471:                                toknames[tokens[tok].ctx]);
                    472:                return(0);
                    473:        }
                    474:
1.14      kristaps  475:        /* LINTED */
1.6       kristaps  476:        do {
                    477:                t = tree->last->tok;
1.51      kristaps  478:                if ( ! roffexit(tree, t))
1.6       kristaps  479:                        return(0);
                    480:        } while (t != tokens[tok].ctx);
1.1       kristaps  481:
1.9       kristaps  482:        return(1);
1.1       kristaps  483: }
                    484:
                    485:
                    486: static int
                    487: rofffindarg(const char *name)
                    488: {
                    489:        size_t           i;
                    490:
                    491:        /* FIXME: use a table, this is slow but ok for now. */
                    492:
                    493:        /* LINTED */
                    494:        for (i = 0; i < ROFF_ARGMAX; i++)
                    495:                /* LINTED */
1.4       kristaps  496:                if (0 == strcmp(name, tokargnames[i]))
1.1       kristaps  497:                        return((int)i);
                    498:
                    499:        return(ROFF_ARGMAX);
                    500: }
                    501:
                    502:
                    503: static int
1.6       kristaps  504: rofffindtok(const char *buf)
1.1       kristaps  505: {
1.6       kristaps  506:        char             token[4];
1.23      kristaps  507:        int              i;
1.1       kristaps  508:
1.6       kristaps  509:        for (i = 0; *buf && ! isspace(*buf) && i < 3; i++, buf++)
                    510:                token[i] = *buf;
                    511:
1.9       kristaps  512:        if (i == 3)
1.6       kristaps  513:                return(ROFF_MAX);
                    514:
                    515:        token[i] = 0;
                    516:
1.1       kristaps  517:        /* FIXME: use a table, this is slow but ok for now. */
                    518:
                    519:        /* LINTED */
                    520:        for (i = 0; i < ROFF_MAX; i++)
                    521:                /* LINTED */
1.12      kristaps  522:                if (0 == strcmp(toknames[i], token))
1.1       kristaps  523:                        return((int)i);
1.7       kristaps  524:
1.1       kristaps  525:        return(ROFF_MAX);
                    526: }
                    527:
                    528:
1.20      kristaps  529: static int
1.52      kristaps  530: roffchecksec(struct rofftree *tree, const char *start, int sec)
                    531: {
                    532:        int              prior;
                    533:
                    534:        switch (sec) {
                    535:        case(ROFFSec_SYNOP):
                    536:                if ((prior = ROFFSec_NAME) & tree->asec)
                    537:                        return(1);
                    538:                break;
                    539:        case(ROFFSec_DESC):
                    540:                if ((prior = ROFFSec_SYNOP) & tree->asec)
                    541:                        return(1);
                    542:                break;
                    543:        case(ROFFSec_RETVAL):
                    544:                if ((prior = ROFFSec_DESC) & tree->asec)
                    545:                        return(1);
                    546:                break;
                    547:        case(ROFFSec_ENV):
                    548:                if ((prior = ROFFSec_RETVAL) & tree->asec)
                    549:                        return(1);
                    550:                break;
                    551:        case(ROFFSec_FILES):
                    552:                if ((prior = ROFFSec_ENV) & tree->asec)
                    553:                        return(1);
                    554:                break;
                    555:        case(ROFFSec_EX):
                    556:                if ((prior = ROFFSec_FILES) & tree->asec)
                    557:                        return(1);
                    558:                break;
                    559:        case(ROFFSec_DIAG):
                    560:                if ((prior = ROFFSec_EX) & tree->asec)
                    561:                        return(1);
                    562:                break;
                    563:        case(ROFFSec_ERRS):
                    564:                if ((prior = ROFFSec_DIAG) & tree->asec)
                    565:                        return(1);
                    566:                break;
                    567:        case(ROFFSec_SEEALSO):
                    568:                if ((prior = ROFFSec_ERRS) & tree->asec)
                    569:                        return(1);
                    570:                break;
                    571:        case(ROFFSec_STAND):
                    572:                if ((prior = ROFFSec_SEEALSO) & tree->asec)
                    573:                        return(1);
                    574:                break;
                    575:        case(ROFFSec_HIST):
                    576:                if ((prior = ROFFSec_STAND) & tree->asec)
                    577:                        return(1);
                    578:                break;
                    579:        case(ROFFSec_AUTH):
                    580:                if ((prior = ROFFSec_HIST) & tree->asec)
                    581:                        return(1);
                    582:                break;
                    583:        case(ROFFSec_CAVEATS):
                    584:                if ((prior = ROFFSec_AUTH) & tree->asec)
                    585:                        return(1);
                    586:                break;
                    587:        case(ROFFSec_BUGS):
                    588:                if ((prior = ROFFSec_CAVEATS) & tree->asec)
                    589:                        return(1);
                    590:                break;
                    591:        default:
                    592:                return(1);
                    593:        }
                    594:
                    595:        roff_warn(tree, start, "section violates conventional order");
                    596:        return(1);
                    597: }
                    598:
                    599:
                    600: static int
                    601: roffissec(const char **p)
                    602: {
                    603:
                    604:        assert(*p);
                    605:        if (NULL != *(p + 1)) {
                    606:                if (NULL != *(p + 2))
                    607:                        return(ROFFSec_OTHER);
                    608:                if (0 == strcmp(*p, "RETURN") &&
                    609:                                0 == strcmp(*(p + 1), "VALUES"))
                    610:                        return(ROFFSec_RETVAL);
                    611:                if (0 == strcmp(*p, "SEE") &&
                    612:                                0 == strcmp(*(p + 1), "ALSO"))
                    613:                        return(ROFFSec_SEEALSO);
                    614:                return(ROFFSec_OTHER);
                    615:        }
                    616:
                    617:        if (0 == strcmp(*p, "NAME"))
                    618:                return(ROFFSec_NAME);
                    619:        else if (0 == strcmp(*p, "SYNOPSIS"))
                    620:                return(ROFFSec_SYNOP);
                    621:        else if (0 == strcmp(*p, "DESCRIPTION"))
                    622:                return(ROFFSec_DESC);
                    623:        else if (0 == strcmp(*p, "ENVIRONMENT"))
                    624:                return(ROFFSec_ENV);
                    625:        else if (0 == strcmp(*p, "FILES"))
                    626:                return(ROFFSec_FILES);
                    627:        else if (0 == strcmp(*p, "EXAMPLES"))
                    628:                return(ROFFSec_EX);
                    629:        else if (0 == strcmp(*p, "DIAGNOSTICS"))
                    630:                return(ROFFSec_DIAG);
                    631:        else if (0 == strcmp(*p, "ERRORS"))
                    632:                return(ROFFSec_ERRS);
                    633:        else if (0 == strcmp(*p, "STANDARDS"))
                    634:                return(ROFFSec_STAND);
                    635:        else if (0 == strcmp(*p, "HISTORY"))
                    636:                return(ROFFSec_HIST);
                    637:        else if (0 == strcmp(*p, "AUTHORS"))
                    638:                return(ROFFSec_AUTH);
                    639:        else if (0 == strcmp(*p, "CAVEATS"))
                    640:                return(ROFFSec_CAVEATS);
                    641:        else if (0 == strcmp(*p, "BUGS"))
                    642:                return(ROFFSec_BUGS);
                    643:
                    644:        return(ROFFSec_OTHER);
                    645: }
                    646:
                    647:
                    648: static int
1.51      kristaps  649: roffismsec(const char *p)
                    650: {
                    651:
                    652:        if (0 == strcmp(p, "1"))
                    653:                return(ROFF_MSEC_1);
                    654:        else if (0 == strcmp(p, "2"))
                    655:                return(ROFF_MSEC_2);
                    656:        else if (0 == strcmp(p, "3"))
                    657:                return(ROFF_MSEC_3);
                    658:        else if (0 == strcmp(p, "3p"))
                    659:                return(ROFF_MSEC_3p);
                    660:        else if (0 == strcmp(p, "4"))
                    661:                return(ROFF_MSEC_4);
                    662:        else if (0 == strcmp(p, "5"))
                    663:                return(ROFF_MSEC_5);
                    664:        else if (0 == strcmp(p, "6"))
                    665:                return(ROFF_MSEC_6);
                    666:        else if (0 == strcmp(p, "7"))
                    667:                return(ROFF_MSEC_7);
                    668:        else if (0 == strcmp(p, "8"))
                    669:                return(ROFF_MSEC_8);
                    670:        else if (0 == strcmp(p, "9"))
                    671:                return(ROFF_MSEC_9);
                    672:        else if (0 == strcmp(p, "unass"))
                    673:                return(ROFF_MSEC_UNASS);
                    674:        else if (0 == strcmp(p, "draft"))
                    675:                return(ROFF_MSEC_DRAFT);
                    676:        else if (0 == strcmp(p, "paper"))
                    677:                return(ROFF_MSEC_PAPER);
                    678:
                    679:        return(ROFF_MSEC_MAX);
                    680: }
                    681:
                    682:
                    683: static int
1.53    ! kristaps  684: roffisatt(const char *p)
        !           685: {
        !           686:
        !           687:        assert(p);
        !           688:        if (0 == strcmp(p, "v1"))
        !           689:                return(1);
        !           690:        else if (0 == strcmp(p, "v2"))
        !           691:                return(1);
        !           692:        else if (0 == strcmp(p, "v3"))
        !           693:                return(1);
        !           694:        else if (0 == strcmp(p, "v6"))
        !           695:                return(1);
        !           696:        else if (0 == strcmp(p, "v7"))
        !           697:                return(1);
        !           698:        else if (0 == strcmp(p, "32v"))
        !           699:                return(1);
        !           700:        else if (0 == strcmp(p, "V.1"))
        !           701:                return(1);
        !           702:        else if (0 == strcmp(p, "V.4"))
        !           703:                return(1);
        !           704:
        !           705:        return(0);
        !           706: }
        !           707:
        !           708:
        !           709: static int
1.20      kristaps  710: roffispunct(const char *p)
                    711: {
                    712:
                    713:        if (0 == *p)
                    714:                return(0);
                    715:        if (0 != *(p + 1))
                    716:                return(0);
                    717:
                    718:        switch (*p) {
                    719:        case('{'):
                    720:                /* FALLTHROUGH */
                    721:        case('.'):
                    722:                /* FALLTHROUGH */
                    723:        case(','):
                    724:                /* FALLTHROUGH */
                    725:        case(';'):
                    726:                /* FALLTHROUGH */
                    727:        case(':'):
                    728:                /* FALLTHROUGH */
                    729:        case('?'):
                    730:                /* FALLTHROUGH */
                    731:        case('!'):
                    732:                /* FALLTHROUGH */
                    733:        case('('):
                    734:                /* FALLTHROUGH */
                    735:        case(')'):
                    736:                /* FALLTHROUGH */
                    737:        case('['):
                    738:                /* FALLTHROUGH */
                    739:        case(']'):
                    740:                /* FALLTHROUGH */
                    741:        case('}'):
                    742:                return(1);
                    743:        default:
                    744:                break;
                    745:        }
                    746:
                    747:        return(0);
                    748: }
                    749:
                    750:
1.2       kristaps  751: static int
                    752: rofffindcallable(const char *name)
                    753: {
                    754:        int              c;
                    755:
                    756:        if (ROFF_MAX == (c = rofffindtok(name)))
                    757:                return(ROFF_MAX);
1.8       kristaps  758:        assert(c >= 0 && c < ROFF_MAX);
1.2       kristaps  759:        return(ROFF_CALLABLE & tokens[c].flags ? c : ROFF_MAX);
                    760: }
                    761:
                    762:
1.1       kristaps  763: static struct roffnode *
1.4       kristaps  764: roffnode_new(int tokid, struct rofftree *tree)
1.1       kristaps  765: {
                    766:        struct roffnode *p;
                    767:
1.12      kristaps  768:        if (NULL == (p = malloc(sizeof(struct roffnode))))
                    769:                err(1, "malloc");
1.1       kristaps  770:
                    771:        p->tok = tokid;
                    772:        p->parent = tree->last;
                    773:        tree->last = p;
1.9       kristaps  774:
1.1       kristaps  775:        return(p);
                    776: }
                    777:
                    778:
1.5       kristaps  779: static int
                    780: roffargok(int tokid, int argid)
                    781: {
                    782:        const int       *c;
                    783:
                    784:        if (NULL == (c = tokens[tokid].args))
                    785:                return(0);
                    786:
                    787:        for ( ; ROFF_ARGMAX != *c; c++)
                    788:                if (argid == *c)
                    789:                        return(1);
                    790:
                    791:        return(0);
                    792: }
                    793:
                    794:
1.1       kristaps  795: static void
1.15      kristaps  796: roffnode_free(struct rofftree *tree)
1.1       kristaps  797: {
                    798:        struct roffnode *p;
                    799:
                    800:        assert(tree->last);
                    801:
                    802:        p = tree->last;
                    803:        tree->last = tree->last->parent;
                    804:        free(p);
                    805: }
                    806:
                    807:
1.6       kristaps  808: static int
1.47      kristaps  809: roffspecial(struct rofftree *tree, int tok, const char *start,
                    810:                const int *argc, const char **argv,
                    811:                size_t sz, char **ordp)
1.40      kristaps  812: {
                    813:
1.44      kristaps  814:        switch (tok) {
1.46      kristaps  815:        case (ROFF_At):
                    816:                if (0 == sz)
                    817:                        break;
1.53    ! kristaps  818:                if (roffisatt(*ordp))
1.46      kristaps  819:                        break;
1.52      kristaps  820:                roff_err(tree, *ordp, "invalid `At' arg");
1.46      kristaps  821:                return(0);
1.49      kristaps  822:
1.50      kristaps  823:        case (ROFF_Xr):
1.51      kristaps  824:                if (2 == sz) {
                    825:                        assert(ordp[1]);
                    826:                        if (ROFF_MSEC_MAX != roffismsec(ordp[1]))
                    827:                                break;
                    828:                        roff_warn(tree, start, "invalid `%s' manual "
                    829:                                        "section", toknames[tok]);
                    830:                }
1.50      kristaps  831:                /* FALLTHROUGH */
1.51      kristaps  832:
1.52      kristaps  833:        case (ROFF_Sx):
                    834:                /* FALLTHROUGH*/
1.49      kristaps  835:        case (ROFF_Fn):
                    836:                if (0 != sz)
                    837:                        break;
                    838:                roff_err(tree, start, "`%s' expects at least "
                    839:                                "one arg", toknames[tok]);
                    840:                return(0);
1.46      kristaps  841:
1.44      kristaps  842:        case (ROFF_Nm):
                    843:                if (0 == sz) {
                    844:                        if (0 == tree->name[0]) {
                    845:                                roff_err(tree, start, "`Nm' not set");
                    846:                                return(0);
                    847:                        }
                    848:                        ordp[0] = tree->name;
                    849:                        ordp[1] = NULL;
                    850:                } else if ( ! roffsetname(tree, ordp))
                    851:                        return(0);
                    852:                break;
                    853:
1.48      kristaps  854:        case (ROFF_Rv):
                    855:                /* FALLTHROUGH*/
1.44      kristaps  856:        case (ROFF_Ex):
1.48      kristaps  857:                if (1 == sz)
                    858:                        break;
                    859:                roff_err(tree, start, "`%s' expects one arg",
                    860:                                toknames[tok]);
                    861:                return(0);
1.44      kristaps  862:
                    863:        case (ROFF_Sm):
1.46      kristaps  864:                if (1 != sz) {
1.44      kristaps  865:                        roff_err(tree, start, "`Sm' expects one arg");
                    866:                        return(0);
                    867:                }
                    868:
                    869:                if (0 != strcmp(ordp[0], "on") &&
                    870:                                0 != strcmp(ordp[0], "off")) {
                    871:                        roff_err(tree, start, "`Sm' has invalid argument");
                    872:                        return(0);
                    873:                }
                    874:                break;
1.45      kristaps  875:
                    876:        case (ROFF_Ud):
                    877:                /* FALLTHROUGH */
1.46      kristaps  878:        case (ROFF_Ux):
                    879:                /* FALLTHROUGH */
1.45      kristaps  880:        case (ROFF_Bt):
                    881:                if (0 != sz) {
                    882:                        roff_err(tree, start, "`%s' expects no args",
                    883:                                        toknames[tok]);
                    884:                        return(0);
                    885:                }
                    886:                break;
1.44      kristaps  887:        default:
                    888:                break;
                    889:        }
                    890:
1.50      kristaps  891:        return((*tree->cb.roffspecial)(tree->arg, tok, tree->cur,
                    892:                                argc, argv, (const char **)ordp));
1.40      kristaps  893: }
                    894:
                    895:
                    896: static int
1.51      kristaps  897: roffexit(struct rofftree *tree, int tok)
                    898: {
                    899:
                    900:        assert(tokens[tok].cb);
                    901:        return((*tokens[tok].cb)(tok, tree, NULL, ROFF_EXIT));
                    902: }
                    903:
                    904:
                    905: static int
1.33      kristaps  906: roffcall(struct rofftree *tree, int tok, char **argv)
                    907: {
1.51      kristaps  908:        int              i;
                    909:        enum roffmsec    c;
1.33      kristaps  910:
                    911:        if (NULL == tokens[tok].cb) {
1.51      kristaps  912:                roff_err(tree, *argv, "`%s' is unsupported",
1.33      kristaps  913:                                toknames[tok]);
                    914:                return(0);
                    915:        }
1.51      kristaps  916:        if (tokens[tok].sections && ROFF_MSEC_MAX != tree->section) {
                    917:                i = 0;
                    918:                while (ROFF_MSEC_MAX !=
                    919:                                (c = tokens[tok].sections[i++]))
                    920:                        if (c == tree->section)
                    921:                                break;
                    922:                if (ROFF_MSEC_MAX == c) {
                    923:                        roff_warn(tree, *argv, "`%s' is not a valid "
                    924:                                        "macro in this manual section",
                    925:                                        toknames[tok]);
                    926:                }
                    927:        }
                    928:
                    929:        return((*tokens[tok].cb)(tok, tree, argv, ROFF_ENTER));
1.33      kristaps  930: }
                    931:
                    932:
                    933: static int
1.12      kristaps  934: roffnextopt(const struct rofftree *tree, int tok,
1.18      kristaps  935:                char ***in, char **val)
1.6       kristaps  936: {
1.18      kristaps  937:        char            *arg, **argv;
1.6       kristaps  938:        int              v;
                    939:
                    940:        *val = NULL;
                    941:        argv = *in;
                    942:        assert(argv);
                    943:
                    944:        if (NULL == (arg = *argv))
                    945:                return(-1);
                    946:        if ('-' != *arg)
                    947:                return(-1);
                    948:
1.12      kristaps  949:        if (ROFF_ARGMAX == (v = rofffindarg(arg + 1))) {
                    950:                roff_warn(tree, arg, "argument-like parameter `%s' to "
1.33      kristaps  951:                                "`%s'", arg, toknames[tok]);
1.6       kristaps  952:                return(-1);
1.12      kristaps  953:        }
                    954:
                    955:        if ( ! roffargok(tok, v)) {
                    956:                roff_warn(tree, arg, "invalid argument parameter `%s' to "
                    957:                                "`%s'", tokargnames[v], toknames[tok]);
1.6       kristaps  958:                return(-1);
1.12      kristaps  959:        }
                    960:
                    961:        if ( ! (ROFF_VALUE & tokenargs[v]))
1.6       kristaps  962:                return(v);
                    963:
                    964:        *in = ++argv;
                    965:
1.12      kristaps  966:        if (NULL == *argv) {
                    967:                roff_err(tree, arg, "empty value of `%s' for `%s'",
                    968:                                tokargnames[v], toknames[tok]);
                    969:                return(ROFF_ARGMAX);
                    970:        }
1.6       kristaps  971:
1.12      kristaps  972:        return(v);
1.6       kristaps  973: }
                    974:
                    975:
1.27      kristaps  976: static int
1.31      kristaps  977: roffpurgepunct(struct rofftree *tree, char **argv)
                    978: {
                    979:        int              i;
                    980:
                    981:        i = 0;
                    982:        while (argv[i])
                    983:                i++;
                    984:        assert(i > 0);
                    985:        if ( ! roffispunct(argv[--i]))
                    986:                return(1);
                    987:        while (i >= 0 && roffispunct(argv[i]))
                    988:                i--;
                    989:        i++;
                    990:
                    991:        /* LINTED */
                    992:        while (argv[i])
1.37      kristaps  993:                if ( ! roffdata(tree, 0, argv[i++]))
1.31      kristaps  994:                        return(0);
                    995:        return(1);
                    996: }
                    997:
                    998:
                    999: static int
1.27      kristaps 1000: roffparseopts(struct rofftree *tree, int tok,
                   1001:                char ***args, int *argc, char **argv)
                   1002: {
                   1003:        int              i, c;
                   1004:        char            *v;
                   1005:
                   1006:        i = 0;
                   1007:
                   1008:        while (-1 != (c = roffnextopt(tree, tok, args, &v))) {
                   1009:                if (ROFF_ARGMAX == c)
                   1010:                        return(0);
                   1011:
                   1012:                argc[i] = c;
                   1013:                argv[i] = v;
                   1014:                i++;
                   1015:                *args = *args + 1;
                   1016:        }
                   1017:
                   1018:        argc[i] = ROFF_ARGMAX;
                   1019:        argv[i] = NULL;
                   1020:        return(1);
                   1021: }
                   1022:
                   1023:
1.37      kristaps 1024: static int
                   1025: roffdata(struct rofftree *tree, int space, char *buf)
                   1026: {
                   1027:
1.38      kristaps 1028:        if (0 == *buf)
                   1029:                return(1);
1.37      kristaps 1030:        return((*tree->cb.roffdata)(tree->arg,
                   1031:                                space != 0, tree->cur, buf));
                   1032: }
                   1033:
                   1034:
1.1       kristaps 1035: /* ARGSUSED */
                   1036: static int
                   1037: roff_Dd(ROFFCALL_ARGS)
                   1038: {
1.30      kristaps 1039:        time_t           t;
                   1040:        char            *p, buf[32];
1.1       kristaps 1041:
1.4       kristaps 1042:        if (ROFF_BODY & tree->state) {
                   1043:                assert( ! (ROFF_PRELUDE & tree->state));
                   1044:                assert(ROFF_PRELUDE_Dd & tree->state);
                   1045:                return(roff_text(tok, tree, argv, type));
                   1046:        }
                   1047:
1.1       kristaps 1048:        assert(ROFF_PRELUDE & tree->state);
1.4       kristaps 1049:        assert( ! (ROFF_BODY & tree->state));
                   1050:
1.6       kristaps 1051:        if (ROFF_PRELUDE_Dd & tree->state) {
1.12      kristaps 1052:                roff_err(tree, *argv, "repeated `Dd' in prelude");
1.6       kristaps 1053:                return(0);
                   1054:        } else if (ROFF_PRELUDE_Dt & tree->state) {
1.12      kristaps 1055:                roff_err(tree, *argv, "out-of-order `Dd' in prelude");
1.1       kristaps 1056:                return(0);
                   1057:        }
                   1058:
1.30      kristaps 1059:        assert(NULL == tree->last);
                   1060:
                   1061:        argv++;
                   1062:
                   1063:        if (0 == strcmp(*argv, "$Mdocdate$")) {
                   1064:                t = time(NULL);
                   1065:                if (NULL == localtime_r(&t, &tree->tm))
                   1066:                        err(1, "localtime_r");
                   1067:                tree->state |= ROFF_PRELUDE_Dd;
                   1068:                return(1);
                   1069:        }
                   1070:
                   1071:        /* Build this from Mdocdate or raw date. */
                   1072:
                   1073:        buf[0] = 0;
                   1074:        p = *argv;
                   1075:
                   1076:        if (0 != strcmp(*argv, "$Mdocdate:")) {
                   1077:                while (*argv) {
                   1078:                        if (strlcat(buf, *argv++, sizeof(buf))
                   1079:                                        < sizeof(buf))
                   1080:                                continue;
                   1081:                        roff_err(tree, p, "bad `Dd' date");
                   1082:                        return(0);
                   1083:                }
                   1084:                if (strptime(buf, "%b%d,%Y", &tree->tm)) {
                   1085:                        tree->state |= ROFF_PRELUDE_Dd;
                   1086:                        return(1);
                   1087:                }
                   1088:                roff_err(tree, *argv, "bad `Dd' date");
                   1089:                return(0);
                   1090:        }
                   1091:
                   1092:        argv++;
                   1093:        while (*argv && **argv != '$') {
                   1094:                if (strlcat(buf, *argv++, sizeof(buf))
                   1095:                                >= sizeof(buf)) {
                   1096:                        roff_err(tree, p, "bad `Dd' Mdocdate");
                   1097:                        return(0);
                   1098:                }
                   1099:                if (strlcat(buf, " ", sizeof(buf))
                   1100:                                >= sizeof(buf)) {
                   1101:                        roff_err(tree, p, "bad `Dd' Mdocdate");
                   1102:                        return(0);
                   1103:                }
                   1104:        }
                   1105:        if (NULL == *argv) {
                   1106:                roff_err(tree, p, "bad `Dd' Mdocdate");
                   1107:                return(0);
                   1108:        }
                   1109:
                   1110:        if (NULL == strptime(buf, "%b %d %Y", &tree->tm)) {
                   1111:                roff_err(tree, *argv, "bad `Dd' Mdocdate");
                   1112:                return(0);
                   1113:        }
1.4       kristaps 1114:
1.1       kristaps 1115:        tree->state |= ROFF_PRELUDE_Dd;
                   1116:        return(1);
                   1117: }
                   1118:
                   1119:
                   1120: /* ARGSUSED */
                   1121: static int
                   1122: roff_Dt(ROFFCALL_ARGS)
                   1123: {
                   1124:
1.4       kristaps 1125:        if (ROFF_BODY & tree->state) {
                   1126:                assert( ! (ROFF_PRELUDE & tree->state));
                   1127:                assert(ROFF_PRELUDE_Dt & tree->state);
                   1128:                return(roff_text(tok, tree, argv, type));
                   1129:        }
                   1130:
1.1       kristaps 1131:        assert(ROFF_PRELUDE & tree->state);
1.4       kristaps 1132:        assert( ! (ROFF_BODY & tree->state));
                   1133:
1.6       kristaps 1134:        if ( ! (ROFF_PRELUDE_Dd & tree->state)) {
1.12      kristaps 1135:                roff_err(tree, *argv, "out-of-order `Dt' in prelude");
1.1       kristaps 1136:                return(0);
1.6       kristaps 1137:        } else if (ROFF_PRELUDE_Dt & tree->state) {
1.12      kristaps 1138:                roff_err(tree, *argv, "repeated `Dt' in prelude");
1.6       kristaps 1139:                return(0);
1.1       kristaps 1140:        }
                   1141:
1.30      kristaps 1142:        argv++;
                   1143:        if (NULL == *argv) {
                   1144:                roff_err(tree, *argv, "`Dt' needs document title");
                   1145:                return(0);
                   1146:        } else if (strlcpy(tree->title, *argv, sizeof(tree->title))
                   1147:                        >= sizeof(tree->title)) {
                   1148:                roff_err(tree, *argv, "`Dt' document title too long");
                   1149:                return(0);
                   1150:        }
                   1151:
                   1152:        argv++;
                   1153:        if (NULL == *argv) {
                   1154:                roff_err(tree, *argv, "`Dt' needs section");
                   1155:                return(0);
1.51      kristaps 1156:        }
                   1157:
                   1158:        if (ROFF_MSEC_MAX == (tree->section = roffismsec(*argv))) {
                   1159:                roff_err(tree, *argv, "bad `Dt' section");
1.30      kristaps 1160:                return(0);
                   1161:        }
                   1162:
                   1163:        argv++;
                   1164:        if (NULL == *argv) {
                   1165:                tree->volume[0] = 0;
                   1166:        } else if (strlcpy(tree->volume, *argv, sizeof(tree->volume))
                   1167:                        >= sizeof(tree->volume)) {
                   1168:                roff_err(tree, *argv, "`Dt' volume too long");
                   1169:                return(0);
                   1170:        }
1.4       kristaps 1171:
1.1       kristaps 1172:        assert(NULL == tree->last);
                   1173:        tree->state |= ROFF_PRELUDE_Dt;
                   1174:
                   1175:        return(1);
                   1176: }
                   1177:
                   1178:
1.42      kristaps 1179: static int
                   1180: roffsetname(struct rofftree *tree, char **ordp)
                   1181: {
                   1182:
                   1183:        assert(*ordp);
                   1184:
                   1185:        /* FIXME: not all sections can set this. */
                   1186:
                   1187:        if (NULL != *(ordp + 1)) {
                   1188:                roff_err(tree, *ordp, "too many `Nm' args");
                   1189:                return(0);
                   1190:        }
                   1191:
                   1192:        if (strlcpy(tree->name, *ordp, sizeof(tree->name))
                   1193:                        >= sizeof(tree->name)) {
                   1194:                roff_err(tree, *ordp, "`Nm' arg too long");
                   1195:                return(0);
                   1196:        }
                   1197:
                   1198:        return(1);
                   1199: }
                   1200:
                   1201:
1.1       kristaps 1202: /* ARGSUSED */
1.33      kristaps 1203: static int
                   1204: roff_Ns(ROFFCALL_ARGS)
                   1205: {
                   1206:        int              j, c, first;
1.40      kristaps 1207:        char            *morep[1];
1.33      kristaps 1208:
                   1209:        first = (*argv++ == tree->cur);
1.40      kristaps 1210:        morep[0] = NULL;
1.33      kristaps 1211:
1.47      kristaps 1212:        if ( ! roffspecial(tree, tok, *argv, NULL, NULL, 0, morep))
1.33      kristaps 1213:                return(0);
1.31      kristaps 1214:
1.33      kristaps 1215:        while (*argv) {
                   1216:                if (ROFF_MAX != (c = rofffindcallable(*argv))) {
                   1217:                        if ( ! roffcall(tree, c, argv))
                   1218:                                return(0);
1.31      kristaps 1219:                        break;
                   1220:                }
1.33      kristaps 1221:
                   1222:                if ( ! roffispunct(*argv)) {
1.37      kristaps 1223:                        if ( ! roffdata(tree, 1, *argv++))
                   1224:                                return(0);
                   1225:                        continue;
1.31      kristaps 1226:                }
1.37      kristaps 1227:
1.33      kristaps 1228:                for (j = 0; argv[j]; j++)
                   1229:                        if ( ! roffispunct(argv[j]))
                   1230:                                break;
                   1231:
                   1232:                if (argv[j]) {
1.37      kristaps 1233:                        if ( ! roffdata(tree, 0, *argv++))
                   1234:                                return(0);
                   1235:                        continue;
1.33      kristaps 1236:                }
                   1237:
1.31      kristaps 1238:                break;
                   1239:        }
                   1240:
                   1241:        if ( ! first)
                   1242:                return(1);
1.33      kristaps 1243:
1.31      kristaps 1244:        return(roffpurgepunct(tree, argv));
                   1245: }
                   1246:
                   1247:
                   1248: /* ARGSUSED */
                   1249: static int
1.1       kristaps 1250: roff_Os(ROFFCALL_ARGS)
                   1251: {
1.30      kristaps 1252:        char            *p;
1.1       kristaps 1253:
1.30      kristaps 1254:        if (ROFF_BODY & tree->state) {
1.4       kristaps 1255:                assert( ! (ROFF_PRELUDE & tree->state));
                   1256:                assert(ROFF_PRELUDE_Os & tree->state);
                   1257:                return(roff_text(tok, tree, argv, type));
                   1258:        }
1.1       kristaps 1259:
                   1260:        assert(ROFF_PRELUDE & tree->state);
                   1261:        if ( ! (ROFF_PRELUDE_Dt & tree->state) ||
                   1262:                        ! (ROFF_PRELUDE_Dd & tree->state)) {
1.12      kristaps 1263:                roff_err(tree, *argv, "out-of-order `Os' in prelude");
1.1       kristaps 1264:                return(0);
                   1265:        }
                   1266:
1.30      kristaps 1267:        tree->os[0] = 0;
                   1268:
                   1269:        p = *++argv;
                   1270:
                   1271:        while (*argv) {
                   1272:                if (strlcat(tree->os, *argv++, sizeof(tree->os))
                   1273:                                < sizeof(tree->os))
                   1274:                        continue;
                   1275:                roff_err(tree, p, "`Os' value too long");
                   1276:                return(0);
                   1277:        }
                   1278:
                   1279:        if (0 == tree->os[0])
                   1280:                if (strlcpy(tree->os, "LOCAL", sizeof(tree->os))
                   1281:                                >= sizeof(tree->os)) {
                   1282:                        roff_err(tree, p, "`Os' value too long");
                   1283:                        return(0);
                   1284:                }
1.1       kristaps 1285:
                   1286:        tree->state |= ROFF_PRELUDE_Os;
                   1287:        tree->state &= ~ROFF_PRELUDE;
                   1288:        tree->state |= ROFF_BODY;
                   1289:
1.51      kristaps 1290:        assert(ROFF_MSEC_MAX != tree->section);
                   1291:        assert(0 != tree->title[0]);
                   1292:        assert(0 != tree->os[0]);
                   1293:
1.4       kristaps 1294:        assert(NULL == tree->last);
                   1295:
1.36      kristaps 1296:        return((*tree->cb.roffhead)(tree->arg, &tree->tm,
                   1297:                                tree->os, tree->title, tree->section,
                   1298:                                tree->volume));
1.1       kristaps 1299: }
                   1300:
                   1301:
                   1302: /* ARGSUSED */
                   1303: static int
1.2       kristaps 1304: roff_layout(ROFFCALL_ARGS)
1.1       kristaps 1305: {
1.37      kristaps 1306:        int              i, c, argcp[ROFF_MAXLINEARG];
1.53    ! kristaps 1307:        char            *argvp[ROFF_MAXLINEARG], *p;
1.1       kristaps 1308:
1.52      kristaps 1309:        /*
                   1310:         * The roff_layout function is for multi-line macros.  A layout
                   1311:         * has a start and end point, which is either declared
                   1312:         * explicitly or implicitly.  An explicit start and end is
                   1313:         * embodied by `.Bl' and `.El', with the former being the start
                   1314:         * and the latter being an end.  The `.Sh' and `.Ss' tags, on
                   1315:         * the other hand, are implicit.  The scope of a layout is the
                   1316:         * space between start and end.  Explicit layouts may not close
                   1317:         * out implicit ones and vice versa; implicit layouts may close
                   1318:         * out other implicit layouts.
                   1319:         */
                   1320:
                   1321:        assert( ! (ROFF_CALLABLE & tokens[tok].flags));
                   1322:
1.4       kristaps 1323:        if (ROFF_PRELUDE & tree->state) {
1.27      kristaps 1324:                roff_err(tree, *argv, "bad `%s' in prelude",
1.12      kristaps 1325:                                toknames[tok]);
1.4       kristaps 1326:                return(0);
1.27      kristaps 1327:        } else if (ROFF_EXIT == type) {
1.15      kristaps 1328:                roffnode_free(tree);
1.35      kristaps 1329:                if ( ! (*tree->cb.roffblkbodyout)(tree->arg, tok))
                   1330:                        return(0);
1.15      kristaps 1331:                return((*tree->cb.roffblkout)(tree->arg, tok));
1.2       kristaps 1332:        }
1.1       kristaps 1333:
1.27      kristaps 1334:        assert( ! (ROFF_CALLABLE & tokens[tok].flags));
1.5       kristaps 1335:
1.53    ! kristaps 1336:        p = *argv++;
1.5       kristaps 1337:
1.27      kristaps 1338:        if ( ! roffparseopts(tree, tok, &argv, argcp, argvp))
                   1339:                return(0);
1.4       kristaps 1340:        if (NULL == roffnode_new(tok, tree))
1.2       kristaps 1341:                return(0);
                   1342:
1.27      kristaps 1343:        /*
                   1344:         * Layouts have two parts: the layout body and header.  The
                   1345:         * layout header is the trailing text of the line macro, while
                   1346:         * the layout body is everything following until termination.
1.52      kristaps 1347:         * Example:
                   1348:         *
                   1349:         * .It Fl f ) ;
                   1350:         * Bar.
                   1351:         *
                   1352:         * ...Produces...
                   1353:         *
                   1354:         * <block>
                   1355:         *      <head>
                   1356:         *              <!Fl f!> ;
                   1357:         *      </head>
                   1358:         *
                   1359:         *      <body>
                   1360:         *              Bar.
                   1361:         *      </body>
                   1362:         * </block>
1.27      kristaps 1363:         */
                   1364:
1.50      kristaps 1365:        if ( ! (*tree->cb.roffblkin)(tree->arg, tok, argcp,
                   1366:                                (const char **)argvp))
1.18      kristaps 1367:                return(0);
1.52      kristaps 1368:
                   1369:        /* +++ Begin run macro-specific hooks over argv. */
                   1370:
                   1371:        switch (tok) {
                   1372:        case (ROFF_Sh):
                   1373:                if (NULL == *argv) {
                   1374:                        roff_err(tree, *(argv - 1),
                   1375:                                        "`Sh' expects arguments");
                   1376:                        return(0);
                   1377:                }
                   1378:                tree->csec = roffissec((const char **)argv);
                   1379:                if ( ! (ROFFSec_OTHER & tree->csec) &&
                   1380:                                tree->asec & tree->csec)
                   1381:                        roff_warn(tree, *argv, "section repeated");
                   1382:                if (0 == tree->asec && ! (ROFFSec_NAME & tree->csec)) {
                   1383:                        roff_err(tree, *argv, "`NAME' section "
                   1384:                                        "must be first");
                   1385:                        return(0);
                   1386:                } else if ( ! roffchecksec(tree, *argv, tree->csec))
                   1387:                        return(0);
                   1388:
                   1389:                tree->asec |= tree->csec;
                   1390:                break;
                   1391:        default:
                   1392:                break;
                   1393:        }
                   1394:
                   1395:        /* --- End run macro-specific hooks over argv. */
                   1396:
1.18      kristaps 1397:        if (NULL == *argv)
1.35      kristaps 1398:                return((*tree->cb.roffblkbodyin)
1.50      kristaps 1399:                                (tree->arg, tok, argcp,
                   1400:                                 (const char **)argvp));
1.35      kristaps 1401:
1.50      kristaps 1402:        if ( ! (*tree->cb.roffblkheadin)(tree->arg, tok, argcp,
                   1403:                                (const char **)argvp))
1.2       kristaps 1404:                return(0);
                   1405:
1.27      kristaps 1406:        /*
                   1407:         * If there are no parsable parts, then write remaining tokens
                   1408:         * into the layout header and exit.
                   1409:         */
                   1410:
1.2       kristaps 1411:        if ( ! (ROFF_PARSED & tokens[tok].flags)) {
1.21      kristaps 1412:                i = 0;
1.37      kristaps 1413:                while (*argv)
                   1414:                        if ( ! roffdata(tree, i++, *argv++))
1.14      kristaps 1415:                                return(0);
1.37      kristaps 1416:
1.35      kristaps 1417:                if ( ! (*tree->cb.roffblkheadout)(tree->arg, tok))
                   1418:                        return(0);
1.52      kristaps 1419:                return((*tree->cb.roffblkbodyin)(tree->arg, tok, argcp,
1.50      kristaps 1420:                                 (const char **)argvp));
1.2       kristaps 1421:        }
                   1422:
1.27      kristaps 1423:        /*
                   1424:         * Parsable elements may be in the header (or be the header, for
                   1425:         * that matter).  Follow the regular parsing rules for these.
                   1426:         */
                   1427:
1.21      kristaps 1428:        i = 0;
1.1       kristaps 1429:        while (*argv) {
1.26      kristaps 1430:                if (ROFF_MAX == (c = rofffindcallable(*argv))) {
                   1431:                        assert(tree->arg);
1.37      kristaps 1432:                        if ( ! roffdata(tree, i++, *argv++))
1.8       kristaps 1433:                                return(0);
1.26      kristaps 1434:                        continue;
                   1435:                }
1.33      kristaps 1436:                if ( ! roffcall(tree, c, argv))
1.14      kristaps 1437:                        return(0);
1.26      kristaps 1438:                break;
1.21      kristaps 1439:        }
                   1440:
                   1441:        /*
1.27      kristaps 1442:         * If there's trailing punctuation in the header, then write it
                   1443:         * out now.  Here we mimic the behaviour of a line-dominant text
                   1444:         * macro.
1.21      kristaps 1445:         */
                   1446:
1.35      kristaps 1447:        if (NULL == *argv) {
                   1448:                if ( ! (*tree->cb.roffblkheadout)(tree->arg, tok))
                   1449:                        return(0);
                   1450:                return((*tree->cb.roffblkbodyin)
1.50      kristaps 1451:                                (tree->arg, tok, argcp,
                   1452:                                 (const char **)argvp));
1.35      kristaps 1453:        }
1.21      kristaps 1454:
1.27      kristaps 1455:        /*
                   1456:         * Expensive.  Scan to the end of line then work backwards until
                   1457:         * a token isn't punctuation.
                   1458:         */
                   1459:
1.31      kristaps 1460:        if ( ! roffpurgepunct(tree, argv))
                   1461:                return(0);
1.35      kristaps 1462:        if ( ! (*tree->cb.roffblkheadout)(tree->arg, tok))
                   1463:                return(0);
1.52      kristaps 1464:        return((*tree->cb.roffblkbodyin)(tree->arg,
                   1465:                                tok, argcp, (const char **)argvp));
1.2       kristaps 1466: }
                   1467:
                   1468:
                   1469: /* ARGSUSED */
                   1470: static int
1.40      kristaps 1471: roff_ordered(ROFFCALL_ARGS)
                   1472: {
1.44      kristaps 1473:        int              i, first, c, argcp[ROFF_MAXLINEARG];
                   1474:        char            *ordp[ROFF_MAXLINEARG], *p,
                   1475:                        *argvp[ROFF_MAXLINEARG];
1.40      kristaps 1476:
1.52      kristaps 1477:        /*
                   1478:         * Ordered macros pass their arguments directly to handlers,
                   1479:         * instead of considering it free-form text.  Thus, the
                   1480:         * following macro looks as follows:
                   1481:         *
                   1482:         * .Xr foo 1 ) ,
                   1483:         *
                   1484:         * .Xr arg1 arg2 punctuation
                   1485:         */
                   1486:
1.40      kristaps 1487:        if (ROFF_PRELUDE & tree->state) {
                   1488:                roff_err(tree, *argv, "`%s' disallowed in prelude",
                   1489:                                toknames[tok]);
                   1490:                return(0);
                   1491:        }
                   1492:
                   1493:        first = (*argv == tree->cur);
1.44      kristaps 1494:        p = *argv++;
1.52      kristaps 1495:        ordp[0] = NULL;
1.40      kristaps 1496:
1.44      kristaps 1497:        if ( ! roffparseopts(tree, tok, &argv, argcp, argvp))
                   1498:                return(0);
1.40      kristaps 1499:
1.52      kristaps 1500:        if (NULL == *argv)
1.47      kristaps 1501:                return(roffspecial(tree, tok, p, argcp,
                   1502:                                        (const char **)argvp, 0, ordp));
1.40      kristaps 1503:
                   1504:        i = 0;
                   1505:        while (*argv && i < ROFF_MAXLINEARG) {
1.46      kristaps 1506:                c = ROFF_PARSED & tokens[tok].flags ?
                   1507:                        rofffindcallable(*argv) : ROFF_MAX;
1.42      kristaps 1508:
                   1509:                if (ROFF_MAX == c && ! roffispunct(*argv)) {
                   1510:                        ordp[i++] = *argv++;
                   1511:                        continue;
                   1512:                }
                   1513:                ordp[i] = NULL;
                   1514:
                   1515:                if (ROFF_MAX == c)
                   1516:                        break;
                   1517:
1.47      kristaps 1518:                if ( ! roffspecial(tree, tok, p, argcp,
                   1519:                                        (const char **)argvp,
                   1520:                                        (size_t)i, ordp))
1.42      kristaps 1521:                        return(0);
1.40      kristaps 1522:
1.46      kristaps 1523:                return(roffcall(tree, c, argv));
1.40      kristaps 1524:        }
                   1525:
1.42      kristaps 1526:        assert(i != ROFF_MAXLINEARG);
1.40      kristaps 1527:        ordp[i] = NULL;
                   1528:
1.47      kristaps 1529:        if ( ! roffspecial(tree, tok, p, argcp,
                   1530:                                (const char**)argvp,
                   1531:                                (size_t)i, ordp))
1.40      kristaps 1532:                return(0);
                   1533:
                   1534:        /* FIXME: error if there's stuff after the punctuation. */
                   1535:
                   1536:        if ( ! first || NULL == *argv)
                   1537:                return(1);
                   1538:
                   1539:        return(roffpurgepunct(tree, argv));
                   1540: }
                   1541:
                   1542:
                   1543: /* ARGSUSED */
                   1544: static int
1.2       kristaps 1545: roff_text(ROFFCALL_ARGS)
                   1546: {
1.37      kristaps 1547:        int              i, j, first, c, argcp[ROFF_MAXLINEARG];
                   1548:        char            *argvp[ROFF_MAXLINEARG];
1.2       kristaps 1549:
1.52      kristaps 1550:        /*
                   1551:         * Text macros are similar to special tokens, except that
                   1552:         * arguments are instead flushed as pure data: we're only
                   1553:         * concerned with the macro and its arguments.  Example:
                   1554:         *
                   1555:         * .Fl v W f ;
                   1556:         *
                   1557:         * ...Produces...
                   1558:         *
                   1559:         * <fl> v W f </fl> ;
                   1560:         */
                   1561:
1.4       kristaps 1562:        if (ROFF_PRELUDE & tree->state) {
1.12      kristaps 1563:                roff_err(tree, *argv, "`%s' disallowed in prelude",
                   1564:                                toknames[tok]);
1.4       kristaps 1565:                return(0);
                   1566:        }
                   1567:
1.27      kristaps 1568:        first = (*argv == tree->cur);
1.12      kristaps 1569:        argv++;
1.5       kristaps 1570:
1.27      kristaps 1571:        if ( ! roffparseopts(tree, tok, &argv, argcp, argvp))
                   1572:                return(0);
1.50      kristaps 1573:        if ( ! (*tree->cb.roffin)(tree->arg, tok, argcp,
                   1574:                                (const char **)argvp))
1.2       kristaps 1575:                return(0);
1.27      kristaps 1576:        if (NULL == *argv)
                   1577:                return((*tree->cb.roffout)(tree->arg, tok));
1.1       kristaps 1578:
1.2       kristaps 1579:        if ( ! (ROFF_PARSED & tokens[tok].flags)) {
1.21      kristaps 1580:                i = 0;
1.37      kristaps 1581:                while (*argv)
                   1582:                        if ( ! roffdata(tree, i++, *argv++))
1.14      kristaps 1583:                                return(0);
1.37      kristaps 1584:
1.15      kristaps 1585:                return((*tree->cb.roffout)(tree->arg, tok));
1.2       kristaps 1586:        }
1.1       kristaps 1587:
1.27      kristaps 1588:        /*
                   1589:         * Deal with punctuation.  Ugly.  Work ahead until we encounter
                   1590:         * terminating punctuation.  If we encounter it and all
                   1591:         * subsequent tokens are punctuation, then stop processing (the
                   1592:         * line-dominant macro will print these tokens after closure).
1.49      kristaps 1593:         * If the punctuation is followed by non-punctuation, then close
                   1594:         * and re-open our scope, then continue.
1.27      kristaps 1595:         */
                   1596:
1.21      kristaps 1597:        i = 0;
1.2       kristaps 1598:        while (*argv) {
1.33      kristaps 1599:                if (ROFF_MAX != (c = rofffindcallable(*argv))) {
                   1600:                        if ( ! (ROFF_LSCOPE & tokens[tok].flags))
                   1601:                                if ( ! (*tree->cb.roffout)(tree->arg, tok))
                   1602:                                        return(0);
                   1603:
                   1604:                        if ( ! roffcall(tree, c, argv))
                   1605:                                return(0);
                   1606:
                   1607:                        if (ROFF_LSCOPE & tokens[tok].flags)
                   1608:                                if ( ! (*tree->cb.roffout)(tree->arg, tok))
1.27      kristaps 1609:                                        return(0);
1.33      kristaps 1610:
                   1611:                        break;
                   1612:                }
1.21      kristaps 1613:
1.33      kristaps 1614:                if ( ! roffispunct(*argv)) {
1.37      kristaps 1615:                        if ( ! roffdata(tree, i++, *argv++))
1.33      kristaps 1616:                                return(0);
                   1617:                        continue;
                   1618:                }
1.27      kristaps 1619:
1.33      kristaps 1620:                i = 1;
                   1621:                for (j = 0; argv[j]; j++)
                   1622:                        if ( ! roffispunct(argv[j]))
                   1623:                                break;
1.27      kristaps 1624:
1.33      kristaps 1625:                if (argv[j]) {
1.49      kristaps 1626:                        if (ROFF_LSCOPE & tokens[tok].flags) {
                   1627:                                if ( ! roffdata(tree, 0, *argv++))
                   1628:                                        return(0);
                   1629:                                continue;
                   1630:                        }
                   1631:                        if ( ! (*tree->cb.roffout)(tree->arg, tok))
                   1632:                                return(0);
1.37      kristaps 1633:                        if ( ! roffdata(tree, 0, *argv++))
1.27      kristaps 1634:                                return(0);
1.50      kristaps 1635:                        if ( ! (*tree->cb.roffin)(tree->arg, tok,
                   1636:                                                argcp,
                   1637:                                                (const char **)argvp))
1.49      kristaps 1638:                                return(0);
                   1639:
                   1640:                        i = 0;
1.33      kristaps 1641:                        continue;
1.8       kristaps 1642:                }
1.20      kristaps 1643:
1.33      kristaps 1644:                if ( ! (*tree->cb.roffout)(tree->arg, tok))
1.14      kristaps 1645:                        return(0);
1.20      kristaps 1646:                break;
1.14      kristaps 1647:        }
1.12      kristaps 1648:
1.27      kristaps 1649:        if (NULL == *argv)
                   1650:                return((*tree->cb.roffout)(tree->arg, tok));
                   1651:        if ( ! first)
                   1652:                return(1);
1.21      kristaps 1653:
1.31      kristaps 1654:        return(roffpurgepunct(tree, argv));
1.6       kristaps 1655: }
                   1656:
                   1657:
1.9       kristaps 1658: /* ARGSUSED */
1.6       kristaps 1659: static int
1.27      kristaps 1660: roff_noop(ROFFCALL_ARGS)
1.6       kristaps 1661: {
                   1662:
                   1663:        return(1);
1.1       kristaps 1664: }
1.9       kristaps 1665:
                   1666:
                   1667: /* ARGSUSED */
                   1668: static int
1.27      kristaps 1669: roff_depr(ROFFCALL_ARGS)
1.9       kristaps 1670: {
                   1671:
1.27      kristaps 1672:        roff_err(tree, *argv, "`%s' is deprecated", toknames[tok]);
                   1673:        return(0);
1.11      kristaps 1674: }
1.12      kristaps 1675:
                   1676:
                   1677: static void
                   1678: roff_warn(const struct rofftree *tree, const char *pos, char *fmt, ...)
                   1679: {
                   1680:        va_list          ap;
                   1681:        char             buf[128];
                   1682:
                   1683:        va_start(ap, fmt);
                   1684:        (void)vsnprintf(buf, sizeof(buf), fmt, ap);
                   1685:        va_end(ap);
                   1686:
1.15      kristaps 1687:        (*tree->cb.roffmsg)(tree->arg,
                   1688:                        ROFF_WARN, tree->cur, pos, buf);
1.12      kristaps 1689: }
                   1690:
                   1691:
                   1692: static void
                   1693: roff_err(const struct rofftree *tree, const char *pos, char *fmt, ...)
                   1694: {
                   1695:        va_list          ap;
                   1696:        char             buf[128];
                   1697:
                   1698:        va_start(ap, fmt);
                   1699:        (void)vsnprintf(buf, sizeof(buf), fmt, ap);
                   1700:        va_end(ap);
                   1701:
1.15      kristaps 1702:        (*tree->cb.roffmsg)(tree->arg,
                   1703:                        ROFF_ERROR, tree->cur, pos, buf);
1.12      kristaps 1704: }
1.33      kristaps 1705:

CVSweb