1*bcf2ba68Sschwarze /* $OpenBSD: mdoc_markdown.c,v 1.38 2025/01/20 00:52:00 schwarze Exp $ */ 2b3257404Sschwarze /* 3*bcf2ba68Sschwarze * Copyright (c) 2017, 2018, 2020, 2025 Ingo Schwarze <schwarze@openbsd.org> 4b3257404Sschwarze * 5b3257404Sschwarze * Permission to use, copy, modify, and distribute this software for any 6b3257404Sschwarze * purpose with or without fee is hereby granted, provided that the above 7b3257404Sschwarze * copyright notice and this permission notice appear in all copies. 8b3257404Sschwarze * 9b3257404Sschwarze * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 10b3257404Sschwarze * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11b3257404Sschwarze * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 12b3257404Sschwarze * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13b3257404Sschwarze * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14b3257404Sschwarze * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15b3257404Sschwarze * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 166a6803e4Sschwarze * 176a6803e4Sschwarze * Markdown formatter for mdoc(7) used by mandoc(1). 18b3257404Sschwarze */ 19b3257404Sschwarze #include <sys/types.h> 20b3257404Sschwarze 21b3257404Sschwarze #include <assert.h> 22b3257404Sschwarze #include <ctype.h> 23b3257404Sschwarze #include <stdio.h> 247c539ecbSschwarze #include <stdlib.h> 25b3257404Sschwarze #include <string.h> 26b3257404Sschwarze 27b3257404Sschwarze #include "mandoc_aux.h" 28b3257404Sschwarze #include "mandoc.h" 29b3257404Sschwarze #include "roff.h" 30b3257404Sschwarze #include "mdoc.h" 31b3257404Sschwarze #include "main.h" 32b3257404Sschwarze 33b3257404Sschwarze struct md_act { 346a6803e4Sschwarze int (*cond)(struct roff_node *); 356a6803e4Sschwarze int (*pre)(struct roff_node *); 366a6803e4Sschwarze void (*post)(struct roff_node *); 37b3257404Sschwarze const char *prefix; /* pre-node string constant */ 38b3257404Sschwarze const char *suffix; /* post-node string constant */ 39b3257404Sschwarze }; 40b3257404Sschwarze 41b3257404Sschwarze static void md_nodelist(struct roff_node *); 42b3257404Sschwarze static void md_node(struct roff_node *); 436a6803e4Sschwarze static const char *md_stack(char); 44b3257404Sschwarze static void md_preword(void); 45b3257404Sschwarze static void md_rawword(const char *); 46b3257404Sschwarze static void md_word(const char *); 47b3257404Sschwarze static void md_named(const char *); 48b3257404Sschwarze static void md_char(unsigned char); 4975d8c96aSschwarze static void md_uri(const char *); 50b3257404Sschwarze 51b3257404Sschwarze static int md_cond_head(struct roff_node *); 52b3257404Sschwarze static int md_cond_body(struct roff_node *); 53b3257404Sschwarze 547c539ecbSschwarze static int md_pre_abort(struct roff_node *); 55b3257404Sschwarze static int md_pre_raw(struct roff_node *); 56b3257404Sschwarze static int md_pre_word(struct roff_node *); 57b3257404Sschwarze static int md_pre_skip(struct roff_node *); 58b3257404Sschwarze static void md_pre_syn(struct roff_node *); 592fe9aa39Sschwarze static int md_pre_An(struct roff_node *); 60b3257404Sschwarze static int md_pre_Ap(struct roff_node *); 61b3257404Sschwarze static int md_pre_Bd(struct roff_node *); 62b3257404Sschwarze static int md_pre_Bk(struct roff_node *); 63b3257404Sschwarze static int md_pre_Bl(struct roff_node *); 64b3257404Sschwarze static int md_pre_D1(struct roff_node *); 65b3257404Sschwarze static int md_pre_Dl(struct roff_node *); 66b3257404Sschwarze static int md_pre_En(struct roff_node *); 67b3257404Sschwarze static int md_pre_Eo(struct roff_node *); 68b3257404Sschwarze static int md_pre_Fa(struct roff_node *); 69b3257404Sschwarze static int md_pre_Fd(struct roff_node *); 70b3257404Sschwarze static int md_pre_Fn(struct roff_node *); 71b3257404Sschwarze static int md_pre_Fo(struct roff_node *); 72b3257404Sschwarze static int md_pre_In(struct roff_node *); 73b3257404Sschwarze static int md_pre_It(struct roff_node *); 74b3257404Sschwarze static int md_pre_Lk(struct roff_node *); 7575d8c96aSschwarze static int md_pre_Mt(struct roff_node *); 76b3257404Sschwarze static int md_pre_Nd(struct roff_node *); 77b3257404Sschwarze static int md_pre_Nm(struct roff_node *); 78b3257404Sschwarze static int md_pre_No(struct roff_node *); 79b3257404Sschwarze static int md_pre_Ns(struct roff_node *); 80b3257404Sschwarze static int md_pre_Pp(struct roff_node *); 81b3257404Sschwarze static int md_pre_Rs(struct roff_node *); 82b3257404Sschwarze static int md_pre_Sh(struct roff_node *); 83b3257404Sschwarze static int md_pre_Sm(struct roff_node *); 84b3257404Sschwarze static int md_pre_Vt(struct roff_node *); 85b3257404Sschwarze static int md_pre_Xr(struct roff_node *); 86*bcf2ba68Sschwarze static int md_pre__R(struct roff_node *); 87b3257404Sschwarze static int md_pre__T(struct roff_node *); 88b3257404Sschwarze static int md_pre_br(struct roff_node *); 89b3257404Sschwarze 90b3257404Sschwarze static void md_post_raw(struct roff_node *); 91b3257404Sschwarze static void md_post_word(struct roff_node *); 92b3257404Sschwarze static void md_post_pc(struct roff_node *); 93b3257404Sschwarze static void md_post_Bk(struct roff_node *); 94b3257404Sschwarze static void md_post_Bl(struct roff_node *); 95b3257404Sschwarze static void md_post_D1(struct roff_node *); 96b3257404Sschwarze static void md_post_En(struct roff_node *); 97b3257404Sschwarze static void md_post_Eo(struct roff_node *); 98b3257404Sschwarze static void md_post_Fa(struct roff_node *); 99b3257404Sschwarze static void md_post_Fd(struct roff_node *); 1008e60e820Sschwarze static void md_post_Fl(struct roff_node *); 101b3257404Sschwarze static void md_post_Fn(struct roff_node *); 102b3257404Sschwarze static void md_post_Fo(struct roff_node *); 103b3257404Sschwarze static void md_post_In(struct roff_node *); 104b3257404Sschwarze static void md_post_It(struct roff_node *); 105b3257404Sschwarze static void md_post_Lb(struct roff_node *); 106b3257404Sschwarze static void md_post_Nm(struct roff_node *); 107b3257404Sschwarze static void md_post_Pf(struct roff_node *); 108b3257404Sschwarze static void md_post_Vt(struct roff_node *); 109b3257404Sschwarze static void md_post__T(struct roff_node *); 110b3257404Sschwarze 11116fe0cfcSschwarze static const struct md_act md_acts[MDOC_MAX - MDOC_Dd] = { 112b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Dd */ 113b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Dt */ 114b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Os */ 115b3257404Sschwarze { NULL, md_pre_Sh, NULL, NULL, NULL }, /* Sh */ 116b3257404Sschwarze { NULL, md_pre_Sh, NULL, NULL, NULL }, /* Ss */ 117b3257404Sschwarze { NULL, md_pre_Pp, NULL, NULL, NULL }, /* Pp */ 118b3257404Sschwarze { md_cond_body, md_pre_D1, md_post_D1, NULL, NULL }, /* D1 */ 119b3257404Sschwarze { md_cond_body, md_pre_Dl, md_post_D1, NULL, NULL }, /* Dl */ 120b3257404Sschwarze { md_cond_body, md_pre_Bd, md_post_D1, NULL, NULL }, /* Bd */ 121b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Ed */ 122b3257404Sschwarze { md_cond_body, md_pre_Bl, md_post_Bl, NULL, NULL }, /* Bl */ 123b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* El */ 124b3257404Sschwarze { NULL, md_pre_It, md_post_It, NULL, NULL }, /* It */ 125b3257404Sschwarze { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Ad */ 1262fe9aa39Sschwarze { NULL, md_pre_An, NULL, NULL, NULL }, /* An */ 12714a309e3Sschwarze { NULL, md_pre_Ap, NULL, NULL, NULL }, /* Ap */ 128b3257404Sschwarze { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Ar */ 129b3257404Sschwarze { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Cd */ 130b3257404Sschwarze { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Cm */ 131b3257404Sschwarze { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Dv */ 132b3257404Sschwarze { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Er */ 133b3257404Sschwarze { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Ev */ 134b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Ex */ 135b3257404Sschwarze { NULL, md_pre_Fa, md_post_Fa, NULL, NULL }, /* Fa */ 136b3257404Sschwarze { NULL, md_pre_Fd, md_post_Fd, "**", "**" }, /* Fd */ 1378e60e820Sschwarze { NULL, md_pre_raw, md_post_Fl, "**-", "**" }, /* Fl */ 138b3257404Sschwarze { NULL, md_pre_Fn, md_post_Fn, NULL, NULL }, /* Fn */ 139b3257404Sschwarze { NULL, md_pre_Fd, md_post_raw, "*", "*" }, /* Ft */ 140b3257404Sschwarze { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Ic */ 1415f580400Sschwarze { NULL, md_pre_In, md_post_In, NULL, NULL }, /* In */ 142b3257404Sschwarze { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Li */ 143b3257404Sschwarze { md_cond_head, md_pre_Nd, NULL, NULL, NULL }, /* Nd */ 144b3257404Sschwarze { NULL, md_pre_Nm, md_post_Nm, "**", "**" }, /* Nm */ 145b3257404Sschwarze { md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Op */ 1467c539ecbSschwarze { NULL, md_pre_abort, NULL, NULL, NULL }, /* Ot */ 147b3257404Sschwarze { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Pa */ 148b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Rv */ 149b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* St */ 150b3257404Sschwarze { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Va */ 151b3257404Sschwarze { NULL, md_pre_Vt, md_post_Vt, "*", "*" }, /* Vt */ 152b3257404Sschwarze { NULL, md_pre_Xr, NULL, NULL, NULL }, /* Xr */ 153b3257404Sschwarze { NULL, NULL, md_post_pc, NULL, NULL }, /* %A */ 154b3257404Sschwarze { NULL, md_pre_raw, md_post_pc, "*", "*" }, /* %B */ 155b3257404Sschwarze { NULL, NULL, md_post_pc, NULL, NULL }, /* %D */ 156b3257404Sschwarze { NULL, md_pre_raw, md_post_pc, "*", "*" }, /* %I */ 157b3257404Sschwarze { NULL, md_pre_raw, md_post_pc, "*", "*" }, /* %J */ 158b3257404Sschwarze { NULL, NULL, md_post_pc, NULL, NULL }, /* %N */ 159b3257404Sschwarze { NULL, NULL, md_post_pc, NULL, NULL }, /* %O */ 160b3257404Sschwarze { NULL, NULL, md_post_pc, NULL, NULL }, /* %P */ 161*bcf2ba68Sschwarze { NULL, md_pre__R, md_post_pc, NULL, NULL }, /* %R */ 162b3257404Sschwarze { NULL, md_pre__T, md_post__T, NULL, NULL }, /* %T */ 163b3257404Sschwarze { NULL, NULL, md_post_pc, NULL, NULL }, /* %V */ 164b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Ac */ 165b3257404Sschwarze { md_cond_body, md_pre_word, md_post_word, "<", ">" }, /* Ao */ 166b3257404Sschwarze { md_cond_body, md_pre_word, md_post_word, "<", ">" }, /* Aq */ 167b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* At */ 168b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Bc */ 169b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Bf XXX not implemented */ 170b3257404Sschwarze { md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Bo */ 171b3257404Sschwarze { md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Bq */ 172b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Bsx */ 173b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Bx */ 174b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Db */ 175b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Dc */ 176b3257404Sschwarze { md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Do */ 177b3257404Sschwarze { md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Dq */ 178b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Ec */ 179b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Ef */ 180b3257404Sschwarze { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Em */ 181b3257404Sschwarze { md_cond_body, md_pre_Eo, md_post_Eo, NULL, NULL }, /* Eo */ 182b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Fx */ 183b3257404Sschwarze { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Ms */ 184b3257404Sschwarze { NULL, md_pre_No, NULL, NULL, NULL }, /* No */ 185b3257404Sschwarze { NULL, md_pre_Ns, NULL, NULL, NULL }, /* Ns */ 186b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Nx */ 187b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Ox */ 188b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Pc */ 189b3257404Sschwarze { NULL, NULL, md_post_Pf, NULL, NULL }, /* Pf */ 190b3257404Sschwarze { md_cond_body, md_pre_word, md_post_word, "(", ")" }, /* Po */ 191b3257404Sschwarze { md_cond_body, md_pre_word, md_post_word, "(", ")" }, /* Pq */ 192b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Qc */ 193b3257404Sschwarze { md_cond_body, md_pre_raw, md_post_raw, "'`", "`'" }, /* Ql */ 194b3257404Sschwarze { md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Qo */ 195b3257404Sschwarze { md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Qq */ 196b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Re */ 197b3257404Sschwarze { md_cond_body, md_pre_Rs, NULL, NULL, NULL }, /* Rs */ 198b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Sc */ 199b3257404Sschwarze { md_cond_body, md_pre_word, md_post_word, "'", "'" }, /* So */ 200b3257404Sschwarze { md_cond_body, md_pre_word, md_post_word, "'", "'" }, /* Sq */ 201b3257404Sschwarze { NULL, md_pre_Sm, NULL, NULL, NULL }, /* Sm */ 202b3257404Sschwarze { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Sx */ 203b3257404Sschwarze { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Sy */ 204b3257404Sschwarze { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Tn */ 205b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Ux */ 206b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Xc */ 207b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Xo */ 208b3257404Sschwarze { NULL, md_pre_Fo, md_post_Fo, "**", "**" }, /* Fo */ 209b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Fc */ 210b3257404Sschwarze { md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Oo */ 211b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Oc */ 212b3257404Sschwarze { NULL, md_pre_Bk, md_post_Bk, NULL, NULL }, /* Bk */ 213b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Ek */ 214b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Bt */ 215b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Hf */ 216b3257404Sschwarze { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Fr */ 217b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Ud */ 218b3257404Sschwarze { NULL, NULL, md_post_Lb, NULL, NULL }, /* Lb */ 2197c539ecbSschwarze { NULL, md_pre_abort, NULL, NULL, NULL }, /* Lp */ 220b3257404Sschwarze { NULL, md_pre_Lk, NULL, NULL, NULL }, /* Lk */ 22175d8c96aSschwarze { NULL, md_pre_Mt, NULL, NULL, NULL }, /* Mt */ 222b3257404Sschwarze { md_cond_body, md_pre_word, md_post_word, "{", "}" }, /* Brq */ 223b3257404Sschwarze { md_cond_body, md_pre_word, md_post_word, "{", "}" }, /* Bro */ 224b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Brc */ 225b3257404Sschwarze { NULL, NULL, md_post_pc, NULL, NULL }, /* %C */ 226b3257404Sschwarze { NULL, md_pre_skip, NULL, NULL, NULL }, /* Es */ 227b3257404Sschwarze { md_cond_body, md_pre_En, md_post_En, NULL, NULL }, /* En */ 228b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Dx */ 229b3257404Sschwarze { NULL, NULL, md_post_pc, NULL, NULL }, /* %Q */ 23078ed643eSschwarze { NULL, md_pre_Lk, md_post_pc, NULL, NULL }, /* %U */ 231b3257404Sschwarze { NULL, NULL, NULL, NULL, NULL }, /* Ta */ 2328b1bef1aSschwarze { NULL, md_pre_skip, NULL, NULL, NULL }, /* Tg */ 233b3257404Sschwarze }; 23416fe0cfcSschwarze static const struct md_act *md_act(enum roff_tok); 235b3257404Sschwarze 236b3257404Sschwarze static int outflags; 237b3257404Sschwarze #define MD_spc (1 << 0) /* Blank character before next word. */ 238b3257404Sschwarze #define MD_spc_force (1 << 1) /* Even before trailing punctuation. */ 239b3257404Sschwarze #define MD_nonl (1 << 2) /* Prevent linebreak in markdown code. */ 240b3257404Sschwarze #define MD_nl (1 << 3) /* Break markdown code line. */ 241b3257404Sschwarze #define MD_br (1 << 4) /* Insert an output line break. */ 242b3257404Sschwarze #define MD_sp (1 << 5) /* Insert a paragraph break. */ 243b3257404Sschwarze #define MD_Sm (1 << 6) /* Horizontal spacing mode. */ 244b3257404Sschwarze #define MD_Bk (1 << 7) /* Word keep mode. */ 2452fe9aa39Sschwarze #define MD_An_split (1 << 8) /* Author mode is "split". */ 2462fe9aa39Sschwarze #define MD_An_nosplit (1 << 9) /* Author mode is "nosplit". */ 247b3257404Sschwarze 248b3257404Sschwarze static int escflags; /* Escape in generated markdown code: */ 249b3257404Sschwarze #define ESC_BOL (1 << 0) /* "#*+-" near the beginning of a line. */ 250b3257404Sschwarze #define ESC_NUM (1 << 1) /* "." after a leading number. */ 251b3257404Sschwarze #define ESC_HYP (1 << 2) /* "(" immediately after "]". */ 252b3257404Sschwarze #define ESC_SQU (1 << 4) /* "]" when "[" is open. */ 253b3257404Sschwarze #define ESC_FON (1 << 5) /* "*" immediately after unrelated "*". */ 254bed87e42Sschwarze #define ESC_EOL (1 << 6) /* " " at the and of a line. */ 255b3257404Sschwarze 256b3257404Sschwarze static int code_blocks, quote_blocks, list_blocks; 257b3257404Sschwarze static int outcount; 258b3257404Sschwarze 25916fe0cfcSschwarze 26016fe0cfcSschwarze static const struct md_act * 26116fe0cfcSschwarze md_act(enum roff_tok tok) 26216fe0cfcSschwarze { 26316fe0cfcSschwarze assert(tok >= MDOC_Dd && tok <= MDOC_MAX); 26416fe0cfcSschwarze return md_acts + (tok - MDOC_Dd); 26516fe0cfcSschwarze } 26616fe0cfcSschwarze 267b3257404Sschwarze void 2686b86842eSschwarze markdown_mdoc(void *arg, const struct roff_meta *mdoc) 269b3257404Sschwarze { 270b3257404Sschwarze outflags = MD_Sm; 2716b86842eSschwarze md_word(mdoc->title); 2726b86842eSschwarze if (mdoc->msec != NULL) { 273b3257404Sschwarze outflags &= ~MD_spc; 274b3257404Sschwarze md_word("("); 2756b86842eSschwarze md_word(mdoc->msec); 276b3257404Sschwarze md_word(")"); 277b3257404Sschwarze } 278b3257404Sschwarze md_word("-"); 2796b86842eSschwarze md_word(mdoc->vol); 2806b86842eSschwarze if (mdoc->arch != NULL) { 281b3257404Sschwarze md_word("("); 2826b86842eSschwarze md_word(mdoc->arch); 283b3257404Sschwarze md_word(")"); 284b3257404Sschwarze } 285b3257404Sschwarze outflags |= MD_sp; 286b3257404Sschwarze 287b3257404Sschwarze md_nodelist(mdoc->first->child); 288b3257404Sschwarze 289b3257404Sschwarze outflags |= MD_sp; 2906b86842eSschwarze md_word(mdoc->os); 291b3257404Sschwarze md_word("-"); 2926b86842eSschwarze md_word(mdoc->date); 293b3257404Sschwarze putchar('\n'); 294b3257404Sschwarze } 295b3257404Sschwarze 296b3257404Sschwarze static void 297b3257404Sschwarze md_nodelist(struct roff_node *n) 298b3257404Sschwarze { 299b3257404Sschwarze while (n != NULL) { 300b3257404Sschwarze md_node(n); 301b3257404Sschwarze n = n->next; 302b3257404Sschwarze } 303b3257404Sschwarze } 304b3257404Sschwarze 305b3257404Sschwarze static void 306b3257404Sschwarze md_node(struct roff_node *n) 307b3257404Sschwarze { 308b3257404Sschwarze const struct md_act *act; 309b3257404Sschwarze int cond, process_children; 310b3257404Sschwarze 3114c293873Sschwarze if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT) 312b3257404Sschwarze return; 313b3257404Sschwarze 314b3257404Sschwarze if (outflags & MD_nonl) 315b3257404Sschwarze outflags &= ~(MD_nl | MD_sp); 3167ebbefbeSschwarze else if (outflags & MD_spc && 3177ebbefbeSschwarze n->flags & NODE_LINE && 3187ebbefbeSschwarze !roff_node_transparent(n)) 319b3257404Sschwarze outflags |= MD_nl; 320b3257404Sschwarze 321b3257404Sschwarze act = NULL; 322b3257404Sschwarze cond = 0; 323b3257404Sschwarze process_children = 1; 324b3257404Sschwarze n->flags &= ~NODE_ENDED; 325b3257404Sschwarze 32629478532Sschwarze if (n->type == ROFFT_TEXT) { 327b3257404Sschwarze if (n->flags & NODE_DELIMC) 328b3257404Sschwarze outflags &= ~(MD_spc | MD_spc_force); 329b3257404Sschwarze else if (outflags & MD_Sm) 330b3257404Sschwarze outflags |= MD_spc_force; 331b3257404Sschwarze md_word(n->string); 332b3257404Sschwarze if (n->flags & NODE_DELIMO) 333b3257404Sschwarze outflags &= ~(MD_spc | MD_spc_force); 334b3257404Sschwarze else if (outflags & MD_Sm) 335b3257404Sschwarze outflags |= MD_spc; 33629478532Sschwarze } else if (n->tok < ROFF_MAX) { 33729478532Sschwarze switch (n->tok) { 33829478532Sschwarze case ROFF_br: 339c4d3fa85Sschwarze process_children = md_pre_br(n); 340c4d3fa85Sschwarze break; 3416561cb23Sschwarze case ROFF_sp: 3426561cb23Sschwarze process_children = md_pre_Pp(n); 3436561cb23Sschwarze break; 344644b390bSschwarze default: 345c4d3fa85Sschwarze process_children = 0; 346b3257404Sschwarze break; 34729478532Sschwarze } 34829478532Sschwarze } else { 34916fe0cfcSschwarze act = md_act(n->tok); 350b3257404Sschwarze cond = act->cond == NULL || (*act->cond)(n); 351b3257404Sschwarze if (cond && act->pre != NULL && 352b3257404Sschwarze (n->end == ENDBODY_NOT || n->child != NULL)) 353b3257404Sschwarze process_children = (*act->pre)(n); 354b3257404Sschwarze } 355b3257404Sschwarze 356b3257404Sschwarze if (process_children && n->child != NULL) 357b3257404Sschwarze md_nodelist(n->child); 358b3257404Sschwarze 359b3257404Sschwarze if (n->flags & NODE_ENDED) 360b3257404Sschwarze return; 361b3257404Sschwarze 362b3257404Sschwarze if (cond && act->post != NULL) 363b3257404Sschwarze (*act->post)(n); 364b3257404Sschwarze 365b3257404Sschwarze if (n->end != ENDBODY_NOT) 366b3257404Sschwarze n->body->flags |= NODE_ENDED; 367b3257404Sschwarze } 368b3257404Sschwarze 369b3257404Sschwarze static const char * 370b3257404Sschwarze md_stack(char c) 371b3257404Sschwarze { 372b3257404Sschwarze static char *stack; 373b3257404Sschwarze static size_t sz; 374b3257404Sschwarze static size_t cur; 375b3257404Sschwarze 376b3257404Sschwarze switch (c) { 377b3257404Sschwarze case '\0': 378b3257404Sschwarze break; 379b3257404Sschwarze case (char)-1: 380b3257404Sschwarze assert(cur); 381b3257404Sschwarze stack[--cur] = '\0'; 382b3257404Sschwarze break; 383b3257404Sschwarze default: 384b3257404Sschwarze if (cur + 1 >= sz) { 385b3257404Sschwarze sz += 8; 386b3257404Sschwarze stack = mandoc_realloc(stack, sz); 387b3257404Sschwarze } 388b3257404Sschwarze stack[cur] = c; 389b3257404Sschwarze stack[++cur] = '\0'; 390b3257404Sschwarze break; 391b3257404Sschwarze } 392b3257404Sschwarze return stack == NULL ? "" : stack; 393b3257404Sschwarze } 394b3257404Sschwarze 395b3257404Sschwarze /* 396b3257404Sschwarze * Handle vertical and horizontal spacing. 397b3257404Sschwarze */ 398b3257404Sschwarze static void 399b3257404Sschwarze md_preword(void) 400b3257404Sschwarze { 4010b477a38Sschwarze const char *cp; 4020b477a38Sschwarze 403b3257404Sschwarze /* 404b3257404Sschwarze * If a list block is nested inside a code block or a blockquote, 405b3257404Sschwarze * blank lines for paragraph breaks no longer work; instead, 406b3257404Sschwarze * they terminate the list. Work around this markdown issue 407b3257404Sschwarze * by using mere line breaks instead. 408b3257404Sschwarze */ 409bed87e42Sschwarze 410b3257404Sschwarze if (list_blocks && outflags & MD_sp) { 411b3257404Sschwarze outflags &= ~MD_sp; 412b3257404Sschwarze outflags |= MD_br; 413b3257404Sschwarze } 414b3257404Sschwarze 415bed87e42Sschwarze /* 416bed87e42Sschwarze * End the old line if requested. 417bed87e42Sschwarze * Escape whitespace at the end of the markdown line 418bed87e42Sschwarze * such that it won't look like an output line break. 419bed87e42Sschwarze */ 420b3257404Sschwarze 421b3257404Sschwarze if (outflags & MD_sp) 422b3257404Sschwarze putchar('\n'); 423b3257404Sschwarze else if (outflags & MD_br) { 424b3257404Sschwarze putchar(' '); 425b3257404Sschwarze putchar(' '); 426bed87e42Sschwarze } else if (outflags & MD_nl && escflags & ESC_EOL) 427bed87e42Sschwarze md_named("zwnj"); 428b3257404Sschwarze 429b3257404Sschwarze /* Start a new line if necessary. */ 430b3257404Sschwarze 431b3257404Sschwarze if (outflags & (MD_nl | MD_br | MD_sp)) { 432b3257404Sschwarze putchar('\n'); 4330b477a38Sschwarze for (cp = md_stack('\0'); *cp != '\0'; cp++) { 4340b477a38Sschwarze putchar(*cp); 4350b477a38Sschwarze if (*cp == '>') 4360b477a38Sschwarze putchar(' '); 4370b477a38Sschwarze } 438b3257404Sschwarze outflags &= ~(MD_nl | MD_br | MD_sp); 439b3257404Sschwarze escflags = ESC_BOL; 440b3257404Sschwarze outcount = 0; 441b3257404Sschwarze 442b3257404Sschwarze /* Handle horizontal spacing. */ 443b3257404Sschwarze 444b3257404Sschwarze } else if (outflags & MD_spc) { 445b3257404Sschwarze if (outflags & MD_Bk) 446b3257404Sschwarze fputs(" ", stdout); 447b3257404Sschwarze else 448b3257404Sschwarze putchar(' '); 449b3257404Sschwarze escflags &= ~ESC_FON; 450b3257404Sschwarze outcount++; 451b3257404Sschwarze } 452b3257404Sschwarze 453b3257404Sschwarze outflags &= ~(MD_spc_force | MD_nonl); 454b3257404Sschwarze if (outflags & MD_Sm) 455b3257404Sschwarze outflags |= MD_spc; 456b3257404Sschwarze else 457b3257404Sschwarze outflags &= ~MD_spc; 458b3257404Sschwarze } 459b3257404Sschwarze 460b3257404Sschwarze /* 461b3257404Sschwarze * Print markdown syntax elements. 462b3257404Sschwarze * Can also be used for constant strings when neither escaping 463b3257404Sschwarze * nor delimiter handling is required. 464b3257404Sschwarze */ 465b3257404Sschwarze static void 466b3257404Sschwarze md_rawword(const char *s) 467b3257404Sschwarze { 468b3257404Sschwarze md_preword(); 469b3257404Sschwarze 470bed87e42Sschwarze if (*s == '\0') 471b3257404Sschwarze return; 472b3257404Sschwarze 473b3257404Sschwarze if (escflags & ESC_FON) { 474b3257404Sschwarze escflags &= ~ESC_FON; 475b3257404Sschwarze if (*s == '*' && !code_blocks) 476b3257404Sschwarze fputs("‌", stdout); 477b3257404Sschwarze } 478b3257404Sschwarze 479b3257404Sschwarze while (*s != '\0') { 480b3257404Sschwarze switch(*s) { 481b3257404Sschwarze case '*': 482b3257404Sschwarze if (s[1] == '\0') 483b3257404Sschwarze escflags |= ESC_FON; 484b3257404Sschwarze break; 485b3257404Sschwarze case '[': 486b3257404Sschwarze escflags |= ESC_SQU; 487b3257404Sschwarze break; 488b3257404Sschwarze case ']': 489b3257404Sschwarze escflags |= ESC_HYP; 490b3257404Sschwarze escflags &= ~ESC_SQU; 491b3257404Sschwarze break; 492b3257404Sschwarze default: 493b3257404Sschwarze break; 494b3257404Sschwarze } 495b3257404Sschwarze md_char(*s++); 496b3257404Sschwarze } 497bed87e42Sschwarze if (s[-1] == ' ') 498bed87e42Sschwarze escflags |= ESC_EOL; 499bed87e42Sschwarze else 500bed87e42Sschwarze escflags &= ~ESC_EOL; 501b3257404Sschwarze } 502b3257404Sschwarze 503b3257404Sschwarze /* 504b3257404Sschwarze * Print text and mdoc(7) syntax elements. 505b3257404Sschwarze */ 506b3257404Sschwarze static void 507b3257404Sschwarze md_word(const char *s) 508b3257404Sschwarze { 509b3257404Sschwarze const char *seq, *prevfont, *currfont, *nextfont; 510b3257404Sschwarze char c; 5116167ec38Sschwarze int bs, sz, uc, breakline; 512b3257404Sschwarze 513b3257404Sschwarze /* No spacing before closing delimiters. */ 514b3257404Sschwarze if (s[0] != '\0' && s[1] == '\0' && 515b3257404Sschwarze strchr("!),.:;?]", s[0]) != NULL && 516b3257404Sschwarze (outflags & MD_spc_force) == 0) 517b3257404Sschwarze outflags &= ~MD_spc; 518b3257404Sschwarze 519b3257404Sschwarze md_preword(); 520b3257404Sschwarze 521bed87e42Sschwarze if (*s == '\0') 522bed87e42Sschwarze return; 523bed87e42Sschwarze 524b3257404Sschwarze /* No spacing after opening delimiters. */ 525b3257404Sschwarze if ((s[0] == '(' || s[0] == '[') && s[1] == '\0') 526b3257404Sschwarze outflags &= ~MD_spc; 527b3257404Sschwarze 5286167ec38Sschwarze breakline = 0; 529b3257404Sschwarze prevfont = currfont = ""; 530b3257404Sschwarze while ((c = *s++) != '\0') { 531b3257404Sschwarze bs = 0; 532b3257404Sschwarze switch(c) { 533b3257404Sschwarze case ASCII_NBRSP: 534b3257404Sschwarze if (code_blocks) 535b3257404Sschwarze c = ' '; 536b3257404Sschwarze else { 537b3257404Sschwarze md_named("nbsp"); 538b3257404Sschwarze c = '\0'; 539b3257404Sschwarze } 540b3257404Sschwarze break; 541b3257404Sschwarze case ASCII_HYPH: 542b3257404Sschwarze bs = escflags & ESC_BOL && !code_blocks; 543b3257404Sschwarze c = '-'; 544b3257404Sschwarze break; 545b3257404Sschwarze case ASCII_BREAK: 546b3257404Sschwarze continue; 547b3257404Sschwarze case '#': 548b3257404Sschwarze case '+': 549b3257404Sschwarze case '-': 550b3257404Sschwarze bs = escflags & ESC_BOL && !code_blocks; 551b3257404Sschwarze break; 552b3257404Sschwarze case '(': 553b3257404Sschwarze bs = escflags & ESC_HYP && !code_blocks; 554b3257404Sschwarze break; 555b3257404Sschwarze case ')': 556433f0baeSschwarze bs = escflags & ESC_NUM && !code_blocks; 557b3257404Sschwarze break; 558b3257404Sschwarze case '*': 559b3257404Sschwarze case '[': 560b3257404Sschwarze case '_': 561b3257404Sschwarze case '`': 562b3257404Sschwarze bs = !code_blocks; 563b3257404Sschwarze break; 564b3257404Sschwarze case '.': 565b3257404Sschwarze bs = escflags & ESC_NUM && !code_blocks; 566b3257404Sschwarze break; 567b3257404Sschwarze case '<': 568b3257404Sschwarze if (code_blocks == 0) { 569b3257404Sschwarze md_named("lt"); 570b3257404Sschwarze c = '\0'; 571b3257404Sschwarze } 572b3257404Sschwarze break; 573b3257404Sschwarze case '=': 574b3257404Sschwarze if (escflags & ESC_BOL && !code_blocks) { 575b3257404Sschwarze md_named("equals"); 576b3257404Sschwarze c = '\0'; 577b3257404Sschwarze } 578b3257404Sschwarze break; 579b3257404Sschwarze case '>': 580b3257404Sschwarze if (code_blocks == 0) { 581b3257404Sschwarze md_named("gt"); 582b3257404Sschwarze c = '\0'; 583b3257404Sschwarze } 584b3257404Sschwarze break; 585b3257404Sschwarze case '\\': 586b3257404Sschwarze uc = 0; 587b3257404Sschwarze nextfont = NULL; 588b3257404Sschwarze switch (mandoc_escape(&s, &seq, &sz)) { 589b3257404Sschwarze case ESCAPE_UNICODE: 590b3257404Sschwarze uc = mchars_num2uc(seq + 1, sz - 1); 591b3257404Sschwarze break; 592b3257404Sschwarze case ESCAPE_NUMBERED: 593b3257404Sschwarze uc = mchars_num2char(seq, sz); 594b3257404Sschwarze break; 595b3257404Sschwarze case ESCAPE_SPECIAL: 596b3257404Sschwarze uc = mchars_spec2cp(seq, sz); 597b3257404Sschwarze break; 5986f6722cbSschwarze case ESCAPE_UNDEF: 5996f6722cbSschwarze uc = *seq; 6006f6722cbSschwarze break; 6018138dde8Sschwarze case ESCAPE_DEVICE: 6028138dde8Sschwarze md_rawword("markdown"); 6038138dde8Sschwarze continue; 604b3257404Sschwarze case ESCAPE_FONTBOLD: 6057d063611Sschwarze case ESCAPE_FONTCB: 606b3257404Sschwarze nextfont = "**"; 607b3257404Sschwarze break; 608b3257404Sschwarze case ESCAPE_FONTITALIC: 6097d063611Sschwarze case ESCAPE_FONTCI: 610b3257404Sschwarze nextfont = "*"; 611b3257404Sschwarze break; 612b3257404Sschwarze case ESCAPE_FONTBI: 613b3257404Sschwarze nextfont = "***"; 614b3257404Sschwarze break; 615b3257404Sschwarze case ESCAPE_FONT: 6167d063611Sschwarze case ESCAPE_FONTCR: 617b3257404Sschwarze case ESCAPE_FONTROMAN: 618b3257404Sschwarze nextfont = ""; 619b3257404Sschwarze break; 620b3257404Sschwarze case ESCAPE_FONTPREV: 621b3257404Sschwarze nextfont = prevfont; 622b3257404Sschwarze break; 6236167ec38Sschwarze case ESCAPE_BREAK: 6246167ec38Sschwarze breakline = 1; 6256167ec38Sschwarze break; 626b3257404Sschwarze case ESCAPE_NOSPACE: 627b3257404Sschwarze case ESCAPE_SKIPCHAR: 628b3257404Sschwarze case ESCAPE_OVERSTRIKE: 629b3257404Sschwarze /* XXX not implemented */ 630b3257404Sschwarze /* FALLTHROUGH */ 631b3257404Sschwarze case ESCAPE_ERROR: 632b3257404Sschwarze default: 633b3257404Sschwarze break; 634b3257404Sschwarze } 635b3257404Sschwarze if (nextfont != NULL && !code_blocks) { 636b3257404Sschwarze if (*currfont != '\0') { 637b3257404Sschwarze outflags &= ~MD_spc; 638b3257404Sschwarze md_rawword(currfont); 639b3257404Sschwarze } 640b3257404Sschwarze prevfont = currfont; 641b3257404Sschwarze currfont = nextfont; 642b3257404Sschwarze if (*currfont != '\0') { 643b3257404Sschwarze outflags &= ~MD_spc; 644b3257404Sschwarze md_rawword(currfont); 645b3257404Sschwarze } 646b3257404Sschwarze } 647b3257404Sschwarze if (uc) { 648b3257404Sschwarze if ((uc < 0x20 && uc != 0x09) || 649b3257404Sschwarze (uc > 0x7E && uc < 0xA0)) 650b3257404Sschwarze uc = 0xFFFD; 651b3257404Sschwarze if (code_blocks) { 652b3257404Sschwarze seq = mchars_uc2str(uc); 653b3257404Sschwarze fputs(seq, stdout); 654b3257404Sschwarze outcount += strlen(seq); 655b3257404Sschwarze } else { 656b3257404Sschwarze printf("&#%d;", uc); 657b3257404Sschwarze outcount++; 658b3257404Sschwarze } 659b3257404Sschwarze escflags &= ~ESC_FON; 660b3257404Sschwarze } 661b3257404Sschwarze c = '\0'; 662b3257404Sschwarze break; 663b3257404Sschwarze case ']': 664b3257404Sschwarze bs = escflags & ESC_SQU && !code_blocks; 665b3257404Sschwarze escflags |= ESC_HYP; 666b3257404Sschwarze break; 667b3257404Sschwarze default: 668b3257404Sschwarze break; 669b3257404Sschwarze } 670b3257404Sschwarze if (bs) 671b3257404Sschwarze putchar('\\'); 672b3257404Sschwarze md_char(c); 6736167ec38Sschwarze if (breakline && 6746167ec38Sschwarze (*s == '\0' || *s == ' ' || *s == ASCII_NBRSP)) { 6756167ec38Sschwarze printf(" \n"); 6766167ec38Sschwarze breakline = 0; 6776167ec38Sschwarze while (*s == ' ' || *s == ASCII_NBRSP) 6786167ec38Sschwarze s++; 6796167ec38Sschwarze } 680b3257404Sschwarze } 681b3257404Sschwarze if (*currfont != '\0') { 682b3257404Sschwarze outflags &= ~MD_spc; 683b3257404Sschwarze md_rawword(currfont); 684bed87e42Sschwarze } else if (s[-2] == ' ') 685bed87e42Sschwarze escflags |= ESC_EOL; 686bed87e42Sschwarze else 687bed87e42Sschwarze escflags &= ~ESC_EOL; 688b3257404Sschwarze } 689b3257404Sschwarze 690b3257404Sschwarze /* 691b3257404Sschwarze * Print a single HTML named character reference. 692b3257404Sschwarze */ 693b3257404Sschwarze static void 694b3257404Sschwarze md_named(const char *s) 695b3257404Sschwarze { 696b3257404Sschwarze printf("&%s;", s); 697bed87e42Sschwarze escflags &= ~(ESC_FON | ESC_EOL); 698b3257404Sschwarze outcount++; 699b3257404Sschwarze } 700b3257404Sschwarze 701b3257404Sschwarze /* 702b3257404Sschwarze * Print a single raw character and maintain certain escape flags. 703b3257404Sschwarze */ 704b3257404Sschwarze static void 705b3257404Sschwarze md_char(unsigned char c) 706b3257404Sschwarze { 707b3257404Sschwarze if (c != '\0') { 708b3257404Sschwarze putchar(c); 709b3257404Sschwarze if (c == '*') 710b3257404Sschwarze escflags |= ESC_FON; 711b3257404Sschwarze else 712b3257404Sschwarze escflags &= ~ESC_FON; 713b3257404Sschwarze outcount++; 714b3257404Sschwarze } 715b3257404Sschwarze if (c != ']') 716b3257404Sschwarze escflags &= ~ESC_HYP; 717b3257404Sschwarze if (c == ' ' || c == '\t' || c == '>') 718b3257404Sschwarze return; 719b3257404Sschwarze if (isdigit(c) == 0) 720b3257404Sschwarze escflags &= ~ESC_NUM; 721b3257404Sschwarze else if (escflags & ESC_BOL) 722b3257404Sschwarze escflags |= ESC_NUM; 723b3257404Sschwarze escflags &= ~ESC_BOL; 724b3257404Sschwarze } 725b3257404Sschwarze 726b3257404Sschwarze static int 727b3257404Sschwarze md_cond_head(struct roff_node *n) 728b3257404Sschwarze { 729b3257404Sschwarze return n->type == ROFFT_HEAD; 730b3257404Sschwarze } 731b3257404Sschwarze 732b3257404Sschwarze static int 733b3257404Sschwarze md_cond_body(struct roff_node *n) 734b3257404Sschwarze { 735b3257404Sschwarze return n->type == ROFFT_BODY; 736b3257404Sschwarze } 737b3257404Sschwarze 738b3257404Sschwarze static int 7397c539ecbSschwarze md_pre_abort(struct roff_node *n) 7407c539ecbSschwarze { 7417c539ecbSschwarze abort(); 7427c539ecbSschwarze } 7437c539ecbSschwarze 7447c539ecbSschwarze static int 745b3257404Sschwarze md_pre_raw(struct roff_node *n) 746b3257404Sschwarze { 747b3257404Sschwarze const char *prefix; 748b3257404Sschwarze 74916fe0cfcSschwarze if ((prefix = md_act(n->tok)->prefix) != NULL) { 750b3257404Sschwarze md_rawword(prefix); 751b3257404Sschwarze outflags &= ~MD_spc; 7520272cb5cSschwarze if (strchr(prefix, '`') != NULL) 753113c1f55Sschwarze code_blocks++; 754b3257404Sschwarze } 755b3257404Sschwarze return 1; 756b3257404Sschwarze } 757b3257404Sschwarze 758b3257404Sschwarze static void 759b3257404Sschwarze md_post_raw(struct roff_node *n) 760b3257404Sschwarze { 761b3257404Sschwarze const char *suffix; 762b3257404Sschwarze 76316fe0cfcSschwarze if ((suffix = md_act(n->tok)->suffix) != NULL) { 764b3257404Sschwarze outflags &= ~(MD_spc | MD_nl); 765b3257404Sschwarze md_rawword(suffix); 7660272cb5cSschwarze if (strchr(suffix, '`') != NULL) 767113c1f55Sschwarze code_blocks--; 768b3257404Sschwarze } 769b3257404Sschwarze } 770b3257404Sschwarze 771b3257404Sschwarze static int 772b3257404Sschwarze md_pre_word(struct roff_node *n) 773b3257404Sschwarze { 774b3257404Sschwarze const char *prefix; 775b3257404Sschwarze 77616fe0cfcSschwarze if ((prefix = md_act(n->tok)->prefix) != NULL) { 777b3257404Sschwarze md_word(prefix); 778b3257404Sschwarze outflags &= ~MD_spc; 779b3257404Sschwarze } 780b3257404Sschwarze return 1; 781b3257404Sschwarze } 782b3257404Sschwarze 783b3257404Sschwarze static void 784b3257404Sschwarze md_post_word(struct roff_node *n) 785b3257404Sschwarze { 786b3257404Sschwarze const char *suffix; 787b3257404Sschwarze 78816fe0cfcSschwarze if ((suffix = md_act(n->tok)->suffix) != NULL) { 789b3257404Sschwarze outflags &= ~(MD_spc | MD_nl); 790b3257404Sschwarze md_word(suffix); 791b3257404Sschwarze } 792b3257404Sschwarze } 793b3257404Sschwarze 794b3257404Sschwarze static void 795b3257404Sschwarze md_post_pc(struct roff_node *n) 796b3257404Sschwarze { 7977ebbefbeSschwarze struct roff_node *nn; 7987ebbefbeSschwarze 799b3257404Sschwarze md_post_raw(n); 800b3257404Sschwarze if (n->parent->tok != MDOC_Rs) 801b3257404Sschwarze return; 8027ebbefbeSschwarze 8037ebbefbeSschwarze if ((nn = roff_node_next(n)) != NULL) { 804b3257404Sschwarze md_word(","); 8057ebbefbeSschwarze if (nn->tok == n->tok && 8067ebbefbeSschwarze (nn = roff_node_prev(n)) != NULL && 8077ebbefbeSschwarze nn->tok == n->tok) 808b3257404Sschwarze md_word("and"); 809b3257404Sschwarze } else { 810b3257404Sschwarze md_word("."); 811b3257404Sschwarze outflags |= MD_nl; 812b3257404Sschwarze } 813b3257404Sschwarze } 814b3257404Sschwarze 815b3257404Sschwarze static int 816b3257404Sschwarze md_pre_skip(struct roff_node *n) 817b3257404Sschwarze { 818b3257404Sschwarze return 0; 819b3257404Sschwarze } 820b3257404Sschwarze 821b3257404Sschwarze static void 822b3257404Sschwarze md_pre_syn(struct roff_node *n) 823b3257404Sschwarze { 8247ebbefbeSschwarze struct roff_node *np; 8257ebbefbeSschwarze 8267ebbefbeSschwarze if ((n->flags & NODE_SYNPRETTY) == 0 || 8277ebbefbeSschwarze (np = roff_node_prev(n)) == NULL) 828b3257404Sschwarze return; 829b3257404Sschwarze 8307ebbefbeSschwarze if (np->tok == n->tok && 831b3257404Sschwarze n->tok != MDOC_Ft && 832b3257404Sschwarze n->tok != MDOC_Fo && 833b3257404Sschwarze n->tok != MDOC_Fn) { 834b3257404Sschwarze outflags |= MD_br; 835b3257404Sschwarze return; 836b3257404Sschwarze } 837b3257404Sschwarze 8387ebbefbeSschwarze switch (np->tok) { 839b3257404Sschwarze case MDOC_Fd: 840b3257404Sschwarze case MDOC_Fn: 841b3257404Sschwarze case MDOC_Fo: 842b3257404Sschwarze case MDOC_In: 843b3257404Sschwarze case MDOC_Vt: 844b3257404Sschwarze outflags |= MD_sp; 845b3257404Sschwarze break; 846b3257404Sschwarze case MDOC_Ft: 847b3257404Sschwarze if (n->tok != MDOC_Fn && n->tok != MDOC_Fo) { 848b3257404Sschwarze outflags |= MD_sp; 849b3257404Sschwarze break; 850b3257404Sschwarze } 851b3257404Sschwarze /* FALLTHROUGH */ 852b3257404Sschwarze default: 853b3257404Sschwarze outflags |= MD_br; 854b3257404Sschwarze break; 855b3257404Sschwarze } 856b3257404Sschwarze } 857b3257404Sschwarze 858b3257404Sschwarze static int 8592fe9aa39Sschwarze md_pre_An(struct roff_node *n) 8602fe9aa39Sschwarze { 8612fe9aa39Sschwarze switch (n->norm->An.auth) { 8622fe9aa39Sschwarze case AUTH_split: 8632fe9aa39Sschwarze outflags &= ~MD_An_nosplit; 8642fe9aa39Sschwarze outflags |= MD_An_split; 8652fe9aa39Sschwarze return 0; 8662fe9aa39Sschwarze case AUTH_nosplit: 8672fe9aa39Sschwarze outflags &= ~MD_An_split; 8682fe9aa39Sschwarze outflags |= MD_An_nosplit; 8692fe9aa39Sschwarze return 0; 8702fe9aa39Sschwarze default: 8712fe9aa39Sschwarze if (outflags & MD_An_split) 8722fe9aa39Sschwarze outflags |= MD_br; 8732fe9aa39Sschwarze else if (n->sec == SEC_AUTHORS && 8742fe9aa39Sschwarze ! (outflags & MD_An_nosplit)) 8752fe9aa39Sschwarze outflags |= MD_An_split; 8762fe9aa39Sschwarze return 1; 8772fe9aa39Sschwarze } 8782fe9aa39Sschwarze } 8792fe9aa39Sschwarze 8802fe9aa39Sschwarze static int 881b3257404Sschwarze md_pre_Ap(struct roff_node *n) 882b3257404Sschwarze { 883b3257404Sschwarze outflags &= ~MD_spc; 884b3257404Sschwarze md_word("'"); 885b3257404Sschwarze outflags &= ~MD_spc; 886b3257404Sschwarze return 0; 887b3257404Sschwarze } 888b3257404Sschwarze 889b3257404Sschwarze static int 890b3257404Sschwarze md_pre_Bd(struct roff_node *n) 891b3257404Sschwarze { 892b3257404Sschwarze switch (n->norm->Bd.type) { 893b3257404Sschwarze case DISP_unfilled: 894b3257404Sschwarze case DISP_literal: 895b3257404Sschwarze return md_pre_Dl(n); 896b3257404Sschwarze default: 897b3257404Sschwarze return md_pre_D1(n); 898b3257404Sschwarze } 899b3257404Sschwarze } 900b3257404Sschwarze 901b3257404Sschwarze static int 902b3257404Sschwarze md_pre_Bk(struct roff_node *n) 903b3257404Sschwarze { 904b3257404Sschwarze switch (n->type) { 905b3257404Sschwarze case ROFFT_BLOCK: 906b3257404Sschwarze return 1; 907b3257404Sschwarze case ROFFT_BODY: 908b3257404Sschwarze outflags |= MD_Bk; 909b3257404Sschwarze return 1; 910b3257404Sschwarze default: 911b3257404Sschwarze return 0; 912b3257404Sschwarze } 913b3257404Sschwarze } 914b3257404Sschwarze 915b3257404Sschwarze static void 916b3257404Sschwarze md_post_Bk(struct roff_node *n) 917b3257404Sschwarze { 918b3257404Sschwarze if (n->type == ROFFT_BODY) 919b3257404Sschwarze outflags &= ~MD_Bk; 920b3257404Sschwarze } 921b3257404Sschwarze 922b3257404Sschwarze static int 923b3257404Sschwarze md_pre_Bl(struct roff_node *n) 924b3257404Sschwarze { 925b3257404Sschwarze n->norm->Bl.count = 0; 926b3257404Sschwarze if (n->norm->Bl.type == LIST_column) 927b3257404Sschwarze md_pre_Dl(n); 928b3257404Sschwarze outflags |= MD_sp; 929b3257404Sschwarze return 1; 930b3257404Sschwarze } 931b3257404Sschwarze 932b3257404Sschwarze static void 933b3257404Sschwarze md_post_Bl(struct roff_node *n) 934b3257404Sschwarze { 935b3257404Sschwarze n->norm->Bl.count = 0; 936b3257404Sschwarze if (n->norm->Bl.type == LIST_column) 937b3257404Sschwarze md_post_D1(n); 938b3257404Sschwarze outflags |= MD_sp; 939b3257404Sschwarze } 940b3257404Sschwarze 941b3257404Sschwarze static int 942b3257404Sschwarze md_pre_D1(struct roff_node *n) 943b3257404Sschwarze { 944b3257404Sschwarze /* 945b3257404Sschwarze * Markdown blockquote syntax does not work inside code blocks. 946b3257404Sschwarze * The best we can do is fall back to another nested code block. 947b3257404Sschwarze */ 948b3257404Sschwarze if (code_blocks) { 949b3257404Sschwarze md_stack('\t'); 950b3257404Sschwarze code_blocks++; 951b3257404Sschwarze } else { 952b3257404Sschwarze md_stack('>'); 953b3257404Sschwarze quote_blocks++; 954b3257404Sschwarze } 955b3257404Sschwarze outflags |= MD_sp; 956b3257404Sschwarze return 1; 957b3257404Sschwarze } 958b3257404Sschwarze 959b3257404Sschwarze static void 960b3257404Sschwarze md_post_D1(struct roff_node *n) 961b3257404Sschwarze { 962b3257404Sschwarze md_stack((char)-1); 963b3257404Sschwarze if (code_blocks) 964b3257404Sschwarze code_blocks--; 965b3257404Sschwarze else 966b3257404Sschwarze quote_blocks--; 967b3257404Sschwarze outflags |= MD_sp; 968b3257404Sschwarze } 969b3257404Sschwarze 970b3257404Sschwarze static int 971b3257404Sschwarze md_pre_Dl(struct roff_node *n) 972b3257404Sschwarze { 973b3257404Sschwarze /* 974b3257404Sschwarze * Markdown code block syntax does not work inside blockquotes. 975b3257404Sschwarze * The best we can do is fall back to another nested blockquote. 976b3257404Sschwarze */ 977b3257404Sschwarze if (quote_blocks) { 978b3257404Sschwarze md_stack('>'); 979b3257404Sschwarze quote_blocks++; 980b3257404Sschwarze } else { 981b3257404Sschwarze md_stack('\t'); 982b3257404Sschwarze code_blocks++; 983b3257404Sschwarze } 984b3257404Sschwarze outflags |= MD_sp; 985b3257404Sschwarze return 1; 986b3257404Sschwarze } 987b3257404Sschwarze 988b3257404Sschwarze static int 989b3257404Sschwarze md_pre_En(struct roff_node *n) 990b3257404Sschwarze { 991b3257404Sschwarze if (n->norm->Es == NULL || 992b3257404Sschwarze n->norm->Es->child == NULL) 993b3257404Sschwarze return 1; 994b3257404Sschwarze 995b3257404Sschwarze md_word(n->norm->Es->child->string); 996b3257404Sschwarze outflags &= ~MD_spc; 997b3257404Sschwarze return 1; 998b3257404Sschwarze } 999b3257404Sschwarze 1000b3257404Sschwarze static void 1001b3257404Sschwarze md_post_En(struct roff_node *n) 1002b3257404Sschwarze { 1003b3257404Sschwarze if (n->norm->Es == NULL || 1004b3257404Sschwarze n->norm->Es->child == NULL || 1005b3257404Sschwarze n->norm->Es->child->next == NULL) 1006b3257404Sschwarze return; 1007b3257404Sschwarze 1008b3257404Sschwarze outflags &= ~MD_spc; 1009b3257404Sschwarze md_word(n->norm->Es->child->next->string); 1010b3257404Sschwarze } 1011b3257404Sschwarze 1012b3257404Sschwarze static int 1013b3257404Sschwarze md_pre_Eo(struct roff_node *n) 1014b3257404Sschwarze { 1015b3257404Sschwarze if (n->end == ENDBODY_NOT && 1016b3257404Sschwarze n->parent->head->child == NULL && 1017b3257404Sschwarze n->child != NULL && 1018b3257404Sschwarze n->child->end != ENDBODY_NOT) 1019b3257404Sschwarze md_preword(); 1020b3257404Sschwarze else if (n->end != ENDBODY_NOT ? n->child != NULL : 1021b3257404Sschwarze n->parent->head->child != NULL && (n->child != NULL || 1022b3257404Sschwarze (n->parent->tail != NULL && n->parent->tail->child != NULL))) 1023b3257404Sschwarze outflags &= ~(MD_spc | MD_nl); 1024b3257404Sschwarze return 1; 1025b3257404Sschwarze } 1026b3257404Sschwarze 1027b3257404Sschwarze static void 1028b3257404Sschwarze md_post_Eo(struct roff_node *n) 1029b3257404Sschwarze { 1030b3257404Sschwarze if (n->end != ENDBODY_NOT) { 1031b3257404Sschwarze outflags |= MD_spc; 1032b3257404Sschwarze return; 1033b3257404Sschwarze } 1034b3257404Sschwarze 10359956bc5fSschwarze if (n->child == NULL && n->parent->head->child == NULL) 10369956bc5fSschwarze return; 1037b3257404Sschwarze 10389956bc5fSschwarze if (n->parent->tail != NULL && n->parent->tail->child != NULL) 1039b3257404Sschwarze outflags &= ~MD_spc; 10409956bc5fSschwarze else 1041b3257404Sschwarze outflags |= MD_spc; 1042b3257404Sschwarze } 1043b3257404Sschwarze 1044b3257404Sschwarze static int 1045b3257404Sschwarze md_pre_Fa(struct roff_node *n) 1046b3257404Sschwarze { 1047b3257404Sschwarze int am_Fa; 1048b3257404Sschwarze 1049b3257404Sschwarze am_Fa = n->tok == MDOC_Fa; 1050b3257404Sschwarze 1051b3257404Sschwarze if (am_Fa) 1052b3257404Sschwarze n = n->child; 1053b3257404Sschwarze 1054b3257404Sschwarze while (n != NULL) { 1055b3257404Sschwarze md_rawword("*"); 1056b3257404Sschwarze outflags &= ~MD_spc; 1057b3257404Sschwarze md_node(n); 1058b3257404Sschwarze outflags &= ~MD_spc; 1059b3257404Sschwarze md_rawword("*"); 1060b3257404Sschwarze if ((n = n->next) != NULL) 1061b3257404Sschwarze md_word(","); 1062b3257404Sschwarze } 1063b3257404Sschwarze return 0; 1064b3257404Sschwarze } 1065b3257404Sschwarze 1066b3257404Sschwarze static void 1067b3257404Sschwarze md_post_Fa(struct roff_node *n) 1068b3257404Sschwarze { 10697ebbefbeSschwarze struct roff_node *nn; 10707ebbefbeSschwarze 10717ebbefbeSschwarze if ((nn = roff_node_next(n)) != NULL && nn->tok == MDOC_Fa) 1072b3257404Sschwarze md_word(","); 1073b3257404Sschwarze } 1074b3257404Sschwarze 1075b3257404Sschwarze static int 1076b3257404Sschwarze md_pre_Fd(struct roff_node *n) 1077b3257404Sschwarze { 1078b3257404Sschwarze md_pre_syn(n); 1079b3257404Sschwarze md_pre_raw(n); 1080b3257404Sschwarze return 1; 1081b3257404Sschwarze } 1082b3257404Sschwarze 1083b3257404Sschwarze static void 1084b3257404Sschwarze md_post_Fd(struct roff_node *n) 1085b3257404Sschwarze { 1086b3257404Sschwarze md_post_raw(n); 1087b3257404Sschwarze outflags |= MD_br; 1088b3257404Sschwarze } 1089b3257404Sschwarze 10908e60e820Sschwarze static void 10918e60e820Sschwarze md_post_Fl(struct roff_node *n) 10928e60e820Sschwarze { 10937ebbefbeSschwarze struct roff_node *nn; 10947ebbefbeSschwarze 10958e60e820Sschwarze md_post_raw(n); 10967ebbefbeSschwarze if (n->child == NULL && (nn = roff_node_next(n)) != NULL && 10977ebbefbeSschwarze nn->type != ROFFT_TEXT && (nn->flags & NODE_LINE) == 0) 10988e60e820Sschwarze outflags &= ~MD_spc; 10998e60e820Sschwarze } 11008e60e820Sschwarze 1101b3257404Sschwarze static int 1102b3257404Sschwarze md_pre_Fn(struct roff_node *n) 1103b3257404Sschwarze { 1104b3257404Sschwarze md_pre_syn(n); 1105b3257404Sschwarze 1106b3257404Sschwarze if ((n = n->child) == NULL) 1107b3257404Sschwarze return 0; 1108b3257404Sschwarze 1109b3257404Sschwarze md_rawword("**"); 1110b3257404Sschwarze outflags &= ~MD_spc; 1111b3257404Sschwarze md_node(n); 1112b3257404Sschwarze outflags &= ~MD_spc; 1113b3257404Sschwarze md_rawword("**"); 1114b3257404Sschwarze outflags &= ~MD_spc; 1115b3257404Sschwarze md_word("("); 1116b3257404Sschwarze 1117b3257404Sschwarze if ((n = n->next) != NULL) 1118b3257404Sschwarze md_pre_Fa(n); 1119b3257404Sschwarze return 0; 1120b3257404Sschwarze } 1121b3257404Sschwarze 1122b3257404Sschwarze static void 1123b3257404Sschwarze md_post_Fn(struct roff_node *n) 1124b3257404Sschwarze { 1125b3257404Sschwarze md_word(")"); 1126b3257404Sschwarze if (n->flags & NODE_SYNPRETTY) { 1127b3257404Sschwarze md_word(";"); 1128b3257404Sschwarze outflags |= MD_sp; 1129b3257404Sschwarze } 1130b3257404Sschwarze } 1131b3257404Sschwarze 1132b3257404Sschwarze static int 1133b3257404Sschwarze md_pre_Fo(struct roff_node *n) 1134b3257404Sschwarze { 1135b3257404Sschwarze switch (n->type) { 1136b3257404Sschwarze case ROFFT_BLOCK: 1137b3257404Sschwarze md_pre_syn(n); 1138b3257404Sschwarze break; 1139b3257404Sschwarze case ROFFT_HEAD: 1140b3257404Sschwarze if (n->child == NULL) 1141b3257404Sschwarze return 0; 1142b3257404Sschwarze md_pre_raw(n); 1143b3257404Sschwarze break; 1144b3257404Sschwarze case ROFFT_BODY: 1145b3257404Sschwarze outflags &= ~(MD_spc | MD_nl); 1146b3257404Sschwarze md_word("("); 1147b3257404Sschwarze break; 1148b3257404Sschwarze default: 1149b3257404Sschwarze break; 1150b3257404Sschwarze } 1151b3257404Sschwarze return 1; 1152b3257404Sschwarze } 1153b3257404Sschwarze 1154b3257404Sschwarze static void 1155b3257404Sschwarze md_post_Fo(struct roff_node *n) 1156b3257404Sschwarze { 1157b3257404Sschwarze switch (n->type) { 1158b3257404Sschwarze case ROFFT_HEAD: 1159b3257404Sschwarze if (n->child != NULL) 1160b3257404Sschwarze md_post_raw(n); 1161b3257404Sschwarze break; 1162b3257404Sschwarze case ROFFT_BODY: 1163b3257404Sschwarze md_post_Fn(n); 1164b3257404Sschwarze break; 1165b3257404Sschwarze default: 1166b3257404Sschwarze break; 1167b3257404Sschwarze } 1168b3257404Sschwarze } 1169b3257404Sschwarze 1170b3257404Sschwarze static int 1171b3257404Sschwarze md_pre_In(struct roff_node *n) 1172b3257404Sschwarze { 1173b3257404Sschwarze if (n->flags & NODE_SYNPRETTY) { 1174b3257404Sschwarze md_pre_syn(n); 11755f580400Sschwarze md_rawword("**"); 1176b3257404Sschwarze outflags &= ~MD_spc; 1177b3257404Sschwarze md_word("#include <"); 1178b3257404Sschwarze } else { 1179b3257404Sschwarze md_word("<"); 1180b3257404Sschwarze outflags &= ~MD_spc; 11815f580400Sschwarze md_rawword("*"); 1182b3257404Sschwarze } 11835f580400Sschwarze outflags &= ~MD_spc; 1184b3257404Sschwarze return 1; 1185b3257404Sschwarze } 1186b3257404Sschwarze 1187b3257404Sschwarze static void 1188b3257404Sschwarze md_post_In(struct roff_node *n) 1189b3257404Sschwarze { 1190b3257404Sschwarze if (n->flags & NODE_SYNPRETTY) { 1191b3257404Sschwarze outflags &= ~MD_spc; 11925f580400Sschwarze md_rawword(">**"); 1193b3257404Sschwarze outflags |= MD_nl; 1194b3257404Sschwarze } else { 1195b3257404Sschwarze outflags &= ~MD_spc; 11965f580400Sschwarze md_rawword("*>"); 1197b3257404Sschwarze } 1198b3257404Sschwarze } 1199b3257404Sschwarze 1200b3257404Sschwarze static int 1201b3257404Sschwarze md_pre_It(struct roff_node *n) 1202b3257404Sschwarze { 1203b3257404Sschwarze struct roff_node *bln; 1204b3257404Sschwarze 1205b3257404Sschwarze switch (n->type) { 1206b3257404Sschwarze case ROFFT_BLOCK: 1207b3257404Sschwarze return 1; 1208b3257404Sschwarze 1209b3257404Sschwarze case ROFFT_HEAD: 1210b3257404Sschwarze bln = n->parent->parent; 12119e131d10Sschwarze if (bln->norm->Bl.comp == 0 && 12129e131d10Sschwarze bln->norm->Bl.type != LIST_column) 1213b3257404Sschwarze outflags |= MD_sp; 1214b3257404Sschwarze outflags |= MD_nl; 1215b3257404Sschwarze 1216b3257404Sschwarze switch (bln->norm->Bl.type) { 1217b3257404Sschwarze case LIST_item: 1218b3257404Sschwarze outflags |= MD_br; 1219b3257404Sschwarze return 0; 1220b3257404Sschwarze case LIST_inset: 1221b3257404Sschwarze case LIST_diag: 1222b3257404Sschwarze case LIST_ohang: 1223b3257404Sschwarze outflags |= MD_br; 1224b3257404Sschwarze return 1; 1225b3257404Sschwarze case LIST_tag: 1226b3257404Sschwarze case LIST_hang: 1227b3257404Sschwarze outflags |= MD_sp; 1228b3257404Sschwarze return 1; 1229b3257404Sschwarze case LIST_bullet: 1230b3257404Sschwarze md_rawword("*\t"); 1231b3257404Sschwarze break; 1232b3257404Sschwarze case LIST_dash: 1233b3257404Sschwarze case LIST_hyphen: 1234b3257404Sschwarze md_rawword("-\t"); 1235b3257404Sschwarze break; 1236b3257404Sschwarze case LIST_enum: 1237b3257404Sschwarze md_preword(); 1238b60e0d16Sschwarze if (bln->norm->Bl.count < 99) 1239b60e0d16Sschwarze bln->norm->Bl.count++; 1240b60e0d16Sschwarze printf("%d.\t", bln->norm->Bl.count); 1241b3257404Sschwarze escflags &= ~ESC_FON; 1242b3257404Sschwarze break; 12439e131d10Sschwarze case LIST_column: 12449e131d10Sschwarze outflags |= MD_br; 12459e131d10Sschwarze return 0; 1246b3257404Sschwarze default: 1247b3257404Sschwarze return 0; 1248b3257404Sschwarze } 1249b3257404Sschwarze outflags &= ~MD_spc; 1250b3257404Sschwarze outflags |= MD_nonl; 1251b3257404Sschwarze outcount = 0; 1252b3257404Sschwarze md_stack('\t'); 1253b3257404Sschwarze if (code_blocks || quote_blocks) 1254b3257404Sschwarze list_blocks++; 1255b3257404Sschwarze return 0; 1256b3257404Sschwarze 1257b3257404Sschwarze case ROFFT_BODY: 1258b3257404Sschwarze bln = n->parent->parent; 1259b3257404Sschwarze switch (bln->norm->Bl.type) { 1260b3257404Sschwarze case LIST_ohang: 1261b3257404Sschwarze outflags |= MD_br; 1262b3257404Sschwarze break; 1263b3257404Sschwarze case LIST_tag: 1264b3257404Sschwarze case LIST_hang: 1265b3257404Sschwarze md_pre_D1(n); 1266b3257404Sschwarze break; 1267b3257404Sschwarze default: 1268b3257404Sschwarze break; 1269b3257404Sschwarze } 1270b3257404Sschwarze return 1; 1271b3257404Sschwarze 1272b3257404Sschwarze default: 1273b3257404Sschwarze return 0; 1274b3257404Sschwarze } 1275b3257404Sschwarze } 1276b3257404Sschwarze 1277b3257404Sschwarze static void 1278b3257404Sschwarze md_post_It(struct roff_node *n) 1279b3257404Sschwarze { 1280b3257404Sschwarze struct roff_node *bln; 1281b3257404Sschwarze int i, nc; 1282b3257404Sschwarze 1283b3257404Sschwarze if (n->type != ROFFT_BODY) 1284b3257404Sschwarze return; 1285b3257404Sschwarze 1286b3257404Sschwarze bln = n->parent->parent; 1287b3257404Sschwarze switch (bln->norm->Bl.type) { 1288b3257404Sschwarze case LIST_bullet: 1289b3257404Sschwarze case LIST_dash: 1290b3257404Sschwarze case LIST_hyphen: 1291b3257404Sschwarze case LIST_enum: 1292b3257404Sschwarze md_stack((char)-1); 1293b3257404Sschwarze if (code_blocks || quote_blocks) 1294b3257404Sschwarze list_blocks--; 1295b3257404Sschwarze break; 1296b3257404Sschwarze case LIST_tag: 1297b3257404Sschwarze case LIST_hang: 1298b3257404Sschwarze md_post_D1(n); 1299b3257404Sschwarze break; 1300b3257404Sschwarze 1301b3257404Sschwarze case LIST_column: 1302b3257404Sschwarze if (n->next == NULL) 1303b3257404Sschwarze break; 1304b3257404Sschwarze 1305b3257404Sschwarze /* Calculate the array index of the current column. */ 1306b3257404Sschwarze 1307b3257404Sschwarze i = 0; 1308b3257404Sschwarze while ((n = n->prev) != NULL && n->type != ROFFT_HEAD) 1309b3257404Sschwarze i++; 1310b3257404Sschwarze 1311b3257404Sschwarze /* 1312b3257404Sschwarze * If a width was specified for this column, 1313b3257404Sschwarze * subtract what printed, and 1314b3257404Sschwarze * add the same spacing as in mdoc_term.c. 1315b3257404Sschwarze */ 1316b3257404Sschwarze 1317b3257404Sschwarze nc = bln->norm->Bl.ncols; 1318b3257404Sschwarze i = i < nc ? strlen(bln->norm->Bl.cols[i]) - outcount + 1319b3257404Sschwarze (nc < 5 ? 4 : nc == 5 ? 3 : 1) : 1; 1320b3257404Sschwarze if (i < 1) 1321b3257404Sschwarze i = 1; 1322b3257404Sschwarze while (i-- > 0) 1323b3257404Sschwarze putchar(' '); 1324b3257404Sschwarze 1325b3257404Sschwarze outflags &= ~MD_spc; 1326b3257404Sschwarze escflags &= ~ESC_FON; 1327b3257404Sschwarze outcount = 0; 1328b3257404Sschwarze break; 1329b3257404Sschwarze 1330b3257404Sschwarze default: 1331b3257404Sschwarze break; 1332b3257404Sschwarze } 1333b3257404Sschwarze } 1334b3257404Sschwarze 1335b3257404Sschwarze static void 1336b3257404Sschwarze md_post_Lb(struct roff_node *n) 1337b3257404Sschwarze { 1338b3257404Sschwarze if (n->sec == SEC_LIBRARY) 1339b3257404Sschwarze outflags |= MD_br; 1340b3257404Sschwarze } 1341b3257404Sschwarze 134275d8c96aSschwarze static void 134375d8c96aSschwarze md_uri(const char *s) 1344b3257404Sschwarze { 134575d8c96aSschwarze while (*s != '\0') { 1346433f0baeSschwarze if (strchr("%()<>", *s) != NULL) { 134778ed643eSschwarze printf("%%%2.2hhX", *s); 134878ed643eSschwarze outcount += 3; 134978ed643eSschwarze } else { 135078ed643eSschwarze putchar(*s); 135178ed643eSschwarze outcount++; 135278ed643eSschwarze } 135375d8c96aSschwarze s++; 135475d8c96aSschwarze } 135578ed643eSschwarze } 135678ed643eSschwarze 135775d8c96aSschwarze static int 135875d8c96aSschwarze md_pre_Lk(struct roff_node *n) 135975d8c96aSschwarze { 1360eb4d0f30Sschwarze const struct roff_node *link, *descr, *punct; 136175d8c96aSschwarze 136275d8c96aSschwarze if ((link = n->child) == NULL) 136375d8c96aSschwarze return 0; 136475d8c96aSschwarze 1365eb4d0f30Sschwarze /* Find beginning of trailing punctuation. */ 1366eb4d0f30Sschwarze punct = n->last; 1367eb4d0f30Sschwarze while (punct != link && punct->flags & NODE_DELIMC) 1368eb4d0f30Sschwarze punct = punct->prev; 1369eb4d0f30Sschwarze punct = punct->next; 1370eb4d0f30Sschwarze 137196aebfb3Sschwarze /* Link text. */ 137296aebfb3Sschwarze descr = link->next; 1373eb4d0f30Sschwarze if (descr == punct) 137496aebfb3Sschwarze descr = link; /* no text */ 137575d8c96aSschwarze md_rawword("["); 1376b3257404Sschwarze outflags &= ~MD_spc; 137775d8c96aSschwarze do { 137875d8c96aSschwarze md_word(descr->string); 137996aebfb3Sschwarze descr = descr->next; 1380eb4d0f30Sschwarze } while (descr != punct); 138175d8c96aSschwarze outflags &= ~MD_spc; 138296aebfb3Sschwarze 138396aebfb3Sschwarze /* Link target. */ 138475d8c96aSschwarze md_rawword("]("); 138575d8c96aSschwarze md_uri(link->string); 138675d8c96aSschwarze outflags &= ~MD_spc; 138775d8c96aSschwarze md_rawword(")"); 138896aebfb3Sschwarze 138996aebfb3Sschwarze /* Trailing punctuation. */ 1390eb4d0f30Sschwarze while (punct != NULL) { 1391eb4d0f30Sschwarze md_word(punct->string); 1392eb4d0f30Sschwarze punct = punct->next; 139396aebfb3Sschwarze } 139475d8c96aSschwarze return 0; 139575d8c96aSschwarze } 139675d8c96aSschwarze 139775d8c96aSschwarze static int 139875d8c96aSschwarze md_pre_Mt(struct roff_node *n) 139975d8c96aSschwarze { 140075d8c96aSschwarze const struct roff_node *nch; 140175d8c96aSschwarze 140275d8c96aSschwarze md_rawword("["); 140375d8c96aSschwarze outflags &= ~MD_spc; 140475d8c96aSschwarze for (nch = n->child; nch != NULL; nch = nch->next) 140575d8c96aSschwarze md_word(nch->string); 140675d8c96aSschwarze outflags &= ~MD_spc; 140775d8c96aSschwarze md_rawword("](mailto:"); 140875d8c96aSschwarze for (nch = n->child; nch != NULL; nch = nch->next) { 140975d8c96aSschwarze md_uri(nch->string); 141075d8c96aSschwarze if (nch->next != NULL) { 141175d8c96aSschwarze putchar(' '); 141275d8c96aSschwarze outcount++; 141375d8c96aSschwarze } 141475d8c96aSschwarze } 141575d8c96aSschwarze outflags &= ~MD_spc; 141675d8c96aSschwarze md_rawword(")"); 1417b3257404Sschwarze return 0; 1418b3257404Sschwarze } 1419b3257404Sschwarze 1420b3257404Sschwarze static int 1421b3257404Sschwarze md_pre_Nd(struct roff_node *n) 1422b3257404Sschwarze { 1423b3257404Sschwarze outflags &= ~MD_nl; 1424b3257404Sschwarze outflags |= MD_spc; 1425b3257404Sschwarze md_word("-"); 1426b3257404Sschwarze return 1; 1427b3257404Sschwarze } 1428b3257404Sschwarze 1429b3257404Sschwarze static int 1430b3257404Sschwarze md_pre_Nm(struct roff_node *n) 1431b3257404Sschwarze { 1432b3257404Sschwarze switch (n->type) { 1433b3257404Sschwarze case ROFFT_BLOCK: 1434b3257404Sschwarze outflags |= MD_Bk; 1435b3257404Sschwarze md_pre_syn(n); 1436b3257404Sschwarze break; 1437b3257404Sschwarze case ROFFT_HEAD: 1438b3257404Sschwarze case ROFFT_ELEM: 1439b3257404Sschwarze md_pre_raw(n); 1440b3257404Sschwarze break; 1441b3257404Sschwarze default: 1442b3257404Sschwarze break; 1443b3257404Sschwarze } 1444b3257404Sschwarze return 1; 1445b3257404Sschwarze } 1446b3257404Sschwarze 1447b3257404Sschwarze static void 1448b3257404Sschwarze md_post_Nm(struct roff_node *n) 1449b3257404Sschwarze { 1450b3257404Sschwarze switch (n->type) { 1451b3257404Sschwarze case ROFFT_BLOCK: 1452b3257404Sschwarze outflags &= ~MD_Bk; 1453b3257404Sschwarze break; 1454b3257404Sschwarze case ROFFT_HEAD: 1455b3257404Sschwarze case ROFFT_ELEM: 1456b3257404Sschwarze md_post_raw(n); 1457b3257404Sschwarze break; 1458b3257404Sschwarze default: 1459b3257404Sschwarze break; 1460b3257404Sschwarze } 1461b3257404Sschwarze } 1462b3257404Sschwarze 1463b3257404Sschwarze static int 1464b3257404Sschwarze md_pre_No(struct roff_node *n) 1465b3257404Sschwarze { 1466b3257404Sschwarze outflags |= MD_spc_force; 1467b3257404Sschwarze return 1; 1468b3257404Sschwarze } 1469b3257404Sschwarze 1470b3257404Sschwarze static int 1471b3257404Sschwarze md_pre_Ns(struct roff_node *n) 1472b3257404Sschwarze { 1473b3257404Sschwarze outflags &= ~MD_spc; 1474b3257404Sschwarze return 0; 1475b3257404Sschwarze } 1476b3257404Sschwarze 1477b3257404Sschwarze static void 1478b3257404Sschwarze md_post_Pf(struct roff_node *n) 1479b3257404Sschwarze { 1480b3257404Sschwarze if (n->next != NULL && (n->next->flags & NODE_LINE) == 0) 1481b3257404Sschwarze outflags &= ~MD_spc; 1482b3257404Sschwarze } 1483b3257404Sschwarze 1484b3257404Sschwarze static int 1485b3257404Sschwarze md_pre_Pp(struct roff_node *n) 1486b3257404Sschwarze { 1487b3257404Sschwarze outflags |= MD_sp; 1488b3257404Sschwarze return 0; 1489b3257404Sschwarze } 1490b3257404Sschwarze 1491b3257404Sschwarze static int 1492b3257404Sschwarze md_pre_Rs(struct roff_node *n) 1493b3257404Sschwarze { 1494b3257404Sschwarze if (n->sec == SEC_SEE_ALSO) 1495b3257404Sschwarze outflags |= MD_sp; 1496b3257404Sschwarze return 1; 1497b3257404Sschwarze } 1498b3257404Sschwarze 1499b3257404Sschwarze static int 1500b3257404Sschwarze md_pre_Sh(struct roff_node *n) 1501b3257404Sschwarze { 1502b3257404Sschwarze switch (n->type) { 15032fe9aa39Sschwarze case ROFFT_BLOCK: 15042fe9aa39Sschwarze if (n->sec == SEC_AUTHORS) 15052fe9aa39Sschwarze outflags &= ~(MD_An_split | MD_An_nosplit); 15062fe9aa39Sschwarze break; 1507b3257404Sschwarze case ROFFT_HEAD: 1508b3257404Sschwarze outflags |= MD_sp; 1509b3257404Sschwarze md_rawword(n->tok == MDOC_Sh ? "#" : "##"); 1510b3257404Sschwarze break; 1511b3257404Sschwarze case ROFFT_BODY: 1512b3257404Sschwarze outflags |= MD_sp; 1513b3257404Sschwarze break; 1514b3257404Sschwarze default: 1515b3257404Sschwarze break; 1516b3257404Sschwarze } 1517b3257404Sschwarze return 1; 1518b3257404Sschwarze } 1519b3257404Sschwarze 1520b3257404Sschwarze static int 1521b3257404Sschwarze md_pre_Sm(struct roff_node *n) 1522b3257404Sschwarze { 1523b3257404Sschwarze if (n->child == NULL) 1524b3257404Sschwarze outflags ^= MD_Sm; 1525b3257404Sschwarze else if (strcmp("on", n->child->string) == 0) 1526b3257404Sschwarze outflags |= MD_Sm; 1527b3257404Sschwarze else 1528b3257404Sschwarze outflags &= ~MD_Sm; 1529b3257404Sschwarze 1530b3257404Sschwarze if (outflags & MD_Sm) 1531b3257404Sschwarze outflags |= MD_spc; 1532b3257404Sschwarze 1533b3257404Sschwarze return 0; 1534b3257404Sschwarze } 1535b3257404Sschwarze 1536b3257404Sschwarze static int 1537b3257404Sschwarze md_pre_Vt(struct roff_node *n) 1538b3257404Sschwarze { 1539b3257404Sschwarze switch (n->type) { 1540b3257404Sschwarze case ROFFT_BLOCK: 1541b3257404Sschwarze md_pre_syn(n); 1542b3257404Sschwarze return 1; 1543b3257404Sschwarze case ROFFT_BODY: 1544b3257404Sschwarze case ROFFT_ELEM: 1545b3257404Sschwarze md_pre_raw(n); 1546b3257404Sschwarze return 1; 1547b3257404Sschwarze default: 1548b3257404Sschwarze return 0; 1549b3257404Sschwarze } 1550b3257404Sschwarze } 1551b3257404Sschwarze 1552b3257404Sschwarze static void 1553b3257404Sschwarze md_post_Vt(struct roff_node *n) 1554b3257404Sschwarze { 1555b3257404Sschwarze switch (n->type) { 1556b3257404Sschwarze case ROFFT_BODY: 1557b3257404Sschwarze case ROFFT_ELEM: 1558b3257404Sschwarze md_post_raw(n); 1559b3257404Sschwarze break; 1560b3257404Sschwarze default: 1561b3257404Sschwarze break; 1562b3257404Sschwarze } 1563b3257404Sschwarze } 1564b3257404Sschwarze 1565b3257404Sschwarze static int 1566b3257404Sschwarze md_pre_Xr(struct roff_node *n) 1567b3257404Sschwarze { 1568b3257404Sschwarze n = n->child; 1569b3257404Sschwarze if (n == NULL) 1570b3257404Sschwarze return 0; 1571b3257404Sschwarze md_node(n); 1572b3257404Sschwarze n = n->next; 1573b3257404Sschwarze if (n == NULL) 1574b3257404Sschwarze return 0; 1575b3257404Sschwarze outflags &= ~MD_spc; 1576b3257404Sschwarze md_word("("); 1577b3257404Sschwarze md_node(n); 1578b3257404Sschwarze md_word(")"); 1579b3257404Sschwarze return 0; 1580b3257404Sschwarze } 1581b3257404Sschwarze 1582b3257404Sschwarze static int 1583*bcf2ba68Sschwarze md_pre__R(struct roff_node *n) 1584*bcf2ba68Sschwarze { 1585*bcf2ba68Sschwarze const unsigned char *cp; 1586*bcf2ba68Sschwarze const char *arg; 1587*bcf2ba68Sschwarze 1588*bcf2ba68Sschwarze arg = n->child->string; 1589*bcf2ba68Sschwarze 1590*bcf2ba68Sschwarze if (strncmp(arg, "RFC ", 4) != 0) 1591*bcf2ba68Sschwarze return 1; 1592*bcf2ba68Sschwarze cp = arg += 4; 1593*bcf2ba68Sschwarze while (isdigit(*cp)) 1594*bcf2ba68Sschwarze cp++; 1595*bcf2ba68Sschwarze if (*cp != '\0') 1596*bcf2ba68Sschwarze return 1; 1597*bcf2ba68Sschwarze 1598*bcf2ba68Sschwarze md_rawword("[RFC "); 1599*bcf2ba68Sschwarze outflags &= ~MD_spc; 1600*bcf2ba68Sschwarze md_rawword(arg); 1601*bcf2ba68Sschwarze outflags &= ~MD_spc; 1602*bcf2ba68Sschwarze md_rawword("](http://www.rfc-editor.org/rfc/rfc"); 1603*bcf2ba68Sschwarze outflags &= ~MD_spc; 1604*bcf2ba68Sschwarze md_rawword(arg); 1605*bcf2ba68Sschwarze outflags &= ~MD_spc; 1606*bcf2ba68Sschwarze md_rawword(".html)"); 1607*bcf2ba68Sschwarze return 0; 1608*bcf2ba68Sschwarze } 1609*bcf2ba68Sschwarze 1610*bcf2ba68Sschwarze static int 1611b3257404Sschwarze md_pre__T(struct roff_node *n) 1612b3257404Sschwarze { 16136b7d675aSschwarze if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T) 1614b3257404Sschwarze md_word("\""); 1615b3257404Sschwarze else 1616b3257404Sschwarze md_rawword("*"); 1617b3257404Sschwarze outflags &= ~MD_spc; 1618b3257404Sschwarze return 1; 1619b3257404Sschwarze } 1620b3257404Sschwarze 1621b3257404Sschwarze static void 1622b3257404Sschwarze md_post__T(struct roff_node *n) 1623b3257404Sschwarze { 1624b3257404Sschwarze outflags &= ~MD_spc; 16256b7d675aSschwarze if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T) 1626b3257404Sschwarze md_word("\""); 1627b3257404Sschwarze else 1628b3257404Sschwarze md_rawword("*"); 1629b3257404Sschwarze md_post_pc(n); 1630b3257404Sschwarze } 1631b3257404Sschwarze 1632b3257404Sschwarze static int 1633b3257404Sschwarze md_pre_br(struct roff_node *n) 1634b3257404Sschwarze { 1635b3257404Sschwarze outflags |= MD_br; 1636b3257404Sschwarze return 0; 1637b3257404Sschwarze } 1638