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

Annotation of docbook2mdoc/docbook2mdoc.c, Revision 1.145

1.145   ! schwarze    1: /* $Id: docbook2mdoc.c,v 1.144 2019/05/01 11:34:19 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>
1.103     schwarze   22: #include <string.h>
1.1       kristaps   23:
1.139     schwarze   24: #include "xmalloc.h"
1.74      schwarze   25: #include "node.h"
1.75      schwarze   26: #include "macro.h"
1.74      schwarze   27: #include "format.h"
                     28:
                     29: /*
                     30:  * The implementation of the mdoc(7) formatter.
                     31:  */
1.12      kristaps   32:
1.74      schwarze   33: static void     pnode_print(struct format *, struct pnode *);
1.25      kristaps   34:
1.37      kristaps   35:
1.54      schwarze   36: static void
1.93      schwarze   37: pnode_printtext(struct format *f, struct pnode *n)
                     38: {
1.94      schwarze   39:        struct pnode    *nn;
1.93      schwarze   40:        char            *cp;
1.103     schwarze   41:        int              accept_arg;
1.93      schwarze   42:
1.103     schwarze   43:        cp = n->b;
                     44:        accept_arg = f->flags & FMT_ARG;
1.138     schwarze   45:        if (f->linestate == LINE_MACRO && !accept_arg &&
                     46:            (n->flags & NFLAG_SPC) == 0) {
1.103     schwarze   47:                for (;;) {
                     48:                        if (*cp == '\0')
                     49:                                return;
                     50:                        if (strchr("!),.:;?]", *cp) == NULL)
                     51:                                break;
                     52:                        printf(" %c", *cp++);
                     53:                }
                     54:                if (isspace((unsigned char)*cp)) {
                     55:                        while (isspace((unsigned char)*cp))
                     56:                                cp++;
                     57:                        macro_close(f);
                     58:                } else {
                     59:                        fputs(" Ns", stdout);
                     60:                        f->flags &= FMT_IMPL;
                     61:                        accept_arg = 1;
                     62:                }
1.94      schwarze   63:        }
1.103     schwarze   64:        if (f->linestate == LINE_MACRO && !accept_arg &&
                     65:            (f->flags & (FMT_CHILD | FMT_IMPL)) == 0)
                     66:                macro_close(f);
1.94      schwarze   67:
                     68:        /*
                     69:         * Text preceding a macro without intervening whitespace
                     70:         * requires a .Pf macro.
                     71:         * Set the spacing flag to avoid a redundant .Ns macro.
                     72:         */
                     73:
                     74:        if (f->linestate != LINE_MACRO &&
1.138     schwarze   75:            (nn = TAILQ_NEXT(n, child)) != NULL &&
                     76:             (nn->flags & NFLAG_SPC) == 0) {
1.113     schwarze   77:                switch (pnode_class(nn->node)) {
                     78:                case CLASS_LINE:
                     79:                case CLASS_ENCL:
                     80:                        macro_open(f, "Pf");
                     81:                        accept_arg = 1;
                     82:                        f->flags |= FMT_CHILD;
1.138     schwarze   83:                        nn->flags |= NFLAG_SPC;
1.113     schwarze   84:                        break;
                     85:                default:
                     86:                        break;
                     87:                }
1.93      schwarze   88:        }
                     89:
1.103     schwarze   90:        switch (f->linestate) {
1.107     schwarze   91:        case LINE_NEW:
                     92:                break;
1.103     schwarze   93:        case LINE_TEXT:
1.138     schwarze   94:                if (n->flags & NFLAG_SPC) {
                     95:                        if (n->flags & NFLAG_LINE &&
                     96:                            pnode_class(n->node) == CLASS_TEXT)
1.107     schwarze   97:                                macro_close(f);
                     98:                        else
                     99:                                putchar(' ');
1.105     schwarze  100:                }
1.103     schwarze  101:                break;
                    102:        case LINE_MACRO:
1.136     schwarze  103:                if (accept_arg == 0)
                    104:                        macro_close(f);
1.138     schwarze  105:                else if (n->flags & NFLAG_SPC ||
                    106:                    (f->flags & FMT_ARG) == 0 ||
1.136     schwarze  107:                    (nn = TAILQ_PREV(n, pnodeq, child)) == NULL ||
                    108:                    pnode_class(nn->node) != CLASS_TEXT)
1.93      schwarze  109:                        putchar(' ');
1.103     schwarze  110:                break;
1.93      schwarze  111:        }
                    112:
                    113:        if (n->node == NODE_ESCAPE) {
                    114:                fputs(n->b, stdout);
1.107     schwarze  115:                if (f->linestate == LINE_NEW)
                    116:                        f->linestate = LINE_TEXT;
1.93      schwarze  117:                return;
                    118:        }
                    119:
                    120:        /*
                    121:         * Remove the prefix '-' from <option> elements
                    122:         * because the arguments of .Fl macros do not need it.
                    123:         */
                    124:
                    125:        if (n->parent != NULL && n->parent->node == NODE_OPTION && *cp == '-')
                    126:                cp++;
                    127:
1.107     schwarze  128:        if (f->linestate == LINE_MACRO)
                    129:                macro_addarg(f, cp, 0);
                    130:        else
                    131:                print_text(f, cp, 0);
1.93      schwarze  132: }
                    133:
                    134: static void
1.126     schwarze  135: pnode_printimagedata(struct format *f, struct pnode *n)
                    136: {
                    137:        const char      *cp;
                    138:
                    139:        if ((cp = pnode_getattr_raw(n, ATTRKEY_FILEREF, NULL)) == NULL)
                    140:                cp = pnode_getattr_raw(n, ATTRKEY_ENTITYREF, NULL);
                    141:        if (cp != NULL) {
                    142:                print_text(f, "[image:", ARG_SPACE);
                    143:                print_text(f, cp, ARG_SPACE);
                    144:                print_text(f, "]", 0);
                    145:        } else
                    146:                print_text(f, "[image]", ARG_SPACE);
                    147: }
                    148:
                    149: static void
1.110     schwarze  150: pnode_printrefnamediv(struct format *f, struct pnode *n)
                    151: {
                    152:        struct pnode    *nc, *nn;
                    153:        int              comma;
                    154:
1.130     schwarze  155:        f->parastate = PARA_HAVE;
1.110     schwarze  156:        macro_line(f, "Sh NAME");
1.130     schwarze  157:        f->parastate = PARA_HAVE;
1.110     schwarze  158:        comma = 0;
                    159:        TAILQ_FOREACH_SAFE(nc, &n->childq, child, nn) {
                    160:                if (nc->node != NODE_REFNAME)
                    161:                        continue;
                    162:                if (comma)
                    163:                        macro_addarg(f, ",", ARG_SPACE);
                    164:                macro_open(f, "Nm");
                    165:                macro_addnode(f, nc, ARG_SPACE);
                    166:                pnode_unlink(nc);
                    167:                comma = 1;
                    168:        }
                    169:        macro_close(f);
                    170: }
                    171:
1.37      kristaps  172: /*
1.10      kristaps  173:  * If the SYNOPSIS macro has a superfluous title, kill it.
1.8       kristaps  174:  */
1.1       kristaps  175: static void
1.101     schwarze  176: pnode_printrefsynopsisdiv(struct format *f, struct pnode *n)
1.6       kristaps  177: {
1.101     schwarze  178:        struct pnode    *nc, *nn;
1.6       kristaps  179:
1.101     schwarze  180:        TAILQ_FOREACH_SAFE(nc, &n->childq, child, nn)
                    181:                if (nc->node == NODE_TITLE)
                    182:                        pnode_unlink(nc);
1.71      schwarze  183:
1.130     schwarze  184:        f->parastate = PARA_HAVE;
1.101     schwarze  185:        macro_line(f, "Sh SYNOPSIS");
1.130     schwarze  186:        f->parastate = PARA_HAVE;
1.6       kristaps  187: }
                    188:
1.8       kristaps  189: /*
                    190:  * Start a hopefully-named `Sh' section.
                    191:  */
1.6       kristaps  192: static void
1.119     schwarze  193: pnode_printsection(struct format *f, struct pnode *n)
1.1       kristaps  194: {
1.115     schwarze  195:        struct pnode    *nc, *ncc;
1.52      schwarze  196:        int              flags, level;
                    197:
1.143     schwarze  198:        if (n->parent == NULL)
1.56      schwarze  199:                return;
                    200:
1.101     schwarze  201:        level = ++f->level;
1.72      schwarze  202:        flags = ARG_SPACE;
1.121     schwarze  203:        switch (n->node) {
                    204:        case NODE_SECTION:
                    205:        case NODE_APPENDIX:
                    206:                if (level == 1)
                    207:                        flags |= ARG_UPPER;
                    208:                break;
                    209:        case NODE_SIMPLESECT:
                    210:                if (level < 2)
                    211:                        level = 2;
                    212:                break;
1.142     schwarze  213:        case NODE_NOTE:
1.121     schwarze  214:                if (level < 3)
1.52      schwarze  215:                        level = 3;
1.121     schwarze  216:                break;
1.142     schwarze  217:        default:
                    218:                abort();
1.52      schwarze  219:        }
1.1       kristaps  220:
1.101     schwarze  221:        TAILQ_FOREACH(nc, &n->childq, child)
                    222:                if (nc->node == NODE_TITLE)
1.1       kristaps  223:                        break;
                    224:
1.52      schwarze  225:        switch (level) {
1.62      schwarze  226:        case 1:
1.101     schwarze  227:                macro_close(f);
1.130     schwarze  228:                f->parastate = PARA_HAVE;
1.101     schwarze  229:                macro_open(f, "Sh");
1.29      kristaps  230:                break;
1.62      schwarze  231:        case 2:
1.101     schwarze  232:                macro_close(f);
1.130     schwarze  233:                f->parastate = PARA_HAVE;
1.101     schwarze  234:                macro_open(f, "Ss");
1.29      kristaps  235:                break;
1.52      schwarze  236:        default:
1.130     schwarze  237:                if (f->parastate == PARA_MID)
                    238:                        f->parastate = PARA_WANT;
1.101     schwarze  239:                macro_open(f, "Sy");
1.29      kristaps  240:                break;
                    241:        }
1.142     schwarze  242:        macro_addnode(f, nc, flags);
1.101     schwarze  243:        macro_close(f);
1.115     schwarze  244:
                    245:        /*
                    246:         * DocBook has no equivalent for -split mode,
                    247:         * so just switch the default in the AUTHORS section.
                    248:         */
                    249:
                    250:        if (nc != NULL) {
1.130     schwarze  251:                if (level == 1 &&
                    252:                    (ncc = TAILQ_FIRST(&nc->childq)) != NULL &&
                    253:                    ncc->node == NODE_TEXT &&
1.115     schwarze  254:                    strcasecmp(ncc->b, "AUTHORS") == 0)
                    255:                        macro_line(f, "An -nosplit");
                    256:                pnode_unlink(nc);
                    257:        }
1.130     schwarze  258:        f->parastate = level > 2 ? PARA_WANT : PARA_HAVE;
1.1       kristaps  259: }
                    260:
1.8       kristaps  261: /*
                    262:  * Start a reference, extracting the title and volume.
                    263:  */
1.1       kristaps  264: static void
1.101     schwarze  265: pnode_printciterefentry(struct format *f, struct pnode *n)
1.1       kristaps  266: {
1.101     schwarze  267:        struct pnode    *nc, *title, *manvol;
1.1       kristaps  268:
                    269:        title = manvol = NULL;
1.101     schwarze  270:        TAILQ_FOREACH(nc, &n->childq, child) {
                    271:                if (nc->node == NODE_MANVOLNUM)
                    272:                        manvol = nc;
                    273:                else if (nc->node == NODE_REFENTRYTITLE)
                    274:                        title = nc;
1.69      schwarze  275:        }
1.101     schwarze  276:        macro_open(f, "Xr");
1.69      schwarze  277:        if (title == NULL)
1.101     schwarze  278:                macro_addarg(f, "unknown", ARG_SPACE);
1.69      schwarze  279:        else
1.101     schwarze  280:                macro_addnode(f, title, ARG_SPACE | ARG_SINGLE);
1.69      schwarze  281:        if (manvol == NULL)
1.101     schwarze  282:                macro_addarg(f, "1", ARG_SPACE);
1.64      schwarze  283:        else
1.101     schwarze  284:                macro_addnode(f, manvol, ARG_SPACE | ARG_SINGLE);
                    285:        pnode_unlinksub(n);
1.1       kristaps  286: }
                    287:
1.40      kristaps  288: /*
1.41      kristaps  289:  * The <mml:mfenced> node is a little peculiar.
                    290:  * First, it can have arbitrary open and closing tokens, which default
                    291:  * to parentheses.
                    292:  * Second, >1 arguments are separated by commas.
                    293:  */
                    294: static void
1.101     schwarze  295: pnode_printmathfenced(struct format *f, struct pnode *n)
1.41      kristaps  296: {
1.101     schwarze  297:        struct pnode    *nc;
1.41      kristaps  298:
1.101     schwarze  299:        printf("left %s ", pnode_getattr_raw(n, ATTRKEY_OPEN, "("));
1.41      kristaps  300:
1.101     schwarze  301:        nc = TAILQ_FIRST(&n->childq);
                    302:        pnode_print(f, nc);
1.41      kristaps  303:
1.101     schwarze  304:        while ((nc = TAILQ_NEXT(nc, child)) != NULL) {
1.41      kristaps  305:                putchar(',');
1.101     schwarze  306:                pnode_print(f, nc);
1.41      kristaps  307:        }
1.101     schwarze  308:        printf("right %s ", pnode_getattr_raw(n, ATTRKEY_CLOSE, ")"));
                    309:        pnode_unlinksub(n);
1.41      kristaps  310: }
                    311:
                    312: /*
1.40      kristaps  313:  * These math nodes require special handling because they have infix
                    314:  * syntax, instead of the usual prefix or prefix.
                    315:  * So we need to break up the first and second child node with a
                    316:  * particular eqn(7) word.
                    317:  */
                    318: static void
1.101     schwarze  319: pnode_printmath(struct format *f, struct pnode *n)
1.40      kristaps  320: {
1.101     schwarze  321:        struct pnode    *nc;
1.40      kristaps  322:
1.101     schwarze  323:        nc = TAILQ_FIRST(&n->childq);
                    324:        pnode_print(f, nc);
1.40      kristaps  325:
1.101     schwarze  326:        switch (n->node) {
1.62      schwarze  327:        case NODE_MML_MSUP:
1.42      kristaps  328:                fputs(" sup ", stdout);
1.40      kristaps  329:                break;
1.62      schwarze  330:        case NODE_MML_MFRAC:
1.42      kristaps  331:                fputs(" over ", stdout);
1.40      kristaps  332:                break;
1.62      schwarze  333:        case NODE_MML_MSUB:
1.42      kristaps  334:                fputs(" sub ", stdout);
1.40      kristaps  335:                break;
                    336:        default:
                    337:                break;
                    338:        }
                    339:
1.101     schwarze  340:        nc = TAILQ_NEXT(nc, child);
                    341:        pnode_print(f, nc);
                    342:        pnode_unlinksub(n);
1.40      kristaps  343: }
                    344:
1.3       kristaps  345: static void
1.101     schwarze  346: pnode_printfuncprototype(struct format *f, struct pnode *n)
1.3       kristaps  347: {
1.145   ! schwarze  348:        struct pnode    *fdef, *fps, *ftype, *nc, *nn;
1.3       kristaps  349:
1.132     schwarze  350:        /*
                    351:         * Extract <funcdef> child and ignore <void> child.
                    352:         * Leave other children in place, to be treated as parameters.
                    353:         */
                    354:
                    355:        fdef = NULL;
                    356:        TAILQ_FOREACH_SAFE(nc, &n->childq, child, nn) {
                    357:                switch (nc->node) {
                    358:                case NODE_FUNCDEF:
                    359:                        if (fdef == NULL) {
                    360:                                fdef = nc;
                    361:                                TAILQ_REMOVE(&n->childq, nc, child);
                    362:                                nc->parent = NULL;
                    363:                        }
                    364:                        break;
                    365:                case NODE_VOID:
                    366:                        pnode_unlink(nc);
1.3       kristaps  367:                        break;
1.132     schwarze  368:                default:
                    369:                        break;
                    370:                }
                    371:        }
1.3       kristaps  372:
1.132     schwarze  373:        /*
                    374:         * If no children are left, the function is void; use .Fn.
                    375:         * Otherwise, use .Fo.
                    376:         */
                    377:
                    378:        nc = TAILQ_FIRST(&n->childq);
1.86      schwarze  379:        if (fdef != NULL) {
1.132     schwarze  380:                ftype = TAILQ_FIRST(&fdef->childq);
                    381:                if (ftype != NULL && ftype->node == NODE_TEXT) {
                    382:                        macro_argline(f, "Ft", ftype->b);
                    383:                        pnode_unlink(ftype);
                    384:                }
                    385:                if (nc == NULL) {
                    386:                        macro_open(f, "Fn");
                    387:                        macro_addnode(f, fdef, ARG_SPACE | ARG_SINGLE);
                    388:                        macro_addarg(f, "void", ARG_SPACE);
                    389:                        macro_close(f);
                    390:                } else
                    391:                        macro_nodeline(f, "Fo", fdef, ARG_SINGLE);
1.86      schwarze  392:                pnode_unlink(fdef);
1.132     schwarze  393:        } else if (nc == NULL)
                    394:                macro_line(f, "Fn UNKNOWN void");
                    395:        else
1.101     schwarze  396:                macro_line(f, "Fo UNKNOWN");
1.3       kristaps  397:
1.132     schwarze  398:        if (nc == NULL)
                    399:                return;
                    400:
                    401:        while (nc != NULL) {
1.145   ! schwarze  402:                if ((fps = pnode_takefirst(nc, NODE_FUNCPARAMS)) != NULL) {
        !           403:                        macro_open(f, "Fa \"");
        !           404:                        macro_addnode(f, nc, ARG_QUOTED);
        !           405:                        macro_addarg(f, "(", ARG_QUOTED);
        !           406:                        macro_addnode(f, fps, ARG_QUOTED);
        !           407:                        macro_addarg(f, ")", ARG_QUOTED);
        !           408:                        putchar('"');
        !           409:                        macro_close(f);
        !           410:                } else
        !           411:                        macro_nodeline(f, "Fa", nc, ARG_SINGLE);
1.132     schwarze  412:                pnode_unlink(nc);
                    413:                nc = TAILQ_FIRST(&n->childq);
                    414:        }
1.101     schwarze  415:        macro_line(f, "Fc");
1.3       kristaps  416: }
                    417:
1.44      schwarze  418: /*
1.10      kristaps  419:  * The <arg> element is more complicated than it should be because text
                    420:  * nodes are treated like ".Ar foo", but non-text nodes need to be
                    421:  * re-sent into the printer (i.e., without the preceding ".Ar").
1.12      kristaps  422:  * This also handles the case of "repetition" (or in other words, the
                    423:  * ellipsis following an argument) and optionality.
1.10      kristaps  424:  */
1.4       kristaps  425: static void
1.101     schwarze  426: pnode_printarg(struct format *f, struct pnode *n)
1.4       kristaps  427: {
1.101     schwarze  428:        struct pnode    *nc;
                    429:        struct pattr    *a;
1.103     schwarze  430:        int              isop, isrep, was_impl;
1.12      kristaps  431:
                    432:        isop = 1;
1.103     schwarze  433:        isrep = was_impl = 0;
1.101     schwarze  434:        TAILQ_FOREACH(a, &n->attrq, child) {
                    435:                if (a->key == ATTRKEY_CHOICE &&
                    436:                    (a->val == ATTRVAL_PLAIN || a->val == ATTRVAL_REQ))
1.12      kristaps  437:                        isop = 0;
1.101     schwarze  438:                else if (a->key == ATTRKEY_REP && a->val == ATTRVAL_REPEAT)
1.12      kristaps  439:                        isrep = 1;
                    440:        }
1.103     schwarze  441:        if (isop) {
                    442:                if (f->flags & FMT_IMPL) {
                    443:                        was_impl = 1;
                    444:                        macro_open(f, "Oo");
                    445:                } else {
                    446:                        macro_open(f, "Op");
                    447:                        f->flags |= FMT_IMPL;
                    448:                }
                    449:        }
1.101     schwarze  450:        TAILQ_FOREACH(nc, &n->childq, child) {
                    451:                if (nc->node == NODE_TEXT)
                    452:                        macro_open(f, "Ar");
                    453:                pnode_print(f, nc);
1.10      kristaps  454:        }
1.117     schwarze  455:        if (isrep && f->linestate == LINE_MACRO)
                    456:                macro_addarg(f, "...", ARG_SPACE);
1.103     schwarze  457:        if (isop) {
                    458:                if (was_impl)
                    459:                        macro_open(f, "Oc");
                    460:                else
                    461:                        f->flags &= ~FMT_IMPL;
                    462:        }
1.101     schwarze  463:        pnode_unlinksub(n);
1.4       kristaps  464: }
                    465:
1.24      kristaps  466: static void
1.101     schwarze  467: pnode_printgroup(struct format *f, struct pnode *n)
1.24      kristaps  468: {
1.117     schwarze  469:        struct pnode    *nc;
1.101     schwarze  470:        struct pattr    *a;
1.117     schwarze  471:        int              bar, isop, isrep, was_impl;
1.24      kristaps  472:
                    473:        isop = 1;
1.117     schwarze  474:        isrep = was_impl = 0;
                    475:        TAILQ_FOREACH(a, &n->attrq, child) {
1.101     schwarze  476:                if (a->key == ATTRKEY_CHOICE &&
1.117     schwarze  477:                    (a->val == ATTRVAL_PLAIN || a->val == ATTRVAL_REQ))
1.24      kristaps  478:                        isop = 0;
1.117     schwarze  479:                else if (a->key == ATTRKEY_REP && a->val == ATTRVAL_REPEAT)
                    480:                        isrep = 1;
                    481:        }
                    482:        if (isop) {
                    483:                if (f->flags & FMT_IMPL) {
                    484:                        was_impl = 1;
                    485:                        macro_open(f, "Oo");
                    486:                } else {
                    487:                        macro_open(f, "Op");
                    488:                        f->flags |= FMT_IMPL;
                    489:                }
                    490:        } else if (isrep) {
                    491:                if (f->flags & FMT_IMPL) {
                    492:                        was_impl = 1;
                    493:                        macro_open(f, "Bro");
                    494:                } else {
                    495:                        macro_open(f, "Brq");
                    496:                        f->flags |= FMT_IMPL;
1.24      kristaps  497:                }
1.117     schwarze  498:        }
                    499:        bar = 0;
1.101     schwarze  500:        TAILQ_FOREACH(nc, &n->childq, child) {
1.117     schwarze  501:                if (bar && f->linestate == LINE_MACRO)
                    502:                        macro_addarg(f, "|", ARG_SPACE);
1.101     schwarze  503:                pnode_print(f, nc);
1.117     schwarze  504:                bar = 1;
                    505:        }
                    506:        if (isop) {
                    507:                if (was_impl)
                    508:                        macro_open(f, "Oc");
                    509:                else
                    510:                        f->flags &= ~FMT_IMPL;
                    511:        } else if (isrep) {
                    512:                if (was_impl)
                    513:                        macro_open(f, "Brc");
                    514:                else
                    515:                        f->flags &= ~FMT_IMPL;
1.24      kristaps  516:        }
1.117     schwarze  517:        if (isrep && f->linestate == LINE_MACRO)
                    518:                macro_addarg(f, "...", ARG_SPACE);
1.101     schwarze  519:        pnode_unlinksub(n);
1.24      kristaps  520: }
                    521:
1.7       kristaps  522: static void
1.118     schwarze  523: pnode_printsystemitem(struct format *f, struct pnode *n)
                    524: {
                    525:        switch (pnode_getattr(n, ATTRKEY_CLASS)) {
                    526:        case ATTRVAL_IPADDRESS:
                    527:                break;
                    528:        case ATTRVAL_SYSTEMNAME:
                    529:                macro_open(f, "Pa");
                    530:                break;
                    531:        case ATTRVAL_EVENT:
                    532:        default:
                    533:                macro_open(f, "Sy");
                    534:                break;
                    535:        }
                    536: }
                    537:
                    538: static void
1.78      schwarze  539: pnode_printauthor(struct format *f, struct pnode *n)
                    540: {
1.101     schwarze  541:        struct pnode    *nc, *nn;
1.78      schwarze  542:        int              have_contrib, have_name;
                    543:
                    544:        /*
                    545:         * Print <contrib> children up front, before the .An scope,
                    546:         * and figure out whether we a name of a person.
                    547:         */
                    548:
                    549:        have_contrib = have_name = 0;
1.101     schwarze  550:        TAILQ_FOREACH_SAFE(nc, &n->childq, child, nn) {
1.78      schwarze  551:                switch (nc->node) {
                    552:                case NODE_CONTRIB:
                    553:                        if (have_contrib)
                    554:                                print_text(f, ",", 0);
                    555:                        print_textnode(f, nc);
                    556:                        pnode_unlink(nc);
                    557:                        have_contrib = 1;
                    558:                        break;
                    559:                case NODE_PERSONNAME:
                    560:                        have_name = 1;
                    561:                        break;
                    562:                default:
                    563:                        break;
                    564:                }
                    565:        }
                    566:        if (TAILQ_FIRST(&n->childq) == NULL)
                    567:                return;
                    568:
                    569:        if (have_contrib)
                    570:                print_text(f, ":", 0);
                    571:
                    572:        /*
                    573:          * If we have a name, print it in the .An scope and leave
                    574:          * all other content for child handlers, to print after the
                    575:          * scope.  Otherwise, print everything in the scope.
                    576:         */
                    577:
                    578:        macro_open(f, "An");
1.101     schwarze  579:        TAILQ_FOREACH_SAFE(nc, &n->childq, child, nn) {
1.78      schwarze  580:                if (nc->node == NODE_PERSONNAME || have_name == 0) {
                    581:                        macro_addnode(f, nc, ARG_SPACE);
                    582:                        pnode_unlink(nc);
                    583:                }
1.80      schwarze  584:        }
                    585:
                    586:        /*
                    587:         * If there is an email address,
                    588:         * print it on the same macro line.
                    589:         */
                    590:
                    591:        if ((nc = pnode_findfirst(n, NODE_EMAIL)) != NULL) {
1.103     schwarze  592:                f->flags |= FMT_CHILD;
1.115     schwarze  593:                macro_open(f, "Aq Mt");
                    594:                macro_addnode(f, nc, ARG_SPACE);
1.80      schwarze  595:                pnode_unlink(nc);
1.78      schwarze  596:        }
                    597:
                    598:        /*
                    599:         * If there are still unprinted children, end the scope
                    600:         * with a comma.  Otherwise, leave the scope open in case
                    601:         * a text node follows that starts with closing punctuation.
                    602:         */
                    603:
                    604:        if (TAILQ_FIRST(&n->childq) != NULL) {
                    605:                macro_addarg(f, ",", ARG_SPACE);
                    606:                macro_close(f);
                    607:        }
                    608: }
                    609:
                    610: static void
1.129     schwarze  611: pnode_printxref(struct format *f, struct pnode *n)
                    612: {
                    613:        const char      *linkend;
                    614:
                    615:        linkend = pnode_getattr_raw(n, ATTRKEY_LINKEND, NULL);
                    616:        if (linkend != NULL) {
                    617:                macro_open(f, "Sx");
                    618:                macro_addarg(f, linkend, ARG_SPACE);
                    619:        }
                    620: }
                    621:
                    622: static void
1.96      schwarze  623: pnode_printlink(struct format *f, struct pnode *n)
                    624: {
1.109     schwarze  625:        struct pnode    *nc;
1.96      schwarze  626:        const char      *uri, *text;
                    627:
                    628:        uri = pnode_getattr_raw(n, ATTRKEY_LINKEND, NULL);
                    629:        if (uri != NULL) {
                    630:                if (TAILQ_FIRST(&n->childq) != NULL) {
1.109     schwarze  631:                        TAILQ_FOREACH(nc, &n->childq, child)
                    632:                                pnode_print(f, nc);
1.96      schwarze  633:                        text = "";
1.109     schwarze  634:                } else if ((text = pnode_getattr_raw(n,
                    635:                    ATTRKEY_ENDTERM, NULL)) != NULL) {
                    636:                        if (f->linestate == LINE_MACRO && f->flags & FMT_ARG)
                    637:                                macro_addarg(f, text, ARG_SPACE);
                    638:                        else
1.96      schwarze  639:                                print_text(f, text, ARG_SPACE);
                    640:                }
1.103     schwarze  641:                if (text != NULL) {
1.109     schwarze  642:                        if (f->flags & FMT_IMPL)
                    643:                                macro_open(f, "Po");
                    644:                        else {
                    645:                                macro_open(f, "Pq");
                    646:                                f->flags |= FMT_CHILD;
                    647:                        }
1.103     schwarze  648:                }
1.96      schwarze  649:                macro_open(f, "Sx");
                    650:                macro_addarg(f, uri, ARG_SPACE);
1.109     schwarze  651:                if (text != NULL && f->flags & FMT_IMPL)
                    652:                        macro_open(f, "Pc");
1.96      schwarze  653:                pnode_unlinksub(n);
                    654:                return;
                    655:        }
                    656:        uri = pnode_getattr_raw(n, ATTRKEY_XLINK_HREF, NULL);
                    657:        if (uri == NULL)
                    658:                uri = pnode_getattr_raw(n, ATTRKEY_URL, NULL);
                    659:        if (uri != NULL) {
                    660:                macro_open(f, "Lk");
                    661:                macro_addarg(f, uri, ARG_SPACE | ARG_SINGLE);
                    662:                if (TAILQ_FIRST(&n->childq) != NULL)
                    663:                        macro_addnode(f, n, ARG_SPACE | ARG_SINGLE);
                    664:                pnode_unlinksub(n);
1.128     schwarze  665:        }
                    666: }
                    667:
                    668: static void
                    669: pnode_printolink(struct format *f, struct pnode *n)
                    670: {
                    671:        const char      *uri, *ptr, *local;
                    672:
                    673:        uri = pnode_getattr_raw(n, ATTRKEY_TARGETDOC, NULL);
                    674:        ptr = pnode_getattr_raw(n, ATTRKEY_TARGETPTR, NULL);
                    675:        local = pnode_getattr_raw(n, ATTRKEY_LOCALINFO, NULL);
                    676:        if (uri == NULL) {
                    677:                uri = ptr;
                    678:                ptr = NULL;
                    679:        }
                    680:        if (uri == NULL) {
                    681:                uri = local;
                    682:                local = NULL;
                    683:        }
                    684:        if (uri == NULL)
1.96      schwarze  685:                return;
1.128     schwarze  686:
                    687:        macro_open(f, "Lk");
                    688:        macro_addarg(f, uri, ARG_SPACE | ARG_SINGLE);
                    689:        macro_addnode(f, n, ARG_SPACE | ARG_SINGLE);
                    690:        if (ptr != NULL || local != NULL) {
                    691:                macro_close(f);
                    692:                macro_open(f, "Pq");
                    693:                if (ptr != NULL)
                    694:                        macro_addarg(f, ptr, ARG_SPACE);
                    695:                if (local != NULL)
                    696:                        macro_addarg(f, local, ARG_SPACE);
1.96      schwarze  697:        }
1.128     schwarze  698:        pnode_unlinksub(n);
1.96      schwarze  699: }
                    700:
                    701: static void
1.116     schwarze  702: pnode_printprologue(struct format *f, struct pnode *root)
1.7       kristaps  703: {
1.141     schwarze  704:        struct pnode    *name, *nc;
1.7       kristaps  705:
1.141     schwarze  706:        nc = TAILQ_FIRST(&root->childq);
                    707:        assert(nc->node == NODE_DATE);
                    708:        macro_nodeline(f, "Dd", nc, 0);
                    709:        pnode_unlink(nc);
1.116     schwarze  710:
                    711:        macro_open(f, "Dt");
1.141     schwarze  712:        name = TAILQ_FIRST(&root->childq);
                    713:        assert(name->node == NODE_REFENTRYTITLE);
                    714:        macro_addnode(f, name, ARG_SPACE | ARG_SINGLE | ARG_UPPER);
                    715:        TAILQ_REMOVE(&root->childq, name, child);
                    716:        name->parent = NULL;
                    717:        nc = TAILQ_FIRST(&root->childq);
                    718:        assert (nc->node == NODE_MANVOLNUM);
                    719:        macro_addnode(f, nc, ARG_SPACE | ARG_SINGLE);
                    720:        pnode_unlink(nc);
1.116     schwarze  721:
1.101     schwarze  722:        macro_line(f, "Os");
1.116     schwarze  723:
1.141     schwarze  724:        nc = TAILQ_FIRST(&root->childq);
                    725:        if (nc != NULL && nc->node == NODE_TITLE) {
1.116     schwarze  726:                macro_line(f, "Sh NAME");
1.141     schwarze  727:                macro_nodeline(f, "Nm", name, ARG_SINGLE);
                    728:                macro_nodeline(f, "Nd", nc, 0);
                    729:                pnode_unlink(nc);
1.116     schwarze  730:        }
1.119     schwarze  731:        pnode_unlink(name);
1.130     schwarze  732:        f->parastate = PARA_HAVE;
1.7       kristaps  733: }
                    734:
1.42      kristaps  735: /*
                    736:  * We can have multiple <term> elements within a <varlistentry>, which
                    737:  * we should comma-separate as list headers.
                    738:  */
1.13      kristaps  739: static void
1.101     schwarze  740: pnode_printvarlistentry(struct format *f, struct pnode *n)
1.13      kristaps  741: {
1.133     schwarze  742:        struct pnode    *nc, *nn, *ncc;
                    743:        int              comma;
1.13      kristaps  744:
1.101     schwarze  745:        macro_open(f, "It");
1.130     schwarze  746:        f->parastate = PARA_HAVE;
1.103     schwarze  747:        f->flags |= FMT_IMPL;
1.133     schwarze  748:        comma = -1;
1.108     schwarze  749:        TAILQ_FOREACH_SAFE(nc, &n->childq, child, nn) {
1.101     schwarze  750:                if (nc->node != NODE_TERM && nc->node != NODE_GLOSSTERM)
1.69      schwarze  751:                        continue;
1.133     schwarze  752:                if (comma != -1) {
1.108     schwarze  753:                        switch (f->linestate) {
                    754:                        case LINE_NEW:
                    755:                                break;
                    756:                        case LINE_TEXT:
                    757:                                print_text(f, ",", 0);
                    758:                                break;
                    759:                        case LINE_MACRO:
1.133     schwarze  760:                                macro_addarg(f, ",", comma);
1.108     schwarze  761:                                break;
                    762:                        }
                    763:                }
1.130     schwarze  764:                f->parastate = PARA_HAVE;
1.133     schwarze  765:                comma = (ncc = TAILQ_FIRST(&nc->childq)) == NULL ||
                    766:                    pnode_class(ncc->node) == CLASS_TEXT ? 0 : ARG_SPACE;
1.101     schwarze  767:                pnode_print(f, nc);
1.108     schwarze  768:                pnode_unlink(nc);
1.69      schwarze  769:        }
1.101     schwarze  770:        macro_close(f);
1.130     schwarze  771:        f->parastate = PARA_HAVE;
1.108     schwarze  772:        while ((nc = TAILQ_FIRST(&n->childq)) != NULL) {
                    773:                pnode_print(f, nc);
                    774:                pnode_unlink(nc);
                    775:        }
                    776:        macro_close(f);
1.130     schwarze  777:        f->parastate = PARA_HAVE;
1.71      schwarze  778: }
                    779:
                    780: static void
1.101     schwarze  781: pnode_printtitle(struct format *f, struct pnode *n)
1.71      schwarze  782: {
1.101     schwarze  783:        struct pnode    *nc, *nn;
1.71      schwarze  784:
1.101     schwarze  785:        TAILQ_FOREACH_SAFE(nc, &n->childq, child, nn) {
                    786:                if (nc->node == NODE_TITLE) {
1.130     schwarze  787:                        if (f->parastate == PARA_MID)
                    788:                                f->parastate = PARA_WANT;
                    789:                        macro_nodeline(f, "Sy", nc, 0);
1.101     schwarze  790:                        pnode_unlink(nc);
1.71      schwarze  791:                }
                    792:        }
1.13      kristaps  793: }
                    794:
                    795: static void
1.101     schwarze  796: pnode_printrow(struct format *f, struct pnode *n)
1.25      kristaps  797: {
1.101     schwarze  798:        struct pnode    *nc;
1.25      kristaps  799:
1.101     schwarze  800:        macro_line(f, "Bl -dash -compact");
                    801:        TAILQ_FOREACH(nc, &n->childq, child) {
                    802:                macro_line(f, "It");
                    803:                pnode_print(f, nc);
1.25      kristaps  804:        }
1.101     schwarze  805:        macro_line(f, "El");
                    806:        pnode_unlink(n);
1.25      kristaps  807: }
                    808:
                    809: static void
1.83      schwarze  810: pnode_printtgroup1(struct format *f, struct pnode *n)
1.16      kristaps  811: {
1.82      schwarze  812:        struct pnode    *nc;
                    813:
1.83      schwarze  814:        macro_line(f, "Bl -bullet -compact");
1.82      schwarze  815:        while ((nc = pnode_findfirst(n, NODE_ENTRY)) != NULL) {
1.83      schwarze  816:                macro_line(f, "It");
1.130     schwarze  817:                f->parastate = PARA_HAVE;
1.83      schwarze  818:                pnode_print(f, nc);
1.130     schwarze  819:                f->parastate = PARA_HAVE;
1.82      schwarze  820:                pnode_unlink(nc);
                    821:        }
1.83      schwarze  822:        macro_line(f, "El");
                    823:        pnode_unlinksub(n);
                    824: }
                    825:
                    826: static void
                    827: pnode_printtgroup2(struct format *f, struct pnode *n)
                    828: {
                    829:        struct pnode    *nr, *ne;
                    830:
1.130     schwarze  831:        f->parastate = PARA_HAVE;
1.83      schwarze  832:        macro_line(f, "Bl -tag -width Ds");
                    833:        while ((nr = pnode_findfirst(n, NODE_ROW)) != NULL) {
                    834:                if ((ne = pnode_findfirst(n, NODE_ENTRY)) == NULL)
                    835:                        break;
                    836:                macro_open(f, "It");
1.103     schwarze  837:                f->flags |= FMT_IMPL;
1.130     schwarze  838:                f->parastate = PARA_HAVE;
1.83      schwarze  839:                pnode_print(f, ne);
1.84      schwarze  840:                macro_close(f);
1.83      schwarze  841:                pnode_unlink(ne);
1.130     schwarze  842:                f->parastate = PARA_HAVE;
1.83      schwarze  843:                pnode_print(f, nr);
1.130     schwarze  844:                f->parastate = PARA_HAVE;
1.83      schwarze  845:                pnode_unlink(nr);
                    846:        }
                    847:        macro_line(f, "El");
1.135     schwarze  848:        f->parastate = PARA_WANT;
1.82      schwarze  849:        pnode_unlinksub(n);
                    850: }
                    851:
                    852: static void
1.83      schwarze  853: pnode_printtgroup(struct format *f, struct pnode *n)
1.82      schwarze  854: {
                    855:        struct pnode    *nc;
                    856:
                    857:        switch (atoi(pnode_getattr_raw(n, ATTRKEY_COLS, "0"))) {
                    858:        case 1:
1.83      schwarze  859:                pnode_printtgroup1(f, n);
                    860:                return;
                    861:        case 2:
                    862:                pnode_printtgroup2(f, n);
1.82      schwarze  863:                return;
                    864:        default:
                    865:                break;
                    866:        }
1.16      kristaps  867:
1.130     schwarze  868:        f->parastate = PARA_HAVE;
1.83      schwarze  869:        macro_line(f, "Bl -ohang");
1.82      schwarze  870:        while ((nc = pnode_findfirst(n, NODE_ROW)) != NULL) {
1.83      schwarze  871:                macro_line(f, "It Table Row");
                    872:                pnode_printrow(f, nc);
1.25      kristaps  873:        }
1.83      schwarze  874:        macro_line(f, "El");
1.135     schwarze  875:        f->parastate = PARA_WANT;
1.82      schwarze  876:        pnode_unlinksub(n);
1.25      kristaps  877: }
                    878:
                    879: static void
1.101     schwarze  880: pnode_printlist(struct format *f, struct pnode *n)
1.25      kristaps  881: {
1.101     schwarze  882:        struct pnode    *nc;
1.16      kristaps  883:
1.101     schwarze  884:        pnode_printtitle(f, n);
1.130     schwarze  885:        f->parastate = PARA_HAVE;
1.101     schwarze  886:        macro_argline(f, "Bl",
                    887:            n->node == NODE_ORDEREDLIST ? "-enum" : "-bullet");
                    888:        TAILQ_FOREACH(nc, &n->childq, child) {
                    889:                macro_line(f, "It");
1.130     schwarze  890:                f->parastate = PARA_HAVE;
1.101     schwarze  891:                pnode_print(f, nc);
1.130     schwarze  892:                f->parastate = PARA_HAVE;
1.16      kristaps  893:        }
1.101     schwarze  894:        macro_line(f, "El");
1.135     schwarze  895:        f->parastate = PARA_WANT;
1.101     schwarze  896:        pnode_unlinksub(n);
1.16      kristaps  897: }
                    898:
                    899: static void
1.101     schwarze  900: pnode_printvariablelist(struct format *f, struct pnode *n)
1.13      kristaps  901: {
1.101     schwarze  902:        struct pnode    *nc;
1.13      kristaps  903:
1.101     schwarze  904:        pnode_printtitle(f, n);
1.130     schwarze  905:        f->parastate = PARA_HAVE;
1.101     schwarze  906:        macro_line(f, "Bl -tag -width Ds");
                    907:        TAILQ_FOREACH(nc, &n->childq, child) {
                    908:                if (nc->node == NODE_VARLISTENTRY)
                    909:                        pnode_printvarlistentry(f, nc);
1.69      schwarze  910:                else
1.101     schwarze  911:                        macro_nodeline(f, "It", nc, 0);
1.69      schwarze  912:        }
1.101     schwarze  913:        macro_line(f, "El");
1.135     schwarze  914:        f->parastate = PARA_WANT;
1.101     schwarze  915:        pnode_unlinksub(n);
1.13      kristaps  916: }
                    917:
1.1       kristaps  918: /*
                    919:  * Print a parsed node (or ignore it--whatever).
                    920:  * This is a recursive function.
1.23      kristaps  921:  * FIXME: if we're in a literal context (<screen> or <programlisting> or
                    922:  * whatever), don't print inline macros.
1.1       kristaps  923:  */
                    924: static void
1.101     schwarze  925: pnode_print(struct format *f, struct pnode *n)
1.1       kristaps  926: {
1.102     schwarze  927:        struct pnode    *nc, *nn;
1.112     schwarze  928:        int              was_impl;
1.1       kristaps  929:
1.101     schwarze  930:        if (n == NULL)
1.1       kristaps  931:                return;
                    932:
1.112     schwarze  933:        was_impl = f->flags & FMT_IMPL;
1.138     schwarze  934:        if (n->flags & NFLAG_SPC)
1.103     schwarze  935:                f->flags &= ~FMT_NOSPC;
                    936:        else
                    937:                f->flags |= FMT_NOSPC;
1.1       kristaps  938:
1.101     schwarze  939:        switch (n->node) {
1.62      schwarze  940:        case NODE_ARG:
1.101     schwarze  941:                pnode_printarg(f, n);
1.4       kristaps  942:                break;
1.62      schwarze  943:        case NODE_AUTHOR:
1.101     schwarze  944:                pnode_printauthor(f, n);
1.50      schwarze  945:                break;
1.62      schwarze  946:        case NODE_AUTHORGROUP:
1.101     schwarze  947:                macro_line(f, "An -split");
1.50      schwarze  948:                break;
1.98      schwarze  949:        case NODE_BLOCKQUOTE:
1.130     schwarze  950:                f->parastate = PARA_HAVE;
1.101     schwarze  951:                macro_line(f, "Bd -ragged -offset indent");
1.130     schwarze  952:                f->parastate = PARA_HAVE;
1.98      schwarze  953:                break;
1.62      schwarze  954:        case NODE_CITEREFENTRY:
1.101     schwarze  955:                pnode_printciterefentry(f, n);
1.1       kristaps  956:                break;
1.68      schwarze  957:        case NODE_CITETITLE:
1.101     schwarze  958:                macro_open(f, "%T");
1.68      schwarze  959:                break;
1.62      schwarze  960:        case NODE_COMMAND:
1.101     schwarze  961:                macro_open(f, "Nm");
1.13      kristaps  962:                break;
1.62      schwarze  963:        case NODE_CONSTANT:
1.101     schwarze  964:                macro_open(f, "Dv");
1.33      kristaps  965:                break;
1.121     schwarze  966:        case NODE_COPYRIGHT:
                    967:                print_text(f, "Copyright", ARG_SPACE);
                    968:                fputs(" \\(co", stdout);
                    969:                break;
1.62      schwarze  970:        case NODE_EDITOR:
1.101     schwarze  971:                print_text(f, "editor:", ARG_SPACE);
1.121     schwarze  972:                pnode_printauthor(f, n);
1.50      schwarze  973:                break;
1.65      schwarze  974:        case NODE_EMAIL:
1.115     schwarze  975:                if (was_impl)
                    976:                        macro_open(f, "Ao Mt");
                    977:                else {
                    978:                        macro_open(f, "Aq Mt");
                    979:                        f->flags |= FMT_IMPL;
                    980:                }
1.65      schwarze  981:                break;
1.62      schwarze  982:        case NODE_EMPHASIS:
                    983:        case NODE_FIRSTTERM:
1.99      schwarze  984:        case NODE_GLOSSTERM:
1.123     schwarze  985:                if ((nc = TAILQ_FIRST(&n->childq)) != NULL &&
                    986:                    pnode_class(nc->node) < CLASS_LINE)
                    987:                        macro_open(f, "Em");
1.130     schwarze  988:                if (n->node == NODE_GLOSSTERM)
                    989:                        f->parastate = PARA_HAVE;
1.1       kristaps  990:                break;
1.62      schwarze  991:        case NODE_ENVAR:
1.101     schwarze  992:                macro_open(f, "Ev");
1.79      schwarze  993:                break;
1.90      schwarze  994:        case NODE_ERRORNAME:
1.101     schwarze  995:                macro_open(f, "Er");
1.90      schwarze  996:                break;
1.62      schwarze  997:        case NODE_FILENAME:
1.101     schwarze  998:                macro_open(f, "Pa");
1.17      kristaps  999:                break;
1.125     schwarze 1000:        case NODE_FOOTNOTE:
                   1001:                macro_line(f, "Bo");
1.130     schwarze 1002:                f->parastate = PARA_HAVE;
1.125     schwarze 1003:                break;
1.62      schwarze 1004:        case NODE_FUNCTION:
1.101     schwarze 1005:                macro_open(f, "Fn");
1.3       kristaps 1006:                break;
1.62      schwarze 1007:        case NODE_FUNCPROTOTYPE:
1.101     schwarze 1008:                pnode_printfuncprototype(f, n);
1.3       kristaps 1009:                break;
1.62      schwarze 1010:        case NODE_FUNCSYNOPSISINFO:
1.101     schwarze 1011:                macro_open(f, "Fd");
1.126     schwarze 1012:                break;
                   1013:        case NODE_IMAGEDATA:
                   1014:                pnode_printimagedata(f, n);
1.16      kristaps 1015:                break;
1.62      schwarze 1016:        case NODE_INFORMALEQUATION:
1.130     schwarze 1017:                f->parastate = PARA_HAVE;
1.111     schwarze 1018:                macro_line(f, "Bd -ragged -offset indent");
1.130     schwarze 1019:                f->parastate = PARA_HAVE;
1.111     schwarze 1020:                /* FALLTHROUGH */
                   1021:        case NODE_INLINEEQUATION:
1.101     schwarze 1022:                macro_line(f, "EQ");
1.43      kristaps 1023:                break;
1.62      schwarze 1024:        case NODE_ITEMIZEDLIST:
1.101     schwarze 1025:                pnode_printlist(f, n);
1.24      kristaps 1026:                break;
1.62      schwarze 1027:        case NODE_GROUP:
1.101     schwarze 1028:                pnode_printgroup(f, n);
1.10      kristaps 1029:                break;
1.67      schwarze 1030:        case NODE_KEYSYM:
1.131     schwarze 1031:        case NODE_PRODUCTNAME:
1.101     schwarze 1032:                macro_open(f, "Sy");
1.67      schwarze 1033:                break;
1.62      schwarze 1034:        case NODE_LINK:
1.101     schwarze 1035:                pnode_printlink(f, n);
1.96      schwarze 1036:                break;
1.62      schwarze 1037:        case NODE_LITERAL:
1.122     schwarze 1038:                if (n->parent != NULL && n->parent->node == NODE_QUOTE)
                   1039:                        macro_open(f, "Li");
                   1040:                else if (was_impl)
                   1041:                        macro_open(f, "So Li");
1.112     schwarze 1042:                else {
                   1043:                        macro_open(f, "Ql");
                   1044:                        f->flags |= FMT_IMPL;
                   1045:                }
1.19      kristaps 1046:                break;
1.62      schwarze 1047:        case NODE_LITERALLAYOUT:
1.101     schwarze 1048:                macro_close(f);
1.130     schwarze 1049:                f->parastate = PARA_HAVE;
1.101     schwarze 1050:                macro_argline(f, "Bd", pnode_getattr(n, ATTRKEY_CLASS) ==
1.66      schwarze 1051:                    ATTRVAL_MONOSPACED ? "-literal" : "-unfilled");
1.130     schwarze 1052:                f->parastate = PARA_HAVE;
1.60      schwarze 1053:                break;
1.106     schwarze 1054:        case NODE_MARKUP:
                   1055:                macro_open(f, "Ic");
                   1056:                break;
1.62      schwarze 1057:        case NODE_MML_MFENCED:
1.101     schwarze 1058:                pnode_printmathfenced(f, n);
1.40      kristaps 1059:                break;
1.62      schwarze 1060:        case NODE_MML_MROW:
                   1061:        case NODE_MML_MI:
                   1062:        case NODE_MML_MN:
                   1063:        case NODE_MML_MO:
1.101     schwarze 1064:                if (TAILQ_EMPTY(&n->childq))
1.43      kristaps 1065:                        break;
                   1066:                fputs(" { ", stdout);
1.40      kristaps 1067:                break;
1.62      schwarze 1068:        case NODE_MML_MFRAC:
                   1069:        case NODE_MML_MSUB:
                   1070:        case NODE_MML_MSUP:
1.101     schwarze 1071:                pnode_printmath(f, n);
1.128     schwarze 1072:                break;
                   1073:        case NODE_OLINK:
                   1074:                pnode_printolink(f, n);
1.40      kristaps 1075:                break;
1.62      schwarze 1076:        case NODE_OPTION:
1.123     schwarze 1077:                if ((nc = TAILQ_FIRST(&n->childq)) != NULL &&
                   1078:                    pnode_class(nc->node) < CLASS_LINE)
                   1079:                        macro_open(f, "Fl");
1.1       kristaps 1080:                break;
1.62      schwarze 1081:        case NODE_ORDEREDLIST:
1.101     schwarze 1082:                pnode_printlist(f, n);
1.25      kristaps 1083:                break;
1.62      schwarze 1084:        case NODE_PARA:
1.130     schwarze 1085:                if (f->parastate == PARA_MID)
                   1086:                        f->parastate = PARA_WANT;
1.3       kristaps 1087:                break;
1.87      schwarze 1088:        case NODE_PARAMDEF:
1.62      schwarze 1089:        case NODE_PARAMETER:
1.104     schwarze 1090:                /* More often, these appear inside NODE_FUNCPROTOTYPE. */
                   1091:                macro_open(f, "Fa");
                   1092:                macro_addnode(f, n, ARG_SPACE | ARG_SINGLE);
1.101     schwarze 1093:                pnode_unlinksub(n);
1.1       kristaps 1094:                break;
1.62      schwarze 1095:        case NODE_QUOTE:
1.127     schwarze 1096:                if ((nc = TAILQ_FIRST(&n->childq)) != NULL &&
                   1097:                    nc->node == NODE_FILENAME &&
                   1098:                    TAILQ_NEXT(nc, child) == NULL) {
1.138     schwarze 1099:                        if (n->flags & NFLAG_SPC)
                   1100:                                nc->flags |= NFLAG_SPC;
1.127     schwarze 1101:                } else if (was_impl)
1.112     schwarze 1102:                        macro_open(f, "Do");
                   1103:                else {
                   1104:                        macro_open(f, "Dq");
                   1105:                        f->flags |= FMT_IMPL;
                   1106:                }
1.28      kristaps 1107:                break;
1.62      schwarze 1108:        case NODE_PROGRAMLISTING:
                   1109:        case NODE_SCREEN:
1.88      schwarze 1110:        case NODE_SYNOPSIS:
1.130     schwarze 1111:                f->parastate = PARA_HAVE;
1.101     schwarze 1112:                macro_line(f, "Bd -literal");
1.130     schwarze 1113:                f->parastate = PARA_HAVE;
1.118     schwarze 1114:                break;
                   1115:        case NODE_SYSTEMITEM:
                   1116:                pnode_printsystemitem(f, n);
1.15      kristaps 1117:                break;
1.62      schwarze 1118:        case NODE_REFNAME:
1.110     schwarze 1119:                /* More often, these appear inside NODE_REFNAMEDIV. */
1.101     schwarze 1120:                macro_open(f, "Nm");
1.10      kristaps 1121:                break;
1.62      schwarze 1122:        case NODE_REFNAMEDIV:
1.110     schwarze 1123:                pnode_printrefnamediv(f, n);
1.1       kristaps 1124:                break;
1.62      schwarze 1125:        case NODE_REFPURPOSE:
1.101     schwarze 1126:                macro_open(f, "Nd");
1.10      kristaps 1127:                break;
1.62      schwarze 1128:        case NODE_REFSYNOPSISDIV:
1.101     schwarze 1129:                pnode_printrefsynopsisdiv(f, n);
1.1       kristaps 1130:                break;
1.62      schwarze 1131:        case NODE_SECTION:
1.121     schwarze 1132:        case NODE_SIMPLESECT:
1.100     schwarze 1133:        case NODE_APPENDIX:
1.62      schwarze 1134:        case NODE_NOTE:
1.119     schwarze 1135:                pnode_printsection(f, n);
1.1       kristaps 1136:                break;
1.62      schwarze 1137:        case NODE_REPLACEABLE:
1.101     schwarze 1138:                macro_open(f, "Ar");
1.13      kristaps 1139:                break;
1.62      schwarze 1140:        case NODE_SBR:
1.130     schwarze 1141:                if (f->parastate == PARA_MID)
                   1142:                        macro_line(f, "br");
1.124     schwarze 1143:                break;
                   1144:        case NODE_SUBSCRIPT:
                   1145:                if (f->linestate == LINE_MACRO)
                   1146:                        macro_addarg(f, "_", 0);
                   1147:                else
                   1148:                        print_text(f, "_", 0);
                   1149:                if ((nc = TAILQ_FIRST(&n->childq)) != NULL)
1.138     schwarze 1150:                        nc->flags &= ~(NFLAG_LINE | NFLAG_SPC);
1.124     schwarze 1151:                break;
                   1152:        case NODE_SUPERSCRIPT:
                   1153:                fputs("\\(ha", stdout);
                   1154:                if ((nc = TAILQ_FIRST(&n->childq)) != NULL)
1.138     schwarze 1155:                        nc->flags &= ~(NFLAG_LINE | NFLAG_SPC);
1.25      kristaps 1156:                break;
1.62      schwarze 1157:        case NODE_TEXT:
1.93      schwarze 1158:        case NODE_ESCAPE:
1.101     schwarze 1159:                pnode_printtext(f, n);
1.1       kristaps 1160:                break;
1.82      schwarze 1161:        case NODE_TGROUP:
1.101     schwarze 1162:                pnode_printtgroup(f, n);
1.82      schwarze 1163:                break;
1.62      schwarze 1164:        case NODE_TITLE:
1.121     schwarze 1165:        case NODE_SUBTITLE:
1.130     schwarze 1166:                if (f->parastate == PARA_MID)
                   1167:                        f->parastate = PARA_WANT;
1.101     schwarze 1168:                macro_nodeline(f, "Sy", n, 0);
                   1169:                pnode_unlinksub(n);
1.50      schwarze 1170:                break;
1.62      schwarze 1171:        case NODE_TYPE:
1.101     schwarze 1172:                macro_open(f, "Vt");
1.39      kristaps 1173:                break;
1.62      schwarze 1174:        case NODE_VARIABLELIST:
1.101     schwarze 1175:                pnode_printvariablelist(f, n);
1.13      kristaps 1176:                break;
1.62      schwarze 1177:        case NODE_VARNAME:
1.101     schwarze 1178:                macro_open(f, "Va");
1.132     schwarze 1179:                break;
                   1180:        case NODE_VOID:
                   1181:                print_text(f, "void", ARG_SPACE);
1.129     schwarze 1182:                break;
                   1183:        case NODE_XREF:
                   1184:                pnode_printxref(f, n);
1.23      kristaps 1185:                break;
1.142     schwarze 1186:        case NODE_CAUTION:
                   1187:        case NODE_LEGALNOTICE:
                   1188:        case NODE_PREFACE:
                   1189:        case NODE_TIP:
                   1190:        case NODE_WARNING:
                   1191:                abort();
1.1       kristaps 1192:        default:
                   1193:                break;
                   1194:        }
                   1195:
1.102     schwarze 1196:        TAILQ_FOREACH(nc, &n->childq, child)
                   1197:                pnode_print(f, nc);
1.1       kristaps 1198:
1.101     schwarze 1199:        switch (n->node) {
1.115     schwarze 1200:        case NODE_EMAIL:
                   1201:                if (was_impl) {
                   1202:                        f->flags &= ~FMT_NOSPC;
                   1203:                        macro_open(f, "Ac");
                   1204:                } else
                   1205:                        f->flags &= ~FMT_IMPL;
                   1206:                break;
1.103     schwarze 1207:        case NODE_ESCAPE:
                   1208:        case NODE_TERM:
                   1209:        case NODE_TEXT:
                   1210:                /* Accept more arguments to the previous macro. */
                   1211:                return;
1.125     schwarze 1212:        case NODE_FOOTNOTE:
1.130     schwarze 1213:                f->parastate = PARA_HAVE;
1.125     schwarze 1214:                macro_line(f, "Bc");
                   1215:                break;
1.130     schwarze 1216:        case NODE_GLOSSTERM:
                   1217:                f->parastate = PARA_HAVE;
                   1218:                break;
1.62      schwarze 1219:        case NODE_INFORMALEQUATION:
1.101     schwarze 1220:                macro_line(f, "EN");
1.111     schwarze 1221:                macro_line(f, "Ed");
1.40      kristaps 1222:                break;
1.62      schwarze 1223:        case NODE_INLINEEQUATION:
1.111     schwarze 1224:                macro_line(f, "EN");
1.43      kristaps 1225:                break;
1.112     schwarze 1226:        case NODE_LITERAL:
1.122     schwarze 1227:                if (n->parent != NULL && n->parent->node == NODE_QUOTE)
                   1228:                        /* nothing */;
                   1229:                else if (was_impl) {
1.115     schwarze 1230:                        f->flags &= ~FMT_NOSPC;
1.112     schwarze 1231:                        macro_open(f, "Sc");
1.115     schwarze 1232:                } else
1.112     schwarze 1233:                        f->flags &= ~FMT_IMPL;
                   1234:                break;
1.91      schwarze 1235:        case NODE_MEMBER:
1.102     schwarze 1236:                if ((nn = TAILQ_NEXT(n, child)) != NULL &&
                   1237:                    nn->node != NODE_MEMBER)
                   1238:                        nn = NULL;
1.101     schwarze 1239:                switch (f->linestate) {
1.91      schwarze 1240:                case LINE_TEXT:
1.102     schwarze 1241:                        if (nn != NULL)
1.101     schwarze 1242:                                print_text(f, ",", 0);
1.91      schwarze 1243:                        break;
                   1244:                case LINE_MACRO:
1.102     schwarze 1245:                        if (nn != NULL)
1.101     schwarze 1246:                                macro_addarg(f, ",", ARG_SPACE);
                   1247:                        macro_close(f);
1.91      schwarze 1248:                        break;
                   1249:                case LINE_NEW:
                   1250:                        break;
                   1251:                }
                   1252:                break;
1.62      schwarze 1253:        case NODE_MML_MROW:
                   1254:        case NODE_MML_MI:
                   1255:        case NODE_MML_MN:
                   1256:        case NODE_MML_MO:
1.101     schwarze 1257:                if (TAILQ_EMPTY(&n->childq))
1.43      kristaps 1258:                        break;
                   1259:                fputs(" } ", stdout);
1.40      kristaps 1260:                break;
1.130     schwarze 1261:        case NODE_PARA:
                   1262:                if (f->parastate == PARA_MID)
                   1263:                        f->parastate = PARA_WANT;
                   1264:                break;
1.62      schwarze 1265:        case NODE_QUOTE:
1.127     schwarze 1266:                if ((nc = TAILQ_FIRST(&n->childq)) != NULL &&
                   1267:                    nc->node == NODE_FILENAME &&
                   1268:                    TAILQ_NEXT(nc, child) == NULL)
                   1269:                        /* nothing */;
                   1270:                else if (was_impl) {
1.115     schwarze 1271:                        f->flags &= ~FMT_NOSPC;
1.112     schwarze 1272:                        macro_open(f, "Dc");
1.115     schwarze 1273:                } else
1.112     schwarze 1274:                        f->flags &= ~FMT_IMPL;
1.52      schwarze 1275:                break;
1.62      schwarze 1276:        case NODE_SECTION:
1.142     schwarze 1277:        case NODE_SIMPLESECT:
1.100     schwarze 1278:        case NODE_APPENDIX:
1.62      schwarze 1279:        case NODE_NOTE:
1.143     schwarze 1280:                if (n->parent != NULL)
                   1281:                        f->level--;
1.12      kristaps 1282:                break;
1.98      schwarze 1283:        case NODE_BLOCKQUOTE:
1.62      schwarze 1284:        case NODE_LITERALLAYOUT:
                   1285:        case NODE_PROGRAMLISTING:
                   1286:        case NODE_SCREEN:
1.88      schwarze 1287:        case NODE_SYNOPSIS:
1.130     schwarze 1288:                f->parastate = PARA_HAVE;
1.101     schwarze 1289:                macro_line(f, "Ed");
1.135     schwarze 1290:                f->parastate = PARA_WANT;
1.50      schwarze 1291:                break;
1.130     schwarze 1292:        case NODE_TITLE:
                   1293:        case NODE_SUBTITLE:
                   1294:                f->parastate = PARA_WANT;
                   1295:                break;
1.144     schwarze 1296:        case NODE_YEAR:
                   1297:                if ((nn = TAILQ_NEXT(n, child)) != NULL &&
                   1298:                    nn->node == NODE_YEAR &&
                   1299:                    f->linestate == LINE_TEXT) {
                   1300:                        print_text(f, ",", 0);
                   1301:                        nn->flags |= NFLAG_SPC;
                   1302:                        if ((nc = TAILQ_FIRST(&nn->childq)) != NULL)
                   1303:                                nc->flags |= NFLAG_SPC;
                   1304:                }
1.1       kristaps 1305:        default:
                   1306:                break;
                   1307:        }
1.103     schwarze 1308:        f->flags &= ~FMT_ARG;
1.1       kristaps 1309: }
                   1310:
1.74      schwarze 1311: void
1.114     schwarze 1312: ptree_print_mdoc(struct ptree *tree)
1.74      schwarze 1313: {
                   1314:        struct format    formatter;
1.1       kristaps 1315:
1.74      schwarze 1316:        formatter.level = 0;
                   1317:        formatter.linestate = LINE_NEW;
1.130     schwarze 1318:        formatter.parastate = PARA_HAVE;
1.116     schwarze 1319:        pnode_printprologue(&formatter, tree->root);
1.74      schwarze 1320:        pnode_print(&formatter, tree->root);
                   1321:        if (formatter.linestate != LINE_NEW)
                   1322:                putchar('\n');
1.1       kristaps 1323: }

CVSweb