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

Annotation of mandoc/macro.c, Revision 1.11

1.11    ! kristaps    1: /* $Id: macro.c,v 1.10 2008/12/29 12:19:41 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.2       kristaps   19: #include <assert.h>
                     20: #include <ctype.h>
1.1       kristaps   21: #include <stdlib.h>
1.2       kristaps   22: #include <stdio.h>
1.5       kristaps   23: #include <string.h>
1.11    ! kristaps   24: #ifdef __linux__
        !            25: #include <time.h>
        !            26: #endif
1.2       kristaps   27:
                     28: #include "private.h"
                     29:
1.10      kristaps   30: /* FIXME: maxlineargs should be per LINE, no per TOKEN. */
                     31:
1.3       kristaps   32: #define        _CC(p)  ((const char **)p)
1.2       kristaps   33:
1.6       kristaps   34: static int       scope_rewind_exp(struct mdoc *, int, int, int);
1.7       kristaps   35: static int       scope_rewind_imp(struct mdoc *, int, int);
1.3       kristaps   36: static int       append_text(struct mdoc *, int,
                     37:                        int, int, char *[]);
1.10      kristaps   38: static int       append_const(struct mdoc *, int, int, int, char *[]);
1.6       kristaps   39: static int       append_scoped(struct mdoc *, int, int, int,
                     40:                        const char *[], int, const struct mdoc_arg *);
1.7       kristaps   41: static int       append_delims(struct mdoc *, int, int *, char *);
1.6       kristaps   42:
                     43:
                     44: static int
1.7       kristaps   45: append_delims(struct mdoc *mdoc, int tok, int *pos, char *buf)
1.6       kristaps   46: {
1.7       kristaps   47:        int              c, lastarg;
                     48:        char            *p;
1.6       kristaps   49:
                     50:        if (0 == buf[*pos])
1.7       kristaps   51:                return(1);
1.6       kristaps   52:
1.8       kristaps   53:        mdoc_msg(mdoc, *pos, "`%s' flushing punctuation",
                     54:                        mdoc_macronames[tok]);
1.6       kristaps   55:
1.7       kristaps   56:        for (;;) {
                     57:                lastarg = *pos;
                     58:                c = mdoc_args(mdoc, tok, pos, buf, 0, &p);
                     59:                if (ARGS_ERROR == c)
                     60:                        return(0);
                     61:                else if (ARGS_EOLN == c)
                     62:                        break;
                     63:                assert(mdoc_isdelim(p));
                     64:                mdoc_word_alloc(mdoc, lastarg, p);
1.6       kristaps   65:        }
                     66:
                     67:        return(1);
                     68: }
                     69:
                     70:
                     71: static int
1.7       kristaps   72: scope_rewind_imp(struct mdoc *mdoc, int ppos, int tok)
1.6       kristaps   73: {
                     74:        struct mdoc_node *n;
1.7       kristaps   75:        int               t;
                     76:
                     77:        n = mdoc->last ? mdoc->last->parent : NULL;
1.6       kristaps   78:
                     79:        /* LINTED */
1.7       kristaps   80:        for ( ; n; n = n->parent) {
1.6       kristaps   81:                if (MDOC_BLOCK != n->type)
                     82:                        continue;
1.7       kristaps   83:                if (tok == (t = n->data.block.tok))
1.6       kristaps   84:                        break;
1.7       kristaps   85:                if ( ! (MDOC_EXPLICIT & mdoc_macros[t].flags))
                     86:                        continue;
1.6       kristaps   87:                return(mdoc_err(mdoc, tok, ppos, ERR_SCOPE_BREAK));
                     88:        }
                     89:
1.7       kristaps   90:        if (n) {
                     91:                mdoc->last = n;
                     92:                mdoc_msg(mdoc, ppos, "scope: rewound implicit `%s'",
                     93:                                mdoc_macronames[tok]);
                     94:                return(1);
                     95:        }
1.6       kristaps   96:
1.7       kristaps   97:        mdoc_msg(mdoc, ppos, "scope: new implicit `%s'",
                     98:                        mdoc_macronames[tok]);
1.6       kristaps   99:        return(1);
                    100: }
                    101:
                    102:
                    103: static int
1.7       kristaps  104: scope_rewind_exp(struct mdoc *mdoc, int ppos, int tok, int dst)
1.6       kristaps  105: {
1.7       kristaps  106:        struct mdoc_node *n;
1.6       kristaps  107:
1.7       kristaps  108:        assert(mdoc->last);
1.6       kristaps  109:
1.7       kristaps  110:        /* LINTED */
                    111:        for (n = mdoc->last->parent; n; n = n->parent) {
                    112:                if (MDOC_BLOCK != n->type)
                    113:                        continue;
                    114:                if (dst == n->data.block.tok)
                    115:                        break;
                    116:                return(mdoc_err(mdoc, tok, ppos, ERR_SCOPE_BREAK));
1.6       kristaps  117:        }
                    118:
1.7       kristaps  119:        if (NULL == (mdoc->last = n))
                    120:                return(mdoc_err(mdoc, tok, ppos, ERR_SCOPE_NOCTX));
1.6       kristaps  121:
1.7       kristaps  122:        mdoc_msg(mdoc, ppos, "scope: rewound explicit `%s' to `%s'",
                    123:                        mdoc_macronames[tok], mdoc_macronames[dst]);
1.2       kristaps  124:
1.1       kristaps  125:        return(1);
                    126: }
                    127:
1.2       kristaps  128:
                    129: static int
1.6       kristaps  130: append_scoped(struct mdoc *mdoc, int tok, int pos,
                    131:                int sz, const char *args[],
                    132:                int argc, const struct mdoc_arg *argv)
1.2       kristaps  133: {
1.7       kristaps  134:        enum mdoc_sec     sec;
                    135:        struct mdoc_node *node;
1.5       kristaps  136:
1.4       kristaps  137:        switch (tok) {
                    138:         /* ======= ADD MORE MACRO CHECKS BELOW. ======= */
                    139:        case (MDOC_Sh):
1.6       kristaps  140:                if (0 == sz)
                    141:                        return(mdoc_err(mdoc, tok, pos, ERR_ARGS_GE1));
                    142:
1.5       kristaps  143:                sec = mdoc_atosec((size_t)sz, _CC(args));
                    144:                if (SEC_CUSTOM != sec && sec < mdoc->sec_lastn)
                    145:                        if ( ! mdoc_warn(mdoc, tok, pos, WARN_SEC_OO))
                    146:                                return(0);
                    147:
                    148:                if (SEC_BODY == mdoc->sec_last && SEC_NAME != sec)
                    149:                        return(mdoc_err(mdoc, tok, pos, ERR_SEC_NAME));
                    150:
                    151:                if (SEC_CUSTOM != sec)
                    152:                        mdoc->sec_lastn = sec;
                    153:                mdoc->sec_last = sec;
1.4       kristaps  154:                break;
1.6       kristaps  155:
1.4       kristaps  156:        case (MDOC_Ss):
1.6       kristaps  157:                if (0 == sz)
                    158:                        return(mdoc_err(mdoc, tok, pos, ERR_ARGS_GE1));
                    159:                break;
1.7       kristaps  160:
                    161:        case (MDOC_Bd):
                    162:                assert(mdoc->last);
1.9       kristaps  163:                node = mdoc->last->parent;
                    164:                /* LINTED */
                    165:                for ( ; node; node = node->parent) {
1.7       kristaps  166:                        if (node->type != MDOC_BLOCK)
                    167:                                continue;
                    168:                        if (node->data.block.tok != MDOC_Bd)
                    169:                                continue;
                    170:                        return(mdoc_err(mdoc, tok, pos, ERR_SCOPE_NONEST));
                    171:                }
                    172:                break;
1.6       kristaps  173:
                    174:        case (MDOC_Bl):
1.4       kristaps  175:                break;
1.6       kristaps  176:
1.4       kristaps  177:         /* ======= ADD MORE MACRO CHECKS ABOVE. ======= */
                    178:        default:
                    179:                abort();
                    180:                /* NOTREACHED */
                    181:        }
                    182:
1.6       kristaps  183:        mdoc_block_alloc(mdoc, pos, tok, (size_t)argc, argv);
1.3       kristaps  184:        mdoc_head_alloc(mdoc, pos, tok, (size_t)sz, _CC(args));
1.2       kristaps  185:        mdoc_body_alloc(mdoc, pos, tok);
                    186:        return(1);
                    187: }
                    188:
                    189:
1.1       kristaps  190: static int
1.10      kristaps  191: append_const(struct mdoc *mdoc, int tok,
                    192:                int pos, int sz, char *args[])
                    193: {
                    194:
                    195:        switch (tok) {
                    196:         /* ======= ADD MORE MACRO CHECKS BELOW. ======= */
1.11    ! kristaps  197:        case (MDOC_At):
        !           198:                /* This needs special handling. */
        !           199:                if (0 == sz)
        !           200:                        break;
        !           201:                else if (sz > 2)
        !           202:                        return(mdoc_err(mdoc, tok, pos, ERR_ARGS_LE2));
        !           203:
        !           204:                if (ATT_DEFAULT != mdoc_atoatt(args[0])) {
        !           205:                        mdoc_elem_alloc(mdoc, pos, tok, 0,
        !           206:                                        NULL, 1, _CC(&args[0]));
        !           207:                } else {
        !           208:                        mdoc_elem_alloc(mdoc, pos, tok,
        !           209:                                        0, NULL, 0, NULL);
        !           210:                        if (mdoc_isdelim(args[0]))
        !           211:                                return(mdoc_err(mdoc, tok, pos, ERR_SYNTAX_NOPUNCT));
        !           212:                        mdoc_word_alloc(mdoc, pos, args[0]);
        !           213:                }
        !           214:
        !           215:                if (1 == sz)
        !           216:                        return(1);
        !           217:                if (mdoc_isdelim(args[1]))
        !           218:                        return(mdoc_err(mdoc, tok, pos, ERR_SYNTAX_NOPUNCT));
        !           219:                mdoc_word_alloc(mdoc, pos, args[1]);
        !           220:                return(1);
        !           221:
1.10      kristaps  222:        case (MDOC_Bx):
                    223:                /* FALLTHROUGH */
                    224:        case (MDOC_Bsx):
                    225:                /* FALLTHROUGH */
                    226:        case (MDOC_Os):
                    227:                /* FALLTHROUGH */
                    228:        case (MDOC_Fx):
                    229:                /* FALLTHROUGH */
                    230:        case (MDOC_Nx):
                    231:                assert(sz <= 1);
                    232:                break;
                    233:
                    234:        case (MDOC_Ux):
                    235:                assert(0 == sz);
                    236:                break;
                    237:
1.11    ! kristaps  238:        case (MDOC_Bt):
        !           239:                /* FALLTHROUGH */
        !           240:        case (MDOC_Ud):
        !           241:                if (0 == sz)
        !           242:                        break;
        !           243:                return(mdoc_err(mdoc, tok, pos, ERR_ARGS_EQ0));
        !           244:
1.10      kristaps  245:         /* ======= ADD MORE MACRO CHECKS ABOVE. ======= */
                    246:        default:
                    247:                abort();
                    248:                /* NOTREACHED */
                    249:        }
                    250:
                    251:        mdoc_elem_alloc(mdoc, pos, tok, 0, NULL, (size_t)sz, _CC(args));
                    252:        return(1);
                    253: }
                    254:
                    255:
                    256: static int
1.3       kristaps  257: append_text(struct mdoc *mdoc, int tok,
                    258:                int pos, int sz, char *args[])
1.1       kristaps  259: {
                    260:
1.3       kristaps  261:        assert(sz >= 0);
1.2       kristaps  262:        args[sz] = NULL;
                    263:
                    264:        switch (tok) {
1.4       kristaps  265:         /* ======= ADD MORE MACRO CHECKS BELOW. ======= */
1.7       kristaps  266:        case (MDOC_Pp):
                    267:                if (0 == sz)
                    268:                        break;
                    269:                if ( ! mdoc_warn(mdoc, tok, pos, WARN_ARGS_EQ0))
                    270:                        return(0);
                    271:                break;
                    272:
1.2       kristaps  273:        case (MDOC_Ft):
                    274:                /* FALLTHROUGH */
                    275:        case (MDOC_Li):
                    276:                /* FALLTHROUGH */
                    277:        case (MDOC_Ms):
                    278:                /* FALLTHROUGH */
                    279:        case (MDOC_Pa):
                    280:                /* FALLTHROUGH */
                    281:        case (MDOC_Tn):
1.4       kristaps  282:                if (0 < sz)
                    283:                        break;
                    284:                if ( ! mdoc_warn(mdoc, tok, pos, WARN_ARGS_GE1))
1.1       kristaps  285:                        return(0);
1.4       kristaps  286:                break;
1.7       kristaps  287:
1.2       kristaps  288:        case (MDOC_Ar):
                    289:                /* FALLTHROUGH */
                    290:        case (MDOC_Cm):
                    291:                /* FALLTHROUGH */
                    292:        case (MDOC_Fl):
1.7       kristaps  293:                /* These can have no arguments. */
1.4       kristaps  294:                break;
1.7       kristaps  295:
1.2       kristaps  296:        case (MDOC_Ad):
                    297:                /* FALLTHROUGH */
                    298:        case (MDOC_Em):
                    299:                /* FALLTHROUGH */
                    300:        case (MDOC_Er):
                    301:                /* FALLTHROUGH */
                    302:        case (MDOC_Ev):
                    303:                /* FALLTHROUGH */
                    304:        case (MDOC_Fa):
                    305:                /* FALLTHROUGH */
                    306:        case (MDOC_Dv):
                    307:                /* FALLTHROUGH */
                    308:        case (MDOC_Ic):
                    309:                /* FALLTHROUGH */
                    310:        case (MDOC_Va):
                    311:                /* FALLTHROUGH */
                    312:        case (MDOC_Vt):
1.4       kristaps  313:                if (0 < sz)
                    314:                        break;
                    315:                return(mdoc_err(mdoc, tok, pos, ERR_ARGS_GE1));
                    316:         /* ======= ADD MORE MACRO CHECKS ABOVE. ======= */
1.2       kristaps  317:        default:
1.4       kristaps  318:                abort();
                    319:                /* NOTREACHED */
1.2       kristaps  320:        }
                    321:
1.7       kristaps  322:        mdoc_elem_alloc(mdoc, pos, tok, 0, NULL, (size_t)sz, _CC(args));
1.4       kristaps  323:        return(1);
1.2       kristaps  324: }
                    325:
1.1       kristaps  326:
1.2       kristaps  327: int
1.5       kristaps  328: macro_text(MACRO_PROT_ARGS)
1.2       kristaps  329: {
1.7       kristaps  330:        int               lastarg, lastpunct, c, j;
1.2       kristaps  331:        char             *args[MDOC_LINEARG_MAX], *p;
1.1       kristaps  332:
1.5       kristaps  333:        if (SEC_PROLOGUE == mdoc->sec_lastn)
                    334:                return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE));
                    335:
1.7       kristaps  336:        /* Token pre-processing.  */
                    337:
                    338:        switch (tok) {
                    339:        case (MDOC_Pp):
                    340:                /* `.Pp' ignored when following `.Sh' or `.Ss'. */
                    341:                assert(mdoc->last);
                    342:                if (MDOC_BODY != mdoc->last->type)
                    343:                        break;
                    344:                switch (mdoc->last->data.body.tok) {
                    345:                case (MDOC_Ss):
                    346:                        /* FALLTHROUGH */
                    347:                case (MDOC_Sh):
                    348:                        if ( ! mdoc_warn(mdoc, tok, ppos, WARN_IGN_AFTER_BLK))
                    349:                                return(0);
                    350:                        return(1);
                    351:                default:
                    352:                        break;
                    353:                }
                    354:                break;
                    355:        default:
                    356:                break;
                    357:        }
                    358:
                    359:        /* Process line parameters. */
                    360:
                    361:        j = 0;
                    362:        lastarg = ppos;
                    363:        lastpunct = 0;
                    364:
1.2       kristaps  365: again:
1.7       kristaps  366:        if (j == MDOC_LINEARG_MAX)
                    367:                return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY));
                    368:
                    369:        /*
                    370:         * Parse out the next argument, unquoted and unescaped.   If
                    371:         * we're a word (which may be punctuation followed eventually by
                    372:         * a real word), then fall into checking for callables.  If
                    373:         * only punctuation remains and we're the first, then flush
                    374:         * arguments, punctuation and exit; else, return to the caller.
                    375:         */
                    376:
1.6       kristaps  377:        lastarg = *pos;
1.2       kristaps  378:
1.7       kristaps  379:        switch (mdoc_args(mdoc, tok, pos, buf, ARGS_DELIM, &args[j])) {
                    380:        case (ARGS_ERROR):
1.2       kristaps  381:                return(0);
1.7       kristaps  382:        case (ARGS_WORD):
                    383:                break;
                    384:        case (ARGS_PUNCT):
                    385:                if ( ! lastpunct && ! append_text(mdoc, tok, ppos, j, args))
                    386:                        return(0);
1.8       kristaps  387:                if (ppos > 1)
                    388:                        return(1);
1.7       kristaps  389:                return(append_delims(mdoc, tok, pos, buf));
                    390:        case (ARGS_EOLN):
1.8       kristaps  391:                if (lastpunct)
                    392:                        return(1);
1.7       kristaps  393:                return(append_text(mdoc, tok, ppos, j, args));
                    394:        default:
                    395:                abort();
                    396:                /* NOTREACHED */
                    397:        }
1.2       kristaps  398:
1.7       kristaps  399:        /*
                    400:         * Command found.  First flush out arguments, then call the
                    401:         * command.  If we're the line macro when it exits, flush
                    402:         * terminal punctuation.
                    403:         */
1.2       kristaps  404:
                    405:        if (MDOC_MAX != (c = mdoc_find(mdoc, args[j]))) {
1.7       kristaps  406:                if ( ! lastpunct && ! append_text(mdoc, tok, ppos, j, args))
                    407:                        return(0);
                    408:                if ( ! mdoc_macro(mdoc, c, lastarg, pos, buf))
                    409:                        return(0);
                    410:                if (ppos > 1)
                    411:                        return(1);
                    412:                return(append_delims(mdoc, tok, pos, buf));
1.2       kristaps  413:        }
                    414:
1.7       kristaps  415:        /* Word/non-term-punctuation found. */
1.2       kristaps  416:
1.4       kristaps  417:        if ( ! mdoc_isdelim(args[j])) {
1.7       kristaps  418:                /* Words are appended to the array of arguments. */
1.2       kristaps  419:                j++;
1.8       kristaps  420:                lastpunct = 0;
1.2       kristaps  421:                goto again;
                    422:        }
                    423:
1.7       kristaps  424:        /*
                    425:         * For punctuation, flush all collected words, then flush
                    426:         * punctuation, then start collecting again.   Of course, this
                    427:         * is non-terminal punctuation.
                    428:         */
1.2       kristaps  429:
1.7       kristaps  430:        p = args[j];
                    431:        if ( ! lastpunct && ! append_text(mdoc, tok, ppos, j, args))
                    432:                return(0);
1.2       kristaps  433:
1.7       kristaps  434:        mdoc_word_alloc(mdoc, lastarg, p);
                    435:        j = 0;
1.2       kristaps  436:        lastpunct = 1;
                    437:
                    438:        goto again;
                    439:        /* NOTREACHED */
                    440: }
1.1       kristaps  441:
                    442:
1.2       kristaps  443: int
1.5       kristaps  444: macro_prologue_dtitle(MACRO_PROT_ARGS)
                    445: {
1.7       kristaps  446:        int               lastarg, j;
1.5       kristaps  447:        char             *args[MDOC_LINEARG_MAX];
                    448:
                    449:        if (SEC_PROLOGUE != mdoc->sec_lastn)
                    450:                return(mdoc_err(mdoc, tok, ppos, ERR_SEC_NPROLOGUE));
                    451:        if (0 == mdoc->meta.date)
                    452:                return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE_OO));
                    453:        if (mdoc->meta.title[0])
                    454:                return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE_REP));
                    455:
                    456:        j = -1;
1.7       kristaps  457:        lastarg = ppos;
1.5       kristaps  458:
                    459: again:
1.6       kristaps  460:        if (j == MDOC_LINEARG_MAX)
                    461:                return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY));
1.5       kristaps  462:
1.7       kristaps  463:        lastarg = *pos;
                    464:
                    465:        switch (mdoc_args(mdoc, tok, pos, buf, 0, &args[++j])) {
                    466:        case (ARGS_EOLN):
1.5       kristaps  467:                if (mdoc->meta.title)
                    468:                        return(1);
                    469:                if ( ! mdoc_warn(mdoc, tok, ppos, WARN_ARGS_GE1))
                    470:                        return(0);
                    471:                (void)xstrlcpy(mdoc->meta.title,
                    472:                                "UNTITLED", META_TITLE_SZ);
                    473:                return(1);
1.7       kristaps  474:        case (ARGS_ERROR):
1.5       kristaps  475:                return(0);
1.7       kristaps  476:        default:
                    477:                break;
                    478:        }
                    479:
1.5       kristaps  480:        if (MDOC_MAX != mdoc_find(mdoc, args[j]) && ! mdoc_warn
                    481:                        (mdoc, tok, lastarg, WARN_SYNTAX_MACLIKE))
                    482:                return(0);
                    483:
                    484:        if (0 == j) {
                    485:                if (xstrlcpy(mdoc->meta.title, args[0], META_TITLE_SZ))
                    486:                        goto again;
1.7       kristaps  487:                return(mdoc_err(mdoc, tok, lastarg, ERR_SYNTAX_ARGFORM));
1.5       kristaps  488:
                    489:        } else if (1 == j) {
                    490:                mdoc->meta.msec = mdoc_atomsec(args[1]);
                    491:                if (MSEC_DEFAULT != mdoc->meta.msec)
                    492:                        goto again;
1.7       kristaps  493:                return(mdoc_err(mdoc, tok, -1, ERR_SYNTAX_ARGFORM));
1.5       kristaps  494:
                    495:        } else if (2 == j) {
                    496:                mdoc->meta.vol = mdoc_atovol(args[2]);
                    497:                if (VOL_DEFAULT != mdoc->meta.vol)
                    498:                        goto again;
                    499:                mdoc->meta.arch = mdoc_atoarch(args[2]);
                    500:                if (ARCH_DEFAULT != mdoc->meta.arch)
                    501:                        goto again;
1.7       kristaps  502:                return(mdoc_err(mdoc, tok, lastarg, ERR_SYNTAX_ARGFORM));
1.5       kristaps  503:        }
                    504:
                    505:        return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY));
                    506: }
                    507:
                    508:
                    509: int
1.6       kristaps  510: macro_prologue_os(MACRO_PROT_ARGS)
                    511: {
1.7       kristaps  512:        int               lastarg, j;
1.6       kristaps  513:        char             *args[MDOC_LINEARG_MAX];
                    514:
                    515:        if (SEC_PROLOGUE != mdoc->sec_lastn)
                    516:                return(mdoc_err(mdoc, tok, ppos, ERR_SEC_NPROLOGUE));
                    517:        if (0 == mdoc->meta.title[0])
                    518:                return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE_OO));
                    519:        if (mdoc->meta.os[0])
                    520:                return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE_REP));
                    521:
                    522:        j = -1;
1.7       kristaps  523:        lastarg = ppos;
1.6       kristaps  524:
                    525: again:
                    526:        if (j == MDOC_LINEARG_MAX)
                    527:                return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY));
                    528:
1.7       kristaps  529:        lastarg = *pos;
                    530:
                    531:        switch (mdoc_args(mdoc, tok, pos, buf,
                    532:                                ARGS_QUOTED, &args[++j])) {
                    533:        case (ARGS_EOLN):
1.6       kristaps  534:                mdoc->sec_lastn = mdoc->sec_last = SEC_BODY;
                    535:                return(1);
1.7       kristaps  536:        case (ARGS_ERROR):
1.6       kristaps  537:                return(0);
1.7       kristaps  538:        default:
                    539:                break;
                    540:        }
1.6       kristaps  541:
                    542:        if ( ! xstrlcat(mdoc->meta.os, args[j], sizeof(mdoc->meta.os)))
1.7       kristaps  543:                return(mdoc_err(mdoc, tok, lastarg, ERR_SYNTAX_ARGFORM));
1.6       kristaps  544:        if ( ! xstrlcat(mdoc->meta.os, " ", sizeof(mdoc->meta.os)))
1.7       kristaps  545:                return(mdoc_err(mdoc, tok, lastarg, ERR_SYNTAX_ARGFORM));
1.6       kristaps  546:
                    547:        goto again;
                    548:        /* NOTREACHED */
                    549: }
                    550:
                    551:
                    552: int
1.5       kristaps  553: macro_prologue_ddate(MACRO_PROT_ARGS)
                    554: {
1.7       kristaps  555:        int               lastarg, j;
1.5       kristaps  556:        char             *args[MDOC_LINEARG_MAX], date[64];
                    557:
                    558:        if (SEC_PROLOGUE != mdoc->sec_lastn)
                    559:                return(mdoc_err(mdoc, tok, ppos, ERR_SEC_NPROLOGUE));
                    560:        if (mdoc->meta.title[0])
                    561:                return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE_OO));
                    562:        if (mdoc->meta.date)
                    563:                return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE_REP));
                    564:
                    565:        j = -1;
                    566:        date[0] = 0;
1.7       kristaps  567:        lastarg = ppos;
1.5       kristaps  568:
                    569: again:
1.6       kristaps  570:        if (j == MDOC_LINEARG_MAX)
                    571:                return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY));
                    572:
1.7       kristaps  573:        lastarg = *pos;
                    574:        switch (mdoc_args(mdoc, tok, pos, buf, 0, &args[++j])) {
                    575:        case (ARGS_EOLN):
1.5       kristaps  576:                if (mdoc->meta.date)
                    577:                        return(1);
                    578:                mdoc->meta.date = mdoc_atotime(date);
                    579:                if (mdoc->meta.date)
                    580:                        return(1);
1.7       kristaps  581:                return(mdoc_err(mdoc, tok, ppos, ERR_SYNTAX_ARGFORM));
                    582:        case (ARGS_ERROR):
1.5       kristaps  583:                return(0);
1.7       kristaps  584:        default:
                    585:                break;
                    586:        }
1.5       kristaps  587:
                    588:        if (MDOC_MAX != mdoc_find(mdoc, args[j]) && ! mdoc_warn
                    589:                        (mdoc, tok, lastarg, WARN_SYNTAX_MACLIKE))
                    590:                return(0);
                    591:
                    592:        if (0 == j) {
                    593:                if (xstrcmp("$Mdocdate$", args[j])) {
                    594:                        mdoc->meta.date = time(NULL);
                    595:                        goto again;
                    596:                } else if (xstrcmp("$Mdocdate:", args[j]))
                    597:                        goto again;
                    598:        } else if (4 == j)
                    599:                if ( ! xstrcmp("$", args[j]))
                    600:                        goto again;
                    601:
                    602:        if ( ! xstrlcat(date, args[j], sizeof(date)))
1.7       kristaps  603:                return(mdoc_err(mdoc, tok, lastarg, ERR_SYNTAX_ARGFORM));
1.5       kristaps  604:        if ( ! xstrlcat(date, " ", sizeof(date)))
1.7       kristaps  605:                return(mdoc_err(mdoc, tok, lastarg, ERR_SYNTAX_ARGFORM));
1.5       kristaps  606:
                    607:        goto again;
                    608:        /* NOTREACHED */
                    609: }
                    610:
                    611:
                    612: int
1.6       kristaps  613: macro_scoped_explicit(MACRO_PROT_ARGS)
                    614: {
                    615:        int               c, lastarg, j;
                    616:        struct mdoc_arg   argv[MDOC_LINEARG_MAX];
1.7       kristaps  617:        struct mdoc_node *n;
1.6       kristaps  618:
                    619:        if (SEC_PROLOGUE == mdoc->sec_lastn)
                    620:                return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE));
                    621:
                    622:        /*
                    623:         * First close out the explicit scope.  The `end' tags (such as
                    624:         * `.El' to `.Bl' don't cause anything to happen: we merely
                    625:         * readjust our last parse point.
                    626:         */
                    627:
                    628:        switch (tok) {
                    629:        case (MDOC_El):
                    630:                return(scope_rewind_exp(mdoc, ppos, tok, MDOC_Bl));
1.7       kristaps  631:        case (MDOC_Ed):
                    632:                return(scope_rewind_exp(mdoc, ppos, tok, MDOC_Bd));
1.6       kristaps  633:        default:
                    634:                break;
                    635:        }
                    636:
                    637:        assert(MDOC_EXPLICIT & mdoc_macros[tok].flags);
                    638:
1.7       kristaps  639:        /* Token pre-processing. */
                    640:
                    641:        switch (tok) {
                    642:        case (MDOC_Bl):
                    643:                /* FALLTHROUGH */
                    644:        case (MDOC_Bd):
                    645:                /* `.Pp' ignored when preceding `.Bl' or `.Bd'. */
                    646:                assert(mdoc->last);
                    647:                if (MDOC_ELEM != mdoc->last->type)
                    648:                        break;
                    649:                if (MDOC_Pp != mdoc->last->data.elem.tok)
                    650:                        break;
                    651:                if ( ! mdoc_warn(mdoc, tok, ppos, WARN_IGN_BEFORE_BLK))
                    652:                        return(0);
                    653:                assert(mdoc->last->prev);
                    654:                n = mdoc->last;
                    655:                mdoc->last = mdoc->last->prev;
                    656:                mdoc->last->next = NULL;
                    657:                mdoc_node_free(n);
                    658:                break;
                    659:        default:
                    660:                break;
                    661:        }
                    662:
1.6       kristaps  663:        lastarg = *pos;
                    664:
                    665:        for (j = 0; j < MDOC_LINEARG_MAX; j++) {
                    666:                lastarg = *pos;
1.7       kristaps  667:                c = mdoc_argv(mdoc, tok, &argv[j], pos, buf);
1.6       kristaps  668:                if (0 == c)
                    669:                        break;
                    670:                else if (1 == c)
                    671:                        continue;
                    672:
                    673:                mdoc_argv_free(j, argv);
                    674:                return(0);
                    675:        }
                    676:
                    677:        if (MDOC_LINEARG_MAX == j) {
                    678:                mdoc_argv_free(j, argv);
                    679:                return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY));
                    680:        }
                    681:
                    682:        c = append_scoped(mdoc, tok, ppos, 0, NULL, j, argv);
                    683:        mdoc_argv_free(j, argv);
                    684:        return(c);
                    685: }
                    686:
                    687:
1.8       kristaps  688: /*
                    689:  * Implicity-scoped macros, like `.Ss', have a scope that terminates
                    690:  * with a subsequent call to the same macro.  Implicit macros cannot
                    691:  * break the scope of explicitly-scoped macros; however, they can break
                    692:  * the scope of other implicit macros (so `.Sh' can break `.Ss').  This
                    693:  * is ok with macros like `.It' because they exist only within an
                    694:  * explicit context.
                    695:  *
                    696:  * These macros put line arguments (which it's allowed to have) into the
                    697:  * HEAD section and open a BODY scope to be used until the macro scope
                    698:  * closes.
                    699:  */
1.6       kristaps  700: int
1.5       kristaps  701: macro_scoped_implicit(MACRO_PROT_ARGS)
1.2       kristaps  702: {
1.7       kristaps  703:        int               lastarg, j;
1.2       kristaps  704:        char             *args[MDOC_LINEARG_MAX];
                    705:        struct mdoc_node *n;
                    706:
1.5       kristaps  707:        assert( ! (MDOC_EXPLICIT & mdoc_macros[tok].flags));
1.1       kristaps  708:
1.5       kristaps  709:        if (SEC_PROLOGUE == mdoc->sec_lastn)
                    710:                return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE));
1.1       kristaps  711:
1.7       kristaps  712:        /* Token pre-processing. */
1.6       kristaps  713:
1.7       kristaps  714:        switch (tok) {
                    715:        case (MDOC_Ss):
                    716:                /* FALLTHROUGH */
                    717:        case (MDOC_Sh):
                    718:                /* `.Pp' ignored when preceding `.Ss' or `.Sh'. */
                    719:                if (NULL == mdoc->last)
                    720:                        break;
                    721:                if (MDOC_ELEM != mdoc->last->type)
                    722:                        break;
                    723:                if (MDOC_Pp != mdoc->last->data.elem.tok)
1.1       kristaps  724:                        break;
1.7       kristaps  725:                if ( ! mdoc_warn(mdoc, tok, ppos, WARN_IGN_BEFORE_BLK))
                    726:                        return(0);
                    727:                assert(mdoc->last->prev);
                    728:                n = mdoc->last;
1.8       kristaps  729:                mdoc_msg(mdoc, ppos, "removing prior `Pp' macro");
1.7       kristaps  730:                mdoc->last = mdoc->last->prev;
                    731:                mdoc->last->next = NULL;
                    732:                mdoc_node_free(n);
                    733:                break;
                    734:        default:
                    735:                break;
1.2       kristaps  736:        }
                    737:
1.8       kristaps  738:        /* Rewind our scope. */
                    739:
                    740:        if ( ! scope_rewind_imp(mdoc, ppos, tok))
                    741:                return(0);
1.2       kristaps  742:
                    743:        j = 0;
1.7       kristaps  744:        lastarg = ppos;
1.2       kristaps  745:
1.8       kristaps  746:        /*
                    747:         * Process until we hit a line.  Note that current implicit
                    748:         * macros don't have any arguments, so we don't need to do any
                    749:         * argument processing.
                    750:         */
                    751:
1.2       kristaps  752: again:
1.7       kristaps  753:        if (j == MDOC_LINEARG_MAX)
                    754:                return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY));
                    755:
1.6       kristaps  756:        lastarg = *pos;
1.2       kristaps  757:
1.7       kristaps  758:        switch (mdoc_args(mdoc, tok, pos, buf, 0, &args[j])) {
                    759:        case (ARGS_ERROR):
1.2       kristaps  760:                return(0);
1.7       kristaps  761:        case (ARGS_EOLN):
                    762:                return(append_scoped(mdoc, tok, ppos, j, _CC(args), 0, NULL));
                    763:        default:
                    764:                break;
                    765:        }
1.1       kristaps  766:
1.7       kristaps  767:        if (MDOC_MAX != mdoc_find(mdoc, args[j]))
1.3       kristaps  768:                if ( ! mdoc_warn(mdoc, tok, lastarg, WARN_SYNTAX_MACLIKE))
1.1       kristaps  769:                        return(0);
                    770:
1.2       kristaps  771:        j++;
                    772:        goto again;
1.8       kristaps  773:        /* NOTREACHED */
                    774: }
                    775:
                    776:
                    777: /*
                    778:  * A line-scoped macro opens a scope for the contents of its line, which
                    779:  * are placed under the HEAD node.  Punctuation trailing the line is put
                    780:  * as a sibling to the HEAD node, under the BLOCK node.
                    781:  */
                    782: int
                    783: macro_scoped_line(MACRO_PROT_ARGS)
                    784: {
                    785:        int               lastarg, c, j;
                    786:        char              *p;
                    787:        struct mdoc_node  *n;
                    788:
                    789:        if (SEC_PROLOGUE == mdoc->sec_lastn)
                    790:                return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE));
                    791:
                    792:        assert(1 == ppos);
                    793:
                    794:        /* Token pre-processing.  */
                    795:
                    796:        switch (tok) {
                    797:        case (MDOC_D1):
                    798:                /* FALLTHROUGH */
                    799:        case (MDOC_Dl):
                    800:                /* These can't be nested in a display block. */
                    801:                assert(mdoc->last);
                    802:                for (n = mdoc->last->parent ; n; n = n->parent)
                    803:                        if (MDOC_BLOCK != n->type)
                    804:                                continue;
                    805:                        else if (MDOC_Bd == n->data.block.tok)
                    806:                                break;
                    807:                if (NULL == n)
                    808:                        break;
                    809:                return(mdoc_err(mdoc, tok, ppos, ERR_SCOPE_NONEST));
                    810:        default:
                    811:                break;
                    812:        }
                    813:
                    814:        /*
                    815:         * All line-scoped macros have a HEAD and optionally a BODY
                    816:         * section.  We open our scope here; when we exit this function,
                    817:         * we'll rewind our scope appropriately.
                    818:         */
                    819:
                    820:        mdoc_block_alloc(mdoc, ppos, tok, 0, NULL);
                    821:        mdoc_head_alloc(mdoc, ppos, tok, 0, NULL);
                    822:
                    823:        /* Process line parameters. */
                    824:
                    825:        j = 0;
                    826:        lastarg = ppos;
                    827:
                    828: again:
                    829:        if (j == MDOC_LINEARG_MAX)
                    830:                return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY));
                    831:
                    832:        lastarg = *pos;
                    833:        c = mdoc_args(mdoc, tok, pos, buf, ARGS_DELIM, &p);
                    834:
                    835:        switch (c) {
                    836:        case (ARGS_ERROR):
                    837:                return(0);
                    838:        case (ARGS_WORD):
                    839:                break;
                    840:        case (ARGS_PUNCT):
                    841:                if ( ! append_delims(mdoc, tok, pos, buf))
                    842:                        return(0);
                    843:                return(scope_rewind_imp(mdoc, ppos, tok));
                    844:        case (ARGS_EOLN):
                    845:                return(scope_rewind_imp(mdoc, ppos, tok));
                    846:        default:
                    847:                abort();
                    848:                /* NOTREACHED */
                    849:        }
                    850:
                    851:        if (MDOC_MAX != (c = mdoc_find(mdoc, p))) {
                    852:                if ( ! mdoc_macro(mdoc, c, lastarg, pos, buf))
                    853:                        return(0);
                    854:                if ( ! append_delims(mdoc, tok, pos, buf))
                    855:                        return(0);
                    856:                return(scope_rewind_imp(mdoc, ppos, tok));
                    857:        }
1.1       kristaps  858:
1.8       kristaps  859:        if (mdoc_isdelim(p))
                    860:                j = 0;
                    861:
                    862:        mdoc_word_alloc(mdoc, lastarg, p);
                    863:        goto again;
1.2       kristaps  864:        /* NOTREACHED */
1.1       kristaps  865: }
1.5       kristaps  866:
1.7       kristaps  867:
1.9       kristaps  868: /*
                    869:  * Partial-line scope is identical to line scope (macro_scoped_line())
                    870:  * except that trailing punctuation is appended to the BLOCK, instead of
                    871:  * contained within the HEAD.
                    872:  */
1.7       kristaps  873: int
1.8       kristaps  874: macro_scoped_pline(MACRO_PROT_ARGS)
1.7       kristaps  875: {
1.8       kristaps  876:        int               lastarg, c, j;
                    877:        char              *p;
1.7       kristaps  878:
1.8       kristaps  879:        if (SEC_PROLOGUE == mdoc->sec_lastn)
                    880:                return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE));
                    881:
                    882:        /* Token pre-processing.  */
                    883:
                    884:        switch (tok) {
1.9       kristaps  885:        case (MDOC_Ql):
                    886:                if ( ! mdoc_warn(mdoc, tok, ppos, WARN_COMPAT_TROFF))
                    887:                        return(0);
                    888:                break;
1.8       kristaps  889:        default:
                    890:                break;
                    891:        }
                    892:
                    893:        mdoc_block_alloc(mdoc, ppos, tok, 0, NULL);
                    894:        mdoc_head_alloc(mdoc, ppos, tok, 0, NULL);
                    895:
                    896:        /* Process line parameters. */
                    897:
                    898:        j = 0;
                    899:        lastarg = ppos;
                    900:
                    901: again:
                    902:        if (j == MDOC_LINEARG_MAX)
                    903:                return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY));
                    904:
                    905:        lastarg = *pos;
                    906:        c = mdoc_args(mdoc, tok, pos, buf, ARGS_DELIM, &p);
                    907:
                    908:        switch (c) {
                    909:        case (ARGS_ERROR):
                    910:                return(0);
                    911:        case (ARGS_WORD):
                    912:                break;
                    913:        case (ARGS_PUNCT):
                    914:                if ( ! scope_rewind_imp(mdoc, ppos, tok))
                    915:                        return(0);
                    916:                if (ppos > 1)
                    917:                        return(1);
                    918:                return(append_delims(mdoc, tok, pos, buf));
                    919:        case (ARGS_EOLN):
                    920:                return(scope_rewind_imp(mdoc, ppos, tok));
                    921:        default:
                    922:                abort();
                    923:                /* NOTREACHED */
                    924:        }
                    925:
                    926:        if (MDOC_MAX != (c = mdoc_find(mdoc, p))) {
                    927:                if ( ! mdoc_macro(mdoc, c, lastarg, pos, buf))
                    928:                        return(0);
                    929:                if ( ! scope_rewind_imp(mdoc, ppos, tok))
                    930:                        return(0);
                    931:                if (ppos > 1)
                    932:                        return(1);
                    933:                return(append_delims(mdoc, tok, pos, buf));
                    934:        }
                    935:
                    936:        if (mdoc_isdelim(p))
                    937:                j = 0;
                    938:
                    939:        mdoc_word_alloc(mdoc, lastarg, p);
                    940:        goto again;
                    941:        /* NOTREACHED */
1.7       kristaps  942: }
1.8       kristaps  943:
1.10      kristaps  944:
                    945: /*
                    946:  * A delimited-constant macro is similar to a general text macro: the
                    947:  * macro is followed by a 0 or 1 arguments (possibly-unspecified) then
                    948:  * terminating punctuation, other words, or another callable macro.
                    949:  */
                    950: int
                    951: macro_constant_delimited(MACRO_PROT_ARGS)
                    952: {
                    953:        int               lastarg, flushed, c, maxargs;
                    954:        char             *p, *pp;
                    955:
                    956:        if (SEC_PROLOGUE == mdoc->sec_lastn)
                    957:                return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE));
                    958:
                    959:        /* Process line parameters. */
                    960:
                    961:        lastarg = ppos;
                    962:        flushed = 0;
                    963:
1.11    ! kristaps  964:        /* Token pre-processing. */
        !           965:
1.10      kristaps  966:        switch (tok) {
                    967:        case (MDOC_Ux):
                    968:                maxargs = 0;
                    969:                break;
                    970:        default:
                    971:                maxargs = 1;
                    972:                break;
                    973:        }
                    974:
                    975: again:
                    976:        lastarg = *pos;
                    977:
                    978:        switch (mdoc_args(mdoc, tok, pos, buf, ARGS_DELIM, &p)) {
                    979:        case (ARGS_ERROR):
                    980:                return(0);
                    981:        case (ARGS_WORD):
                    982:                break;
                    983:        case (ARGS_PUNCT):
                    984:                if ( ! flushed && ! append_const(mdoc, tok, ppos, 0, &p))
                    985:                        return(0);
                    986:                if (ppos > 1)
                    987:                        return(1);
                    988:                return(append_delims(mdoc, tok, pos, buf));
                    989:        case (ARGS_EOLN):
                    990:                if (flushed)
                    991:                        return(1);
                    992:                return(append_const(mdoc, tok, ppos, 0, &p));
                    993:        default:
                    994:                abort();
                    995:                /* NOTREACHED */
                    996:        }
                    997:
1.11    ! kristaps  998:        /* Accepts no arguments: flush out symbol and continue. */
        !           999:
1.10      kristaps 1000:        if (0 == maxargs) {
                   1001:                pp = p;
                   1002:                if ( ! append_const(mdoc, tok, ppos, 0, &p))
                   1003:                        return(0);
                   1004:                p = pp;
                   1005:                flushed = 1;
                   1006:        }
                   1007:
                   1008:        if (MDOC_MAX != (c = mdoc_find(mdoc, p))) {
                   1009:                if ( ! flushed && ! append_const(mdoc, tok, ppos, 0, &p))
                   1010:                        return(0);
                   1011:                if ( ! mdoc_macro(mdoc, c, lastarg, pos, buf))
                   1012:                        return(0);
                   1013:                if (ppos > 1)
                   1014:                        return(1);
                   1015:                return(append_delims(mdoc, tok, pos, buf));
                   1016:        }
                   1017:
1.11    ! kristaps 1018:        /*
        !          1019:         * We only accept one argument; subsequent tokens are considered
        !          1020:         * as literal words (until a macro).
        !          1021:         */
        !          1022:
1.10      kristaps 1023:        if ( ! flushed && ! mdoc_isdelim(p)) {
                   1024:               if ( ! append_const(mdoc, tok, ppos, 1, &p))
                   1025:                        return(0);
                   1026:                flushed = 1;
                   1027:                goto again;
                   1028:        } else if ( ! flushed) {
                   1029:                pp = p;
                   1030:                if ( ! append_const(mdoc, tok, ppos, 0, &p))
                   1031:                        return(0);
                   1032:                p = pp;
                   1033:                flushed = 1;
                   1034:        }
                   1035:
                   1036:        mdoc_word_alloc(mdoc, lastarg, p);
                   1037:        goto again;
                   1038:        /* NOTREACHED */
                   1039: }
1.11    ! kristaps 1040:
        !          1041:
        !          1042: int
        !          1043: macro_constant(MACRO_PROT_ARGS)
        !          1044: {
        !          1045:        int               lastarg, j;
        !          1046:        char             *args[MDOC_LINEARG_MAX];
        !          1047:
        !          1048:        if (SEC_PROLOGUE == mdoc->sec_lastn)
        !          1049:                return(mdoc_err(mdoc, tok, ppos, ERR_SEC_PROLOGUE));
        !          1050:
        !          1051:        j = 0;
        !          1052:
        !          1053: again:
        !          1054:        if (j == MDOC_LINEARG_MAX)
        !          1055:                return(mdoc_err(mdoc, tok, lastarg, ERR_ARGS_MANY));
        !          1056:
        !          1057:        lastarg = *pos;
        !          1058:
        !          1059:        switch (mdoc_args(mdoc, tok, pos, buf, 0, &args[j])) {
        !          1060:        case (ARGS_ERROR):
        !          1061:                return(0);
        !          1062:        case (ARGS_WORD):
        !          1063:                break;
        !          1064:        case (ARGS_EOLN):
        !          1065:                return(append_const(mdoc, tok, ppos, j, args));
        !          1066:        default:
        !          1067:                abort();
        !          1068:                /* NOTREACHED */
        !          1069:        }
        !          1070:
        !          1071:        if (MDOC_MAX != mdoc_find(mdoc, args[j]))
        !          1072:                if ( ! mdoc_warn(mdoc, tok, lastarg, WARN_SYNTAX_MACLIKE))
        !          1073:                        return(0);
        !          1074:
        !          1075:        j++;
        !          1076:        goto again;
        !          1077:        /* NOTREACHED */
        !          1078: }

CVSweb