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

Annotation of docbook2mdoc/docbook2mdoc.c, Revision 1.70

1.70    ! schwarze    1: /* $Id: docbook2mdoc.c,v 1.69 2019/03/24 16:45:46 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
1.70    ! schwarze  714: print_text(struct parse *p, const char *word)
        !           715: {
        !           716:        switch (p->linestate) {
        !           717:        case LINE_NEW:
        !           718:                break;
        !           719:        case LINE_TEXT:
        !           720:                putchar(' ');
        !           721:                break;
        !           722:        case LINE_MACRO:
        !           723:                macro_close(p);
        !           724:                break;
        !           725:        }
        !           726:        fputs(word, stdout);
        !           727:        p->linestate = LINE_TEXT;
        !           728: }
        !           729:
        !           730: static void
1.54      schwarze  731: pnode_printpara(struct parse *p, struct pnode *pn)
                    732: {
                    733:        struct pnode    *pp;
                    734:
1.61      schwarze  735:        if ((pp = TAILQ_PREV(pn, pnodeq, child)) == NULL &&
                    736:            (pp = pn->parent) == NULL)
1.54      schwarze  737:                return;
                    738:
1.61      schwarze  739:        switch (pp->node) {
                    740:        case NODE_ENTRY:
                    741:        case NODE_LISTITEM:
                    742:                return;
                    743:        case NODE_PREFACE:
                    744:        case NODE_SECTION:
                    745:                if (p->level < 3)
                    746:                        return;
                    747:                break;
                    748:        default:
                    749:                break;
                    750:        }
1.69      schwarze  751:        macro_line(p, "Pp");
1.54      schwarze  752: }
                    753:
1.37      kristaps  754: /*
1.10      kristaps  755:  * If the SYNOPSIS macro has a superfluous title, kill it.
1.8       kristaps  756:  */
1.1       kristaps  757: static void
1.6       kristaps  758: pnode_printrefsynopsisdiv(struct parse *p, struct pnode *pn)
                    759: {
                    760:        struct pnode    *pp;
                    761:
1.44      schwarze  762:        TAILQ_FOREACH(pp, &pn->childq, child)
1.64      schwarze  763:                if (pp->node == NODE_TITLE) {
1.6       kristaps  764:                        pnode_unlink(pp);
1.10      kristaps  765:                        return;
1.6       kristaps  766:                }
                    767: }
                    768:
1.8       kristaps  769: /*
                    770:  * Start a hopefully-named `Sh' section.
                    771:  */
1.6       kristaps  772: static void
1.1       kristaps  773: pnode_printrefsect(struct parse *p, struct pnode *pn)
                    774: {
                    775:        struct pnode    *pp;
1.52      schwarze  776:        const char      *title;
                    777:        int              flags, level;
                    778:
1.64      schwarze  779:        if (pn->parent == NULL)
1.56      schwarze  780:                return;
                    781:
1.52      schwarze  782:        level = ++p->level;
1.64      schwarze  783:        flags = level == 1 ? MACROLINE_UPPER : 0;
                    784:        if (level < 3) {
1.52      schwarze  785:                switch (pn->node) {
1.62      schwarze  786:                case NODE_CAUTION:
                    787:                case NODE_NOTE:
                    788:                case NODE_TIP:
                    789:                case NODE_WARNING:
1.52      schwarze  790:                        level = 3;
                    791:                        break;
                    792:                default:
                    793:                        break;
                    794:                }
                    795:        }
1.1       kristaps  796:
                    797:        TAILQ_FOREACH(pp, &pn->childq, child)
1.64      schwarze  798:                if (pp->node == NODE_TITLE)
1.1       kristaps  799:                        break;
                    800:
1.64      schwarze  801:        if (pp == NULL) {
1.52      schwarze  802:                switch (pn->node) {
1.62      schwarze  803:                case NODE_PREFACE:
1.52      schwarze  804:                        title = "Preface";
                    805:                        break;
1.62      schwarze  806:                case NODE_CAUTION:
1.52      schwarze  807:                        title = "Caution";
                    808:                        break;
1.62      schwarze  809:                case NODE_NOTE:
1.52      schwarze  810:                        title = "Note";
                    811:                        break;
1.62      schwarze  812:                case NODE_TIP:
1.52      schwarze  813:                        title = "Tip";
                    814:                        break;
1.62      schwarze  815:                case NODE_WARNING:
1.52      schwarze  816:                        title = "Warning";
                    817:                        break;
                    818:                default:
                    819:                        title = "Unknown";
                    820:                        break;
                    821:                }
                    822:        }
                    823:
                    824:        switch (level) {
1.62      schwarze  825:        case 1:
1.69      schwarze  826:                macro_open(p, "Sh");
1.29      kristaps  827:                break;
1.62      schwarze  828:        case 2:
1.69      schwarze  829:                macro_open(p, "Ss");
1.29      kristaps  830:                break;
1.52      schwarze  831:        default:
1.54      schwarze  832:                pnode_printpara(p, pn);
1.69      schwarze  833:                macro_open(p, "Sy");
1.29      kristaps  834:                break;
                    835:        }
1.20      kristaps  836:
1.64      schwarze  837:        if (pp != NULL) {
1.69      schwarze  838:                macro_addnode(p, pp, flags);
1.5       kristaps  839:                pnode_unlink(pp);
1.52      schwarze  840:        } else
1.69      schwarze  841:                macro_addarg(p, title, 0);
                    842:        macro_close(p);
1.1       kristaps  843: }
                    844:
1.8       kristaps  845: /*
                    846:  * Start a reference, extracting the title and volume.
                    847:  */
1.1       kristaps  848: static void
                    849: pnode_printciterefentry(struct parse *p, struct pnode *pn)
                    850: {
                    851:        struct pnode    *pp, *title, *manvol;
                    852:
                    853:        title = manvol = NULL;
1.69      schwarze  854:        TAILQ_FOREACH(pp, &pn->childq, child) {
1.64      schwarze  855:                if (pp->node == NODE_MANVOLNUM)
1.1       kristaps  856:                        manvol = pp;
1.64      schwarze  857:                else if (pp->node == NODE_REFENTRYTITLE)
1.1       kristaps  858:                        title = pp;
1.69      schwarze  859:        }
                    860:        macro_open(p, "Xr");
                    861:        if (title == NULL)
                    862:                macro_addarg(p, "unknown", 0);
                    863:        else
                    864:                macro_addnode(p, title, 0);
                    865:        if (manvol == NULL)
                    866:                macro_addarg(p, "1", 0);
1.64      schwarze  867:        else
1.69      schwarze  868:                macro_addnode(p, manvol, 0);
                    869:        macro_close(p);
1.1       kristaps  870: }
                    871:
                    872: static void
                    873: pnode_printrefmeta(struct parse *p, struct pnode *pn)
                    874: {
                    875:        struct pnode    *pp, *title, *manvol;
                    876:
                    877:        title = manvol = NULL;
1.69      schwarze  878:        TAILQ_FOREACH(pp, &pn->childq, child) {
1.64      schwarze  879:                if (pp->node == NODE_MANVOLNUM)
1.1       kristaps  880:                        manvol = pp;
1.64      schwarze  881:                else if (pp->node == NODE_REFENTRYTITLE)
1.1       kristaps  882:                        title = pp;
1.69      schwarze  883:        }
                    884:        macro_open(p, "Dt");
                    885:        if (title == NULL)
                    886:                macro_addarg(p, "UNKNOWN", 0);
                    887:        else
                    888:                macro_addnode(p, title, MACROLINE_UPPER);
                    889:        if (manvol == NULL)
                    890:                macro_addarg(p, "1", 0);
1.13      kristaps  891:        else
1.69      schwarze  892:                macro_addnode(p, manvol, 0);
                    893:        macro_close(p);
1.1       kristaps  894: }
                    895:
1.3       kristaps  896: static void
                    897: pnode_printfuncdef(struct parse *p, struct pnode *pn)
                    898: {
                    899:        struct pnode    *pp, *ftype, *func;
                    900:
                    901:        ftype = func = NULL;
1.69      schwarze  902:        TAILQ_FOREACH(pp, &pn->childq, child) {
1.64      schwarze  903:                if (pp->node == NODE_TEXT)
1.3       kristaps  904:                        ftype = pp;
1.64      schwarze  905:                else if (pp->node == NODE_FUNCTION)
1.3       kristaps  906:                        func = pp;
1.13      kristaps  907:        }
1.69      schwarze  908:        if (ftype != NULL)
                    909:                macro_nodeline(p, "Ft", ftype);
                    910:        macro_open(p, "Fo");
                    911:        if (func == NULL)
                    912:                macro_addarg(p, "UNKNOWN", 0);
                    913:        else
                    914:                macro_addnode(p, func, 0);
                    915:        macro_close(p);
1.3       kristaps  916: }
                    917:
                    918: static void
                    919: pnode_printparamdef(struct parse *p, struct pnode *pn)
                    920: {
                    921:        struct pnode    *pp, *ptype, *param;
1.69      schwarze  922:        int              flags;
1.3       kristaps  923:
                    924:        ptype = param = NULL;
1.69      schwarze  925:        TAILQ_FOREACH(pp, &pn->childq, child) {
1.64      schwarze  926:                if (pp->node == NODE_TEXT)
1.3       kristaps  927:                        ptype = pp;
1.64      schwarze  928:                else if (pp->node == NODE_PARAMETER)
1.3       kristaps  929:                        param = pp;
1.69      schwarze  930:        }
                    931:        macro_open(p, "Fa \"");
                    932:        flags = MACROLINE_NOWS;
1.64      schwarze  933:        if (ptype != NULL) {
1.69      schwarze  934:                macro_addnode(p, ptype, flags);
                    935:                flags = 0;
1.3       kristaps  936:        }
1.64      schwarze  937:        if (param != NULL)
1.69      schwarze  938:                macro_addnode(p, param, flags);
                    939:        flags = MACROLINE_NOWS;
                    940:        macro_addarg(p, "\"", flags);
                    941:        macro_close(p);
1.3       kristaps  942: }
                    943:
1.40      kristaps  944: /*
1.41      kristaps  945:  * The <mml:mfenced> node is a little peculiar.
                    946:  * First, it can have arbitrary open and closing tokens, which default
                    947:  * to parentheses.
                    948:  * Second, >1 arguments are separated by commas.
                    949:  */
                    950: static void
                    951: pnode_printmathfenced(struct parse *p, struct pnode *pn)
                    952: {
                    953:        struct pnode    *pp;
                    954:
1.59      schwarze  955:        printf("left %s ", pnode_getattr_raw(pn, ATTRKEY_OPEN, "("));
1.41      kristaps  956:
                    957:        pp = TAILQ_FIRST(&pn->childq);
                    958:        pnode_print(p, pp);
                    959:
1.64      schwarze  960:        while ((pp = TAILQ_NEXT(pp, child)) != NULL) {
1.41      kristaps  961:                putchar(',');
                    962:                pnode_print(p, pp);
                    963:        }
                    964:
1.59      schwarze  965:        printf("right %s ", pnode_getattr_raw(pn, ATTRKEY_CLOSE, ")"));
1.41      kristaps  966: }
                    967:
                    968: /*
1.40      kristaps  969:  * These math nodes require special handling because they have infix
                    970:  * syntax, instead of the usual prefix or prefix.
                    971:  * So we need to break up the first and second child node with a
                    972:  * particular eqn(7) word.
                    973:  */
                    974: static void
                    975: pnode_printmath(struct parse *p, struct pnode *pn)
                    976: {
                    977:        struct pnode    *pp;
                    978:
                    979:        pp = TAILQ_FIRST(&pn->childq);
                    980:        pnode_print(p, pp);
                    981:
                    982:        switch (pn->node) {
1.62      schwarze  983:        case NODE_MML_MSUP:
1.42      kristaps  984:                fputs(" sup ", stdout);
1.40      kristaps  985:                break;
1.62      schwarze  986:        case NODE_MML_MFRAC:
1.42      kristaps  987:                fputs(" over ", stdout);
1.40      kristaps  988:                break;
1.62      schwarze  989:        case NODE_MML_MSUB:
1.42      kristaps  990:                fputs(" sub ", stdout);
1.40      kristaps  991:                break;
                    992:        default:
                    993:                break;
                    994:        }
                    995:
                    996:        pp = TAILQ_NEXT(pp, child);
                    997:        pnode_print(p, pp);
                    998: }
                    999:
1.3       kristaps 1000: static void
                   1001: pnode_printfuncprototype(struct parse *p, struct pnode *pn)
                   1002: {
                   1003:        struct pnode    *pp, *fdef;
                   1004:
                   1005:        TAILQ_FOREACH(fdef, &pn->childq, child)
1.64      schwarze 1006:                if (fdef->node == NODE_FUNCDEF)
1.3       kristaps 1007:                        break;
                   1008:
1.64      schwarze 1009:        if (fdef != NULL)
1.3       kristaps 1010:                pnode_printfuncdef(p, fdef);
1.4       kristaps 1011:        else
1.69      schwarze 1012:                macro_line(p, "Fo UNKNOWN");
1.3       kristaps 1013:
1.44      schwarze 1014:        TAILQ_FOREACH(pp, &pn->childq, child)
1.64      schwarze 1015:                if (pp->node == NODE_PARAMDEF)
1.3       kristaps 1016:                        pnode_printparamdef(p, pp);
                   1017:
1.69      schwarze 1018:        macro_line(p, "Fc");
1.3       kristaps 1019: }
                   1020:
1.44      schwarze 1021: /*
1.10      kristaps 1022:  * The <arg> element is more complicated than it should be because text
                   1023:  * nodes are treated like ".Ar foo", but non-text nodes need to be
                   1024:  * re-sent into the printer (i.e., without the preceding ".Ar").
1.12      kristaps 1025:  * This also handles the case of "repetition" (or in other words, the
                   1026:  * ellipsis following an argument) and optionality.
1.10      kristaps 1027:  */
1.4       kristaps 1028: static void
1.10      kristaps 1029: pnode_printarg(struct parse *p, struct pnode *pn)
1.4       kristaps 1030: {
                   1031:        struct pnode    *pp;
1.12      kristaps 1032:        struct pattr    *ap;
                   1033:        int              isop, isrep;
                   1034:
                   1035:        isop = 1;
                   1036:        isrep = 0;
1.69      schwarze 1037:        TAILQ_FOREACH(ap, &pn->attrq, child) {
1.64      schwarze 1038:                if (ap->key == ATTRKEY_CHOICE &&
                   1039:                    (ap->val == ATTRVAL_PLAIN || ap->val == ATTRVAL_REQ))
1.12      kristaps 1040:                        isop = 0;
1.64      schwarze 1041:                else if (ap->key == ATTRKEY_REP && ap->val == ATTRVAL_REPEAT)
1.12      kristaps 1042:                        isrep = 1;
                   1043:        }
1.69      schwarze 1044:        if (isop)
                   1045:                macro_open(p, "Op");
1.4       kristaps 1046:
1.10      kristaps 1047:        TAILQ_FOREACH(pp, &pn->childq, child) {
1.69      schwarze 1048:                if (pp->node == NODE_TEXT)
                   1049:                        macro_open(p, "Ar");
1.10      kristaps 1050:                pnode_print(p, pp);
1.64      schwarze 1051:                if (isrep && pp->node == NODE_TEXT)
1.69      schwarze 1052:                        macro_addarg(p, "...", 0);
1.10      kristaps 1053:        }
1.4       kristaps 1054: }
                   1055:
1.24      kristaps 1056: static void
                   1057: pnode_printgroup(struct parse *p, struct pnode *pn)
                   1058: {
                   1059:        struct pnode    *pp, *np;
                   1060:        struct pattr    *ap;
                   1061:        int              isop, sv;
                   1062:
                   1063:        isop = 1;
1.44      schwarze 1064:        TAILQ_FOREACH(ap, &pn->attrq, child)
1.64      schwarze 1065:                if (ap->key == ATTRKEY_CHOICE &&
                   1066:                    (ap->val == ATTRVAL_PLAIN || ap->val == ATTRVAL_REQ)) {
1.24      kristaps 1067:                        isop = 0;
                   1068:                        break;
                   1069:                }
                   1070:
1.44      schwarze 1071:        /*
1.24      kristaps 1072:         * Make sure we're on a macro line.
                   1073:         * This will prevent pnode_print() for putting us on a
                   1074:         * subsequent line.
                   1075:         */
1.69      schwarze 1076:        sv = p->linestate == LINE_NEW;
1.44      schwarze 1077:        if (isop)
1.69      schwarze 1078:                macro_open(p, "Op");
1.24      kristaps 1079:        else if (sv)
1.69      schwarze 1080:                macro_open(p, "No");
1.24      kristaps 1081:
                   1082:        /*
                   1083:         * Keep on printing text separated by the vertical bar as long
                   1084:         * as we're within the same origin node as the group.
                   1085:         * This is kind of a nightmare.
                   1086:         * Eh, DocBook...
                   1087:         * FIXME: if there's a "Fl", we don't cut off the leading "-"
                   1088:         * like we do in pnode_print().
                   1089:         */
                   1090:        TAILQ_FOREACH(pp, &pn->childq, child) {
                   1091:                pnode_print(p, pp);
                   1092:                np = TAILQ_NEXT(pp, child);
1.64      schwarze 1093:                while (np != NULL) {
1.24      kristaps 1094:                        if (pp->node != np->node)
                   1095:                                break;
1.69      schwarze 1096:                        macro_addarg(p, "|", 0);
                   1097:                        macro_addnode(p, np, 0);
1.24      kristaps 1098:                        pp = np;
                   1099:                        np = TAILQ_NEXT(np, child);
                   1100:                }
                   1101:        }
1.69      schwarze 1102:        if (sv)
                   1103:                macro_close(p);
1.24      kristaps 1104: }
                   1105:
1.7       kristaps 1106: static void
                   1107: pnode_printprologue(struct parse *p, struct pnode *pn)
                   1108: {
                   1109:        struct pnode    *pp;
                   1110:
1.64      schwarze 1111:        pp = p->root == NULL ? NULL :
1.9       kristaps 1112:                pnode_findfirst(p->root, NODE_REFMETA);
                   1113:
1.69      schwarze 1114:        macro_line(p, "Dd $Mdocdate" "$");
1.64      schwarze 1115:        if (pp != NULL) {
1.7       kristaps 1116:                pnode_printrefmeta(p, pp);
                   1117:                pnode_unlink(pp);
1.69      schwarze 1118:        } else {
                   1119:                macro_open(p, "Dt");
                   1120:                macro_addarg(p,
                   1121:                    pnode_getattr_raw(p->root, ATTRKEY_ID, "UNKNOWN"), 0);
                   1122:                macro_addarg(p, "1", 0);
                   1123:                macro_close(p);
                   1124:        }
                   1125:        macro_line(p, "Os");
1.43      kristaps 1126:
1.64      schwarze 1127:        if (p->flags & PARSE_EQN) {
1.69      schwarze 1128:                macro_line(p, "EQ");
1.70    ! schwarze 1129:                print_text(p, "delim $$");
1.69      schwarze 1130:                macro_line(p, "EN");
1.43      kristaps 1131:        }
1.7       kristaps 1132: }
                   1133:
1.42      kristaps 1134: /*
                   1135:  * We can have multiple <term> elements within a <varlistentry>, which
                   1136:  * we should comma-separate as list headers.
                   1137:  */
1.13      kristaps 1138: static void
                   1139: pnode_printvarlistentry(struct parse *p, struct pnode *pn)
                   1140: {
                   1141:        struct pnode    *pp;
1.42      kristaps 1142:        int              first = 1;
1.13      kristaps 1143:
1.69      schwarze 1144:        macro_open(p, "It");
                   1145:        TAILQ_FOREACH(pp, &pn->childq, child) {
                   1146:                if (pp->node != NODE_TERM)
                   1147:                        continue;
                   1148:                if ( ! first)
1.70    ! schwarze 1149:                        macro_addarg(p, ",", MACROLINE_NOWS);
1.69      schwarze 1150:                pnode_print(p, pp);
                   1151:                first = 0;
                   1152:        }
                   1153:        macro_close(p);
1.13      kristaps 1154:        TAILQ_FOREACH(pp, &pn->childq, child)
1.69      schwarze 1155:                if (pp->node != NODE_TERM)
1.13      kristaps 1156:                        pnode_print(p, pp);
                   1157: }
                   1158:
                   1159: static void
1.25      kristaps 1160: pnode_printrow(struct parse *p, struct pnode *pn)
                   1161: {
                   1162:        struct pnode    *pp;
                   1163:
1.69      schwarze 1164:        macro_line(p, "Bl -dash -compact");
1.25      kristaps 1165:        TAILQ_FOREACH(pp, &pn->childq, child) {
1.69      schwarze 1166:                macro_line(p, "It");
1.25      kristaps 1167:                pnode_print(p, pp);
                   1168:        }
1.69      schwarze 1169:        macro_line(p, "El");
1.25      kristaps 1170: }
                   1171:
                   1172: static void
                   1173: pnode_printtable(struct parse *p, struct pnode *pn)
1.16      kristaps 1174: {
                   1175:        struct pnode    *pp;
                   1176:
1.69      schwarze 1177:        TAILQ_FOREACH(pp, &pn->childq, child) {
1.64      schwarze 1178:                if (pp->node == NODE_TITLE) {
1.54      schwarze 1179:                        pnode_printpara(p, pp);
1.16      kristaps 1180:                        pnode_print(p, pp);
                   1181:                        pnode_unlink(pp);
                   1182:                }
1.69      schwarze 1183:        }
                   1184:        macro_line(p, "Bl -ohang");
1.64      schwarze 1185:        while ((pp = pnode_findfirst(pn, NODE_ROW)) != NULL) {
1.69      schwarze 1186:                macro_line(p, "It Table Row");
1.25      kristaps 1187:                pnode_printrow(p, pp);
                   1188:                pnode_unlink(pp);
                   1189:        }
1.69      schwarze 1190:        macro_line(p, "El");
1.25      kristaps 1191: }
                   1192:
                   1193: static void
                   1194: pnode_printlist(struct parse *p, struct pnode *pn)
                   1195: {
                   1196:        struct pnode    *pp;
1.16      kristaps 1197:
1.69      schwarze 1198:        TAILQ_FOREACH(pp, &pn->childq, child) {
1.64      schwarze 1199:                if (pp->node == NODE_TITLE) {
1.54      schwarze 1200:                        pnode_printpara(p, pp);
1.25      kristaps 1201:                        pnode_print(p, pp);
                   1202:                        pnode_unlink(pp);
                   1203:                }
1.69      schwarze 1204:        }
                   1205:        macro_argline(p, "Bl",
                   1206:            pn->node == NODE_ORDEREDLIST ? "-enum" : "-bullet");
1.16      kristaps 1207:        TAILQ_FOREACH(pp, &pn->childq, child) {
1.69      schwarze 1208:                macro_line(p, "It");
1.16      kristaps 1209:                pnode_print(p, pp);
                   1210:        }
1.69      schwarze 1211:        macro_line(p, "El");
1.16      kristaps 1212: }
                   1213:
                   1214: static void
1.13      kristaps 1215: pnode_printvariablelist(struct parse *p, struct pnode *pn)
                   1216: {
                   1217:        struct pnode    *pp;
                   1218:
1.69      schwarze 1219:        TAILQ_FOREACH(pp, &pn->childq, child) {
1.64      schwarze 1220:                if (pp->node == NODE_TITLE) {
1.54      schwarze 1221:                        pnode_printpara(p, pp);
1.13      kristaps 1222:                        pnode_print(p, pp);
                   1223:                        pnode_unlink(pp);
                   1224:                }
1.69      schwarze 1225:        }
                   1226:        macro_line(p, "Bl -tag -width Ds");
                   1227:        TAILQ_FOREACH(pp, &pn->childq, child) {
                   1228:                if (pp->node == NODE_VARLISTENTRY)
1.13      kristaps 1229:                        pnode_print(p, pp);
1.69      schwarze 1230:                else
                   1231:                        macro_nodeline(p, "It", pp);
                   1232:        }
                   1233:        macro_line(p, "El");
1.13      kristaps 1234: }
                   1235:
1.1       kristaps 1236: /*
                   1237:  * Print a parsed node (or ignore it--whatever).
                   1238:  * This is a recursive function.
1.23      kristaps 1239:  * FIXME: if we're in a literal context (<screen> or <programlisting> or
                   1240:  * whatever), don't print inline macros.
1.1       kristaps 1241:  */
                   1242: static void
                   1243: pnode_print(struct parse *p, struct pnode *pn)
                   1244: {
                   1245:        struct pnode    *pp;
1.59      schwarze 1246:        const char      *ccp;
1.1       kristaps 1247:        char            *cp;
1.69      schwarze 1248:        int              last;
                   1249:        enum linestate   sv;
1.1       kristaps 1250:
1.64      schwarze 1251:        if (pn == NULL)
1.1       kristaps 1252:                return;
                   1253:
1.69      schwarze 1254:        sv = p->linestate;
1.1       kristaps 1255:
                   1256:        switch (pn->node) {
1.62      schwarze 1257:        case NODE_APPLICATION:
1.69      schwarze 1258:                macro_open(p, "Nm");
1.27      kristaps 1259:                break;
1.62      schwarze 1260:        case NODE_ANCHOR:
1.30      kristaps 1261:                /* Don't print anything! */
                   1262:                return;
1.62      schwarze 1263:        case NODE_ARG:
1.10      kristaps 1264:                pnode_printarg(p, pn);
1.4       kristaps 1265:                pnode_unlinksub(pn);
                   1266:                break;
1.62      schwarze 1267:        case NODE_AUTHOR:
1.69      schwarze 1268:                macro_open(p, "An");
1.50      schwarze 1269:                break;
1.62      schwarze 1270:        case NODE_AUTHORGROUP:
1.69      schwarze 1271:                macro_line(p, "An -split");
1.50      schwarze 1272:                break;
1.62      schwarze 1273:        case NODE_BOOKINFO:
1.69      schwarze 1274:                macro_line(p, "Sh NAME");
1.50      schwarze 1275:                break;
1.62      schwarze 1276:        case NODE_CITEREFENTRY:
1.1       kristaps 1277:                pnode_printciterefentry(p, pn);
1.4       kristaps 1278:                pnode_unlinksub(pn);
1.1       kristaps 1279:                break;
1.68      schwarze 1280:        case NODE_CITETITLE:
1.69      schwarze 1281:                macro_open(p, "%T");
1.68      schwarze 1282:                break;
1.62      schwarze 1283:        case NODE_CODE:
1.69      schwarze 1284:                macro_open(p, "Li");
1.4       kristaps 1285:                break;
1.62      schwarze 1286:        case NODE_COMMAND:
1.69      schwarze 1287:                macro_open(p, "Nm");
1.13      kristaps 1288:                break;
1.62      schwarze 1289:        case NODE_CONSTANT:
1.69      schwarze 1290:                macro_open(p, "Dv");
1.33      kristaps 1291:                break;
1.62      schwarze 1292:        case NODE_EDITOR:
1.70    ! schwarze 1293:                print_text(p, "editor:");
1.69      schwarze 1294:                macro_open(p, "An");
1.50      schwarze 1295:                break;
1.65      schwarze 1296:        case NODE_EMAIL:
1.69      schwarze 1297:                macro_open(p, "Aq Mt");
1.65      schwarze 1298:                break;
1.62      schwarze 1299:        case NODE_EMPHASIS:
                   1300:        case NODE_FIRSTTERM:
1.69      schwarze 1301:                macro_open(p, "Em");
1.1       kristaps 1302:                break;
1.62      schwarze 1303:        case NODE_ENVAR:
1.69      schwarze 1304:                macro_open(p, "Ev");
1.21      kristaps 1305:                break;
1.62      schwarze 1306:        case NODE_FILENAME:
1.69      schwarze 1307:                macro_open(p, "Pa");
1.17      kristaps 1308:                break;
1.62      schwarze 1309:        case NODE_FUNCTION:
1.69      schwarze 1310:                macro_open(p, "Fn");
1.3       kristaps 1311:                break;
1.62      schwarze 1312:        case NODE_FUNCPROTOTYPE:
1.3       kristaps 1313:                pnode_printfuncprototype(p, pn);
1.4       kristaps 1314:                pnode_unlinksub(pn);
1.3       kristaps 1315:                break;
1.62      schwarze 1316:        case NODE_FUNCSYNOPSISINFO:
1.69      schwarze 1317:                macro_open(p, "Fd");
1.16      kristaps 1318:                break;
1.62      schwarze 1319:        case NODE_INDEXTERM:
1.55      schwarze 1320:                return;
1.62      schwarze 1321:        case NODE_INFORMALEQUATION:
1.69      schwarze 1322:                macro_line(p, "EQ");
1.43      kristaps 1323:                break;
1.62      schwarze 1324:        case NODE_INLINEEQUATION:
1.69      schwarze 1325:                if (p->linestate == LINE_NEW)
                   1326:                        p->linestate = LINE_TEXT;
                   1327:                putchar('$');
1.43      kristaps 1328:                break;
1.62      schwarze 1329:        case NODE_ITEMIZEDLIST:
1.25      kristaps 1330:                pnode_printlist(p, pn);
                   1331:                pnode_unlinksub(pn);
1.24      kristaps 1332:                break;
1.62      schwarze 1333:        case NODE_GROUP:
1.24      kristaps 1334:                pnode_printgroup(p, pn);
                   1335:                pnode_unlinksub(pn);
1.10      kristaps 1336:                break;
1.67      schwarze 1337:        case NODE_KEYSYM:
1.69      schwarze 1338:                macro_open(p, "Sy");
1.67      schwarze 1339:                break;
1.62      schwarze 1340:        case NODE_LEGALNOTICE:
1.69      schwarze 1341:                macro_line(p, "Sh LEGAL NOTICE");
1.50      schwarze 1342:                break;
1.62      schwarze 1343:        case NODE_LINK:
1.59      schwarze 1344:                ccp = pnode_getattr_raw(pn, ATTRKEY_LINKEND, NULL);
1.64      schwarze 1345:                if (ccp == NULL)
1.58      schwarze 1346:                        break;
1.69      schwarze 1347:                macro_argline(p, "Sx", ccp);
1.58      schwarze 1348:                return;
1.62      schwarze 1349:        case NODE_LITERAL:
1.69      schwarze 1350:                macro_open(p, "Li");
1.19      kristaps 1351:                break;
1.62      schwarze 1352:        case NODE_LITERALLAYOUT:
1.69      schwarze 1353:                macro_argline(p, "Bd", pnode_getattr(pn, ATTRKEY_CLASS) ==
1.66      schwarze 1354:                    ATTRVAL_MONOSPACED ? "-literal" : "-unfilled");
1.60      schwarze 1355:                break;
1.62      schwarze 1356:        case NODE_MML_MFENCED:
1.41      kristaps 1357:                pnode_printmathfenced(p, pn);
                   1358:                pnode_unlinksub(pn);
1.40      kristaps 1359:                break;
1.62      schwarze 1360:        case NODE_MML_MROW:
                   1361:        case NODE_MML_MI:
                   1362:        case NODE_MML_MN:
                   1363:        case NODE_MML_MO:
1.43      kristaps 1364:                if (TAILQ_EMPTY(&pn->childq))
                   1365:                        break;
                   1366:                fputs(" { ", stdout);
1.40      kristaps 1367:                break;
1.62      schwarze 1368:        case NODE_MML_MFRAC:
                   1369:        case NODE_MML_MSUB:
                   1370:        case NODE_MML_MSUP:
1.40      kristaps 1371:                pnode_printmath(p, pn);
                   1372:                pnode_unlinksub(pn);
                   1373:                break;
1.62      schwarze 1374:        case NODE_OPTION:
1.69      schwarze 1375:                macro_open(p, "Fl");
1.1       kristaps 1376:                break;
1.62      schwarze 1377:        case NODE_ORDEREDLIST:
1.25      kristaps 1378:                pnode_printlist(p, pn);
                   1379:                pnode_unlinksub(pn);
                   1380:                break;
1.62      schwarze 1381:        case NODE_PARA:
1.54      schwarze 1382:                pnode_printpara(p, pn);
1.3       kristaps 1383:                break;
1.62      schwarze 1384:        case NODE_PARAMETER:
1.10      kristaps 1385:                /* Suppress non-text children... */
1.69      schwarze 1386:                macro_open(p, "Fa \"");
                   1387:                macro_addnode(p, pn, MACROLINE_NOWS);
                   1388:                macro_addarg(p, "\"", MACROLINE_NOWS);
                   1389:                macro_close(p);
1.4       kristaps 1390:                pnode_unlinksub(pn);
1.1       kristaps 1391:                break;
1.62      schwarze 1392:        case NODE_QUOTE:
1.69      schwarze 1393:                macro_open(p, "Qo");
1.28      kristaps 1394:                break;
1.62      schwarze 1395:        case NODE_PROGRAMLISTING:
                   1396:        case NODE_SCREEN:
1.69      schwarze 1397:                macro_line(p, "Bd -literal");
1.15      kristaps 1398:                break;
1.62      schwarze 1399:        case NODE_REFENTRYINFO:
1.15      kristaps 1400:                /* Suppress. */
                   1401:                pnode_unlinksub(pn);
1.1       kristaps 1402:                break;
1.62      schwarze 1403:        case NODE_REFMETA:
1.7       kristaps 1404:                abort();
1.1       kristaps 1405:                break;
1.62      schwarze 1406:        case NODE_REFNAME:
1.10      kristaps 1407:                /* Suppress non-text children... */
1.70    ! schwarze 1408:                macro_open(p, "Nm");
        !          1409:                macro_addnode(p, pn, 0);
1.4       kristaps 1410:                pnode_unlinksub(pn);
1.10      kristaps 1411:                break;
1.62      schwarze 1412:        case NODE_REFNAMEDIV:
1.69      schwarze 1413:                macro_line(p, "Sh NAME");
1.1       kristaps 1414:                break;
1.62      schwarze 1415:        case NODE_REFPURPOSE:
1.69      schwarze 1416:                macro_open(p, "Nd");
1.10      kristaps 1417:                break;
1.62      schwarze 1418:        case NODE_REFSYNOPSISDIV:
1.6       kristaps 1419:                pnode_printrefsynopsisdiv(p, pn);
1.69      schwarze 1420:                macro_line(p, "Sh SYNOPSIS");
1.1       kristaps 1421:                break;
1.62      schwarze 1422:        case NODE_PREFACE:
                   1423:        case NODE_SECTION:
                   1424:        case NODE_NOTE:
                   1425:        case NODE_TIP:
                   1426:        case NODE_CAUTION:
                   1427:        case NODE_WARNING:
1.1       kristaps 1428:                pnode_printrefsect(p, pn);
                   1429:                break;
1.62      schwarze 1430:        case NODE_REPLACEABLE:
1.69      schwarze 1431:                macro_open(p, "Ar");
1.13      kristaps 1432:                break;
1.62      schwarze 1433:        case NODE_SBR:
1.69      schwarze 1434:                macro_line(p, "br");
1.19      kristaps 1435:                break;
1.62      schwarze 1436:        case NODE_SGMLTAG:
1.69      schwarze 1437:                macro_open(p, "Li");
1.30      kristaps 1438:                break;
1.62      schwarze 1439:        case NODE_STRUCTNAME:
1.69      schwarze 1440:                macro_open(p, "Vt");
1.25      kristaps 1441:                break;
1.62      schwarze 1442:        case NODE_TABLE:
                   1443:        case NODE_INFORMALTABLE:
1.25      kristaps 1444:                pnode_printtable(p, pn);
                   1445:                pnode_unlinksub(pn);
1.10      kristaps 1446:                break;
1.62      schwarze 1447:        case NODE_TEXT:
1.1       kristaps 1448:                bufclear(p);
                   1449:                bufappend(p, pn);
1.64      schwarze 1450:                if (p->bsz == 0) {
1.37      kristaps 1451:                        assert(pn->real != pn->b);
                   1452:                        break;
                   1453:                }
1.69      schwarze 1454:                if (p->linestate == LINE_NEW)
                   1455:                        p->linestate = LINE_TEXT;
                   1456:                else
                   1457:                        putchar(' ');
1.37      kristaps 1458:
1.1       kristaps 1459:                /*
                   1460:                 * Output all characters, squeezing out whitespace
1.44      schwarze 1461:                 * between newlines.
1.1       kristaps 1462:                 * XXX: all whitespace, including tabs (?).
                   1463:                 * Remember to escape control characters and escapes.
                   1464:                 */
1.20      kristaps 1465:                cp = p->b;
1.37      kristaps 1466:
1.20      kristaps 1467:                /*
                   1468:                 * There's often a superfluous "-" in its <option> tags
                   1469:                 * before the actual flags themselves.
                   1470:                 * "Fl" does this for us, so remove it.
                   1471:                 */
1.64      schwarze 1472:                if (pn->parent != NULL &&
                   1473:                    pn->parent->node == NODE_OPTION &&
                   1474:                    *cp == '-')
1.20      kristaps 1475:                        cp++;
1.64      schwarze 1476:                for (last = '\n'; *cp != '\0'; ) {
                   1477:                        if (last == '\n') {
1.1       kristaps 1478:                                /* Consume all whitespace. */
1.46      schwarze 1479:                                if (isspace((unsigned char)*cp)) {
                   1480:                                        while (isspace((unsigned char)*cp))
1.1       kristaps 1481:                                                cp++;
                   1482:                                        continue;
1.64      schwarze 1483:                                } else if (*cp == '\'' || *cp == '.')
1.1       kristaps 1484:                                        fputs("\\&", stdout);
                   1485:                        }
                   1486:                        putchar(last = *cp++);
                   1487:                        /* If we're a character escape, escape us. */
1.64      schwarze 1488:                        if (last == '\\')
1.1       kristaps 1489:                                putchar('e');
                   1490:                }
                   1491:                break;
1.62      schwarze 1492:        case NODE_TITLE:
1.69      schwarze 1493:                if (pn->parent->node == NODE_BOOKINFO)
                   1494:                        macro_open(p, "Nd");
1.50      schwarze 1495:                break;
1.62      schwarze 1496:        case NODE_TYPE:
1.69      schwarze 1497:                macro_open(p, "Vt");
1.39      kristaps 1498:                break;
1.62      schwarze 1499:        case NODE_USERINPUT:
1.69      schwarze 1500:                macro_open(p, "Li");
1.26      kristaps 1501:                break;
1.62      schwarze 1502:        case NODE_VARIABLELIST:
1.13      kristaps 1503:                pnode_printvariablelist(p, pn);
                   1504:                pnode_unlinksub(pn);
                   1505:                break;
1.62      schwarze 1506:        case NODE_VARLISTENTRY:
1.13      kristaps 1507:                pnode_printvarlistentry(p, pn);
1.69      schwarze 1508:                pnode_unlinksub(pn);
1.13      kristaps 1509:                break;
1.62      schwarze 1510:        case NODE_VARNAME:
1.69      schwarze 1511:                macro_open(p, "Va");
1.23      kristaps 1512:                break;
1.1       kristaps 1513:        default:
                   1514:                break;
                   1515:        }
                   1516:
                   1517:        TAILQ_FOREACH(pp, &pn->childq, child)
                   1518:                pnode_print(p, pp);
                   1519:
                   1520:        switch (pn->node) {
1.62      schwarze 1521:        case NODE_INFORMALEQUATION:
1.69      schwarze 1522:                macro_line(p, "EN");
1.40      kristaps 1523:                break;
1.62      schwarze 1524:        case NODE_INLINEEQUATION:
1.43      kristaps 1525:                fputs("$ ", stdout);
1.69      schwarze 1526:                p->linestate = sv;
1.43      kristaps 1527:                break;
1.62      schwarze 1528:        case NODE_MML_MROW:
                   1529:        case NODE_MML_MI:
                   1530:        case NODE_MML_MN:
                   1531:        case NODE_MML_MO:
1.43      kristaps 1532:                if (TAILQ_EMPTY(&pn->childq))
                   1533:                        break;
                   1534:                fputs(" } ", stdout);
1.40      kristaps 1535:                break;
1.62      schwarze 1536:        case NODE_APPLICATION:
                   1537:        case NODE_ARG:
                   1538:        case NODE_AUTHOR:
                   1539:        case NODE_CITEREFENTRY:
1.68      schwarze 1540:        case NODE_CITETITLE:
1.62      schwarze 1541:        case NODE_CODE:
                   1542:        case NODE_COMMAND:
                   1543:        case NODE_CONSTANT:
                   1544:        case NODE_EDITOR:
1.65      schwarze 1545:        case NODE_EMAIL:
1.62      schwarze 1546:        case NODE_EMPHASIS:
                   1547:        case NODE_ENVAR:
                   1548:        case NODE_FILENAME:
                   1549:        case NODE_FIRSTTERM:
                   1550:        case NODE_FUNCTION:
                   1551:        case NODE_FUNCSYNOPSISINFO:
1.67      schwarze 1552:        case NODE_KEYSYM:
1.62      schwarze 1553:        case NODE_LITERAL:
                   1554:        case NODE_OPTION:
                   1555:        case NODE_PARAMETER:
                   1556:        case NODE_REPLACEABLE:
                   1557:        case NODE_REFPURPOSE:
                   1558:        case NODE_SGMLTAG:
                   1559:        case NODE_STRUCTNAME:
                   1560:        case NODE_TYPE:
                   1561:        case NODE_USERINPUT:
                   1562:        case NODE_VARNAME:
1.69      schwarze 1563:                if (sv != LINE_MACRO && p->linestate == LINE_MACRO)
                   1564:                        macro_closepunct(p, pn);
1.28      kristaps 1565:                break;
1.62      schwarze 1566:        case NODE_QUOTE:
1.69      schwarze 1567:                if (sv == LINE_NEW)
                   1568:                        macro_close(p);
                   1569:                sv = p->linestate;
                   1570:                macro_open(p, "Qc");
                   1571:                if (sv == LINE_NEW)
                   1572:                        macro_close(p);
1.10      kristaps 1573:                break;
1.62      schwarze 1574:        case NODE_REFNAME:
1.12      kristaps 1575:                /*
                   1576:                 * If we're in the NAME macro and we have multiple
                   1577:                 * <refname> macros in sequence, then print out a
                   1578:                 * trailing comma before the newline.
                   1579:                 */
1.64      schwarze 1580:                if (pn->parent != NULL &&
                   1581:                    pn->parent->node == NODE_REFNAMEDIV &&
                   1582:                    TAILQ_NEXT(pn, child) != NULL &&
                   1583:                    TAILQ_NEXT(pn, child)->node == NODE_REFNAME)
1.70    ! schwarze 1584:                        macro_addarg(p, ",", 0);
1.69      schwarze 1585:                if (sv == LINE_NEW)
                   1586:                        macro_close(p);
1.52      schwarze 1587:                break;
1.62      schwarze 1588:        case NODE_PREFACE:
                   1589:        case NODE_SECTION:
                   1590:        case NODE_NOTE:
                   1591:        case NODE_TIP:
                   1592:        case NODE_CAUTION:
                   1593:        case NODE_WARNING:
1.52      schwarze 1594:                p->level--;
1.12      kristaps 1595:                break;
1.62      schwarze 1596:        case NODE_LITERALLAYOUT:
                   1597:        case NODE_PROGRAMLISTING:
                   1598:        case NODE_SCREEN:
1.69      schwarze 1599:                macro_line(p, "Ed");
1.50      schwarze 1600:                break;
1.62      schwarze 1601:        case NODE_TITLE:
1.69      schwarze 1602:                if (pn->parent->node == NODE_BOOKINFO)
                   1603:                        macro_line(p, "Sh AUTHORS");
1.1       kristaps 1604:                break;
                   1605:        default:
                   1606:                break;
                   1607:        }
                   1608: }
                   1609:
                   1610: /*
                   1611:  * Loop around the read buffer until we've drained it of all data.
                   1612:  * Invoke the parser context with each buffer fill.
                   1613:  */
                   1614: static int
1.44      schwarze 1615: readfile(XML_Parser xp, int fd,
1.1       kristaps 1616:        char *b, size_t bsz, const char *fn)
                   1617: {
                   1618:        struct parse     p;
                   1619:        int              rc;
                   1620:        ssize_t          ssz;
                   1621:
                   1622:        memset(&p, 0, sizeof(struct parse));
                   1623:
                   1624:        p.b = malloc(p.bsz = p.mbsz = 1024);
1.12      kristaps 1625:        p.fname = fn;
                   1626:        p.xml = xp;
1.1       kristaps 1627:
                   1628:        XML_SetCharacterDataHandler(xp, xml_char);
                   1629:        XML_SetElementHandler(xp, xml_elem_start, xml_elem_end);
                   1630:        XML_SetUserData(xp, &p);
                   1631:
                   1632:        while ((ssz = read(fd, b, bsz)) >= 0) {
1.64      schwarze 1633:                if ((rc = XML_Parse(xp, b, ssz, 0 == ssz)) == 0)
1.30      kristaps 1634:                        fprintf(stderr, "%s:%zu:%zu: %s\n", fn,
                   1635:                                XML_GetCurrentLineNumber(xp),
1.44      schwarze 1636:                                XML_GetCurrentColumnNumber(xp),
1.1       kristaps 1637:                                XML_ErrorString
                   1638:                                (XML_GetErrorCode(xp)));
                   1639:                else if ( ! p.stop && ssz > 0)
                   1640:                        continue;
1.44      schwarze 1641:                /*
1.1       kristaps 1642:                 * Exit when we've read all or errors have occured
                   1643:                 * during the parse sequence.
                   1644:                 */
1.69      schwarze 1645:                p.linestate = LINE_NEW;
1.7       kristaps 1646:                pnode_printprologue(&p, p.root);
1.1       kristaps 1647:                pnode_print(&p, p.root);
1.69      schwarze 1648:                if (p.linestate != LINE_NEW)
                   1649:                        putchar('\n');
1.1       kristaps 1650:                pnode_free(p.root);
                   1651:                free(p.b);
1.63      schwarze 1652:                return rc != 0 && p.stop == 0;
1.1       kristaps 1653:        }
                   1654:
                   1655:        /* Read error has occured. */
                   1656:        perror(fn);
                   1657:        pnode_free(p.root);
                   1658:        free(p.b);
1.63      schwarze 1659:        return 0;
1.1       kristaps 1660: }
                   1661:
                   1662: int
                   1663: main(int argc, char *argv[])
                   1664: {
                   1665:        XML_Parser       xp;
                   1666:        const char      *fname;
                   1667:        char            *buf;
1.38      kristaps 1668:        int              fd, rc, ch;
                   1669:        const char      *progname;
                   1670:
                   1671:        progname = strrchr(argv[0], '/');
                   1672:        if (progname == NULL)
                   1673:                progname = argv[0];
                   1674:        else
                   1675:                ++progname;
1.1       kristaps 1676:
                   1677:        fname = "-";
                   1678:        xp = NULL;
                   1679:        buf = NULL;
1.63      schwarze 1680:        rc = 1;
1.1       kristaps 1681:
1.64      schwarze 1682:        while ((ch = getopt(argc, argv, "W")) != -1)
1.38      kristaps 1683:                switch (ch) {
1.62      schwarze 1684:                case 'W':
1.38      kristaps 1685:                        warn = 1;
                   1686:                        break;
                   1687:                default:
                   1688:                        goto usage;
                   1689:                }
1.1       kristaps 1690:
                   1691:        argc -= optind;
                   1692:        argv += optind;
                   1693:
1.45      schwarze 1694:        if (argc > 1) {
                   1695:                fprintf(stderr, "%s: Too many arguments\n", argv[1]);
                   1696:                goto usage;
                   1697:        } else if (argc > 0)
1.1       kristaps 1698:                fname = argv[0];
                   1699:
                   1700:        /* Read from stdin or a file. */
1.64      schwarze 1701:        fd = strcmp(fname, "-") == 0 ?
1.1       kristaps 1702:                STDIN_FILENO : open(fname, O_RDONLY, 0);
                   1703:
                   1704:        /*
                   1705:         * Open file for reading.
                   1706:         * Allocate a read buffer.
                   1707:         * Create the parser context.
                   1708:         * Dive directly into the parse.
                   1709:         */
1.64      schwarze 1710:        if (fd == -1)
1.1       kristaps 1711:                perror(fname);
1.64      schwarze 1712:        else if ((buf = malloc(4096)) == NULL)
1.1       kristaps 1713:                perror(NULL);
1.64      schwarze 1714:        else if ((xp = XML_ParserCreate(NULL)) == NULL)
1.1       kristaps 1715:                perror(NULL);
1.63      schwarze 1716:        else if (readfile(xp, fd, buf, 4096, fname))
                   1717:                rc = 0;
1.1       kristaps 1718:
                   1719:        XML_ParserFree(xp);
                   1720:        free(buf);
1.64      schwarze 1721:        if (fd != STDIN_FILENO)
1.1       kristaps 1722:                close(fd);
1.63      schwarze 1723:        return rc;
1.38      kristaps 1724:
                   1725: usage:
1.45      schwarze 1726:        fprintf(stderr, "usage: %s [-W] [input_filename]\n", progname);
1.63      schwarze 1727:        return 1;
1.1       kristaps 1728: }

CVSweb