mdoc.c revision 1.122
1/* $OpenBSD: mdoc.c,v 1.122 2014/11/28 06:26:46 schwarze Exp $ */ 2/* 3 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4 * Copyright (c) 2010, 2012, 2013, 2014 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 "mdoc.h" 29#include "mandoc.h" 30#include "mandoc_aux.h" 31#include "libmdoc.h" 32#include "libmandoc.h" 33 34const char *const __mdoc_macronames[MDOC_MAX + 1] = { 35 "Ap", "Dd", "Dt", "Os", 36 "Sh", "Ss", "Pp", "D1", 37 "Dl", "Bd", "Ed", "Bl", 38 "El", "It", "Ad", "An", 39 "Ar", "Cd", "Cm", "Dv", 40 "Er", "Ev", "Ex", "Fa", 41 "Fd", "Fl", "Fn", "Ft", 42 "Ic", "In", "Li", "Nd", 43 "Nm", "Op", "Ot", "Pa", 44 "Rv", "St", "Va", "Vt", 45 "Xr", "%A", "%B", "%D", 46 "%I", "%J", "%N", "%O", 47 "%P", "%R", "%T", "%V", 48 "Ac", "Ao", "Aq", "At", 49 "Bc", "Bf", "Bo", "Bq", 50 "Bsx", "Bx", "Db", "Dc", 51 "Do", "Dq", "Ec", "Ef", 52 "Em", "Eo", "Fx", "Ms", 53 "No", "Ns", "Nx", "Ox", 54 "Pc", "Pf", "Po", "Pq", 55 "Qc", "Ql", "Qo", "Qq", 56 "Re", "Rs", "Sc", "So", 57 "Sq", "Sm", "Sx", "Sy", 58 "Tn", "Ux", "Xc", "Xo", 59 "Fo", "Fc", "Oo", "Oc", 60 "Bk", "Ek", "Bt", "Hf", 61 "Fr", "Ud", "Lb", "Lp", 62 "Lk", "Mt", "Brq", "Bro", 63 "Brc", "%C", "Es", "En", 64 "Dx", "%Q", "br", "sp", 65 "%U", "Ta", "ll", "text", 66 }; 67 68const char *const __mdoc_argnames[MDOC_ARG_MAX] = { 69 "split", "nosplit", "ragged", 70 "unfilled", "literal", "file", 71 "offset", "bullet", "dash", 72 "hyphen", "item", "enum", 73 "tag", "diag", "hang", 74 "ohang", "inset", "column", 75 "width", "compact", "std", 76 "filled", "words", "emphasis", 77 "symbolic", "nested", "centered" 78 }; 79 80const char * const *mdoc_macronames = __mdoc_macronames; 81const char * const *mdoc_argnames = __mdoc_argnames; 82 83static void mdoc_node_free(struct mdoc_node *); 84static void mdoc_node_unlink(struct mdoc *, 85 struct mdoc_node *); 86static void mdoc_free1(struct mdoc *); 87static void mdoc_alloc1(struct mdoc *); 88static struct mdoc_node *node_alloc(struct mdoc *, int, int, 89 enum mdoct, enum mdoc_type); 90static void node_append(struct mdoc *, struct mdoc_node *); 91static int mdoc_ptext(struct mdoc *, int, char *, int); 92static int mdoc_pmacro(struct mdoc *, int, char *, int); 93 94 95const struct mdoc_node * 96mdoc_node(const struct mdoc *mdoc) 97{ 98 99 return(mdoc->first); 100} 101 102const struct mdoc_meta * 103mdoc_meta(const struct mdoc *mdoc) 104{ 105 106 return(&mdoc->meta); 107} 108 109/* 110 * Frees volatile resources (parse tree, meta-data, fields). 111 */ 112static void 113mdoc_free1(struct mdoc *mdoc) 114{ 115 116 if (mdoc->first) 117 mdoc_node_delete(mdoc, mdoc->first); 118 free(mdoc->meta.msec); 119 free(mdoc->meta.vol); 120 free(mdoc->meta.arch); 121 free(mdoc->meta.date); 122 free(mdoc->meta.title); 123 free(mdoc->meta.os); 124 free(mdoc->meta.name); 125} 126 127/* 128 * Allocate all volatile resources (parse tree, meta-data, fields). 129 */ 130static void 131mdoc_alloc1(struct mdoc *mdoc) 132{ 133 134 memset(&mdoc->meta, 0, sizeof(struct mdoc_meta)); 135 mdoc->flags = 0; 136 mdoc->lastnamed = mdoc->lastsec = SEC_NONE; 137 mdoc->last = mandoc_calloc(1, sizeof(struct mdoc_node)); 138 mdoc->first = mdoc->last; 139 mdoc->last->type = MDOC_ROOT; 140 mdoc->last->tok = MDOC_MAX; 141 mdoc->next = MDOC_NEXT_CHILD; 142} 143 144/* 145 * Free up volatile resources (see mdoc_free1()) then re-initialises the 146 * data with mdoc_alloc1(). After invocation, parse data has been reset 147 * and the parser is ready for re-invocation on a new tree; however, 148 * cross-parse non-volatile data is kept intact. 149 */ 150void 151mdoc_reset(struct mdoc *mdoc) 152{ 153 154 mdoc_free1(mdoc); 155 mdoc_alloc1(mdoc); 156} 157 158/* 159 * Completely free up all volatile and non-volatile parse resources. 160 * After invocation, the pointer is no longer usable. 161 */ 162void 163mdoc_free(struct mdoc *mdoc) 164{ 165 166 mdoc_free1(mdoc); 167 free(mdoc); 168} 169 170/* 171 * Allocate volatile and non-volatile parse resources. 172 */ 173struct mdoc * 174mdoc_alloc(struct roff *roff, struct mparse *parse, 175 const char *defos, int quick) 176{ 177 struct mdoc *p; 178 179 p = mandoc_calloc(1, sizeof(struct mdoc)); 180 181 p->parse = parse; 182 p->defos = defos; 183 p->quick = quick; 184 p->roff = roff; 185 186 mdoc_hash_init(); 187 mdoc_alloc1(p); 188 return(p); 189} 190 191int 192mdoc_endparse(struct mdoc *mdoc) 193{ 194 195 mdoc_macroend(mdoc); 196 return(1); 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, MDOC_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, MDOC_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 != MDOC_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(MDOC_ROOT != p->type); 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 MDOC_BODY: 309 if (ENDBODY_NOT != p->end) 310 break; 311 /* FALLTHROUGH */ 312 case MDOC_TAIL: 313 /* FALLTHROUGH */ 314 case MDOC_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 MDOC_HEAD: 325 assert(MDOC_BLOCK == p->parent->type); 326 p->parent->head = p; 327 break; 328 case MDOC_TAIL: 329 assert(MDOC_BLOCK == p->parent->type); 330 p->parent->tail = p; 331 break; 332 case MDOC_BODY: 333 if (p->end) 334 break; 335 assert(MDOC_BLOCK == p->parent->type); 336 p->parent->body = p; 337 break; 338 default: 339 break; 340 } 341 342 mdoc->last = p; 343 344 switch (p->type) { 345 case MDOC_TBL: 346 /* FALLTHROUGH */ 347 case MDOC_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 mdoc_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->lastline = line; 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 382void 383mdoc_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 392struct mdoc_node * 393mdoc_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 405struct mdoc_node * 406mdoc_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 416void 417mdoc_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 p = node_alloc(mdoc, line, pos, tok, MDOC_BODY); 423 p->pending = body; 424 p->norm = body->norm; 425 p->end = end; 426 node_append(mdoc, p); 427 mdoc->next = MDOC_NEXT_SIBLING; 428} 429 430struct mdoc_node * 431mdoc_block_alloc(struct mdoc *mdoc, int line, int pos, 432 enum mdoct tok, struct mdoc_arg *args) 433{ 434 struct mdoc_node *p; 435 436 p = node_alloc(mdoc, line, pos, tok, MDOC_BLOCK); 437 p->args = args; 438 if (p->args) 439 (args->refcnt)++; 440 441 switch (tok) { 442 case MDOC_Bd: 443 /* FALLTHROUGH */ 444 case MDOC_Bf: 445 /* FALLTHROUGH */ 446 case MDOC_Bl: 447 /* FALLTHROUGH */ 448 case MDOC_En: 449 /* FALLTHROUGH */ 450 case MDOC_Rs: 451 p->norm = mandoc_calloc(1, sizeof(union mdoc_data)); 452 break; 453 default: 454 break; 455 } 456 node_append(mdoc, p); 457 mdoc->next = MDOC_NEXT_CHILD; 458 return(p); 459} 460 461void 462mdoc_elem_alloc(struct mdoc *mdoc, int line, int pos, 463 enum mdoct tok, struct mdoc_arg *args) 464{ 465 struct mdoc_node *p; 466 467 p = node_alloc(mdoc, line, pos, tok, MDOC_ELEM); 468 p->args = args; 469 if (p->args) 470 (args->refcnt)++; 471 472 switch (tok) { 473 case MDOC_An: 474 p->norm = mandoc_calloc(1, sizeof(union mdoc_data)); 475 break; 476 default: 477 break; 478 } 479 node_append(mdoc, p); 480 mdoc->next = MDOC_NEXT_CHILD; 481} 482 483void 484mdoc_word_alloc(struct mdoc *mdoc, int line, int pos, const char *p) 485{ 486 struct mdoc_node *n; 487 488 n = node_alloc(mdoc, line, pos, MDOC_MAX, MDOC_TEXT); 489 n->string = roff_strdup(mdoc->roff, p); 490 node_append(mdoc, n); 491 mdoc->next = MDOC_NEXT_SIBLING; 492} 493 494void 495mdoc_word_append(struct mdoc *mdoc, const char *p) 496{ 497 struct mdoc_node *n; 498 char *addstr, *newstr; 499 500 n = mdoc->last; 501 addstr = roff_strdup(mdoc->roff, p); 502 mandoc_asprintf(&newstr, "%s %s", n->string, addstr); 503 free(addstr); 504 free(n->string); 505 n->string = newstr; 506 mdoc->next = MDOC_NEXT_SIBLING; 507} 508 509static void 510mdoc_node_free(struct mdoc_node *p) 511{ 512 513 if (MDOC_BLOCK == p->type || MDOC_ELEM == p->type) 514 free(p->norm); 515 if (p->string) 516 free(p->string); 517 if (p->args) 518 mdoc_argv_free(p->args); 519 free(p); 520} 521 522static void 523mdoc_node_unlink(struct mdoc *mdoc, struct mdoc_node *n) 524{ 525 526 /* Adjust siblings. */ 527 528 if (n->prev) 529 n->prev->next = n->next; 530 if (n->next) 531 n->next->prev = n->prev; 532 533 /* Adjust parent. */ 534 535 if (n->parent) { 536 n->parent->nchild--; 537 if (n->parent->child == n) 538 n->parent->child = n->prev ? n->prev : n->next; 539 if (n->parent->last == n) 540 n->parent->last = n->prev ? n->prev : NULL; 541 } 542 543 /* Adjust parse point, if applicable. */ 544 545 if (mdoc && mdoc->last == n) { 546 if (n->prev) { 547 mdoc->last = n->prev; 548 mdoc->next = MDOC_NEXT_SIBLING; 549 } else { 550 mdoc->last = n->parent; 551 mdoc->next = MDOC_NEXT_CHILD; 552 } 553 } 554 555 if (mdoc && mdoc->first == n) 556 mdoc->first = NULL; 557} 558 559void 560mdoc_node_delete(struct mdoc *mdoc, struct mdoc_node *p) 561{ 562 563 while (p->child) { 564 assert(p->nchild); 565 mdoc_node_delete(mdoc, p->child); 566 } 567 assert(0 == p->nchild); 568 569 mdoc_node_unlink(mdoc, p); 570 mdoc_node_free(p); 571} 572 573void 574mdoc_node_relink(struct mdoc *mdoc, struct mdoc_node *p) 575{ 576 577 mdoc_node_unlink(mdoc, p); 578 node_append(mdoc, p); 579} 580 581/* 582 * Parse free-form text, that is, a line that does not begin with the 583 * control character. 584 */ 585static int 586mdoc_ptext(struct mdoc *mdoc, int line, char *buf, int offs) 587{ 588 char *c, *ws, *end; 589 struct mdoc_node *n; 590 591 assert(mdoc->last); 592 n = mdoc->last; 593 594 /* 595 * Divert directly to list processing if we're encountering a 596 * columnar MDOC_BLOCK with or without a prior MDOC_BLOCK entry 597 * (a MDOC_BODY means it's already open, in which case we should 598 * process within its context in the normal way). 599 */ 600 601 if (MDOC_Bl == n->tok && MDOC_BODY == n->type && 602 LIST_column == n->norm->Bl.type) { 603 /* `Bl' is open without any children. */ 604 mdoc->flags |= MDOC_FREECOL; 605 mdoc_macro(mdoc, MDOC_It, line, offs, &offs, buf); 606 return(1); 607 } 608 609 if (MDOC_It == n->tok && MDOC_BLOCK == n->type && 610 NULL != n->parent && 611 MDOC_Bl == n->parent->tok && 612 LIST_column == n->parent->norm->Bl.type) { 613 /* `Bl' has block-level `It' children. */ 614 mdoc->flags |= MDOC_FREECOL; 615 mdoc_macro(mdoc, MDOC_It, line, offs, &offs, buf); 616 return(1); 617 } 618 619 /* 620 * Search for the beginning of unescaped trailing whitespace (ws) 621 * and for the first character not to be output (end). 622 */ 623 624 /* FIXME: replace with strcspn(). */ 625 ws = NULL; 626 for (c = end = buf + offs; *c; c++) { 627 switch (*c) { 628 case ' ': 629 if (NULL == ws) 630 ws = c; 631 continue; 632 case '\t': 633 /* 634 * Always warn about trailing tabs, 635 * even outside literal context, 636 * where they should be put on the next line. 637 */ 638 if (NULL == ws) 639 ws = c; 640 /* 641 * Strip trailing tabs in literal context only; 642 * outside, they affect the next line. 643 */ 644 if (MDOC_LITERAL & mdoc->flags) 645 continue; 646 break; 647 case '\\': 648 /* Skip the escaped character, too, if any. */ 649 if (c[1]) 650 c++; 651 /* FALLTHROUGH */ 652 default: 653 ws = NULL; 654 break; 655 } 656 end = c + 1; 657 } 658 *end = '\0'; 659 660 if (ws) 661 mandoc_msg(MANDOCERR_SPACE_EOL, mdoc->parse, 662 line, (int)(ws-buf), NULL); 663 664 if (buf[offs] == '\0' && ! (mdoc->flags & MDOC_LITERAL)) { 665 mandoc_msg(MANDOCERR_FI_BLANK, mdoc->parse, 666 line, (int)(c - buf), NULL); 667 668 /* 669 * Insert a `sp' in the case of a blank line. Technically, 670 * blank lines aren't allowed, but enough manuals assume this 671 * behaviour that we want to work around it. 672 */ 673 mdoc_elem_alloc(mdoc, line, offs, MDOC_sp, NULL); 674 mdoc->next = MDOC_NEXT_SIBLING; 675 mdoc_valid_post(mdoc); 676 return(1); 677 } 678 679 mdoc_word_alloc(mdoc, line, offs, buf+offs); 680 681 if (mdoc->flags & MDOC_LITERAL) 682 return(1); 683 684 /* 685 * End-of-sentence check. If the last character is an unescaped 686 * EOS character, then flag the node as being the end of a 687 * sentence. The front-end will know how to interpret this. 688 */ 689 690 assert(buf < end); 691 692 if (mandoc_eos(buf+offs, (size_t)(end-buf-offs))) 693 mdoc->last->flags |= MDOC_EOS; 694 return(1); 695} 696 697/* 698 * Parse a macro line, that is, a line beginning with the control 699 * character. 700 */ 701static int 702mdoc_pmacro(struct mdoc *mdoc, int ln, char *buf, int offs) 703{ 704 struct mdoc_node *n; 705 const char *cp; 706 enum mdoct tok; 707 int i, sv; 708 char mac[5]; 709 710 sv = offs; 711 712 /* 713 * Copy the first word into a nil-terminated buffer. 714 * Stop when a space, tab, escape, or eoln is encountered. 715 */ 716 717 i = 0; 718 while (i < 4 && strchr(" \t\\", buf[offs]) == NULL) 719 mac[i++] = buf[offs++]; 720 721 mac[i] = '\0'; 722 723 tok = (i > 1 && i < 4) ? mdoc_hash_find(mac) : MDOC_MAX; 724 725 if (tok == MDOC_MAX) { 726 mandoc_msg(MANDOCERR_MACRO, mdoc->parse, 727 ln, sv, buf + sv - 1); 728 return(1); 729 } 730 731 /* Skip a leading escape sequence or tab. */ 732 733 switch (buf[offs]) { 734 case '\\': 735 cp = buf + offs + 1; 736 mandoc_escape(&cp, NULL, NULL); 737 offs = cp - buf; 738 break; 739 case '\t': 740 offs++; 741 break; 742 default: 743 break; 744 } 745 746 /* Jump to the next non-whitespace word. */ 747 748 while (buf[offs] && ' ' == buf[offs]) 749 offs++; 750 751 /* 752 * Trailing whitespace. Note that tabs are allowed to be passed 753 * into the parser as "text", so we only warn about spaces here. 754 */ 755 756 if ('\0' == buf[offs] && ' ' == buf[offs - 1]) 757 mandoc_msg(MANDOCERR_SPACE_EOL, mdoc->parse, 758 ln, offs - 1, NULL); 759 760 /* 761 * If an initial macro or a list invocation, divert directly 762 * into macro processing. 763 */ 764 765 if (NULL == mdoc->last || MDOC_It == tok || MDOC_El == tok) { 766 mdoc_macro(mdoc, tok, ln, sv, &offs, buf); 767 return(1); 768 } 769 770 n = mdoc->last; 771 assert(mdoc->last); 772 773 /* 774 * If the first macro of a `Bl -column', open an `It' block 775 * context around the parsed macro. 776 */ 777 778 if (MDOC_Bl == n->tok && MDOC_BODY == n->type && 779 LIST_column == n->norm->Bl.type) { 780 mdoc->flags |= MDOC_FREECOL; 781 mdoc_macro(mdoc, MDOC_It, ln, sv, &sv, buf); 782 return(1); 783 } 784 785 /* 786 * If we're following a block-level `It' within a `Bl -column' 787 * context (perhaps opened in the above block or in ptext()), 788 * then open an `It' block context around the parsed macro. 789 */ 790 791 if (MDOC_It == n->tok && MDOC_BLOCK == n->type && 792 NULL != n->parent && 793 MDOC_Bl == n->parent->tok && 794 LIST_column == n->parent->norm->Bl.type) { 795 mdoc->flags |= MDOC_FREECOL; 796 mdoc_macro(mdoc, MDOC_It, ln, sv, &sv, buf); 797 return(1); 798 } 799 800 /* Normal processing of a macro. */ 801 802 mdoc_macro(mdoc, tok, ln, sv, &offs, buf); 803 804 /* In quick mode (for mandocdb), abort after the NAME section. */ 805 806 if (mdoc->quick && MDOC_Sh == tok && 807 SEC_NAME != mdoc->last->sec) 808 return(2); 809 810 return(1); 811} 812 813enum mdelim 814mdoc_isdelim(const char *p) 815{ 816 817 if ('\0' == p[0]) 818 return(DELIM_NONE); 819 820 if ('\0' == p[1]) 821 switch (p[0]) { 822 case '(': 823 /* FALLTHROUGH */ 824 case '[': 825 return(DELIM_OPEN); 826 case '|': 827 return(DELIM_MIDDLE); 828 case '.': 829 /* FALLTHROUGH */ 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 return(DELIM_CLOSE); 844 default: 845 return(DELIM_NONE); 846 } 847 848 if ('\\' != p[0]) 849 return(DELIM_NONE); 850 851 if (0 == strcmp(p + 1, ".")) 852 return(DELIM_CLOSE); 853 if (0 == strcmp(p + 1, "fR|\\fP")) 854 return(DELIM_MIDDLE); 855 856 return(DELIM_NONE); 857} 858 859void 860mdoc_deroff(char **dest, const struct mdoc_node *n) 861{ 862 char *cp; 863 size_t sz; 864 865 if (MDOC_TEXT != n->type) { 866 for (n = n->child; n; n = n->next) 867 mdoc_deroff(dest, n); 868 return; 869 } 870 871 /* Skip leading whitespace. */ 872 873 for (cp = n->string; '\0' != *cp; cp++) 874 if (0 == isspace((unsigned char)*cp)) 875 break; 876 877 /* Skip trailing whitespace. */ 878 879 for (sz = strlen(cp); sz; sz--) 880 if (0 == isspace((unsigned char)cp[sz-1])) 881 break; 882 883 /* Skip empty strings. */ 884 885 if (0 == sz) 886 return; 887 888 if (NULL == *dest) { 889 *dest = mandoc_strndup(cp, sz); 890 return; 891 } 892 893 mandoc_asprintf(&cp, "%s %*s", *dest, (int)sz, cp); 894 free(*dest); 895 *dest = cp; 896} 897