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