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

Annotation of docbook2mdoc/docbook2mdoc.c, Revision 1.142

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

CVSweb