mdoc.c revision 1.40
1/* $Id: mdoc.c,v 1.40 2010/04/07 23:15:05 schwarze Exp $ */ 2/* 3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@kth.se> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17#include <sys/types.h> 18 19#include <assert.h> 20#include <ctype.h> 21#include <stdarg.h> 22#include <stdio.h> 23#include <stdlib.h> 24#include <string.h> 25#include <time.h> 26 27#include "libmdoc.h" 28#include "libmandoc.h" 29 30const char *const __mdoc_merrnames[MERRMAX] = { 31 "trailing whitespace", /* ETAILWS */ 32 "unexpected quoted parameter", /* EQUOTPARM */ 33 "unterminated quoted parameter", /* EQUOTTERM */ 34 "argument parameter suggested", /* EARGVAL */ 35 "macro disallowed in prologue", /* EBODYPROL */ 36 "macro disallowed in body", /* EPROLBODY */ 37 "text disallowed in prologue", /* ETEXTPROL */ 38 "blank line disallowed", /* ENOBLANK */ 39 "text parameter too long", /* ETOOLONG */ 40 "invalid escape sequence", /* EESCAPE */ 41 "invalid character", /* EPRINT */ 42 "document has no body", /* ENODAT */ 43 "document has no prologue", /* ENOPROLOGUE */ 44 "expected line arguments", /* ELINE */ 45 "invalid AT&T argument", /* EATT */ 46 "default name not yet set", /* ENAME */ 47 "missing list type", /* ELISTTYPE */ 48 "missing display type", /* EDISPTYPE */ 49 "too many display types", /* EMULTIDISP */ 50 "too many list types", /* EMULTILIST */ 51 "NAME section must be first", /* ESECNAME */ 52 "badly-formed NAME section", /* ENAMESECINC */ 53 "argument repeated", /* EARGREP */ 54 "expected boolean parameter", /* EBOOL */ 55 "inconsistent column syntax", /* ECOLMIS */ 56 "nested display invalid", /* ENESTDISP */ 57 "width argument missing", /* EMISSWIDTH */ 58 "invalid section for this manual section", /* EWRONGMSEC */ 59 "section out of conventional order", /* ESECOOO */ 60 "section repeated", /* ESECREP */ 61 "invalid standard argument", /* EBADSTAND */ 62 "multi-line arguments discouraged", /* ENOMULTILINE */ 63 "multi-line arguments suggested", /* EMULTILINE */ 64 "line arguments discouraged", /* ENOLINE */ 65 "prologue macro out of conventional order", /* EPROLOOO */ 66 "prologue macro repeated", /* EPROLREP */ 67 "invalid manual section", /* EBADMSEC */ 68 "invalid section", /* EBADSEC */ 69 "invalid font mode", /* EFONT */ 70 "invalid date syntax", /* EBADDATE */ 71 "invalid number format", /* ENUMFMT */ 72 "superfluous width argument", /* ENOWIDTH */ 73 "system: utsname error", /* EUTSNAME */ 74 "obsolete macro", /* EOBS */ 75 "end-of-line scope violation", /* EIMPBRK */ 76 "empty macro ignored", /* EIGNE */ 77 "unclosed explicit scope", /* EOPEN */ 78 "unterminated quoted phrase", /* EQUOTPHR */ 79 "closure macro without prior context", /* ENOCTX */ 80 "no description found for library", /* ELIB */ 81 "bad child for parent context", /* EBADCHILD */ 82 "list arguments preceding type", /* ENOTYPE */ 83}; 84 85const char *const __mdoc_macronames[MDOC_MAX] = { 86 "Ap", "Dd", "Dt", "Os", 87 "Sh", "Ss", "Pp", "D1", 88 "Dl", "Bd", "Ed", "Bl", 89 "El", "It", "Ad", "An", 90 "Ar", "Cd", "Cm", "Dv", 91 "Er", "Ev", "Ex", "Fa", 92 "Fd", "Fl", "Fn", "Ft", 93 "Ic", "In", "Li", "Nd", 94 "Nm", "Op", "Ot", "Pa", 95 "Rv", "St", "Va", "Vt", 96 /* LINTED */ 97 "Xr", "%A", "%B", "%D", 98 /* LINTED */ 99 "%I", "%J", "%N", "%O", 100 /* LINTED */ 101 "%P", "%R", "%T", "%V", 102 "Ac", "Ao", "Aq", "At", 103 "Bc", "Bf", "Bo", "Bq", 104 "Bsx", "Bx", "Db", "Dc", 105 "Do", "Dq", "Ec", "Ef", 106 "Em", "Eo", "Fx", "Ms", 107 "No", "Ns", "Nx", "Ox", 108 "Pc", "Pf", "Po", "Pq", 109 "Qc", "Ql", "Qo", "Qq", 110 "Re", "Rs", "Sc", "So", 111 "Sq", "Sm", "Sx", "Sy", 112 "Tn", "Ux", "Xc", "Xo", 113 "Fo", "Fc", "Oo", "Oc", 114 "Bk", "Ek", "Bt", "Hf", 115 "Fr", "Ud", "Lb", "Lp", 116 "Lk", "Mt", "Brq", "Bro", 117 /* LINTED */ 118 "Brc", "%C", "Es", "En", 119 /* LINTED */ 120 "Dx", "%Q", "br", "sp", 121 /* LINTED */ 122 "%U", "eos" 123 }; 124 125const char *const __mdoc_argnames[MDOC_ARG_MAX] = { 126 "split", "nosplit", "ragged", 127 "unfilled", "literal", "file", 128 "offset", "bullet", "dash", 129 "hyphen", "item", "enum", 130 "tag", "diag", "hang", 131 "ohang", "inset", "column", 132 "width", "compact", "std", 133 "filled", "words", "emphasis", 134 "symbolic", "nested", "centered" 135 }; 136 137const char * const *mdoc_macronames = __mdoc_macronames; 138const char * const *mdoc_argnames = __mdoc_argnames; 139 140static void mdoc_node_free(struct mdoc_node *); 141static void mdoc_node_unlink(struct mdoc *, 142 struct mdoc_node *); 143static void mdoc_free1(struct mdoc *); 144static void mdoc_alloc1(struct mdoc *); 145static struct mdoc_node *node_alloc(struct mdoc *, int, int, 146 enum mdoct, enum mdoc_type); 147static int node_append(struct mdoc *, 148 struct mdoc_node *); 149static int parsetext(struct mdoc *, int, char *); 150static int parsemacro(struct mdoc *, int, char *); 151static int macrowarn(struct mdoc *, int, const char *); 152static int pstring(struct mdoc *, int, int, 153 const char *, size_t); 154 155const struct mdoc_node * 156mdoc_node(const struct mdoc *m) 157{ 158 159 return(MDOC_HALT & m->flags ? NULL : m->first); 160} 161 162 163const struct mdoc_meta * 164mdoc_meta(const struct mdoc *m) 165{ 166 167 return(MDOC_HALT & m->flags ? NULL : &m->meta); 168} 169 170 171/* 172 * Frees volatile resources (parse tree, meta-data, fields). 173 */ 174static void 175mdoc_free1(struct mdoc *mdoc) 176{ 177 178 if (mdoc->first) 179 mdoc_node_delete(mdoc, mdoc->first); 180 if (mdoc->meta.title) 181 free(mdoc->meta.title); 182 if (mdoc->meta.os) 183 free(mdoc->meta.os); 184 if (mdoc->meta.name) 185 free(mdoc->meta.name); 186 if (mdoc->meta.arch) 187 free(mdoc->meta.arch); 188 if (mdoc->meta.vol) 189 free(mdoc->meta.vol); 190} 191 192 193/* 194 * Allocate all volatile resources (parse tree, meta-data, fields). 195 */ 196static void 197mdoc_alloc1(struct mdoc *mdoc) 198{ 199 200 memset(&mdoc->meta, 0, sizeof(struct mdoc_meta)); 201 mdoc->flags = 0; 202 mdoc->lastnamed = mdoc->lastsec = SEC_NONE; 203 mdoc->last = mandoc_calloc(1, sizeof(struct mdoc_node)); 204 mdoc->first = mdoc->last; 205 mdoc->last->type = MDOC_ROOT; 206 mdoc->next = MDOC_NEXT_CHILD; 207} 208 209 210/* 211 * Free up volatile resources (see mdoc_free1()) then re-initialises the 212 * data with mdoc_alloc1(). After invocation, parse data has been reset 213 * and the parser is ready for re-invocation on a new tree; however, 214 * cross-parse non-volatile data is kept intact. 215 */ 216void 217mdoc_reset(struct mdoc *mdoc) 218{ 219 220 mdoc_free1(mdoc); 221 mdoc_alloc1(mdoc); 222} 223 224 225/* 226 * Completely free up all volatile and non-volatile parse resources. 227 * After invocation, the pointer is no longer usable. 228 */ 229void 230mdoc_free(struct mdoc *mdoc) 231{ 232 233 mdoc_free1(mdoc); 234 free(mdoc); 235} 236 237 238/* 239 * Allocate volatile and non-volatile parse resources. 240 */ 241struct mdoc * 242mdoc_alloc(void *data, int pflags, const struct mdoc_cb *cb) 243{ 244 struct mdoc *p; 245 246 p = mandoc_calloc(1, sizeof(struct mdoc)); 247 248 if (cb) 249 memcpy(&p->cb, cb, sizeof(struct mdoc_cb)); 250 251 p->data = data; 252 p->pflags = pflags; 253 254 mdoc_hash_init(); 255 mdoc_alloc1(p); 256 return(p); 257} 258 259 260/* 261 * Climb back up the parse tree, validating open scopes. Mostly calls 262 * through to macro_end() in macro.c. 263 */ 264int 265mdoc_endparse(struct mdoc *m) 266{ 267 268 if (MDOC_HALT & m->flags) 269 return(0); 270 else if (mdoc_macroend(m)) 271 return(1); 272 m->flags |= MDOC_HALT; 273 return(0); 274} 275 276 277/* 278 * Main parse routine. Parses a single line -- really just hands off to 279 * the macro (parsemacro()) or text parser (parsetext()). 280 */ 281int 282mdoc_parseln(struct mdoc *m, int ln, char *buf) 283{ 284 285 if (MDOC_HALT & m->flags) 286 return(0); 287 288 return('.' == *buf ? parsemacro(m, ln, buf) : 289 parsetext(m, ln, buf)); 290} 291 292 293int 294mdoc_verr(struct mdoc *mdoc, int ln, int pos, 295 const char *fmt, ...) 296{ 297 char buf[256]; 298 va_list ap; 299 300 if (NULL == mdoc->cb.mdoc_err) 301 return(0); 302 303 va_start(ap, fmt); 304 (void)vsnprintf(buf, sizeof(buf) - 1, fmt, ap); 305 va_end(ap); 306 307 return((*mdoc->cb.mdoc_err)(mdoc->data, ln, pos, buf)); 308} 309 310 311int 312mdoc_vwarn(struct mdoc *mdoc, int ln, int pos, const char *fmt, ...) 313{ 314 char buf[256]; 315 va_list ap; 316 317 if (NULL == mdoc->cb.mdoc_warn) 318 return(0); 319 320 va_start(ap, fmt); 321 (void)vsnprintf(buf, sizeof(buf) - 1, fmt, ap); 322 va_end(ap); 323 324 return((*mdoc->cb.mdoc_warn)(mdoc->data, ln, pos, buf)); 325} 326 327 328int 329mdoc_err(struct mdoc *m, int line, int pos, int iserr, enum merr type) 330{ 331 const char *p; 332 333 p = __mdoc_merrnames[(int)type]; 334 assert(p); 335 336 if (iserr) 337 return(mdoc_verr(m, line, pos, p)); 338 339 return(mdoc_vwarn(m, line, pos, p)); 340} 341 342 343int 344mdoc_macro(struct mdoc *m, enum mdoct tok, 345 int ln, int pp, int *pos, char *buf) 346{ 347 348 assert(tok < MDOC_MAX); 349 /* 350 * If we're in the prologue, deny "body" macros. Similarly, if 351 * we're in the body, deny prologue calls. 352 */ 353 if (MDOC_PROLOGUE & mdoc_macros[tok].flags && 354 MDOC_PBODY & m->flags) 355 return(mdoc_perr(m, ln, pp, EPROLBODY)); 356 if ( ! (MDOC_PROLOGUE & mdoc_macros[tok].flags) && 357 ! (MDOC_PBODY & m->flags)) { 358 if ( ! mdoc_pwarn(m, ln, pp, EBODYPROL)) 359 return(0); 360 if (NULL == m->meta.title) 361 m->meta.title = mandoc_strdup("unknown"); 362 if (NULL == m->meta.vol) 363 m->meta.vol = mandoc_strdup("local"); 364 if (NULL == m->meta.os) 365 m->meta.os = mandoc_strdup("local"); 366 if (0 == m->meta.date) 367 m->meta.date = time(NULL); 368 m->flags |= MDOC_PBODY; 369 } 370 371 return((*mdoc_macros[tok].fp)(m, tok, ln, pp, pos, buf)); 372} 373 374 375static int 376node_append(struct mdoc *mdoc, struct mdoc_node *p) 377{ 378 379 assert(mdoc->last); 380 assert(mdoc->first); 381 assert(MDOC_ROOT != p->type); 382 383 switch (mdoc->next) { 384 case (MDOC_NEXT_SIBLING): 385 mdoc->last->next = p; 386 p->prev = mdoc->last; 387 p->parent = mdoc->last->parent; 388 break; 389 case (MDOC_NEXT_CHILD): 390 mdoc->last->child = p; 391 p->parent = mdoc->last; 392 break; 393 default: 394 abort(); 395 /* NOTREACHED */ 396 } 397 398 p->parent->nchild++; 399 400 if ( ! mdoc_valid_pre(mdoc, p)) 401 return(0); 402 if ( ! mdoc_action_pre(mdoc, p)) 403 return(0); 404 405 switch (p->type) { 406 case (MDOC_HEAD): 407 assert(MDOC_BLOCK == p->parent->type); 408 p->parent->head = p; 409 break; 410 case (MDOC_TAIL): 411 assert(MDOC_BLOCK == p->parent->type); 412 p->parent->tail = p; 413 break; 414 case (MDOC_BODY): 415 assert(MDOC_BLOCK == p->parent->type); 416 p->parent->body = p; 417 break; 418 default: 419 break; 420 } 421 422 mdoc->last = p; 423 424 switch (p->type) { 425 case (MDOC_TEXT): 426 if ( ! mdoc_valid_post(mdoc)) 427 return(0); 428 if ( ! mdoc_action_post(mdoc)) 429 return(0); 430 break; 431 default: 432 break; 433 } 434 435 return(1); 436} 437 438 439static struct mdoc_node * 440node_alloc(struct mdoc *m, int line, int pos, 441 enum mdoct tok, enum mdoc_type type) 442{ 443 struct mdoc_node *p; 444 445 p = mandoc_calloc(1, sizeof(struct mdoc_node)); 446 p->sec = m->lastsec; 447 p->line = line; 448 p->pos = pos; 449 p->tok = tok; 450 p->type = type; 451 452 return(p); 453} 454 455 456int 457mdoc_tail_alloc(struct mdoc *m, int line, int pos, enum mdoct tok) 458{ 459 struct mdoc_node *p; 460 461 p = node_alloc(m, line, pos, tok, MDOC_TAIL); 462 if ( ! node_append(m, p)) 463 return(0); 464 m->next = MDOC_NEXT_CHILD; 465 return(1); 466} 467 468 469int 470mdoc_head_alloc(struct mdoc *m, int line, int pos, enum mdoct tok) 471{ 472 struct mdoc_node *p; 473 474 assert(m->first); 475 assert(m->last); 476 477 p = node_alloc(m, line, pos, tok, MDOC_HEAD); 478 if ( ! node_append(m, p)) 479 return(0); 480 m->next = MDOC_NEXT_CHILD; 481 return(1); 482} 483 484 485int 486mdoc_body_alloc(struct mdoc *m, int line, int pos, enum mdoct tok) 487{ 488 struct mdoc_node *p; 489 490 p = node_alloc(m, line, pos, tok, MDOC_BODY); 491 if ( ! node_append(m, p)) 492 return(0); 493 m->next = MDOC_NEXT_CHILD; 494 return(1); 495} 496 497 498int 499mdoc_block_alloc(struct mdoc *m, int line, int pos, 500 enum mdoct tok, struct mdoc_arg *args) 501{ 502 struct mdoc_node *p; 503 504 p = node_alloc(m, line, pos, tok, MDOC_BLOCK); 505 p->args = args; 506 if (p->args) 507 (args->refcnt)++; 508 if ( ! node_append(m, p)) 509 return(0); 510 m->next = MDOC_NEXT_CHILD; 511 return(1); 512} 513 514 515int 516mdoc_elem_alloc(struct mdoc *m, int line, int pos, 517 enum mdoct tok, struct mdoc_arg *args) 518{ 519 struct mdoc_node *p; 520 521 p = node_alloc(m, line, pos, tok, MDOC_ELEM); 522 p->args = args; 523 if (p->args) 524 (args->refcnt)++; 525 if ( ! node_append(m, p)) 526 return(0); 527 m->next = MDOC_NEXT_CHILD; 528 return(1); 529} 530 531 532static int 533pstring(struct mdoc *m, int line, int pos, const char *p, size_t len) 534{ 535 struct mdoc_node *n; 536 size_t sv; 537 538 n = node_alloc(m, line, pos, -1, MDOC_TEXT); 539 n->string = mandoc_malloc(len + 1); 540 sv = strlcpy(n->string, p, len + 1); 541 542 /* Prohibit truncation. */ 543 assert(sv < len + 1); 544 545 if ( ! node_append(m, n)) 546 return(0); 547 m->next = MDOC_NEXT_SIBLING; 548 return(1); 549} 550 551 552int 553mdoc_word_alloc(struct mdoc *m, int line, int pos, const char *p) 554{ 555 556 return(pstring(m, line, pos, p, strlen(p))); 557} 558 559 560void 561mdoc_node_free(struct mdoc_node *p) 562{ 563 564 if (p->string) 565 free(p->string); 566 if (p->args) 567 mdoc_argv_free(p->args); 568 free(p); 569} 570 571 572static void 573mdoc_node_unlink(struct mdoc *m, struct mdoc_node *n) 574{ 575 576 /* Adjust siblings. */ 577 578 if (n->prev) 579 n->prev->next = n->next; 580 if (n->next) 581 n->next->prev = n->prev; 582 583 /* Adjust parent. */ 584 585 if (n->parent) { 586 n->parent->nchild--; 587 if (n->parent->child == n) 588 n->parent->child = n->prev ? n->prev : n->next; 589 } 590 591 /* Adjust parse point, if applicable. */ 592 593 if (m && m->last == n) { 594 if (n->prev) { 595 m->last = n->prev; 596 m->next = MDOC_NEXT_SIBLING; 597 } else { 598 m->last = n->parent; 599 m->next = MDOC_NEXT_CHILD; 600 } 601 } 602 603 if (m && m->first == n) 604 m->first = NULL; 605} 606 607 608void 609mdoc_node_delete(struct mdoc *m, struct mdoc_node *p) 610{ 611 612 while (p->child) { 613 assert(p->nchild); 614 mdoc_node_delete(m, p->child); 615 } 616 assert(0 == p->nchild); 617 618 mdoc_node_unlink(m, p); 619 mdoc_node_free(p); 620} 621 622 623/* 624 * Parse free-form text, that is, a line that does not begin with the 625 * control character. 626 */ 627static int 628parsetext(struct mdoc *m, int line, char *buf) 629{ 630 int i, j; 631 char sv; 632 633 if (SEC_NONE == m->lastnamed) 634 return(mdoc_perr(m, line, 0, ETEXTPROL)); 635 636 /* 637 * If in literal mode, then pass the buffer directly to the 638 * back-end, as it should be preserved as a single term. 639 */ 640 641 if (MDOC_LITERAL & m->flags) 642 return(mdoc_word_alloc(m, line, 0, buf)); 643 644 /* Disallow blank/white-space lines in non-literal mode. */ 645 646 for (i = 0; ' ' == buf[i]; i++) 647 /* Skip leading whitespace. */ ; 648 649 if ('\0' == buf[i]) { 650 if ( ! mdoc_pwarn(m, line, 0, ENOBLANK)) 651 return(0); 652 /* 653 * Assume that a `Pp' should be inserted in the case of 654 * a blank line. Technically, blank lines aren't 655 * allowed, but enough manuals assume this behaviour 656 * that we want to work around it. 657 */ 658 if ( ! mdoc_elem_alloc(m, line, 0, MDOC_Pp, NULL)) 659 return(0); 660 } 661 662 /* 663 * Break apart a free-form line into tokens. Spaces are 664 * stripped out of the input. 665 */ 666 667 for (j = i; buf[i]; i++) { 668 if (' ' != buf[i]) 669 continue; 670 671 /* Escaped whitespace. */ 672 if (i && ' ' == buf[i] && '\\' == buf[i - 1]) 673 continue; 674 675 sv = buf[i]; 676 buf[i++] = '\0'; 677 678 if ( ! pstring(m, line, j, &buf[j], (size_t)(i - j))) 679 return(0); 680 681 /* Trailing whitespace? Check at overwritten byte. */ 682 683 if (' ' == sv && '\0' == buf[i]) 684 if ( ! mdoc_pwarn(m, line, i - 1, ETAILWS)) 685 return(0); 686 687 for ( ; ' ' == buf[i]; i++) 688 /* Skip trailing whitespace. */ ; 689 690 j = i; 691 692 /* Trailing whitespace? */ 693 694 if (' ' == buf[i - 1] && '\0' == buf[i]) 695 if ( ! mdoc_pwarn(m, line, i - 1, ETAILWS)) 696 return(0); 697 698 if ('\0' == buf[i]) 699 break; 700 } 701 702 if (j != i && ! pstring(m, line, j, &buf[j], (size_t)(i - j))) 703 return(0); 704 705 /* 706 * Mark the end of a sentence. Only works when you respect 707 * Jason's rule: "new sentence, new line". 708 */ 709 if ('.' == buf[i-1] || '!' == buf[i-1] || '?' == buf[i-1]) { 710 m->next = MDOC_NEXT_SIBLING; 711 if ( ! mdoc_elem_alloc(m, line, i, MDOC_eos, NULL)) 712 return(0); 713 } 714 715 m->next = MDOC_NEXT_SIBLING; 716 return(1); 717} 718 719 720 721static int 722macrowarn(struct mdoc *m, int ln, const char *buf) 723{ 724 if ( ! (MDOC_IGN_MACRO & m->pflags)) 725 return(mdoc_verr(m, ln, 0, 726 "unknown macro: %s%s", 727 buf, strlen(buf) > 3 ? "..." : "")); 728 return(mdoc_vwarn(m, ln, 0, "unknown macro: %s%s", 729 buf, strlen(buf) > 3 ? "..." : "")); 730} 731 732 733/* 734 * Parse a macro line, that is, a line beginning with the control 735 * character. 736 */ 737int 738parsemacro(struct mdoc *m, int ln, char *buf) 739{ 740 int i, j, c; 741 char mac[5]; 742 struct mdoc_node *n; 743 char *t; 744 745 /* Empty lines are ignored. */ 746 747 if ('\0' == buf[1]) 748 return(1); 749 750 i = 1; 751 752 /* Accept whitespace after the initial control char. */ 753 754 if (' ' == buf[i]) { 755 i++; 756 while (buf[i] && ' ' == buf[i]) 757 i++; 758 if ('\0' == buf[i]) 759 return(1); 760 } 761 762 /* Copy the first word into a nil-terminated buffer. */ 763 764 for (j = 0; j < 4; j++, i++) { 765 if ('\0' == (mac[j] = buf[i])) 766 break; 767 else if (' ' == buf[i]) 768 break; 769 770 /* Check for invalid characters. */ 771 772 if (isgraph((u_char)buf[i])) 773 continue; 774 return(mdoc_perr(m, ln, i, EPRINT)); 775 } 776 777 mac[j] = 0; 778 779 if (j == 4 || j < 2) { 780 if ( ! macrowarn(m, ln, mac)) 781 goto err; 782 return(1); 783 } 784 785 if (MDOC_MAX == (c = mdoc_hash_find(mac))) { 786 if ( ! macrowarn(m, ln, mac)) 787 goto err; 788 return(1); 789 } 790 791 /* The macro is sane. Jump to the next word. */ 792 793 while (buf[i] && ' ' == buf[i]) 794 i++; 795 796 /* Trailing whitespace? */ 797 798 if ('\0' == buf[i] && ' ' == buf[i - 1]) 799 if ( ! mdoc_pwarn(m, ln, i - 1, ETAILWS)) 800 goto err; 801 802 /* 803 * Begin recursive parse sequence. Since we're at the start of 804 * the line, we don't need to do callable/parseable checks. 805 */ 806 if ( ! mdoc_macro(m, c, ln, 1, &i, buf)) 807 goto err; 808 809 /* 810 * Mark the end of a sentence, but be careful not to insert 811 * markers into reference blocks. 812 */ 813 n = m->last; 814 if (n->child) 815 n = n->child; 816 while (n->next) 817 n = n->next; 818 if (MDOC_TEXT == n->type && m->last->parent->tok != MDOC_Rs) { 819 t = n->string; 820 while (t[0] && t[1]) 821 t++; 822 if ('.' == *t || '!' == *t || '?' == *t) { 823 if ( ! mdoc_elem_alloc(m, ln, i, MDOC_eos, NULL)) 824 return(0); 825 m->next = MDOC_NEXT_SIBLING; 826 } 827 } 828 829 return(1); 830 831err: /* Error out. */ 832 833 m->flags |= MDOC_HALT; 834 return(0); 835} 836 837 838