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