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

Annotation of mandoc/mdoc_markdown.c, Revision 1.40

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

CVSweb