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

Annotation of mandoc/mdoc_markdown.c, Revision 1.33

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

CVSweb