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