mdoc.c revision 1.120
1/* $OpenBSD: mdoc.c,v 1.120 2014/11/28 03:13:58 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 return(mdoc_macroend(mdoc)); 196} 197 198int 199mdoc_addeqn(struct mdoc *mdoc, const struct eqn *ep) 200{ 201 struct mdoc_node *n; 202 203 n = node_alloc(mdoc, ep->ln, ep->pos, MDOC_MAX, MDOC_EQN); 204 n->eqn = ep; 205 if (ep->ln > mdoc->last->line) 206 n->flags |= MDOC_LINE; 207 node_append(mdoc, n); 208 mdoc->next = MDOC_NEXT_SIBLING; 209 return(1); 210} 211 212int 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 return(1); 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 */ 228int 229mdoc_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 251int 252mdoc_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(1); 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 275 return((*mdoc_macros[tok].fp)(mdoc, tok, line, ppos, pos, buf)); 276} 277 278 279static void 280node_append(struct mdoc *mdoc, struct mdoc_node *p) 281{ 282 283 assert(mdoc->last); 284 assert(mdoc->first); 285 assert(MDOC_ROOT != p->type); 286 287 switch (mdoc->next) { 288 case MDOC_NEXT_SIBLING: 289 mdoc->last->next = p; 290 p->prev = mdoc->last; 291 p->parent = mdoc->last->parent; 292 break; 293 case MDOC_NEXT_CHILD: 294 mdoc->last->child = p; 295 p->parent = mdoc->last; 296 break; 297 default: 298 abort(); 299 /* NOTREACHED */ 300 } 301 302 p->parent->nchild++; 303 304 /* 305 * Copy over the normalised-data pointer of our parent. Not 306 * everybody has one, but copying a null pointer is fine. 307 */ 308 309 switch (p->type) { 310 case MDOC_BODY: 311 if (ENDBODY_NOT != p->end) 312 break; 313 /* FALLTHROUGH */ 314 case MDOC_TAIL: 315 /* FALLTHROUGH */ 316 case MDOC_HEAD: 317 p->norm = p->parent->norm; 318 break; 319 default: 320 break; 321 } 322 323 mdoc_valid_pre(mdoc, p); 324 325 switch (p->type) { 326 case MDOC_HEAD: 327 assert(MDOC_BLOCK == p->parent->type); 328 p->parent->head = p; 329 break; 330 case MDOC_TAIL: 331 assert(MDOC_BLOCK == p->parent->type); 332 p->parent->tail = p; 333 break; 334 case MDOC_BODY: 335 if (p->end) 336 break; 337 assert(MDOC_BLOCK == p->parent->type); 338 p->parent->body = p; 339 break; 340 default: 341 break; 342 } 343 344 mdoc->last = p; 345 346 switch (p->type) { 347 case MDOC_TBL: 348 /* FALLTHROUGH */ 349 case MDOC_TEXT: 350 mdoc_valid_post(mdoc); 351 break; 352 default: 353 break; 354 } 355} 356 357static struct mdoc_node * 358node_alloc(struct mdoc *mdoc, int line, int pos, 359 enum mdoct tok, enum mdoc_type type) 360{ 361 struct mdoc_node *p; 362 363 p = mandoc_calloc(1, sizeof(struct mdoc_node)); 364 p->sec = mdoc->lastsec; 365 p->line = line; 366 p->pos = pos; 367 p->lastline = line; 368 p->tok = tok; 369 p->type = type; 370 371 /* Flag analysis. */ 372 373 if (MDOC_SYNOPSIS & mdoc->flags) 374 p->flags |= MDOC_SYNPRETTY; 375 else 376 p->flags &= ~MDOC_SYNPRETTY; 377 if (MDOC_NEWLINE & mdoc->flags) 378 p->flags |= MDOC_LINE; 379 mdoc->flags &= ~MDOC_NEWLINE; 380 381 return(p); 382} 383 384void 385mdoc_tail_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok) 386{ 387 struct mdoc_node *p; 388 389 p = node_alloc(mdoc, line, pos, tok, MDOC_TAIL); 390 node_append(mdoc, p); 391 mdoc->next = MDOC_NEXT_CHILD; 392} 393 394struct mdoc_node * 395mdoc_head_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok) 396{ 397 struct mdoc_node *p; 398 399 assert(mdoc->first); 400 assert(mdoc->last); 401 p = node_alloc(mdoc, line, pos, tok, MDOC_HEAD); 402 node_append(mdoc, p); 403 mdoc->next = MDOC_NEXT_CHILD; 404 return(p); 405} 406 407struct mdoc_node * 408mdoc_body_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok) 409{ 410 struct mdoc_node *p; 411 412 p = node_alloc(mdoc, line, pos, tok, MDOC_BODY); 413 node_append(mdoc, p); 414 mdoc->next = MDOC_NEXT_CHILD; 415 return(p); 416} 417 418void 419mdoc_endbody_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok, 420 struct mdoc_node *body, enum mdoc_endbody end) 421{ 422 struct mdoc_node *p; 423 424 p = node_alloc(mdoc, line, pos, tok, MDOC_BODY); 425 p->pending = body; 426 p->norm = body->norm; 427 p->end = end; 428 node_append(mdoc, p); 429 mdoc->next = MDOC_NEXT_SIBLING; 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, MDOC_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, MDOC_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, MDOC_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 (MDOC_BLOCK == p->type || MDOC_ELEM == p->type) 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 MDOC_BLOCK with or without a prior MDOC_BLOCK entry 599 * (a MDOC_BODY means it's already open, in which case we should 600 * process within its context in the normal way). 601 */ 602 603 if (MDOC_Bl == n->tok && MDOC_BODY == n->type && 604 LIST_column == n->norm->Bl.type) { 605 /* `Bl' is open without any children. */ 606 mdoc->flags |= MDOC_FREECOL; 607 return(mdoc_macro(mdoc, MDOC_It, line, offs, &offs, buf)); 608 } 609 610 if (MDOC_It == n->tok && MDOC_BLOCK == n->type && 611 NULL != n->parent && 612 MDOC_Bl == n->parent->tok && 613 LIST_column == n->parent->norm->Bl.type) { 614 /* `Bl' has block-level `It' children. */ 615 mdoc->flags |= MDOC_FREECOL; 616 return(mdoc_macro(mdoc, MDOC_It, line, offs, &offs, buf)); 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 695 return(1); 696} 697 698/* 699 * Parse a macro line, that is, a line beginning with the control 700 * character. 701 */ 702static int 703mdoc_pmacro(struct mdoc *mdoc, int ln, char *buf, int offs) 704{ 705 struct mdoc_node *n; 706 const char *cp; 707 enum mdoct tok; 708 int i, sv; 709 char mac[5]; 710 711 sv = offs; 712 713 /* 714 * Copy the first word into a nil-terminated buffer. 715 * Stop when a space, tab, escape, or eoln is encountered. 716 */ 717 718 i = 0; 719 while (i < 4 && strchr(" \t\\", buf[offs]) == NULL) 720 mac[i++] = buf[offs++]; 721 722 mac[i] = '\0'; 723 724 tok = (i > 1 && i < 4) ? mdoc_hash_find(mac) : MDOC_MAX; 725 726 if (tok == MDOC_MAX) { 727 mandoc_msg(MANDOCERR_MACRO, mdoc->parse, 728 ln, sv, buf + sv - 1); 729 return(1); 730 } 731 732 /* Skip a leading escape sequence or tab. */ 733 734 switch (buf[offs]) { 735 case '\\': 736 cp = buf + offs + 1; 737 mandoc_escape(&cp, NULL, NULL); 738 offs = cp - buf; 739 break; 740 case '\t': 741 offs++; 742 break; 743 default: 744 break; 745 } 746 747 /* Jump to the next non-whitespace word. */ 748 749 while (buf[offs] && ' ' == buf[offs]) 750 offs++; 751 752 /* 753 * Trailing whitespace. Note that tabs are allowed to be passed 754 * into the parser as "text", so we only warn about spaces here. 755 */ 756 757 if ('\0' == buf[offs] && ' ' == buf[offs - 1]) 758 mandoc_msg(MANDOCERR_SPACE_EOL, mdoc->parse, 759 ln, offs - 1, NULL); 760 761 /* 762 * If an initial macro or a list invocation, divert directly 763 * into macro processing. 764 */ 765 766 if (NULL == mdoc->last || MDOC_It == tok || MDOC_El == tok) 767 return(mdoc_macro(mdoc, tok, ln, sv, &offs, buf)); 768 769 n = mdoc->last; 770 assert(mdoc->last); 771 772 /* 773 * If the first macro of a `Bl -column', open an `It' block 774 * context around the parsed macro. 775 */ 776 777 if (MDOC_Bl == n->tok && MDOC_BODY == n->type && 778 LIST_column == n->norm->Bl.type) { 779 mdoc->flags |= MDOC_FREECOL; 780 return(mdoc_macro(mdoc, MDOC_It, ln, sv, &sv, buf)); 781 } 782 783 /* 784 * If we're following a block-level `It' within a `Bl -column' 785 * context (perhaps opened in the above block or in ptext()), 786 * then open an `It' block context around the parsed macro. 787 */ 788 789 if (MDOC_It == n->tok && MDOC_BLOCK == n->type && 790 NULL != n->parent && 791 MDOC_Bl == n->parent->tok && 792 LIST_column == n->parent->norm->Bl.type) { 793 mdoc->flags |= MDOC_FREECOL; 794 return(mdoc_macro(mdoc, MDOC_It, ln, sv, &sv, buf)); 795 } 796 797 /* Normal processing of a macro. */ 798 799 if ( ! mdoc_macro(mdoc, tok, ln, sv, &offs, buf)) 800 return(0); 801 802 /* In quick mode (for mandocdb), abort after the NAME section. */ 803 804 if (mdoc->quick && MDOC_Sh == tok && 805 SEC_NAME != mdoc->last->sec) 806 return(2); 807 808 return(1); 809} 810 811enum mdelim 812mdoc_isdelim(const char *p) 813{ 814 815 if ('\0' == p[0]) 816 return(DELIM_NONE); 817 818 if ('\0' == p[1]) 819 switch (p[0]) { 820 case '(': 821 /* FALLTHROUGH */ 822 case '[': 823 return(DELIM_OPEN); 824 case '|': 825 return(DELIM_MIDDLE); 826 case '.': 827 /* FALLTHROUGH */ 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 return(DELIM_CLOSE); 842 default: 843 return(DELIM_NONE); 844 } 845 846 if ('\\' != p[0]) 847 return(DELIM_NONE); 848 849 if (0 == strcmp(p + 1, ".")) 850 return(DELIM_CLOSE); 851 if (0 == strcmp(p + 1, "fR|\\fP")) 852 return(DELIM_MIDDLE); 853 854 return(DELIM_NONE); 855} 856 857void 858mdoc_deroff(char **dest, const struct mdoc_node *n) 859{ 860 char *cp; 861 size_t sz; 862 863 if (MDOC_TEXT != n->type) { 864 for (n = n->child; n; n = n->next) 865 mdoc_deroff(dest, n); 866 return; 867 } 868 869 /* Skip leading whitespace. */ 870 871 for (cp = n->string; '\0' != *cp; cp++) 872 if (0 == isspace((unsigned char)*cp)) 873 break; 874 875 /* Skip trailing whitespace. */ 876 877 for (sz = strlen(cp); sz; sz--) 878 if (0 == isspace((unsigned char)cp[sz-1])) 879 break; 880 881 /* Skip empty strings. */ 882 883 if (0 == sz) 884 return; 885 886 if (NULL == *dest) { 887 *dest = mandoc_strndup(cp, sz); 888 return; 889 } 890 891 mandoc_asprintf(&cp, "%s %*s", *dest, (int)sz, cp); 892 free(*dest); 893 *dest = cp; 894} 895