1139749Simp/* $Id: mdoc_markdown.c,v 1.37 2021/08/10 12:55:03 schwarze Exp $ */ 2197404Sjoel/* 3197404Sjoel * Copyright (c) 2017, 2018, 2020 Ingo Schwarze <schwarze@openbsd.org> 4197404Sjoel * 550724Scg * Permission to use, copy, modify, and distribute this software for any 6197404Sjoel * purpose with or without fee is hereby granted, provided that the above 7197404Sjoel * copyright notice and this permission notice appear in all copies. 8197404Sjoel * 9197404Sjoel * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 10197404Sjoel * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11197404Sjoel * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 12197404Sjoel * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13197404Sjoel * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14197404Sjoel * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15197404Sjoel * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16197404Sjoel * 17197404Sjoel * Markdown formatter for mdoc(7) used by mandoc(1). 18197404Sjoel */ 19197404Sjoel#include "config.h" 20197404Sjoel 21197404Sjoel#include <sys/types.h> 22197404Sjoel 23197404Sjoel#include <assert.h> 24197404Sjoel#include <ctype.h> 25197404Sjoel#include <stdio.h> 26197404Sjoel#include <stdlib.h> 27197404Sjoel#include <string.h> 28197404Sjoel 29119853Scg#include "mandoc_aux.h" 30197404Sjoel#include "mandoc.h" 3150724Scg#include "roff.h" 3250724Scg#include "mdoc.h" 3350724Scg#include "main.h" 3450724Scg 3550724Scgstruct md_act { 3650724Scg int (*cond)(struct roff_node *); 3750724Scg int (*pre)(struct roff_node *); 3850724Scg void (*post)(struct roff_node *); 3950724Scg const char *prefix; /* pre-node string constant */ 4050724Scg const char *suffix; /* post-node string constant */ 4150724Scg}; 4250724Scg 4350724Scgstatic void md_nodelist(struct roff_node *); 4450724Scgstatic void md_node(struct roff_node *); 4550724Scgstatic const char *md_stack(char); 4650724Scgstatic void md_preword(void); 4750724Scgstatic void md_rawword(const char *); 4850724Scgstatic void md_word(const char *); 4950724Scgstatic void md_named(const char *); 5050724Scgstatic void md_char(unsigned char); 5150724Scgstatic void md_uri(const char *); 5250724Scg 5350724Scgstatic int md_cond_head(struct roff_node *); 5450724Scgstatic int md_cond_body(struct roff_node *); 5550724Scg 5650724Scgstatic int md_pre_abort(struct roff_node *); 5750724Scgstatic int md_pre_raw(struct roff_node *); 5850724Scgstatic int md_pre_word(struct roff_node *); 5950724Scgstatic int md_pre_skip(struct roff_node *); 6050724Scgstatic void md_pre_syn(struct roff_node *); 6150724Scgstatic int md_pre_An(struct roff_node *); 6250724Scgstatic int md_pre_Ap(struct roff_node *); 6350724Scgstatic int md_pre_Bd(struct roff_node *); 6450724Scgstatic int md_pre_Bk(struct roff_node *); 6553413Srogerstatic int md_pre_Bl(struct roff_node *); 66197404Sjoelstatic int md_pre_D1(struct roff_node *); 67197404Sjoelstatic int md_pre_Dl(struct roff_node *); 68197404Sjoelstatic int md_pre_En(struct roff_node *); 6953413Srogerstatic int md_pre_Eo(struct roff_node *); 7053413Srogerstatic int md_pre_Fa(struct roff_node *); 7154831Scgstatic int md_pre_Fd(struct roff_node *); 7254831Scgstatic int md_pre_Fn(struct roff_node *); 7353413Srogerstatic int md_pre_Fo(struct roff_node *); 7453413Srogerstatic int md_pre_In(struct roff_node *); 7553413Srogerstatic int md_pre_It(struct roff_node *); 76193640Sariffstatic int md_pre_Lk(struct roff_node *); 77193640Sariffstatic int md_pre_Mt(struct roff_node *); 78193640Sariffstatic int md_pre_Nd(struct roff_node *); 79193640Sariffstatic int md_pre_Nm(struct roff_node *); 8053465Scgstatic int md_pre_No(struct roff_node *); 8153465Scgstatic int md_pre_Ns(struct roff_node *); 8253465Scgstatic int md_pre_Pp(struct roff_node *); 8350724Scgstatic int md_pre_Rs(struct roff_node *); 84119287Simpstatic int md_pre_Sh(struct roff_node *); 85119287Simpstatic int md_pre_Sm(struct roff_node *); 8650724Scgstatic int md_pre_Vt(struct roff_node *); 8753413Srogerstatic int md_pre_Xr(struct roff_node *); 8853413Srogerstatic int md_pre__T(struct roff_node *); 8970134Scgstatic int md_pre_br(struct roff_node *); 9070134Scg 9182180Scgstatic void md_post_raw(struct roff_node *); 9282180Scgstatic void md_post_word(struct roff_node *); 9350724Scgstatic void md_post_pc(struct roff_node *); 9450724Scgstatic void md_post_Bk(struct roff_node *); 9550724Scgstatic void md_post_Bl(struct roff_node *); 9650724Scgstatic void md_post_D1(struct roff_node *); 9753413Srogerstatic void md_post_En(struct roff_node *); 9856154Speterstatic void md_post_Eo(struct roff_node *); 9976086Scgstatic void md_post_Fa(struct roff_node *); 100119548Sorionstatic void md_post_Fd(struct roff_node *); 10150724Scgstatic void md_post_Fl(struct roff_node *); 10278033Scgstatic void md_post_Fn(struct roff_node *); 10376086Scgstatic void md_post_Fo(struct roff_node *); 10476086Scgstatic void md_post_In(struct roff_node *); 10576086Scgstatic void md_post_It(struct roff_node *); 10676086Scgstatic void md_post_Lb(struct roff_node *); 10776086Scgstatic void md_post_Nm(struct roff_node *); 10876086Scgstatic void md_post_Pf(struct roff_node *); 10976086Scgstatic void md_post_Vt(struct roff_node *); 11076086Scgstatic void md_post__T(struct roff_node *); 11176086Scg 11276086Scgstatic const struct md_act md_acts[MDOC_MAX - MDOC_Dd] = { 11395678Scg { NULL, NULL, NULL, NULL, NULL }, /* Dd */ 11476086Scg { NULL, NULL, NULL, NULL, NULL }, /* Dt */ 115119548Sorion { NULL, NULL, NULL, NULL, NULL }, /* Os */ 116119548Sorion { NULL, md_pre_Sh, NULL, NULL, NULL }, /* Sh */ 11784658Scg { NULL, md_pre_Sh, NULL, NULL, NULL }, /* Ss */ 11859019Scg { NULL, md_pre_Pp, NULL, NULL, NULL }, /* Pp */ 119152419Sariff { md_cond_body, md_pre_D1, md_post_D1, NULL, NULL }, /* D1 */ 120152419Sariff { md_cond_body, md_pre_Dl, md_post_D1, NULL, NULL }, /* Dl */ 121152419Sariff { md_cond_body, md_pre_Bd, md_post_D1, NULL, NULL }, /* Bd */ 122152419Sariff { NULL, NULL, NULL, NULL, NULL }, /* Ed */ 123152419Sariff { md_cond_body, md_pre_Bl, md_post_Bl, NULL, NULL }, /* Bl */ 124152419Sariff { NULL, NULL, NULL, NULL, NULL }, /* El */ 125167648Sariff { NULL, md_pre_It, md_post_It, NULL, NULL }, /* It */ 126167648Sariff { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Ad */ 127167648Sariff { NULL, md_pre_An, NULL, NULL, NULL }, /* An */ 128167648Sariff { NULL, md_pre_Ap, NULL, NULL, NULL }, /* Ap */ 129167648Sariff { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Ar */ 130152419Sariff { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Cd */ 131152419Sariff { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Cm */ 132152419Sariff { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Dv */ 13350724Scg { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Er */ 13450724Scg { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Ev */ 13550724Scg { NULL, NULL, NULL, NULL, NULL }, /* Ex */ 13655209Scg { NULL, md_pre_Fa, md_post_Fa, NULL, NULL }, /* Fa */ 13750724Scg { NULL, md_pre_Fd, md_post_Fd, "**", "**" }, /* Fd */ 13874763Scg { NULL, md_pre_raw, md_post_Fl, "**-", "**" }, /* Fl */ 13974763Scg { NULL, md_pre_Fn, md_post_Fn, NULL, NULL }, /* Fn */ 140152419Sariff { NULL, md_pre_Fd, md_post_raw, "*", "*" }, /* Ft */ 141152419Sariff { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Ic */ 142164614Sariff { NULL, md_pre_In, md_post_In, NULL, NULL }, /* In */ 143164614Sariff { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Li */ 144164614Sariff { md_cond_head, md_pre_Nd, NULL, NULL, NULL }, /* Nd */ 14555209Scg { NULL, md_pre_Nm, md_post_Nm, "**", "**" }, /* Nm */ 14650724Scg { md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Op */ 147152419Sariff { NULL, md_pre_abort, NULL, NULL, NULL }, /* Ot */ 148152419Sariff { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Pa */ 149152419Sariff { NULL, NULL, NULL, NULL, NULL }, /* Rv */ 150152419Sariff { NULL, NULL, NULL, NULL, NULL }, /* St */ 151152419Sariff { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Va */ 152152419Sariff { NULL, md_pre_Vt, md_post_Vt, "*", "*" }, /* Vt */ 153152419Sariff { NULL, md_pre_Xr, NULL, NULL, NULL }, /* Xr */ 154152419Sariff { NULL, NULL, md_post_pc, NULL, NULL }, /* %A */ 155152419Sariff { NULL, md_pre_raw, md_post_pc, "*", "*" }, /* %B */ 156152419Sariff { NULL, NULL, md_post_pc, NULL, NULL }, /* %D */ 157152419Sariff { NULL, md_pre_raw, md_post_pc, "*", "*" }, /* %I */ 158152419Sariff { NULL, md_pre_raw, md_post_pc, "*", "*" }, /* %J */ 159152419Sariff { NULL, NULL, md_post_pc, NULL, NULL }, /* %N */ 160152419Sariff { NULL, NULL, md_post_pc, NULL, NULL }, /* %O */ 161152419Sariff { NULL, NULL, md_post_pc, NULL, NULL }, /* %P */ 162152419Sariff { NULL, NULL, md_post_pc, NULL, NULL }, /* %R */ 163152419Sariff { NULL, md_pre__T, md_post__T, NULL, NULL }, /* %T */ 164152419Sariff { NULL, NULL, md_post_pc, NULL, NULL }, /* %V */ 165152419Sariff { NULL, NULL, NULL, NULL, NULL }, /* Ac */ 166152419Sariff { md_cond_body, md_pre_word, md_post_word, "<", ">" }, /* Ao */ 167152419Sariff { md_cond_body, md_pre_word, md_post_word, "<", ">" }, /* Aq */ 168152419Sariff { NULL, NULL, NULL, NULL, NULL }, /* At */ 169152419Sariff { NULL, NULL, NULL, NULL, NULL }, /* Bc */ 170152419Sariff { NULL, NULL, NULL, NULL, NULL }, /* Bf XXX not implemented */ 171152419Sariff { md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Bo */ 172152419Sariff { md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Bq */ 173152419Sariff { NULL, NULL, NULL, NULL, NULL }, /* Bsx */ 174152419Sariff { NULL, NULL, NULL, NULL, NULL }, /* Bx */ 175152419Sariff { NULL, NULL, NULL, NULL, NULL }, /* Db */ 176152419Sariff { NULL, NULL, NULL, NULL, NULL }, /* Dc */ 177152419Sariff { md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Do */ 178152419Sariff { md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Dq */ 179152419Sariff { NULL, NULL, NULL, NULL, NULL }, /* Ec */ 180152419Sariff { NULL, NULL, NULL, NULL, NULL }, /* Ef */ 181152419Sariff { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Em */ 182152419Sariff { md_cond_body, md_pre_Eo, md_post_Eo, NULL, NULL }, /* Eo */ 183152419Sariff { NULL, NULL, NULL, NULL, NULL }, /* Fx */ 184152419Sariff { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Ms */ 185152419Sariff { NULL, md_pre_No, NULL, NULL, NULL }, /* No */ 186152419Sariff { NULL, md_pre_Ns, NULL, NULL, NULL }, /* Ns */ 187152419Sariff { NULL, NULL, NULL, NULL, NULL }, /* Nx */ 188152419Sariff { NULL, NULL, NULL, NULL, NULL }, /* Ox */ 189152419Sariff { NULL, NULL, NULL, NULL, NULL }, /* Pc */ 190152419Sariff { NULL, NULL, md_post_Pf, NULL, NULL }, /* Pf */ 191152419Sariff { md_cond_body, md_pre_word, md_post_word, "(", ")" }, /* Po */ 192152419Sariff { md_cond_body, md_pre_word, md_post_word, "(", ")" }, /* Pq */ 193152419Sariff { NULL, NULL, NULL, NULL, NULL }, /* Qc */ 194152419Sariff { md_cond_body, md_pre_raw, md_post_raw, "'`", "`'" }, /* Ql */ 195152419Sariff { md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Qo */ 196152419Sariff { md_cond_body, md_pre_word, md_post_word, "\"", "\"" }, /* Qq */ 197152419Sariff { NULL, NULL, NULL, NULL, NULL }, /* Re */ 198152419Sariff { md_cond_body, md_pre_Rs, NULL, NULL, NULL }, /* Rs */ 199152419Sariff { NULL, NULL, NULL, NULL, NULL }, /* Sc */ 200152419Sariff { md_cond_body, md_pre_word, md_post_word, "'", "'" }, /* So */ 201152419Sariff { md_cond_body, md_pre_word, md_post_word, "'", "'" }, /* Sq */ 202152419Sariff { NULL, md_pre_Sm, NULL, NULL, NULL }, /* Sm */ 203152419Sariff { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Sx */ 204152419Sariff { NULL, md_pre_raw, md_post_raw, "**", "**" }, /* Sy */ 205152419Sariff { NULL, md_pre_raw, md_post_raw, "`", "`" }, /* Tn */ 206152419Sariff { NULL, NULL, NULL, NULL, NULL }, /* Ux */ 207152419Sariff { NULL, NULL, NULL, NULL, NULL }, /* Xc */ 208152419Sariff { NULL, NULL, NULL, NULL, NULL }, /* Xo */ 20955209Scg { NULL, md_pre_Fo, md_post_Fo, "**", "**" }, /* Fo */ 21050724Scg { NULL, NULL, NULL, NULL, NULL }, /* Fc */ 21150724Scg { md_cond_body, md_pre_word, md_post_word, "[", "]" }, /* Oo */ 21250724Scg { NULL, NULL, NULL, NULL, NULL }, /* Oc */ 21350724Scg { NULL, md_pre_Bk, md_post_Bk, NULL, NULL }, /* Bk */ 21465644Scg { NULL, NULL, NULL, NULL, NULL }, /* Ek */ 21565644Scg { NULL, NULL, NULL, NULL, NULL }, /* Bt */ 21665644Scg { NULL, NULL, NULL, NULL, NULL }, /* Hf */ 21765644Scg { NULL, md_pre_raw, md_post_raw, "*", "*" }, /* Fr */ 21859019Scg { NULL, NULL, NULL, NULL, NULL }, /* Ud */ 21954831Scg { NULL, NULL, md_post_Lb, NULL, NULL }, /* Lb */ 220164614Sariff { NULL, md_pre_abort, NULL, NULL, NULL }, /* Lp */ 22184658Scg { NULL, md_pre_Lk, NULL, NULL, NULL }, /* Lk */ 22250724Scg { NULL, md_pre_Mt, NULL, NULL, NULL }, /* Mt */ 223150832Snetchild { md_cond_body, md_pre_word, md_post_word, "{", "}" }, /* Brq */ 224150832Snetchild { md_cond_body, md_pre_word, md_post_word, "{", "}" }, /* Bro */ 225152419Sariff { NULL, NULL, NULL, NULL, NULL }, /* Brc */ 226152419Sariff { NULL, NULL, md_post_pc, NULL, NULL }, /* %C */ 227148591Snetchild { NULL, md_pre_skip, NULL, NULL, NULL }, /* Es */ 228164614Sariff { md_cond_body, md_pre_En, md_post_En, NULL, NULL }, /* En */ 229164614Sariff { NULL, NULL, NULL, NULL, NULL }, /* Dx */ 23055209Scg { NULL, NULL, md_post_pc, NULL, NULL }, /* %Q */ 23150724Scg { NULL, md_pre_Lk, md_post_pc, NULL, NULL }, /* %U */ 232150832Snetchild { NULL, NULL, NULL, NULL, NULL }, /* Ta */ 233150832Snetchild { NULL, md_pre_skip, NULL, NULL, NULL }, /* Tg */ 234150832Snetchild}; 23554831Scgstatic const struct md_act *md_act(enum roff_tok); 23653413Sroger 23754831Scgstatic int outflags; 238150832Snetchild#define MD_spc (1 << 0) /* Blank character before next word. */ 239164614Sariff#define MD_spc_force (1 << 1) /* Even before trailing punctuation. */ 240164614Sariff#define MD_nonl (1 << 2) /* Prevent linebreak in markdown code. */ 241164614Sariff#define MD_nl (1 << 3) /* Break markdown code line. */ 242164614Sariff#define MD_br (1 << 4) /* Insert an output line break. */ 243150832Snetchild#define MD_sp (1 << 5) /* Insert a paragraph break. */ 24454831Scg#define MD_Sm (1 << 6) /* Horizontal spacing mode. */ 245164614Sariff#define MD_Bk (1 << 7) /* Word keep mode. */ 24650724Scg#define MD_An_split (1 << 8) /* Author mode is "split". */ 247164614Sariff#define MD_An_nosplit (1 << 9) /* Author mode is "nosplit". */ 248193640Sariff 249193640Sariffstatic int escflags; /* Escape in generated markdown code: */ 250193640Sariff#define ESC_BOL (1 << 0) /* "#*+-" near the beginning of a line. */ 251193640Sariff#define ESC_NUM (1 << 1) /* "." after a leading number. */ 25264881Scg#define ESC_HYP (1 << 2) /* "(" immediately after "]". */ 25350724Scg#define ESC_SQU (1 << 4) /* "]" when "[" is open. */ 254150832Snetchild#define ESC_FON (1 << 5) /* "*" immediately after unrelated "*". */ 25550724Scg#define ESC_EOL (1 << 6) /* " " at the and of a line. */ 25650724Scg 25750724Scgstatic int code_blocks, quote_blocks, list_blocks; 25850724Scgstatic int outcount; 25950724Scg 26050724Scg 26150724Scgstatic const struct md_act * 26250724Scgmd_act(enum roff_tok tok) 26350724Scg{ 264152419Sariff assert(tok >= MDOC_Dd && tok <= MDOC_MAX); 26550724Scg return md_acts + (tok - MDOC_Dd); 26650724Scg} 26750724Scg 26850724Scgvoid 26950724Scgmarkdown_mdoc(void *arg, const struct roff_meta *mdoc) 27050724Scg{ 27150724Scg outflags = MD_Sm; 27250724Scg md_word(mdoc->title); 27354831Scg if (mdoc->msec != NULL) { 27454831Scg outflags &= ~MD_spc; 27550724Scg md_word("("); 276164614Sariff md_word(mdoc->msec); 277148591Snetchild md_word(")"); 278148591Snetchild } 279148591Snetchild md_word("-"); 280148591Snetchild md_word(mdoc->vol); 281164614Sariff if (mdoc->arch != NULL) { 282148591Snetchild md_word("("); 283164614Sariff md_word(mdoc->arch); 284148591Snetchild md_word(")"); 285164614Sariff } 286148591Snetchild outflags |= MD_sp; 287164614Sariff 288148591Snetchild md_nodelist(mdoc->first->child); 289148591Snetchild 290148591Snetchild outflags |= MD_sp; 291150832Snetchild md_word(mdoc->os); 292164614Sariff md_word("-"); 293148591Snetchild md_word(mdoc->date); 294148591Snetchild putchar('\n'); 295148591Snetchild} 296148591Snetchild 297148591Snetchildstatic void 298148591Snetchildmd_nodelist(struct roff_node *n) 299148591Snetchild{ 300148591Snetchild while (n != NULL) { 301148591Snetchild md_node(n); 302148591Snetchild n = n->next; 303148591Snetchild } 304148591Snetchild} 305148591Snetchild 306148591Snetchildstatic void 307148591Snetchildmd_node(struct roff_node *n) 30870134Scg{ 30970134Scg const struct md_act *act; 31070134Scg int cond, process_children; 31150724Scg 31274763Scg if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT) 31350724Scg return; 314152419Sariff 31550724Scg if (outflags & MD_nonl) 316164614Sariff outflags &= ~(MD_nl | MD_sp); 31750724Scg else if (outflags & MD_spc && 318152419Sariff n->flags & NODE_LINE && 31950724Scg !roff_node_transparent(n)) 320164614Sariff outflags |= MD_nl; 321164614Sariff 322164614Sariff act = NULL; 323164614Sariff cond = 0; 324152419Sariff process_children = 1; 325152419Sariff n->flags &= ~NODE_ENDED; 326152419Sariff 327152419Sariff if (n->type == ROFFT_TEXT) { 328152419Sariff if (n->flags & NODE_DELIMC) 329152419Sariff outflags &= ~(MD_spc | MD_spc_force); 330152419Sariff else if (outflags & MD_Sm) 331152419Sariff outflags |= MD_spc_force; 332152419Sariff md_word(n->string); 33350724Scg if (n->flags & NODE_DELIMO) 33450724Scg outflags &= ~(MD_spc | MD_spc_force); 335164614Sariff else if (outflags & MD_Sm) 336164614Sariff outflags |= MD_spc; 337164614Sariff } else if (n->tok < ROFF_MAX) { 338164614Sariff switch (n->tok) { 339152419Sariff case ROFF_br: 340152419Sariff process_children = md_pre_br(n); 34150724Scg break; 342164614Sariff case ROFF_sp: 34350724Scg process_children = md_pre_Pp(n); 34450724Scg break; 34550724Scg default: 34674763Scg process_children = 0; 34750724Scg break; 348150832Snetchild } 349152419Sariff } else { 35050724Scg act = md_act(n->tok); 351164614Sariff cond = act->cond == NULL || (*act->cond)(n); 352164614Sariff if (cond && act->pre != NULL && 35350724Scg (n->end == ENDBODY_NOT || n->child != NULL)) 354164614Sariff process_children = (*act->pre)(n); 355164614Sariff } 356164614Sariff 357164614Sariff if (process_children && n->child != NULL) 358206033Sjoel md_nodelist(n->child); 359150832Snetchild 360150832Snetchild if (n->flags & NODE_ENDED) 361152419Sariff return; 362164614Sariff 363152419Sariff if (cond && act->post != NULL) 364164614Sariff (*act->post)(n); 365152419Sariff 36650724Scg if (n->end != ENDBODY_NOT) 367206033Sjoel n->body->flags |= NODE_ENDED; 368150832Snetchild} 369152419Sariff 370164614Sariffstatic const char * 371164614Sariffmd_stack(char c) 37250724Scg{ 373150832Snetchild static char *stack; 374152419Sariff static size_t sz; 375152419Sariff static size_t cur; 376150832Snetchild 377150832Snetchild switch (c) { 378164614Sariff case '\0': 37950724Scg break; 38050724Scg case (char)-1: 381193640Sariff assert(cur); 382164614Sariff stack[--cur] = '\0'; 38350724Scg break; 384150832Snetchild default: 38550724Scg if (cur + 1 >= sz) { 38650724Scg sz += 8; 387150832Snetchild stack = mandoc_realloc(stack, sz); 38850724Scg } 38950724Scg stack[cur] = c; 39050724Scg stack[++cur] = '\0'; 39150724Scg break; 39250724Scg } 393150832Snetchild return stack == NULL ? "" : stack; 394152419Sariff} 395164614Sariff 396152419Sariff/* 397150832Snetchild * Handle vertical and horizontal spacing. 398150832Snetchild */ 399150832Snetchildstatic void 400150832Snetchildmd_preword(void) 401150832Snetchild{ 402150832Snetchild const char *cp; 403150832Snetchild 404150832Snetchild /* 405164614Sariff * If a list block is nested inside a code block or a blockquote, 40650724Scg * blank lines for paragraph breaks no longer work; instead, 40750724Scg * they terminate the list. Work around this markdown issue 40870134Scg * by using mere line breaks instead. 409164614Sariff */ 410164614Sariff 411164614Sariff if (list_blocks && outflags & MD_sp) { 412193640Sariff outflags &= ~MD_sp; 41370134Scg outflags |= MD_br; 41470134Scg } 41570134Scg 41670134Scg /* 41770134Scg * End the old line if requested. 41850724Scg * Escape whitespace at the end of the markdown line 419164614Sariff * such that it won't look like an output line break. 42050724Scg */ 421164614Sariff 42250724Scg if (outflags & MD_sp) 423150832Snetchild putchar('\n'); 424150832Snetchild else if (outflags & MD_br) { 425148591Snetchild putchar(' '); 426148591Snetchild putchar(' '); 427164614Sariff } else if (outflags & MD_nl && escflags & ESC_EOL) 428148591Snetchild md_named("zwnj"); 429164614Sariff 430164614Sariff /* Start a new line if necessary. */ 43150724Scg 432148591Snetchild if (outflags & (MD_nl | MD_br | MD_sp)) { 433148591Snetchild putchar('\n'); 434150832Snetchild for (cp = md_stack('\0'); *cp != '\0'; cp++) { 435164614Sariff putchar(*cp); 43650724Scg if (*cp == '>') 43750724Scg putchar(' '); 43850724Scg } 43950724Scg outflags &= ~(MD_nl | MD_br | MD_sp); 44050724Scg escflags = ESC_BOL; 44150724Scg outcount = 0; 442164614Sariff 443164614Sariff /* Handle horizontal spacing. */ 44450724Scg 44550724Scg } else if (outflags & MD_spc) { 446152419Sariff if (outflags & MD_Bk) 447152419Sariff fputs(" ", stdout); 44850724Scg else 449152419Sariff putchar(' '); 450152419Sariff escflags &= ~ESC_FON; 451152419Sariff outcount++; 452152419Sariff } 453152419Sariff 454164614Sariff outflags &= ~(MD_spc_force | MD_nonl); 455152419Sariff if (outflags & MD_Sm) 456164614Sariff outflags |= MD_spc; 457152419Sariff else 458164614Sariff outflags &= ~MD_spc; 459164614Sariff} 460164614Sariff 461152419Sariff/* 462164614Sariff * Print markdown syntax elements. 463152419Sariff * Can also be used for constant strings when neither escaping 464152419Sariff * nor delimiter handling is required. 465164614Sariff */ 466152419Sariffstatic void 467164614Sariffmd_rawword(const char *s) 468152419Sariff{ 469152419Sariff md_preword(); 470152419Sariff 471164614Sariff if (*s == '\0') 472152419Sariff return; 473164614Sariff 474152419Sariff if (escflags & ESC_FON) { 475152419Sariff escflags &= ~ESC_FON; 476152419Sariff if (*s == '*' && !code_blocks) 477152419Sariff fputs("‌", stdout); 478152419Sariff } 479152419Sariff 480152419Sariff while (*s != '\0') { 481152419Sariff switch(*s) { 482152419Sariff case '*': 483152419Sariff if (s[1] == '\0') 484152419Sariff escflags |= ESC_FON; 485152419Sariff break; 486152419Sariff case '[': 487152419Sariff escflags |= ESC_SQU; 488152419Sariff break; 489164614Sariff case ']': 490152419Sariff escflags |= ESC_HYP; 491152419Sariff escflags &= ~ESC_SQU; 492152419Sariff break; 493152419Sariff default: 494152419Sariff break; 49550724Scg } 49650724Scg md_char(*s++); 49750724Scg } 49884658Scg if (s[-1] == ' ') 499164614Sariff escflags |= ESC_EOL; 500164614Sariff else 501150832Snetchild escflags &= ~ESC_EOL; 502152419Sariff} 503168847Sariff 504164614Sariff/* 505150832Snetchild * Print text and mdoc(7) syntax elements. 50650724Scg */ 507152419Sariffstatic void 508164614Sariffmd_word(const char *s) 509164614Sariff{ 510164614Sariff const char *seq, *prevfont, *currfont, *nextfont; 511164614Sariff char c; 512164614Sariff int bs, sz, uc, breakline; 513164614Sariff 514152419Sariff /* No spacing before closing delimiters. */ 515164614Sariff if (s[0] != '\0' && s[1] == '\0' && 516164614Sariff strchr("!),.:;?]", s[0]) != NULL && 517164614Sariff (outflags & MD_spc_force) == 0) 518164614Sariff outflags &= ~MD_spc; 519164614Sariff 520164614Sariff md_preword(); 521152419Sariff 52250724Scg if (*s == '\0') 523148591Snetchild return; 524164614Sariff 525164614Sariff /* No spacing after opening delimiters. */ 526164614Sariff if ((s[0] == '(' || s[0] == '[') && s[1] == '\0') 527164614Sariff outflags &= ~MD_spc; 52850724Scg 529150832Snetchild breakline = 0; 530164614Sariff prevfont = currfont = ""; 53150724Scg while ((c = *s++) != '\0') { 53250724Scg bs = 0; 53350724Scg switch(c) { 534164614Sariff case ASCII_NBRSP: 53550724Scg if (code_blocks) 53650724Scg c = ' '; 53750724Scg else { 53850724Scg md_named("nbsp"); 539150832Snetchild c = '\0'; 54050724Scg } 541152419Sariff break; 542152419Sariff case ASCII_HYPH: 543164614Sariff bs = escflags & ESC_BOL && !code_blocks; 544164614Sariff c = '-'; 545193640Sariff break; 546164614Sariff case ASCII_BREAK: 547152419Sariff continue; 548152419Sariff case '#': 549164614Sariff case '+': 550164614Sariff case '-': 551193640Sariff bs = escflags & ESC_BOL && !code_blocks; 552164614Sariff break; 553152419Sariff case '(': 55450724Scg bs = escflags & ESC_HYP && !code_blocks; 55550724Scg break; 556164614Sariff case ')': 557164614Sariff bs = escflags & ESC_NUM && !code_blocks; 558193640Sariff break; 559164614Sariff case '*': 56050724Scg case '[': 561148591Snetchild case '_': 562150832Snetchild case '`': 56350724Scg bs = !code_blocks; 564164614Sariff break; 56550724Scg case '.': 56650724Scg bs = escflags & ESC_NUM && !code_blocks; 567193640Sariff break; 568164614Sariff case '<': 56950724Scg if (code_blocks == 0) { 57050724Scg md_named("lt"); 57150724Scg c = '\0'; 57250724Scg } 573164614Sariff break; 574152419Sariff case '=': 575171250Sariff if (escflags & ESC_BOL && !code_blocks) { 576171250Sariff md_named("equals"); 577164614Sariff c = '\0'; 578171250Sariff } 579152419Sariff break; 580152419Sariff case '>': 581152419Sariff if (code_blocks == 0) { 582152419Sariff md_named("gt"); 583152419Sariff c = '\0'; 584152419Sariff } 585152419Sariff break; 586152419Sariff case '\\': 587218909Sbrucec uc = 0; 588152419Sariff nextfont = NULL; 589152419Sariff switch (mandoc_escape(&s, &seq, &sz)) { 590152419Sariff case ESCAPE_UNICODE: 591152419Sariff uc = mchars_num2uc(seq + 1, sz - 1); 592152419Sariff break; 593152419Sariff case ESCAPE_NUMBERED: 594152419Sariff uc = mchars_num2char(seq, sz); 595152419Sariff break; 596152419Sariff case ESCAPE_SPECIAL: 597152419Sariff uc = mchars_spec2cp(seq, sz); 598152419Sariff break; 599152419Sariff case ESCAPE_UNDEF: 600152419Sariff uc = *seq; 601152419Sariff break; 602152419Sariff case ESCAPE_DEVICE: 603152419Sariff md_rawword("markdown"); 604152419Sariff continue; 605152419Sariff case ESCAPE_FONTBOLD: 606150832Snetchild case ESCAPE_FONTCB: 607148591Snetchild nextfont = "**"; 608150832Snetchild break; 609164614Sariff case ESCAPE_FONTITALIC: 61050724Scg case ESCAPE_FONTCI: 61150724Scg nextfont = "*"; 612193640Sariff break; 613164614Sariff case ESCAPE_FONTBI: 61454831Scg nextfont = "***"; 61554831Scg break; 61654831Scg case ESCAPE_FONT: 617152419Sariff case ESCAPE_FONTCR: 618152419Sariff case ESCAPE_FONTROMAN: 61954831Scg nextfont = ""; 620150832Snetchild break; 621148591Snetchild case ESCAPE_FONTPREV: 622152419Sariff nextfont = prevfont; 623148591Snetchild break; 624152419Sariff case ESCAPE_BREAK: 625150832Snetchild breakline = 1; 626164614Sariff break; 627148591Snetchild case ESCAPE_NOSPACE: 628164614Sariff case ESCAPE_SKIPCHAR: 629164614Sariff case ESCAPE_OVERSTRIKE: 63054831Scg /* XXX not implemented */ 63154831Scg /* FALLTHROUGH */ 63250724Scg case ESCAPE_ERROR: 633167648Sariff default: 63450724Scg break; 63570321Scg } 636164614Sariff if (nextfont != NULL && !code_blocks) { 63770321Scg if (*currfont != '\0') { 638167648Sariff outflags &= ~MD_spc; 639164614Sariff md_rawword(currfont); 640167648Sariff } 641167648Sariff prevfont = currfont; 642167648Sariff currfont = nextfont; 643167648Sariff if (*currfont != '\0') { 644167648Sariff outflags &= ~MD_spc; 645167648Sariff md_rawword(currfont); 646167648Sariff } 647167648Sariff } 648164614Sariff if (uc) { 649167648Sariff if ((uc < 0x20 && uc != 0x09) || 650167648Sariff (uc > 0x7E && uc < 0xA0)) 651167648Sariff uc = 0xFFFD; 652167648Sariff if (code_blocks) { 653167648Sariff seq = mchars_uc2str(uc); 654167648Sariff fputs(seq, stdout); 655167648Sariff outcount += strlen(seq); 656167648Sariff } else { 657167648Sariff printf("&#%d;", uc); 658164614Sariff outcount++; 659167648Sariff } 660167648Sariff escflags &= ~ESC_FON; 661164614Sariff } 662167648Sariff c = '\0'; 663164614Sariff break; 664164614Sariff case ']': 665164614Sariff bs = escflags & ESC_SQU && !code_blocks; 666167648Sariff escflags |= ESC_HYP; 667164614Sariff break; 668193640Sariff default: 669167648Sariff break; 670167648Sariff } 671193640Sariff if (bs) 672167648Sariff putchar('\\'); 673167648Sariff md_char(c); 674167648Sariff if (breakline && 675167648Sariff (*s == '\0' || *s == ' ' || *s == ASCII_NBRSP)) { 676167648Sariff printf(" \n"); 677167648Sariff breakline = 0; 678167648Sariff while (*s == ' ' || *s == ASCII_NBRSP) 679164614Sariff s++; 680164614Sariff } 681164614Sariff } 682164614Sariff if (*currfont != '\0') { 683164614Sariff outflags &= ~MD_spc; 684164614Sariff md_rawword(currfont); 685164614Sariff } else if (s[-2] == ' ') 686164614Sariff escflags |= ESC_EOL; 687164614Sariff else 688164614Sariff escflags &= ~ESC_EOL; 689164614Sariff} 690164614Sariff 691164614Sariff/* 692164614Sariff * Print a single HTML named character reference. 693164614Sariff */ 694164614Sariffstatic void 695164614Sariffmd_named(const char *s) 696164614Sariff{ 697164614Sariff printf("&%s;", s); 698164614Sariff escflags &= ~(ESC_FON | ESC_EOL); 699164614Sariff outcount++; 700164614Sariff} 701164614Sariff 702164614Sariff/* 703164614Sariff * Print a single raw character and maintain certain escape flags. 704164614Sariff */ 705164614Sariffstatic void 706164614Sariffmd_char(unsigned char c) 707164614Sariff{ 708164614Sariff if (c != '\0') { 709164614Sariff putchar(c); 710164614Sariff if (c == '*') 711164614Sariff escflags |= ESC_FON; 712164614Sariff else 713164614Sariff escflags &= ~ESC_FON; 714164614Sariff outcount++; 715164614Sariff } 716164614Sariff if (c != ']') 717164614Sariff escflags &= ~ESC_HYP; 718164614Sariff if (c == ' ' || c == '\t' || c == '>') 719164614Sariff return; 720164614Sariff if (isdigit(c) == 0) 721164614Sariff escflags &= ~ESC_NUM; 722164614Sariff else if (escflags & ESC_BOL) 723164614Sariff escflags |= ESC_NUM; 724164614Sariff escflags &= ~ESC_BOL; 725164614Sariff} 726164614Sariff 727164614Sariffstatic int 728164614Sariffmd_cond_head(struct roff_node *n) 729164614Sariff{ 730164614Sariff return n->type == ROFFT_HEAD; 731164614Sariff} 732164614Sariff 733164614Sariffstatic int 734164614Sariffmd_cond_body(struct roff_node *n) 735150832Snetchild{ 736164614Sariff return n->type == ROFFT_BODY; 737164614Sariff} 738164614Sariff 739164614Sariffstatic int 740164614Sariffmd_pre_abort(struct roff_node *n) 741164614Sariff{ 742164614Sariff abort(); 743164614Sariff} 744164614Sariff 745164614Sariffstatic int 746164614Sariffmd_pre_raw(struct roff_node *n) 747164614Sariff{ 748164614Sariff const char *prefix; 749164614Sariff 750164614Sariff if ((prefix = md_act(n->tok)->prefix) != NULL) { 751164614Sariff md_rawword(prefix); 75250724Scg outflags &= ~MD_spc; 75350724Scg if (*prefix == '`') 75450724Scg code_blocks++; 75570134Scg } 75650724Scg return 1; 75750724Scg} 75850724Scg 759152419Sariffstatic void 76050724Scgmd_post_raw(struct roff_node *n) 761170521Sariff{ 762170521Sariff const char *suffix; 76360958Scg 764164614Sariff if ((suffix = md_act(n->tok)->suffix) != NULL) { 765193640Sariff outflags &= ~(MD_spc | MD_nl); 766152419Sariff md_rawword(suffix); 767152419Sariff if (*suffix == '`') 768193640Sariff code_blocks--; 769152419Sariff } 77050724Scg} 77150724Scg 772152419Sariffstatic int 773152419Sariffmd_pre_word(struct roff_node *n) 774164614Sariff{ 775164614Sariff const char *prefix; 776164614Sariff 777164614Sariff if ((prefix = md_act(n->tok)->prefix) != NULL) { 778164614Sariff md_word(prefix); 779164614Sariff outflags &= ~MD_spc; 780164614Sariff } 781152419Sariff return 1; 782152419Sariff} 783164614Sariff 784164614Sariffstatic void 785164614Sariffmd_post_word(struct roff_node *n) 786164614Sariff{ 787152419Sariff const char *suffix; 788152419Sariff 789164614Sariff if ((suffix = md_act(n->tok)->suffix) != NULL) { 790164614Sariff outflags &= ~(MD_spc | MD_nl); 791164614Sariff md_word(suffix); 792164614Sariff } 793164614Sariff} 794164614Sariff 795164614Sariffstatic void 796164614Sariffmd_post_pc(struct roff_node *n) 797164614Sariff{ 798152419Sariff struct roff_node *nn; 799152419Sariff 800164614Sariff md_post_raw(n); 801164614Sariff if (n->parent->tok != MDOC_Rs) 802164614Sariff return; 803164614Sariff 804152419Sariff if ((nn = roff_node_next(n)) != NULL) { 805164614Sariff md_word(","); 806164614Sariff if (nn->tok == n->tok && 807164614Sariff (nn = roff_node_prev(n)) != NULL && 80850724Scg nn->tok == n->tok) 80950724Scg md_word("and"); 81050724Scg } else { 81150724Scg md_word("."); 812164614Sariff outflags |= MD_nl; 813164614Sariff } 814164614Sariff} 815164614Sariff 816164614Sariffstatic int 817148591Snetchildmd_pre_skip(struct roff_node *n) 81859323Scg{ 819164614Sariff return 0; 820164614Sariff} 821164614Sariff 822164614Sariffstatic void 823164614Sariffmd_pre_syn(struct roff_node *n) 824164614Sariff{ 82550724Scg struct roff_node *np; 826148591Snetchild 827148591Snetchild if ((n->flags & NODE_SYNPRETTY) == 0 || 828164614Sariff (np = roff_node_prev(n)) == NULL) 829164614Sariff return; 830164614Sariff 831164614Sariff if (np->tok == n->tok && 832164614Sariff n->tok != MDOC_Ft && 833164614Sariff n->tok != MDOC_Fo && 834164614Sariff n->tok != MDOC_Fn) { 835164614Sariff outflags |= MD_br; 836164614Sariff return; 837164614Sariff } 838164614Sariff 839164614Sariff switch (np->tok) { 840164614Sariff case MDOC_Fd: 841164614Sariff case MDOC_Fn: 842164614Sariff case MDOC_Fo: 843164614Sariff case MDOC_In: 844164614Sariff case MDOC_Vt: 845164614Sariff outflags |= MD_sp; 846164614Sariff break; 847164614Sariff case MDOC_Ft: 848150832Snetchild if (n->tok != MDOC_Fn && n->tok != MDOC_Fo) { 849164614Sariff outflags |= MD_sp; 85050724Scg break; 85150724Scg } 852193640Sariff /* FALLTHROUGH */ 85370134Scg default: 85450724Scg outflags |= MD_br; 85550724Scg break; 85650724Scg } 857164614Sariff} 85859323Scg 859150832Snetchildstatic int 860164614Sariffmd_pre_An(struct roff_node *n) 861164614Sariff{ 862164614Sariff switch (n->norm->An.auth) { 863164614Sariff case AUTH_split: 864164614Sariff outflags &= ~MD_An_nosplit; 865164614Sariff outflags |= MD_An_split; 866164614Sariff return 0; 867164614Sariff case AUTH_nosplit: 868164614Sariff outflags &= ~MD_An_split; 869164614Sariff outflags |= MD_An_nosplit; 870164614Sariff return 0; 871164614Sariff default: 872164614Sariff if (outflags & MD_An_split) 873164614Sariff outflags |= MD_br; 874164614Sariff else if (n->sec == SEC_AUTHORS && 875150832Snetchild ! (outflags & MD_An_nosplit)) 876164614Sariff outflags |= MD_An_split; 877167648Sariff return 1; 878164614Sariff } 879164614Sariff} 88050724Scg 88150724Scgstatic int 88274763Scgmd_pre_Ap(struct roff_node *n) 88370134Scg{ 88450724Scg outflags &= ~MD_spc; 88550724Scg md_word("'"); 886150832Snetchild outflags &= ~MD_spc; 887164614Sariff return 0; 88850724Scg} 88950724Scg 89070134Scgstatic int 891164614Sariffmd_pre_Bd(struct roff_node *n) 892164614Sariff{ 893164614Sariff switch (n->norm->Bd.type) { 894164614Sariff case DISP_unfilled: 895167648Sariff case DISP_literal: 896164614Sariff return md_pre_Dl(n); 897164614Sariff default: 898164614Sariff return md_pre_D1(n); 899193640Sariff } 90070134Scg} 90170134Scg 90270134Scgstatic int 90370134Scgmd_pre_Bk(struct roff_node *n) 904164614Sariff{ 905164614Sariff switch (n->type) { 906164614Sariff case ROFFT_BLOCK: 907164614Sariff return 1; 908167648Sariff case ROFFT_BODY: 909164614Sariff outflags |= MD_Bk; 910164614Sariff return 1; 911164614Sariff default: 912193640Sariff return 0; 91370134Scg } 91470134Scg} 91570134Scg 91670134Scgstatic void 91750724Scgmd_post_Bk(struct roff_node *n) 91850724Scg{ 91954831Scg if (n->type == ROFFT_BODY) 92050724Scg outflags &= ~MD_Bk; 92150724Scg} 922150832Snetchild 92350724Scgstatic int 924150832Snetchildmd_pre_Bl(struct roff_node *n) 925164614Sariff{ 926164614Sariff n->norm->Bl.count = 0; 927164614Sariff if (n->norm->Bl.type == LIST_column) 928164614Sariff md_pre_Dl(n); 929148591Snetchild outflags |= MD_sp; 930148591Snetchild return 1; 931150832Snetchild} 932148591Snetchild 933148591Snetchildstatic void 93450724Scgmd_post_Bl(struct roff_node *n) 93550724Scg{ 936164614Sariff n->norm->Bl.count = 0; 937164614Sariff if (n->norm->Bl.type == LIST_column) 938164614Sariff md_post_D1(n); 939164614Sariff outflags |= MD_sp; 940164614Sariff} 941164614Sariff 94250724Scgstatic int 943148591Snetchildmd_pre_D1(struct roff_node *n) 944148591Snetchild{ 945150832Snetchild /* 94650724Scg * Markdown blockquote syntax does not work inside code blocks. 947164614Sariff * The best we can do is fall back to another nested code block. 948164614Sariff */ 949164614Sariff if (code_blocks) { 950164614Sariff md_stack('\t'); 951164614Sariff code_blocks++; 952164614Sariff } else { 95350724Scg md_stack('>'); 95450724Scg quote_blocks++; 95554831Scg } 95654831Scg outflags |= MD_sp; 95754831Scg return 1; 95854831Scg} 959152419Sariff 960152419Sariffstatic void 961150832Snetchildmd_post_D1(struct roff_node *n) 962152419Sariff{ 963150832Snetchild md_stack((char)-1); 964164614Sariff if (code_blocks) 965152419Sariff code_blocks--; 966152419Sariff else 967152419Sariff quote_blocks--; 968152419Sariff outflags |= MD_sp; 969152419Sariff} 970152419Sariff 971150832Snetchildstatic int 972150832Snetchildmd_pre_Dl(struct roff_node *n) 973152419Sariff{ 974152419Sariff /* 975152419Sariff * Markdown code block syntax does not work inside blockquotes. 976164614Sariff * The best we can do is fall back to another nested blockquote. 977164614Sariff */ 978152419Sariff if (quote_blocks) { 979152419Sariff md_stack('>'); 980152419Sariff quote_blocks++; 981150832Snetchild } else { 982152419Sariff md_stack('\t'); 983152419Sariff code_blocks++; 984152419Sariff } 985152419Sariff outflags |= MD_sp; 986164614Sariff return 1; 987152419Sariff} 988164614Sariff 989152419Sariffstatic int 990152419Sariffmd_pre_En(struct roff_node *n) 991150832Snetchild{ 992164614Sariff if (n->norm->Es == NULL || 993152419Sariff n->norm->Es->child == NULL) 994164614Sariff return 1; 995152419Sariff 996152419Sariff md_word(n->norm->Es->child->string); 997164614Sariff outflags &= ~MD_spc; 998152419Sariff return 1; 999148591Snetchild} 100053413Sroger 100154831Scgstatic void 1002148591Snetchildmd_post_En(struct roff_node *n) 100353413Sroger{ 1004164614Sariff if (n->norm->Es == NULL || 1005164614Sariff n->norm->Es->child == NULL || 1006164614Sariff n->norm->Es->child->next == NULL) 1007164614Sariff return; 1008164614Sariff 1009164614Sariff outflags &= ~MD_spc; 1010164614Sariff md_word(n->norm->Es->child->next->string); 1011164614Sariff} 1012164614Sariff 1013164614Sariffstatic int 1014164614Sariffmd_pre_Eo(struct roff_node *n) 1015150832Snetchild{ 101653413Sroger if (n->end == ENDBODY_NOT && 1017164614Sariff n->parent->head->child == NULL && 101854831Scg n->child != NULL && 101953413Sroger n->child->end != ENDBODY_NOT) 102054831Scg md_preword(); 102153413Sroger else if (n->end != ENDBODY_NOT ? n->child != NULL : 1022150832Snetchild n->parent->head->child != NULL && (n->child != NULL || 102353413Sroger (n->parent->tail != NULL && n->parent->tail->child != NULL))) 1024154285Sariff outflags &= ~(MD_spc | MD_nl); 102553413Sroger return 1; 102653413Sroger} 1027150832Snetchild 1028152419Sariffstatic void 1029152419Sariffmd_post_Eo(struct roff_node *n) 103054831Scg{ 103153413Sroger if (n->end != ENDBODY_NOT) { 1032148591Snetchild outflags |= MD_spc; 1033150832Snetchild return; 1034150832Snetchild } 1035164614Sariff 1036164614Sariff if (n->child == NULL && n->parent->head->child == NULL) 1037154285Sariff return; 1038154285Sariff 1039154285Sariff if (n->parent->tail != NULL && n->parent->tail->child != NULL) 1040154285Sariff outflags &= ~MD_spc; 1041154285Sariff else 1042154285Sariff outflags |= MD_spc; 1043154285Sariff} 1044154285Sariff 1045154285Sariffstatic int 1046154285Sariffmd_pre_Fa(struct roff_node *n) 1047154285Sariff{ 1048154285Sariff int am_Fa; 1049154285Sariff 1050148591Snetchild am_Fa = n->tok == MDOC_Fa; 1051148591Snetchild 1052148591Snetchild if (am_Fa) 1053148591Snetchild n = n->child; 105453413Sroger 1055148591Snetchild while (n != NULL) { 1056148591Snetchild md_rawword("*"); 1057148591Snetchild outflags &= ~MD_spc; 105876086Scg md_node(n); 105976086Scg outflags &= ~MD_spc; 106076086Scg md_rawword("*"); 106195678Scg if ((n = n->next) != NULL) 1062148591Snetchild md_word(","); 1063148591Snetchild } 1064148591Snetchild return 0; 106574753Scg} 106655209Scg 106753413Srogerstatic void 1068152419Sariffmd_post_Fa(struct roff_node *n) 1069152419Sariff{ 107053413Sroger struct roff_node *nn; 1071152419Sariff 1072148591Snetchild if ((nn = roff_node_next(n)) != NULL && nn->tok == MDOC_Fa) 107353413Sroger md_word(","); 1074148591Snetchild} 107553413Sroger 107654831Scgstatic int 1077164614Sariffmd_pre_Fd(struct roff_node *n) 107853413Sroger{ 1079164614Sariff md_pre_syn(n); 108053413Sroger md_pre_raw(n); 1081164614Sariff return 1; 1082164614Sariff} 1083164614Sariff 1084164614Sariffstatic void 1085164614Sariffmd_post_Fd(struct roff_node *n) 1086164614Sariff{ 1087164614Sariff md_post_raw(n); 1088164614Sariff outflags |= MD_br; 1089164614Sariff} 1090164614Sariff 1091164614Sariffstatic void 109253413Srogermd_post_Fl(struct roff_node *n) 109353413Sroger{ 109453413Sroger struct roff_node *nn; 109553413Sroger 109653413Sroger md_post_raw(n); 1097148591Snetchild if (n->child == NULL && (nn = roff_node_next(n)) != NULL && 1098148591Snetchild nn->type != ROFFT_TEXT && (nn->flags & NODE_LINE) == 0) 1099148591Snetchild outflags &= ~MD_spc; 1100148591Snetchild} 1101148591Snetchild 1102150832Snetchildstatic int 110353413Srogermd_pre_Fn(struct roff_node *n) 110453413Sroger{ 110553413Sroger md_pre_syn(n); 110653413Sroger 110770134Scg if ((n = n->child) == NULL) 110870134Scg return 0; 110970134Scg 1110164614Sariff md_rawword("**"); 111153413Sroger outflags &= ~MD_spc; 1112150832Snetchild md_node(n); 111353413Sroger outflags &= ~MD_spc; 111453413Sroger md_rawword("**"); 1115167648Sariff outflags &= ~MD_spc; 1116148591Snetchild md_word("("); 111753413Sroger 1118167648Sariff if ((n = n->next) != NULL) 111953413Sroger md_pre_Fa(n); 1120148591Snetchild return 0; 112153413Sroger} 1122164614Sariff 1123164614Sariffstatic void 1124148591Snetchildmd_post_Fn(struct roff_node *n) 1125164614Sariff{ 1126164614Sariff md_word(")"); 1127164614Sariff if (n->flags & NODE_SYNPRETTY) { 1128148591Snetchild md_word(";"); 1129164614Sariff outflags |= MD_sp; 113053413Sroger } 1131164614Sariff} 1132164614Sariff 1133164614Sariffstatic int 113454831Scgmd_pre_Fo(struct roff_node *n) 1135164614Sariff{ 113654831Scg switch (n->type) { 1137164614Sariff case ROFFT_BLOCK: 1138164614Sariff md_pre_syn(n); 1139164614Sariff break; 114053413Sroger case ROFFT_HEAD: 114153413Sroger if (n->child == NULL) 1142148591Snetchild return 0; 114370134Scg md_pre_raw(n); 1144164614Sariff break; 114553413Sroger case ROFFT_BODY: 114653413Sroger outflags &= ~(MD_spc | MD_nl); 114770134Scg md_word("("); 114870134Scg break; 114953413Sroger default: 1150150832Snetchild break; 115154831Scg } 115253413Sroger return 1; 1153164614Sariff} 1154148591Snetchild 115554831Scgstatic void 1156164614Sariffmd_post_Fo(struct roff_node *n) 115753413Sroger{ 115854831Scg switch (n->type) { 1159148591Snetchild case ROFFT_HEAD: 116054831Scg if (n->child != NULL) 1161164614Sariff md_post_raw(n); 1162164614Sariff break; 1163148591Snetchild case ROFFT_BODY: 1164164614Sariff md_post_Fn(n); 1165164614Sariff break; 1166164614Sariff default: 1167148591Snetchild break; 1168164614Sariff } 116954831Scg} 1170164614Sariff 1171164614Sariffstatic int 1172164614Sariffmd_pre_In(struct roff_node *n) 117354831Scg{ 1174164614Sariff if (n->flags & NODE_SYNPRETTY) { 117553413Sroger md_pre_syn(n); 1176164614Sariff md_rawword("**"); 1177164614Sariff outflags &= ~MD_spc; 1178148591Snetchild md_word("#include <"); 117954831Scg } else { 118054831Scg md_word("<"); 1181148591Snetchild outflags &= ~MD_spc; 118253413Sroger md_rawword("*"); 118354831Scg } 1184164614Sariff outflags &= ~MD_spc; 1185148591Snetchild return 1; 118654831Scg} 1187164614Sariff 1188148591Snetchildstatic void 118954831Scgmd_post_In(struct roff_node *n) 119053413Sroger{ 119153413Sroger if (n->flags & NODE_SYNPRETTY) { 119270134Scg outflags &= ~MD_spc; 1193164614Sariff md_rawword(">**"); 1194164614Sariff outflags |= MD_nl; 1195193640Sariff } else { 119670134Scg outflags &= ~MD_spc; 119770134Scg md_rawword("*>"); 119870134Scg } 119970134Scg} 120070134Scg 1201164614Sariffstatic int 1202164614Sariffmd_pre_It(struct roff_node *n) 120354831Scg{ 1204150832Snetchild struct roff_node *bln; 120553413Sroger 1206164614Sariff switch (n->type) { 1207164614Sariff case ROFFT_BLOCK: 120854831Scg return 1; 1209148591Snetchild 1210164614Sariff case ROFFT_HEAD: 121153413Sroger bln = n->parent->parent; 121253413Sroger if (bln->norm->Bl.comp == 0 && 121353413Sroger bln->norm->Bl.type != LIST_column) 1214164614Sariff outflags |= MD_sp; 1215148591Snetchild outflags |= MD_nl; 1216150832Snetchild 121753413Sroger switch (bln->norm->Bl.type) { 1218164614Sariff case LIST_item: 1219164614Sariff outflags |= MD_br; 122053413Sroger return 0; 1221148591Snetchild case LIST_inset: 122253413Sroger case LIST_diag: 122353413Sroger case LIST_ohang: 1224164614Sariff outflags |= MD_br; 1225164614Sariff return 1; 122654831Scg case LIST_tag: 1227164614Sariff case LIST_hang: 122854831Scg outflags |= MD_sp; 1229150832Snetchild return 1; 1230150832Snetchild case LIST_bullet: 1231164614Sariff md_rawword("*\t"); 1232164614Sariff break; 1233164614Sariff case LIST_dash: 1234164614Sariff case LIST_hyphen: 123554831Scg md_rawword("-\t"); 123654831Scg break; 123754831Scg case LIST_enum: 123854831Scg md_preword(); 123954831Scg if (bln->norm->Bl.count < 99) 124054831Scg bln->norm->Bl.count++; 124154831Scg printf("%d.\t", bln->norm->Bl.count); 124254831Scg escflags &= ~ESC_FON; 1243164614Sariff break; 1244164614Sariff case LIST_column: 124554831Scg outflags |= MD_br; 1246164614Sariff return 0; 124754831Scg default: 1248164614Sariff return 0; 1249164614Sariff } 125054831Scg outflags &= ~MD_spc; 1251164614Sariff outflags |= MD_nonl; 125254831Scg outcount = 0; 125354831Scg md_stack('\t'); 1254164614Sariff if (code_blocks || quote_blocks) 1255164614Sariff list_blocks++; 1256164614Sariff return 0; 1257164614Sariff 125854831Scg case ROFFT_BODY: 125954831Scg bln = n->parent->parent; 126053413Sroger switch (bln->norm->Bl.type) { 1261164614Sariff case LIST_ohang: 126253413Sroger outflags |= MD_br; 126353413Sroger break; 1264164614Sariff case LIST_tag: 1265164614Sariff case LIST_hang: 126654831Scg md_pre_D1(n); 1267164614Sariff break; 126853413Sroger default: 1269150832Snetchild break; 1270150832Snetchild } 1271164614Sariff return 1; 1272164614Sariff 1273164614Sariff default: 1274164614Sariff return 0; 1275152419Sariff } 127654831Scg} 1277164614Sariff 1278152419Sariffstatic void 1279152419Sariffmd_post_It(struct roff_node *n) 1280164614Sariff{ 1281164614Sariff struct roff_node *bln; 1282152419Sariff int i, nc; 1283152419Sariff 1284164614Sariff if (n->type != ROFFT_BODY) 1285164614Sariff return; 1286152419Sariff 1287164614Sariff bln = n->parent->parent; 1288164614Sariff switch (bln->norm->Bl.type) { 1289152419Sariff case LIST_bullet: 1290164614Sariff case LIST_dash: 129153413Sroger case LIST_hyphen: 129253413Sroger case LIST_enum: 1293150832Snetchild md_stack((char)-1); 129455209Scg if (code_blocks || quote_blocks) 129554831Scg list_blocks--; 1296150832Snetchild break; 129753413Sroger case LIST_tag: 1298148591Snetchild case LIST_hang: 1299164614Sariff md_post_D1(n); 1300164614Sariff break; 1301164614Sariff 1302148591Snetchild case LIST_column: 130354831Scg if (n->next == NULL) 1304150832Snetchild break; 1305150832Snetchild 1306164614Sariff /* Calculate the array index of the current column. */ 130753413Sroger 130853413Sroger i = 0; 130950724Scg while ((n = n->prev) != NULL && n->type != ROFFT_HEAD) 131050724Scg i++; 131150724Scg 131250724Scg /* 131350724Scg * If a width was specified for this column, 131450724Scg * subtract what printed, and 131550724Scg * add the same spacing as in mdoc_term.c. 131650724Scg */ 131750724Scg 131876086Scg nc = bln->norm->Bl.ncols; 131976086Scg i = i < nc ? strlen(bln->norm->Bl.cols[i]) - outcount + 132050724Scg (nc < 5 ? 4 : nc == 5 ? 3 : 1) : 1; 1321164614Sariff if (i < 1) 132276086Scg i = 1; 132376086Scg while (i-- > 0) 132478033Scg putchar(' '); 132578033Scg 1326164614Sariff outflags &= ~MD_spc; 132776086Scg escflags &= ~ESC_FON; 132876086Scg outcount = 0; 1329164614Sariff break; 133076086Scg 133176086Scg default: 1332164614Sariff break; 133376086Scg } 133476086Scg} 1335164614Sariff 133676086Scgstatic void 133776086Scgmd_post_Lb(struct roff_node *n) 1338164614Sariff{ 133976086Scg if (n->sec == SEC_LIBRARY) 134076086Scg outflags |= MD_br; 1341164614Sariff} 134276086Scg 134376086Scgstatic void 1344164614Sariffmd_uri(const char *s) 1345164614Sariff{ 1346164614Sariff while (*s != '\0') { 1347164614Sariff if (strchr("%()<>", *s) != NULL) { 1348164614Sariff printf("%%%2.2hhX", *s); 134976086Scg outcount += 3; 135076086Scg } else { 135178033Scg putchar(*s); 1352164614Sariff outcount++; 1353164614Sariff } 1354164614Sariff s++; 1355164614Sariff } 1356119548Sorion} 1357119548Sorion 1358119548Sorionstatic int 1359164614Sariffmd_pre_Lk(struct roff_node *n) 1360164614Sariff{ 1361164614Sariff const struct roff_node *link, *descr, *punct; 1362119548Sorion 1363119548Sorion if ((link = n->child) == NULL) 1364164614Sariff return 0; 1365164614Sariff 1366164614Sariff /* Find beginning of trailing punctuation. */ 1367164614Sariff punct = n->last; 1368164614Sariff while (punct != link && punct->flags & NODE_DELIMC) 1369119548Sorion punct = punct->prev; 137076086Scg punct = punct->next; 137176086Scg 137276086Scg /* Link text. */ 137376086Scg descr = link->next; 1374164614Sariff if (descr == punct) 137576086Scg descr = link; /* no text */ 137676086Scg md_rawword("["); 1377164614Sariff outflags &= ~MD_spc; 137895678Scg do { 137995678Scg md_word(descr->string); 1380164614Sariff descr = descr->next; 138176086Scg } while (descr != punct); 138276086Scg outflags &= ~MD_spc; 1383164614Sariff 1384164614Sariff /* Link target. */ 1385164614Sariff md_rawword("]("); 1386164614Sariff md_uri(link->string); 1387164614Sariff outflags &= ~MD_spc; 138876086Scg md_rawword(")"); 138976086Scg 1390164614Sariff /* Trailing punctuation. */ 139150724Scg while (punct != NULL) { 139250724Scg md_word(punct->string); 139350724Scg punct = punct->next; 139450724Scg } 1395150832Snetchild return 0; 1396148591Snetchild} 1397148591Snetchild 1398148591Snetchildstatic int 1399150832Snetchildmd_pre_Mt(struct roff_node *n) 1400150832Snetchild{ 1401148591Snetchild const struct roff_node *nch; 1402148591Snetchild 1403148591Snetchild md_rawword("["); 1404150832Snetchild outflags &= ~MD_spc; 1405150832Snetchild for (nch = n->child; nch != NULL; nch = nch->next) 1406150832Snetchild md_word(nch->string); 1407150832Snetchild outflags &= ~MD_spc; 1408170289Sdwmalone md_rawword("](mailto:"); 1409148591Snetchild for (nch = n->child; nch != NULL; nch = nch->next) { 1410148591Snetchild md_uri(nch->string); 1411150832Snetchild if (nch->next != NULL) { 1412148591Snetchild putchar(' '); 1413150832Snetchild outcount++; 1414148591Snetchild } 1415150832Snetchild } 1416148591Snetchild outflags &= ~MD_spc; 1417148591Snetchild md_rawword(")"); 1418148591Snetchild return 0; 1419148591Snetchild} 1420148591Snetchild 1421148591Snetchildstatic int 1422148591Snetchildmd_pre_Nd(struct roff_node *n) 1423148591Snetchild{ 1424148591Snetchild outflags &= ~MD_nl; 1425148591Snetchild outflags |= MD_spc; 1426148591Snetchild md_word("-"); 1427150832Snetchild return 1; 1428150832Snetchild} 1429150832Snetchild 1430148591Snetchildstatic int 1431148591Snetchildmd_pre_Nm(struct roff_node *n) 1432148591Snetchild{ 1433150832Snetchild switch (n->type) { 1434148591Snetchild case ROFFT_BLOCK: 1435148591Snetchild outflags |= MD_Bk; 1436148591Snetchild md_pre_syn(n); 1437150832Snetchild break; 1438150832Snetchild case ROFFT_HEAD: 1439148591Snetchild case ROFFT_ELEM: 1440148591Snetchild md_pre_raw(n); 1441148591Snetchild break; 1442150832Snetchild default: 1443148591Snetchild break; 1444150832Snetchild } 1445170289Sdwmalone return 1; 1446164614Sariff} 1447148591Snetchild 1448150832Snetchildstatic void 1449150832Snetchildmd_post_Nm(struct roff_node *n) 1450150832Snetchild{ 1451148591Snetchild switch (n->type) { 1452150832Snetchild case ROFFT_BLOCK: 1453148591Snetchild outflags &= ~MD_Bk; 1454150832Snetchild break; 1455150832Snetchild case ROFFT_HEAD: 1456150832Snetchild case ROFFT_ELEM: 1457148591Snetchild md_post_raw(n); 1458150832Snetchild break; 1459150832Snetchild default: 1460150832Snetchild break; 1461150832Snetchild } 1462150832Snetchild} 1463150832Snetchild 1464150832Snetchildstatic int 1465150832Snetchildmd_pre_No(struct roff_node *n) 1466150832Snetchild{ 1467150832Snetchild outflags |= MD_spc_force; 1468150832Snetchild return 1; 1469150832Snetchild} 1470152419Sariff 1471152419Sariffstatic int 1472150832Snetchildmd_pre_Ns(struct roff_node *n) 1473150832Snetchild{ 1474170289Sdwmalone outflags &= ~MD_spc; 1475164614Sariff return 0; 1476150832Snetchild} 1477150832Snetchild 1478150832Snetchildstatic void 1479150832Snetchildmd_post_Pf(struct roff_node *n) 1480150832Snetchild{ 1481150832Snetchild if (n->next != NULL && (n->next->flags & NODE_LINE) == 0) 1482152419Sariff outflags &= ~MD_spc; 1483152419Sariff} 1484152419Sariff 1485152419Sariffstatic int 1486150832Snetchildmd_pre_Pp(struct roff_node *n) 1487152419Sariff{ 1488152419Sariff outflags |= MD_sp; 1489152419Sariff return 0; 1490152419Sariff} 1491152419Sariff 1492152419Sariffstatic int 1493152419Sariffmd_pre_Rs(struct roff_node *n) 1494152419Sariff{ 1495152419Sariff if (n->sec == SEC_SEE_ALSO) 1496152419Sariff outflags |= MD_sp; 1497150832Snetchild return 1; 1498152419Sariff} 1499152419Sariff 1500152419Sariffstatic int 1501150832Snetchildmd_pre_Sh(struct roff_node *n) 1502150832Snetchild{ 1503150832Snetchild switch (n->type) { 1504150832Snetchild case ROFFT_BLOCK: 1505150832Snetchild if (n->sec == SEC_AUTHORS) 1506152419Sariff outflags &= ~(MD_An_split | MD_An_nosplit); 1507152419Sariff break; 1508152419Sariff case ROFFT_HEAD: 1509152419Sariff outflags |= MD_sp; 1510152419Sariff md_rawword(n->tok == MDOC_Sh ? "#" : "##"); 1511152419Sariff break; 1512152419Sariff case ROFFT_BODY: 1513152419Sariff outflags |= MD_sp; 1514152419Sariff break; 1515152419Sariff default: 1516152419Sariff break; 1517152419Sariff } 1518152419Sariff return 1; 1519170815Sariff} 1520170815Sariff 1521152419Sariffstatic int 1522152419Sariffmd_pre_Sm(struct roff_node *n) 1523152419Sariff{ 1524152419Sariff if (n->child == NULL) 1525152419Sariff outflags ^= MD_Sm; 1526152419Sariff else if (strcmp("on", n->child->string) == 0) 1527152419Sariff outflags |= MD_Sm; 1528152419Sariff else 1529170289Sdwmalone outflags &= ~MD_Sm; 1530164614Sariff 1531152419Sariff if (outflags & MD_Sm) 1532152419Sariff outflags |= MD_spc; 1533152419Sariff 1534152419Sariff return 0; 1535152419Sariff} 1536152419Sariff 1537170815Sariffstatic int 1538170815Sariffmd_pre_Vt(struct roff_node *n) 1539170815Sariff{ 1540170815Sariff switch (n->type) { 1541170815Sariff case ROFFT_BLOCK: 1542170815Sariff md_pre_syn(n); 1543170815Sariff return 1; 1544170815Sariff case ROFFT_BODY: 1545152419Sariff case ROFFT_ELEM: 1546170815Sariff md_pre_raw(n); 1547170815Sariff return 1; 1548170815Sariff default: 1549170815Sariff return 0; 1550170815Sariff } 1551170815Sariff} 1552170815Sariff 1553152419Sariffstatic void 1554152419Sariffmd_post_Vt(struct roff_node *n) 1555152419Sariff{ 1556152419Sariff switch (n->type) { 1557170815Sariff case ROFFT_BODY: 1558152419Sariff case ROFFT_ELEM: 1559152419Sariff md_post_raw(n); 1560164614Sariff break; 1561152419Sariff default: 1562164614Sariff break; 1563152419Sariff } 1564152419Sariff} 1565152419Sariff 1566170815Sariffstatic int 1567170815Sariffmd_pre_Xr(struct roff_node *n) 1568170815Sariff{ 1569170815Sariff n = n->child; 1570152419Sariff if (n == NULL) 1571170815Sariff return 0; 1572170815Sariff md_node(n); 1573170815Sariff n = n->next; 1574170815Sariff if (n == NULL) 1575164614Sariff return 0; 1576152419Sariff outflags &= ~MD_spc; 1577152419Sariff md_word("("); 1578152419Sariff md_node(n); 1579152419Sariff md_word(")"); 1580152419Sariff return 0; 1581152419Sariff} 1582152419Sariff 1583152419Sariffstatic int 1584170815Sariffmd_pre__T(struct roff_node *n) 1585152419Sariff{ 1586170815Sariff if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T) 1587170815Sariff md_word("\""); 1588170815Sariff else 1589152419Sariff md_rawword("*"); 1590152419Sariff outflags &= ~MD_spc; 1591164614Sariff return 1; 1592164614Sariff} 1593164614Sariff 1594164614Sariffstatic void 1595164614Sariffmd_post__T(struct roff_node *n) 1596164614Sariff{ 1597164614Sariff outflags &= ~MD_spc; 1598164614Sariff if (n->parent->tok == MDOC_Rs && n->parent->norm->Rs.quote_T) 1599164614Sariff md_word("\""); 1600164614Sariff else 1601164614Sariff md_rawword("*"); 1602164614Sariff md_post_pc(n); 1603164614Sariff} 1604164614Sariff 1605164614Sariffstatic int 1606170289Sdwmalonemd_pre_br(struct roff_node *n) 1607164614Sariff{ 1608164614Sariff outflags |= MD_br; 1609164614Sariff return 0; 1610164614Sariff} 1611164614Sariff