1 /* $Id: mdoc_argv.c,v 1.1 2009/04/06 20:30:40 kristaps Exp $ */ 2 /* 3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@openbsd.org> 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 7 * above copyright notice and this permission notice appear in all 8 * copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17 * PERFORMANCE OF THIS SOFTWARE. 18 */ 19 #include <sys/types.h> 20 21 #include <assert.h> 22 #include <ctype.h> 23 #include <stdlib.h> 24 #include <stdio.h> 25 #include <string.h> 26 27 #include "libmdoc.h" 28 29 /* 30 * Routines to parse arguments of macros. Arguments follow the syntax 31 * of `-arg [val [valN...]]'. Arguments come in all types: quoted 32 * arguments, multiple arguments per value, no-value arguments, etc. 33 * 34 * There's no limit to the number or arguments that may be allocated. 35 */ 36 37 #define ARGS_QUOTED (1 << 0) 38 #define ARGS_DELIM (1 << 1) 39 #define ARGS_TABSEP (1 << 2) 40 #define ARGS_ARGVLIKE (1 << 3) 41 42 #define ARGV_NONE (1 << 0) 43 #define ARGV_SINGLE (1 << 1) 44 #define ARGV_MULTI (1 << 2) 45 #define ARGV_OPT_SINGLE (1 << 3) 46 47 #define MULTI_STEP 5 48 49 enum mwarn { 50 WQUOTPARM, 51 WARGVPARM, 52 WCOLEMPTY, 53 WTAILWS 54 }; 55 56 enum merr { 57 EQUOTTERM, 58 EMALLOC, 59 EARGVAL 60 }; 61 62 static int argv_a2arg(int, const char *); 63 static int args(struct mdoc *, int, int *, 64 char *, int, char **); 65 static int argv(struct mdoc *, int, 66 struct mdoc_argv *, int *, char *); 67 static int argv_single(struct mdoc *, int, 68 struct mdoc_argv *, int *, char *); 69 static int argv_opt_single(struct mdoc *, int, 70 struct mdoc_argv *, int *, char *); 71 static int argv_multi(struct mdoc *, int, 72 struct mdoc_argv *, int *, char *); 73 static int pwarn(struct mdoc *, int, int, enum mwarn); 74 static int perr(struct mdoc *, int, int, enum merr); 75 76 #define verr(m, t) perr((m), (m)->last->line, (m)->last->pos, (t)) 77 78 /* Per-argument flags. */ 79 80 static int mdoc_argvflags[MDOC_ARG_MAX] = { 81 ARGV_NONE, /* MDOC_Split */ 82 ARGV_NONE, /* MDOC_Nosplit */ 83 ARGV_NONE, /* MDOC_Ragged */ 84 ARGV_NONE, /* MDOC_Unfilled */ 85 ARGV_NONE, /* MDOC_Literal */ 86 ARGV_NONE, /* MDOC_File */ 87 ARGV_SINGLE, /* MDOC_Offset */ 88 ARGV_NONE, /* MDOC_Bullet */ 89 ARGV_NONE, /* MDOC_Dash */ 90 ARGV_NONE, /* MDOC_Hyphen */ 91 ARGV_NONE, /* MDOC_Item */ 92 ARGV_NONE, /* MDOC_Enum */ 93 ARGV_NONE, /* MDOC_Tag */ 94 ARGV_NONE, /* MDOC_Diag */ 95 ARGV_NONE, /* MDOC_Hang */ 96 ARGV_NONE, /* MDOC_Ohang */ 97 ARGV_NONE, /* MDOC_Inset */ 98 ARGV_MULTI, /* MDOC_Column */ 99 ARGV_SINGLE, /* MDOC_Width */ 100 ARGV_NONE, /* MDOC_Compact */ 101 ARGV_OPT_SINGLE, /* MDOC_Std */ 102 ARGV_NONE, /* MDOC_Filled */ 103 ARGV_NONE, /* MDOC_Words */ 104 ARGV_NONE, /* MDOC_Emphasis */ 105 ARGV_NONE, /* MDOC_Symbolic */ 106 ARGV_NONE /* MDOC_Symbolic */ 107 }; 108 109 static int mdoc_argflags[MDOC_MAX] = { 110 0, /* \" */ 111 0, /* Dd */ 112 0, /* Dt */ 113 0, /* Os */ 114 ARGS_QUOTED, /* Sh */ 115 ARGS_QUOTED, /* Ss */ 116 ARGS_DELIM, /* Pp */ 117 ARGS_DELIM, /* D1 */ 118 ARGS_DELIM | ARGS_QUOTED, /* Dl */ 119 0, /* Bd */ 120 0, /* Ed */ 121 0, /* Bl */ 122 0, /* El */ 123 0, /* It */ 124 ARGS_DELIM, /* Ad */ 125 ARGS_DELIM, /* An */ 126 ARGS_DELIM | ARGS_QUOTED, /* Ar */ 127 ARGS_QUOTED, /* Cd */ 128 ARGS_DELIM, /* Cm */ 129 ARGS_DELIM, /* Dv */ 130 ARGS_DELIM, /* Er */ 131 ARGS_DELIM, /* Ev */ 132 0, /* Ex */ 133 ARGS_DELIM | ARGS_QUOTED, /* Fa */ 134 0, /* Fd */ 135 ARGS_DELIM, /* Fl */ 136 ARGS_DELIM | ARGS_QUOTED, /* Fn */ 137 ARGS_DELIM | ARGS_QUOTED, /* Ft */ 138 ARGS_DELIM, /* Ic */ 139 0, /* In */ 140 ARGS_DELIM | ARGS_QUOTED, /* Li */ 141 ARGS_QUOTED, /* Nd */ 142 ARGS_DELIM, /* Nm */ 143 ARGS_DELIM, /* Op */ 144 0, /* Ot */ 145 ARGS_DELIM, /* Pa */ 146 0, /* Rv */ 147 ARGS_DELIM | ARGS_ARGVLIKE, /* St */ 148 ARGS_DELIM, /* Va */ 149 ARGS_DELIM, /* Vt */ 150 ARGS_DELIM, /* Xr */ 151 ARGS_QUOTED, /* %A */ 152 ARGS_QUOTED, /* %B */ 153 ARGS_QUOTED, /* %D */ 154 ARGS_QUOTED, /* %I */ 155 ARGS_QUOTED, /* %J */ 156 ARGS_QUOTED, /* %N */ 157 ARGS_QUOTED, /* %O */ 158 ARGS_QUOTED, /* %P */ 159 ARGS_QUOTED, /* %R */ 160 ARGS_QUOTED, /* %T */ 161 ARGS_QUOTED, /* %V */ 162 ARGS_DELIM, /* Ac */ 163 0, /* Ao */ 164 ARGS_DELIM, /* Aq */ 165 ARGS_DELIM, /* At */ 166 ARGS_DELIM, /* Bc */ 167 0, /* Bf */ 168 0, /* Bo */ 169 ARGS_DELIM, /* Bq */ 170 ARGS_DELIM, /* Bsx */ 171 ARGS_DELIM, /* Bx */ 172 0, /* Db */ 173 ARGS_DELIM, /* Dc */ 174 0, /* Do */ 175 ARGS_DELIM, /* Dq */ 176 ARGS_DELIM, /* Ec */ 177 0, /* Ef */ 178 ARGS_DELIM, /* Em */ 179 0, /* Eo */ 180 ARGS_DELIM, /* Fx */ 181 ARGS_DELIM, /* Ms */ 182 ARGS_DELIM, /* No */ 183 ARGS_DELIM, /* Ns */ 184 ARGS_DELIM, /* Nx */ 185 ARGS_DELIM, /* Ox */ 186 ARGS_DELIM, /* Pc */ 187 ARGS_DELIM, /* Pf */ 188 0, /* Po */ 189 ARGS_DELIM, /* Pq */ 190 ARGS_DELIM, /* Qc */ 191 ARGS_DELIM, /* Ql */ 192 0, /* Qo */ 193 ARGS_DELIM, /* Qq */ 194 0, /* Re */ 195 0, /* Rs */ 196 ARGS_DELIM, /* Sc */ 197 0, /* So */ 198 ARGS_DELIM, /* Sq */ 199 0, /* Sm */ 200 ARGS_DELIM, /* Sx */ 201 ARGS_DELIM | ARGS_QUOTED, /* Sy */ 202 ARGS_DELIM, /* Tn */ 203 ARGS_DELIM, /* Ux */ 204 ARGS_DELIM, /* Xc */ 205 0, /* Xo */ 206 ARGS_QUOTED, /* Fo */ 207 0, /* Fc */ 208 0, /* Oo */ 209 ARGS_DELIM, /* Oc */ 210 0, /* Bk */ 211 0, /* Ek */ 212 0, /* Bt */ 213 0, /* Hf */ 214 0, /* Fr */ 215 0, /* Ud */ 216 0, /* Lb */ 217 0, /* Ap */ 218 ARGS_DELIM, /* Lp */ 219 ARGS_DELIM | ARGS_QUOTED, /* Lk */ 220 ARGS_DELIM | ARGS_QUOTED, /* Mt */ 221 ARGS_DELIM, /* Brq */ 222 0, /* Bro */ 223 ARGS_DELIM, /* Brc */ 224 ARGS_QUOTED, /* %C */ 225 0, /* Es */ 226 0, /* En */ 227 0, /* Dx */ 228 ARGS_QUOTED, /* %Q */ 229 }; 230 231 232 /* 233 * Parse an argument from line text. This comes in the form of -key 234 * [value0...], which may either have a single mandatory value, at least 235 * one mandatory value, an optional single value, or no value. 236 */ 237 int 238 mdoc_argv(struct mdoc *mdoc, int line, int tok, 239 struct mdoc_arg **v, int *pos, char *buf) 240 { 241 int i; 242 char *p, sv; 243 struct mdoc_argv tmp; 244 struct mdoc_arg *arg; 245 246 if (0 == buf[*pos]) 247 return(ARGV_EOLN); 248 249 assert(' ' != buf[*pos]); 250 251 if ('-' != buf[*pos] || ARGS_ARGVLIKE & mdoc_argflags[tok]) 252 return(ARGV_WORD); 253 254 /* Parse through to the first unescaped space. */ 255 256 i = *pos; 257 p = &buf[++(*pos)]; 258 259 assert(*pos > 0); 260 261 /* LINTED */ 262 while (buf[*pos]) { 263 if (' ' == buf[*pos]) 264 if ('\\' != buf[*pos - 1]) 265 break; 266 (*pos)++; 267 } 268 269 /* XXX - save zeroed byte, if not an argument. */ 270 271 sv = 0; 272 if (buf[*pos]) { 273 sv = buf[*pos]; 274 buf[(*pos)++] = 0; 275 } 276 277 (void)memset(&tmp, 0, sizeof(struct mdoc_argv)); 278 tmp.line = line; 279 tmp.pos = *pos; 280 281 /* See if our token accepts the argument. */ 282 283 if (MDOC_ARG_MAX == (tmp.arg = argv_a2arg(tok, p))) { 284 /* XXX - restore saved zeroed byte. */ 285 if (sv) 286 buf[*pos - 1] = sv; 287 if ( ! pwarn(mdoc, line, i, WARGVPARM)) 288 return(ARGV_ERROR); 289 return(ARGV_WORD); 290 } 291 292 while (buf[*pos] && ' ' == buf[*pos]) 293 (*pos)++; 294 295 if ( ! argv(mdoc, line, &tmp, pos, buf)) 296 return(ARGV_ERROR); 297 298 if (NULL == (arg = *v)) { 299 *v = calloc(1, sizeof(struct mdoc_arg)); 300 if (NULL == *v) { 301 (void)verr(mdoc, EMALLOC); 302 return(ARGV_ERROR); 303 } 304 arg = *v; 305 } 306 307 arg->argc++; 308 arg->argv = realloc(arg->argv, arg->argc * 309 sizeof(struct mdoc_argv)); 310 311 if (NULL == arg->argv) { 312 (void)verr(mdoc, EMALLOC); 313 return(ARGV_ERROR); 314 } 315 316 (void)memcpy(&arg->argv[(int)arg->argc - 1], 317 &tmp, sizeof(struct mdoc_argv)); 318 319 return(ARGV_ARG); 320 } 321 322 323 void 324 mdoc_argv_free(struct mdoc_arg *p) 325 { 326 int i, j; 327 328 if (NULL == p) 329 return; 330 331 if (p->refcnt) { 332 --(p->refcnt); 333 if (p->refcnt) 334 return; 335 } 336 assert(p->argc); 337 338 /* LINTED */ 339 for (i = 0; i < (int)p->argc; i++) { 340 if (0 == p->argv[i].sz) 341 continue; 342 /* LINTED */ 343 for (j = 0; j < (int)p->argv[i].sz; j++) 344 free(p->argv[i].value[j]); 345 346 free(p->argv[i].value); 347 } 348 349 free(p->argv); 350 free(p); 351 } 352 353 354 355 static int 356 perr(struct mdoc *mdoc, int line, int pos, enum merr code) 357 { 358 char *p; 359 360 p = NULL; 361 switch (code) { 362 case (EMALLOC): 363 p = "memory exhausted"; 364 break; 365 case (EQUOTTERM): 366 p = "unterminated quoted parameter"; 367 break; 368 case (EARGVAL): 369 p = "argument requires a value"; 370 break; 371 } 372 assert(p); 373 return(mdoc_perr(mdoc, line, pos, p)); 374 } 375 376 377 static int 378 pwarn(struct mdoc *mdoc, int line, int pos, enum mwarn code) 379 { 380 char *p; 381 int c; 382 383 p = NULL; 384 c = WARN_SYNTAX; 385 switch (code) { 386 case (WQUOTPARM): 387 p = "unexpected quoted parameter"; 388 break; 389 case (WARGVPARM): 390 p = "argument-like parameter"; 391 break; 392 case (WCOLEMPTY): 393 p = "last list column is empty"; 394 c = WARN_COMPAT; 395 break; 396 case (WTAILWS): 397 p = "trailing whitespace"; 398 c = WARN_COMPAT; 399 break; 400 } 401 assert(p); 402 return(mdoc_pwarn(mdoc, line, pos, c, p)); 403 } 404 405 406 int 407 mdoc_args(struct mdoc *mdoc, int line, 408 int *pos, char *buf, int tok, char **v) 409 { 410 int fl, c, i; 411 struct mdoc_node *n; 412 413 fl = (0 == tok) ? 0 : mdoc_argflags[tok]; 414 415 /* 416 * Override per-macro argument flags with context-specific ones. 417 * As of now, this is only valid for `It' depending on its list 418 * context. 419 */ 420 421 switch (tok) { 422 case (MDOC_It): 423 for (n = mdoc->last; n; n = n->parent) 424 if (MDOC_BLOCK == n->type && MDOC_Bl == n->tok) 425 break; 426 427 assert(n); 428 c = (int)(n->args ? n->args->argc : 0); 429 assert(c > 0); 430 431 /* 432 * Using `Bl -column' adds ARGS_TABSEP to the arguments 433 * and invalidates ARGS_DELIM. Using `Bl -diag' allows 434 * for quoted arguments. 435 */ 436 437 /* LINTED */ 438 for (i = 0; i < c; i++) { 439 switch (n->args->argv[i].arg) { 440 case (MDOC_Column): 441 fl |= ARGS_TABSEP; 442 fl &= ~ARGS_DELIM; 443 i = c; 444 break; 445 case (MDOC_Diag): 446 fl |= ARGS_QUOTED; 447 i = c; 448 break; 449 default: 450 break; 451 } 452 } 453 break; 454 default: 455 break; 456 } 457 458 return(args(mdoc, line, pos, buf, fl, v)); 459 } 460 461 462 static int 463 args(struct mdoc *mdoc, int line, 464 int *pos, char *buf, int fl, char **v) 465 { 466 int i; 467 char *p, *pp; 468 469 assert(*pos > 0); 470 471 if (0 == buf[*pos]) 472 return(ARGS_EOLN); 473 474 if ('\"' == buf[*pos] && ! (fl & ARGS_QUOTED)) 475 if ( ! pwarn(mdoc, line, *pos, WQUOTPARM)) 476 return(ARGS_ERROR); 477 478 if ( ! (fl & ARGS_ARGVLIKE) && '-' == buf[*pos]) 479 if ( ! pwarn(mdoc, line, *pos, WARGVPARM)) 480 return(ARGS_ERROR); 481 482 /* 483 * If the first character is a delimiter and we're to look for 484 * delimited strings, then pass down the buffer seeing if it 485 * follows the pattern of [[::delim::][ ]+]+. 486 */ 487 488 if ((fl & ARGS_DELIM) && mdoc_iscdelim(buf[*pos])) { 489 for (i = *pos; buf[i]; ) { 490 if ( ! mdoc_iscdelim(buf[i])) 491 break; 492 i++; 493 /* There must be at least one space... */ 494 if (0 == buf[i] || ' ' != buf[i]) 495 break; 496 i++; 497 while (buf[i] && ' ' == buf[i]) 498 i++; 499 } 500 if (0 == buf[i]) { 501 *v = &buf[*pos]; 502 return(ARGS_PUNCT); 503 } 504 } 505 506 /* First parse non-quoted strings. */ 507 508 if ('\"' != buf[*pos] || ! (ARGS_QUOTED & fl)) { 509 *v = &buf[*pos]; 510 511 /* 512 * Thar be dragons here! If we're tab-separated, search 513 * ahead for either a tab or the `Ta' macro. 514 * If a `Ta' is detected, it must be space-buffered before and 515 * after. If either of these hold true, then prune out the 516 * extra spaces and call it an argument. 517 */ 518 519 if (ARGS_TABSEP & fl) { 520 /* Scan ahead to unescaped tab. */ 521 522 p = strchr(*v, '\t'); 523 524 /* Scan ahead to unescaped `Ta'. */ 525 526 for (pp = *v; ; pp++) { 527 if (NULL == (pp = strstr(pp, "Ta"))) 528 break; 529 if (pp > *v && ' ' != *(pp - 1)) 530 continue; 531 if (' ' == *(pp + 2) || 0 == *(pp + 2)) 532 break; 533 } 534 535 /* Choose delimiter tab/Ta. */ 536 537 if (p && pp) 538 p = (p < pp ? p : pp); 539 else if ( ! p && pp) 540 p = pp; 541 542 /* Strip delimiter's preceding whitespace. */ 543 544 if (p && p > *v) { 545 pp = p - 1; 546 while (pp > *v && ' ' == *pp) 547 pp--; 548 if (pp == *v && ' ' == *pp) 549 *pp = 0; 550 else if (' ' == *pp) 551 *(pp + 1) = 0; 552 } 553 554 /* ...in- and proceding whitespace. */ 555 556 if (p && ('\t' != *p)) { 557 *p++ = 0; 558 *p++ = 0; 559 } else if (p) 560 *p++ = 0; 561 562 if (p) { 563 while (' ' == *p) 564 p++; 565 if (0 != *p) 566 *(p - 1) = 0; 567 *pos += (int)(p - *v); 568 } 569 570 if (p && 0 == *p) 571 if ( ! pwarn(mdoc, line, *pos, WCOLEMPTY)) 572 return(0); 573 if (p && 0 == *p && p > *v && ' ' == *(p - 1)) 574 if ( ! pwarn(mdoc, line, *pos, WTAILWS)) 575 return(0); 576 577 if (p) 578 return(ARGS_PHRASE); 579 580 /* Configure the eoln case, too. */ 581 582 p = strchr(*v, 0); 583 assert(p); 584 585 if (p > *v && ' ' == *(p - 1)) 586 if ( ! pwarn(mdoc, line, *pos, WTAILWS)) 587 return(0); 588 *pos += (int)(p - *v); 589 590 return(ARGS_PHRASE); 591 } 592 593 /* Do non-tabsep look-ahead here. */ 594 595 if ( ! (ARGS_TABSEP & fl)) 596 while (buf[*pos]) { 597 if (' ' == buf[*pos]) 598 if ('\\' != buf[*pos - 1]) 599 break; 600 (*pos)++; 601 } 602 603 if (0 == buf[*pos]) 604 return(ARGS_WORD); 605 606 buf[(*pos)++] = 0; 607 608 if (0 == buf[*pos]) 609 return(ARGS_WORD); 610 611 if ( ! (ARGS_TABSEP & fl)) 612 while (buf[*pos] && ' ' == buf[*pos]) 613 (*pos)++; 614 615 if (buf[*pos]) 616 return(ARGS_WORD); 617 618 if ( ! pwarn(mdoc, line, *pos, WTAILWS)) 619 return(ARGS_ERROR); 620 621 return(ARGS_WORD); 622 } 623 624 /* 625 * If we're a quoted string (and quoted strings are allowed), 626 * then parse ahead to the next quote. If none's found, it's an 627 * error. After, parse to the next word. 628 */ 629 630 *v = &buf[++(*pos)]; 631 632 while (buf[*pos] && '\"' != buf[*pos]) 633 (*pos)++; 634 635 if (0 == buf[*pos]) { 636 (void)perr(mdoc, line, *pos, EQUOTTERM); 637 return(ARGS_ERROR); 638 } 639 640 buf[(*pos)++] = 0; 641 if (0 == buf[*pos]) 642 return(ARGS_QWORD); 643 644 while (buf[*pos] && ' ' == buf[*pos]) 645 (*pos)++; 646 647 if (buf[*pos]) 648 return(ARGS_QWORD); 649 650 if ( ! pwarn(mdoc, line, *pos, WTAILWS)) 651 return(ARGS_ERROR); 652 653 return(ARGS_QWORD); 654 } 655 656 657 static int 658 argv_a2arg(int tok, const char *argv) 659 { 660 661 /* 662 * Parse an argument identifier from its text. XXX - this 663 * should really be table-driven to clarify the code. 664 * 665 * If you add an argument to the list, make sure that you 666 * register it here with its one or more macros! 667 */ 668 669 switch (tok) { 670 case (MDOC_An): 671 if (0 == strcmp(argv, "split")) 672 return(MDOC_Split); 673 else if (0 == strcmp(argv, "nosplit")) 674 return(MDOC_Nosplit); 675 break; 676 677 case (MDOC_Bd): 678 if (0 == strcmp(argv, "ragged")) 679 return(MDOC_Ragged); 680 else if (0 == strcmp(argv, "unfilled")) 681 return(MDOC_Unfilled); 682 else if (0 == strcmp(argv, "filled")) 683 return(MDOC_Filled); 684 else if (0 == strcmp(argv, "literal")) 685 return(MDOC_Literal); 686 else if (0 == strcmp(argv, "file")) 687 return(MDOC_File); 688 else if (0 == strcmp(argv, "offset")) 689 return(MDOC_Offset); 690 else if (0 == strcmp(argv, "compact")) 691 return(MDOC_Compact); 692 break; 693 694 case (MDOC_Bf): 695 if (0 == strcmp(argv, "emphasis")) 696 return(MDOC_Emphasis); 697 else if (0 == strcmp(argv, "literal")) 698 return(MDOC_Literal); 699 else if (0 == strcmp(argv, "symbolic")) 700 return(MDOC_Symbolic); 701 break; 702 703 case (MDOC_Bk): 704 if (0 == strcmp(argv, "words")) 705 return(MDOC_Words); 706 break; 707 708 case (MDOC_Bl): 709 if (0 == strcmp(argv, "bullet")) 710 return(MDOC_Bullet); 711 else if (0 == strcmp(argv, "dash")) 712 return(MDOC_Dash); 713 else if (0 == strcmp(argv, "hyphen")) 714 return(MDOC_Hyphen); 715 else if (0 == strcmp(argv, "item")) 716 return(MDOC_Item); 717 else if (0 == strcmp(argv, "enum")) 718 return(MDOC_Enum); 719 else if (0 == strcmp(argv, "tag")) 720 return(MDOC_Tag); 721 else if (0 == strcmp(argv, "diag")) 722 return(MDOC_Diag); 723 else if (0 == strcmp(argv, "hang")) 724 return(MDOC_Hang); 725 else if (0 == strcmp(argv, "ohang")) 726 return(MDOC_Ohang); 727 else if (0 == strcmp(argv, "inset")) 728 return(MDOC_Inset); 729 else if (0 == strcmp(argv, "column")) 730 return(MDOC_Column); 731 else if (0 == strcmp(argv, "width")) 732 return(MDOC_Width); 733 else if (0 == strcmp(argv, "offset")) 734 return(MDOC_Offset); 735 else if (0 == strcmp(argv, "compact")) 736 return(MDOC_Compact); 737 else if (0 == strcmp(argv, "nested")) 738 return(MDOC_Nested); 739 break; 740 741 case (MDOC_Rv): 742 /* FALLTHROUGH */ 743 case (MDOC_Ex): 744 if (0 == strcmp(argv, "std")) 745 return(MDOC_Std); 746 break; 747 default: 748 break; 749 } 750 751 return(MDOC_ARG_MAX); 752 } 753 754 755 static int 756 argv_multi(struct mdoc *mdoc, int line, 757 struct mdoc_argv *v, int *pos, char *buf) 758 { 759 int c, ppos; 760 char *p; 761 762 ppos = *pos; 763 764 for (v->sz = 0; ; v->sz++) { 765 if ('-' == buf[*pos]) 766 break; 767 c = args(mdoc, line, pos, buf, ARGS_QUOTED, &p); 768 if (ARGS_ERROR == c) 769 return(0); 770 else if (ARGS_EOLN == c) 771 break; 772 773 if (0 == v->sz % MULTI_STEP) { 774 v->value = realloc(v->value, 775 (v->sz + MULTI_STEP) * sizeof(char *)); 776 if (NULL == v->value) { 777 (void)verr(mdoc, EMALLOC); 778 return(ARGV_ERROR); 779 } 780 } 781 if (NULL == (v->value[(int)v->sz] = strdup(p))) 782 return(verr(mdoc, EMALLOC)); 783 } 784 785 if (v->sz) 786 return(1); 787 788 return(perr(mdoc, line, ppos, EARGVAL)); 789 } 790 791 792 static int 793 argv_opt_single(struct mdoc *mdoc, int line, 794 struct mdoc_argv *v, int *pos, char *buf) 795 { 796 int c; 797 char *p; 798 799 if ('-' == buf[*pos]) 800 return(1); 801 802 c = args(mdoc, line, pos, buf, ARGS_QUOTED, &p); 803 if (ARGS_ERROR == c) 804 return(0); 805 if (ARGS_EOLN == c) 806 return(1); 807 808 v->sz = 1; 809 if (NULL == (v->value = calloc(1, sizeof(char *)))) 810 return(verr(mdoc, EMALLOC)); 811 if (NULL == (v->value[0] = strdup(p))) 812 return(verr(mdoc, EMALLOC)); 813 814 return(1); 815 } 816 817 818 /* 819 * Parse a single, mandatory value from the stream. 820 */ 821 static int 822 argv_single(struct mdoc *mdoc, int line, 823 struct mdoc_argv *v, int *pos, char *buf) 824 { 825 int c, ppos; 826 char *p; 827 828 ppos = *pos; 829 830 c = args(mdoc, line, pos, buf, ARGS_QUOTED, &p); 831 if (ARGS_ERROR == c) 832 return(0); 833 if (ARGS_EOLN == c) 834 return(perr(mdoc, line, ppos, EARGVAL)); 835 836 v->sz = 1; 837 if (NULL == (v->value = calloc(1, sizeof(char *)))) 838 return(verr(mdoc, EMALLOC)); 839 if (NULL == (v->value[0] = strdup(p))) 840 return(verr(mdoc, EMALLOC)); 841 842 return(1); 843 } 844 845 846 /* 847 * Determine rules for parsing arguments. Arguments can either accept 848 * no parameters, an optional single parameter, one parameter, or 849 * multiple parameters. 850 */ 851 static int 852 argv(struct mdoc *mdoc, int line, 853 struct mdoc_argv *v, int *pos, char *buf) 854 { 855 856 v->sz = 0; 857 v->value = NULL; 858 859 switch (mdoc_argvflags[v->arg]) { 860 case (ARGV_SINGLE): 861 return(argv_single(mdoc, line, v, pos, buf)); 862 case (ARGV_MULTI): 863 return(argv_multi(mdoc, line, v, pos, buf)); 864 case (ARGV_OPT_SINGLE): 865 return(argv_opt_single(mdoc, line, v, pos, buf)); 866 default: 867 /* ARGV_NONE */ 868 break; 869 } 870 871 return(1); 872 } 873