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

Annotation of mandoc/roff.c, Revision 1.52

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

CVSweb