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