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

Annotation of docbook2mdoc/docbook2mdoc.c, Revision 1.71

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

CVSweb