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