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

Annotation of mandoc/mdoc_markdown.c, Revision 1.1

1.1     ! schwarze    1: /*     $Id$ */
        !             2: /*
        !             3:  * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
        !             4:  *
        !             5:  * Permission to use, copy, modify, and distribute this software for any
        !             6:  * purpose with or without fee is hereby granted, provided that the above
        !             7:  * copyright notice and this permission notice appear in all copies.
        !             8:  *
        !             9:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
        !            10:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
        !            11:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
        !            12:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
        !            13:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
        !            14:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
        !            15:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
        !            16:  */
        !            17: #include <sys/types.h>
        !            18:
        !            19: #include <assert.h>
        !            20: #include <ctype.h>
        !            21: #include <stdio.h>
        !            22: #include <string.h>
        !            23:
        !            24: #include "mandoc_aux.h"
        !            25: #include "mandoc.h"
        !            26: #include "roff.h"
        !            27: #include "mdoc.h"
        !            28: #include "main.h"
        !            29:
        !            30: struct md_act {
        !            31:        int             (*cond)(struct roff_node *n);
        !            32:        int             (*pre)(struct roff_node *n);
        !            33:        void            (*post)(struct roff_node *n);
        !            34:        const char       *prefix; /* pre-node string constant */
        !            35:        const char       *suffix; /* post-node string constant */
        !            36: };
        !            37:
        !            38: static void     md_nodelist(struct roff_node *);
        !            39: static void     md_node(struct roff_node *);
        !            40: static const char *md_stack(char c);
        !            41: static void     md_preword(void);
        !            42: static void     md_rawword(const char *);
        !            43: static void     md_word(const char *);
        !            44: static void     md_named(const char *);
        !            45: static void     md_char(unsigned char);
        !            46:
        !            47: static int      md_cond_head(struct roff_node *);
        !            48: static int      md_cond_body(struct roff_node *);
        !            49:
        !            50: static int      md_pre_raw(struct roff_node *);
        !            51: static int      md_pre_word(struct roff_node *);
        !            52: static int      md_pre_skip(struct roff_node *);
        !            53: static void     md_pre_syn(struct roff_node *);
        !            54: static int      md_pre_Ap(struct roff_node *);
        !            55: static int      md_pre_Bd(struct roff_node *);
        !            56: static int      md_pre_Bk(struct roff_node *);
        !            57: static int      md_pre_Bl(struct roff_node *);
        !            58: static int      md_pre_D1(struct roff_node *);
        !            59: static int      md_pre_Dl(struct roff_node *);
        !            60: static int      md_pre_En(struct roff_node *);
        !            61: static int      md_pre_Eo(struct roff_node *);
        !            62: static int      md_pre_Fa(struct roff_node *);
        !            63: static int      md_pre_Fd(struct roff_node *);
        !            64: static int      md_pre_Fn(struct roff_node *);
        !            65: static int      md_pre_Fo(struct roff_node *);
        !            66: static int      md_pre_In(struct roff_node *);
        !            67: static int      md_pre_It(struct roff_node *);
        !            68: static int      md_pre_Lk(struct roff_node *);
        !            69: static int      md_pre_Nd(struct roff_node *);
        !            70: static int      md_pre_Nm(struct roff_node *);
        !            71: static int      md_pre_No(struct roff_node *);
        !            72: static int      md_pre_Ns(struct roff_node *);
        !            73: static int      md_pre_Pp(struct roff_node *);
        !            74: static int      md_pre_Rs(struct roff_node *);
        !            75: static int      md_pre_Sh(struct roff_node *);
        !            76: static int      md_pre_Sm(struct roff_node *);
        !            77: static int      md_pre_Vt(struct roff_node *);
        !            78: static int      md_pre_Xr(struct roff_node *);
        !            79: static int      md_pre__T(struct roff_node *);
        !            80: static int      md_pre_br(struct roff_node *);
        !            81:
        !            82: static void     md_post_raw(struct roff_node *);
        !            83: static void     md_post_word(struct roff_node *);
        !            84: static void     md_post_pc(struct roff_node *);
        !            85: static void     md_post_Bk(struct roff_node *);
        !            86: static void     md_post_Bl(struct roff_node *);
        !            87: static void     md_post_D1(struct roff_node *);
        !            88: static void     md_post_En(struct roff_node *);
        !            89: static void     md_post_Eo(struct roff_node *);
        !            90: static void     md_post_Fa(struct roff_node *);
        !            91: static void     md_post_Fd(struct roff_node *);
        !            92: static void     md_post_Fn(struct roff_node *);
        !            93: static void     md_post_Fo(struct roff_node *);
        !            94: static void     md_post_In(struct roff_node *);
        !            95: static void     md_post_It(struct roff_node *);
        !            96: static void     md_post_Lb(struct roff_node *);
        !            97: static void     md_post_Nm(struct roff_node *);
        !            98: static void     md_post_Pf(struct roff_node *);
        !            99: static void     md_post_Vt(struct roff_node *);
        !           100: static void     md_post__T(struct roff_node *);
        !           101:
        !           102: static const struct md_act md_acts[MDOC_MAX + 1] = {
        !           103:        { NULL, md_pre_Ap, NULL, NULL, NULL }, /* Ap */
        !           104:        { NULL, NULL, NULL, NULL, NULL }, /* Dd */
        !           105:        { NULL, NULL, NULL, NULL, NULL }, /* Dt */
        !           106:        { NULL, NULL, NULL, NULL, NULL }, /* Os */
        !           107:        { NULL, md_pre_Sh, NULL, NULL, NULL }, /* Sh */
        !           108:        { NULL, md_pre_Sh, NULL, NULL, NULL }, /* Ss */
        !           109:        { NULL, md_pre_Pp, NULL, NULL, NULL }, /* Pp */
        !           110:        { md_cond_body, md_pre_D1, md_post_D1, NULL, NULL }, /* D1 */
        !           111:        { md_cond_body, md_pre_Dl, md_post_D1, NULL, NULL }, /* Dl */
        !           112:        { md_cond_body, md_pre_Bd, md_post_D1, NULL, NULL }, /* Bd */
        !           113:        { NULL, NULL, NULL, NULL, NULL }, /* Ed */
        !           114:        { md_cond_body, md_pre_Bl, md_post_Bl, NULL, NULL }, /* Bl */
        !           115:        { NULL, NULL, NULL, NULL, NULL }, /* El */
        !           116:        { NULL, md_pre_It, md_post_It, NULL, NULL }, /* It */
        !           117:        { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Ad */
        !           118:        { NULL, NULL, NULL, NULL, NULL }, /* An */
        !           119:        { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Ar */
        !           120:        { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Cd */
        !           121:        { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Cm */
        !           122:        { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Dv */
        !           123:        { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Er */
        !           124:        { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Ev */
        !           125:        { NULL, NULL, NULL, NULL, NULL }, /* Ex */
        !           126:        { NULL, md_pre_Fa, md_post_Fa, NULL, NULL }, /* Fa */
        !           127:        { NULL, md_pre_Fd, md_post_Fd, "**", "**" }, /* Fd */
        !           128:        { NULL, md_pre_raw, md_post_raw, "**-", "**" }, /* Fl */
        !           129:        { NULL, md_pre_Fn, md_post_Fn, NULL, NULL }, /* Fn */
        !           130:        { NULL, md_pre_Fd, md_post_raw, "*", "*" }, /* Ft */
        !           131:        { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Ic */
        !           132:        { NULL, md_pre_In, md_post_In, "*", "*" }, /* In */
        !           133:        { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Li */
        !           134:        { md_cond_head, md_pre_Nd, NULL, NULL, NULL }, /* Nd */
        !           135:        { NULL, md_pre_Nm, md_post_Nm, "**", "**" }, /* Nm */
        !           136:        { md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Op */
        !           137:        { NULL, md_pre_Fd, md_post_raw, "*", "*" }, /* Ot */
        !           138:        { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Pa */
        !           139:        { NULL, NULL, NULL, NULL, NULL }, /* Rv */
        !           140:        { NULL, NULL, NULL, NULL, NULL }, /* St */
        !           141:        { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Va */
        !           142:        { NULL, md_pre_Vt, md_post_Vt, "*", "*" }, /* Vt */
        !           143:        { NULL, md_pre_Xr, NULL, NULL, NULL }, /* Xr */
        !           144:        { NULL, NULL, md_post_pc, NULL, NULL }, /* %A */
        !           145:        { NULL, md_pre_raw, md_post_pc, "*", "*" }, /* %B */
        !           146:        { NULL, NULL, md_post_pc, NULL, NULL }, /* %D */
        !           147:        { NULL, md_pre_raw, md_post_pc, "*", "*" }, /* %I */
        !           148:        { NULL, md_pre_raw, md_post_pc, "*", "*" }, /* %J */
        !           149:        { NULL, NULL, md_post_pc, NULL, NULL }, /* %N */
        !           150:        { NULL, NULL, md_post_pc, NULL, NULL }, /* %O */
        !           151:        { NULL, NULL, md_post_pc, NULL, NULL }, /* %P */
        !           152:        { NULL, NULL, md_post_pc, NULL, NULL }, /* %R */
        !           153:        { NULL, md_pre__T, md_post__T, NULL, NULL }, /* %T */
        !           154:        { NULL, NULL, md_post_pc, NULL, NULL }, /* %V */
        !           155:        { NULL, NULL, NULL, NULL, NULL }, /* Ac */
        !           156:        { md_cond_body, md_pre_word, md_post_word, "<", ">" }, /* Ao */
        !           157:        { md_cond_body, md_pre_word, md_post_word, "<", ">" }, /* Aq */
        !           158:        { NULL, NULL, NULL, NULL, NULL }, /* At */
        !           159:        { NULL, NULL, NULL, NULL, NULL }, /* Bc */
        !           160:        { NULL, NULL, NULL, NULL, NULL }, /* Bf XXX not implemented */
        !           161:        { md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Bo */
        !           162:        { md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Bq */
        !           163:        { NULL, NULL, NULL, NULL, NULL }, /* Bsx */
        !           164:        { NULL, NULL, NULL, NULL, NULL }, /* Bx */
        !           165:        { NULL, NULL, NULL, NULL, NULL }, /* Db */
        !           166:        { NULL, NULL, NULL, NULL, NULL }, /* Dc */
        !           167:        { md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Do */
        !           168:        { md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Dq */
        !           169:        { NULL, NULL, NULL, NULL, NULL }, /* Ec */
        !           170:        { NULL, NULL, NULL, NULL, NULL }, /* Ef */
        !           171:        { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Em */
        !           172:        { md_cond_body, md_pre_Eo, md_post_Eo, NULL, NULL }, /* Eo */
        !           173:        { NULL, NULL, NULL, NULL, NULL }, /* Fx */
        !           174:        { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Ms */
        !           175:        { NULL, md_pre_No, NULL, NULL, NULL }, /* No */
        !           176:        { NULL, md_pre_Ns, NULL, NULL, NULL }, /* Ns */
        !           177:        { NULL, NULL, NULL, NULL, NULL }, /* Nx */
        !           178:        { NULL, NULL, NULL, NULL, NULL }, /* Ox */
        !           179:        { NULL, NULL, NULL, NULL, NULL }, /* Pc */
        !           180:        { NULL, NULL, md_post_Pf, NULL, NULL }, /* Pf */
        !           181:        { md_cond_body, md_pre_word, md_post_word, "(", ")" }, /* Po */
        !           182:        { md_cond_body, md_pre_word, md_post_word, "(", ")" }, /* Pq */
        !           183:        { NULL, NULL, NULL, NULL, NULL }, /* Qc */
        !           184:        { md_cond_body, md_pre_raw, md_post_raw, "'`", "`'" }, /* Ql */
        !           185:        { md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Qo */
        !           186:        { md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Qq */
        !           187:        { NULL, NULL, NULL, NULL, NULL }, /* Re */
        !           188:        { md_cond_body, md_pre_Rs, NULL, NULL, NULL }, /* Rs */
        !           189:        { NULL, NULL, NULL, NULL, NULL }, /* Sc */
        !           190:        { md_cond_body, md_pre_word, md_post_word, "'", "'" }, /* So */
        !           191:        { md_cond_body, md_pre_word, md_post_word, "'", "'" }, /* Sq */
        !           192:        { NULL, md_pre_Sm, NULL, NULL, NULL }, /* Sm */
        !           193:        { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Sx */
        !           194:        { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Sy */
        !           195:        { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Tn */
        !           196:        { NULL, NULL, NULL, NULL, NULL }, /* Ux */
        !           197:        { NULL, NULL, NULL, NULL, NULL }, /* Xc */
        !           198:        { NULL, NULL, NULL, NULL, NULL }, /* Xo */
        !           199:        { NULL, md_pre_Fo, md_post_Fo, "**", "**" }, /* Fo */
        !           200:        { NULL, NULL, NULL, NULL, NULL }, /* Fc */
        !           201:        { md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Oo */
        !           202:        { NULL, NULL, NULL, NULL, NULL }, /* Oc */
        !           203:        { NULL, md_pre_Bk, md_post_Bk, NULL, NULL }, /* Bk */
        !           204:        { NULL, NULL, NULL, NULL, NULL }, /* Ek */
        !           205:        { NULL, NULL, NULL, NULL, NULL }, /* Bt */
        !           206:        { NULL, NULL, NULL, NULL, NULL }, /* Hf */
        !           207:        { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Fr */
        !           208:        { NULL, NULL, NULL, NULL, NULL }, /* Ud */
        !           209:        { NULL, NULL, md_post_Lb, NULL, NULL }, /* Lb */
        !           210:        { NULL, md_pre_Pp, NULL, NULL, NULL }, /* Lp */
        !           211:        { NULL, md_pre_Lk, NULL, NULL, NULL }, /* Lk */
        !           212:        { NULL, md_pre_raw, md_post_raw, "<", ">" }, /* Mt */
        !           213:        { md_cond_body, md_pre_word, md_post_word, "{", "}" }, /* Brq */
        !           214:        { md_cond_body, md_pre_word, md_post_word, "{", "}" }, /* Bro */
        !           215:        { NULL, NULL, NULL, NULL, NULL }, /* Brc */
        !           216:        { NULL, NULL, md_post_pc, NULL, NULL }, /* %C */
        !           217:        { NULL, md_pre_skip, NULL, NULL, NULL }, /* Es */
        !           218:        { md_cond_body, md_pre_En, md_post_En, NULL, NULL }, /* En */
        !           219:        { NULL, NULL, NULL, NULL, NULL }, /* Dx */
        !           220:        { NULL, NULL, md_post_pc, NULL, NULL }, /* %Q */
        !           221:        { NULL, md_pre_br, NULL, NULL, NULL }, /* br */
        !           222:        { NULL, md_pre_Pp, NULL, NULL, NULL }, /* sp */
        !           223:        { NULL, NULL, md_post_pc, NULL, NULL }, /* %U */
        !           224:        { NULL, NULL, NULL, NULL, NULL }, /* Ta */
        !           225:        { NULL, NULL, NULL, NULL, NULL }, /* ll */
        !           226:        { NULL, NULL, NULL, NULL, NULL }, /* ROOT */
        !           227: };
        !           228:
        !           229: static int      outflags;
        !           230: #define        MD_spc           (1 << 0)  /* Blank character before next word. */
        !           231: #define        MD_spc_force     (1 << 1)  /* Even before trailing punctuation. */
        !           232: #define        MD_nonl          (1 << 2)  /* Prevent linebreak in markdown code. */
        !           233: #define        MD_nl            (1 << 3)  /* Break markdown code line. */
        !           234: #define        MD_br            (1 << 4)  /* Insert an output line break. */
        !           235: #define        MD_sp            (1 << 5)  /* Insert a paragraph break. */
        !           236: #define        MD_Sm            (1 << 6)  /* Horizontal spacing mode. */
        !           237: #define        MD_Bk            (1 << 7)  /* Word keep mode. */
        !           238:
        !           239: static int      escflags; /* Escape in generated markdown code: */
        !           240: #define        ESC_BOL  (1 << 0)  /* "#*+-" near the beginning of a line. */
        !           241: #define        ESC_NUM  (1 << 1)  /* "." after a leading number. */
        !           242: #define        ESC_HYP  (1 << 2)  /* "(" immediately after "]". */
        !           243: #define        ESC_PAR  (1 << 3)  /* ")" when "(" is open. */
        !           244: #define        ESC_SQU  (1 << 4)  /* "]" when "[" is open. */
        !           245: #define        ESC_FON  (1 << 5)  /* "*" immediately after unrelated "*". */
        !           246:
        !           247: static int      code_blocks, quote_blocks, list_blocks;
        !           248: static int      outcount;
        !           249:
        !           250: void
        !           251: markdown_mdoc(void *arg, const struct roff_man *mdoc)
        !           252: {
        !           253:        outflags = MD_Sm;
        !           254:        md_word(mdoc->meta.title);
        !           255:        if (mdoc->meta.msec != NULL) {
        !           256:                outflags &= ~MD_spc;
        !           257:                md_word("(");
        !           258:                md_word(mdoc->meta.msec);
        !           259:                md_word(")");
        !           260:        }
        !           261:        md_word("-");
        !           262:        md_word(mdoc->meta.vol);
        !           263:        if (mdoc->meta.arch != NULL) {
        !           264:                md_word("(");
        !           265:                md_word(mdoc->meta.arch);
        !           266:                md_word(")");
        !           267:        }
        !           268:        outflags |= MD_sp;
        !           269:
        !           270:        md_nodelist(mdoc->first->child);
        !           271:
        !           272:        outflags |= MD_sp;
        !           273:        md_word(mdoc->meta.os);
        !           274:        md_word("-");
        !           275:        md_word(mdoc->meta.date);
        !           276:        putchar('\n');
        !           277: }
        !           278:
        !           279: static void
        !           280: md_nodelist(struct roff_node *n)
        !           281: {
        !           282:        while (n != NULL) {
        !           283:                md_node(n);
        !           284:                n = n->next;
        !           285:        }
        !           286: }
        !           287:
        !           288: static void
        !           289: md_node(struct roff_node *n)
        !           290: {
        !           291:        const struct md_act     *act;
        !           292:        int                      cond, process_children;
        !           293:
        !           294:        if (n->flags & NODE_NOPRT)
        !           295:                return;
        !           296:
        !           297:        if (outflags & MD_nonl)
        !           298:                outflags &= ~(MD_nl | MD_sp);
        !           299:        else if (outflags & MD_spc && n->flags & NODE_LINE)
        !           300:                outflags |= MD_nl;
        !           301:
        !           302:        act = NULL;
        !           303:        cond = 0;
        !           304:        process_children = 1;
        !           305:        n->flags &= ~NODE_ENDED;
        !           306:
        !           307:        switch (n->type) {
        !           308:        case ROFFT_TEXT:
        !           309:                if (n->flags & NODE_DELIMC)
        !           310:                        outflags &= ~(MD_spc | MD_spc_force);
        !           311:                else if (outflags & MD_Sm)
        !           312:                        outflags |= MD_spc_force;
        !           313:                md_word(n->string);
        !           314:                if (n->flags & NODE_DELIMO)
        !           315:                        outflags &= ~(MD_spc | MD_spc_force);
        !           316:                else if (outflags & MD_Sm)
        !           317:                        outflags |= MD_spc;
        !           318:                break;
        !           319:        default:
        !           320:                act = md_acts + n->tok;
        !           321:                cond = act->cond == NULL || (*act->cond)(n);
        !           322:                if (cond && act->pre != NULL &&
        !           323:                    (n->end == ENDBODY_NOT || n->child != NULL))
        !           324:                        process_children = (*act->pre)(n);
        !           325:                break;
        !           326:        }
        !           327:
        !           328:        if (process_children && n->child != NULL)
        !           329:                md_nodelist(n->child);
        !           330:
        !           331:        if (n->flags & NODE_ENDED)
        !           332:                return;
        !           333:
        !           334:        if (cond && act->post != NULL)
        !           335:                (*act->post)(n);
        !           336:
        !           337:        if (n->end != ENDBODY_NOT)
        !           338:                n->body->flags |= NODE_ENDED;
        !           339: }
        !           340:
        !           341: static const char *
        !           342: md_stack(char c)
        !           343: {
        !           344:        static char     *stack;
        !           345:        static size_t    sz;
        !           346:        static size_t    cur;
        !           347:
        !           348:        switch (c) {
        !           349:        case '\0':
        !           350:                break;
        !           351:        case (char)-1:
        !           352:                assert(cur);
        !           353:                stack[--cur] = '\0';
        !           354:                break;
        !           355:        default:
        !           356:                if (cur + 1 >= sz) {
        !           357:                        sz += 8;
        !           358:                        stack = mandoc_realloc(stack, sz);
        !           359:                }
        !           360:                stack[cur] = c;
        !           361:                stack[++cur] = '\0';
        !           362:                break;
        !           363:        }
        !           364:        return stack == NULL ? "" : stack;
        !           365: }
        !           366:
        !           367: /*
        !           368:  * Handle vertical and horizontal spacing.
        !           369:  */
        !           370: static void
        !           371: md_preword(void)
        !           372: {
        !           373:        /*
        !           374:         * If a list block is nested inside a code block or a blockquote,
        !           375:         * blank lines for paragraph breaks no longer work; instead,
        !           376:         * they terminate the list.  Work around this markdown issue
        !           377:         * by using mere line breaks instead.
        !           378:         */
        !           379:        if (list_blocks && outflags & MD_sp) {
        !           380:                outflags &= ~MD_sp;
        !           381:                outflags |= MD_br;
        !           382:        }
        !           383:
        !           384:        /* End the old line if requested. */
        !           385:
        !           386:        if (outflags & MD_sp)
        !           387:                putchar('\n');
        !           388:        else if (outflags & MD_br) {
        !           389:                putchar(' ');
        !           390:                putchar(' ');
        !           391: #ifdef DEBUG
        !           392:                putchar(':');
        !           393:                putchar(':');
        !           394:                putchar(' ');
        !           395:                putchar(' ');
        !           396: #endif
        !           397:        }
        !           398:
        !           399:        /* Start a new line if necessary. */
        !           400:
        !           401:        if (outflags & (MD_nl | MD_br | MD_sp)) {
        !           402:                putchar('\n');
        !           403:                fputs(md_stack('\0'), stdout);
        !           404:                outflags &= ~(MD_nl | MD_br | MD_sp);
        !           405:                escflags = ESC_BOL;
        !           406:                outcount = 0;
        !           407:
        !           408:        /* Handle horizontal spacing. */
        !           409:
        !           410:        } else if (outflags & MD_spc) {
        !           411:                if (outflags & MD_Bk)
        !           412:                        fputs("&nbsp;", stdout);
        !           413:                else
        !           414:                        putchar(' ');
        !           415:                escflags &= ~ESC_FON;
        !           416:                outcount++;
        !           417:        }
        !           418:
        !           419:        outflags &= ~(MD_spc_force | MD_nonl);
        !           420:        if (outflags & MD_Sm)
        !           421:                outflags |= MD_spc;
        !           422:        else
        !           423:                outflags &= ~MD_spc;
        !           424: }
        !           425:
        !           426: /*
        !           427:  * Print markdown syntax elements.
        !           428:  * Can also be used for constant strings when neither escaping
        !           429:  * nor delimiter handling is required.
        !           430:  */
        !           431: static void
        !           432: md_rawword(const char *s)
        !           433: {
        !           434:        md_preword();
        !           435:
        !           436:        if (*s == 0)
        !           437:                return;
        !           438:
        !           439:        if (escflags & ESC_FON) {
        !           440:                escflags &= ~ESC_FON;
        !           441:                if (*s == '*' && !code_blocks)
        !           442:                        fputs("&zwnj;", stdout);
        !           443:        }
        !           444:
        !           445:        while (*s != '\0') {
        !           446:                switch(*s) {
        !           447:                case '(':
        !           448:                        escflags |= ESC_PAR;
        !           449:                        break;
        !           450:                case ')':
        !           451:                        escflags |= ~ESC_PAR;
        !           452:                        break;
        !           453:                case '*':
        !           454:                        if (s[1] == '\0')
        !           455:                                escflags |= ESC_FON;
        !           456:                        break;
        !           457:                case '[':
        !           458:                        escflags |= ESC_SQU;
        !           459:                        break;
        !           460:                case ']':
        !           461:                        escflags |= ESC_HYP;
        !           462:                        escflags &= ~ESC_SQU;
        !           463:                        break;
        !           464:                default:
        !           465:                        break;
        !           466:                }
        !           467:                md_char(*s++);
        !           468:        }
        !           469: }
        !           470:
        !           471: /*
        !           472:  * Print text and mdoc(7) syntax elements.
        !           473:  */
        !           474: static void
        !           475: md_word(const char *s)
        !           476: {
        !           477:        const char      *seq, *prevfont, *currfont, *nextfont;
        !           478:        char             c;
        !           479:        int              bs, sz, uc;
        !           480:
        !           481:        /* No spacing before closing delimiters. */
        !           482:        if (s[0] != '\0' && s[1] == '\0' &&
        !           483:            strchr("!),.:;?]", s[0]) != NULL &&
        !           484:            (outflags & MD_spc_force) == 0)
        !           485:                outflags &= ~MD_spc;
        !           486:
        !           487:        md_preword();
        !           488:
        !           489:        /* No spacing after opening delimiters. */
        !           490:        if ((s[0] == '(' || s[0] == '[') && s[1] == '\0')
        !           491:                outflags &= ~MD_spc;
        !           492:
        !           493:        prevfont = currfont = "";
        !           494:        while ((c = *s++) != '\0') {
        !           495:                bs = 0;
        !           496:                switch(c) {
        !           497:                case ASCII_NBRSP:
        !           498:                        if (code_blocks)
        !           499:                                c = ' ';
        !           500:                        else {
        !           501:                                md_named("nbsp");
        !           502:                                c = '\0';
        !           503:                        }
        !           504:                        break;
        !           505:                case ASCII_HYPH:
        !           506:                        bs = escflags & ESC_BOL && !code_blocks;
        !           507:                        c = '-';
        !           508:                        break;
        !           509:                case ASCII_BREAK:
        !           510:                        continue;
        !           511:                case '#':
        !           512:                case '+':
        !           513:                case '-':
        !           514:                        bs = escflags & ESC_BOL && !code_blocks;
        !           515:                        break;
        !           516:                case '(':
        !           517:                        bs = escflags & ESC_HYP && !code_blocks;
        !           518:                        break;
        !           519:                case ')':
        !           520:                        bs = escflags & ESC_PAR && !code_blocks;
        !           521:                        break;
        !           522:                case '*':
        !           523:                case '[':
        !           524:                case '_':
        !           525:                case '`':
        !           526:                        bs = !code_blocks;
        !           527:                        break;
        !           528:                case '.':
        !           529:                        bs = escflags & ESC_NUM && !code_blocks;
        !           530:                        break;
        !           531:                case '<':
        !           532:                        if (code_blocks == 0) {
        !           533:                                md_named("lt");
        !           534:                                c = '\0';
        !           535:                        }
        !           536:                        break;
        !           537:                case '=':
        !           538:                        if (escflags & ESC_BOL && !code_blocks) {
        !           539:                                md_named("equals");
        !           540:                                c = '\0';
        !           541:                        }
        !           542:                        break;
        !           543:                case '>':
        !           544:                        if (code_blocks == 0) {
        !           545:                                md_named("gt");
        !           546:                                c = '\0';
        !           547:                        }
        !           548:                        break;
        !           549:                case '\\':
        !           550:                        uc = 0;
        !           551:                        nextfont = NULL;
        !           552:                        switch (mandoc_escape(&s, &seq, &sz)) {
        !           553:                        case ESCAPE_UNICODE:
        !           554:                                uc = mchars_num2uc(seq + 1, sz - 1);
        !           555:                                break;
        !           556:                        case ESCAPE_NUMBERED:
        !           557:                                uc = mchars_num2char(seq, sz);
        !           558:                                break;
        !           559:                        case ESCAPE_SPECIAL:
        !           560:                                uc = mchars_spec2cp(seq, sz);
        !           561:                                break;
        !           562:                        case ESCAPE_FONTBOLD:
        !           563:                                nextfont = "**";
        !           564:                                break;
        !           565:                        case ESCAPE_FONTITALIC:
        !           566:                                nextfont = "*";
        !           567:                                break;
        !           568:                        case ESCAPE_FONTBI:
        !           569:                                nextfont = "***";
        !           570:                                break;
        !           571:                        case ESCAPE_FONT:
        !           572:                        case ESCAPE_FONTROMAN:
        !           573:                                nextfont = "";
        !           574:                                break;
        !           575:                        case ESCAPE_FONTPREV:
        !           576:                                nextfont = prevfont;
        !           577:                                break;
        !           578:                        case ESCAPE_NOSPACE:
        !           579:                        case ESCAPE_SKIPCHAR:
        !           580:                        case ESCAPE_OVERSTRIKE:
        !           581:                                /* XXX not implemented */
        !           582:                                /* FALLTHROUGH */
        !           583:                        case ESCAPE_ERROR:
        !           584:                        default:
        !           585:                                break;
        !           586:                        }
        !           587:                        if (nextfont != NULL && !code_blocks) {
        !           588:                                if (*currfont != '\0') {
        !           589:                                        outflags &= ~MD_spc;
        !           590:                                        md_rawword(currfont);
        !           591:                                }
        !           592:                                prevfont = currfont;
        !           593:                                currfont = nextfont;
        !           594:                                if (*currfont != '\0') {
        !           595:                                        outflags &= ~MD_spc;
        !           596:                                        md_rawword(currfont);
        !           597:                                }
        !           598:                        }
        !           599:                        if (uc) {
        !           600:                                if ((uc < 0x20 && uc != 0x09) ||
        !           601:                                    (uc > 0x7E && uc < 0xA0))
        !           602:                                        uc = 0xFFFD;
        !           603:                                if (code_blocks) {
        !           604:                                        seq = mchars_uc2str(uc);
        !           605:                                        fputs(seq, stdout);
        !           606:                                        outcount += strlen(seq);
        !           607:                                } else {
        !           608:                                        printf("&#%d;", uc);
        !           609:                                        outcount++;
        !           610:                                }
        !           611:                                escflags &= ~ESC_FON;
        !           612:                        }
        !           613:                        c = '\0';
        !           614:                        break;
        !           615:                case ']':
        !           616:                        bs = escflags & ESC_SQU && !code_blocks;
        !           617:                        escflags |= ESC_HYP;
        !           618:                        break;
        !           619:                default:
        !           620:                        break;
        !           621:                }
        !           622:                if (bs)
        !           623:                        putchar('\\');
        !           624:                md_char(c);
        !           625:        }
        !           626:        if (*currfont != '\0') {
        !           627:                outflags &= ~MD_spc;
        !           628:                md_rawword(currfont);
        !           629:        }
        !           630: }
        !           631:
        !           632: /*
        !           633:  * Print a single HTML named character reference.
        !           634:  */
        !           635: static void
        !           636: md_named(const char *s)
        !           637: {
        !           638:        printf("&%s;", s);
        !           639:        escflags &= ~ESC_FON;
        !           640:        outcount++;
        !           641: }
        !           642:
        !           643: /*
        !           644:  * Print a single raw character and maintain certain escape flags.
        !           645:  */
        !           646: static void
        !           647: md_char(unsigned char c)
        !           648: {
        !           649:        if (c != '\0') {
        !           650:                putchar(c);
        !           651:                if (c == '*')
        !           652:                        escflags |= ESC_FON;
        !           653:                else
        !           654:                        escflags &= ~ESC_FON;
        !           655:                outcount++;
        !           656:        }
        !           657:        if (c != ']')
        !           658:                escflags &= ~ESC_HYP;
        !           659:        if (c == ' ' || c == '\t' || c == '>')
        !           660:                return;
        !           661:        if (isdigit(c) == 0)
        !           662:                escflags &= ~ESC_NUM;
        !           663:        else if (escflags & ESC_BOL)
        !           664:                escflags |= ESC_NUM;
        !           665:        escflags &= ~ESC_BOL;
        !           666: }
        !           667:
        !           668: static int
        !           669: md_cond_head(struct roff_node *n)
        !           670: {
        !           671:        return n->type == ROFFT_HEAD;
        !           672: }
        !           673:
        !           674: static int
        !           675: md_cond_body(struct roff_node *n)
        !           676: {
        !           677:        return n->type == ROFFT_BODY;
        !           678: }
        !           679:
        !           680: static int
        !           681: md_pre_raw(struct roff_node *n)
        !           682: {
        !           683:        const char      *prefix;
        !           684:
        !           685:        if ((prefix = md_acts[n->tok].prefix) != NULL) {
        !           686:                md_rawword(prefix);
        !           687:                outflags &= ~MD_spc;
        !           688:        }
        !           689:        return 1;
        !           690: }
        !           691:
        !           692: static void
        !           693: md_post_raw(struct roff_node *n)
        !           694: {
        !           695:        const char      *suffix;
        !           696:
        !           697:        if ((suffix = md_acts[n->tok].suffix) != NULL) {
        !           698:                outflags &= ~(MD_spc | MD_nl);
        !           699:                md_rawword(suffix);
        !           700:        }
        !           701: }
        !           702:
        !           703: static int
        !           704: md_pre_word(struct roff_node *n)
        !           705: {
        !           706:        const char      *prefix;
        !           707:
        !           708:        if ((prefix = md_acts[n->tok].prefix) != NULL) {
        !           709:                md_word(prefix);
        !           710:                outflags &= ~MD_spc;
        !           711:        }
        !           712:        return 1;
        !           713: }
        !           714:
        !           715: static void
        !           716: md_post_word(struct roff_node *n)
        !           717: {
        !           718:        const char      *suffix;
        !           719:
        !           720:        if ((suffix = md_acts[n->tok].suffix) != NULL) {
        !           721:                outflags &= ~(MD_spc | MD_nl);
        !           722:                md_word(suffix);
        !           723:        }
        !           724: }
        !           725:
        !           726: static void
        !           727: md_post_pc(struct roff_node *n)
        !           728: {
        !           729:        md_post_raw(n);
        !           730:        if (n->parent->tok != MDOC_Rs)
        !           731:                return;
        !           732:        if (n->next != NULL) {
        !           733:                md_word(",");
        !           734:                if (n->prev != NULL &&
        !           735:                    n->prev->tok == n->tok &&
        !           736:                    n->next->tok == n->tok)
        !           737:                        md_word("and");
        !           738:        } else {
        !           739:                md_word(".");
        !           740:                outflags |= MD_nl;
        !           741:        }
        !           742: }
        !           743:
        !           744: static int
        !           745: md_pre_skip(struct roff_node *n)
        !           746: {
        !           747:        return 0;
        !           748: }
        !           749:
        !           750: static void
        !           751: md_pre_syn(struct roff_node *n)
        !           752: {
        !           753:        if (n->prev == NULL || ! (n->flags & NODE_SYNPRETTY))
        !           754:                return;
        !           755:
        !           756:        if (n->prev->tok == n->tok &&
        !           757:            n->tok != MDOC_Ft &&
        !           758:            n->tok != MDOC_Fo &&
        !           759:            n->tok != MDOC_Fn) {
        !           760:                outflags |= MD_br;
        !           761:                return;
        !           762:        }
        !           763:
        !           764:        switch (n->prev->tok) {
        !           765:        case MDOC_Fd:
        !           766:        case MDOC_Fn:
        !           767:        case MDOC_Fo:
        !           768:        case MDOC_In:
        !           769:        case MDOC_Vt:
        !           770:                outflags |= MD_sp;
        !           771:                break;
        !           772:        case MDOC_Ft:
        !           773:                if (n->tok != MDOC_Fn && n->tok != MDOC_Fo) {
        !           774:                        outflags |= MD_sp;
        !           775:                        break;
        !           776:                }
        !           777:                /* FALLTHROUGH */
        !           778:        default:
        !           779:                outflags |= MD_br;
        !           780:                break;
        !           781:        }
        !           782: }
        !           783:
        !           784: static int
        !           785: md_pre_Ap(struct roff_node *n)
        !           786: {
        !           787:        outflags &= ~MD_spc;
        !           788:        md_word("'");
        !           789:        outflags &= ~MD_spc;
        !           790:        return 0;
        !           791: }
        !           792:
        !           793: static int
        !           794: md_pre_Bd(struct roff_node *n)
        !           795: {
        !           796:        switch (n->norm->Bd.type) {
        !           797:        case DISP_unfilled:
        !           798:        case DISP_literal:
        !           799:                return md_pre_Dl(n);
        !           800:        default:
        !           801:                return md_pre_D1(n);
        !           802:        }
        !           803: }
        !           804:
        !           805: static int
        !           806: md_pre_Bk(struct roff_node *n)
        !           807: {
        !           808:        switch (n->type) {
        !           809:        case ROFFT_BLOCK:
        !           810:                return 1;
        !           811:        case ROFFT_BODY:
        !           812:                outflags |= MD_Bk;
        !           813:                return 1;
        !           814:        default:
        !           815:                return 0;
        !           816:        }
        !           817: }
        !           818:
        !           819: static void
        !           820: md_post_Bk(struct roff_node *n)
        !           821: {
        !           822:        if (n->type == ROFFT_BODY)
        !           823:                outflags &= ~MD_Bk;
        !           824: }
        !           825:
        !           826: static int
        !           827: md_pre_Bl(struct roff_node *n)
        !           828: {
        !           829:        n->norm->Bl.count = 0;
        !           830:        if (n->norm->Bl.type == LIST_column)
        !           831:                md_pre_Dl(n);
        !           832:        outflags |= MD_sp;
        !           833:        return 1;
        !           834: }
        !           835:
        !           836: static void
        !           837: md_post_Bl(struct roff_node *n)
        !           838: {
        !           839:        n->norm->Bl.count = 0;
        !           840:        if (n->norm->Bl.type == LIST_column)
        !           841:                md_post_D1(n);
        !           842:        outflags |= MD_sp;
        !           843: }
        !           844:
        !           845: static int
        !           846: md_pre_D1(struct roff_node *n)
        !           847: {
        !           848:        /*
        !           849:         * Markdown blockquote syntax does not work inside code blocks.
        !           850:         * The best we can do is fall back to another nested code block.
        !           851:         */
        !           852:        if (code_blocks) {
        !           853:                md_stack('\t');
        !           854:                code_blocks++;
        !           855:        } else {
        !           856:                md_stack('>');
        !           857:                quote_blocks++;
        !           858:        }
        !           859:        outflags |= MD_sp;
        !           860:        return 1;
        !           861: }
        !           862:
        !           863: static void
        !           864: md_post_D1(struct roff_node *n)
        !           865: {
        !           866:        md_stack((char)-1);
        !           867:        if (code_blocks)
        !           868:                code_blocks--;
        !           869:        else
        !           870:                quote_blocks--;
        !           871:        outflags |= MD_sp;
        !           872: }
        !           873:
        !           874: static int
        !           875: md_pre_Dl(struct roff_node *n)
        !           876: {
        !           877:        /*
        !           878:         * Markdown code block syntax does not work inside blockquotes.
        !           879:         * The best we can do is fall back to another nested blockquote.
        !           880:         */
        !           881:        if (quote_blocks) {
        !           882:                md_stack('>');
        !           883:                quote_blocks++;
        !           884:        } else {
        !           885:                md_stack('\t');
        !           886:                code_blocks++;
        !           887:        }
        !           888:        outflags |= MD_sp;
        !           889:        return 1;
        !           890: }
        !           891:
        !           892: static int
        !           893: md_pre_En(struct roff_node *n)
        !           894: {
        !           895:        if (n->norm->Es == NULL ||
        !           896:            n->norm->Es->child == NULL)
        !           897:                return 1;
        !           898:
        !           899:        md_word(n->norm->Es->child->string);
        !           900:        outflags &= ~MD_spc;
        !           901:        return 1;
        !           902: }
        !           903:
        !           904: static void
        !           905: md_post_En(struct roff_node *n)
        !           906: {
        !           907:        if (n->norm->Es == NULL ||
        !           908:            n->norm->Es->child == NULL ||
        !           909:            n->norm->Es->child->next == NULL)
        !           910:                return;
        !           911:
        !           912:        outflags &= ~MD_spc;
        !           913:        md_word(n->norm->Es->child->next->string);
        !           914: }
        !           915:
        !           916: static int
        !           917: md_pre_Eo(struct roff_node *n)
        !           918: {
        !           919:        if (n->end == ENDBODY_NOT &&
        !           920:            n->parent->head->child == NULL &&
        !           921:            n->child != NULL &&
        !           922:            n->child->end != ENDBODY_NOT)
        !           923:                md_preword();
        !           924:        else if (n->end != ENDBODY_NOT ? n->child != NULL :
        !           925:            n->parent->head->child != NULL && (n->child != NULL ||
        !           926:            (n->parent->tail != NULL && n->parent->tail->child != NULL)))
        !           927:                outflags &= ~(MD_spc | MD_nl);
        !           928:        return 1;
        !           929: }
        !           930:
        !           931: static void
        !           932: md_post_Eo(struct roff_node *n)
        !           933: {
        !           934:        int      body, tail;
        !           935:
        !           936:        if (n->end != ENDBODY_NOT) {
        !           937:                outflags |= MD_spc;
        !           938:                return;
        !           939:        }
        !           940:
        !           941:        body = n->child != NULL || n->parent->head->child != NULL;
        !           942:        tail = n->parent->tail != NULL && n->parent->tail->child != NULL;
        !           943:
        !           944:        if (body && tail)
        !           945:                outflags &= ~MD_spc;
        !           946:         else if ( ! (body || tail))
        !           947:                md_preword();
        !           948:         else if ( ! tail)
        !           949:                outflags |= MD_spc;
        !           950: }
        !           951:
        !           952: static int
        !           953: md_pre_Fa(struct roff_node *n)
        !           954: {
        !           955:        int      am_Fa;
        !           956:
        !           957:        am_Fa = n->tok == MDOC_Fa;
        !           958:
        !           959:        if (am_Fa)
        !           960:                n = n->child;
        !           961:
        !           962:        while (n != NULL) {
        !           963:                md_rawword("*");
        !           964:                outflags &= ~MD_spc;
        !           965:                md_node(n);
        !           966:                outflags &= ~MD_spc;
        !           967:                md_rawword("*");
        !           968:                if ((n = n->next) != NULL)
        !           969:                        md_word(",");
        !           970:        }
        !           971:        return 0;
        !           972: }
        !           973:
        !           974: static void
        !           975: md_post_Fa(struct roff_node *n)
        !           976: {
        !           977:        if (n->next != NULL && n->next->tok == MDOC_Fa)
        !           978:                md_word(",");
        !           979: }
        !           980:
        !           981: static int
        !           982: md_pre_Fd(struct roff_node *n)
        !           983: {
        !           984:        md_pre_syn(n);
        !           985:        md_pre_raw(n);
        !           986:        return 1;
        !           987: }
        !           988:
        !           989: static void
        !           990: md_post_Fd(struct roff_node *n)
        !           991: {
        !           992:        md_post_raw(n);
        !           993:        outflags |= MD_br;
        !           994: }
        !           995:
        !           996: static int
        !           997: md_pre_Fn(struct roff_node *n)
        !           998: {
        !           999:        md_pre_syn(n);
        !          1000:
        !          1001:        if ((n = n->child) == NULL)
        !          1002:                return 0;
        !          1003:
        !          1004:        md_rawword("**");
        !          1005:        outflags &= ~MD_spc;
        !          1006:        md_node(n);
        !          1007:        outflags &= ~MD_spc;
        !          1008:        md_rawword("**");
        !          1009:        outflags &= ~MD_spc;
        !          1010:        md_word("(");
        !          1011:
        !          1012:        if ((n = n->next) != NULL)
        !          1013:                md_pre_Fa(n);
        !          1014:        return 0;
        !          1015: }
        !          1016:
        !          1017: static void
        !          1018: md_post_Fn(struct roff_node *n)
        !          1019: {
        !          1020:        md_word(")");
        !          1021:        if (n->flags & NODE_SYNPRETTY) {
        !          1022:                md_word(";");
        !          1023:                outflags |= MD_sp;
        !          1024:        }
        !          1025: }
        !          1026:
        !          1027: static int
        !          1028: md_pre_Fo(struct roff_node *n)
        !          1029: {
        !          1030:        switch (n->type) {
        !          1031:        case ROFFT_BLOCK:
        !          1032:                md_pre_syn(n);
        !          1033:                break;
        !          1034:        case ROFFT_HEAD:
        !          1035:                if (n->child == NULL)
        !          1036:                        return 0;
        !          1037:                md_pre_raw(n);
        !          1038:                break;
        !          1039:        case ROFFT_BODY:
        !          1040:                outflags &= ~(MD_spc | MD_nl);
        !          1041:                md_word("(");
        !          1042:                break;
        !          1043:        default:
        !          1044:                break;
        !          1045:        }
        !          1046:        return 1;
        !          1047: }
        !          1048:
        !          1049: static void
        !          1050: md_post_Fo(struct roff_node *n)
        !          1051: {
        !          1052:        switch (n->type) {
        !          1053:        case ROFFT_HEAD:
        !          1054:                if (n->child != NULL)
        !          1055:                        md_post_raw(n);
        !          1056:                break;
        !          1057:        case ROFFT_BODY:
        !          1058:                md_post_Fn(n);
        !          1059:                break;
        !          1060:        default:
        !          1061:                break;
        !          1062:        }
        !          1063: }
        !          1064:
        !          1065: static int
        !          1066: md_pre_In(struct roff_node *n)
        !          1067: {
        !          1068:        if (n->flags & NODE_SYNPRETTY) {
        !          1069:                md_pre_syn(n);
        !          1070:                md_pre_raw(n);
        !          1071:                md_rawword("*");
        !          1072:                outflags &= ~MD_spc;
        !          1073:                md_word("#include <");
        !          1074:                outflags &= ~MD_spc;
        !          1075:        } else {
        !          1076:                md_word("<");
        !          1077:                outflags &= ~MD_spc;
        !          1078:                md_pre_raw(n);
        !          1079:        }
        !          1080:        return 1;
        !          1081: }
        !          1082:
        !          1083: static void
        !          1084: md_post_In(struct roff_node *n)
        !          1085: {
        !          1086:        if (n->flags & NODE_SYNPRETTY) {
        !          1087:                outflags &= ~MD_spc;
        !          1088:                md_rawword(">*");
        !          1089:                md_post_raw(n);
        !          1090:                outflags |= MD_nl;
        !          1091:        } else {
        !          1092:                md_post_raw(n);
        !          1093:                outflags &= ~MD_spc;
        !          1094:                md_rawword(">");
        !          1095:        }
        !          1096: }
        !          1097:
        !          1098: static int
        !          1099: md_pre_It(struct roff_node *n)
        !          1100: {
        !          1101:        struct roff_node        *bln;
        !          1102:
        !          1103:        switch (n->type) {
        !          1104:        case ROFFT_BLOCK:
        !          1105:                return 1;
        !          1106:
        !          1107:        case ROFFT_HEAD:
        !          1108:                bln = n->parent->parent;
        !          1109:                if (bln->norm->Bl.comp == 0)
        !          1110:                        outflags |= MD_sp;
        !          1111:                outflags |= MD_nl;
        !          1112:
        !          1113:                switch (bln->norm->Bl.type) {
        !          1114:                case LIST_item:
        !          1115:                        outflags |= MD_br;
        !          1116:                        return 0;
        !          1117:                case LIST_inset:
        !          1118:                case LIST_diag:
        !          1119:                case LIST_ohang:
        !          1120:                        outflags |= MD_br;
        !          1121:                        return 1;
        !          1122:                case LIST_tag:
        !          1123:                case LIST_hang:
        !          1124:                        outflags |= MD_sp;
        !          1125:                        return 1;
        !          1126:                case LIST_bullet:
        !          1127:                        md_rawword("*\t");
        !          1128:                        break;
        !          1129:                case LIST_dash:
        !          1130:                case LIST_hyphen:
        !          1131:                        md_rawword("-\t");
        !          1132:                        break;
        !          1133:                case LIST_enum:
        !          1134:                        md_preword();
        !          1135:                        printf("%d.\t", ++bln->norm->Bl.count);
        !          1136:                        escflags &= ~ESC_FON;
        !          1137:                        break;
        !          1138:                default:
        !          1139:                        return 0;
        !          1140:                }
        !          1141:                outflags &= ~MD_spc;
        !          1142:                outflags |= MD_nonl;
        !          1143:                outcount = 0;
        !          1144:                md_stack('\t');
        !          1145:                if (code_blocks || quote_blocks)
        !          1146:                        list_blocks++;
        !          1147:                return 0;
        !          1148:
        !          1149:        case ROFFT_BODY:
        !          1150:                bln = n->parent->parent;
        !          1151:                switch (bln->norm->Bl.type) {
        !          1152:                case LIST_ohang:
        !          1153:                        outflags |= MD_br;
        !          1154:                        break;
        !          1155:                case LIST_tag:
        !          1156:                case LIST_hang:
        !          1157:                        md_pre_D1(n);
        !          1158:                        break;
        !          1159:                default:
        !          1160:                        break;
        !          1161:                }
        !          1162:                return 1;
        !          1163:
        !          1164:        default:
        !          1165:                return 0;
        !          1166:        }
        !          1167: }
        !          1168:
        !          1169: static void
        !          1170: md_post_It(struct roff_node *n)
        !          1171: {
        !          1172:        struct roff_node        *bln;
        !          1173:        int                      i, nc;
        !          1174:
        !          1175:        if (n->type != ROFFT_BODY)
        !          1176:                return;
        !          1177:
        !          1178:        bln = n->parent->parent;
        !          1179:        switch (bln->norm->Bl.type) {
        !          1180:        case LIST_bullet:
        !          1181:        case LIST_dash:
        !          1182:        case LIST_hyphen:
        !          1183:        case LIST_enum:
        !          1184:                md_stack((char)-1);
        !          1185:                if (code_blocks || quote_blocks)
        !          1186:                        list_blocks--;
        !          1187:                break;
        !          1188:        case LIST_tag:
        !          1189:        case LIST_hang:
        !          1190:                md_post_D1(n);
        !          1191:                break;
        !          1192:
        !          1193:        case LIST_column:
        !          1194:                if (n->next == NULL)
        !          1195:                        break;
        !          1196:
        !          1197:                /* Calculate the array index of the current column. */
        !          1198:
        !          1199:                i = 0;
        !          1200:                while ((n = n->prev) != NULL && n->type != ROFFT_HEAD)
        !          1201:                        i++;
        !          1202:
        !          1203:                /*
        !          1204:                 * If a width was specified for this column,
        !          1205:                 * subtract what printed, and
        !          1206:                 * add the same spacing as in mdoc_term.c.
        !          1207:                 */
        !          1208:
        !          1209:                nc = bln->norm->Bl.ncols;
        !          1210:                i = i < nc ? strlen(bln->norm->Bl.cols[i]) - outcount +
        !          1211:                    (nc < 5 ? 4 : nc == 5 ? 3 : 1) : 1;
        !          1212:                if (i < 1)
        !          1213:                        i = 1;
        !          1214:                while (i-- > 0)
        !          1215:                        putchar(' ');
        !          1216:
        !          1217:                outflags &= ~MD_spc;
        !          1218:                escflags &= ~ESC_FON;
        !          1219:                outcount = 0;
        !          1220:                break;
        !          1221:
        !          1222:        default:
        !          1223:                break;
        !          1224:        }
        !          1225: }
        !          1226:
        !          1227: static void
        !          1228: md_post_Lb(struct roff_node *n)
        !          1229: {
        !          1230:        if (n->sec == SEC_LIBRARY)
        !          1231:                outflags |= MD_br;
        !          1232: }
        !          1233:
        !          1234: static int
        !          1235: md_pre_Lk(struct roff_node *n)
        !          1236: {
        !          1237:        const struct roff_node *link, *descr;
        !          1238:
        !          1239:        if ((link = n->child) == NULL)
        !          1240:                return 0;
        !          1241:
        !          1242:        if ((descr = link->next) != NULL) {
        !          1243:                md_rawword("[");
        !          1244:                outflags &= ~MD_spc;
        !          1245:                while (descr != NULL) {
        !          1246:                        md_word(descr->string);
        !          1247:                        descr = descr->next;
        !          1248:                }
        !          1249:                outflags &= ~MD_spc;
        !          1250:                md_rawword("](");
        !          1251:        } else
        !          1252:                md_rawword("<");
        !          1253:
        !          1254:        outflags &= ~MD_spc;
        !          1255:        md_word(link->string);
        !          1256:        outflags &= ~MD_spc;
        !          1257:        md_rawword(link->next == NULL ? ">" : ")");
        !          1258:        return 0;
        !          1259: }
        !          1260:
        !          1261: static int
        !          1262: md_pre_Nd(struct roff_node *n)
        !          1263: {
        !          1264:        outflags &= ~MD_nl;
        !          1265:        outflags |= MD_spc;
        !          1266:        md_word("-");
        !          1267:        return 1;
        !          1268: }
        !          1269:
        !          1270: static int
        !          1271: md_pre_Nm(struct roff_node *n)
        !          1272: {
        !          1273:        switch (n->type) {
        !          1274:        case ROFFT_BLOCK:
        !          1275:                outflags |= MD_Bk;
        !          1276:                md_pre_syn(n);
        !          1277:                break;
        !          1278:        case ROFFT_HEAD:
        !          1279:        case ROFFT_ELEM:
        !          1280:                md_pre_raw(n);
        !          1281:                break;
        !          1282:        default:
        !          1283:                break;
        !          1284:        }
        !          1285:        return 1;
        !          1286: }
        !          1287:
        !          1288: static void
        !          1289: md_post_Nm(struct roff_node *n)
        !          1290: {
        !          1291:        switch (n->type) {
        !          1292:        case ROFFT_BLOCK:
        !          1293:                outflags &= ~MD_Bk;
        !          1294:                break;
        !          1295:        case ROFFT_HEAD:
        !          1296:        case ROFFT_ELEM:
        !          1297:                md_post_raw(n);
        !          1298:                break;
        !          1299:        default:
        !          1300:                break;
        !          1301:        }
        !          1302: }
        !          1303:
        !          1304: static int
        !          1305: md_pre_No(struct roff_node *n)
        !          1306: {
        !          1307:        outflags |= MD_spc_force;
        !          1308:        return 1;
        !          1309: }
        !          1310:
        !          1311: static int
        !          1312: md_pre_Ns(struct roff_node *n)
        !          1313: {
        !          1314:        outflags &= ~MD_spc;
        !          1315:        return 0;
        !          1316: }
        !          1317:
        !          1318: static void
        !          1319: md_post_Pf(struct roff_node *n)
        !          1320: {
        !          1321:        if (n->next != NULL && (n->next->flags & NODE_LINE) == 0)
        !          1322:                outflags &= ~MD_spc;
        !          1323: }
        !          1324:
        !          1325: static int
        !          1326: md_pre_Pp(struct roff_node *n)
        !          1327: {
        !          1328:        outflags |= MD_sp;
        !          1329:        return 0;
        !          1330: }
        !          1331:
        !          1332: static int
        !          1333: md_pre_Rs(struct roff_node *n)
        !          1334: {
        !          1335:        if (n->sec == SEC_SEE_ALSO)
        !          1336:                outflags |= MD_sp;
        !          1337:        return 1;
        !          1338: }
        !          1339:
        !          1340: static int
        !          1341: md_pre_Sh(struct roff_node *n)
        !          1342: {
        !          1343:        switch (n->type) {
        !          1344:        case ROFFT_HEAD:
        !          1345:                outflags |= MD_sp;
        !          1346:                md_rawword(n->tok == MDOC_Sh ? "#" : "##");
        !          1347:                break;
        !          1348:        case ROFFT_BODY:
        !          1349:                outflags |= MD_sp;
        !          1350:                break;
        !          1351:        default:
        !          1352:                break;
        !          1353:        }
        !          1354:        return 1;
        !          1355: }
        !          1356:
        !          1357: static int
        !          1358: md_pre_Sm(struct roff_node *n)
        !          1359: {
        !          1360:        if (n->child == NULL)
        !          1361:                outflags ^= MD_Sm;
        !          1362:        else if (strcmp("on", n->child->string) == 0)
        !          1363:                outflags |= MD_Sm;
        !          1364:        else
        !          1365:                outflags &= ~MD_Sm;
        !          1366:
        !          1367:        if (outflags & MD_Sm)
        !          1368:                outflags |= MD_spc;
        !          1369:
        !          1370:        return 0;
        !          1371: }
        !          1372:
        !          1373: static int
        !          1374: md_pre_Vt(struct roff_node *n)
        !          1375: {
        !          1376:        switch (n->type) {
        !          1377:        case ROFFT_BLOCK:
        !          1378:                md_pre_syn(n);
        !          1379:                return 1;
        !          1380:        case ROFFT_BODY:
        !          1381:        case ROFFT_ELEM:
        !          1382:                md_pre_raw(n);
        !          1383:                return 1;
        !          1384:        default:
        !          1385:                return 0;
        !          1386:        }
        !          1387: }
        !          1388:
        !          1389: static void
        !          1390: md_post_Vt(struct roff_node *n)
        !          1391: {
        !          1392:        switch (n->type) {
        !          1393:        case ROFFT_BODY:
        !          1394:        case ROFFT_ELEM:
        !          1395:                md_post_raw(n);
        !          1396:                break;
        !          1397:        default:
        !          1398:                break;
        !          1399:        }
        !          1400: }
        !          1401:
        !          1402: static int
        !          1403: md_pre_Xr(struct roff_node *n)
        !          1404: {
        !          1405:        n = n->child;
        !          1406:        if (n == NULL)
        !          1407:                return 0;
        !          1408:        md_node(n);
        !          1409:        n = n->next;
        !          1410:        if (n == NULL)
        !          1411:                return 0;
        !          1412:        outflags &= ~MD_spc;
        !          1413:        md_word("(");
        !          1414:        md_node(n);
        !          1415:        md_word(")");
        !          1416:        return 0;
        !          1417: }
        !          1418:
        !          1419: static int
        !          1420: md_pre__T(struct roff_node *n)
        !          1421: {
        !          1422:        if (n->parent != NULL && n->parent->tok == MDOC_Rs &&
        !          1423:            n->parent->norm->Rs.quote_T)
        !          1424:                md_word("\"");
        !          1425:        else
        !          1426:                md_rawword("*");
        !          1427:        outflags &= ~MD_spc;
        !          1428:        return 1;
        !          1429: }
        !          1430:
        !          1431: static void
        !          1432: md_post__T(struct roff_node *n)
        !          1433: {
        !          1434:        outflags &= ~MD_spc;
        !          1435:        if (n->parent != NULL && n->parent->tok == MDOC_Rs &&
        !          1436:            n->parent->norm->Rs.quote_T)
        !          1437:                md_word("\"");
        !          1438:        else
        !          1439:                md_rawword("*");
        !          1440:        md_post_pc(n);
        !          1441: }
        !          1442:
        !          1443: static int
        !          1444: md_pre_br(struct roff_node *n)
        !          1445: {
        !          1446:        outflags |= MD_br;
        !          1447:        return 0;
        !          1448: }

CVSweb