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

Annotation of docbook2mdoc/docbook2mdoc.c, Revision 1.134

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

CVSweb