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

Annotation of docbook2mdoc/docbook2mdoc.c, Revision 1.74

1.74    ! schwarze    1: /* $Id: docbook2mdoc.c,v 1.73 2019/03/25 23:14:44 schwarze Exp $ */
1.1       kristaps    2: /*
                      3:  * Copyright (c) 2014 Kristaps Dzonsons <kristaps@bsd.lv>
1.50      schwarze    4:  * Copyright (c) 2019 Ingo Schwarze <schwarze@openbsd.org>
1.1       kristaps    5:  *
                      6:  * Permission to use, copy, modify, and distribute this software for any
                      7:  * purpose with or without fee is hereby granted, provided that the above
                      8:  * copyright notice and this permission notice appear in all copies.
                      9:  *
                     10:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
                     11:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     12:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
                     13:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     14:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     15:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     16:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     17:  */
                     18: #include <assert.h>
                     19: #include <ctype.h>
                     20: #include <stdio.h>
                     21: #include <stdlib.h>
                     22:
1.74    ! schwarze   23: #include "node.h"
        !            24: #include "format.h"
        !            25:
        !            26: /*
        !            27:  * The implementation of the mdoc(7) formatter.
        !            28:  */
1.12      kristaps   29:
1.69      schwarze   30: enum   linestate {
                     31:        LINE_NEW = 0,
                     32:        LINE_TEXT,
                     33:        LINE_MACRO
                     34: };
                     35:
1.74    ! schwarze   36: struct format {
        !            37:        int              level;      /* Header level, starting at 1. */
1.69      schwarze   38:        enum linestate   linestate;
1.1       kristaps   39: };
                     40:
1.74    ! schwarze   41: static void     pnode_print(struct format *, struct pnode *);
1.25      kristaps   42:
                     43:
1.69      schwarze   44: static void
1.74    ! schwarze   45: macro_open(struct format *p, const char *name)
1.69      schwarze   46: {
                     47:        switch (p->linestate) {
                     48:        case LINE_TEXT:
                     49:                putchar('\n');
                     50:                /* FALLTHROUGH */
                     51:        case LINE_NEW:
                     52:                putchar('.');
                     53:                p->linestate = LINE_MACRO;
                     54:                break;
                     55:        case LINE_MACRO:
                     56:                putchar(' ');
                     57:                break;
                     58:        }
                     59:        fputs(name, stdout);
                     60: }
                     61:
                     62: static void
1.74    ! schwarze   63: macro_close(struct format *p)
1.69      schwarze   64: {
                     65:        assert(p->linestate == LINE_MACRO);
                     66:        putchar('\n');
                     67:        p->linestate = LINE_NEW;
                     68: }
                     69:
                     70: static void
1.74    ! schwarze   71: macro_line(struct format *p, const char *name)
1.69      schwarze   72: {
                     73:        macro_open(p, name);
                     74:        macro_close(p);
                     75: }
                     76:
1.72      schwarze   77: #define        ARG_SPACE       1  /* Insert whitespace before this argument. */
                     78: #define        ARG_SINGLE      2  /* Quote argument if it contains whitespace. */
                     79: #define        ARG_QUOTED      4  /* We are already in a quoted argument. */
                     80: #define        ARG_UPPER       8  /* Covert argument to upper case. */
1.1       kristaps   81: /*
1.69      schwarze   82:  * Print an argument string on a macro line, collapsing whitespace.
1.1       kristaps   83:  */
                     84: static void
1.74    ! schwarze   85: macro_addarg(struct format *p, const char *arg, int flags)
1.1       kristaps   86: {
1.69      schwarze   87:        const char      *cp;
1.1       kristaps   88:
1.69      schwarze   89:        assert(p->linestate == LINE_MACRO);
1.72      schwarze   90:
                     91:        /* Quote if requested and necessary. */
                     92:
                     93:        if ((flags & (ARG_SINGLE | ARG_QUOTED)) == ARG_SINGLE) {
                     94:                for (cp = arg; *cp != '\0'; cp++)
                     95:                        if (isspace((unsigned char)*cp))
                     96:                                break;
                     97:                if (*cp != '\0') {
                     98:                        if (flags & ARG_SPACE) {
                     99:                                putchar(' ');
                    100:                                flags &= ~ ARG_SPACE;
                    101:                        }
                    102:                        putchar('"');
                    103:                        flags = ARG_QUOTED;
                    104:                }
                    105:        }
                    106:
1.69      schwarze  107:        for (cp = arg; *cp != '\0'; cp++) {
1.72      schwarze  108:
                    109:                /* Collapse whitespace. */
                    110:
1.69      schwarze  111:                if (isspace((unsigned char)*cp)) {
1.72      schwarze  112:                        flags |= ARG_SPACE;
1.69      schwarze  113:                        continue;
1.72      schwarze  114:                } else if (flags & ARG_SPACE) {
1.69      schwarze  115:                        putchar(' ');
1.72      schwarze  116:                        flags &= ~ ARG_SPACE;
1.69      schwarze  117:                }
1.72      schwarze  118:
1.1       kristaps  119:                /* Escape us if we look like a macro. */
1.72      schwarze  120:
                    121:                if ((flags & ARG_QUOTED) == 0 &&
                    122:                    (cp == arg || isspace((unsigned char)cp[-1])) &&
1.64      schwarze  123:                    isupper((unsigned char)cp[0]) &&
                    124:                    islower((unsigned char)cp[1]) &&
                    125:                    (cp[2] == '\0' || cp[2] == ' ' ||
                    126:                     (islower((unsigned char)cp[2]) &&
                    127:                      (cp[3] == '\0' || cp[3] == ' '))))
1.1       kristaps  128:                        fputs("\\&", stdout);
1.72      schwarze  129:
                    130:                if (*cp == '"')
                    131:                        fputs("\\(dq", stdout);
                    132:                else if (flags & ARG_UPPER)
1.46      schwarze  133:                        putchar(toupper((unsigned char)*cp));
1.12      kristaps  134:                else
1.46      schwarze  135:                        putchar(*cp);
1.64      schwarze  136:                if (*cp == '\\')
1.1       kristaps  137:                        putchar('e');
                    138:        }
                    139: }
                    140:
1.12      kristaps  141: static void
1.74    ! schwarze  142: macro_argline(struct format *p, const char *name, const char *arg)
1.12      kristaps  143: {
1.69      schwarze  144:        macro_open(p, name);
1.72      schwarze  145:        macro_addarg(p, arg, ARG_SPACE);
1.69      schwarze  146:        macro_close(p);
1.12      kristaps  147: }
                    148:
1.1       kristaps  149: /*
1.72      schwarze  150:  * Recursively append text from the children of a node to a macro line.
1.1       kristaps  151:  */
                    152: static void
1.74    ! schwarze  153: macro_addnode(struct format *p, struct pnode *pn, int flags)
1.1       kristaps  154: {
1.72      schwarze  155:        int              quote_now;
                    156:
                    157:        assert(p->linestate == LINE_MACRO);
                    158:
                    159:        /*
                    160:         * If the only child is a text node, just add that text,
                    161:         * letting macro_addarg() decide about quoting.
                    162:         */
                    163:
                    164:        pn = TAILQ_FIRST(&pn->childq);
                    165:        if (pn != NULL && pn->node == NODE_TEXT &&
                    166:            TAILQ_NEXT(pn, child) == NULL) {
                    167:                macro_addarg(p, pn->b, flags);
                    168:                return;
                    169:        }
                    170:
                    171:        /*
                    172:         * If we want the argument quoted and are not already
                    173:         * in a quoted context, quote now.
                    174:         */
                    175:
                    176:        quote_now = 0;
                    177:        if (flags & ARG_SINGLE) {
                    178:                if ((flags & ARG_QUOTED) == 0) {
                    179:                        if (flags & ARG_SPACE) {
                    180:                                putchar(' ');
                    181:                                flags &= ~ARG_SPACE;
                    182:                        }
                    183:                        putchar('"');
                    184:                        flags |= ARG_QUOTED;
                    185:                        quote_now = 1;
                    186:                }
                    187:                flags &= ~ARG_SINGLE;
                    188:        }
                    189:
                    190:        /*
                    191:         * Iterate to child and sibling nodes,
                    192:         * inserting whitespace between nodes.
                    193:         */
                    194:
                    195:        while (pn != NULL) {
                    196:                if (pn->node == NODE_TEXT)
                    197:                        macro_addarg(p, pn->b, flags);
                    198:                else
                    199:                        macro_addnode(p, pn, flags);
                    200:                pn = TAILQ_NEXT(pn, child);
                    201:                flags |= ARG_SPACE;
                    202:        }
                    203:        if (quote_now)
                    204:                putchar('"');
1.1       kristaps  205: }
                    206:
1.10      kristaps  207: static void
1.74    ! schwarze  208: macro_nodeline(struct format *p, const char *name, struct pnode *pn, int flags)
1.10      kristaps  209: {
1.69      schwarze  210:        macro_open(p, name);
1.72      schwarze  211:        macro_addnode(p, pn, ARG_SPACE | flags);
1.69      schwarze  212:        macro_close(p);
1.10      kristaps  213: }
                    214:
1.8       kristaps  215: /*
1.69      schwarze  216:  * If the next node is a text node starting with closing punctuation,
                    217:  * emit the closing punctuation as a trailing macro argument.
1.37      kristaps  218:  */
                    219: static void
1.74    ! schwarze  220: macro_closepunct(struct format *p, struct pnode *pn)
1.37      kristaps  221: {
1.69      schwarze  222:        if ((pn = TAILQ_NEXT(pn, child)) != NULL &&
                    223:            pn->node == NODE_TEXT && pn->bsz > 0 &&
1.64      schwarze  224:            (pn->b[0] == ',' || pn->b[0] == '.') &&
                    225:            (pn->bsz == 1 || isspace((unsigned char)pn->b[1]))) {
1.37      kristaps  226:                putchar(' ');
                    227:                putchar(pn->b[0]);
                    228:                pn->b++;
                    229:                pn->bsz--;
1.44      schwarze  230:        }
1.69      schwarze  231:        macro_close(p);
1.37      kristaps  232: }
                    233:
1.54      schwarze  234: static void
1.74    ! schwarze  235: print_text(struct format *p, const char *word)
1.70      schwarze  236: {
                    237:        switch (p->linestate) {
                    238:        case LINE_NEW:
                    239:                break;
                    240:        case LINE_TEXT:
                    241:                putchar(' ');
                    242:                break;
                    243:        case LINE_MACRO:
                    244:                macro_close(p);
                    245:                break;
                    246:        }
                    247:        fputs(word, stdout);
                    248:        p->linestate = LINE_TEXT;
                    249: }
                    250:
                    251: static void
1.74    ! schwarze  252: pnode_printpara(struct format *p, struct pnode *pn)
1.54      schwarze  253: {
                    254:        struct pnode    *pp;
                    255:
1.61      schwarze  256:        if ((pp = TAILQ_PREV(pn, pnodeq, child)) == NULL &&
                    257:            (pp = pn->parent) == NULL)
1.54      schwarze  258:                return;
                    259:
1.61      schwarze  260:        switch (pp->node) {
                    261:        case NODE_ENTRY:
                    262:        case NODE_LISTITEM:
                    263:                return;
                    264:        case NODE_PREFACE:
                    265:        case NODE_SECTION:
                    266:                if (p->level < 3)
                    267:                        return;
                    268:                break;
                    269:        default:
                    270:                break;
                    271:        }
1.69      schwarze  272:        macro_line(p, "Pp");
1.54      schwarze  273: }
                    274:
1.37      kristaps  275: /*
1.10      kristaps  276:  * If the SYNOPSIS macro has a superfluous title, kill it.
1.8       kristaps  277:  */
1.1       kristaps  278: static void
1.74    ! schwarze  279: pnode_printrefsynopsisdiv(struct format *p, struct pnode *pn)
1.6       kristaps  280: {
1.71      schwarze  281:        struct pnode    *pp, *pq;
1.6       kristaps  282:
1.71      schwarze  283:        TAILQ_FOREACH_SAFE(pp, &pn->childq, child, pq)
                    284:                if (pp->node == NODE_TITLE)
1.6       kristaps  285:                        pnode_unlink(pp);
1.71      schwarze  286:
                    287:        macro_line(p, "Sh SYNOPSIS");
1.6       kristaps  288: }
                    289:
1.8       kristaps  290: /*
                    291:  * Start a hopefully-named `Sh' section.
                    292:  */
1.6       kristaps  293: static void
1.74    ! schwarze  294: pnode_printrefsect(struct format *p, struct pnode *pn)
1.1       kristaps  295: {
                    296:        struct pnode    *pp;
1.52      schwarze  297:        const char      *title;
                    298:        int              flags, level;
                    299:
1.64      schwarze  300:        if (pn->parent == NULL)
1.56      schwarze  301:                return;
                    302:
1.52      schwarze  303:        level = ++p->level;
1.72      schwarze  304:        flags = ARG_SPACE;
                    305:        if (level == 1)
                    306:                flags |= ARG_UPPER;
1.64      schwarze  307:        if (level < 3) {
1.52      schwarze  308:                switch (pn->node) {
1.62      schwarze  309:                case NODE_CAUTION:
                    310:                case NODE_NOTE:
                    311:                case NODE_TIP:
                    312:                case NODE_WARNING:
1.52      schwarze  313:                        level = 3;
                    314:                        break;
                    315:                default:
                    316:                        break;
                    317:                }
                    318:        }
1.1       kristaps  319:
                    320:        TAILQ_FOREACH(pp, &pn->childq, child)
1.64      schwarze  321:                if (pp->node == NODE_TITLE)
1.1       kristaps  322:                        break;
                    323:
1.64      schwarze  324:        if (pp == NULL) {
1.52      schwarze  325:                switch (pn->node) {
1.62      schwarze  326:                case NODE_PREFACE:
1.52      schwarze  327:                        title = "Preface";
                    328:                        break;
1.62      schwarze  329:                case NODE_CAUTION:
1.52      schwarze  330:                        title = "Caution";
                    331:                        break;
1.62      schwarze  332:                case NODE_NOTE:
1.52      schwarze  333:                        title = "Note";
                    334:                        break;
1.62      schwarze  335:                case NODE_TIP:
1.52      schwarze  336:                        title = "Tip";
                    337:                        break;
1.62      schwarze  338:                case NODE_WARNING:
1.52      schwarze  339:                        title = "Warning";
                    340:                        break;
                    341:                default:
                    342:                        title = "Unknown";
                    343:                        break;
                    344:                }
                    345:        }
                    346:
                    347:        switch (level) {
1.62      schwarze  348:        case 1:
1.69      schwarze  349:                macro_open(p, "Sh");
1.29      kristaps  350:                break;
1.62      schwarze  351:        case 2:
1.69      schwarze  352:                macro_open(p, "Ss");
1.29      kristaps  353:                break;
1.52      schwarze  354:        default:
1.54      schwarze  355:                pnode_printpara(p, pn);
1.69      schwarze  356:                macro_open(p, "Sy");
1.29      kristaps  357:                break;
                    358:        }
1.20      kristaps  359:
1.64      schwarze  360:        if (pp != NULL) {
1.69      schwarze  361:                macro_addnode(p, pp, flags);
1.5       kristaps  362:                pnode_unlink(pp);
1.52      schwarze  363:        } else
1.72      schwarze  364:                macro_addarg(p, title, ARG_SPACE | ARG_QUOTED);
1.69      schwarze  365:        macro_close(p);
1.1       kristaps  366: }
                    367:
1.8       kristaps  368: /*
                    369:  * Start a reference, extracting the title and volume.
                    370:  */
1.1       kristaps  371: static void
1.74    ! schwarze  372: pnode_printciterefentry(struct format *p, struct pnode *pn)
1.1       kristaps  373: {
                    374:        struct pnode    *pp, *title, *manvol;
                    375:
                    376:        title = manvol = NULL;
1.69      schwarze  377:        TAILQ_FOREACH(pp, &pn->childq, child) {
1.64      schwarze  378:                if (pp->node == NODE_MANVOLNUM)
1.1       kristaps  379:                        manvol = pp;
1.64      schwarze  380:                else if (pp->node == NODE_REFENTRYTITLE)
1.1       kristaps  381:                        title = pp;
1.69      schwarze  382:        }
                    383:        macro_open(p, "Xr");
                    384:        if (title == NULL)
1.72      schwarze  385:                macro_addarg(p, "unknown", ARG_SPACE);
1.69      schwarze  386:        else
1.72      schwarze  387:                macro_addnode(p, title, ARG_SPACE | ARG_SINGLE);
1.69      schwarze  388:        if (manvol == NULL)
1.72      schwarze  389:                macro_addarg(p, "1", ARG_SPACE);
1.64      schwarze  390:        else
1.72      schwarze  391:                macro_addnode(p, manvol, ARG_SPACE | ARG_SINGLE);
1.69      schwarze  392:        macro_close(p);
1.71      schwarze  393:        pnode_unlinksub(pn);
1.1       kristaps  394: }
                    395:
                    396: static void
1.74    ! schwarze  397: pnode_printrefmeta(struct format *p, struct pnode *pn)
1.1       kristaps  398: {
                    399:        struct pnode    *pp, *title, *manvol;
                    400:
                    401:        title = manvol = NULL;
1.69      schwarze  402:        TAILQ_FOREACH(pp, &pn->childq, child) {
1.64      schwarze  403:                if (pp->node == NODE_MANVOLNUM)
1.1       kristaps  404:                        manvol = pp;
1.64      schwarze  405:                else if (pp->node == NODE_REFENTRYTITLE)
1.1       kristaps  406:                        title = pp;
1.69      schwarze  407:        }
                    408:        macro_open(p, "Dt");
                    409:        if (title == NULL)
1.72      schwarze  410:                macro_addarg(p, "UNKNOWN", ARG_SPACE);
1.69      schwarze  411:        else
1.72      schwarze  412:                macro_addnode(p, title, ARG_SPACE | ARG_SINGLE | ARG_UPPER);
1.69      schwarze  413:        if (manvol == NULL)
1.72      schwarze  414:                macro_addarg(p, "1", ARG_SPACE);
1.13      kristaps  415:        else
1.72      schwarze  416:                macro_addnode(p, manvol, ARG_SPACE | ARG_SINGLE);
1.69      schwarze  417:        macro_close(p);
1.71      schwarze  418:        pnode_unlink(pn);
1.1       kristaps  419: }
                    420:
1.3       kristaps  421: static void
1.74    ! schwarze  422: pnode_printfuncdef(struct format *p, struct pnode *pn)
1.3       kristaps  423: {
                    424:        struct pnode    *pp, *ftype, *func;
                    425:
                    426:        ftype = func = NULL;
1.69      schwarze  427:        TAILQ_FOREACH(pp, &pn->childq, child) {
1.64      schwarze  428:                if (pp->node == NODE_TEXT)
1.3       kristaps  429:                        ftype = pp;
1.64      schwarze  430:                else if (pp->node == NODE_FUNCTION)
1.3       kristaps  431:                        func = pp;
1.13      kristaps  432:        }
1.69      schwarze  433:        if (ftype != NULL)
1.72      schwarze  434:                macro_argline(p, "Ft", ftype->b);
1.69      schwarze  435:        macro_open(p, "Fo");
                    436:        if (func == NULL)
1.72      schwarze  437:                macro_addarg(p, "UNKNOWN", ARG_SPACE);
1.69      schwarze  438:        else
1.72      schwarze  439:                macro_addnode(p, func, ARG_SPACE | ARG_SINGLE);
1.69      schwarze  440:        macro_close(p);
1.3       kristaps  441: }
                    442:
1.40      kristaps  443: /*
1.41      kristaps  444:  * The <mml:mfenced> node is a little peculiar.
                    445:  * First, it can have arbitrary open and closing tokens, which default
                    446:  * to parentheses.
                    447:  * Second, >1 arguments are separated by commas.
                    448:  */
                    449: static void
1.74    ! schwarze  450: pnode_printmathfenced(struct format *p, struct pnode *pn)
1.41      kristaps  451: {
                    452:        struct pnode    *pp;
                    453:
1.59      schwarze  454:        printf("left %s ", pnode_getattr_raw(pn, ATTRKEY_OPEN, "("));
1.41      kristaps  455:
                    456:        pp = TAILQ_FIRST(&pn->childq);
                    457:        pnode_print(p, pp);
                    458:
1.64      schwarze  459:        while ((pp = TAILQ_NEXT(pp, child)) != NULL) {
1.41      kristaps  460:                putchar(',');
                    461:                pnode_print(p, pp);
                    462:        }
1.59      schwarze  463:        printf("right %s ", pnode_getattr_raw(pn, ATTRKEY_CLOSE, ")"));
1.71      schwarze  464:        pnode_unlinksub(pn);
1.41      kristaps  465: }
                    466:
                    467: /*
1.40      kristaps  468:  * These math nodes require special handling because they have infix
                    469:  * syntax, instead of the usual prefix or prefix.
                    470:  * So we need to break up the first and second child node with a
                    471:  * particular eqn(7) word.
                    472:  */
                    473: static void
1.74    ! schwarze  474: pnode_printmath(struct format *p, struct pnode *pn)
1.40      kristaps  475: {
                    476:        struct pnode    *pp;
                    477:
                    478:        pp = TAILQ_FIRST(&pn->childq);
                    479:        pnode_print(p, pp);
                    480:
                    481:        switch (pn->node) {
1.62      schwarze  482:        case NODE_MML_MSUP:
1.42      kristaps  483:                fputs(" sup ", stdout);
1.40      kristaps  484:                break;
1.62      schwarze  485:        case NODE_MML_MFRAC:
1.42      kristaps  486:                fputs(" over ", stdout);
1.40      kristaps  487:                break;
1.62      schwarze  488:        case NODE_MML_MSUB:
1.42      kristaps  489:                fputs(" sub ", stdout);
1.40      kristaps  490:                break;
                    491:        default:
                    492:                break;
                    493:        }
                    494:
                    495:        pp = TAILQ_NEXT(pp, child);
                    496:        pnode_print(p, pp);
1.71      schwarze  497:        pnode_unlinksub(pn);
1.40      kristaps  498: }
                    499:
1.3       kristaps  500: static void
1.74    ! schwarze  501: pnode_printfuncprototype(struct format *p, struct pnode *pn)
1.3       kristaps  502: {
                    503:        struct pnode    *pp, *fdef;
                    504:
                    505:        TAILQ_FOREACH(fdef, &pn->childq, child)
1.64      schwarze  506:                if (fdef->node == NODE_FUNCDEF)
1.3       kristaps  507:                        break;
                    508:
1.64      schwarze  509:        if (fdef != NULL)
1.3       kristaps  510:                pnode_printfuncdef(p, fdef);
1.4       kristaps  511:        else
1.69      schwarze  512:                macro_line(p, "Fo UNKNOWN");
1.3       kristaps  513:
1.44      schwarze  514:        TAILQ_FOREACH(pp, &pn->childq, child)
1.64      schwarze  515:                if (pp->node == NODE_PARAMDEF)
1.72      schwarze  516:                        macro_nodeline(p, "Fa", pp, ARG_SINGLE);
1.3       kristaps  517:
1.69      schwarze  518:        macro_line(p, "Fc");
1.71      schwarze  519:        pnode_unlinksub(pn);
1.3       kristaps  520: }
                    521:
1.44      schwarze  522: /*
1.10      kristaps  523:  * The <arg> element is more complicated than it should be because text
                    524:  * nodes are treated like ".Ar foo", but non-text nodes need to be
                    525:  * re-sent into the printer (i.e., without the preceding ".Ar").
1.12      kristaps  526:  * This also handles the case of "repetition" (or in other words, the
                    527:  * ellipsis following an argument) and optionality.
1.10      kristaps  528:  */
1.4       kristaps  529: static void
1.74    ! schwarze  530: pnode_printarg(struct format *p, struct pnode *pn)
1.4       kristaps  531: {
                    532:        struct pnode    *pp;
1.12      kristaps  533:        struct pattr    *ap;
                    534:        int              isop, isrep;
                    535:
                    536:        isop = 1;
                    537:        isrep = 0;
1.69      schwarze  538:        TAILQ_FOREACH(ap, &pn->attrq, child) {
1.64      schwarze  539:                if (ap->key == ATTRKEY_CHOICE &&
                    540:                    (ap->val == ATTRVAL_PLAIN || ap->val == ATTRVAL_REQ))
1.12      kristaps  541:                        isop = 0;
1.64      schwarze  542:                else if (ap->key == ATTRKEY_REP && ap->val == ATTRVAL_REPEAT)
1.12      kristaps  543:                        isrep = 1;
                    544:        }
1.69      schwarze  545:        if (isop)
                    546:                macro_open(p, "Op");
1.4       kristaps  547:
1.10      kristaps  548:        TAILQ_FOREACH(pp, &pn->childq, child) {
1.69      schwarze  549:                if (pp->node == NODE_TEXT)
                    550:                        macro_open(p, "Ar");
1.10      kristaps  551:                pnode_print(p, pp);
1.64      schwarze  552:                if (isrep && pp->node == NODE_TEXT)
1.72      schwarze  553:                        macro_addarg(p, "...", ARG_SPACE);
1.10      kristaps  554:        }
1.71      schwarze  555:        pnode_unlinksub(pn);
1.4       kristaps  556: }
                    557:
1.24      kristaps  558: static void
1.74    ! schwarze  559: pnode_printgroup(struct format *p, struct pnode *pn)
1.24      kristaps  560: {
                    561:        struct pnode    *pp, *np;
                    562:        struct pattr    *ap;
                    563:        int              isop, sv;
                    564:
                    565:        isop = 1;
1.44      schwarze  566:        TAILQ_FOREACH(ap, &pn->attrq, child)
1.64      schwarze  567:                if (ap->key == ATTRKEY_CHOICE &&
                    568:                    (ap->val == ATTRVAL_PLAIN || ap->val == ATTRVAL_REQ)) {
1.24      kristaps  569:                        isop = 0;
                    570:                        break;
                    571:                }
                    572:
1.44      schwarze  573:        /*
1.24      kristaps  574:         * Make sure we're on a macro line.
                    575:         * This will prevent pnode_print() for putting us on a
                    576:         * subsequent line.
                    577:         */
1.69      schwarze  578:        sv = p->linestate == LINE_NEW;
1.44      schwarze  579:        if (isop)
1.69      schwarze  580:                macro_open(p, "Op");
1.24      kristaps  581:        else if (sv)
1.69      schwarze  582:                macro_open(p, "No");
1.24      kristaps  583:
                    584:        /*
                    585:         * Keep on printing text separated by the vertical bar as long
                    586:         * as we're within the same origin node as the group.
                    587:         * This is kind of a nightmare.
                    588:         * Eh, DocBook...
                    589:         * FIXME: if there's a "Fl", we don't cut off the leading "-"
                    590:         * like we do in pnode_print().
                    591:         */
                    592:        TAILQ_FOREACH(pp, &pn->childq, child) {
                    593:                pnode_print(p, pp);
                    594:                np = TAILQ_NEXT(pp, child);
1.64      schwarze  595:                while (np != NULL) {
1.24      kristaps  596:                        if (pp->node != np->node)
                    597:                                break;
1.72      schwarze  598:                        macro_addarg(p, "|", ARG_SPACE);
                    599:                        macro_addnode(p, np, ARG_SPACE);
1.24      kristaps  600:                        pp = np;
                    601:                        np = TAILQ_NEXT(np, child);
                    602:                }
                    603:        }
1.69      schwarze  604:        if (sv)
                    605:                macro_close(p);
1.71      schwarze  606:        pnode_unlinksub(pn);
1.24      kristaps  607: }
                    608:
1.7       kristaps  609: static void
1.74    ! schwarze  610: pnode_printprologue(struct format *p, struct ptree *tree)
1.7       kristaps  611: {
1.74    ! schwarze  612:        struct pnode    *refmeta;
1.7       kristaps  613:
1.74    ! schwarze  614:        refmeta = tree->root == NULL ? NULL :
        !           615:            pnode_findfirst(tree->root, NODE_REFMETA);
1.9       kristaps  616:
1.69      schwarze  617:        macro_line(p, "Dd $Mdocdate" "$");
1.74    ! schwarze  618:        if (refmeta == NULL) {
1.69      schwarze  619:                macro_open(p, "Dt");
                    620:                macro_addarg(p,
1.74    ! schwarze  621:                    pnode_getattr_raw(tree->root, ATTRKEY_ID, "UNKNOWN"),
1.72      schwarze  622:                    ARG_SPACE | ARG_SINGLE | ARG_UPPER);
                    623:                macro_addarg(p, "1", ARG_SPACE);
1.69      schwarze  624:                macro_close(p);
1.74    ! schwarze  625:        } else
        !           626:                pnode_printrefmeta(p, refmeta);
1.69      schwarze  627:        macro_line(p, "Os");
1.43      kristaps  628:
1.74    ! schwarze  629:        if (tree->flags & TREE_EQN) {
1.69      schwarze  630:                macro_line(p, "EQ");
1.70      schwarze  631:                print_text(p, "delim $$");
1.69      schwarze  632:                macro_line(p, "EN");
1.43      kristaps  633:        }
1.7       kristaps  634: }
                    635:
1.42      kristaps  636: /*
                    637:  * We can have multiple <term> elements within a <varlistentry>, which
                    638:  * we should comma-separate as list headers.
                    639:  */
1.13      kristaps  640: static void
1.74    ! schwarze  641: pnode_printvarlistentry(struct format *p, struct pnode *pn)
1.13      kristaps  642: {
                    643:        struct pnode    *pp;
1.42      kristaps  644:        int              first = 1;
1.13      kristaps  645:
1.69      schwarze  646:        macro_open(p, "It");
                    647:        TAILQ_FOREACH(pp, &pn->childq, child) {
                    648:                if (pp->node != NODE_TERM)
                    649:                        continue;
                    650:                if ( ! first)
1.72      schwarze  651:                        macro_addarg(p, ",", 0);
1.69      schwarze  652:                pnode_print(p, pp);
                    653:                first = 0;
                    654:        }
                    655:        macro_close(p);
1.13      kristaps  656:        TAILQ_FOREACH(pp, &pn->childq, child)
1.69      schwarze  657:                if (pp->node != NODE_TERM)
1.13      kristaps  658:                        pnode_print(p, pp);
1.71      schwarze  659:        pnode_unlinksub(pn);
                    660: }
                    661:
                    662: static void
1.74    ! schwarze  663: pnode_printtitle(struct format *p, struct pnode *pn)
1.71      schwarze  664: {
                    665:        struct pnode    *pp, *pq;
                    666:
                    667:        TAILQ_FOREACH_SAFE(pp, &pn->childq, child, pq) {
                    668:                if (pp->node == NODE_TITLE) {
                    669:                        pnode_printpara(p, pp);
                    670:                        pnode_print(p, pp);
                    671:                        pnode_unlink(pp);
                    672:                }
                    673:        }
1.13      kristaps  674: }
                    675:
                    676: static void
1.74    ! schwarze  677: pnode_printrow(struct format *p, struct pnode *pn)
1.25      kristaps  678: {
                    679:        struct pnode    *pp;
                    680:
1.69      schwarze  681:        macro_line(p, "Bl -dash -compact");
1.25      kristaps  682:        TAILQ_FOREACH(pp, &pn->childq, child) {
1.69      schwarze  683:                macro_line(p, "It");
1.25      kristaps  684:                pnode_print(p, pp);
                    685:        }
1.69      schwarze  686:        macro_line(p, "El");
1.71      schwarze  687:        pnode_unlink(pn);
1.25      kristaps  688: }
                    689:
                    690: static void
1.74    ! schwarze  691: pnode_printtable(struct format *p, struct pnode *pn)
1.16      kristaps  692: {
                    693:        struct pnode    *pp;
                    694:
1.71      schwarze  695:        pnode_printtitle(p, pn);
1.69      schwarze  696:        macro_line(p, "Bl -ohang");
1.64      schwarze  697:        while ((pp = pnode_findfirst(pn, NODE_ROW)) != NULL) {
1.69      schwarze  698:                macro_line(p, "It Table Row");
1.25      kristaps  699:                pnode_printrow(p, pp);
                    700:        }
1.69      schwarze  701:        macro_line(p, "El");
1.71      schwarze  702:        pnode_unlinksub(pn);
1.25      kristaps  703: }
                    704:
                    705: static void
1.74    ! schwarze  706: pnode_printlist(struct format *p, struct pnode *pn)
1.25      kristaps  707: {
                    708:        struct pnode    *pp;
1.16      kristaps  709:
1.71      schwarze  710:        pnode_printtitle(p, pn);
1.69      schwarze  711:        macro_argline(p, "Bl",
                    712:            pn->node == NODE_ORDEREDLIST ? "-enum" : "-bullet");
1.16      kristaps  713:        TAILQ_FOREACH(pp, &pn->childq, child) {
1.69      schwarze  714:                macro_line(p, "It");
1.16      kristaps  715:                pnode_print(p, pp);
                    716:        }
1.69      schwarze  717:        macro_line(p, "El");
1.71      schwarze  718:        pnode_unlinksub(pn);
1.16      kristaps  719: }
                    720:
                    721: static void
1.74    ! schwarze  722: pnode_printvariablelist(struct format *p, struct pnode *pn)
1.13      kristaps  723: {
                    724:        struct pnode    *pp;
                    725:
1.71      schwarze  726:        pnode_printtitle(p, pn);
1.69      schwarze  727:        macro_line(p, "Bl -tag -width Ds");
                    728:        TAILQ_FOREACH(pp, &pn->childq, child) {
                    729:                if (pp->node == NODE_VARLISTENTRY)
1.13      kristaps  730:                        pnode_print(p, pp);
1.69      schwarze  731:                else
1.72      schwarze  732:                        macro_nodeline(p, "It", pp, 0);
1.69      schwarze  733:        }
                    734:        macro_line(p, "El");
1.71      schwarze  735:        pnode_unlinksub(pn);
1.13      kristaps  736: }
                    737:
1.1       kristaps  738: /*
                    739:  * Print a parsed node (or ignore it--whatever).
                    740:  * This is a recursive function.
1.23      kristaps  741:  * FIXME: if we're in a literal context (<screen> or <programlisting> or
                    742:  * whatever), don't print inline macros.
1.1       kristaps  743:  */
                    744: static void
1.74    ! schwarze  745: pnode_print(struct format *p, struct pnode *pn)
1.1       kristaps  746: {
                    747:        struct pnode    *pp;
1.59      schwarze  748:        const char      *ccp;
1.1       kristaps  749:        char            *cp;
1.69      schwarze  750:        int              last;
                    751:        enum linestate   sv;
1.1       kristaps  752:
1.64      schwarze  753:        if (pn == NULL)
1.1       kristaps  754:                return;
                    755:
1.69      schwarze  756:        sv = p->linestate;
1.1       kristaps  757:
                    758:        switch (pn->node) {
1.62      schwarze  759:        case NODE_APPLICATION:
1.69      schwarze  760:                macro_open(p, "Nm");
1.27      kristaps  761:                break;
1.62      schwarze  762:        case NODE_ANCHOR:
1.30      kristaps  763:                /* Don't print anything! */
                    764:                return;
1.62      schwarze  765:        case NODE_ARG:
1.10      kristaps  766:                pnode_printarg(p, pn);
1.4       kristaps  767:                break;
1.62      schwarze  768:        case NODE_AUTHOR:
1.69      schwarze  769:                macro_open(p, "An");
1.50      schwarze  770:                break;
1.62      schwarze  771:        case NODE_AUTHORGROUP:
1.69      schwarze  772:                macro_line(p, "An -split");
1.50      schwarze  773:                break;
1.62      schwarze  774:        case NODE_BOOKINFO:
1.69      schwarze  775:                macro_line(p, "Sh NAME");
1.50      schwarze  776:                break;
1.62      schwarze  777:        case NODE_CITEREFENTRY:
1.1       kristaps  778:                pnode_printciterefentry(p, pn);
                    779:                break;
1.68      schwarze  780:        case NODE_CITETITLE:
1.69      schwarze  781:                macro_open(p, "%T");
1.68      schwarze  782:                break;
1.62      schwarze  783:        case NODE_CODE:
1.69      schwarze  784:                macro_open(p, "Li");
1.4       kristaps  785:                break;
1.62      schwarze  786:        case NODE_COMMAND:
1.69      schwarze  787:                macro_open(p, "Nm");
1.13      kristaps  788:                break;
1.62      schwarze  789:        case NODE_CONSTANT:
1.69      schwarze  790:                macro_open(p, "Dv");
1.33      kristaps  791:                break;
1.62      schwarze  792:        case NODE_EDITOR:
1.70      schwarze  793:                print_text(p, "editor:");
1.69      schwarze  794:                macro_open(p, "An");
1.50      schwarze  795:                break;
1.65      schwarze  796:        case NODE_EMAIL:
1.69      schwarze  797:                macro_open(p, "Aq Mt");
1.65      schwarze  798:                break;
1.62      schwarze  799:        case NODE_EMPHASIS:
                    800:        case NODE_FIRSTTERM:
1.69      schwarze  801:                macro_open(p, "Em");
1.1       kristaps  802:                break;
1.62      schwarze  803:        case NODE_ENVAR:
1.69      schwarze  804:                macro_open(p, "Ev");
1.21      kristaps  805:                break;
1.62      schwarze  806:        case NODE_FILENAME:
1.69      schwarze  807:                macro_open(p, "Pa");
1.17      kristaps  808:                break;
1.62      schwarze  809:        case NODE_FUNCTION:
1.69      schwarze  810:                macro_open(p, "Fn");
1.3       kristaps  811:                break;
1.62      schwarze  812:        case NODE_FUNCPROTOTYPE:
1.3       kristaps  813:                pnode_printfuncprototype(p, pn);
                    814:                break;
1.62      schwarze  815:        case NODE_FUNCSYNOPSISINFO:
1.69      schwarze  816:                macro_open(p, "Fd");
1.16      kristaps  817:                break;
1.62      schwarze  818:        case NODE_INDEXTERM:
1.55      schwarze  819:                return;
1.62      schwarze  820:        case NODE_INFORMALEQUATION:
1.69      schwarze  821:                macro_line(p, "EQ");
1.43      kristaps  822:                break;
1.62      schwarze  823:        case NODE_INLINEEQUATION:
1.69      schwarze  824:                if (p->linestate == LINE_NEW)
                    825:                        p->linestate = LINE_TEXT;
                    826:                putchar('$');
1.43      kristaps  827:                break;
1.62      schwarze  828:        case NODE_ITEMIZEDLIST:
1.25      kristaps  829:                pnode_printlist(p, pn);
1.24      kristaps  830:                break;
1.62      schwarze  831:        case NODE_GROUP:
1.24      kristaps  832:                pnode_printgroup(p, pn);
1.10      kristaps  833:                break;
1.67      schwarze  834:        case NODE_KEYSYM:
1.69      schwarze  835:                macro_open(p, "Sy");
1.67      schwarze  836:                break;
1.62      schwarze  837:        case NODE_LEGALNOTICE:
1.69      schwarze  838:                macro_line(p, "Sh LEGAL NOTICE");
1.50      schwarze  839:                break;
1.62      schwarze  840:        case NODE_LINK:
1.59      schwarze  841:                ccp = pnode_getattr_raw(pn, ATTRKEY_LINKEND, NULL);
1.64      schwarze  842:                if (ccp == NULL)
1.58      schwarze  843:                        break;
1.69      schwarze  844:                macro_argline(p, "Sx", ccp);
1.58      schwarze  845:                return;
1.62      schwarze  846:        case NODE_LITERAL:
1.69      schwarze  847:                macro_open(p, "Li");
1.19      kristaps  848:                break;
1.62      schwarze  849:        case NODE_LITERALLAYOUT:
1.69      schwarze  850:                macro_argline(p, "Bd", pnode_getattr(pn, ATTRKEY_CLASS) ==
1.66      schwarze  851:                    ATTRVAL_MONOSPACED ? "-literal" : "-unfilled");
1.60      schwarze  852:                break;
1.62      schwarze  853:        case NODE_MML_MFENCED:
1.41      kristaps  854:                pnode_printmathfenced(p, pn);
1.40      kristaps  855:                break;
1.62      schwarze  856:        case NODE_MML_MROW:
                    857:        case NODE_MML_MI:
                    858:        case NODE_MML_MN:
                    859:        case NODE_MML_MO:
1.43      kristaps  860:                if (TAILQ_EMPTY(&pn->childq))
                    861:                        break;
                    862:                fputs(" { ", stdout);
1.40      kristaps  863:                break;
1.62      schwarze  864:        case NODE_MML_MFRAC:
                    865:        case NODE_MML_MSUB:
                    866:        case NODE_MML_MSUP:
1.40      kristaps  867:                pnode_printmath(p, pn);
                    868:                break;
1.62      schwarze  869:        case NODE_OPTION:
1.69      schwarze  870:                macro_open(p, "Fl");
1.1       kristaps  871:                break;
1.62      schwarze  872:        case NODE_ORDEREDLIST:
1.25      kristaps  873:                pnode_printlist(p, pn);
                    874:                break;
1.62      schwarze  875:        case NODE_PARA:
1.54      schwarze  876:                pnode_printpara(p, pn);
1.3       kristaps  877:                break;
1.62      schwarze  878:        case NODE_PARAMETER:
1.72      schwarze  879:                macro_nodeline(p, "Fa", pn, ARG_SINGLE);
1.4       kristaps  880:                pnode_unlinksub(pn);
1.1       kristaps  881:                break;
1.62      schwarze  882:        case NODE_QUOTE:
1.69      schwarze  883:                macro_open(p, "Qo");
1.28      kristaps  884:                break;
1.62      schwarze  885:        case NODE_PROGRAMLISTING:
                    886:        case NODE_SCREEN:
1.69      schwarze  887:                macro_line(p, "Bd -literal");
1.15      kristaps  888:                break;
1.62      schwarze  889:        case NODE_REFENTRYINFO:
1.15      kristaps  890:                /* Suppress. */
                    891:                pnode_unlinksub(pn);
1.1       kristaps  892:                break;
1.62      schwarze  893:        case NODE_REFMETA:
1.7       kristaps  894:                abort();
1.1       kristaps  895:                break;
1.62      schwarze  896:        case NODE_REFNAME:
1.10      kristaps  897:                /* Suppress non-text children... */
1.70      schwarze  898:                macro_open(p, "Nm");
1.72      schwarze  899:                macro_addnode(p, pn, ARG_SPACE | ARG_SINGLE);
1.4       kristaps  900:                pnode_unlinksub(pn);
1.10      kristaps  901:                break;
1.62      schwarze  902:        case NODE_REFNAMEDIV:
1.69      schwarze  903:                macro_line(p, "Sh NAME");
1.1       kristaps  904:                break;
1.62      schwarze  905:        case NODE_REFPURPOSE:
1.69      schwarze  906:                macro_open(p, "Nd");
1.10      kristaps  907:                break;
1.62      schwarze  908:        case NODE_REFSYNOPSISDIV:
1.6       kristaps  909:                pnode_printrefsynopsisdiv(p, pn);
1.1       kristaps  910:                break;
1.62      schwarze  911:        case NODE_PREFACE:
                    912:        case NODE_SECTION:
                    913:        case NODE_NOTE:
                    914:        case NODE_TIP:
                    915:        case NODE_CAUTION:
                    916:        case NODE_WARNING:
1.1       kristaps  917:                pnode_printrefsect(p, pn);
                    918:                break;
1.62      schwarze  919:        case NODE_REPLACEABLE:
1.69      schwarze  920:                macro_open(p, "Ar");
1.13      kristaps  921:                break;
1.62      schwarze  922:        case NODE_SBR:
1.69      schwarze  923:                macro_line(p, "br");
1.19      kristaps  924:                break;
1.62      schwarze  925:        case NODE_SGMLTAG:
1.69      schwarze  926:                macro_open(p, "Li");
1.30      kristaps  927:                break;
1.62      schwarze  928:        case NODE_STRUCTNAME:
1.69      schwarze  929:                macro_open(p, "Vt");
1.25      kristaps  930:                break;
1.62      schwarze  931:        case NODE_TABLE:
                    932:        case NODE_INFORMALTABLE:
1.25      kristaps  933:                pnode_printtable(p, pn);
1.10      kristaps  934:                break;
1.62      schwarze  935:        case NODE_TEXT:
1.72      schwarze  936:                if (pn->bsz == 0) {
1.37      kristaps  937:                        assert(pn->real != pn->b);
                    938:                        break;
                    939:                }
1.69      schwarze  940:                if (p->linestate == LINE_NEW)
                    941:                        p->linestate = LINE_TEXT;
                    942:                else
                    943:                        putchar(' ');
1.37      kristaps  944:
1.1       kristaps  945:                /*
                    946:                 * Output all characters, squeezing out whitespace
1.44      schwarze  947:                 * between newlines.
1.1       kristaps  948:                 * XXX: all whitespace, including tabs (?).
                    949:                 * Remember to escape control characters and escapes.
                    950:                 */
1.72      schwarze  951:                cp = pn->b;
1.37      kristaps  952:
1.20      kristaps  953:                /*
                    954:                 * There's often a superfluous "-" in its <option> tags
                    955:                 * before the actual flags themselves.
                    956:                 * "Fl" does this for us, so remove it.
                    957:                 */
1.64      schwarze  958:                if (pn->parent != NULL &&
                    959:                    pn->parent->node == NODE_OPTION &&
                    960:                    *cp == '-')
1.20      kristaps  961:                        cp++;
1.64      schwarze  962:                for (last = '\n'; *cp != '\0'; ) {
                    963:                        if (last == '\n') {
1.1       kristaps  964:                                /* Consume all whitespace. */
1.46      schwarze  965:                                if (isspace((unsigned char)*cp)) {
                    966:                                        while (isspace((unsigned char)*cp))
1.1       kristaps  967:                                                cp++;
                    968:                                        continue;
1.64      schwarze  969:                                } else if (*cp == '\'' || *cp == '.')
1.1       kristaps  970:                                        fputs("\\&", stdout);
                    971:                        }
                    972:                        putchar(last = *cp++);
                    973:                        /* If we're a character escape, escape us. */
1.64      schwarze  974:                        if (last == '\\')
1.1       kristaps  975:                                putchar('e');
                    976:                }
                    977:                break;
1.62      schwarze  978:        case NODE_TITLE:
1.69      schwarze  979:                if (pn->parent->node == NODE_BOOKINFO)
                    980:                        macro_open(p, "Nd");
1.50      schwarze  981:                break;
1.62      schwarze  982:        case NODE_TYPE:
1.69      schwarze  983:                macro_open(p, "Vt");
1.39      kristaps  984:                break;
1.62      schwarze  985:        case NODE_USERINPUT:
1.69      schwarze  986:                macro_open(p, "Li");
1.26      kristaps  987:                break;
1.62      schwarze  988:        case NODE_VARIABLELIST:
1.13      kristaps  989:                pnode_printvariablelist(p, pn);
                    990:                break;
1.62      schwarze  991:        case NODE_VARLISTENTRY:
1.13      kristaps  992:                pnode_printvarlistentry(p, pn);
                    993:                break;
1.62      schwarze  994:        case NODE_VARNAME:
1.69      schwarze  995:                macro_open(p, "Va");
1.23      kristaps  996:                break;
1.1       kristaps  997:        default:
                    998:                break;
                    999:        }
                   1000:
                   1001:        TAILQ_FOREACH(pp, &pn->childq, child)
                   1002:                pnode_print(p, pp);
                   1003:
                   1004:        switch (pn->node) {
1.62      schwarze 1005:        case NODE_INFORMALEQUATION:
1.69      schwarze 1006:                macro_line(p, "EN");
1.40      kristaps 1007:                break;
1.62      schwarze 1008:        case NODE_INLINEEQUATION:
1.43      kristaps 1009:                fputs("$ ", stdout);
1.69      schwarze 1010:                p->linestate = sv;
1.43      kristaps 1011:                break;
1.62      schwarze 1012:        case NODE_MML_MROW:
                   1013:        case NODE_MML_MI:
                   1014:        case NODE_MML_MN:
                   1015:        case NODE_MML_MO:
1.43      kristaps 1016:                if (TAILQ_EMPTY(&pn->childq))
                   1017:                        break;
                   1018:                fputs(" } ", stdout);
1.40      kristaps 1019:                break;
1.62      schwarze 1020:        case NODE_APPLICATION:
                   1021:        case NODE_ARG:
                   1022:        case NODE_AUTHOR:
                   1023:        case NODE_CITEREFENTRY:
1.68      schwarze 1024:        case NODE_CITETITLE:
1.62      schwarze 1025:        case NODE_CODE:
                   1026:        case NODE_COMMAND:
                   1027:        case NODE_CONSTANT:
                   1028:        case NODE_EDITOR:
1.65      schwarze 1029:        case NODE_EMAIL:
1.62      schwarze 1030:        case NODE_EMPHASIS:
                   1031:        case NODE_ENVAR:
                   1032:        case NODE_FILENAME:
                   1033:        case NODE_FIRSTTERM:
                   1034:        case NODE_FUNCTION:
                   1035:        case NODE_FUNCSYNOPSISINFO:
1.67      schwarze 1036:        case NODE_KEYSYM:
1.62      schwarze 1037:        case NODE_LITERAL:
                   1038:        case NODE_OPTION:
                   1039:        case NODE_PARAMETER:
                   1040:        case NODE_REPLACEABLE:
                   1041:        case NODE_REFPURPOSE:
                   1042:        case NODE_SGMLTAG:
                   1043:        case NODE_STRUCTNAME:
                   1044:        case NODE_TYPE:
                   1045:        case NODE_USERINPUT:
                   1046:        case NODE_VARNAME:
1.69      schwarze 1047:                if (sv != LINE_MACRO && p->linestate == LINE_MACRO)
                   1048:                        macro_closepunct(p, pn);
1.28      kristaps 1049:                break;
1.62      schwarze 1050:        case NODE_QUOTE:
1.69      schwarze 1051:                if (sv == LINE_NEW)
                   1052:                        macro_close(p);
                   1053:                sv = p->linestate;
                   1054:                macro_open(p, "Qc");
                   1055:                if (sv == LINE_NEW)
                   1056:                        macro_close(p);
1.10      kristaps 1057:                break;
1.62      schwarze 1058:        case NODE_REFNAME:
1.12      kristaps 1059:                /*
                   1060:                 * If we're in the NAME macro and we have multiple
                   1061:                 * <refname> macros in sequence, then print out a
                   1062:                 * trailing comma before the newline.
                   1063:                 */
1.64      schwarze 1064:                if (pn->parent != NULL &&
                   1065:                    pn->parent->node == NODE_REFNAMEDIV &&
                   1066:                    TAILQ_NEXT(pn, child) != NULL &&
                   1067:                    TAILQ_NEXT(pn, child)->node == NODE_REFNAME)
1.72      schwarze 1068:                        macro_addarg(p, ",", ARG_SPACE);
1.69      schwarze 1069:                if (sv == LINE_NEW)
                   1070:                        macro_close(p);
1.52      schwarze 1071:                break;
1.62      schwarze 1072:        case NODE_PREFACE:
                   1073:        case NODE_SECTION:
                   1074:        case NODE_NOTE:
                   1075:        case NODE_TIP:
                   1076:        case NODE_CAUTION:
                   1077:        case NODE_WARNING:
1.52      schwarze 1078:                p->level--;
1.12      kristaps 1079:                break;
1.62      schwarze 1080:        case NODE_LITERALLAYOUT:
                   1081:        case NODE_PROGRAMLISTING:
                   1082:        case NODE_SCREEN:
1.69      schwarze 1083:                macro_line(p, "Ed");
1.50      schwarze 1084:                break;
1.62      schwarze 1085:        case NODE_TITLE:
1.69      schwarze 1086:                if (pn->parent->node == NODE_BOOKINFO)
                   1087:                        macro_line(p, "Sh AUTHORS");
1.1       kristaps 1088:                break;
                   1089:        default:
                   1090:                break;
                   1091:        }
                   1092: }
                   1093:
1.74    ! schwarze 1094: void
        !          1095: ptree_print(struct ptree *tree)
        !          1096: {
        !          1097:        struct format    formatter;
1.1       kristaps 1098:
1.74    ! schwarze 1099:        formatter.level = 0;
        !          1100:        formatter.linestate = LINE_NEW;
        !          1101:        pnode_printprologue(&formatter, tree);
        !          1102:        pnode_print(&formatter, tree->root);
        !          1103:        if (formatter.linestate != LINE_NEW)
        !          1104:                putchar('\n');
1.1       kristaps 1105: }

CVSweb