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

Annotation of mandoc/mdoc_macro.c, Revision 1.1

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

CVSweb