1 /* $Id: mdoc.c,v 1.238 2015/02/12 13:00:52 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4 * Copyright (c) 2010, 2012-2015 Ingo Schwarze <schwarze@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 #include "config.h" 19 20 #include <sys/types.h> 21 22 #include <assert.h> 23 #include <ctype.h> 24 #include <stdarg.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <time.h> 29 30 #include "mdoc.h" 31 #include "mandoc.h" 32 #include "mandoc_aux.h" 33 #include "libmdoc.h" 34 #include "libmandoc.h" 35 36 const char *const __mdoc_macronames[MDOC_MAX + 1] = { 37 "Ap", "Dd", "Dt", "Os", 38 "Sh", "Ss", "Pp", "D1", 39 "Dl", "Bd", "Ed", "Bl", 40 "El", "It", "Ad", "An", 41 "Ar", "Cd", "Cm", "Dv", 42 "Er", "Ev", "Ex", "Fa", 43 "Fd", "Fl", "Fn", "Ft", 44 "Ic", "In", "Li", "Nd", 45 "Nm", "Op", "Ot", "Pa", 46 "Rv", "St", "Va", "Vt", 47 "Xr", "%A", "%B", "%D", 48 "%I", "%J", "%N", "%O", 49 "%P", "%R", "%T", "%V", 50 "Ac", "Ao", "Aq", "At", 51 "Bc", "Bf", "Bo", "Bq", 52 "Bsx", "Bx", "Db", "Dc", 53 "Do", "Dq", "Ec", "Ef", 54 "Em", "Eo", "Fx", "Ms", 55 "No", "Ns", "Nx", "Ox", 56 "Pc", "Pf", "Po", "Pq", 57 "Qc", "Ql", "Qo", "Qq", 58 "Re", "Rs", "Sc", "So", 59 "Sq", "Sm", "Sx", "Sy", 60 "Tn", "Ux", "Xc", "Xo", 61 "Fo", "Fc", "Oo", "Oc", 62 "Bk", "Ek", "Bt", "Hf", 63 "Fr", "Ud", "Lb", "Lp", 64 "Lk", "Mt", "Brq", "Bro", 65 "Brc", "%C", "Es", "En", 66 "Dx", "%Q", "br", "sp", 67 "%U", "Ta", "ll", "text", 68 }; 69 70 const char *const __mdoc_argnames[MDOC_ARG_MAX] = { 71 "split", "nosplit", "ragged", 72 "unfilled", "literal", "file", 73 "offset", "bullet", "dash", 74 "hyphen", "item", "enum", 75 "tag", "diag", "hang", 76 "ohang", "inset", "column", 77 "width", "compact", "std", 78 "filled", "words", "emphasis", 79 "symbolic", "nested", "centered" 80 }; 81 82 const char * const *mdoc_macronames = __mdoc_macronames; 83 const char * const *mdoc_argnames = __mdoc_argnames; 84 85 static void mdoc_node_free(struct mdoc_node *); 86 static void mdoc_node_unlink(struct mdoc *, 87 struct mdoc_node *); 88 static void mdoc_free1(struct mdoc *); 89 static void mdoc_alloc1(struct mdoc *); 90 static struct mdoc_node *node_alloc(struct mdoc *, int, int, 91 enum mdoct, enum mdoc_type); 92 static void node_append(struct mdoc *, struct mdoc_node *); 93 static int mdoc_ptext(struct mdoc *, int, char *, int); 94 static int mdoc_pmacro(struct mdoc *, int, char *, int); 95 96 97 const struct mdoc_node * 98 mdoc_node(const struct mdoc *mdoc) 99 { 100 101 return(mdoc->first); 102 } 103 104 const struct mdoc_meta * 105 mdoc_meta(const struct mdoc *mdoc) 106 { 107 108 return(&mdoc->meta); 109 } 110 111 /* 112 * Frees volatile resources (parse tree, meta-data, fields). 113 */ 114 static void 115 mdoc_free1(struct mdoc *mdoc) 116 { 117 118 if (mdoc->first) 119 mdoc_node_delete(mdoc, mdoc->first); 120 free(mdoc->meta.msec); 121 free(mdoc->meta.vol); 122 free(mdoc->meta.arch); 123 free(mdoc->meta.date); 124 free(mdoc->meta.title); 125 free(mdoc->meta.os); 126 free(mdoc->meta.name); 127 } 128 129 /* 130 * Allocate all volatile resources (parse tree, meta-data, fields). 131 */ 132 static void 133 mdoc_alloc1(struct mdoc *mdoc) 134 { 135 136 memset(&mdoc->meta, 0, sizeof(struct mdoc_meta)); 137 mdoc->flags = 0; 138 mdoc->lastnamed = mdoc->lastsec = SEC_NONE; 139 mdoc->last = mandoc_calloc(1, sizeof(struct mdoc_node)); 140 mdoc->first = mdoc->last; 141 mdoc->last->type = MDOC_ROOT; 142 mdoc->last->tok = MDOC_MAX; 143 mdoc->next = MDOC_NEXT_CHILD; 144 } 145 146 /* 147 * Free up volatile resources (see mdoc_free1()) then re-initialises the 148 * data with mdoc_alloc1(). After invocation, parse data has been reset 149 * and the parser is ready for re-invocation on a new tree; however, 150 * cross-parse non-volatile data is kept intact. 151 */ 152 void 153 mdoc_reset(struct mdoc *mdoc) 154 { 155 156 mdoc_free1(mdoc); 157 mdoc_alloc1(mdoc); 158 } 159 160 /* 161 * Completely free up all volatile and non-volatile parse resources. 162 * After invocation, the pointer is no longer usable. 163 */ 164 void 165 mdoc_free(struct mdoc *mdoc) 166 { 167 168 mdoc_free1(mdoc); 169 free(mdoc); 170 } 171 172 /* 173 * Allocate volatile and non-volatile parse resources. 174 */ 175 struct mdoc * 176 mdoc_alloc(struct roff *roff, struct mparse *parse, 177 const char *defos, int quick) 178 { 179 struct mdoc *p; 180 181 p = mandoc_calloc(1, sizeof(struct mdoc)); 182 183 p->parse = parse; 184 p->defos = defos; 185 p->quick = quick; 186 p->roff = roff; 187 188 mdoc_hash_init(); 189 mdoc_alloc1(p); 190 return(p); 191 } 192 193 void 194 mdoc_endparse(struct mdoc *mdoc) 195 { 196 197 mdoc_macroend(mdoc); 198 } 199 200 void 201 mdoc_addeqn(struct mdoc *mdoc, const struct eqn *ep) 202 { 203 struct mdoc_node *n; 204 205 n = node_alloc(mdoc, ep->ln, ep->pos, MDOC_MAX, MDOC_EQN); 206 n->eqn = ep; 207 if (ep->ln > mdoc->last->line) 208 n->flags |= MDOC_LINE; 209 node_append(mdoc, n); 210 mdoc->next = MDOC_NEXT_SIBLING; 211 } 212 213 void 214 mdoc_addspan(struct mdoc *mdoc, const struct tbl_span *sp) 215 { 216 struct mdoc_node *n; 217 218 n = node_alloc(mdoc, sp->line, 0, MDOC_MAX, MDOC_TBL); 219 n->span = sp; 220 node_append(mdoc, n); 221 mdoc->next = MDOC_NEXT_SIBLING; 222 } 223 224 /* 225 * Main parse routine. Parses a single line -- really just hands off to 226 * the macro (mdoc_pmacro()) or text parser (mdoc_ptext()). 227 */ 228 int 229 mdoc_parseln(struct mdoc *mdoc, int ln, char *buf, int offs) 230 { 231 232 if (mdoc->last->type != MDOC_EQN || ln > mdoc->last->line) 233 mdoc->flags |= MDOC_NEWLINE; 234 235 /* 236 * Let the roff nS register switch SYNOPSIS mode early, 237 * such that the parser knows at all times 238 * whether this mode is on or off. 239 * Note that this mode is also switched by the Sh macro. 240 */ 241 if (roff_getreg(mdoc->roff, "nS")) 242 mdoc->flags |= MDOC_SYNOPSIS; 243 else 244 mdoc->flags &= ~MDOC_SYNOPSIS; 245 246 return(roff_getcontrol(mdoc->roff, buf, &offs) ? 247 mdoc_pmacro(mdoc, ln, buf, offs) : 248 mdoc_ptext(mdoc, ln, buf, offs)); 249 } 250 251 void 252 mdoc_macro(MACRO_PROT_ARGS) 253 { 254 assert(tok < MDOC_MAX); 255 256 if (mdoc->flags & MDOC_PBODY) { 257 if (tok == MDOC_Dt) { 258 mandoc_vmsg(MANDOCERR_DT_LATE, 259 mdoc->parse, line, ppos, 260 "Dt %s", buf + *pos); 261 return; 262 } 263 } else if ( ! (mdoc_macros[tok].flags & MDOC_PROLOGUE)) { 264 if (mdoc->meta.title == NULL) { 265 mandoc_vmsg(MANDOCERR_DT_NOTITLE, 266 mdoc->parse, line, ppos, "%s %s", 267 mdoc_macronames[tok], buf + *pos); 268 mdoc->meta.title = mandoc_strdup("UNTITLED"); 269 } 270 if (NULL == mdoc->meta.vol) 271 mdoc->meta.vol = mandoc_strdup("LOCAL"); 272 mdoc->flags |= MDOC_PBODY; 273 } 274 (*mdoc_macros[tok].fp)(mdoc, tok, line, ppos, pos, buf); 275 } 276 277 278 static void 279 node_append(struct mdoc *mdoc, struct mdoc_node *p) 280 { 281 282 assert(mdoc->last); 283 assert(mdoc->first); 284 assert(MDOC_ROOT != p->type); 285 286 switch (mdoc->next) { 287 case MDOC_NEXT_SIBLING: 288 mdoc->last->next = p; 289 p->prev = mdoc->last; 290 p->parent = mdoc->last->parent; 291 break; 292 case MDOC_NEXT_CHILD: 293 mdoc->last->child = p; 294 p->parent = mdoc->last; 295 break; 296 default: 297 abort(); 298 /* NOTREACHED */ 299 } 300 301 p->parent->nchild++; 302 303 /* 304 * Copy over the normalised-data pointer of our parent. Not 305 * everybody has one, but copying a null pointer is fine. 306 */ 307 308 switch (p->type) { 309 case MDOC_BODY: 310 if (ENDBODY_NOT != p->end) 311 break; 312 /* FALLTHROUGH */ 313 case MDOC_TAIL: 314 /* FALLTHROUGH */ 315 case MDOC_HEAD: 316 p->norm = p->parent->norm; 317 break; 318 default: 319 break; 320 } 321 322 mdoc_valid_pre(mdoc, p); 323 324 switch (p->type) { 325 case MDOC_HEAD: 326 assert(MDOC_BLOCK == p->parent->type); 327 p->parent->head = p; 328 break; 329 case MDOC_TAIL: 330 assert(MDOC_BLOCK == p->parent->type); 331 p->parent->tail = p; 332 break; 333 case MDOC_BODY: 334 if (p->end) 335 break; 336 assert(MDOC_BLOCK == p->parent->type); 337 p->parent->body = p; 338 break; 339 default: 340 break; 341 } 342 343 mdoc->last = p; 344 345 switch (p->type) { 346 case MDOC_TBL: 347 /* FALLTHROUGH */ 348 case MDOC_TEXT: 349 mdoc_valid_post(mdoc); 350 break; 351 default: 352 break; 353 } 354 } 355 356 static struct mdoc_node * 357 node_alloc(struct mdoc *mdoc, int line, int pos, 358 enum mdoct tok, enum mdoc_type type) 359 { 360 struct mdoc_node *p; 361 362 p = mandoc_calloc(1, sizeof(struct mdoc_node)); 363 p->sec = mdoc->lastsec; 364 p->line = line; 365 p->pos = pos; 366 p->tok = tok; 367 p->type = type; 368 369 /* Flag analysis. */ 370 371 if (MDOC_SYNOPSIS & mdoc->flags) 372 p->flags |= MDOC_SYNPRETTY; 373 else 374 p->flags &= ~MDOC_SYNPRETTY; 375 if (MDOC_NEWLINE & mdoc->flags) 376 p->flags |= MDOC_LINE; 377 mdoc->flags &= ~MDOC_NEWLINE; 378 379 return(p); 380 } 381 382 void 383 mdoc_tail_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok) 384 { 385 struct mdoc_node *p; 386 387 p = node_alloc(mdoc, line, pos, tok, MDOC_TAIL); 388 node_append(mdoc, p); 389 mdoc->next = MDOC_NEXT_CHILD; 390 } 391 392 struct mdoc_node * 393 mdoc_head_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok) 394 { 395 struct mdoc_node *p; 396 397 assert(mdoc->first); 398 assert(mdoc->last); 399 p = node_alloc(mdoc, line, pos, tok, MDOC_HEAD); 400 node_append(mdoc, p); 401 mdoc->next = MDOC_NEXT_CHILD; 402 return(p); 403 } 404 405 struct mdoc_node * 406 mdoc_body_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok) 407 { 408 struct mdoc_node *p; 409 410 p = node_alloc(mdoc, line, pos, tok, MDOC_BODY); 411 node_append(mdoc, p); 412 mdoc->next = MDOC_NEXT_CHILD; 413 return(p); 414 } 415 416 struct mdoc_node * 417 mdoc_endbody_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok, 418 struct mdoc_node *body, enum mdoc_endbody end) 419 { 420 struct mdoc_node *p; 421 422 body->flags |= MDOC_ENDED; 423 body->parent->flags |= MDOC_ENDED; 424 p = node_alloc(mdoc, line, pos, tok, MDOC_BODY); 425 p->body = body; 426 p->norm = body->norm; 427 p->end = end; 428 node_append(mdoc, p); 429 mdoc->next = MDOC_NEXT_SIBLING; 430 return(p); 431 } 432 433 struct mdoc_node * 434 mdoc_block_alloc(struct mdoc *mdoc, int line, int pos, 435 enum mdoct tok, struct mdoc_arg *args) 436 { 437 struct mdoc_node *p; 438 439 p = node_alloc(mdoc, line, pos, tok, MDOC_BLOCK); 440 p->args = args; 441 if (p->args) 442 (args->refcnt)++; 443 444 switch (tok) { 445 case MDOC_Bd: 446 /* FALLTHROUGH */ 447 case MDOC_Bf: 448 /* FALLTHROUGH */ 449 case MDOC_Bl: 450 /* FALLTHROUGH */ 451 case MDOC_En: 452 /* FALLTHROUGH */ 453 case MDOC_Rs: 454 p->norm = mandoc_calloc(1, sizeof(union mdoc_data)); 455 break; 456 default: 457 break; 458 } 459 node_append(mdoc, p); 460 mdoc->next = MDOC_NEXT_CHILD; 461 return(p); 462 } 463 464 void 465 mdoc_elem_alloc(struct mdoc *mdoc, int line, int pos, 466 enum mdoct tok, struct mdoc_arg *args) 467 { 468 struct mdoc_node *p; 469 470 p = node_alloc(mdoc, line, pos, tok, MDOC_ELEM); 471 p->args = args; 472 if (p->args) 473 (args->refcnt)++; 474 475 switch (tok) { 476 case MDOC_An: 477 p->norm = mandoc_calloc(1, sizeof(union mdoc_data)); 478 break; 479 default: 480 break; 481 } 482 node_append(mdoc, p); 483 mdoc->next = MDOC_NEXT_CHILD; 484 } 485 486 void 487 mdoc_word_alloc(struct mdoc *mdoc, int line, int pos, const char *p) 488 { 489 struct mdoc_node *n; 490 491 n = node_alloc(mdoc, line, pos, MDOC_MAX, MDOC_TEXT); 492 n->string = roff_strdup(mdoc->roff, p); 493 node_append(mdoc, n); 494 mdoc->next = MDOC_NEXT_SIBLING; 495 } 496 497 void 498 mdoc_word_append(struct mdoc *mdoc, const char *p) 499 { 500 struct mdoc_node *n; 501 char *addstr, *newstr; 502 503 n = mdoc->last; 504 addstr = roff_strdup(mdoc->roff, p); 505 mandoc_asprintf(&newstr, "%s %s", n->string, addstr); 506 free(addstr); 507 free(n->string); 508 n->string = newstr; 509 mdoc->next = MDOC_NEXT_SIBLING; 510 } 511 512 static void 513 mdoc_node_free(struct mdoc_node *p) 514 { 515 516 if (MDOC_BLOCK == p->type || MDOC_ELEM == p->type) 517 free(p->norm); 518 if (p->string) 519 free(p->string); 520 if (p->args) 521 mdoc_argv_free(p->args); 522 free(p); 523 } 524 525 static void 526 mdoc_node_unlink(struct mdoc *mdoc, struct mdoc_node *n) 527 { 528 529 /* Adjust siblings. */ 530 531 if (n->prev) 532 n->prev->next = n->next; 533 if (n->next) 534 n->next->prev = n->prev; 535 536 /* Adjust parent. */ 537 538 if (n->parent) { 539 n->parent->nchild--; 540 if (n->parent->child == n) 541 n->parent->child = n->prev ? n->prev : n->next; 542 if (n->parent->last == n) 543 n->parent->last = n->prev ? n->prev : NULL; 544 } 545 546 /* Adjust parse point, if applicable. */ 547 548 if (mdoc && mdoc->last == n) { 549 if (n->prev) { 550 mdoc->last = n->prev; 551 mdoc->next = MDOC_NEXT_SIBLING; 552 } else { 553 mdoc->last = n->parent; 554 mdoc->next = MDOC_NEXT_CHILD; 555 } 556 } 557 558 if (mdoc && mdoc->first == n) 559 mdoc->first = NULL; 560 } 561 562 void 563 mdoc_node_delete(struct mdoc *mdoc, struct mdoc_node *p) 564 { 565 566 while (p->child) { 567 assert(p->nchild); 568 mdoc_node_delete(mdoc, p->child); 569 } 570 assert(0 == p->nchild); 571 572 mdoc_node_unlink(mdoc, p); 573 mdoc_node_free(p); 574 } 575 576 void 577 mdoc_node_relink(struct mdoc *mdoc, struct mdoc_node *p) 578 { 579 580 mdoc_node_unlink(mdoc, p); 581 node_append(mdoc, p); 582 } 583 584 /* 585 * Parse free-form text, that is, a line that does not begin with the 586 * control character. 587 */ 588 static int 589 mdoc_ptext(struct mdoc *mdoc, int line, char *buf, int offs) 590 { 591 char *c, *ws, *end; 592 struct mdoc_node *n; 593 594 assert(mdoc->last); 595 n = mdoc->last; 596 597 /* 598 * Divert directly to list processing if we're encountering a 599 * columnar MDOC_BLOCK with or without a prior MDOC_BLOCK entry 600 * (a MDOC_BODY means it's already open, in which case we should 601 * process within its context in the normal way). 602 */ 603 604 if (n->tok == MDOC_Bl && n->type == MDOC_BODY && 605 n->end == ENDBODY_NOT && n->norm->Bl.type == LIST_column) { 606 /* `Bl' is open without any children. */ 607 mdoc->flags |= MDOC_FREECOL; 608 mdoc_macro(mdoc, MDOC_It, line, offs, &offs, buf); 609 return(1); 610 } 611 612 if (MDOC_It == n->tok && MDOC_BLOCK == n->type && 613 NULL != n->parent && 614 MDOC_Bl == n->parent->tok && 615 LIST_column == n->parent->norm->Bl.type) { 616 /* `Bl' has block-level `It' children. */ 617 mdoc->flags |= MDOC_FREECOL; 618 mdoc_macro(mdoc, MDOC_It, line, offs, &offs, buf); 619 return(1); 620 } 621 622 /* 623 * Search for the beginning of unescaped trailing whitespace (ws) 624 * and for the first character not to be output (end). 625 */ 626 627 /* FIXME: replace with strcspn(). */ 628 ws = NULL; 629 for (c = end = buf + offs; *c; c++) { 630 switch (*c) { 631 case ' ': 632 if (NULL == ws) 633 ws = c; 634 continue; 635 case '\t': 636 /* 637 * Always warn about trailing tabs, 638 * even outside literal context, 639 * where they should be put on the next line. 640 */ 641 if (NULL == ws) 642 ws = c; 643 /* 644 * Strip trailing tabs in literal context only; 645 * outside, they affect the next line. 646 */ 647 if (MDOC_LITERAL & mdoc->flags) 648 continue; 649 break; 650 case '\\': 651 /* Skip the escaped character, too, if any. */ 652 if (c[1]) 653 c++; 654 /* FALLTHROUGH */ 655 default: 656 ws = NULL; 657 break; 658 } 659 end = c + 1; 660 } 661 *end = '\0'; 662 663 if (ws) 664 mandoc_msg(MANDOCERR_SPACE_EOL, mdoc->parse, 665 line, (int)(ws-buf), NULL); 666 667 if (buf[offs] == '\0' && ! (mdoc->flags & MDOC_LITERAL)) { 668 mandoc_msg(MANDOCERR_FI_BLANK, mdoc->parse, 669 line, (int)(c - buf), NULL); 670 671 /* 672 * Insert a `sp' in the case of a blank line. Technically, 673 * blank lines aren't allowed, but enough manuals assume this 674 * behaviour that we want to work around it. 675 */ 676 mdoc_elem_alloc(mdoc, line, offs, MDOC_sp, NULL); 677 mdoc->next = MDOC_NEXT_SIBLING; 678 mdoc_valid_post(mdoc); 679 return(1); 680 } 681 682 mdoc_word_alloc(mdoc, line, offs, buf+offs); 683 684 if (mdoc->flags & MDOC_LITERAL) 685 return(1); 686 687 /* 688 * End-of-sentence check. If the last character is an unescaped 689 * EOS character, then flag the node as being the end of a 690 * sentence. The front-end will know how to interpret this. 691 */ 692 693 assert(buf < end); 694 695 if (mandoc_eos(buf+offs, (size_t)(end-buf-offs))) 696 mdoc->last->flags |= MDOC_EOS; 697 return(1); 698 } 699 700 /* 701 * Parse a macro line, that is, a line beginning with the control 702 * character. 703 */ 704 static int 705 mdoc_pmacro(struct mdoc *mdoc, int ln, char *buf, int offs) 706 { 707 struct mdoc_node *n; 708 const char *cp; 709 enum mdoct tok; 710 int i, sv; 711 char mac[5]; 712 713 sv = offs; 714 715 /* 716 * Copy the first word into a nil-terminated buffer. 717 * Stop when a space, tab, escape, or eoln is encountered. 718 */ 719 720 i = 0; 721 while (i < 4 && strchr(" \t\\", buf[offs]) == NULL) 722 mac[i++] = buf[offs++]; 723 724 mac[i] = '\0'; 725 726 tok = (i > 1 && i < 4) ? mdoc_hash_find(mac) : MDOC_MAX; 727 728 if (tok == MDOC_MAX) { 729 mandoc_msg(MANDOCERR_MACRO, mdoc->parse, 730 ln, sv, buf + sv - 1); 731 return(1); 732 } 733 734 /* Skip a leading escape sequence or tab. */ 735 736 switch (buf[offs]) { 737 case '\\': 738 cp = buf + offs + 1; 739 mandoc_escape(&cp, NULL, NULL); 740 offs = cp - buf; 741 break; 742 case '\t': 743 offs++; 744 break; 745 default: 746 break; 747 } 748 749 /* Jump to the next non-whitespace word. */ 750 751 while (buf[offs] && ' ' == buf[offs]) 752 offs++; 753 754 /* 755 * Trailing whitespace. Note that tabs are allowed to be passed 756 * into the parser as "text", so we only warn about spaces here. 757 */ 758 759 if ('\0' == buf[offs] && ' ' == buf[offs - 1]) 760 mandoc_msg(MANDOCERR_SPACE_EOL, mdoc->parse, 761 ln, offs - 1, NULL); 762 763 /* 764 * If an initial macro or a list invocation, divert directly 765 * into macro processing. 766 */ 767 768 if (NULL == mdoc->last || MDOC_It == tok || MDOC_El == tok) { 769 mdoc_macro(mdoc, tok, ln, sv, &offs, buf); 770 return(1); 771 } 772 773 n = mdoc->last; 774 assert(mdoc->last); 775 776 /* 777 * If the first macro of a `Bl -column', open an `It' block 778 * context around the parsed macro. 779 */ 780 781 if (n->tok == MDOC_Bl && n->type == MDOC_BODY && 782 n->end == ENDBODY_NOT && n->norm->Bl.type == LIST_column) { 783 mdoc->flags |= MDOC_FREECOL; 784 mdoc_macro(mdoc, MDOC_It, ln, sv, &sv, buf); 785 return(1); 786 } 787 788 /* 789 * If we're following a block-level `It' within a `Bl -column' 790 * context (perhaps opened in the above block or in ptext()), 791 * then open an `It' block context around the parsed macro. 792 */ 793 794 if (MDOC_It == n->tok && MDOC_BLOCK == n->type && 795 NULL != n->parent && 796 MDOC_Bl == n->parent->tok && 797 LIST_column == n->parent->norm->Bl.type) { 798 mdoc->flags |= MDOC_FREECOL; 799 mdoc_macro(mdoc, MDOC_It, ln, sv, &sv, buf); 800 return(1); 801 } 802 803 /* Normal processing of a macro. */ 804 805 mdoc_macro(mdoc, tok, ln, sv, &offs, buf); 806 807 /* In quick mode (for mandocdb), abort after the NAME section. */ 808 809 if (mdoc->quick && MDOC_Sh == tok && 810 SEC_NAME != mdoc->last->sec) 811 return(2); 812 813 return(1); 814 } 815 816 enum mdelim 817 mdoc_isdelim(const char *p) 818 { 819 820 if ('\0' == p[0]) 821 return(DELIM_NONE); 822 823 if ('\0' == p[1]) 824 switch (p[0]) { 825 case '(': 826 /* FALLTHROUGH */ 827 case '[': 828 return(DELIM_OPEN); 829 case '|': 830 return(DELIM_MIDDLE); 831 case '.': 832 /* FALLTHROUGH */ 833 case ',': 834 /* FALLTHROUGH */ 835 case ';': 836 /* FALLTHROUGH */ 837 case ':': 838 /* FALLTHROUGH */ 839 case '?': 840 /* FALLTHROUGH */ 841 case '!': 842 /* FALLTHROUGH */ 843 case ')': 844 /* FALLTHROUGH */ 845 case ']': 846 return(DELIM_CLOSE); 847 default: 848 return(DELIM_NONE); 849 } 850 851 if ('\\' != p[0]) 852 return(DELIM_NONE); 853 854 if (0 == strcmp(p + 1, ".")) 855 return(DELIM_CLOSE); 856 if (0 == strcmp(p + 1, "fR|\\fP")) 857 return(DELIM_MIDDLE); 858 859 return(DELIM_NONE); 860 } 861 862 void 863 mdoc_deroff(char **dest, const struct mdoc_node *n) 864 { 865 char *cp; 866 size_t sz; 867 868 if (MDOC_TEXT != n->type) { 869 for (n = n->child; n; n = n->next) 870 mdoc_deroff(dest, n); 871 return; 872 } 873 874 /* Skip leading whitespace. */ 875 876 for (cp = n->string; '\0' != *cp; cp++) 877 if (0 == isspace((unsigned char)*cp)) 878 break; 879 880 /* Skip trailing whitespace. */ 881 882 for (sz = strlen(cp); sz; sz--) 883 if (0 == isspace((unsigned char)cp[sz-1])) 884 break; 885 886 /* Skip empty strings. */ 887 888 if (0 == sz) 889 return; 890 891 if (NULL == *dest) { 892 *dest = mandoc_strndup(cp, sz); 893 return; 894 } 895 896 mandoc_asprintf(&cp, "%s %*s", *dest, (int)sz, cp); 897 free(*dest); 898 *dest = cp; 899 } 900