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

Annotation of docbook2mdoc/docbook2mdoc.c, Revision 1.69

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

CVSweb