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