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

Annotation of docbook2mdoc/docbook2mdoc.c, Revision 1.137

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

CVSweb