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