mdoc.c revision 1.1.1.8
1/* $Vendor-Id: mdoc.c,v 1.145 2010/05/31 21:32:57 kristaps Exp $ */ 2/* 3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv> 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#ifdef HAVE_CONFIG_H 18#include "config.h" 19#endif 20 21#include <sys/types.h> 22 23#include <assert.h> 24#include <ctype.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 macrowarn(struct mdoc *, int, 102 const char *, int); 103 104 105const struct mdoc_node * 106mdoc_node(const struct mdoc *m) 107{ 108 109 return(MDOC_HALT & m->flags ? NULL : m->first); 110} 111 112 113const struct mdoc_meta * 114mdoc_meta(const struct mdoc *m) 115{ 116 117 return(MDOC_HALT & m->flags ? NULL : &m->meta); 118} 119 120 121/* 122 * Frees volatile resources (parse tree, meta-data, fields). 123 */ 124static void 125mdoc_free1(struct mdoc *mdoc) 126{ 127 128 if (mdoc->first) 129 mdoc_node_delete(mdoc, mdoc->first); 130 if (mdoc->meta.title) 131 free(mdoc->meta.title); 132 if (mdoc->meta.os) 133 free(mdoc->meta.os); 134 if (mdoc->meta.name) 135 free(mdoc->meta.name); 136 if (mdoc->meta.arch) 137 free(mdoc->meta.arch); 138 if (mdoc->meta.vol) 139 free(mdoc->meta.vol); 140 if (mdoc->meta.msec) 141 free(mdoc->meta.msec); 142} 143 144 145/* 146 * Allocate all volatile resources (parse tree, meta-data, fields). 147 */ 148static void 149mdoc_alloc1(struct mdoc *mdoc) 150{ 151 152 memset(&mdoc->meta, 0, sizeof(struct mdoc_meta)); 153 mdoc->flags = 0; 154 mdoc->lastnamed = mdoc->lastsec = SEC_NONE; 155 mdoc->last = mandoc_calloc(1, sizeof(struct mdoc_node)); 156 mdoc->first = mdoc->last; 157 mdoc->last->type = MDOC_ROOT; 158 mdoc->next = MDOC_NEXT_CHILD; 159} 160 161 162/* 163 * Free up volatile resources (see mdoc_free1()) then re-initialises the 164 * data with mdoc_alloc1(). After invocation, parse data has been reset 165 * and the parser is ready for re-invocation on a new tree; however, 166 * cross-parse non-volatile data is kept intact. 167 */ 168void 169mdoc_reset(struct mdoc *mdoc) 170{ 171 172 mdoc_free1(mdoc); 173 mdoc_alloc1(mdoc); 174} 175 176 177/* 178 * Completely free up all volatile and non-volatile parse resources. 179 * After invocation, the pointer is no longer usable. 180 */ 181void 182mdoc_free(struct mdoc *mdoc) 183{ 184 185 mdoc_free1(mdoc); 186 free(mdoc); 187} 188 189 190/* 191 * Allocate volatile and non-volatile parse resources. 192 */ 193struct mdoc * 194mdoc_alloc(void *data, int pflags, mandocmsg msg) 195{ 196 struct mdoc *p; 197 198 p = mandoc_calloc(1, sizeof(struct mdoc)); 199 200 p->msg = msg; 201 p->data = data; 202 p->pflags = pflags; 203 204 mdoc_hash_init(); 205 mdoc_alloc1(p); 206 return(p); 207} 208 209 210/* 211 * Climb back up the parse tree, validating open scopes. Mostly calls 212 * through to macro_end() in macro.c. 213 */ 214int 215mdoc_endparse(struct mdoc *m) 216{ 217 218 if (MDOC_HALT & m->flags) 219 return(0); 220 else if (mdoc_macroend(m)) 221 return(1); 222 m->flags |= MDOC_HALT; 223 return(0); 224} 225 226 227/* 228 * Main parse routine. Parses a single line -- really just hands off to 229 * the macro (mdoc_pmacro()) or text parser (mdoc_ptext()). 230 */ 231int 232mdoc_parseln(struct mdoc *m, int ln, char *buf, int offs) 233{ 234 235 if (MDOC_HALT & m->flags) 236 return(0); 237 238 m->flags |= MDOC_NEWLINE; 239 return(('.' == buf[offs] || '\'' == buf[offs]) ? 240 mdoc_pmacro(m, ln, buf, offs) : 241 mdoc_ptext(m, ln, buf, offs)); 242} 243 244 245int 246mdoc_vmsg(struct mdoc *mdoc, enum mandocerr t, 247 int ln, int pos, const char *fmt, ...) 248{ 249 char buf[256]; 250 va_list ap; 251 252 va_start(ap, fmt); 253 vsnprintf(buf, sizeof(buf) - 1, fmt, ap); 254 va_end(ap); 255 256 return((*mdoc->msg)(t, mdoc->data, ln, pos, buf)); 257} 258 259 260int 261mdoc_macro(struct mdoc *m, enum mdoct tok, 262 int ln, int pp, int *pos, char *buf) 263{ 264 assert(tok < MDOC_MAX); 265 266 /* If we're in the body, deny prologue calls. */ 267 268 if (MDOC_PROLOGUE & mdoc_macros[tok].flags && 269 MDOC_PBODY & m->flags) 270 return(mdoc_pmsg(m, ln, pp, MANDOCERR_BADBODY)); 271 272 /* If we're in the prologue, deny "body" macros. */ 273 274 if ( ! (MDOC_PROLOGUE & mdoc_macros[tok].flags) && 275 ! (MDOC_PBODY & m->flags)) { 276 if ( ! mdoc_pmsg(m, ln, pp, MANDOCERR_BADPROLOG)) 277 return(0); 278 if (NULL == m->meta.title) 279 m->meta.title = mandoc_strdup("UNKNOWN"); 280 if (NULL == m->meta.vol) 281 m->meta.vol = mandoc_strdup("LOCAL"); 282 if (NULL == m->meta.os) 283 m->meta.os = mandoc_strdup("LOCAL"); 284 if (0 == m->meta.date) 285 m->meta.date = time(NULL); 286 m->flags |= MDOC_PBODY; 287 } 288 289 return((*mdoc_macros[tok].fp)(m, tok, ln, pp, pos, buf)); 290} 291 292 293static int 294node_append(struct mdoc *mdoc, struct mdoc_node *p) 295{ 296 297 assert(mdoc->last); 298 assert(mdoc->first); 299 assert(MDOC_ROOT != p->type); 300 301 switch (mdoc->next) { 302 case (MDOC_NEXT_SIBLING): 303 mdoc->last->next = p; 304 p->prev = mdoc->last; 305 p->parent = mdoc->last->parent; 306 break; 307 case (MDOC_NEXT_CHILD): 308 mdoc->last->child = p; 309 p->parent = mdoc->last; 310 break; 311 default: 312 abort(); 313 /* NOTREACHED */ 314 } 315 316 p->parent->nchild++; 317 318 if ( ! mdoc_valid_pre(mdoc, p)) 319 return(0); 320 if ( ! mdoc_action_pre(mdoc, p)) 321 return(0); 322 323 switch (p->type) { 324 case (MDOC_HEAD): 325 assert(MDOC_BLOCK == p->parent->type); 326 p->parent->head = p; 327 break; 328 case (MDOC_TAIL): 329 assert(MDOC_BLOCK == p->parent->type); 330 p->parent->tail = p; 331 break; 332 case (MDOC_BODY): 333 assert(MDOC_BLOCK == p->parent->type); 334 p->parent->body = p; 335 break; 336 default: 337 break; 338 } 339 340 mdoc->last = p; 341 342 switch (p->type) { 343 case (MDOC_TEXT): 344 if ( ! mdoc_valid_post(mdoc)) 345 return(0); 346 if ( ! mdoc_action_post(mdoc)) 347 return(0); 348 break; 349 default: 350 break; 351 } 352 353 return(1); 354} 355 356 357static struct mdoc_node * 358node_alloc(struct mdoc *m, int line, int pos, 359 enum mdoct tok, enum mdoc_type type) 360{ 361 struct mdoc_node *p; 362 363 p = mandoc_calloc(1, sizeof(struct mdoc_node)); 364 p->sec = m->lastsec; 365 p->line = line; 366 p->pos = pos; 367 p->tok = tok; 368 p->type = type; 369 if (MDOC_NEWLINE & m->flags) 370 p->flags |= MDOC_LINE; 371 m->flags &= ~MDOC_NEWLINE; 372 return(p); 373} 374 375 376int 377mdoc_tail_alloc(struct mdoc *m, int line, int pos, enum mdoct tok) 378{ 379 struct mdoc_node *p; 380 381 p = node_alloc(m, line, pos, tok, MDOC_TAIL); 382 if ( ! node_append(m, p)) 383 return(0); 384 m->next = MDOC_NEXT_CHILD; 385 return(1); 386} 387 388 389int 390mdoc_head_alloc(struct mdoc *m, int line, int pos, enum mdoct tok) 391{ 392 struct mdoc_node *p; 393 394 assert(m->first); 395 assert(m->last); 396 397 p = node_alloc(m, line, pos, tok, MDOC_HEAD); 398 if ( ! node_append(m, p)) 399 return(0); 400 m->next = MDOC_NEXT_CHILD; 401 return(1); 402} 403 404 405int 406mdoc_body_alloc(struct mdoc *m, int line, int pos, enum mdoct tok) 407{ 408 struct mdoc_node *p; 409 410 p = node_alloc(m, line, pos, tok, MDOC_BODY); 411 if ( ! node_append(m, p)) 412 return(0); 413 m->next = MDOC_NEXT_CHILD; 414 return(1); 415} 416 417 418int 419mdoc_block_alloc(struct mdoc *m, int line, int pos, 420 enum mdoct tok, struct mdoc_arg *args) 421{ 422 struct mdoc_node *p; 423 424 p = node_alloc(m, line, pos, tok, MDOC_BLOCK); 425 p->args = args; 426 if (p->args) 427 (args->refcnt)++; 428 if ( ! node_append(m, p)) 429 return(0); 430 m->next = MDOC_NEXT_CHILD; 431 return(1); 432} 433 434 435int 436mdoc_elem_alloc(struct mdoc *m, int line, int pos, 437 enum mdoct tok, struct mdoc_arg *args) 438{ 439 struct mdoc_node *p; 440 441 p = node_alloc(m, line, pos, tok, MDOC_ELEM); 442 p->args = args; 443 if (p->args) 444 (args->refcnt)++; 445 if ( ! node_append(m, p)) 446 return(0); 447 m->next = MDOC_NEXT_CHILD; 448 return(1); 449} 450 451 452int 453mdoc_word_alloc(struct mdoc *m, int line, int pos, const char *p) 454{ 455 struct mdoc_node *n; 456 size_t sv, len; 457 458 len = strlen(p); 459 460 n = node_alloc(m, line, pos, MDOC_MAX, MDOC_TEXT); 461 n->string = mandoc_malloc(len + 1); 462 sv = strlcpy(n->string, p, len + 1); 463 464 /* Prohibit truncation. */ 465 assert(sv < len + 1); 466 467 if ( ! node_append(m, n)) 468 return(0); 469 470 m->next = MDOC_NEXT_SIBLING; 471 return(1); 472} 473 474 475void 476mdoc_node_free(struct mdoc_node *p) 477{ 478 479 if (p->string) 480 free(p->string); 481 if (p->args) 482 mdoc_argv_free(p->args); 483 free(p); 484} 485 486 487static void 488mdoc_node_unlink(struct mdoc *m, struct mdoc_node *n) 489{ 490 491 /* Adjust siblings. */ 492 493 if (n->prev) 494 n->prev->next = n->next; 495 if (n->next) 496 n->next->prev = n->prev; 497 498 /* Adjust parent. */ 499 500 if (n->parent) { 501 n->parent->nchild--; 502 if (n->parent->child == n) 503 n->parent->child = n->prev ? n->prev : n->next; 504 } 505 506 /* Adjust parse point, if applicable. */ 507 508 if (m && m->last == n) { 509 if (n->prev) { 510 m->last = n->prev; 511 m->next = MDOC_NEXT_SIBLING; 512 } else { 513 m->last = n->parent; 514 m->next = MDOC_NEXT_CHILD; 515 } 516 } 517 518 if (m && m->first == n) 519 m->first = NULL; 520} 521 522 523void 524mdoc_node_delete(struct mdoc *m, struct mdoc_node *p) 525{ 526 527 while (p->child) { 528 assert(p->nchild); 529 mdoc_node_delete(m, p->child); 530 } 531 assert(0 == p->nchild); 532 533 mdoc_node_unlink(m, p); 534 mdoc_node_free(p); 535} 536 537 538/* 539 * Parse free-form text, that is, a line that does not begin with the 540 * control character. 541 */ 542static int 543mdoc_ptext(struct mdoc *m, int line, char *buf, int offs) 544{ 545 char *c, *ws, *end; 546 struct mdoc_node *n; 547 548 /* Ignore bogus comments. */ 549 550 if ('\\' == buf[offs] && 551 '.' == buf[offs + 1] && 552 '"' == buf[offs + 2]) 553 return(mdoc_pmsg(m, line, offs, MANDOCERR_BADCOMMENT)); 554 555 /* No text before an initial macro. */ 556 557 if (SEC_NONE == m->lastnamed) 558 return(mdoc_pmsg(m, line, offs, MANDOCERR_NOTEXT)); 559 560 assert(m->last); 561 n = m->last; 562 563 /* 564 * Divert directly to list processing if we're encountering a 565 * columnar MDOC_BLOCK with or without a prior MDOC_BLOCK entry 566 * (a MDOC_BODY means it's already open, in which case we should 567 * process within its context in the normal way). 568 */ 569 570 if (MDOC_Bl == n->tok && MDOC_BODY == n->type && 571 LIST_column == n->data.list) { 572 /* `Bl' is open without any children. */ 573 m->flags |= MDOC_FREECOL; 574 return(mdoc_macro(m, MDOC_It, line, offs, &offs, buf)); 575 } 576 577 if (MDOC_It == n->tok && MDOC_BLOCK == n->type && 578 NULL != n->parent && 579 MDOC_Bl == n->parent->tok && 580 LIST_column == n->parent->data.list) { 581 /* `Bl' has block-level `It' children. */ 582 m->flags |= MDOC_FREECOL; 583 return(mdoc_macro(m, MDOC_It, line, offs, &offs, buf)); 584 } 585 586 /* 587 * Search for the beginning of unescaped trailing whitespace (ws) 588 * and for the first character not to be output (end). 589 */ 590 591 /* FIXME: replace with strcspn(). */ 592 ws = NULL; 593 for (c = end = buf + offs; *c; c++) { 594 switch (*c) { 595 case '-': 596 if (mandoc_hyph(buf + offs, c)) 597 *c = ASCII_HYPH; 598 ws = NULL; 599 break; 600 case ' ': 601 if (NULL == ws) 602 ws = c; 603 continue; 604 case '\t': 605 /* 606 * Always warn about trailing tabs, 607 * even outside literal context, 608 * where they should be put on the next line. 609 */ 610 if (NULL == ws) 611 ws = c; 612 /* 613 * Strip trailing tabs in literal context only; 614 * outside, they affect the next line. 615 */ 616 if (MDOC_LITERAL & m->flags) 617 continue; 618 break; 619 case '\\': 620 /* Skip the escaped character, too, if any. */ 621 if (c[1]) 622 c++; 623 /* FALLTHROUGH */ 624 default: 625 ws = NULL; 626 break; 627 } 628 end = c + 1; 629 } 630 *end = '\0'; 631 632 if (ws) 633 if ( ! mdoc_pmsg(m, line, (int)(ws-buf), MANDOCERR_EOLNSPACE)) 634 return(0); 635 636 if ('\0' == buf[offs] && ! (MDOC_LITERAL & m->flags)) { 637 if ( ! mdoc_pmsg(m, line, (int)(c-buf), MANDOCERR_NOBLANKLN)) 638 return(0); 639 640 /* 641 * Insert a `Pp' in the case of a blank line. Technically, 642 * blank lines aren't allowed, but enough manuals assume this 643 * behaviour that we want to work around it. 644 */ 645 if ( ! mdoc_elem_alloc(m, line, offs, MDOC_Pp, NULL)) 646 return(0); 647 648 m->next = MDOC_NEXT_SIBLING; 649 return(1); 650 } 651 652 if ( ! mdoc_word_alloc(m, line, offs, buf+offs)) 653 return(0); 654 655 if (MDOC_LITERAL & m->flags) 656 return(1); 657 658 /* 659 * End-of-sentence check. If the last character is an unescaped 660 * EOS character, then flag the node as being the end of a 661 * sentence. The front-end will know how to interpret this. 662 */ 663 664 assert(buf < end); 665 666 if (mandoc_eos(buf+offs, (size_t)(end-buf-offs))) 667 m->last->flags |= MDOC_EOS; 668 669 return(1); 670} 671 672 673static int 674macrowarn(struct mdoc *m, int ln, const char *buf, int offs) 675{ 676 int rc; 677 678 rc = mdoc_vmsg(m, MANDOCERR_MACRO, ln, offs, 679 "unknown macro: %s%s", 680 buf, strlen(buf) > 3 ? "..." : ""); 681 682 /* FIXME: logic should be in driver. */ 683 /* FIXME: broken, will error out and not omit a message. */ 684 return(MDOC_IGN_MACRO & m->pflags ? rc : 0); 685} 686 687 688/* 689 * Parse a macro line, that is, a line beginning with the control 690 * character. 691 */ 692int 693mdoc_pmacro(struct mdoc *m, int ln, char *buf, int offs) 694{ 695 enum mdoct tok; 696 int i, j, sv; 697 char mac[5]; 698 struct mdoc_node *n; 699 700 /* Empty lines are ignored. */ 701 702 offs++; 703 704 if ('\0' == buf[offs]) 705 return(1); 706 707 i = offs; 708 709 /* Accept whitespace after the initial control char. */ 710 711 if (' ' == buf[i]) { 712 i++; 713 while (buf[i] && ' ' == buf[i]) 714 i++; 715 if ('\0' == buf[i]) 716 return(1); 717 } 718 719 sv = i; 720 721 /* Copy the first word into a nil-terminated buffer. */ 722 723 for (j = 0; j < 4; j++, i++) { 724 if ('\0' == (mac[j] = buf[i])) 725 break; 726 else if (' ' == buf[i]) 727 break; 728 729 /* Check for invalid characters. */ 730 731 if (isgraph((u_char)buf[i])) 732 continue; 733 if ( ! mdoc_pmsg(m, ln, i, MANDOCERR_BADCHAR)) 734 return(0); 735 i--; 736 } 737 738 mac[j] = '\0'; 739 740 if (j == 4 || j < 2) { 741 if ( ! macrowarn(m, ln, mac, sv)) 742 goto err; 743 return(1); 744 } 745 746 if (MDOC_MAX == (tok = mdoc_hash_find(mac))) { 747 if ( ! macrowarn(m, ln, mac, sv)) 748 goto err; 749 return(1); 750 } 751 752 /* The macro is sane. Jump to the next word. */ 753 754 while (buf[i] && ' ' == buf[i]) 755 i++; 756 757 /* 758 * Trailing whitespace. Note that tabs are allowed to be passed 759 * into the parser as "text", so we only warn about spaces here. 760 */ 761 762 if ('\0' == buf[i] && ' ' == buf[i - 1]) 763 if ( ! mdoc_pmsg(m, ln, i - 1, MANDOCERR_EOLNSPACE)) 764 goto err; 765 766 /* 767 * If an initial macro or a list invocation, divert directly 768 * into macro processing. 769 */ 770 771 if (NULL == m->last || MDOC_It == tok || MDOC_El == tok) { 772 if ( ! mdoc_macro(m, tok, ln, sv, &i, buf)) 773 goto err; 774 return(1); 775 } 776 777 n = m->last; 778 assert(m->last); 779 780 /* 781 * If the first macro of a `Bl -column', open an `It' block 782 * context around the parsed macro. 783 */ 784 785 if (MDOC_Bl == n->tok && MDOC_BODY == n->type && 786 LIST_column == n->data.list) { 787 m->flags |= MDOC_FREECOL; 788 if ( ! mdoc_macro(m, MDOC_It, ln, sv, &sv, buf)) 789 goto err; 790 return(1); 791 } 792 793 /* 794 * If we're following a block-level `It' within a `Bl -column' 795 * context (perhaps opened in the above block or in ptext()), 796 * then open an `It' block context around the parsed macro. 797 */ 798 799 if (MDOC_It == n->tok && MDOC_BLOCK == n->type && 800 NULL != n->parent && 801 MDOC_Bl == n->parent->tok && 802 LIST_column == n->parent->data.list) { 803 m->flags |= MDOC_FREECOL; 804 if ( ! mdoc_macro(m, MDOC_It, ln, sv, &sv, buf)) 805 goto err; 806 return(1); 807 } 808 809 /* Normal processing of a macro. */ 810 811 if ( ! mdoc_macro(m, tok, ln, sv, &i, buf)) 812 goto err; 813 814 return(1); 815 816err: /* Error out. */ 817 818 m->flags |= MDOC_HALT; 819 return(0); 820} 821 822 823