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

Annotation of docbook2mdoc/docbook2mdoc.c, Revision 1.147

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

CVSweb