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