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