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

Annotation of docbook2mdoc/docbook2mdoc.c, Revision 1.73

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

CVSweb