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