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