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

Annotation of src/usr.bin/mandoc/mdoc_macro.c, Revision 1.1

1.1     ! kristaps    1: /* $Id: mdoc_macro.c,v 1.6 2009/04/02 06:51:44 kristaps Exp $ */
        !             2: /*
        !             3:  * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@openbsd.org>
        !             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:  */
        !            19: #include <assert.h>
        !            20: #include <ctype.h>
        !            21: #include <stdlib.h>
        !            22: #include <stdio.h>
        !            23: #include <string.h>
        !            24:
        !            25: #include "libmdoc.h"
        !            26:
        !            27: /* FIXME: .Fl, .Ar, .Cd handling of `|'. */
        !            28:
        !            29: enum   mwarn {
        !            30:        WIGNE,
        !            31:        WIMPBRK,
        !            32:        WMACPARM,
        !            33:        WOBS
        !            34: };
        !            35:
        !            36: enum   merr {
        !            37:        EOPEN,
        !            38:        EQUOT,
        !            39:        ENOCTX,
        !            40:        ENOPARMS
        !            41: };
        !            42:
        !            43: #define        REWIND_REWIND   (1 << 0)
        !            44: #define        REWIND_NOHALT   (1 << 1)
        !            45: #define        REWIND_HALT     (1 << 2)
        !            46:
        !            47: static int       obsolete(MACRO_PROT_ARGS);
        !            48: static int       blk_part_exp(MACRO_PROT_ARGS);
        !            49: static int       in_line_eoln(MACRO_PROT_ARGS);
        !            50: static int       in_line_argn(MACRO_PROT_ARGS);
        !            51: static int       in_line(MACRO_PROT_ARGS);
        !            52: static int       blk_full(MACRO_PROT_ARGS);
        !            53: static int       blk_exp_close(MACRO_PROT_ARGS);
        !            54: static int       blk_part_imp(MACRO_PROT_ARGS);
        !            55:
        !            56: static int       phrase(struct mdoc *, int, int, char *);
        !            57: static int       rew_dohalt(int, enum mdoc_type,
        !            58:                        const struct mdoc_node *);
        !            59: static int       rew_alt(int);
        !            60: static int       rew_dobreak(int, const struct mdoc_node *);
        !            61: static int       rew_elem(struct mdoc *, int);
        !            62: static int       rew_impblock(struct mdoc *, int, int, int);
        !            63: static int       rew_expblock(struct mdoc *, int, int, int);
        !            64: static int       rew_subblock(enum mdoc_type,
        !            65:                        struct mdoc *, int, int, int);
        !            66: static int       rew_last(struct mdoc *, struct mdoc_node *);
        !            67: static int       append_delims(struct mdoc *, int, int *, char *);
        !            68: static int       lookup(struct mdoc *, int, int, int, const char *);
        !            69: static int       pwarn(struct mdoc *, int, int, enum mwarn);
        !            70: static int       perr(struct mdoc *, int, int, enum merr);
        !            71: static int       swarn(struct mdoc *, enum mdoc_type, int, int,
        !            72:                        const struct mdoc_node *);
        !            73:
        !            74: #define        nerr(m, n, t) perr((m), (n)->line, (n)->pos, (t))
        !            75:
        !            76: /* Central table of library: who gets parsed how. */
        !            77:
        !            78: const  struct mdoc_macro __mdoc_macros[MDOC_MAX] = {
        !            79:        { NULL, 0 }, /* \" */
        !            80:        { in_line_eoln, MDOC_PROLOGUE }, /* Dd */
        !            81:        { in_line_eoln, MDOC_PROLOGUE }, /* Dt */
        !            82:        { in_line_eoln, MDOC_PROLOGUE }, /* Os */
        !            83:        { blk_full, 0 }, /* Sh */
        !            84:        { blk_full, 0 }, /* Ss */
        !            85:        { in_line, 0 }, /* Pp */
        !            86:        { blk_part_imp, MDOC_PARSED }, /* D1 */
        !            87:        { blk_part_imp, MDOC_PARSED }, /* Dl */
        !            88:        { blk_full, MDOC_EXPLICIT }, /* Bd */
        !            89:        { blk_exp_close, MDOC_EXPLICIT }, /* Ed */
        !            90:        { blk_full, MDOC_EXPLICIT }, /* Bl */
        !            91:        { blk_exp_close, MDOC_EXPLICIT }, /* El */
        !            92:        { blk_full, MDOC_PARSED }, /* It */
        !            93:        { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ad */
        !            94:        { in_line, MDOC_PARSED }, /* An */
        !            95:        { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ar */
        !            96:        { in_line_eoln, MDOC_CALLABLE }, /* Cd */
        !            97:        { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Cm */
        !            98:        { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Dv */
        !            99:        { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Er */
        !           100:        { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ev */
        !           101:        { in_line_eoln, 0 }, /* Ex */
        !           102:        { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fa */
        !           103:        { in_line_eoln, 0 }, /* Fd */
        !           104:        { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fl */
        !           105:        { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fn */
        !           106:        { in_line, MDOC_PARSED }, /* Ft */
        !           107:        { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ic */
        !           108:        { in_line_eoln, 0 }, /* In */
        !           109:        { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Li */
        !           110:        { in_line_eoln, 0 }, /* Nd */
        !           111:        { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Nm */
        !           112:        { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Op */
        !           113:        { obsolete, 0 }, /* Ot */
        !           114:        { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Pa */
        !           115:        { in_line_eoln, 0 }, /* Rv */
        !           116:        { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* St */
        !           117:        { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Va */
        !           118:        { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Vt */
        !           119:        { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Xr */
        !           120:        { in_line_eoln, 0 }, /* %A */
        !           121:        { in_line_eoln, 0 }, /* %B */
        !           122:        { in_line_eoln, 0 }, /* %D */
        !           123:        { in_line_eoln, 0 }, /* %I */
        !           124:        { in_line_eoln, 0 }, /* %J */
        !           125:        { in_line_eoln, 0 }, /* %N */
        !           126:        { in_line_eoln, 0 }, /* %O */
        !           127:        { in_line_eoln, 0 }, /* %P */
        !           128:        { in_line_eoln, 0 }, /* %R */
        !           129:        { in_line_eoln, 0 }, /* %T */
        !           130:        { in_line_eoln, 0 }, /* %V */
        !           131:        { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Ac */
        !           132:        { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Ao */
        !           133:        { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Aq */
        !           134:        { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* At */
        !           135:        { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Bc */
        !           136:        { blk_full, MDOC_EXPLICIT }, /* Bf */
        !           137:        { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Bo */
        !           138:        { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Bq */
        !           139:        { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Bsx */
        !           140:        { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Bx */
        !           141:        { in_line_eoln, 0 }, /* Db */
        !           142:        { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Dc */
        !           143:        { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Do */
        !           144:        { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Dq */
        !           145:        { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Ec */
        !           146:        { blk_exp_close, MDOC_EXPLICIT }, /* Ef */
        !           147:        { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Em */
        !           148:        { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Eo */
        !           149:        { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Fx */
        !           150:        { in_line, MDOC_PARSED }, /* Ms */
        !           151:        { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* No */
        !           152:        { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Ns */
        !           153:        { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Nx */
        !           154:        { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Ox */
        !           155:        { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Pc */
        !           156:        { in_line_argn, MDOC_PARSED | MDOC_IGNDELIM }, /* Pf */
        !           157:        { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Po */
        !           158:        { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Pq */
        !           159:        { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Qc */
        !           160:        { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Ql */
        !           161:        { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Qo */
        !           162:        { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Qq */
        !           163:        { blk_exp_close, MDOC_EXPLICIT }, /* Re */
        !           164:        { blk_full, MDOC_EXPLICIT }, /* Rs */
        !           165:        { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Sc */
        !           166:        { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* So */
        !           167:        { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Sq */
        !           168:        { in_line_eoln, 0 }, /* Sm */
        !           169:        { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Sx */
        !           170:        { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Sy */
        !           171:        { in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Tn */
        !           172:        { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Ux */
        !           173:        { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Xc */
        !           174:        { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Xo */
        !           175:        { blk_full, MDOC_EXPLICIT | MDOC_CALLABLE }, /* Fo */
        !           176:        { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Fc */
        !           177:        { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Oo */
        !           178:        { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Oc */
        !           179:        { blk_full, MDOC_EXPLICIT }, /* Bk */
        !           180:        { blk_exp_close, MDOC_EXPLICIT }, /* Ek */
        !           181:        { in_line_eoln, 0 }, /* Bt */
        !           182:        { in_line_eoln, 0 }, /* Hf */
        !           183:        { obsolete, 0 }, /* Fr */
        !           184:        { in_line_eoln, 0 }, /* Ud */
        !           185:        { in_line_eoln, 0 }, /* Lb */
        !           186:        { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Ap */
        !           187:        { in_line, 0 }, /* Lp */
        !           188:        { in_line, MDOC_PARSED }, /* Lk */
        !           189:        { in_line, MDOC_PARSED }, /* Mt */
        !           190:        { blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Brq */
        !           191:        { blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Bro */
        !           192:        { blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Brc */
        !           193:        { in_line_eoln, 0 }, /* %C */
        !           194:        { obsolete, 0 }, /* Es */
        !           195:        { obsolete, 0 }, /* En */
        !           196:        { in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Dx */
        !           197:        { in_line_eoln, 0 }, /* %Q */
        !           198: };
        !           199:
        !           200: const  struct mdoc_macro * const mdoc_macros = __mdoc_macros;
        !           201:
        !           202:
        !           203: static int
        !           204: perr(struct mdoc *mdoc, int line, int pos, enum merr type)
        !           205: {
        !           206:        char            *p;
        !           207:
        !           208:        p = NULL;
        !           209:        switch (type) {
        !           210:        case (EOPEN):
        !           211:                p = "explicit scope still open on exit";
        !           212:                break;
        !           213:        case (EQUOT):
        !           214:                p = "unterminated quotation";
        !           215:                break;
        !           216:        case (ENOCTX):
        !           217:                p = "closure has no prior context";
        !           218:                break;
        !           219:        case (ENOPARMS):
        !           220:                p = "unexpect line arguments";
        !           221:                break;
        !           222:        }
        !           223:        assert(p);
        !           224:        return(mdoc_perr(mdoc, line, pos, p));
        !           225: }
        !           226:
        !           227:
        !           228: static int
        !           229: pwarn(struct mdoc *mdoc, int line, int pos, enum mwarn type)
        !           230: {
        !           231:        char            *p;
        !           232:
        !           233:        p = NULL;
        !           234:        switch (type) {
        !           235:        case (WIGNE):
        !           236:                p = "ignoring empty element";
        !           237:                break;
        !           238:        case (WIMPBRK):
        !           239:                p = "crufty end-of-line scope violation";
        !           240:                break;
        !           241:        case (WMACPARM):
        !           242:                p = "macro-like parameter";
        !           243:                break;
        !           244:        case (WOBS):
        !           245:                p = "macro marked obsolete";
        !           246:                break;
        !           247:        }
        !           248:        assert(p);
        !           249:        return(mdoc_pwarn(mdoc, line, pos, WARN_SYNTAX, p));
        !           250: }
        !           251:
        !           252:
        !           253: static int
        !           254: swarn(struct mdoc *mdoc, enum mdoc_type type,
        !           255:                int line, int pos, const struct mdoc_node *p)
        !           256: {
        !           257:        const char      *n, *t, *tt;
        !           258:
        !           259:        n = t = "<root>";
        !           260:        tt = "block";
        !           261:
        !           262:        switch (type) {
        !           263:        case (MDOC_BODY):
        !           264:                tt = "multi-line";
        !           265:                break;
        !           266:        case (MDOC_HEAD):
        !           267:                tt = "line";
        !           268:                break;
        !           269:        default:
        !           270:                break;
        !           271:        }
        !           272:
        !           273:        switch (p->type) {
        !           274:        case (MDOC_BLOCK):
        !           275:                n = mdoc_macronames[p->tok];
        !           276:                t = "block";
        !           277:                break;
        !           278:        case (MDOC_BODY):
        !           279:                n = mdoc_macronames[p->tok];
        !           280:                t = "multi-line";
        !           281:                break;
        !           282:        case (MDOC_HEAD):
        !           283:                n = mdoc_macronames[p->tok];
        !           284:                t = "line";
        !           285:                break;
        !           286:        default:
        !           287:                break;
        !           288:        }
        !           289:
        !           290:        if ( ! (MDOC_IGN_SCOPE & mdoc->pflags))
        !           291:                return(mdoc_perr(mdoc, line, pos,
        !           292:                                "%s scope breaks %s scope of %s",
        !           293:                                tt, t, n));
        !           294:        return(mdoc_pwarn(mdoc, line, pos, WARN_SYNTAX,
        !           295:                                "%s scope breaks %s scope of %s",
        !           296:                                tt, t, n));
        !           297: }
        !           298:
        !           299:
        !           300: /*
        !           301:  * This is called at the end of parsing.  It must traverse up the tree,
        !           302:  * closing out open [implicit] scopes.  Obviously, open explicit scopes
        !           303:  * are errors.
        !           304:  */
        !           305: int
        !           306: mdoc_macroend(struct mdoc *mdoc)
        !           307: {
        !           308:        struct mdoc_node *n;
        !           309:
        !           310:        /* Scan for open explicit scopes. */
        !           311:
        !           312:        n = MDOC_VALID & mdoc->last->flags ?
        !           313:                mdoc->last->parent : mdoc->last;
        !           314:
        !           315:        for ( ; n; n = n->parent) {
        !           316:                if (MDOC_BLOCK != n->type)
        !           317:                        continue;
        !           318:                if ( ! (MDOC_EXPLICIT & mdoc_macros[n->tok].flags))
        !           319:                        continue;
        !           320:                return(nerr(mdoc, n, EOPEN));
        !           321:        }
        !           322:
        !           323:        return(rew_last(mdoc, mdoc->first));
        !           324: }
        !           325:
        !           326: static int
        !           327: lookup(struct mdoc *mdoc, int line, int pos, int from, const char *p)
        !           328: {
        !           329:        int              res;
        !           330:
        !           331:        res = mdoc_hash_find(mdoc->htab, p);
        !           332:        if (MDOC_PARSED & mdoc_macros[from].flags)
        !           333:                return(res);
        !           334:        if (MDOC_MAX == res)
        !           335:                return(res);
        !           336:        if ( ! pwarn(mdoc, line, pos, WMACPARM))
        !           337:                return(-1);
        !           338:        return(MDOC_MAX);
        !           339: }
        !           340:
        !           341:
        !           342: static int
        !           343: rew_last(struct mdoc *mdoc, struct mdoc_node *to)
        !           344: {
        !           345:
        !           346:        assert(to);
        !           347:        mdoc->next = MDOC_NEXT_SIBLING;
        !           348:
        !           349:        /* LINTED */
        !           350:        while (mdoc->last != to) {
        !           351:                if ( ! mdoc_valid_post(mdoc))
        !           352:                        return(0);
        !           353:                if ( ! mdoc_action_post(mdoc))
        !           354:                        return(0);
        !           355:                mdoc->last = mdoc->last->parent;
        !           356:                assert(mdoc->last);
        !           357:        }
        !           358:
        !           359:        if ( ! mdoc_valid_post(mdoc))
        !           360:                return(0);
        !           361:        return(mdoc_action_post(mdoc));
        !           362: }
        !           363:
        !           364:
        !           365: static int
        !           366: rew_alt(int tok)
        !           367: {
        !           368:        switch (tok) {
        !           369:        case (MDOC_Ac):
        !           370:                return(MDOC_Ao);
        !           371:        case (MDOC_Bc):
        !           372:                return(MDOC_Bo);
        !           373:        case (MDOC_Brc):
        !           374:                return(MDOC_Bro);
        !           375:        case (MDOC_Dc):
        !           376:                return(MDOC_Do);
        !           377:        case (MDOC_Ec):
        !           378:                return(MDOC_Eo);
        !           379:        case (MDOC_Ed):
        !           380:                return(MDOC_Bd);
        !           381:        case (MDOC_Ef):
        !           382:                return(MDOC_Bf);
        !           383:        case (MDOC_Ek):
        !           384:                return(MDOC_Bk);
        !           385:        case (MDOC_El):
        !           386:                return(MDOC_Bl);
        !           387:        case (MDOC_Fc):
        !           388:                return(MDOC_Fo);
        !           389:        case (MDOC_Oc):
        !           390:                return(MDOC_Oo);
        !           391:        case (MDOC_Pc):
        !           392:                return(MDOC_Po);
        !           393:        case (MDOC_Qc):
        !           394:                return(MDOC_Qo);
        !           395:        case (MDOC_Re):
        !           396:                return(MDOC_Rs);
        !           397:        case (MDOC_Sc):
        !           398:                return(MDOC_So);
        !           399:        case (MDOC_Xc):
        !           400:                return(MDOC_Xo);
        !           401:        default:
        !           402:                break;
        !           403:        }
        !           404:        abort();
        !           405:        /* NOTREACHED */
        !           406: }
        !           407:
        !           408:
        !           409: /*
        !           410:  * Rewind rules.  This indicates whether to stop rewinding
        !           411:  * (REWIND_HALT) without touching our current scope, stop rewinding and
        !           412:  * close our current scope (REWIND_REWIND), or continue (REWIND_NOHALT).
        !           413:  * The scope-closing and so on occurs in the various rew_* routines.
        !           414:  */
        !           415: static int
        !           416: rew_dohalt(int tok, enum mdoc_type type, const struct mdoc_node *p)
        !           417: {
        !           418:
        !           419:        if (MDOC_ROOT == p->type)
        !           420:                return(REWIND_HALT);
        !           421:        if (MDOC_VALID & p->flags)
        !           422:                return(REWIND_NOHALT);
        !           423:
        !           424:        switch (tok) {
        !           425:        case (MDOC_Aq):
        !           426:                /* FALLTHROUGH */
        !           427:        case (MDOC_Bq):
        !           428:                /* FALLTHROUGH */
        !           429:        case (MDOC_Brq):
        !           430:                /* FALLTHROUGH */
        !           431:        case (MDOC_D1):
        !           432:                /* FALLTHROUGH */
        !           433:        case (MDOC_Dl):
        !           434:                /* FALLTHROUGH */
        !           435:        case (MDOC_Dq):
        !           436:                /* FALLTHROUGH */
        !           437:        case (MDOC_Op):
        !           438:                /* FALLTHROUGH */
        !           439:        case (MDOC_Pq):
        !           440:                /* FALLTHROUGH */
        !           441:        case (MDOC_Ql):
        !           442:                /* FALLTHROUGH */
        !           443:        case (MDOC_Qq):
        !           444:                /* FALLTHROUGH */
        !           445:        case (MDOC_Sq):
        !           446:                assert(MDOC_HEAD != type);
        !           447:                assert(MDOC_TAIL != type);
        !           448:                if (type == p->type && tok == p->tok)
        !           449:                        return(REWIND_REWIND);
        !           450:                break;
        !           451:        case (MDOC_It):
        !           452:                assert(MDOC_TAIL != type);
        !           453:                if (type == p->type && tok == p->tok)
        !           454:                        return(REWIND_REWIND);
        !           455:                if (MDOC_BODY == p->type && MDOC_Bl == p->tok)
        !           456:                        return(REWIND_HALT);
        !           457:                break;
        !           458:        case (MDOC_Sh):
        !           459:                if (type == p->type && tok == p->tok)
        !           460:                        return(REWIND_REWIND);
        !           461:                break;
        !           462:        case (MDOC_Ss):
        !           463:                assert(MDOC_TAIL != type);
        !           464:                if (type == p->type && tok == p->tok)
        !           465:                        return(REWIND_REWIND);
        !           466:                if (MDOC_BODY == p->type && MDOC_Sh == p->tok)
        !           467:                        return(REWIND_HALT);
        !           468:                break;
        !           469:        case (MDOC_Ao):
        !           470:                /* FALLTHROUGH */
        !           471:        case (MDOC_Bd):
        !           472:                /* FALLTHROUGH */
        !           473:        case (MDOC_Bf):
        !           474:                /* FALLTHROUGH */
        !           475:        case (MDOC_Bk):
        !           476:                /* FALLTHROUGH */
        !           477:        case (MDOC_Bl):
        !           478:                /* FALLTHROUGH */
        !           479:        case (MDOC_Bo):
        !           480:                /* FALLTHROUGH */
        !           481:        case (MDOC_Bro):
        !           482:                /* FALLTHROUGH */
        !           483:        case (MDOC_Do):
        !           484:                /* FALLTHROUGH */
        !           485:        case (MDOC_Eo):
        !           486:                /* FALLTHROUGH */
        !           487:        case (MDOC_Fo):
        !           488:                /* FALLTHROUGH */
        !           489:        case (MDOC_Oo):
        !           490:                /* FALLTHROUGH */
        !           491:        case (MDOC_Po):
        !           492:                /* FALLTHROUGH */
        !           493:        case (MDOC_Qo):
        !           494:                /* FALLTHROUGH */
        !           495:        case (MDOC_Rs):
        !           496:                /* FALLTHROUGH */
        !           497:        case (MDOC_So):
        !           498:                /* FALLTHROUGH */
        !           499:        case (MDOC_Xo):
        !           500:                if (type == p->type && tok == p->tok)
        !           501:                        return(REWIND_REWIND);
        !           502:                break;
        !           503:
        !           504:        /* Multi-line explicit scope close. */
        !           505:        case (MDOC_Ac):
        !           506:                /* FALLTHROUGH */
        !           507:        case (MDOC_Bc):
        !           508:                /* FALLTHROUGH */
        !           509:        case (MDOC_Brc):
        !           510:                /* FALLTHROUGH */
        !           511:        case (MDOC_Dc):
        !           512:                /* FALLTHROUGH */
        !           513:        case (MDOC_Ec):
        !           514:                /* FALLTHROUGH */
        !           515:        case (MDOC_Ed):
        !           516:                /* FALLTHROUGH */
        !           517:        case (MDOC_Ek):
        !           518:                /* FALLTHROUGH */
        !           519:        case (MDOC_El):
        !           520:                /* FALLTHROUGH */
        !           521:        case (MDOC_Fc):
        !           522:                /* FALLTHROUGH */
        !           523:        case (MDOC_Ef):
        !           524:                /* FALLTHROUGH */
        !           525:        case (MDOC_Oc):
        !           526:                /* FALLTHROUGH */
        !           527:        case (MDOC_Pc):
        !           528:                /* FALLTHROUGH */
        !           529:        case (MDOC_Qc):
        !           530:                /* FALLTHROUGH */
        !           531:        case (MDOC_Re):
        !           532:                /* FALLTHROUGH */
        !           533:        case (MDOC_Sc):
        !           534:                /* FALLTHROUGH */
        !           535:        case (MDOC_Xc):
        !           536:                if (type == p->type && rew_alt(tok) == p->tok)
        !           537:                        return(REWIND_REWIND);
        !           538:                break;
        !           539:        default:
        !           540:                abort();
        !           541:                /* NOTREACHED */
        !           542:        }
        !           543:
        !           544:        return(REWIND_NOHALT);
        !           545: }
        !           546:
        !           547:
        !           548: /*
        !           549:  * See if we can break an encountered scope (the rew_dohalt has returned
        !           550:  * REWIND_NOHALT).
        !           551:  */
        !           552: static int
        !           553: rew_dobreak(int tok, const struct mdoc_node *p)
        !           554: {
        !           555:
        !           556:        assert(MDOC_ROOT != p->type);
        !           557:        if (MDOC_ELEM == p->type)
        !           558:                return(1);
        !           559:        if (MDOC_TEXT == p->type)
        !           560:                return(1);
        !           561:        if (MDOC_VALID & p->flags)
        !           562:                return(1);
        !           563:
        !           564:        switch (tok) {
        !           565:        case (MDOC_It):
        !           566:                return(MDOC_It == p->tok);
        !           567:        case (MDOC_Ss):
        !           568:                return(MDOC_Ss == p->tok);
        !           569:        case (MDOC_Sh):
        !           570:                if (MDOC_Ss == p->tok)
        !           571:                        return(1);
        !           572:                return(MDOC_Sh == p->tok);
        !           573:        case (MDOC_El):
        !           574:                if (MDOC_It == p->tok)
        !           575:                        return(1);
        !           576:                break;
        !           577:        case (MDOC_Oc):
        !           578:                /* XXX - experimental! */
        !           579:                if (MDOC_Op == p->tok)
        !           580:                        return(1);
        !           581:                break;
        !           582:        default:
        !           583:                break;
        !           584:        }
        !           585:
        !           586:        if (MDOC_EXPLICIT & mdoc_macros[tok].flags)
        !           587:                return(p->tok == rew_alt(tok));
        !           588:        else if (MDOC_BLOCK == p->type)
        !           589:                return(1);
        !           590:
        !           591:        return(tok == p->tok);
        !           592: }
        !           593:
        !           594:
        !           595: static int
        !           596: rew_elem(struct mdoc *mdoc, int tok)
        !           597: {
        !           598:        struct mdoc_node *n;
        !           599:
        !           600:        n = mdoc->last;
        !           601:        if (MDOC_ELEM != n->type)
        !           602:                n = n->parent;
        !           603:        assert(MDOC_ELEM == n->type);
        !           604:        assert(tok == n->tok);
        !           605:
        !           606:        return(rew_last(mdoc, n));
        !           607: }
        !           608:
        !           609:
        !           610: static int
        !           611: rew_subblock(enum mdoc_type type, struct mdoc *mdoc,
        !           612:                int tok, int line, int ppos)
        !           613: {
        !           614:        struct mdoc_node *n;
        !           615:        int               c;
        !           616:
        !           617:        /* LINTED */
        !           618:        for (n = mdoc->last; n; n = n->parent) {
        !           619:                c = rew_dohalt(tok, type, n);
        !           620:                if (REWIND_HALT == c)
        !           621:                        return(1);
        !           622:                if (REWIND_REWIND == c)
        !           623:                        break;
        !           624:                else if (rew_dobreak(tok, n))
        !           625:                        continue;
        !           626:                if ( ! swarn(mdoc, type, line, ppos, n))
        !           627:                        return(0);
        !           628:        }
        !           629:
        !           630:        assert(n);
        !           631:        return(rew_last(mdoc, n));
        !           632: }
        !           633:
        !           634:
        !           635: static int
        !           636: rew_expblock(struct mdoc *mdoc, int tok, int line, int ppos)
        !           637: {
        !           638:        struct mdoc_node *n;
        !           639:        int               c;
        !           640:
        !           641:        /* LINTED */
        !           642:        for (n = mdoc->last; n; n = n->parent) {
        !           643:                c = rew_dohalt(tok, MDOC_BLOCK, n);
        !           644:                if (REWIND_HALT == c)
        !           645:                        return(perr(mdoc, line, ppos, ENOCTX));
        !           646:                if (REWIND_REWIND == c)
        !           647:                        break;
        !           648:                else if (rew_dobreak(tok, n))
        !           649:                        continue;
        !           650:                if ( ! swarn(mdoc, MDOC_BLOCK, line, ppos, n))
        !           651:                        return(0);
        !           652:        }
        !           653:
        !           654:        assert(n);
        !           655:        return(rew_last(mdoc, n));
        !           656: }
        !           657:
        !           658:
        !           659: static int
        !           660: rew_impblock(struct mdoc *mdoc, int tok, int line, int ppos)
        !           661: {
        !           662:        struct mdoc_node *n;
        !           663:        int               c;
        !           664:
        !           665:        /* LINTED */
        !           666:        for (n = mdoc->last; n; n = n->parent) {
        !           667:                c = rew_dohalt(tok, MDOC_BLOCK, n);
        !           668:                if (REWIND_HALT == c)
        !           669:                        return(1);
        !           670:                else if (REWIND_REWIND == c)
        !           671:                        break;
        !           672:                else if (rew_dobreak(tok, n))
        !           673:                        continue;
        !           674:                if ( ! swarn(mdoc, MDOC_BLOCK, line, ppos, n))
        !           675:                        return(0);
        !           676:        }
        !           677:
        !           678:        assert(n);
        !           679:        return(rew_last(mdoc, n));
        !           680: }
        !           681:
        !           682:
        !           683: static int
        !           684: append_delims(struct mdoc *mdoc, int line, int *pos, char *buf)
        !           685: {
        !           686:        int              c, lastarg;
        !           687:        char            *p;
        !           688:
        !           689:        if (0 == buf[*pos])
        !           690:                return(1);
        !           691:
        !           692:        for (;;) {
        !           693:                lastarg = *pos;
        !           694:                c = mdoc_args(mdoc, line, pos, buf, 0, &p);
        !           695:                assert(ARGS_PHRASE != c);
        !           696:
        !           697:                if (ARGS_ERROR == c)
        !           698:                        return(0);
        !           699:                else if (ARGS_EOLN == c)
        !           700:                        break;
        !           701:                assert(mdoc_isdelim(p));
        !           702:                if ( ! mdoc_word_alloc(mdoc, line, lastarg, p))
        !           703:                        return(0);
        !           704:                mdoc->next = MDOC_NEXT_SIBLING;
        !           705:        }
        !           706:
        !           707:        return(1);
        !           708: }
        !           709:
        !           710:
        !           711: /*
        !           712:  * Close out block partial/full explicit.
        !           713:  */
        !           714: static int
        !           715: blk_exp_close(MACRO_PROT_ARGS)
        !           716: {
        !           717:        int              j, c, lastarg, maxargs, flushed;
        !           718:        char            *p;
        !           719:
        !           720:        switch (tok) {
        !           721:        case (MDOC_Ec):
        !           722:                maxargs = 1;
        !           723:                break;
        !           724:        default:
        !           725:                maxargs = 0;
        !           726:                break;
        !           727:        }
        !           728:
        !           729:        if ( ! (MDOC_CALLABLE & mdoc_macros[tok].flags)) {
        !           730:                if (0 == buf[*pos]) {
        !           731:                        if ( ! rew_subblock(MDOC_BODY, mdoc,
        !           732:                                                tok, line, ppos))
        !           733:                                return(0);
        !           734:                        return(rew_expblock(mdoc, tok, line, ppos));
        !           735:                }
        !           736:                return(perr(mdoc, line, ppos, ENOPARMS));
        !           737:        }
        !           738:
        !           739:        if ( ! rew_subblock(MDOC_BODY, mdoc, tok, line, ppos))
        !           740:                return(0);
        !           741:
        !           742:        if (maxargs > 0) {
        !           743:                if ( ! mdoc_tail_alloc(mdoc, line,
        !           744:                                        ppos, rew_alt(tok)))
        !           745:                        return(0);
        !           746:                mdoc->next = MDOC_NEXT_CHILD;
        !           747:        }
        !           748:
        !           749:        for (lastarg = ppos, flushed = j = 0; ; j++) {
        !           750:                lastarg = *pos;
        !           751:
        !           752:                if (j == maxargs && ! flushed) {
        !           753:                        if ( ! rew_expblock(mdoc, tok, line, ppos))
        !           754:                                return(0);
        !           755:                        flushed = 1;
        !           756:                }
        !           757:
        !           758:                c = mdoc_args(mdoc, line, pos, buf, tok, &p);
        !           759:
        !           760:                if (ARGS_ERROR == c)
        !           761:                        return(0);
        !           762:                if (ARGS_PUNCT == c)
        !           763:                        break;
        !           764:                if (ARGS_EOLN == c)
        !           765:                        break;
        !           766:
        !           767:                if (-1 == (c = lookup(mdoc, line, lastarg, tok, p)))
        !           768:                        return(0);
        !           769:                else if (MDOC_MAX != c) {
        !           770:                        if ( ! flushed) {
        !           771:                                if ( ! rew_expblock(mdoc, tok,
        !           772:                                                        line, ppos))
        !           773:                                        return(0);
        !           774:                                flushed = 1;
        !           775:                        }
        !           776:                        if ( ! mdoc_macro(mdoc, c, line, lastarg, pos, buf))
        !           777:                                return(0);
        !           778:                        break;
        !           779:                }
        !           780:
        !           781:                if ( ! mdoc_word_alloc(mdoc, line, lastarg, p))
        !           782:                        return(0);
        !           783:                mdoc->next = MDOC_NEXT_SIBLING;
        !           784:        }
        !           785:
        !           786:        if ( ! flushed && ! rew_expblock(mdoc, tok, line, ppos))
        !           787:                return(0);
        !           788:
        !           789:        if (ppos > 1)
        !           790:                return(1);
        !           791:        return(append_delims(mdoc, line, pos, buf));
        !           792: }
        !           793:
        !           794:
        !           795: /*
        !           796:  * In-line macros where reserved words cause scope close-reopen.
        !           797:  */
        !           798: static int
        !           799: in_line(MACRO_PROT_ARGS)
        !           800: {
        !           801:        int               la, lastpunct, c, w, cnt, d, nc;
        !           802:        struct mdoc_arg  *arg;
        !           803:        char             *p;
        !           804:
        !           805:        /*
        !           806:         * Whether we allow ignored elements (those without content,
        !           807:         * usually because of reserved words) to squeak by.
        !           808:         */
        !           809:        switch (tok) {
        !           810:        case (MDOC_Lp):
        !           811:                /* FALLTHROUGH */
        !           812:        case (MDOC_Pp):
        !           813:                /* FALLTHROUGH */
        !           814:        case (MDOC_Nm):
        !           815:                /* FALLTHROUGH */
        !           816:        case (MDOC_Fl):
        !           817:                /* FALLTHROUGH */
        !           818:        case (MDOC_Ar):
        !           819:                nc = 1;
        !           820:                break;
        !           821:        default:
        !           822:                nc = 0;
        !           823:                break;
        !           824:        }
        !           825:
        !           826:        for (la = ppos, arg = NULL;; ) {
        !           827:                la = *pos;
        !           828:                c = mdoc_argv(mdoc, line, tok, &arg, pos, buf);
        !           829:
        !           830:                if (ARGV_WORD == c) {
        !           831:                        *pos = la;
        !           832:                        break;
        !           833:                }
        !           834:                if (ARGV_EOLN == c)
        !           835:                        break;
        !           836:                if (ARGV_ARG == c)
        !           837:                        continue;
        !           838:
        !           839:                mdoc_argv_free(arg);
        !           840:                return(0);
        !           841:        }
        !           842:
        !           843:        for (cnt = 0, lastpunct = 1;; ) {
        !           844:                la = *pos;
        !           845:                w = mdoc_args(mdoc, line, pos, buf, tok, &p);
        !           846:
        !           847:                if (ARGS_ERROR == w)
        !           848:                        return(0);
        !           849:                if (ARGS_EOLN == w)
        !           850:                        break;
        !           851:                if (ARGS_PUNCT == w)
        !           852:                        break;
        !           853:
        !           854:                /* Quoted words shouldn't be looked-up. */
        !           855:
        !           856:                c = ARGS_QWORD == w ? MDOC_MAX :
        !           857:                        lookup(mdoc, line, la, tok, p);
        !           858:
        !           859:                /*
        !           860:                 * In this case, we've located a submacro and must
        !           861:                 * execute it.  Close out scope, if open.  If no
        !           862:                 * elements have been generated, either create one (nc)
        !           863:                 * or raise a warning.
        !           864:                 */
        !           865:
        !           866:                if (MDOC_MAX != c && -1 != c) {
        !           867:                        if (0 == lastpunct && ! rew_elem(mdoc, tok))
        !           868:                                return(0);
        !           869:                        if (nc && 0 == cnt) {
        !           870:                                if ( ! mdoc_elem_alloc(mdoc, line, ppos,
        !           871:                                                        tok, arg))
        !           872:                                        return(0);
        !           873:                                mdoc->next = MDOC_NEXT_SIBLING;
        !           874:                        } else if ( ! nc && 0 == cnt) {
        !           875:                                mdoc_argv_free(arg);
        !           876:                                if ( ! pwarn(mdoc, line, ppos, WIGNE))
        !           877:                                        return(0);
        !           878:                        }
        !           879:                        c = mdoc_macro(mdoc, c, line, la, pos, buf);
        !           880:                        if (0 == c)
        !           881:                                return(0);
        !           882:                        if (ppos > 1)
        !           883:                                return(1);
        !           884:                        return(append_delims(mdoc, line, pos, buf));
        !           885:                } else if (-1 == c)
        !           886:                        return(0);
        !           887:
        !           888:                /*
        !           889:                 * Non-quote-enclosed punctuation.  Set up our scope, if
        !           890:                 * a word; rewind the scope, if a delimiter; then append
        !           891:                 * the word.
        !           892:                 */
        !           893:
        !           894:                d = mdoc_isdelim(p);
        !           895:
        !           896:                if (ARGS_QWORD != w && d) {
        !           897:                        if (0 == lastpunct && ! rew_elem(mdoc, tok))
        !           898:                                return(0);
        !           899:                        lastpunct = 1;
        !           900:                } else if (lastpunct) {
        !           901:                        c = mdoc_elem_alloc(mdoc, line, ppos, tok, arg);
        !           902:                        if (0 == c)
        !           903:                                return(0);
        !           904:                        mdoc->next = MDOC_NEXT_CHILD;
        !           905:                        lastpunct = 0;
        !           906:                }
        !           907:
        !           908:                if ( ! d)
        !           909:                        cnt++;
        !           910:                if ( ! mdoc_word_alloc(mdoc, line, la, p))
        !           911:                        return(0);
        !           912:                mdoc->next = MDOC_NEXT_SIBLING;
        !           913:        }
        !           914:
        !           915:        if (0 == lastpunct && ! rew_elem(mdoc, tok))
        !           916:                return(0);
        !           917:
        !           918:        /*
        !           919:         * If no elements have been collected and we're allowed to have
        !           920:         * empties (nc), open a scope and close it out.  Otherwise,
        !           921:         * raise a warning.
        !           922:         *
        !           923:         */
        !           924:        if (nc && 0 == cnt) {
        !           925:                c = mdoc_elem_alloc(mdoc, line, ppos, tok, arg);
        !           926:                if (0 == c)
        !           927:                        return(0);
        !           928:                mdoc->next = MDOC_NEXT_SIBLING;
        !           929:        } else if ( ! nc && 0 == cnt)  {
        !           930:                mdoc_argv_free(arg);
        !           931:                if ( ! pwarn(mdoc, line, ppos, WIGNE))
        !           932:                        return(0);
        !           933:        }
        !           934:
        !           935:        if (ppos > 1)
        !           936:                return(1);
        !           937:        return(append_delims(mdoc, line, pos, buf));
        !           938: }
        !           939:
        !           940:
        !           941: /*
        !           942:  * Block full-explicit and full-implicit.
        !           943:  */
        !           944: static int
        !           945: blk_full(MACRO_PROT_ARGS)
        !           946: {
        !           947:        int               c, lastarg, reopen;
        !           948:        struct mdoc_arg  *arg;
        !           949:        char             *p;
        !           950:
        !           951:        if ( ! (MDOC_EXPLICIT & mdoc_macros[tok].flags)) {
        !           952:                if ( ! rew_subblock(MDOC_BODY, mdoc,
        !           953:                                        tok, line, ppos))
        !           954:                        return(0);
        !           955:                if ( ! rew_impblock(mdoc, tok, line, ppos))
        !           956:                        return(0);
        !           957:        }
        !           958:
        !           959:        for (arg = NULL;; ) {
        !           960:                lastarg = *pos;
        !           961:                c = mdoc_argv(mdoc, line, tok, &arg, pos, buf);
        !           962:
        !           963:                if (ARGV_WORD == c) {
        !           964:                        *pos = lastarg;
        !           965:                        break;
        !           966:                }
        !           967:
        !           968:                if (ARGV_EOLN == c)
        !           969:                        break;
        !           970:                if (ARGV_ARG == c)
        !           971:                        continue;
        !           972:
        !           973:                mdoc_argv_free(arg);
        !           974:                return(0);
        !           975:        }
        !           976:
        !           977:        if ( ! mdoc_block_alloc(mdoc, line, ppos, tok, arg))
        !           978:                return(0);
        !           979:        mdoc->next = MDOC_NEXT_CHILD;
        !           980:
        !           981:        if (0 == buf[*pos]) {
        !           982:                if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
        !           983:                        return(0);
        !           984:                if ( ! rew_subblock(MDOC_HEAD, mdoc,
        !           985:                                        tok, line, ppos))
        !           986:                        return(0);
        !           987:                if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
        !           988:                        return(0);
        !           989:                mdoc->next = MDOC_NEXT_CHILD;
        !           990:                return(1);
        !           991:        }
        !           992:
        !           993:        if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
        !           994:                return(0);
        !           995:        mdoc->next = MDOC_NEXT_CHILD;
        !           996:
        !           997:        for (reopen = 0;; ) {
        !           998:                lastarg = *pos;
        !           999:                c = mdoc_args(mdoc, line, pos, buf, tok, &p);
        !          1000:
        !          1001:                if (ARGS_ERROR == c)
        !          1002:                        return(0);
        !          1003:                if (ARGS_EOLN == c)
        !          1004:                        break;
        !          1005:                if (ARGS_PHRASE == c) {
        !          1006:                        if (reopen && ! mdoc_head_alloc
        !          1007:                                        (mdoc, line, ppos, tok))
        !          1008:                                return(0);
        !          1009:                        mdoc->next = MDOC_NEXT_CHILD;
        !          1010:                        /*
        !          1011:                         * Phrases are self-contained macro phrases used
        !          1012:                         * in the columnar output of a macro. They need
        !          1013:                         * special handling.
        !          1014:                         */
        !          1015:                        if ( ! phrase(mdoc, line, lastarg, buf))
        !          1016:                                return(0);
        !          1017:                        if ( ! rew_subblock(MDOC_HEAD, mdoc,
        !          1018:                                                tok, line, ppos))
        !          1019:                                return(0);
        !          1020:
        !          1021:                        reopen = 1;
        !          1022:                        continue;
        !          1023:                }
        !          1024:
        !          1025:                if (-1 == (c = lookup(mdoc, line, lastarg, tok, p)))
        !          1026:                        return(0);
        !          1027:
        !          1028:                if (MDOC_MAX == c) {
        !          1029:                        if ( ! mdoc_word_alloc(mdoc, line, lastarg, p))
        !          1030:                                return(0);
        !          1031:                        mdoc->next = MDOC_NEXT_SIBLING;
        !          1032:                        continue;
        !          1033:                }
        !          1034:
        !          1035:                if ( ! mdoc_macro(mdoc, c, line, lastarg, pos, buf))
        !          1036:                        return(0);
        !          1037:                break;
        !          1038:        }
        !          1039:
        !          1040:        if (1 == ppos && ! append_delims(mdoc, line, pos, buf))
        !          1041:                return(0);
        !          1042:        if ( ! rew_subblock(MDOC_HEAD, mdoc, tok, line, ppos))
        !          1043:                return(0);
        !          1044:
        !          1045:        if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
        !          1046:                return(0);
        !          1047:        mdoc->next = MDOC_NEXT_CHILD;
        !          1048:
        !          1049:        return(1);
        !          1050: }
        !          1051:
        !          1052:
        !          1053: /*
        !          1054:  * Block partial-imnplicit scope.
        !          1055:  */
        !          1056: static int
        !          1057: blk_part_imp(MACRO_PROT_ARGS)
        !          1058: {
        !          1059:        int               lastarg, c;
        !          1060:        char             *p;
        !          1061:        struct mdoc_node *blk, *body, *n;
        !          1062:
        !          1063:        if ( ! mdoc_block_alloc(mdoc, line, ppos, tok, NULL))
        !          1064:                return(0);
        !          1065:        mdoc->next = MDOC_NEXT_CHILD;
        !          1066:        blk = mdoc->last;
        !          1067:
        !          1068:        if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
        !          1069:                return(0);
        !          1070:        mdoc->next = MDOC_NEXT_SIBLING;
        !          1071:
        !          1072:        if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
        !          1073:                return(0);
        !          1074:        mdoc->next = MDOC_NEXT_CHILD;
        !          1075:        body = mdoc->last;
        !          1076:
        !          1077:        /* XXX - no known argument macros. */
        !          1078:
        !          1079:        for (lastarg = ppos;; ) {
        !          1080:                lastarg = *pos;
        !          1081:                c = mdoc_args(mdoc, line, pos, buf, tok, &p);
        !          1082:                assert(ARGS_PHRASE != c);
        !          1083:
        !          1084:                if (ARGS_ERROR == c)
        !          1085:                        return(0);
        !          1086:                if (ARGS_PUNCT == c)
        !          1087:                        break;
        !          1088:                if (ARGS_EOLN == c)
        !          1089:                        break;
        !          1090:
        !          1091:                if (-1 == (c = lookup(mdoc, line, lastarg, tok, p)))
        !          1092:                        return(0);
        !          1093:                else if (MDOC_MAX == c) {
        !          1094:                        if ( ! mdoc_word_alloc(mdoc, line, lastarg, p))
        !          1095:                                return(0);
        !          1096:                        mdoc->next = MDOC_NEXT_SIBLING;
        !          1097:                        continue;
        !          1098:                }
        !          1099:
        !          1100:                if ( ! mdoc_macro(mdoc, c, line, lastarg, pos, buf))
        !          1101:                        return(0);
        !          1102:                break;
        !          1103:        }
        !          1104:
        !          1105:        /*
        !          1106:         * Since we know what our context is, we can rewind directly to
        !          1107:         * it.  This allows us to accomodate for our scope being
        !          1108:         * violated by another token.
        !          1109:         */
        !          1110:
        !          1111:        for (n = mdoc->last; n; n = n->parent)
        !          1112:                if (body == n)
        !          1113:                        break;
        !          1114:
        !          1115:        if (NULL == n && ! pwarn(mdoc, body->line, body->pos, WIMPBRK))
        !          1116:                        return(0);
        !          1117:
        !          1118:        if (n && ! rew_last(mdoc, body))
        !          1119:                return(0);
        !          1120:
        !          1121:        if (1 == ppos && ! append_delims(mdoc, line, pos, buf))
        !          1122:                return(0);
        !          1123:
        !          1124:        if (n && ! rew_last(mdoc, blk))
        !          1125:                return(0);
        !          1126:
        !          1127:        return(1);
        !          1128: }
        !          1129:
        !          1130:
        !          1131: /*
        !          1132:  * Block partial-explicit macros.
        !          1133:  */
        !          1134: static int
        !          1135: blk_part_exp(MACRO_PROT_ARGS)
        !          1136: {
        !          1137:        int               lastarg, flushed, j, c, maxargs;
        !          1138:        char             *p;
        !          1139:
        !          1140:        lastarg = ppos;
        !          1141:        flushed = 0;
        !          1142:
        !          1143:        /*
        !          1144:         * Number of arguments (head arguments).  Only `Eo' has these,
        !          1145:         */
        !          1146:
        !          1147:        switch (tok) {
        !          1148:        case (MDOC_Eo):
        !          1149:                maxargs = 1;
        !          1150:                break;
        !          1151:        default:
        !          1152:                maxargs = 0;
        !          1153:                break;
        !          1154:        }
        !          1155:
        !          1156:        if ( ! mdoc_block_alloc(mdoc, line, ppos, tok, NULL))
        !          1157:                return(0);
        !          1158:        mdoc->next = MDOC_NEXT_CHILD;
        !          1159:
        !          1160:        if (0 == maxargs) {
        !          1161:                if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
        !          1162:                        return(0);
        !          1163:                if ( ! rew_subblock(MDOC_HEAD, mdoc,
        !          1164:                                        tok, line, ppos))
        !          1165:                        return(0);
        !          1166:                if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
        !          1167:                        return(0);
        !          1168:                flushed = 1;
        !          1169:        } else if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
        !          1170:                return(0);
        !          1171:
        !          1172:        mdoc->next = MDOC_NEXT_CHILD;
        !          1173:
        !          1174:        for (j = 0; ; j++) {
        !          1175:                lastarg = *pos;
        !          1176:                if (j == maxargs && ! flushed) {
        !          1177:                        if ( ! rew_subblock(MDOC_HEAD, mdoc,
        !          1178:                                                tok, line, ppos))
        !          1179:                                return(0);
        !          1180:                        flushed = 1;
        !          1181:                        if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
        !          1182:                                return(0);
        !          1183:                        mdoc->next = MDOC_NEXT_CHILD;
        !          1184:                }
        !          1185:
        !          1186:                c = mdoc_args(mdoc, line, pos, buf, tok, &p);
        !          1187:                assert(ARGS_PHRASE != c);
        !          1188:
        !          1189:                if (ARGS_ERROR == c)
        !          1190:                        return(0);
        !          1191:                if (ARGS_PUNCT == c)
        !          1192:                        break;
        !          1193:                if (ARGS_EOLN == c)
        !          1194:                        break;
        !          1195:
        !          1196:                if (-1 == (c = lookup(mdoc, line, lastarg, tok, p)))
        !          1197:                        return(0);
        !          1198:                else if (MDOC_MAX != c) {
        !          1199:                        if ( ! flushed) {
        !          1200:                                if ( ! rew_subblock(MDOC_HEAD, mdoc,
        !          1201:                                                        tok, line, ppos))
        !          1202:                                        return(0);
        !          1203:                                flushed = 1;
        !          1204:                                if ( ! mdoc_body_alloc(mdoc, line,
        !          1205:                                                        ppos, tok))
        !          1206:                                        return(0);
        !          1207:                                mdoc->next = MDOC_NEXT_CHILD;
        !          1208:                        }
        !          1209:                        if ( ! mdoc_macro(mdoc, c, line, lastarg,
        !          1210:                                                pos, buf))
        !          1211:                                return(0);
        !          1212:                        break;
        !          1213:                }
        !          1214:
        !          1215:                if ( ! flushed && mdoc_isdelim(p)) {
        !          1216:                        if ( ! rew_subblock(MDOC_HEAD, mdoc,
        !          1217:                                                tok, line, ppos))
        !          1218:                                return(0);
        !          1219:                        flushed = 1;
        !          1220:                        if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
        !          1221:                                return(0);
        !          1222:                        mdoc->next = MDOC_NEXT_CHILD;
        !          1223:                }
        !          1224:
        !          1225:                if ( ! mdoc_word_alloc(mdoc, line, lastarg, p))
        !          1226:                        return(0);
        !          1227:                mdoc->next = MDOC_NEXT_SIBLING;
        !          1228:        }
        !          1229:
        !          1230:        if ( ! flushed) {
        !          1231:                if ( ! rew_subblock(MDOC_HEAD, mdoc, tok, line, ppos))
        !          1232:                        return(0);
        !          1233:                if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
        !          1234:                        return(0);
        !          1235:                mdoc->next = MDOC_NEXT_CHILD;
        !          1236:        }
        !          1237:
        !          1238:        if (ppos > 1)
        !          1239:                return(1);
        !          1240:        return(append_delims(mdoc, line, pos, buf));
        !          1241: }
        !          1242:
        !          1243:
        !          1244: /*
        !          1245:  * In-line macros where reserved words signal closure of the macro.
        !          1246:  * Macros also have a fixed number of arguments.
        !          1247:  */
        !          1248: static int
        !          1249: in_line_argn(MACRO_PROT_ARGS)
        !          1250: {
        !          1251:        int               lastarg, flushed, j, c, maxargs;
        !          1252:        struct mdoc_arg  *arg;
        !          1253:        char             *p;
        !          1254:
        !          1255:
        !          1256:        /*
        !          1257:         * Fixed maximum arguments per macro.  Some of these have none
        !          1258:         * and close as soon as the invocation is parsed.
        !          1259:         */
        !          1260:
        !          1261:        switch (tok) {
        !          1262:        case (MDOC_Ap):
        !          1263:                /* FALLTHROUGH */
        !          1264:        case (MDOC_No):
        !          1265:                /* FALLTHROUGH */
        !          1266:        case (MDOC_Ns):
        !          1267:                /* FALLTHROUGH */
        !          1268:        case (MDOC_Ux):
        !          1269:                maxargs = 0;
        !          1270:                break;
        !          1271:        default:
        !          1272:                maxargs = 1;
        !          1273:                break;
        !          1274:        }
        !          1275:
        !          1276:        for (lastarg = ppos, arg = NULL;; ) {
        !          1277:                lastarg = *pos;
        !          1278:                c = mdoc_argv(mdoc, line, tok, &arg, pos, buf);
        !          1279:
        !          1280:                if (ARGV_WORD == c) {
        !          1281:                        *pos = lastarg;
        !          1282:                        break;
        !          1283:                }
        !          1284:
        !          1285:                if (ARGV_EOLN == c)
        !          1286:                        break;
        !          1287:                if (ARGV_ARG == c)
        !          1288:                        continue;
        !          1289:
        !          1290:                mdoc_argv_free(arg);
        !          1291:                return(0);
        !          1292:        }
        !          1293:
        !          1294:        if ( ! mdoc_elem_alloc(mdoc, line, ppos, tok, arg))
        !          1295:                return(0);
        !          1296:        mdoc->next = MDOC_NEXT_CHILD;
        !          1297:
        !          1298:        for (flushed = j = 0; ; j++) {
        !          1299:                lastarg = *pos;
        !          1300:
        !          1301:                if (j == maxargs && ! flushed) {
        !          1302:                        if ( ! rew_elem(mdoc, tok))
        !          1303:                                return(0);
        !          1304:                        flushed = 1;
        !          1305:                }
        !          1306:
        !          1307:                c = mdoc_args(mdoc, line, pos, buf, tok, &p);
        !          1308:
        !          1309:                if (ARGS_ERROR == c)
        !          1310:                        return(0);
        !          1311:                if (ARGS_PUNCT == c)
        !          1312:                        break;
        !          1313:                if (ARGS_EOLN == c)
        !          1314:                        break;
        !          1315:
        !          1316:                if (-1 == (c = lookup(mdoc, line, lastarg, tok, p)))
        !          1317:                        return(0);
        !          1318:                else if (MDOC_MAX != c) {
        !          1319:                        if ( ! flushed && ! rew_elem(mdoc, tok))
        !          1320:                                return(0);
        !          1321:                        flushed = 1;
        !          1322:                        if ( ! mdoc_macro(mdoc, c, line, lastarg, pos, buf))
        !          1323:                                return(0);
        !          1324:                        break;
        !          1325:                }
        !          1326:
        !          1327:                if ( ! (MDOC_IGNDELIM & mdoc_macros[tok].flags) &&
        !          1328:                                ! flushed && mdoc_isdelim(p)) {
        !          1329:                        if ( ! rew_elem(mdoc, tok))
        !          1330:                                return(0);
        !          1331:                        flushed = 1;
        !          1332:                }
        !          1333:
        !          1334:                if ( ! mdoc_word_alloc(mdoc, line, lastarg, p))
        !          1335:                        return(0);
        !          1336:                mdoc->next = MDOC_NEXT_SIBLING;
        !          1337:        }
        !          1338:
        !          1339:        if ( ! flushed && ! rew_elem(mdoc, tok))
        !          1340:                return(0);
        !          1341:
        !          1342:        if (ppos > 1)
        !          1343:                return(1);
        !          1344:        return(append_delims(mdoc, line, pos, buf));
        !          1345: }
        !          1346:
        !          1347:
        !          1348: /*
        !          1349:  * In-line macro that spans an entire line.  May be callable, but has no
        !          1350:  * subsequent parsed arguments.
        !          1351:  */
        !          1352: static int
        !          1353: in_line_eoln(MACRO_PROT_ARGS)
        !          1354: {
        !          1355:        int               c, w, la;
        !          1356:        struct mdoc_arg  *arg;
        !          1357:        char             *p;
        !          1358:
        !          1359:        assert( ! (MDOC_PARSED & mdoc_macros[tok].flags));
        !          1360:
        !          1361:        arg = NULL;
        !          1362:
        !          1363:        for (;;) {
        !          1364:                la = *pos;
        !          1365:                c = mdoc_argv(mdoc, line, tok, &arg, pos, buf);
        !          1366:
        !          1367:                if (ARGV_WORD == c) {
        !          1368:                        *pos = la;
        !          1369:                        break;
        !          1370:                }
        !          1371:                if (ARGV_EOLN == c)
        !          1372:                        break;
        !          1373:                if (ARGV_ARG == c)
        !          1374:                        continue;
        !          1375:
        !          1376:                mdoc_argv_free(arg);
        !          1377:                return(0);
        !          1378:        }
        !          1379:
        !          1380:        if ( ! mdoc_elem_alloc(mdoc, line, ppos, tok, arg))
        !          1381:                return(0);
        !          1382:
        !          1383:        mdoc->next = MDOC_NEXT_CHILD;
        !          1384:
        !          1385:        for (;;) {
        !          1386:                la = *pos;
        !          1387:                w = mdoc_args(mdoc, line, pos, buf, tok, &p);
        !          1388:
        !          1389:                if (ARGS_ERROR == w)
        !          1390:                        return(0);
        !          1391:                if (ARGS_EOLN == w)
        !          1392:                        break;
        !          1393:
        !          1394:                c = ARGS_QWORD == w ? MDOC_MAX :
        !          1395:                        lookup(mdoc, line, la, tok, p);
        !          1396:
        !          1397:                if (MDOC_MAX != c && -1 != c) {
        !          1398:                        if ( ! rew_elem(mdoc, tok))
        !          1399:                                return(0);
        !          1400:                        return(mdoc_macro(mdoc, c, line, la, pos, buf));
        !          1401:                } else if (-1 == c)
        !          1402:                        return(0);
        !          1403:
        !          1404:                if ( ! mdoc_word_alloc(mdoc, line, la, p))
        !          1405:                        return(0);
        !          1406:                mdoc->next = MDOC_NEXT_SIBLING;
        !          1407:        }
        !          1408:
        !          1409:        return(rew_elem(mdoc, tok));
        !          1410: }
        !          1411:
        !          1412:
        !          1413: /* ARGSUSED */
        !          1414: static int
        !          1415: obsolete(MACRO_PROT_ARGS)
        !          1416: {
        !          1417:
        !          1418:        return(pwarn(mdoc, line, ppos, WOBS));
        !          1419: }
        !          1420:
        !          1421:
        !          1422: static int
        !          1423: phrase(struct mdoc *mdoc, int line, int ppos, char *buf)
        !          1424: {
        !          1425:        int              i, la, c, quoted;
        !          1426:
        !          1427:        /*
        !          1428:         * Parse over words in a phrase.  We have to handle this
        !          1429:         * specially because we assume no calling context -- in normal
        !          1430:         * circumstances, we switch argument parsing based on whether
        !          1431:         * the parent macro accepts quotes, tabs, etc.  Here, anything
        !          1432:         * goes.
        !          1433:         */
        !          1434:
        !          1435:        for (i = ppos; buf[i]; ) {
        !          1436:                assert(' ' != buf[i]);
        !          1437:                la = i;
        !          1438:                quoted = 0;
        !          1439:
        !          1440:                /*
        !          1441:                 * Read to next token.  If quoted (check not escaped),
        !          1442:                 * scan ahead to next unescaped quote.  If not quoted or
        !          1443:                 * escape-quoted, then scan ahead to next space.
        !          1444:                 */
        !          1445:
        !          1446:                if ((i && '\"' == buf[i] && '\\' != buf[i - 1]) ||
        !          1447:                                (0 == i && '\"' == buf[i])) {
        !          1448:                        for (la = ++i; buf[i]; i++)
        !          1449:                                if ('\"' != buf[i])
        !          1450:                                        continue;
        !          1451:                                else if ('\\' != buf[i - 1])
        !          1452:                                        break;
        !          1453:                        if (0 == buf[i])
        !          1454:                                return(perr(mdoc, line, la, EQUOT));
        !          1455:                        quoted = 1;
        !          1456:                } else
        !          1457:                        for ( ; buf[i]; i++)
        !          1458:                                if (i && ' ' == buf[i]) {
        !          1459:                                        if ('\\' != buf[i - 1])
        !          1460:                                                break;
        !          1461:                                } else if (' ' == buf[i])
        !          1462:                                        break;
        !          1463:
        !          1464:                /* If not end-of-line, terminate argument. */
        !          1465:
        !          1466:                if (buf[i])
        !          1467:                        buf[i++] = 0;
        !          1468:
        !          1469:                /* Read to next argument. */
        !          1470:
        !          1471:                for ( ; buf[i] && ' ' == buf[i]; i++)
        !          1472:                        /* Spin. */ ;
        !          1473:
        !          1474:                /*
        !          1475:                 * If we're a non-quoted string, try to look up the
        !          1476:                 * value as a macro and execute it, if found.
        !          1477:                 */
        !          1478:
        !          1479:                c = quoted ? MDOC_MAX :
        !          1480:                        mdoc_hash_find(mdoc->htab, &buf[la]);
        !          1481:
        !          1482:                if (MDOC_MAX != c) {
        !          1483:                        if ( ! mdoc_macro(mdoc, c, line, la, &i, buf))
        !          1484:                                return(0);
        !          1485:                        return(append_delims(mdoc, line, &i, buf));
        !          1486:                }
        !          1487:
        !          1488:                /* A regular word or quoted string. */
        !          1489:
        !          1490:                if ( ! mdoc_word_alloc(mdoc, line, la, &buf[la]))
        !          1491:                        return(0);
        !          1492:                mdoc->next = MDOC_NEXT_SIBLING;
        !          1493:        }
        !          1494:
        !          1495:        return(1);
        !          1496: }