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