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