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