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 35struct 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 43static void md_nodelist(struct roff_node *); 44static void md_node(struct roff_node *); 45static const char *md_stack(char); 46static void md_preword(void); 47static void md_rawword(const char *); 48static void md_word(const char *); 49static void md_named(const char *); 50static void md_char(unsigned char); 51static void md_uri(const char *); 52 53static int md_cond_head(struct roff_node *); 54static int md_cond_body(struct roff_node *); 55 56static int md_pre_abort(struct roff_node *); 57static int md_pre_raw(struct roff_node *); 58static int md_pre_word(struct roff_node *); 59static int md_pre_skip(struct roff_node *); 60static void md_pre_syn(struct roff_node *); 61static int md_pre_An(struct roff_node *); 62static int md_pre_Ap(struct roff_node *); 63static int md_pre_Bd(struct roff_node *); 64static int md_pre_Bk(struct roff_node *); 65static int md_pre_Bl(struct roff_node *); 66static int md_pre_D1(struct roff_node *); 67static int md_pre_Dl(struct roff_node *); 68static int md_pre_En(struct roff_node *); 69static int md_pre_Eo(struct roff_node *); 70static int md_pre_Fa(struct roff_node *); 71static int md_pre_Fd(struct roff_node *); 72static int md_pre_Fn(struct roff_node *); 73static int md_pre_Fo(struct roff_node *); 74static int md_pre_In(struct roff_node *); 75static int md_pre_It(struct roff_node *); 76static int md_pre_Lk(struct roff_node *); 77static int md_pre_Mt(struct roff_node *); 78static int md_pre_Nd(struct roff_node *); 79static int md_pre_Nm(struct roff_node *); 80static int md_pre_No(struct roff_node *); 81static int md_pre_Ns(struct roff_node *); 82static int md_pre_Pp(struct roff_node *); 83static int md_pre_Rs(struct roff_node *); 84static int md_pre_Sh(struct roff_node *); 85static int md_pre_Sm(struct roff_node *); 86static int md_pre_Vt(struct roff_node *); 87static int md_pre_Xr(struct roff_node *); 88static int md_pre__T(struct roff_node *); 89static int md_pre_br(struct roff_node *); 90 91static void md_post_raw(struct roff_node *); 92static void md_post_word(struct roff_node *); 93static void md_post_pc(struct roff_node *); 94static void md_post_Bk(struct roff_node *); 95static void md_post_Bl(struct roff_node *); 96static void md_post_D1(struct roff_node *); 97static void md_post_En(struct roff_node *); 98static void md_post_Eo(struct roff_node *); 99static void md_post_Fa(struct roff_node *); 100static void md_post_Fd(struct roff_node *); 101static void md_post_Fl(struct roff_node *); 102static void md_post_Fn(struct roff_node *); 103static void md_post_Fo(struct roff_node *); 104static void md_post_In(struct roff_node *); 105static void md_post_It(struct roff_node *); 106static void md_post_Lb(struct roff_node *); 107static void md_post_Nm(struct roff_node *); 108static void md_post_Pf(struct roff_node *); 109static void md_post_Vt(struct roff_node *); 110static void md_post__T(struct roff_node *); 111 112static 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}; 235static const struct md_act *md_act(enum roff_tok); 236 237static 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 249static 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 257static int code_blocks, quote_blocks, list_blocks; 258static int outcount; 259 260 261static const struct md_act * 262md_act(enum roff_tok tok) 263{ 264 assert(tok >= MDOC_Dd && tok <= MDOC_MAX); 265 return md_acts + (tok - MDOC_Dd); 266} 267 268void 269markdown_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 297static void 298md_nodelist(struct roff_node *n) 299{ 300 while (n != NULL) { 301 md_node(n); 302 n = n->next; 303 } 304} 305 306static void 307md_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 370static const char * 371md_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 */ 399static void 400md_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 */ 466static void 467md_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 */ 507static void 508md_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 */ 694static void 695md_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 */ 705static void 706md_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 727static int 728md_cond_head(struct roff_node *n) 729{ 730 return n->type == ROFFT_HEAD; 731} 732 733static int 734md_cond_body(struct roff_node *n) 735{ 736 return n->type == ROFFT_BODY; 737} 738 739static int 740md_pre_abort(struct roff_node *n) 741{ 742 abort(); 743} 744 745static int 746md_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 759static void 760md_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 772static int 773md_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 784static void 785md_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 795static void 796md_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 816static int 817md_pre_skip(struct roff_node *n) 818{ 819 return 0; 820} 821 822static void 823md_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 859static int 860md_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 881static int 882md_pre_Ap(struct roff_node *n) 883{ 884 outflags &= ~MD_spc; 885 md_word("'"); 886 outflags &= ~MD_spc; 887 return 0; 888} 889 890static int 891md_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 902static int 903md_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 916static void 917md_post_Bk(struct roff_node *n) 918{ 919 if (n->type == ROFFT_BODY) 920 outflags &= ~MD_Bk; 921} 922 923static int 924md_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 933static void 934md_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 942static int 943md_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 960static void 961md_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 971static int 972md_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 989static int 990md_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 1001static void 1002md_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 1013static int 1014md_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 1028static void 1029md_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 1045static int 1046md_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 1067static void 1068md_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 1076static int 1077md_pre_Fd(struct roff_node *n) 1078{ 1079 md_pre_syn(n); 1080 md_pre_raw(n); 1081 return 1; 1082} 1083 1084static void 1085md_post_Fd(struct roff_node *n) 1086{ 1087 md_post_raw(n); 1088 outflags |= MD_br; 1089} 1090 1091static void 1092md_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 1102static int 1103md_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 1123static void 1124md_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 1133static int 1134md_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 1155static void 1156md_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 1171static int 1172md_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 1188static void 1189md_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 1201static int 1202md_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 1278static void 1279md_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 1336static void 1337md_post_Lb(struct roff_node *n) 1338{ 1339 if (n->sec == SEC_LIBRARY) 1340 outflags |= MD_br; 1341} 1342 1343static void 1344md_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 1358static int 1359md_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 1398static int 1399md_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 1421static int 1422md_pre_Nd(struct roff_node *n) 1423{ 1424 outflags &= ~MD_nl; 1425 outflags |= MD_spc; 1426 md_word("-"); 1427 return 1; 1428} 1429 1430static int 1431md_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 1448static void 1449md_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 1464static int 1465md_pre_No(struct roff_node *n) 1466{ 1467 outflags |= MD_spc_force; 1468 return 1; 1469} 1470 1471static int 1472md_pre_Ns(struct roff_node *n) 1473{ 1474 outflags &= ~MD_spc; 1475 return 0; 1476} 1477 1478static void 1479md_post_Pf(struct roff_node *n) 1480{ 1481 if (n->next != NULL && (n->next->flags & NODE_LINE) == 0) 1482 outflags &= ~MD_spc; 1483} 1484 1485static int 1486md_pre_Pp(struct roff_node *n) 1487{ 1488 outflags |= MD_sp; 1489 return 0; 1490} 1491 1492static int 1493md_pre_Rs(struct roff_node *n) 1494{ 1495 if (n->sec == SEC_SEE_ALSO) 1496 outflags |= MD_sp; 1497 return 1; 1498} 1499 1500static int 1501md_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 1521static int 1522md_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 1537static int 1538md_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 1553static void 1554md_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 1566static int 1567md_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 1583static int 1584md_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 1594static void 1595md_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 1605static int 1606md_pre_br(struct roff_node *n) 1607{ 1608 outflags |= MD_br; 1609 return 0; 1610} 1611