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

Annotation of docbook2mdoc/docbook2mdoc.c, Revision 1.72

1.72    ! schwarze    1: /* $Id: docbook2mdoc.c,v 1.71 2019/03/24 23:48:58 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 <sys/queue.h>
                     19:
                     20: #include <assert.h>
                     21: #include <ctype.h>
                     22: #include <expat.h>
                     23: #include <fcntl.h>
                     24: #include <getopt.h>
                     25: #include <stdio.h>
                     26: #include <stdlib.h>
                     27: #include <string.h>
1.7       kristaps   28: #include <unistd.h>
1.1       kristaps   29:
1.13      kristaps   30: #include "extern.h"
1.12      kristaps   31:
1.69      schwarze   32: enum   linestate {
                     33:        LINE_NEW = 0,
                     34:        LINE_TEXT,
                     35:        LINE_MACRO
                     36: };
                     37:
1.12      kristaps   38: /*
1.1       kristaps   39:  * Global parse state.
                     40:  * Keep this as simple and small as possible.
                     41:  */
                     42: struct parse {
1.12      kristaps   43:        XML_Parser       xml;
1.1       kristaps   44:        enum nodeid      node; /* current (NODE_ROOT if pre-tree) */
1.12      kristaps   45:        const char      *fname; /* filename */
1.1       kristaps   46:        int              stop; /* should we stop now? */
1.43      kristaps   47: #define        PARSE_EQN        1
                     48:        unsigned int     flags; /* document-wide flags */
1.1       kristaps   49:        struct pnode    *root; /* root of parse tree */
                     50:        struct pnode    *cur; /* current node in tree */
1.52      schwarze   51:        int              level; /* header level, starting at 1 */
1.69      schwarze   52:        enum linestate   linestate;
1.1       kristaps   53: };
                     54:
                     55: struct node {
1.8       kristaps   56:        const char      *name; /* docbook element name */
1.56      schwarze   57:        enum nodeid      node; /* docbook element to generate */
1.1       kristaps   58: };
                     59:
                     60: TAILQ_HEAD(pnodeq, pnode);
1.12      kristaps   61: TAILQ_HEAD(pattrq, pattr);
                     62:
                     63: struct pattr {
                     64:        enum attrkey     key;
                     65:        enum attrval     val;
                     66:        char            *rawval;
                     67:        TAILQ_ENTRY(pattr) child;
                     68: };
1.1       kristaps   69:
                     70: struct pnode {
                     71:        enum nodeid      node; /* node type */
                     72:        char            *b; /* binary data buffer */
1.37      kristaps   73:        char            *real; /* store for "b" */
1.1       kristaps   74:        size_t           bsz; /* data buffer size */
                     75:        struct pnode    *parent; /* parent (or NULL if top) */
                     76:        struct pnodeq    childq; /* queue of children */
1.12      kristaps   77:        struct pattrq    attrq; /* attributes of node */
1.1       kristaps   78:        TAILQ_ENTRY(pnode) child;
                     79: };
                     80:
1.12      kristaps   81: static const char *attrkeys[ATTRKEY__MAX] = {
                     82:        "choice",
1.60      schwarze   83:        "class",
1.41      kristaps   84:        "close",
1.12      kristaps   85:        "id",
1.58      schwarze   86:        "linkend",
1.41      kristaps   87:        "open",
1.12      kristaps   88:        "rep"
                     89: };
                     90:
                     91: static const char *attrvals[ATTRVAL__MAX] = {
1.60      schwarze   92:        "monospaced",
1.12      kristaps   93:        "norepeat",
                     94:        "opt",
                     95:        "plain",
                     96:        "repeat",
                     97:        "req"
                     98: };
                     99:
1.56      schwarze  100: static const struct node nodes[] = {
                    101:        { "acronym",            NODE_ACRONYM },
                    102:        { "affiliation",        NODE_AFFILIATION },
                    103:        { "anchor",             NODE_ANCHOR },
                    104:        { "application",        NODE_APPLICATION },
                    105:        { "arg",                NODE_ARG },
                    106:        { "author",             NODE_AUTHOR },
                    107:        { "authorgroup",        NODE_AUTHORGROUP },
                    108:        { "blockquote",         NODE_BLOCKQUOTE },
                    109:        { "book",               NODE_BOOK },
                    110:        { "bookinfo",           NODE_BOOKINFO },
                    111:        { "caution",            NODE_CAUTION },
                    112:        { "chapter",            NODE_SECTION },
                    113:        { "citerefentry",       NODE_CITEREFENTRY },
                    114:        { "citetitle",          NODE_CITETITLE },
                    115:        { "cmdsynopsis",        NODE_CMDSYNOPSIS },
                    116:        { "code",               NODE_CODE },
                    117:        { "colspec",            NODE_COLSPEC },
                    118:        { "command",            NODE_COMMAND },
                    119:        { "constant",           NODE_CONSTANT },
                    120:        { "copyright",          NODE_COPYRIGHT },
                    121:        { "date",               NODE_DATE },
                    122:        { "editor",             NODE_EDITOR },
1.65      schwarze  123:        { "email",              NODE_EMAIL },
1.56      schwarze  124:        { "emphasis",           NODE_EMPHASIS },
                    125:        { "entry",              NODE_ENTRY },
                    126:        { "envar",              NODE_ENVAR },
                    127:        { "fieldsynopsis",      NODE_FIELDSYNOPSIS },
                    128:        { "filename",           NODE_FILENAME },
                    129:        { "firstname",          NODE_FIRSTNAME },
                    130:        { "firstterm",          NODE_FIRSTTERM },
                    131:        { "footnote",           NODE_FOOTNOTE },
                    132:        { "funcdef",            NODE_FUNCDEF },
                    133:        { "funcprototype",      NODE_FUNCPROTOTYPE },
                    134:        { "funcsynopsis",       NODE_FUNCSYNOPSIS },
                    135:        { "funcsynopsisinfo",   NODE_FUNCSYNOPSISINFO },
                    136:        { "function",           NODE_FUNCTION },
                    137:        { "glossterm",          NODE_GLOSSTERM },
                    138:        { "group",              NODE_GROUP },
                    139:        { "holder",             NODE_HOLDER },
                    140:        { "index",              NODE_INDEX },
                    141:        { "indexterm",          NODE_INDEXTERM },
                    142:        { "info",               NODE_INFO },
                    143:        { "informalequation",   NODE_INFORMALEQUATION },
                    144:        { "informaltable",      NODE_INFORMALTABLE },
                    145:        { "inlineequation",     NODE_INLINEEQUATION },
                    146:        { "itemizedlist",       NODE_ITEMIZEDLIST },
                    147:        { "keysym",             NODE_KEYSYM },
                    148:        { "legalnotice",        NODE_LEGALNOTICE },
                    149:        { "link",               NODE_LINK },
                    150:        { "listitem",           NODE_LISTITEM },
                    151:        { "literal",            NODE_LITERAL },
                    152:        { "literallayout",      NODE_LITERALLAYOUT },
                    153:        { "manvolnum",          NODE_MANVOLNUM },
                    154:        { "member",             NODE_MEMBER },
                    155:        { "mml:math",           NODE_MML_MATH },
                    156:        { "mml:mfenced",        NODE_MML_MFENCED },
                    157:        { "mml:mfrac",          NODE_MML_MFRAC },
                    158:        { "mml:mi",             NODE_MML_MI },
                    159:        { "mml:mn",             NODE_MML_MN },
                    160:        { "mml:mo",             NODE_MML_MO },
                    161:        { "mml:mrow",           NODE_MML_MROW },
                    162:        { "mml:msub",           NODE_MML_MSUB },
                    163:        { "mml:msup",           NODE_MML_MSUP },
                    164:        { "modifier",           NODE_MODIFIER },
                    165:        { "note",               NODE_NOTE },
                    166:        { "option",             NODE_OPTION },
                    167:        { "orderedlist",        NODE_ORDEREDLIST },
                    168:        { "orgname",            NODE_ORGNAME },
                    169:        { "othername",          NODE_OTHERNAME },
                    170:        { "para",               NODE_PARA },
                    171:        { "paramdef",           NODE_PARAMDEF },
                    172:        { "parameter",          NODE_PARAMETER },
                    173:        { "part",               NODE_SECTION },
1.65      schwarze  174:        { "personname",         NODE_PERSONNAME },
1.56      schwarze  175:        { "phrase",             NODE_PHRASE },
                    176:        { "preface",            NODE_PREFACE },
                    177:        { "primary",            NODE_PRIMARY },
                    178:        { "programlisting",     NODE_PROGRAMLISTING },
                    179:        { "prompt",             NODE_PROMPT },
                    180:        { "quote",              NODE_QUOTE },
                    181:        { "refclass",           NODE_REFCLASS },
                    182:        { "refdescriptor",      NODE_REFDESCRIPTOR },
                    183:        { "refentry",           NODE_REFENTRY },
                    184:        { "refentryinfo",       NODE_REFENTRYINFO },
                    185:        { "refentrytitle",      NODE_REFENTRYTITLE },
                    186:        { "refmeta",            NODE_REFMETA },
                    187:        { "refmetainfo",        NODE_REFMETAINFO },
                    188:        { "refmiscinfo",        NODE_REFMISCINFO },
                    189:        { "refname",            NODE_REFNAME },
                    190:        { "refnamediv",         NODE_REFNAMEDIV },
                    191:        { "refpurpose",         NODE_REFPURPOSE },
                    192:        { "refsect1",           NODE_SECTION },
                    193:        { "refsect2",           NODE_SECTION },
                    194:        { "refsect3",           NODE_SECTION },
                    195:        { "refsection",         NODE_SECTION },
                    196:        { "refsynopsisdiv",     NODE_REFSYNOPSISDIV },
                    197:        { "releaseinfo",        NODE_RELEASEINFO },
                    198:        { "replaceable",        NODE_REPLACEABLE },
                    199:        { "row",                NODE_ROW },
                    200:        { "sbr",                NODE_SBR },
                    201:        { "screen",             NODE_SCREEN },
                    202:        { "secondary",          NODE_SECONDARY },
                    203:        { "sect1",              NODE_SECTION },
                    204:        { "sect2",              NODE_SECTION },
                    205:        { "section",            NODE_SECTION },
                    206:        { "sgmltag",            NODE_SGMLTAG },
                    207:        { "simplelist",         NODE_SIMPLELIST },
                    208:        { "spanspec",           NODE_SPANSPEC },
                    209:        { "structname",         NODE_STRUCTNAME },
                    210:        { "subtitle",           NODE_SUBTITLE },
                    211:        { "surname",            NODE_SURNAME },
                    212:        { "synopsis",           NODE_SYNOPSIS },
                    213:        { "table",              NODE_TABLE },
                    214:        { "tbody",              NODE_TBODY },
                    215:        { "term",               NODE_TERM },
                    216:        { "tfoot",              NODE_TFOOT },
                    217:        { "tgroup",             NODE_TGROUP },
                    218:        { "thead",              NODE_THEAD },
                    219:        { "tip",                NODE_TIP },
                    220:        { "title",              NODE_TITLE },
                    221:        { "trademark",          NODE_TRADEMARK },
                    222:        { "type",               NODE_TYPE },
                    223:        { "ulink",              NODE_ULINK },
                    224:        { "userinput",          NODE_USERINPUT },
                    225:        { "variablelist",       NODE_VARIABLELIST },
                    226:        { "varlistentry",       NODE_VARLISTENTRY },
                    227:        { "varname",            NODE_VARNAME },
                    228:        { "warning",            NODE_WARNING },
                    229:        { "wordasword",         NODE_WORDASWORD },
                    230:        { "year",               NODE_YEAR },
                    231:        { NULL,                 NODE__MAX }
1.1       kristaps  232: };
                    233:
1.38      kristaps  234: static int warn = 0;
                    235:
1.10      kristaps  236: static void
                    237: pnode_print(struct parse *p, struct pnode *pn);
                    238:
1.8       kristaps  239: /*
                    240:  * Process a stream of characters.
                    241:  * We store text as nodes in and of themselves.
                    242:  * If a text node is already open, append to it.
                    243:  * If it's not open, open one under the current context.
                    244:  */
1.1       kristaps  245: static void
                    246: xml_char(void *arg, const XML_Char *p, int sz)
                    247: {
                    248:        struct parse    *ps = arg;
                    249:        struct pnode    *dat;
1.4       kristaps  250:        int              i;
1.1       kristaps  251:
                    252:        /* Stopped or no tree yet. */
1.64      schwarze  253:        if (ps->stop || ps->node == NODE_ROOT)
1.1       kristaps  254:                return;
                    255:
1.64      schwarze  256:        assert(ps->cur != NULL);
1.1       kristaps  257:
                    258:        /*
                    259:         * Are we in the midst of processing text?
                    260:         * If we're not processing text right now, then create a text
                    261:         * node for doing so.
1.4       kristaps  262:         * However, don't do so unless we have some non-whitespace to
1.10      kristaps  263:         * process: strip out all leading whitespace to be sure.
1.1       kristaps  264:         */
1.64      schwarze  265:        if (ps->node != NODE_TEXT) {
1.4       kristaps  266:                for (i = 0; i < sz; i++)
1.46      schwarze  267:                        if ( ! isspace((unsigned char)p[i]))
1.4       kristaps  268:                                break;
                    269:                if (i == sz)
                    270:                        return;
1.10      kristaps  271:                p += i;
                    272:                sz -= i;
1.72    ! schwarze  273:                dat = calloc(1, sizeof(*dat));
1.64      schwarze  274:                if (dat == NULL) {
1.1       kristaps  275:                        perror(NULL);
1.63      schwarze  276:                        exit(1);
1.1       kristaps  277:                }
                    278:
                    279:                dat->node = ps->node = NODE_TEXT;
                    280:                dat->parent = ps->cur;
                    281:                TAILQ_INIT(&dat->childq);
1.12      kristaps  282:                TAILQ_INIT(&dat->attrq);
1.1       kristaps  283:                TAILQ_INSERT_TAIL(&ps->cur->childq, dat, child);
                    284:                ps->cur = dat;
1.64      schwarze  285:                assert(ps->root != NULL);
1.1       kristaps  286:        }
                    287:
                    288:        /* Append to current buffer. */
                    289:        assert(sz >= 0);
1.72    ! schwarze  290:        ps->cur->b = realloc(ps->cur->b, ps->cur->bsz + sz + 1);
1.64      schwarze  291:        if (ps->cur->b == NULL) {
1.1       kristaps  292:                perror(NULL);
1.63      schwarze  293:                exit(1);
1.1       kristaps  294:        }
                    295:        memcpy(ps->cur->b + ps->cur->bsz, p, sz);
1.72    ! schwarze  296:        ps->cur->bsz += sz;
        !           297:        ps->cur->b[ps->cur->bsz] = '\0';
1.37      kristaps  298:        ps->cur->real = ps->cur->b;
1.1       kristaps  299: }
                    300:
1.10      kristaps  301: static void
                    302: pnode_trim(struct pnode *pn)
                    303: {
1.64      schwarze  304:        assert(pn->node == NODE_TEXT);
1.72    ! schwarze  305:        for (; pn->bsz > 0; pn->b[--pn->bsz] = '\0')
1.46      schwarze  306:                if ( ! isspace((unsigned char)pn->b[pn->bsz - 1]))
1.10      kristaps  307:                        break;
                    308: }
                    309:
1.1       kristaps  310: /*
                    311:  * Begin an element.
                    312:  * First, look for the element.
                    313:  * If we don't find it and we're not parsing, keep going.
1.8       kristaps  314:  * If we don't find it and we're parsing, puke and exit.
1.1       kristaps  315:  * If we find it but we're not parsing yet (i.e., it's not a refentry
                    316:  * and thus out of context), keep going.
1.8       kristaps  317:  * If we find it and we're at the root and already have a tree, puke and
                    318:  * exit (FIXME: I don't think this is right?).
                    319:  * If we find it but we're parsing a text node, close out the text node,
                    320:  * return to its parent, and keep going.
1.1       kristaps  321:  * Make sure that the element is in the right context.
                    322:  * Lastly, put the node onto our parse tree and continue.
                    323:  */
                    324: static void
                    325: xml_elem_start(void *arg, const XML_Char *name, const XML_Char **atts)
                    326: {
1.12      kristaps  327:        struct parse     *ps = arg;
1.56      schwarze  328:        const struct node *node;
1.12      kristaps  329:        enum attrkey      key;
                    330:        enum attrval      val;
                    331:        struct pnode     *dat;
                    332:        struct pattr     *pattr;
                    333:        const XML_Char  **att;
1.1       kristaps  334:
1.36      kristaps  335:        /* FIXME: find a better way to ditch other namespaces. */
1.64      schwarze  336:        if (ps->stop || strcmp(name, "xi:include") == 0)
1.1       kristaps  337:                return;
                    338:
                    339:        /* Close out text node, if applicable... */
1.64      schwarze  340:        if (ps->node == NODE_TEXT) {
1.10      kristaps  341:                pnode_trim(ps->cur);
1.1       kristaps  342:                ps->cur = ps->cur->parent;
                    343:                ps->node = ps->cur->node;
                    344:        }
                    345:
1.64      schwarze  346:        for (node = nodes; node->name != NULL; node++)
                    347:                if (strcmp(node->name, name) == 0)
1.1       kristaps  348:                        break;
                    349:
1.64      schwarze  350:        if (node->name == NULL) {
                    351:                if (ps->node == NODE_ROOT)
1.56      schwarze  352:                        return;
1.44      schwarze  353:                fprintf(stderr, "%s:%zu:%zu: unknown node \"%s\"\n",
1.12      kristaps  354:                        ps->fname, XML_GetCurrentLineNumber(ps->xml),
                    355:                        XML_GetCurrentColumnNumber(ps->xml), name);
1.1       kristaps  356:                ps->stop = 1;
                    357:                return;
1.64      schwarze  358:        } else if (ps->node == NODE_ROOT && ps->root != NULL) {
1.12      kristaps  359:                fprintf(stderr, "%s:%zu:%zu: multiple refentries\n",
                    360:                        ps->fname, XML_GetCurrentLineNumber(ps->xml),
                    361:                        XML_GetCurrentColumnNumber(ps->xml));
1.1       kristaps  362:                ps->stop = 1;
                    363:                return;
1.50      schwarze  364:        }
1.1       kristaps  365:
1.64      schwarze  366:        if (node->node == NODE_INLINEEQUATION)
1.43      kristaps  367:                ps->flags |= PARSE_EQN;
                    368:
1.72    ! schwarze  369:        if ((dat = calloc(1, sizeof(*dat))) == NULL) {
1.1       kristaps  370:                perror(NULL);
1.63      schwarze  371:                exit(1);
1.1       kristaps  372:        }
                    373:
1.56      schwarze  374:        dat->node = ps->node = node->node;
1.1       kristaps  375:        dat->parent = ps->cur;
                    376:        TAILQ_INIT(&dat->childq);
1.12      kristaps  377:        TAILQ_INIT(&dat->attrq);
1.1       kristaps  378:
1.64      schwarze  379:        if (ps->cur != NULL)
1.1       kristaps  380:                TAILQ_INSERT_TAIL(&ps->cur->childq, dat, child);
                    381:
                    382:        ps->cur = dat;
1.64      schwarze  383:        if (ps->root == NULL)
1.1       kristaps  384:                ps->root = dat;
1.12      kristaps  385:
                    386:        /*
                    387:         * Process attributes.
                    388:         */
1.64      schwarze  389:        for (att = atts; *att != NULL; att += 2) {
1.12      kristaps  390:                for (key = 0; key < ATTRKEY__MAX; key++)
1.64      schwarze  391:                        if (strcmp(*att, attrkeys[key]) == 0)
1.12      kristaps  392:                                break;
1.64      schwarze  393:                if (key == ATTRKEY__MAX) {
1.44      schwarze  394:                        if (warn)
1.38      kristaps  395:                                fprintf(stderr, "%s:%zu:%zu: warning: "
1.44      schwarze  396:                                        "unknown attribute \"%s\"\n",
                    397:                                        ps->fname,
1.38      kristaps  398:                                        XML_GetCurrentLineNumber(ps->xml),
1.44      schwarze  399:                                        XML_GetCurrentColumnNumber(ps->xml),
1.38      kristaps  400:                                        *att);
1.12      kristaps  401:                        continue;
                    402:                }
                    403:                for (val = 0; val < ATTRVAL__MAX; val++)
1.64      schwarze  404:                        if (strcmp(att[1], attrvals[val]) == 0)
1.12      kristaps  405:                                break;
1.72    ! schwarze  406:                pattr = calloc(1, sizeof(*pattr));
1.12      kristaps  407:                pattr->key = key;
                    408:                pattr->val = val;
1.64      schwarze  409:                if (val == ATTRVAL__MAX)
                    410:                        pattr->rawval = strdup(att[1]);
1.12      kristaps  411:                TAILQ_INSERT_TAIL(&dat->attrq, pattr, child);
                    412:        }
                    413:
1.1       kristaps  414: }
                    415:
                    416: /*
                    417:  * Roll up the parse tree.
1.8       kristaps  418:  * If we're at a text node, roll that one up first.
1.1       kristaps  419:  * If we hit the root, then assign ourselves as the NODE_ROOT.
                    420:  */
                    421: static void
                    422: xml_elem_end(void *arg, const XML_Char *name)
                    423: {
                    424:        struct parse    *ps = arg;
                    425:
1.36      kristaps  426:        /* FIXME: find a better way to ditch other namespaces. */
1.64      schwarze  427:        if (ps->stop || ps->node == NODE_ROOT)
1.36      kristaps  428:                return;
1.64      schwarze  429:        else if (strcmp(name, "xi:include") == 0)
1.1       kristaps  430:                return;
                    431:
                    432:        /* Close out text node, if applicable... */
1.64      schwarze  433:        if (ps->node == NODE_TEXT) {
1.10      kristaps  434:                pnode_trim(ps->cur);
1.1       kristaps  435:                ps->cur = ps->cur->parent;
                    436:                ps->node = ps->cur->node;
                    437:        }
                    438:
1.64      schwarze  439:        if ((ps->cur = ps->cur->parent) == NULL)
1.1       kristaps  440:                ps->node = NODE_ROOT;
                    441:        else
                    442:                ps->node = ps->cur->node;
                    443: }
                    444:
1.8       kristaps  445: /*
                    446:  * Recursively free a node (NULL is ok).
                    447:  */
1.1       kristaps  448: static void
                    449: pnode_free(struct pnode *pn)
                    450: {
                    451:        struct pnode    *pp;
1.12      kristaps  452:        struct pattr    *ap;
1.1       kristaps  453:
1.64      schwarze  454:        if (pn == NULL)
1.1       kristaps  455:                return;
                    456:
1.64      schwarze  457:        while ((pp = TAILQ_FIRST(&pn->childq)) != NULL) {
1.1       kristaps  458:                TAILQ_REMOVE(&pn->childq, pp, child);
                    459:                pnode_free(pp);
                    460:        }
                    461:
1.64      schwarze  462:        while ((ap = TAILQ_FIRST(&pn->attrq)) != NULL) {
1.12      kristaps  463:                TAILQ_REMOVE(&pn->attrq, ap, child);
                    464:                free(ap->rawval);
                    465:                free(ap);
                    466:        }
                    467:
1.37      kristaps  468:        free(pn->real);
1.1       kristaps  469:        free(pn);
                    470: }
                    471:
1.8       kristaps  472: /*
                    473:  * Unlink a node from its parent and pnode_free() it.
                    474:  */
1.1       kristaps  475: static void
                    476: pnode_unlink(struct pnode *pn)
                    477: {
1.64      schwarze  478:        if (pn->parent != NULL)
1.1       kristaps  479:                TAILQ_REMOVE(&pn->parent->childq, pn, child);
                    480:        pnode_free(pn);
                    481: }
                    482:
1.8       kristaps  483: /*
                    484:  * Unlink all children of a node and pnode_free() them.
                    485:  */
1.1       kristaps  486: static void
1.4       kristaps  487: pnode_unlinksub(struct pnode *pn)
                    488: {
                    489:
                    490:        while ( ! TAILQ_EMPTY(&pn->childq))
                    491:                pnode_unlink(TAILQ_FIRST(&pn->childq));
                    492: }
                    493:
1.8       kristaps  494: /*
1.60      schwarze  495:  * Retrieve an enumeration attribute from a node.
                    496:  * Return ATTRVAL__MAX if the node has no such attribute.
                    497:  */
                    498: enum attrval
                    499: pnode_getattr(struct pnode *pn, enum attrkey key)
                    500: {
                    501:        struct pattr    *ap;
                    502:
                    503:        TAILQ_FOREACH(ap, &pn->attrq, child)
                    504:                if (ap->key == key)
                    505:                        return ap->val;
1.63      schwarze  506:        return ATTRVAL__MAX;
1.60      schwarze  507: }
                    508:
                    509: /*
1.59      schwarze  510:  * Retrieve an attribute string from a node.
                    511:  * Return defval if the node has no such attribute.
                    512:  */
                    513: const char *
                    514: pnode_getattr_raw(struct pnode *pn, enum attrkey key, const char *defval)
                    515: {
                    516:        struct pattr    *ap;
                    517:
                    518:        TAILQ_FOREACH(ap, &pn->attrq, child)
                    519:                if (ap->key == key)
1.64      schwarze  520:                        return ap->val == ATTRVAL__MAX ? ap->rawval :
1.59      schwarze  521:                            attrvals[ap->val];
1.63      schwarze  522:        return defval;
1.59      schwarze  523: }
                    524:
                    525: /*
1.25      kristaps  526:  * Recursively search and return the first instance of "node".
                    527:  */
                    528: static struct pnode *
                    529: pnode_findfirst(struct pnode *pn, enum nodeid node)
                    530: {
                    531:        struct pnode    *pp, *res;
                    532:
                    533:        res = NULL;
                    534:        TAILQ_FOREACH(pp, &pn->childq, child) {
                    535:                res = pp->node == node ? pp :
                    536:                        pnode_findfirst(pp, node);
1.64      schwarze  537:                if (res != NULL)
1.25      kristaps  538:                        break;
                    539:        }
                    540:
1.63      schwarze  541:        return res;
1.25      kristaps  542: }
                    543:
1.69      schwarze  544: static void
                    545: macro_open(struct parse *p, const char *name)
                    546: {
                    547:        switch (p->linestate) {
                    548:        case LINE_TEXT:
                    549:                putchar('\n');
                    550:                /* FALLTHROUGH */
                    551:        case LINE_NEW:
                    552:                putchar('.');
                    553:                p->linestate = LINE_MACRO;
                    554:                break;
                    555:        case LINE_MACRO:
                    556:                putchar(' ');
                    557:                break;
                    558:        }
                    559:        fputs(name, stdout);
                    560: }
                    561:
                    562: static void
                    563: macro_close(struct parse *p)
                    564: {
                    565:        assert(p->linestate == LINE_MACRO);
                    566:        putchar('\n');
                    567:        p->linestate = LINE_NEW;
                    568: }
                    569:
                    570: static void
                    571: macro_line(struct parse *p, const char *name)
                    572: {
                    573:        macro_open(p, name);
                    574:        macro_close(p);
                    575: }
                    576:
1.72    ! schwarze  577: #define        ARG_SPACE       1  /* Insert whitespace before this argument. */
        !           578: #define        ARG_SINGLE      2  /* Quote argument if it contains whitespace. */
        !           579: #define        ARG_QUOTED      4  /* We are already in a quoted argument. */
        !           580: #define        ARG_UPPER       8  /* Covert argument to upper case. */
1.1       kristaps  581: /*
1.69      schwarze  582:  * Print an argument string on a macro line, collapsing whitespace.
1.1       kristaps  583:  */
                    584: static void
1.72    ! schwarze  585: macro_addarg(struct parse *p, const char *arg, int flags)
1.1       kristaps  586: {
1.69      schwarze  587:        const char      *cp;
1.1       kristaps  588:
1.69      schwarze  589:        assert(p->linestate == LINE_MACRO);
1.72    ! schwarze  590:
        !           591:        /* Quote if requested and necessary. */
        !           592:
        !           593:        if ((flags & (ARG_SINGLE | ARG_QUOTED)) == ARG_SINGLE) {
        !           594:                for (cp = arg; *cp != '\0'; cp++)
        !           595:                        if (isspace((unsigned char)*cp))
        !           596:                                break;
        !           597:                if (*cp != '\0') {
        !           598:                        if (flags & ARG_SPACE) {
        !           599:                                putchar(' ');
        !           600:                                flags &= ~ ARG_SPACE;
        !           601:                        }
        !           602:                        putchar('"');
        !           603:                        flags = ARG_QUOTED;
        !           604:                }
        !           605:        }
        !           606:
1.69      schwarze  607:        for (cp = arg; *cp != '\0'; cp++) {
1.72    ! schwarze  608:
        !           609:                /* Collapse whitespace. */
        !           610:
1.69      schwarze  611:                if (isspace((unsigned char)*cp)) {
1.72    ! schwarze  612:                        flags |= ARG_SPACE;
1.69      schwarze  613:                        continue;
1.72    ! schwarze  614:                } else if (flags & ARG_SPACE) {
1.69      schwarze  615:                        putchar(' ');
1.72    ! schwarze  616:                        flags &= ~ ARG_SPACE;
1.69      schwarze  617:                }
1.72    ! schwarze  618:
1.1       kristaps  619:                /* Escape us if we look like a macro. */
1.72    ! schwarze  620:
        !           621:                if ((flags & ARG_QUOTED) == 0 &&
        !           622:                    (cp == arg || isspace((unsigned char)cp[-1])) &&
1.64      schwarze  623:                    isupper((unsigned char)cp[0]) &&
                    624:                    islower((unsigned char)cp[1]) &&
                    625:                    (cp[2] == '\0' || cp[2] == ' ' ||
                    626:                     (islower((unsigned char)cp[2]) &&
                    627:                      (cp[3] == '\0' || cp[3] == ' '))))
1.1       kristaps  628:                        fputs("\\&", stdout);
1.72    ! schwarze  629:
        !           630:                if (*cp == '"')
        !           631:                        fputs("\\(dq", stdout);
        !           632:                else if (flags & ARG_UPPER)
1.46      schwarze  633:                        putchar(toupper((unsigned char)*cp));
1.12      kristaps  634:                else
1.46      schwarze  635:                        putchar(*cp);
1.64      schwarze  636:                if (*cp == '\\')
1.1       kristaps  637:                        putchar('e');
                    638:        }
                    639: }
                    640:
1.12      kristaps  641: static void
1.69      schwarze  642: macro_argline(struct parse *p, const char *name, const char *arg)
1.12      kristaps  643: {
1.69      schwarze  644:        macro_open(p, name);
1.72    ! schwarze  645:        macro_addarg(p, arg, ARG_SPACE);
1.69      schwarze  646:        macro_close(p);
1.12      kristaps  647: }
                    648:
1.1       kristaps  649: /*
1.72    ! schwarze  650:  * Recursively append text from the children of a node to a macro line.
1.1       kristaps  651:  */
                    652: static void
1.72    ! schwarze  653: macro_addnode(struct parse *p, struct pnode *pn, int flags)
1.1       kristaps  654: {
1.72    ! schwarze  655:        int              quote_now;
        !           656:
        !           657:        assert(p->linestate == LINE_MACRO);
        !           658:
        !           659:        /*
        !           660:         * If the only child is a text node, just add that text,
        !           661:         * letting macro_addarg() decide about quoting.
        !           662:         */
        !           663:
        !           664:        pn = TAILQ_FIRST(&pn->childq);
        !           665:        if (pn != NULL && pn->node == NODE_TEXT &&
        !           666:            TAILQ_NEXT(pn, child) == NULL) {
        !           667:                macro_addarg(p, pn->b, flags);
        !           668:                return;
        !           669:        }
        !           670:
        !           671:        /*
        !           672:         * If we want the argument quoted and are not already
        !           673:         * in a quoted context, quote now.
        !           674:         */
        !           675:
        !           676:        quote_now = 0;
        !           677:        if (flags & ARG_SINGLE) {
        !           678:                if ((flags & ARG_QUOTED) == 0) {
        !           679:                        if (flags & ARG_SPACE) {
        !           680:                                putchar(' ');
        !           681:                                flags &= ~ARG_SPACE;
        !           682:                        }
        !           683:                        putchar('"');
        !           684:                        flags |= ARG_QUOTED;
        !           685:                        quote_now = 1;
        !           686:                }
        !           687:                flags &= ~ARG_SINGLE;
        !           688:        }
        !           689:
        !           690:        /*
        !           691:         * Iterate to child and sibling nodes,
        !           692:         * inserting whitespace between nodes.
        !           693:         */
        !           694:
        !           695:        while (pn != NULL) {
        !           696:                if (pn->node == NODE_TEXT)
        !           697:                        macro_addarg(p, pn->b, flags);
        !           698:                else
        !           699:                        macro_addnode(p, pn, flags);
        !           700:                pn = TAILQ_NEXT(pn, child);
        !           701:                flags |= ARG_SPACE;
        !           702:        }
        !           703:        if (quote_now)
        !           704:                putchar('"');
1.1       kristaps  705: }
                    706:
1.10      kristaps  707: static void
1.72    ! schwarze  708: macro_nodeline(struct parse *p, const char *name, struct pnode *pn, int flags)
1.10      kristaps  709: {
1.69      schwarze  710:        macro_open(p, name);
1.72    ! schwarze  711:        macro_addnode(p, pn, ARG_SPACE | flags);
1.69      schwarze  712:        macro_close(p);
1.10      kristaps  713: }
                    714:
1.8       kristaps  715: /*
1.69      schwarze  716:  * If the next node is a text node starting with closing punctuation,
                    717:  * emit the closing punctuation as a trailing macro argument.
1.37      kristaps  718:  */
                    719: static void
1.69      schwarze  720: macro_closepunct(struct parse *p, struct pnode *pn)
1.37      kristaps  721: {
1.69      schwarze  722:        if ((pn = TAILQ_NEXT(pn, child)) != NULL &&
                    723:            pn->node == NODE_TEXT && pn->bsz > 0 &&
1.64      schwarze  724:            (pn->b[0] == ',' || pn->b[0] == '.') &&
                    725:            (pn->bsz == 1 || isspace((unsigned char)pn->b[1]))) {
1.37      kristaps  726:                putchar(' ');
                    727:                putchar(pn->b[0]);
                    728:                pn->b++;
                    729:                pn->bsz--;
1.44      schwarze  730:        }
1.69      schwarze  731:        macro_close(p);
1.37      kristaps  732: }
                    733:
1.54      schwarze  734: static void
1.70      schwarze  735: print_text(struct parse *p, const char *word)
                    736: {
                    737:        switch (p->linestate) {
                    738:        case LINE_NEW:
                    739:                break;
                    740:        case LINE_TEXT:
                    741:                putchar(' ');
                    742:                break;
                    743:        case LINE_MACRO:
                    744:                macro_close(p);
                    745:                break;
                    746:        }
                    747:        fputs(word, stdout);
                    748:        p->linestate = LINE_TEXT;
                    749: }
                    750:
                    751: static void
1.54      schwarze  752: pnode_printpara(struct parse *p, struct pnode *pn)
                    753: {
                    754:        struct pnode    *pp;
                    755:
1.61      schwarze  756:        if ((pp = TAILQ_PREV(pn, pnodeq, child)) == NULL &&
                    757:            (pp = pn->parent) == NULL)
1.54      schwarze  758:                return;
                    759:
1.61      schwarze  760:        switch (pp->node) {
                    761:        case NODE_ENTRY:
                    762:        case NODE_LISTITEM:
                    763:                return;
                    764:        case NODE_PREFACE:
                    765:        case NODE_SECTION:
                    766:                if (p->level < 3)
                    767:                        return;
                    768:                break;
                    769:        default:
                    770:                break;
                    771:        }
1.69      schwarze  772:        macro_line(p, "Pp");
1.54      schwarze  773: }
                    774:
1.37      kristaps  775: /*
1.10      kristaps  776:  * If the SYNOPSIS macro has a superfluous title, kill it.
1.8       kristaps  777:  */
1.1       kristaps  778: static void
1.6       kristaps  779: pnode_printrefsynopsisdiv(struct parse *p, struct pnode *pn)
                    780: {
1.71      schwarze  781:        struct pnode    *pp, *pq;
1.6       kristaps  782:
1.71      schwarze  783:        TAILQ_FOREACH_SAFE(pp, &pn->childq, child, pq)
                    784:                if (pp->node == NODE_TITLE)
1.6       kristaps  785:                        pnode_unlink(pp);
1.71      schwarze  786:
                    787:        macro_line(p, "Sh SYNOPSIS");
1.6       kristaps  788: }
                    789:
1.8       kristaps  790: /*
                    791:  * Start a hopefully-named `Sh' section.
                    792:  */
1.6       kristaps  793: static void
1.1       kristaps  794: pnode_printrefsect(struct parse *p, struct pnode *pn)
                    795: {
                    796:        struct pnode    *pp;
1.52      schwarze  797:        const char      *title;
                    798:        int              flags, level;
                    799:
1.64      schwarze  800:        if (pn->parent == NULL)
1.56      schwarze  801:                return;
                    802:
1.52      schwarze  803:        level = ++p->level;
1.72    ! schwarze  804:        flags = ARG_SPACE;
        !           805:        if (level == 1)
        !           806:                flags |= ARG_UPPER;
1.64      schwarze  807:        if (level < 3) {
1.52      schwarze  808:                switch (pn->node) {
1.62      schwarze  809:                case NODE_CAUTION:
                    810:                case NODE_NOTE:
                    811:                case NODE_TIP:
                    812:                case NODE_WARNING:
1.52      schwarze  813:                        level = 3;
                    814:                        break;
                    815:                default:
                    816:                        break;
                    817:                }
                    818:        }
1.1       kristaps  819:
                    820:        TAILQ_FOREACH(pp, &pn->childq, child)
1.64      schwarze  821:                if (pp->node == NODE_TITLE)
1.1       kristaps  822:                        break;
                    823:
1.64      schwarze  824:        if (pp == NULL) {
1.52      schwarze  825:                switch (pn->node) {
1.62      schwarze  826:                case NODE_PREFACE:
1.52      schwarze  827:                        title = "Preface";
                    828:                        break;
1.62      schwarze  829:                case NODE_CAUTION:
1.52      schwarze  830:                        title = "Caution";
                    831:                        break;
1.62      schwarze  832:                case NODE_NOTE:
1.52      schwarze  833:                        title = "Note";
                    834:                        break;
1.62      schwarze  835:                case NODE_TIP:
1.52      schwarze  836:                        title = "Tip";
                    837:                        break;
1.62      schwarze  838:                case NODE_WARNING:
1.52      schwarze  839:                        title = "Warning";
                    840:                        break;
                    841:                default:
                    842:                        title = "Unknown";
                    843:                        break;
                    844:                }
                    845:        }
                    846:
                    847:        switch (level) {
1.62      schwarze  848:        case 1:
1.69      schwarze  849:                macro_open(p, "Sh");
1.29      kristaps  850:                break;
1.62      schwarze  851:        case 2:
1.69      schwarze  852:                macro_open(p, "Ss");
1.29      kristaps  853:                break;
1.52      schwarze  854:        default:
1.54      schwarze  855:                pnode_printpara(p, pn);
1.69      schwarze  856:                macro_open(p, "Sy");
1.29      kristaps  857:                break;
                    858:        }
1.20      kristaps  859:
1.64      schwarze  860:        if (pp != NULL) {
1.69      schwarze  861:                macro_addnode(p, pp, flags);
1.5       kristaps  862:                pnode_unlink(pp);
1.52      schwarze  863:        } else
1.72    ! schwarze  864:                macro_addarg(p, title, ARG_SPACE | ARG_QUOTED);
1.69      schwarze  865:        macro_close(p);
1.1       kristaps  866: }
                    867:
1.8       kristaps  868: /*
                    869:  * Start a reference, extracting the title and volume.
                    870:  */
1.1       kristaps  871: static void
                    872: pnode_printciterefentry(struct parse *p, struct pnode *pn)
                    873: {
                    874:        struct pnode    *pp, *title, *manvol;
                    875:
                    876:        title = manvol = NULL;
1.69      schwarze  877:        TAILQ_FOREACH(pp, &pn->childq, child) {
1.64      schwarze  878:                if (pp->node == NODE_MANVOLNUM)
1.1       kristaps  879:                        manvol = pp;
1.64      schwarze  880:                else if (pp->node == NODE_REFENTRYTITLE)
1.1       kristaps  881:                        title = pp;
1.69      schwarze  882:        }
                    883:        macro_open(p, "Xr");
                    884:        if (title == NULL)
1.72    ! schwarze  885:                macro_addarg(p, "unknown", ARG_SPACE);
1.69      schwarze  886:        else
1.72    ! schwarze  887:                macro_addnode(p, title, ARG_SPACE | ARG_SINGLE);
1.69      schwarze  888:        if (manvol == NULL)
1.72    ! schwarze  889:                macro_addarg(p, "1", ARG_SPACE);
1.64      schwarze  890:        else
1.72    ! schwarze  891:                macro_addnode(p, manvol, ARG_SPACE | ARG_SINGLE);
1.69      schwarze  892:        macro_close(p);
1.71      schwarze  893:        pnode_unlinksub(pn);
1.1       kristaps  894: }
                    895:
                    896: static void
                    897: pnode_printrefmeta(struct parse *p, struct pnode *pn)
                    898: {
                    899:        struct pnode    *pp, *title, *manvol;
                    900:
                    901:        title = manvol = NULL;
1.69      schwarze  902:        TAILQ_FOREACH(pp, &pn->childq, child) {
1.64      schwarze  903:                if (pp->node == NODE_MANVOLNUM)
1.1       kristaps  904:                        manvol = pp;
1.64      schwarze  905:                else if (pp->node == NODE_REFENTRYTITLE)
1.1       kristaps  906:                        title = pp;
1.69      schwarze  907:        }
                    908:        macro_open(p, "Dt");
                    909:        if (title == NULL)
1.72    ! schwarze  910:                macro_addarg(p, "UNKNOWN", ARG_SPACE);
1.69      schwarze  911:        else
1.72    ! schwarze  912:                macro_addnode(p, title, ARG_SPACE | ARG_SINGLE | ARG_UPPER);
1.69      schwarze  913:        if (manvol == NULL)
1.72    ! schwarze  914:                macro_addarg(p, "1", ARG_SPACE);
1.13      kristaps  915:        else
1.72    ! schwarze  916:                macro_addnode(p, manvol, ARG_SPACE | ARG_SINGLE);
1.69      schwarze  917:        macro_close(p);
1.71      schwarze  918:        pnode_unlink(pn);
1.1       kristaps  919: }
                    920:
1.3       kristaps  921: static void
                    922: pnode_printfuncdef(struct parse *p, struct pnode *pn)
                    923: {
                    924:        struct pnode    *pp, *ftype, *func;
                    925:
                    926:        ftype = func = NULL;
1.69      schwarze  927:        TAILQ_FOREACH(pp, &pn->childq, child) {
1.64      schwarze  928:                if (pp->node == NODE_TEXT)
1.3       kristaps  929:                        ftype = pp;
1.64      schwarze  930:                else if (pp->node == NODE_FUNCTION)
1.3       kristaps  931:                        func = pp;
1.13      kristaps  932:        }
1.69      schwarze  933:        if (ftype != NULL)
1.72    ! schwarze  934:                macro_argline(p, "Ft", ftype->b);
1.69      schwarze  935:        macro_open(p, "Fo");
                    936:        if (func == NULL)
1.72    ! schwarze  937:                macro_addarg(p, "UNKNOWN", ARG_SPACE);
1.69      schwarze  938:        else
1.72    ! schwarze  939:                macro_addnode(p, func, ARG_SPACE | ARG_SINGLE);
1.69      schwarze  940:        macro_close(p);
1.3       kristaps  941: }
                    942:
1.40      kristaps  943: /*
1.41      kristaps  944:  * The <mml:mfenced> node is a little peculiar.
                    945:  * First, it can have arbitrary open and closing tokens, which default
                    946:  * to parentheses.
                    947:  * Second, >1 arguments are separated by commas.
                    948:  */
                    949: static void
                    950: pnode_printmathfenced(struct parse *p, struct pnode *pn)
                    951: {
                    952:        struct pnode    *pp;
                    953:
1.59      schwarze  954:        printf("left %s ", pnode_getattr_raw(pn, ATTRKEY_OPEN, "("));
1.41      kristaps  955:
                    956:        pp = TAILQ_FIRST(&pn->childq);
                    957:        pnode_print(p, pp);
                    958:
1.64      schwarze  959:        while ((pp = TAILQ_NEXT(pp, child)) != NULL) {
1.41      kristaps  960:                putchar(',');
                    961:                pnode_print(p, pp);
                    962:        }
1.59      schwarze  963:        printf("right %s ", pnode_getattr_raw(pn, ATTRKEY_CLOSE, ")"));
1.71      schwarze  964:        pnode_unlinksub(pn);
1.41      kristaps  965: }
                    966:
                    967: /*
1.40      kristaps  968:  * These math nodes require special handling because they have infix
                    969:  * syntax, instead of the usual prefix or prefix.
                    970:  * So we need to break up the first and second child node with a
                    971:  * particular eqn(7) word.
                    972:  */
                    973: static void
                    974: pnode_printmath(struct parse *p, struct pnode *pn)
                    975: {
                    976:        struct pnode    *pp;
                    977:
                    978:        pp = TAILQ_FIRST(&pn->childq);
                    979:        pnode_print(p, pp);
                    980:
                    981:        switch (pn->node) {
1.62      schwarze  982:        case NODE_MML_MSUP:
1.42      kristaps  983:                fputs(" sup ", stdout);
1.40      kristaps  984:                break;
1.62      schwarze  985:        case NODE_MML_MFRAC:
1.42      kristaps  986:                fputs(" over ", stdout);
1.40      kristaps  987:                break;
1.62      schwarze  988:        case NODE_MML_MSUB:
1.42      kristaps  989:                fputs(" sub ", stdout);
1.40      kristaps  990:                break;
                    991:        default:
                    992:                break;
                    993:        }
                    994:
                    995:        pp = TAILQ_NEXT(pp, child);
                    996:        pnode_print(p, pp);
1.71      schwarze  997:        pnode_unlinksub(pn);
1.40      kristaps  998: }
                    999:
1.3       kristaps 1000: static void
                   1001: pnode_printfuncprototype(struct parse *p, struct pnode *pn)
                   1002: {
                   1003:        struct pnode    *pp, *fdef;
                   1004:
                   1005:        TAILQ_FOREACH(fdef, &pn->childq, child)
1.64      schwarze 1006:                if (fdef->node == NODE_FUNCDEF)
1.3       kristaps 1007:                        break;
                   1008:
1.64      schwarze 1009:        if (fdef != NULL)
1.3       kristaps 1010:                pnode_printfuncdef(p, fdef);
1.4       kristaps 1011:        else
1.69      schwarze 1012:                macro_line(p, "Fo UNKNOWN");
1.3       kristaps 1013:
1.44      schwarze 1014:        TAILQ_FOREACH(pp, &pn->childq, child)
1.64      schwarze 1015:                if (pp->node == NODE_PARAMDEF)
1.72    ! schwarze 1016:                        macro_nodeline(p, "Fa", pp, ARG_SINGLE);
1.3       kristaps 1017:
1.69      schwarze 1018:        macro_line(p, "Fc");
1.71      schwarze 1019:        pnode_unlinksub(pn);
1.3       kristaps 1020: }
                   1021:
1.44      schwarze 1022: /*
1.10      kristaps 1023:  * The <arg> element is more complicated than it should be because text
                   1024:  * nodes are treated like ".Ar foo", but non-text nodes need to be
                   1025:  * re-sent into the printer (i.e., without the preceding ".Ar").
1.12      kristaps 1026:  * This also handles the case of "repetition" (or in other words, the
                   1027:  * ellipsis following an argument) and optionality.
1.10      kristaps 1028:  */
1.4       kristaps 1029: static void
1.10      kristaps 1030: pnode_printarg(struct parse *p, struct pnode *pn)
1.4       kristaps 1031: {
                   1032:        struct pnode    *pp;
1.12      kristaps 1033:        struct pattr    *ap;
                   1034:        int              isop, isrep;
                   1035:
                   1036:        isop = 1;
                   1037:        isrep = 0;
1.69      schwarze 1038:        TAILQ_FOREACH(ap, &pn->attrq, child) {
1.64      schwarze 1039:                if (ap->key == ATTRKEY_CHOICE &&
                   1040:                    (ap->val == ATTRVAL_PLAIN || ap->val == ATTRVAL_REQ))
1.12      kristaps 1041:                        isop = 0;
1.64      schwarze 1042:                else if (ap->key == ATTRKEY_REP && ap->val == ATTRVAL_REPEAT)
1.12      kristaps 1043:                        isrep = 1;
                   1044:        }
1.69      schwarze 1045:        if (isop)
                   1046:                macro_open(p, "Op");
1.4       kristaps 1047:
1.10      kristaps 1048:        TAILQ_FOREACH(pp, &pn->childq, child) {
1.69      schwarze 1049:                if (pp->node == NODE_TEXT)
                   1050:                        macro_open(p, "Ar");
1.10      kristaps 1051:                pnode_print(p, pp);
1.64      schwarze 1052:                if (isrep && pp->node == NODE_TEXT)
1.72    ! schwarze 1053:                        macro_addarg(p, "...", ARG_SPACE);
1.10      kristaps 1054:        }
1.71      schwarze 1055:        pnode_unlinksub(pn);
1.4       kristaps 1056: }
                   1057:
1.24      kristaps 1058: static void
                   1059: pnode_printgroup(struct parse *p, struct pnode *pn)
                   1060: {
                   1061:        struct pnode    *pp, *np;
                   1062:        struct pattr    *ap;
                   1063:        int              isop, sv;
                   1064:
                   1065:        isop = 1;
1.44      schwarze 1066:        TAILQ_FOREACH(ap, &pn->attrq, child)
1.64      schwarze 1067:                if (ap->key == ATTRKEY_CHOICE &&
                   1068:                    (ap->val == ATTRVAL_PLAIN || ap->val == ATTRVAL_REQ)) {
1.24      kristaps 1069:                        isop = 0;
                   1070:                        break;
                   1071:                }
                   1072:
1.44      schwarze 1073:        /*
1.24      kristaps 1074:         * Make sure we're on a macro line.
                   1075:         * This will prevent pnode_print() for putting us on a
                   1076:         * subsequent line.
                   1077:         */
1.69      schwarze 1078:        sv = p->linestate == LINE_NEW;
1.44      schwarze 1079:        if (isop)
1.69      schwarze 1080:                macro_open(p, "Op");
1.24      kristaps 1081:        else if (sv)
1.69      schwarze 1082:                macro_open(p, "No");
1.24      kristaps 1083:
                   1084:        /*
                   1085:         * Keep on printing text separated by the vertical bar as long
                   1086:         * as we're within the same origin node as the group.
                   1087:         * This is kind of a nightmare.
                   1088:         * Eh, DocBook...
                   1089:         * FIXME: if there's a "Fl", we don't cut off the leading "-"
                   1090:         * like we do in pnode_print().
                   1091:         */
                   1092:        TAILQ_FOREACH(pp, &pn->childq, child) {
                   1093:                pnode_print(p, pp);
                   1094:                np = TAILQ_NEXT(pp, child);
1.64      schwarze 1095:                while (np != NULL) {
1.24      kristaps 1096:                        if (pp->node != np->node)
                   1097:                                break;
1.72    ! schwarze 1098:                        macro_addarg(p, "|", ARG_SPACE);
        !          1099:                        macro_addnode(p, np, ARG_SPACE);
1.24      kristaps 1100:                        pp = np;
                   1101:                        np = TAILQ_NEXT(np, child);
                   1102:                }
                   1103:        }
1.69      schwarze 1104:        if (sv)
                   1105:                macro_close(p);
1.71      schwarze 1106:        pnode_unlinksub(pn);
1.24      kristaps 1107: }
                   1108:
1.7       kristaps 1109: static void
                   1110: pnode_printprologue(struct parse *p, struct pnode *pn)
                   1111: {
                   1112:        struct pnode    *pp;
                   1113:
1.64      schwarze 1114:        pp = p->root == NULL ? NULL :
1.9       kristaps 1115:                pnode_findfirst(p->root, NODE_REFMETA);
                   1116:
1.69      schwarze 1117:        macro_line(p, "Dd $Mdocdate" "$");
1.71      schwarze 1118:        if (pp != NULL)
1.7       kristaps 1119:                pnode_printrefmeta(p, pp);
1.71      schwarze 1120:        else {
1.69      schwarze 1121:                macro_open(p, "Dt");
                   1122:                macro_addarg(p,
1.72    ! schwarze 1123:                    pnode_getattr_raw(p->root, ATTRKEY_ID, "UNKNOWN"),
        !          1124:                    ARG_SPACE | ARG_SINGLE | ARG_UPPER);
        !          1125:                macro_addarg(p, "1", ARG_SPACE);
1.69      schwarze 1126:                macro_close(p);
                   1127:        }
                   1128:        macro_line(p, "Os");
1.43      kristaps 1129:
1.64      schwarze 1130:        if (p->flags & PARSE_EQN) {
1.69      schwarze 1131:                macro_line(p, "EQ");
1.70      schwarze 1132:                print_text(p, "delim $$");
1.69      schwarze 1133:                macro_line(p, "EN");
1.43      kristaps 1134:        }
1.7       kristaps 1135: }
                   1136:
1.42      kristaps 1137: /*
                   1138:  * We can have multiple <term> elements within a <varlistentry>, which
                   1139:  * we should comma-separate as list headers.
                   1140:  */
1.13      kristaps 1141: static void
                   1142: pnode_printvarlistentry(struct parse *p, struct pnode *pn)
                   1143: {
                   1144:        struct pnode    *pp;
1.42      kristaps 1145:        int              first = 1;
1.13      kristaps 1146:
1.69      schwarze 1147:        macro_open(p, "It");
                   1148:        TAILQ_FOREACH(pp, &pn->childq, child) {
                   1149:                if (pp->node != NODE_TERM)
                   1150:                        continue;
                   1151:                if ( ! first)
1.72    ! schwarze 1152:                        macro_addarg(p, ",", 0);
1.69      schwarze 1153:                pnode_print(p, pp);
                   1154:                first = 0;
                   1155:        }
                   1156:        macro_close(p);
1.13      kristaps 1157:        TAILQ_FOREACH(pp, &pn->childq, child)
1.69      schwarze 1158:                if (pp->node != NODE_TERM)
1.13      kristaps 1159:                        pnode_print(p, pp);
1.71      schwarze 1160:        pnode_unlinksub(pn);
                   1161: }
                   1162:
                   1163: static void
                   1164: pnode_printtitle(struct parse *p, struct pnode *pn)
                   1165: {
                   1166:        struct pnode    *pp, *pq;
                   1167:
                   1168:        TAILQ_FOREACH_SAFE(pp, &pn->childq, child, pq) {
                   1169:                if (pp->node == NODE_TITLE) {
                   1170:                        pnode_printpara(p, pp);
                   1171:                        pnode_print(p, pp);
                   1172:                        pnode_unlink(pp);
                   1173:                }
                   1174:        }
1.13      kristaps 1175: }
                   1176:
                   1177: static void
1.25      kristaps 1178: pnode_printrow(struct parse *p, struct pnode *pn)
                   1179: {
                   1180:        struct pnode    *pp;
                   1181:
1.69      schwarze 1182:        macro_line(p, "Bl -dash -compact");
1.25      kristaps 1183:        TAILQ_FOREACH(pp, &pn->childq, child) {
1.69      schwarze 1184:                macro_line(p, "It");
1.25      kristaps 1185:                pnode_print(p, pp);
                   1186:        }
1.69      schwarze 1187:        macro_line(p, "El");
1.71      schwarze 1188:        pnode_unlink(pn);
1.25      kristaps 1189: }
                   1190:
                   1191: static void
                   1192: pnode_printtable(struct parse *p, struct pnode *pn)
1.16      kristaps 1193: {
                   1194:        struct pnode    *pp;
                   1195:
1.71      schwarze 1196:        pnode_printtitle(p, pn);
1.69      schwarze 1197:        macro_line(p, "Bl -ohang");
1.64      schwarze 1198:        while ((pp = pnode_findfirst(pn, NODE_ROW)) != NULL) {
1.69      schwarze 1199:                macro_line(p, "It Table Row");
1.25      kristaps 1200:                pnode_printrow(p, pp);
                   1201:        }
1.69      schwarze 1202:        macro_line(p, "El");
1.71      schwarze 1203:        pnode_unlinksub(pn);
1.25      kristaps 1204: }
                   1205:
                   1206: static void
                   1207: pnode_printlist(struct parse *p, struct pnode *pn)
                   1208: {
                   1209:        struct pnode    *pp;
1.16      kristaps 1210:
1.71      schwarze 1211:        pnode_printtitle(p, pn);
1.69      schwarze 1212:        macro_argline(p, "Bl",
                   1213:            pn->node == NODE_ORDEREDLIST ? "-enum" : "-bullet");
1.16      kristaps 1214:        TAILQ_FOREACH(pp, &pn->childq, child) {
1.69      schwarze 1215:                macro_line(p, "It");
1.16      kristaps 1216:                pnode_print(p, pp);
                   1217:        }
1.69      schwarze 1218:        macro_line(p, "El");
1.71      schwarze 1219:        pnode_unlinksub(pn);
1.16      kristaps 1220: }
                   1221:
                   1222: static void
1.13      kristaps 1223: pnode_printvariablelist(struct parse *p, struct pnode *pn)
                   1224: {
                   1225:        struct pnode    *pp;
                   1226:
1.71      schwarze 1227:        pnode_printtitle(p, pn);
1.69      schwarze 1228:        macro_line(p, "Bl -tag -width Ds");
                   1229:        TAILQ_FOREACH(pp, &pn->childq, child) {
                   1230:                if (pp->node == NODE_VARLISTENTRY)
1.13      kristaps 1231:                        pnode_print(p, pp);
1.69      schwarze 1232:                else
1.72    ! schwarze 1233:                        macro_nodeline(p, "It", pp, 0);
1.69      schwarze 1234:        }
                   1235:        macro_line(p, "El");
1.71      schwarze 1236:        pnode_unlinksub(pn);
1.13      kristaps 1237: }
                   1238:
1.1       kristaps 1239: /*
                   1240:  * Print a parsed node (or ignore it--whatever).
                   1241:  * This is a recursive function.
1.23      kristaps 1242:  * FIXME: if we're in a literal context (<screen> or <programlisting> or
                   1243:  * whatever), don't print inline macros.
1.1       kristaps 1244:  */
                   1245: static void
                   1246: pnode_print(struct parse *p, struct pnode *pn)
                   1247: {
                   1248:        struct pnode    *pp;
1.59      schwarze 1249:        const char      *ccp;
1.1       kristaps 1250:        char            *cp;
1.69      schwarze 1251:        int              last;
                   1252:        enum linestate   sv;
1.1       kristaps 1253:
1.64      schwarze 1254:        if (pn == NULL)
1.1       kristaps 1255:                return;
                   1256:
1.69      schwarze 1257:        sv = p->linestate;
1.1       kristaps 1258:
                   1259:        switch (pn->node) {
1.62      schwarze 1260:        case NODE_APPLICATION:
1.69      schwarze 1261:                macro_open(p, "Nm");
1.27      kristaps 1262:                break;
1.62      schwarze 1263:        case NODE_ANCHOR:
1.30      kristaps 1264:                /* Don't print anything! */
                   1265:                return;
1.62      schwarze 1266:        case NODE_ARG:
1.10      kristaps 1267:                pnode_printarg(p, pn);
1.4       kristaps 1268:                break;
1.62      schwarze 1269:        case NODE_AUTHOR:
1.69      schwarze 1270:                macro_open(p, "An");
1.50      schwarze 1271:                break;
1.62      schwarze 1272:        case NODE_AUTHORGROUP:
1.69      schwarze 1273:                macro_line(p, "An -split");
1.50      schwarze 1274:                break;
1.62      schwarze 1275:        case NODE_BOOKINFO:
1.69      schwarze 1276:                macro_line(p, "Sh NAME");
1.50      schwarze 1277:                break;
1.62      schwarze 1278:        case NODE_CITEREFENTRY:
1.1       kristaps 1279:                pnode_printciterefentry(p, pn);
                   1280:                break;
1.68      schwarze 1281:        case NODE_CITETITLE:
1.69      schwarze 1282:                macro_open(p, "%T");
1.68      schwarze 1283:                break;
1.62      schwarze 1284:        case NODE_CODE:
1.69      schwarze 1285:                macro_open(p, "Li");
1.4       kristaps 1286:                break;
1.62      schwarze 1287:        case NODE_COMMAND:
1.69      schwarze 1288:                macro_open(p, "Nm");
1.13      kristaps 1289:                break;
1.62      schwarze 1290:        case NODE_CONSTANT:
1.69      schwarze 1291:                macro_open(p, "Dv");
1.33      kristaps 1292:                break;
1.62      schwarze 1293:        case NODE_EDITOR:
1.70      schwarze 1294:                print_text(p, "editor:");
1.69      schwarze 1295:                macro_open(p, "An");
1.50      schwarze 1296:                break;
1.65      schwarze 1297:        case NODE_EMAIL:
1.69      schwarze 1298:                macro_open(p, "Aq Mt");
1.65      schwarze 1299:                break;
1.62      schwarze 1300:        case NODE_EMPHASIS:
                   1301:        case NODE_FIRSTTERM:
1.69      schwarze 1302:                macro_open(p, "Em");
1.1       kristaps 1303:                break;
1.62      schwarze 1304:        case NODE_ENVAR:
1.69      schwarze 1305:                macro_open(p, "Ev");
1.21      kristaps 1306:                break;
1.62      schwarze 1307:        case NODE_FILENAME:
1.69      schwarze 1308:                macro_open(p, "Pa");
1.17      kristaps 1309:                break;
1.62      schwarze 1310:        case NODE_FUNCTION:
1.69      schwarze 1311:                macro_open(p, "Fn");
1.3       kristaps 1312:                break;
1.62      schwarze 1313:        case NODE_FUNCPROTOTYPE:
1.3       kristaps 1314:                pnode_printfuncprototype(p, pn);
                   1315:                break;
1.62      schwarze 1316:        case NODE_FUNCSYNOPSISINFO:
1.69      schwarze 1317:                macro_open(p, "Fd");
1.16      kristaps 1318:                break;
1.62      schwarze 1319:        case NODE_INDEXTERM:
1.55      schwarze 1320:                return;
1.62      schwarze 1321:        case NODE_INFORMALEQUATION:
1.69      schwarze 1322:                macro_line(p, "EQ");
1.43      kristaps 1323:                break;
1.62      schwarze 1324:        case NODE_INLINEEQUATION:
1.69      schwarze 1325:                if (p->linestate == LINE_NEW)
                   1326:                        p->linestate = LINE_TEXT;
                   1327:                putchar('$');
1.43      kristaps 1328:                break;
1.62      schwarze 1329:        case NODE_ITEMIZEDLIST:
1.25      kristaps 1330:                pnode_printlist(p, pn);
1.24      kristaps 1331:                break;
1.62      schwarze 1332:        case NODE_GROUP:
1.24      kristaps 1333:                pnode_printgroup(p, pn);
1.10      kristaps 1334:                break;
1.67      schwarze 1335:        case NODE_KEYSYM:
1.69      schwarze 1336:                macro_open(p, "Sy");
1.67      schwarze 1337:                break;
1.62      schwarze 1338:        case NODE_LEGALNOTICE:
1.69      schwarze 1339:                macro_line(p, "Sh LEGAL NOTICE");
1.50      schwarze 1340:                break;
1.62      schwarze 1341:        case NODE_LINK:
1.59      schwarze 1342:                ccp = pnode_getattr_raw(pn, ATTRKEY_LINKEND, NULL);
1.64      schwarze 1343:                if (ccp == NULL)
1.58      schwarze 1344:                        break;
1.69      schwarze 1345:                macro_argline(p, "Sx", ccp);
1.58      schwarze 1346:                return;
1.62      schwarze 1347:        case NODE_LITERAL:
1.69      schwarze 1348:                macro_open(p, "Li");
1.19      kristaps 1349:                break;
1.62      schwarze 1350:        case NODE_LITERALLAYOUT:
1.69      schwarze 1351:                macro_argline(p, "Bd", pnode_getattr(pn, ATTRKEY_CLASS) ==
1.66      schwarze 1352:                    ATTRVAL_MONOSPACED ? "-literal" : "-unfilled");
1.60      schwarze 1353:                break;
1.62      schwarze 1354:        case NODE_MML_MFENCED:
1.41      kristaps 1355:                pnode_printmathfenced(p, pn);
1.40      kristaps 1356:                break;
1.62      schwarze 1357:        case NODE_MML_MROW:
                   1358:        case NODE_MML_MI:
                   1359:        case NODE_MML_MN:
                   1360:        case NODE_MML_MO:
1.43      kristaps 1361:                if (TAILQ_EMPTY(&pn->childq))
                   1362:                        break;
                   1363:                fputs(" { ", stdout);
1.40      kristaps 1364:                break;
1.62      schwarze 1365:        case NODE_MML_MFRAC:
                   1366:        case NODE_MML_MSUB:
                   1367:        case NODE_MML_MSUP:
1.40      kristaps 1368:                pnode_printmath(p, pn);
                   1369:                break;
1.62      schwarze 1370:        case NODE_OPTION:
1.69      schwarze 1371:                macro_open(p, "Fl");
1.1       kristaps 1372:                break;
1.62      schwarze 1373:        case NODE_ORDEREDLIST:
1.25      kristaps 1374:                pnode_printlist(p, pn);
                   1375:                break;
1.62      schwarze 1376:        case NODE_PARA:
1.54      schwarze 1377:                pnode_printpara(p, pn);
1.3       kristaps 1378:                break;
1.62      schwarze 1379:        case NODE_PARAMETER:
1.72    ! schwarze 1380:                macro_nodeline(p, "Fa", pn, ARG_SINGLE);
1.4       kristaps 1381:                pnode_unlinksub(pn);
1.1       kristaps 1382:                break;
1.62      schwarze 1383:        case NODE_QUOTE:
1.69      schwarze 1384:                macro_open(p, "Qo");
1.28      kristaps 1385:                break;
1.62      schwarze 1386:        case NODE_PROGRAMLISTING:
                   1387:        case NODE_SCREEN:
1.69      schwarze 1388:                macro_line(p, "Bd -literal");
1.15      kristaps 1389:                break;
1.62      schwarze 1390:        case NODE_REFENTRYINFO:
1.15      kristaps 1391:                /* Suppress. */
                   1392:                pnode_unlinksub(pn);
1.1       kristaps 1393:                break;
1.62      schwarze 1394:        case NODE_REFMETA:
1.7       kristaps 1395:                abort();
1.1       kristaps 1396:                break;
1.62      schwarze 1397:        case NODE_REFNAME:
1.10      kristaps 1398:                /* Suppress non-text children... */
1.70      schwarze 1399:                macro_open(p, "Nm");
1.72    ! schwarze 1400:                macro_addnode(p, pn, ARG_SPACE | ARG_SINGLE);
1.4       kristaps 1401:                pnode_unlinksub(pn);
1.10      kristaps 1402:                break;
1.62      schwarze 1403:        case NODE_REFNAMEDIV:
1.69      schwarze 1404:                macro_line(p, "Sh NAME");
1.1       kristaps 1405:                break;
1.62      schwarze 1406:        case NODE_REFPURPOSE:
1.69      schwarze 1407:                macro_open(p, "Nd");
1.10      kristaps 1408:                break;
1.62      schwarze 1409:        case NODE_REFSYNOPSISDIV:
1.6       kristaps 1410:                pnode_printrefsynopsisdiv(p, pn);
1.1       kristaps 1411:                break;
1.62      schwarze 1412:        case NODE_PREFACE:
                   1413:        case NODE_SECTION:
                   1414:        case NODE_NOTE:
                   1415:        case NODE_TIP:
                   1416:        case NODE_CAUTION:
                   1417:        case NODE_WARNING:
1.1       kristaps 1418:                pnode_printrefsect(p, pn);
                   1419:                break;
1.62      schwarze 1420:        case NODE_REPLACEABLE:
1.69      schwarze 1421:                macro_open(p, "Ar");
1.13      kristaps 1422:                break;
1.62      schwarze 1423:        case NODE_SBR:
1.69      schwarze 1424:                macro_line(p, "br");
1.19      kristaps 1425:                break;
1.62      schwarze 1426:        case NODE_SGMLTAG:
1.69      schwarze 1427:                macro_open(p, "Li");
1.30      kristaps 1428:                break;
1.62      schwarze 1429:        case NODE_STRUCTNAME:
1.69      schwarze 1430:                macro_open(p, "Vt");
1.25      kristaps 1431:                break;
1.62      schwarze 1432:        case NODE_TABLE:
                   1433:        case NODE_INFORMALTABLE:
1.25      kristaps 1434:                pnode_printtable(p, pn);
1.10      kristaps 1435:                break;
1.62      schwarze 1436:        case NODE_TEXT:
1.72    ! schwarze 1437:                if (pn->bsz == 0) {
1.37      kristaps 1438:                        assert(pn->real != pn->b);
                   1439:                        break;
                   1440:                }
1.69      schwarze 1441:                if (p->linestate == LINE_NEW)
                   1442:                        p->linestate = LINE_TEXT;
                   1443:                else
                   1444:                        putchar(' ');
1.37      kristaps 1445:
1.1       kristaps 1446:                /*
                   1447:                 * Output all characters, squeezing out whitespace
1.44      schwarze 1448:                 * between newlines.
1.1       kristaps 1449:                 * XXX: all whitespace, including tabs (?).
                   1450:                 * Remember to escape control characters and escapes.
                   1451:                 */
1.72    ! schwarze 1452:                cp = pn->b;
1.37      kristaps 1453:
1.20      kristaps 1454:                /*
                   1455:                 * There's often a superfluous "-" in its <option> tags
                   1456:                 * before the actual flags themselves.
                   1457:                 * "Fl" does this for us, so remove it.
                   1458:                 */
1.64      schwarze 1459:                if (pn->parent != NULL &&
                   1460:                    pn->parent->node == NODE_OPTION &&
                   1461:                    *cp == '-')
1.20      kristaps 1462:                        cp++;
1.64      schwarze 1463:                for (last = '\n'; *cp != '\0'; ) {
                   1464:                        if (last == '\n') {
1.1       kristaps 1465:                                /* Consume all whitespace. */
1.46      schwarze 1466:                                if (isspace((unsigned char)*cp)) {
                   1467:                                        while (isspace((unsigned char)*cp))
1.1       kristaps 1468:                                                cp++;
                   1469:                                        continue;
1.64      schwarze 1470:                                } else if (*cp == '\'' || *cp == '.')
1.1       kristaps 1471:                                        fputs("\\&", stdout);
                   1472:                        }
                   1473:                        putchar(last = *cp++);
                   1474:                        /* If we're a character escape, escape us. */
1.64      schwarze 1475:                        if (last == '\\')
1.1       kristaps 1476:                                putchar('e');
                   1477:                }
                   1478:                break;
1.62      schwarze 1479:        case NODE_TITLE:
1.69      schwarze 1480:                if (pn->parent->node == NODE_BOOKINFO)
                   1481:                        macro_open(p, "Nd");
1.50      schwarze 1482:                break;
1.62      schwarze 1483:        case NODE_TYPE:
1.69      schwarze 1484:                macro_open(p, "Vt");
1.39      kristaps 1485:                break;
1.62      schwarze 1486:        case NODE_USERINPUT:
1.69      schwarze 1487:                macro_open(p, "Li");
1.26      kristaps 1488:                break;
1.62      schwarze 1489:        case NODE_VARIABLELIST:
1.13      kristaps 1490:                pnode_printvariablelist(p, pn);
                   1491:                break;
1.62      schwarze 1492:        case NODE_VARLISTENTRY:
1.13      kristaps 1493:                pnode_printvarlistentry(p, pn);
                   1494:                break;
1.62      schwarze 1495:        case NODE_VARNAME:
1.69      schwarze 1496:                macro_open(p, "Va");
1.23      kristaps 1497:                break;
1.1       kristaps 1498:        default:
                   1499:                break;
                   1500:        }
                   1501:
                   1502:        TAILQ_FOREACH(pp, &pn->childq, child)
                   1503:                pnode_print(p, pp);
                   1504:
                   1505:        switch (pn->node) {
1.62      schwarze 1506:        case NODE_INFORMALEQUATION:
1.69      schwarze 1507:                macro_line(p, "EN");
1.40      kristaps 1508:                break;
1.62      schwarze 1509:        case NODE_INLINEEQUATION:
1.43      kristaps 1510:                fputs("$ ", stdout);
1.69      schwarze 1511:                p->linestate = sv;
1.43      kristaps 1512:                break;
1.62      schwarze 1513:        case NODE_MML_MROW:
                   1514:        case NODE_MML_MI:
                   1515:        case NODE_MML_MN:
                   1516:        case NODE_MML_MO:
1.43      kristaps 1517:                if (TAILQ_EMPTY(&pn->childq))
                   1518:                        break;
                   1519:                fputs(" } ", stdout);
1.40      kristaps 1520:                break;
1.62      schwarze 1521:        case NODE_APPLICATION:
                   1522:        case NODE_ARG:
                   1523:        case NODE_AUTHOR:
                   1524:        case NODE_CITEREFENTRY:
1.68      schwarze 1525:        case NODE_CITETITLE:
1.62      schwarze 1526:        case NODE_CODE:
                   1527:        case NODE_COMMAND:
                   1528:        case NODE_CONSTANT:
                   1529:        case NODE_EDITOR:
1.65      schwarze 1530:        case NODE_EMAIL:
1.62      schwarze 1531:        case NODE_EMPHASIS:
                   1532:        case NODE_ENVAR:
                   1533:        case NODE_FILENAME:
                   1534:        case NODE_FIRSTTERM:
                   1535:        case NODE_FUNCTION:
                   1536:        case NODE_FUNCSYNOPSISINFO:
1.67      schwarze 1537:        case NODE_KEYSYM:
1.62      schwarze 1538:        case NODE_LITERAL:
                   1539:        case NODE_OPTION:
                   1540:        case NODE_PARAMETER:
                   1541:        case NODE_REPLACEABLE:
                   1542:        case NODE_REFPURPOSE:
                   1543:        case NODE_SGMLTAG:
                   1544:        case NODE_STRUCTNAME:
                   1545:        case NODE_TYPE:
                   1546:        case NODE_USERINPUT:
                   1547:        case NODE_VARNAME:
1.69      schwarze 1548:                if (sv != LINE_MACRO && p->linestate == LINE_MACRO)
                   1549:                        macro_closepunct(p, pn);
1.28      kristaps 1550:                break;
1.62      schwarze 1551:        case NODE_QUOTE:
1.69      schwarze 1552:                if (sv == LINE_NEW)
                   1553:                        macro_close(p);
                   1554:                sv = p->linestate;
                   1555:                macro_open(p, "Qc");
                   1556:                if (sv == LINE_NEW)
                   1557:                        macro_close(p);
1.10      kristaps 1558:                break;
1.62      schwarze 1559:        case NODE_REFNAME:
1.12      kristaps 1560:                /*
                   1561:                 * If we're in the NAME macro and we have multiple
                   1562:                 * <refname> macros in sequence, then print out a
                   1563:                 * trailing comma before the newline.
                   1564:                 */
1.64      schwarze 1565:                if (pn->parent != NULL &&
                   1566:                    pn->parent->node == NODE_REFNAMEDIV &&
                   1567:                    TAILQ_NEXT(pn, child) != NULL &&
                   1568:                    TAILQ_NEXT(pn, child)->node == NODE_REFNAME)
1.72    ! schwarze 1569:                        macro_addarg(p, ",", ARG_SPACE);
1.69      schwarze 1570:                if (sv == LINE_NEW)
                   1571:                        macro_close(p);
1.52      schwarze 1572:                break;
1.62      schwarze 1573:        case NODE_PREFACE:
                   1574:        case NODE_SECTION:
                   1575:        case NODE_NOTE:
                   1576:        case NODE_TIP:
                   1577:        case NODE_CAUTION:
                   1578:        case NODE_WARNING:
1.52      schwarze 1579:                p->level--;
1.12      kristaps 1580:                break;
1.62      schwarze 1581:        case NODE_LITERALLAYOUT:
                   1582:        case NODE_PROGRAMLISTING:
                   1583:        case NODE_SCREEN:
1.69      schwarze 1584:                macro_line(p, "Ed");
1.50      schwarze 1585:                break;
1.62      schwarze 1586:        case NODE_TITLE:
1.69      schwarze 1587:                if (pn->parent->node == NODE_BOOKINFO)
                   1588:                        macro_line(p, "Sh AUTHORS");
1.1       kristaps 1589:                break;
                   1590:        default:
                   1591:                break;
                   1592:        }
                   1593: }
                   1594:
                   1595: /*
                   1596:  * Loop around the read buffer until we've drained it of all data.
                   1597:  * Invoke the parser context with each buffer fill.
                   1598:  */
                   1599: static int
1.44      schwarze 1600: readfile(XML_Parser xp, int fd,
1.1       kristaps 1601:        char *b, size_t bsz, const char *fn)
                   1602: {
                   1603:        struct parse     p;
                   1604:        int              rc;
                   1605:        ssize_t          ssz;
                   1606:
                   1607:        memset(&p, 0, sizeof(struct parse));
                   1608:
1.12      kristaps 1609:        p.fname = fn;
                   1610:        p.xml = xp;
1.1       kristaps 1611:
                   1612:        XML_SetCharacterDataHandler(xp, xml_char);
                   1613:        XML_SetElementHandler(xp, xml_elem_start, xml_elem_end);
                   1614:        XML_SetUserData(xp, &p);
                   1615:
                   1616:        while ((ssz = read(fd, b, bsz)) >= 0) {
1.64      schwarze 1617:                if ((rc = XML_Parse(xp, b, ssz, 0 == ssz)) == 0)
1.30      kristaps 1618:                        fprintf(stderr, "%s:%zu:%zu: %s\n", fn,
                   1619:                                XML_GetCurrentLineNumber(xp),
1.44      schwarze 1620:                                XML_GetCurrentColumnNumber(xp),
1.1       kristaps 1621:                                XML_ErrorString
                   1622:                                (XML_GetErrorCode(xp)));
                   1623:                else if ( ! p.stop && ssz > 0)
                   1624:                        continue;
1.44      schwarze 1625:                /*
1.1       kristaps 1626:                 * Exit when we've read all or errors have occured
                   1627:                 * during the parse sequence.
                   1628:                 */
1.69      schwarze 1629:                p.linestate = LINE_NEW;
1.7       kristaps 1630:                pnode_printprologue(&p, p.root);
1.1       kristaps 1631:                pnode_print(&p, p.root);
1.69      schwarze 1632:                if (p.linestate != LINE_NEW)
                   1633:                        putchar('\n');
1.1       kristaps 1634:                pnode_free(p.root);
1.63      schwarze 1635:                return rc != 0 && p.stop == 0;
1.1       kristaps 1636:        }
                   1637:
                   1638:        /* Read error has occured. */
                   1639:        perror(fn);
                   1640:        pnode_free(p.root);
1.63      schwarze 1641:        return 0;
1.1       kristaps 1642: }
                   1643:
                   1644: int
                   1645: main(int argc, char *argv[])
                   1646: {
                   1647:        XML_Parser       xp;
                   1648:        const char      *fname;
                   1649:        char            *buf;
1.38      kristaps 1650:        int              fd, rc, ch;
                   1651:        const char      *progname;
                   1652:
                   1653:        progname = strrchr(argv[0], '/');
                   1654:        if (progname == NULL)
                   1655:                progname = argv[0];
                   1656:        else
                   1657:                ++progname;
1.1       kristaps 1658:
                   1659:        fname = "-";
                   1660:        xp = NULL;
                   1661:        buf = NULL;
1.63      schwarze 1662:        rc = 1;
1.1       kristaps 1663:
1.64      schwarze 1664:        while ((ch = getopt(argc, argv, "W")) != -1)
1.38      kristaps 1665:                switch (ch) {
1.62      schwarze 1666:                case 'W':
1.38      kristaps 1667:                        warn = 1;
                   1668:                        break;
                   1669:                default:
                   1670:                        goto usage;
                   1671:                }
1.1       kristaps 1672:
                   1673:        argc -= optind;
                   1674:        argv += optind;
                   1675:
1.45      schwarze 1676:        if (argc > 1) {
                   1677:                fprintf(stderr, "%s: Too many arguments\n", argv[1]);
                   1678:                goto usage;
                   1679:        } else if (argc > 0)
1.1       kristaps 1680:                fname = argv[0];
                   1681:
                   1682:        /* Read from stdin or a file. */
1.64      schwarze 1683:        fd = strcmp(fname, "-") == 0 ?
1.1       kristaps 1684:                STDIN_FILENO : open(fname, O_RDONLY, 0);
                   1685:
                   1686:        /*
                   1687:         * Open file for reading.
                   1688:         * Allocate a read buffer.
                   1689:         * Create the parser context.
                   1690:         * Dive directly into the parse.
                   1691:         */
1.64      schwarze 1692:        if (fd == -1)
1.1       kristaps 1693:                perror(fname);
1.64      schwarze 1694:        else if ((buf = malloc(4096)) == NULL)
1.1       kristaps 1695:                perror(NULL);
1.64      schwarze 1696:        else if ((xp = XML_ParserCreate(NULL)) == NULL)
1.1       kristaps 1697:                perror(NULL);
1.63      schwarze 1698:        else if (readfile(xp, fd, buf, 4096, fname))
                   1699:                rc = 0;
1.1       kristaps 1700:
                   1701:        XML_ParserFree(xp);
                   1702:        free(buf);
1.64      schwarze 1703:        if (fd != STDIN_FILENO)
1.1       kristaps 1704:                close(fd);
1.63      schwarze 1705:        return rc;
1.38      kristaps 1706:
                   1707: usage:
1.45      schwarze 1708:        fprintf(stderr, "usage: %s [-W] [input_filename]\n", progname);
1.63      schwarze 1709:        return 1;
1.1       kristaps 1710: }

CVSweb