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