1 /* @(#)getargs.c 2.71 17/07/16 Copyright 1985, 1988, 1994-2017 J. Schilling */ 2 #include <schily/mconfig.h> 3 #ifndef lint 4 static UConst char sccsid[] = 5 "@(#)getargs.c 2.71 17/07/16 Copyright 1985, 1988, 1994-2017 J. Schilling"; 6 #endif 7 #define NEW 8 /* 9 * Copyright (c) 1985, 1988, 1994-2017 J. Schilling 10 * 11 * 1.3.88 Start implementation of release 2 12 */ 13 /* 14 * Parse arguments on a command line. 15 * Format string type specifier (appearing directly after flag name): 16 * '' BOOL size int set to TRUE (1) 17 * '%' Extended format, next char determines the type: 18 * '%0' BOOL with size modifier set to FALSE (0) 19 * '%1' BOOL with size modifier set to TRUE(1) 20 * '*' string 21 * '?' char 22 * '#' number 23 * '&' call function for any type flag 24 * '~' call function for BOOLEAN flag 25 * '+' inctype +++ NEU +++ 26 * 27 * The format string 'f* ' may be used to disallow -ffoo for f* 28 * The same behavior is implemented for 'f# ', 'f? ' and 'f& '. 29 * The ' ' needs to immediately follow the format type specifier. 30 * 31 * The format string 'f*_' may be used to disallow -f foo for f* 32 * The same behavior is implemented for 'f#_', 'f?_' and 'f&_'. 33 * The '_' needs to immediately follow the format type specifier. 34 * This also allows to implement optional arguments to options. 35 * Note: 'f#_' is only implemented for orthogonality, -f will 36 * be converted to an integer value of 0. 37 * 38 * The '#', '+' and '%[01]' types may have size modifiers added: 39 * 'c'/'C' char 40 * 's'/'S' short 41 * 'i'/'I' int (default == no size modifier) 42 * 'l'/'L' long 43 * 'll'/'LL' long long 44 */ 45 /* 46 * The contents of this file are subject to the terms of the 47 * Common Development and Distribution License, Version 1.0 only 48 * (the "License"). You may not use this file except in compliance 49 * with the License. 50 * 51 * See the file CDDL.Schily.txt in this distribution for details. 52 * A copy of the CDDL is also available via the Internet at 53 * http://www.opensource.org/licenses/cddl1.txt 54 * 55 * When distributing Covered Code, include this CDDL HEADER in each 56 * file and include the License file CDDL.Schily.txt from this distribution. 57 */ 58 /* LINTLIBRARY */ 59 #include <schily/mconfig.h> 60 #include <schily/standard.h> 61 #include <schily/utypes.h> 62 #include <schily/getargs.h> 63 #include <schily/varargs.h> 64 #include <schily/string.h> 65 #include <schily/schily.h> 66 #include <schily/ctype.h> 67 68 /* 69 * Various return values 70 */ 71 #define RETMAX 2 /* Max. value for getargerror() */ 72 #define FLAGDELIM 2 /* "--" stopped flag processing */ 73 #define NOTAFLAG 1 /* Not a flag type argument */ 74 #define NOARGS 0 /* No more args */ 75 #define BADFLAG (-1) /* Not a valid flag argument */ 76 #define BADFMT (-2) /* Error in format string */ 77 #define NOTAFILE (-3) /* Seems to be a flag type arg */ 78 #define RETMIN (-3) /* Min. value for getargerror() */ 79 80 LOCAL char *retnames[] = { 81 "NOTAFILE", 82 "BADFMT", 83 "BADFLAG", 84 "NOARGS", 85 "NOTAFLAG", 86 "FLAGDELIM", 87 }; 88 #define RNAME(a) (retnames[(a)-RETMIN]) 89 90 #define SCANONLY 0 /* Do not try to set argument values */ 91 #define SETARGS 1 /* Set argument values from cmdline */ 92 #define ARGVECTOR 2 /* Use vector instead of list interface */ 93 #define NOEQUAL 4 /* -opt=val not allowed for -opt val */ 94 95 LOCAL struct ga_props *_getprops __PR((struct ga_props *)); 96 97 int _getargs __PR((int *, char *const **, void *, 98 int, 99 struct ga_props *, 100 va_list)); 101 LOCAL int dofile __PR((int *, char *const **, const char **, 102 struct ga_props *)); 103 LOCAL int doflag __PR((int *, char *const **, const char *, 104 void *, int, va_list)); 105 LOCAL int dosflags __PR((const char *, void *, 106 int *, char *const **, 107 int, va_list)); 108 LOCAL int checkfmt __PR((const char *)); 109 LOCAL int checkeql __PR((const char *)); 110 111 LOCAL va_list va_dummy; 112 113 LOCAL char fmtspecs[] = "#?*&~+%"; 114 115 #define isfmtspec(c) (strchr(fmtspecs, c) != NULL) 116 117 LOCAL struct ga_props props_default = { 0, 0, sizeof (struct ga_props) }; 118 LOCAL struct ga_props props_posix = { GAF_POSIX_DEFAULT, 0, sizeof (struct ga_props) }; 119 120 EXPORT int 121 _getarginit(props, size, flags) 122 struct ga_props *props; 123 size_t size; 124 UInt32_t flags; 125 { 126 if (size > sizeof (struct ga_props)) 127 return (-1); 128 129 /* 130 * GAF_POSIX may be used as an alias for the flags that currently 131 * define POSIX behavior. 132 */ 133 if (flags == GAF_POSIX) 134 flags = GAF_POSIX_DEFAULT; 135 136 props->ga_flags = flags; 137 props->ga_oflags = 0; 138 props->ga_size = size; 139 return (0); 140 } 141 142 LOCAL struct ga_props * 143 _getprops(props) 144 struct ga_props *props; 145 { 146 if (props == GA_NO_PROPS) 147 props = &props_default; 148 else if (props == GA_POSIX_PROPS) 149 props = &props_posix; 150 props->ga_oflags = 0; 151 /* 152 * GAF_POSIX may be used as an alias for the flags that currently 153 * define POSIX behavior. 154 */ 155 if (props->ga_flags == GAF_POSIX) 156 props->ga_flags = GAF_POSIX_DEFAULT; 157 158 return (props); 159 } 160 161 /* 162 * get flags until a non flag type argument is reached (old version) 163 */ 164 /* VARARGS3 */ 165 #ifdef PROTOTYPES 166 EXPORT int 167 getargs(int *pac, char *const **pav, const char *fmt, ...) 168 #else 169 EXPORT int 170 getargs(pac, pav, fmt, va_alist) 171 int *pac; 172 char **pav[]; 173 char *fmt; 174 va_dcl 175 #endif 176 { 177 va_list args; 178 int ret; 179 180 #ifdef PROTOTYPES 181 va_start(args, fmt); 182 #else 183 va_start(args); 184 #endif 185 ret = _getargs(pac, pav, (void *)fmt, SETARGS, GA_NO_PROPS, args); 186 va_end(args); 187 return (ret); 188 } 189 190 191 /* 192 * get flags until a non flag type argument is reached (list version) 193 */ 194 /* VARARGS4 */ 195 #ifdef PROTOTYPES 196 EXPORT int 197 getlargs(int *pac, char *const **pav, struct ga_props *props, const char *fmt, ...) 198 #else 199 EXPORT int 200 getlargs(pac, pav, props, fmt, va_alist) 201 int *pac; 202 char **pav[]; 203 struct ga_props *props; 204 char *fmt; 205 va_dcl 206 #endif 207 { 208 va_list args; 209 int ret; 210 211 #ifdef PROTOTYPES 212 va_start(args, fmt); 213 #else 214 va_start(args); 215 #endif 216 ret = _getargs(pac, pav, (void *)fmt, SETARGS, props, args); 217 va_end(args); 218 return (ret); 219 } 220 221 222 /* 223 * get flags until a non flag type argument is reached (vector version) 224 */ 225 EXPORT int 226 getvargs(pac, pav, props, vfmt) 227 int *pac; 228 char * const *pav[]; 229 struct ga_props *props; 230 struct ga_flags *vfmt; 231 { 232 return (_getargs(pac, pav, vfmt, SETARGS | ARGVECTOR, props, va_dummy)); 233 } 234 235 236 /* 237 * get all flags on the command line, do not stop on files (old version) 238 */ 239 /* VARARGS3 */ 240 #ifdef PROTOTYPES 241 EXPORT int 242 getallargs(int *pac, char *const **pav, const char *fmt, ...) 243 #else 244 EXPORT int 245 getallargs(pac, pav, fmt, va_alist) 246 int *pac; 247 char **pav[]; 248 char *fmt; 249 va_dcl 250 #endif 251 { 252 va_list args; 253 int ret; 254 255 #ifdef PROTOTYPES 256 va_start(args, fmt); 257 #else 258 va_start(args); 259 #endif 260 for (; ; (*pac)--, (*pav)++) { 261 if ((ret = _getargs(pac, pav, (void *)fmt, SETARGS, GA_NO_PROPS, args)) < NOTAFLAG) 262 break; 263 } 264 va_end(args); 265 return (ret); 266 } 267 268 269 /* 270 * get all flags on the command line, do not stop on files (list version) 271 */ 272 /* VARARGS4 */ 273 #ifdef PROTOTYPES 274 EXPORT int 275 getlallargs(int *pac, char *const **pav, struct ga_props *props, const char *fmt, ...) 276 #else 277 EXPORT int 278 getlallargs(pac, pav, props, fmt, va_alist) 279 int *pac; 280 char **pav[]; 281 struct ga_props *props; 282 char *fmt; 283 va_dcl 284 #endif 285 { 286 va_list args; 287 int ret; 288 289 #ifdef PROTOTYPES 290 va_start(args, fmt); 291 #else 292 va_start(args); 293 #endif 294 props = _getprops(props); 295 for (; ; (*pac)--, (*pav)++) { 296 if ((ret = _getargs(pac, pav, (void *)fmt, SETARGS, props, args)) < NOTAFLAG) 297 break; 298 /* 299 * The default is to parse all options on the command line and 300 * to let "--" only make the next argument a non-option. 301 */ 302 if (ret == FLAGDELIM && (props->ga_flags & GAF_DELIM_DASHDASH)) 303 break; 304 } 305 va_end(args); 306 return (ret); 307 } 308 309 310 /* 311 * get all flags on the command line, do not stop on files (vector version) 312 */ 313 EXPORT int 314 getvallargs(pac, pav, props, vfmt) 315 int *pac; 316 char * const *pav[]; 317 struct ga_props *props; 318 struct ga_flags *vfmt; 319 { 320 int ret; 321 322 props = _getprops(props); 323 for (; ; (*pac)--, (*pav)++) { 324 if ((ret = _getargs(pac, pav, vfmt, SETARGS | ARGVECTOR, props, va_dummy)) < NOTAFLAG) 325 break; 326 /* 327 * The default is to parse all options on the command line and 328 * to let "--" only make the next argument a non-option. 329 */ 330 if (ret == FLAGDELIM && (props->ga_flags & GAF_DELIM_DASHDASH)) 331 break; 332 } 333 return (ret); 334 } 335 336 337 /* 338 * get next non flag type argument (i.e. a file) (old version) 339 * getfiles() is a dry run getargs() 340 */ 341 EXPORT int 342 getfiles(pac, pav, fmt) 343 int *pac; 344 char *const *pav[]; 345 const char *fmt; 346 { 347 return (_getargs(pac, pav, (void *)fmt, SCANONLY, GA_NO_PROPS, va_dummy)); 348 } 349 350 351 /* 352 * get next non flag type argument (i.e. a file) (list version) 353 * getlfiles() is a dry run getlargs() 354 */ 355 EXPORT int 356 getlfiles(pac, pav, props, fmt) 357 int *pac; 358 char *const *pav[]; 359 struct ga_props *props; 360 const char *fmt; 361 { 362 return (_getargs(pac, pav, (void *)fmt, SCANONLY, props, va_dummy)); 363 } 364 365 366 /* 367 * get next non flag type argument (i.e. a file) (vector version) 368 * getvfiles() is a dry run getvargs() 369 */ 370 EXPORT int 371 getvfiles(pac, pav, props, vfmt) 372 int *pac; 373 char *const *pav[]; 374 struct ga_props *props; 375 struct ga_flags *vfmt; 376 { 377 return (_getargs(pac, pav, vfmt, SCANONLY | ARGVECTOR, props, va_dummy)); 378 } 379 380 381 /* 382 * check args until the next non flag type argmument is reached 383 * *pac is decremented, *pav is incremented so that the 384 * non flag type argument is at *pav[0] 385 * 386 * return code: 387 * +2 FLAGDELIM "--" stopped flag processing 388 * +1 NOTAFLAG not a flag type argument (is a file) 389 * 0 NOARGS no more args 390 * -1 BADFLAG a non-matching flag type argument 391 * -2 BADFMT bad syntax in format string 392 */ 393 /* LOCAL int */ 394 EXPORT int 395 _getargs(pac, pav, vfmt, flags, props, args) 396 register int *pac; 397 register char *const **pav; 398 void *vfmt; 399 int flags; 400 struct ga_props *props; 401 va_list args; 402 { 403 const char *argp; 404 int ret; 405 406 407 props = _getprops(props); 408 409 if (props->ga_flags & GAF_NO_EQUAL) 410 flags |= NOEQUAL; 411 412 for (; *pac > 0; (*pac)--, (*pav)++) { 413 argp = **pav; 414 415 ret = dofile(pac, pav, &argp, props); 416 417 if (ret != NOTAFILE) 418 return (ret); 419 420 ret = doflag(pac, pav, argp, vfmt, flags, args); 421 422 if (ret != NOTAFLAG) 423 return (ret); 424 } 425 return (NOARGS); 426 } 427 428 429 /* 430 * check if *pargp is a file type argument 431 */ 432 LOCAL int 433 dofile(pac, pav, pargp, props) 434 register int *pac; 435 register char *const **pav; 436 const char **pargp; 437 struct ga_props *props; 438 { 439 register const char *argp = *pargp; 440 441 442 if (argp[0] == '-') { 443 /* 444 * "-" is a special non flag type argument 445 * that usually means take stdin instead of a named file 446 */ 447 if (argp[1] == '\0') 448 return (NOTAFLAG); 449 /* 450 * "--" is a prefix to take the next argument 451 * as non flag type argument 452 * NOTE: POSIX requires "--" to indicate the end of the 453 * flags on the command line. Programs that like to be 454 * 100% POSIX compliant call only get[lv]args() once 455 * and then process the list of files from cav[0]. 456 */ 457 if (argp[1] == '-' && argp[2] == '\0') { 458 if (--(*pac) > 0) { 459 (*pav)++; 460 return (FLAGDELIM); 461 } else { 462 return (NOARGS); 463 } 464 } 465 } 466 467 /* 468 * Now check if it may be flag type argument at all. 469 * Flag type arguments begin with a '-', a '+' or contain a '=' 470 * i.e. -flag +flag or flag= 471 * The behavior here may be controlled by props->ga_flags to 472 * allow getargs() to e.g. behave fully POSIX compliant. 473 */ 474 if (argp[0] != '-') { 475 if (argp[0] == '+' && (props->ga_flags & GAF_NO_PLUS) == 0) 476 return (NOTAFILE); /* This is a flag type arg */ 477 478 /* 479 * If 'flag=value' is not allowed at all, speed things up 480 * and do not call checkeql() to check for '='. 481 */ 482 if (props->ga_flags & GAF_NO_EQUAL) 483 return (NOTAFLAG); 484 if (checkeql(argp) && (props->ga_flags & GAF_NEED_DASH) == 0) 485 return (NOTAFILE); /* This is a flag type arg */ 486 return (NOTAFLAG); 487 } 488 return (NOTAFILE); /* This is a flag type arg */ 489 } 490 491 492 /* 493 * compare argp with the format string 494 * if a match is found store the result a la scanf in one of the 495 * arguments pointed to in the va_list 496 * 497 * If (flags & SETARGS) == 0, only check arguments for getfiles() 498 * In case that (flags & SETARGS) == 0 or that (flags & ARGVECTOR) != 0, 499 * va_list may be a dummy argument. 500 */ 501 LOCAL int 502 doflag(pac, pav, argp, vfmt, flags, oargs) 503 int *pac; 504 char *const **pav; 505 register const char *argp; 506 void *vfmt; 507 int flags; 508 va_list oargs; 509 { 510 register const char *fmt = (const char *)vfmt; 511 struct ga_flags *flagp = vfmt; 512 const char *fmtp; 513 long val; 514 Llong llval; 515 int singlecharflag = 0; 516 BOOL isspec; 517 BOOL hasdash = FALSE; 518 BOOL doubledash = FALSE; 519 BOOL haseql = checkeql(argp); 520 const char *sargp; 521 const char *sfmt; 522 va_list args; 523 char *const *spav = *pav; 524 int spac = *pac; 525 void *curarg = (void *)0; 526 527 sfmt = fmt; 528 /* 529 * flags beginning with '-' don't have to include the '-' in 530 * the format string. 531 * flags beginning with '+' have to include it in the format string. 532 */ 533 if (argp[0] == '-') { 534 argp++; 535 hasdash = TRUE; 536 /* 537 * Implement legacy support for --longopt 538 * If we find a double dash, we do not look for combinations 539 * of boolean single char flags. 540 */ 541 if (argp[0] == '-') { 542 argp++; 543 doubledash = TRUE; 544 /* 545 * Allow -- only for long options. 546 */ 547 if (argp[1] == '\0') { 548 return (BADFLAG); 549 } 550 } 551 } 552 sargp = argp; 553 554 /* 555 * Initialize 'args' to the start of the argument list. 556 * I don't know any portable way to copy an arbitrary 557 * C object so I use a system-specific routine 558 * (probably a macro) from stdarg.h. (Remember that 559 * if va_list is an array, 'args' will be a pointer 560 * and '&args' won't be what I would need for memcpy.) 561 * It is a system requirement for SVr4 compatibility 562 * to be able to do this assgignement. If your system 563 * defines va_list to be an array but does not define 564 * va_copy() you are lost. 565 * This is needed to make sure, that 'oargs' will not 566 * be clobbered. 567 */ 568 va_copy(args, oargs); 569 570 if (flags & ARGVECTOR) { 571 sfmt = fmt = flagp->ga_format; 572 if (fmt == NULL) 573 sfmt = fmt = ""; 574 if (flags & SETARGS) 575 curarg = flagp->ga_arg; 576 } else if (flags & SETARGS) { 577 curarg = va_arg(args, void *); 578 } 579 /* 580 * check if the first flag in format string is a singlechar flag 581 */ 582 again: 583 if (fmt[0] != '\0' && 584 (fmt[1] == ',' || fmt[1] == '+' || 585 fmt[1] == '~' || fmt[1] == '%' || fmt[1] == '\0')) 586 singlecharflag++; 587 /* 588 * check the whole format string for a match 589 */ 590 for (;;) { 591 for (; *fmt; fmt++, argp++) { 592 if (*fmt == '\\') { 593 /* 594 * Allow "#?*&+" to appear inside a flag. 595 * NOTE: they must be escaped by '\\' only 596 * inside the the format string. 597 */ 598 fmt++; 599 isspec = FALSE; 600 } else { 601 isspec = isfmtspec(*fmt); 602 } 603 /* 604 * If isspec is TRUE, the arg beeing checked starts 605 * like a valid flag. Argp now points to the rest. 606 */ 607 if (isspec) { 608 /* 609 * If *argp is '+' and we are on the 610 * beginning of the arg that is currently 611 * checked, this cannot be an inc type flag. 612 */ 613 if (*argp == '+' && argp == sargp) 614 continue; 615 /* 616 * skip over to arg of flag 617 */ 618 if (*argp == '=') { 619 if (flags & NOEQUAL) 620 return (BADFLAG); 621 argp++; 622 } else if (*argp != '\0' && haseql) { 623 /* 624 * Flag and arg are not separated by a 625 * space. 626 * Check here for: 627 * xxxxx=yyyyy match on '&' 628 * Checked before: 629 * abc=yyyyy match on 'abc&' 630 * or 'abc*' 631 * or 'abc#' 632 * We come here if 'argp' starts with 633 * the same sequence as a valid flag 634 * and contains an equal sign. 635 * We have tested before if the text 636 * before 'argp' matches exactly. 637 * At this point we have no exact match 638 * and we only allow to match 639 * the special pattern '&'. 640 * We need this e.g. for 'make'. 641 * We allow any flag type argument to 642 * match the format string "&" to set 643 * up a function that handles all odd 644 * stuff that getargs will not grok. 645 * In addition, to allow getargs to be 646 * used for CPP type flags we allow to 647 * match -Dabc=xyz on 'D&'. Note that 648 * Dabc=xyz will not match 'D&'. 649 */ 650 if ((!hasdash && argp != sargp) || *fmt != '&') 651 goto nextarg; 652 } 653 654 /* 655 * The format string 'f* ' may be used 656 * to disallow -ffoo for f* 657 * 658 * The same behavior is implemented for 659 * 'f# '. 'f? ' and 'f& '. 660 */ 661 if (!haseql && *argp != '\0' && 662 (fmt[0] == '*' || fmt[0] == '#' || 663 fmt[0] == '?' || fmt[0] == '&') && 664 fmt[1] == ' ') { 665 goto nextarg; 666 } 667 668 /* 669 * *arpp == '\0' || !haseql 670 * We come here if 'argp' starts with 671 * the same sequence as a valid flag. 672 * This will match on the following args: 673 * -farg match on 'f*' 674 * -f12 match on 'f#' 675 * +12 match on '+#' 676 * -12 match on '#' 677 * and all args that are separated from 678 * their flags. 679 * In the switch statement below, we check 680 * if the text after 'argp' (if *argp != 0) or 681 * the next arg is a valid arg for this flag. 682 */ 683 break; 684 } else if (*fmt == *argp) { 685 if (argp[1] == '\0' && 686 (fmt[1] == '\0' || fmt[1] == ',')) { 687 688 if (flags & SETARGS) 689 *((int *)curarg) = TRUE; 690 691 692 return (checkfmt(fmt)); /* XXX */ 693 } 694 } else { 695 /* 696 * skip over to next format identifier 697 * & reset arg pointer 698 */ 699 nextarg: 700 while (*fmt != ',' && *fmt != '\0') { 701 /* function has extra arg on stack */ 702 if ((*fmt == '&' || *fmt == '~') && 703 (flags & (SETARGS|ARGVECTOR)) == SETARGS) { 704 curarg = va_arg(args, void *); 705 } 706 fmt++; 707 } 708 argp = sargp; 709 break; 710 } 711 } 712 switch (*fmt) { 713 714 case '\0': 715 /* 716 * Boolean type has been tested before. 717 */ 718 if (flags & ARGVECTOR) { 719 if (flagp[1].ga_format != NULL) { 720 flagp++; 721 sfmt = fmt = flagp->ga_format; 722 if (flags & SETARGS) 723 curarg = flagp->ga_arg; 724 argp = sargp; 725 goto again; 726 } 727 } 728 if (singlecharflag && !doubledash && 729 (val = dosflags(sargp, vfmt, pac, pav, 730 flags & ~SETARGS, 731 va_dummy)) == BADFLAG) { 732 return (val); 733 } 734 if (singlecharflag && !doubledash && 735 (val = dosflags(sargp, vfmt, pac, pav, 736 flags, 737 oargs)) != BADFLAG) { 738 return (val); 739 } 740 return (BADFLAG); 741 742 case ',': 743 fmt++; 744 if (fmt[0] == '\0') /* Should we allow "a,b,c,"? */ 745 return (BADFMT); 746 if (fmt[1] == ',' || fmt[1] == '+' || fmt[1] == '\0') 747 singlecharflag++; 748 if ((flags & (SETARGS|ARGVECTOR)) == SETARGS) 749 curarg = va_arg(args, void *); 750 continue; 751 752 case '*': 753 if (*argp == '\0' && fmt[1] != '_') { 754 if (*pac > 1) { 755 (*pac)--; 756 (*pav)++; 757 argp = **pav; 758 } else { 759 return (BADFLAG); 760 } 761 } 762 if (fmt[1] == '_') /* To disallow -f foo for f* */ 763 fmt++; 764 else if (fmt[1] == ' ') /* To disallow -ffoo for f* */ 765 fmt++; 766 767 if (flags & SETARGS) 768 *((const char **)curarg) = argp; 769 770 771 return (checkfmt(fmt)); 772 773 case '?': 774 if (*argp == '\0' && fmt[1] != '_') { 775 if (*pac > 1) { 776 (*pac)--; 777 (*pav)++; 778 argp = **pav; 779 } else { 780 return (BADFLAG); 781 } 782 } 783 if (fmt[1] == '_') /* To disallow -f c for f? */ 784 fmt++; 785 else if (fmt[1] == ' ') /* To disallow -fc for f? */ 786 fmt++; 787 788 /* 789 * Allow -f '' to specify a nul character. 790 * If more than one char arg, it 791 * cannot be a character argument. 792 */ 793 if (argp[0] != '\0' && argp[1] != '\0') 794 goto nextchance; 795 796 if (flags & SETARGS) 797 *((char *)curarg) = *argp; 798 799 800 return (checkfmt(fmt)); 801 802 case '+': 803 /* 804 * inc type is similar to boolean, 805 * there is no arg in argp to convert. 806 */ 807 if (*argp != '\0') 808 goto nextchance; 809 /* 810 * If *fmt is '+' and we are on the beginning 811 * of the format desciptor that is currently 812 * checked, this cannot be an inc type flag. 813 */ 814 if (fmt == sfmt || fmt[-1] == ',') 815 goto nextchance; 816 817 fmtp = fmt; 818 if (fmt[1] == 'l' || fmt[1] == 'L') { 819 if (fmt[2] == 'l' || fmt[2] == 'L') { 820 if (flags & SETARGS) 821 *((Llong *)curarg) += 1; 822 fmt += 2; 823 } else { 824 if (flags & SETARGS) 825 *((long *)curarg) += 1; 826 fmt++; 827 } 828 } else if (fmt[1] == 's' || fmt[1] == 'S') { 829 if (flags & SETARGS) 830 *((short *)curarg) += 1; 831 fmt++; 832 } else if (fmt[1] == 'c' || fmt[1] == 'C') { 833 if (flags & SETARGS) 834 *((char *)curarg) += 1; 835 fmt++; 836 } else { 837 if (fmt[1] == 'i' || fmt[1] == 'I') 838 fmt++; 839 if (flags & SETARGS) 840 *((int *)curarg) += 1; 841 } 842 843 844 return (checkfmt(fmt)); 845 846 case '%': 847 /* 848 * inc type is similar to boolean, 849 * there is no arg in argp to convert. 850 */ 851 if (*argp != '\0') 852 goto nextchance; 853 854 fmt++; 855 if (*fmt == '1') 856 val = TRUE; 857 else if (*fmt == '0') 858 val = FALSE; 859 else 860 goto nextchance; 861 862 fmtp = fmt; 863 llval = (Llong)val; 864 if (fmt[1] == 'l' || fmt[1] == 'L') { 865 if (fmt[2] == 'l' || fmt[2] == 'L') { 866 if (flags & SETARGS) 867 *((Llong *)curarg) = llval; 868 fmt += 2; 869 } else { 870 if (flags & SETARGS) 871 *((long *)curarg) = val; 872 fmt++; 873 } 874 } else if (fmt[1] == 's' || fmt[1] == 'S') { 875 if (flags & SETARGS) 876 *((short *)curarg) = val; 877 fmt++; 878 } else if (fmt[1] == 'c' || fmt[1] == 'C') { 879 if (flags & SETARGS) 880 *((char *)curarg) = val; 881 fmt++; 882 } else { 883 if (fmt[1] == 'i' || fmt[1] == 'I') 884 fmt++; 885 if (flags & SETARGS) 886 *((int *)curarg) = val; 887 } 888 889 890 return (checkfmt(fmt)); 891 892 case '#': 893 if (*argp == '\0' && fmt[1] != '_') { 894 if (*pac > 1) { 895 (*pac)--; 896 (*pav)++; 897 argp = **pav; 898 } else { 899 return (BADFLAG); 900 } 901 } 902 if (fmt[1] == '_') /* To disallow -f 123 for f# */ 903 fmt++; 904 else if (fmt[1] == ' ') /* To disallow -f123 for f# */ 905 fmt++; 906 907 if (*astoll(argp, &llval) != '\0') { 908 /* 909 * arg is not a valid number! 910 * go to next format in the format string 911 * and check if arg matches any other type 912 * in the format specs. 913 */ 914 nextchance: 915 while (*fmt != ',' && *fmt != '\0') { 916 if ((*fmt == '&' || *fmt == '~') && 917 (flags & (SETARGS|ARGVECTOR)) == SETARGS) { 918 curarg = va_arg(args, void *); 919 } 920 fmt++; 921 } 922 argp = sargp; 923 *pac = spac; 924 *pav = spav; 925 continue; 926 } 927 fmtp = fmt; 928 val = (long)llval; 929 if (fmt[1] == 'l' || fmt[1] == 'L') { 930 if (fmt[2] == 'l' || fmt[2] == 'L') { 931 if (flags & SETARGS) 932 *((Llong *)curarg) = llval; 933 fmt += 2; 934 } else { 935 if (flags & SETARGS) 936 *((long *)curarg) = val; 937 fmt++; 938 } 939 } else if (fmt[1] == 's' || fmt[1] == 'S') { 940 if (flags & SETARGS) 941 *((short *)curarg) = (short)val; 942 fmt++; 943 } else if (fmt[1] == 'c' || fmt[1] == 'C') { 944 if (flags & SETARGS) 945 *((char *)curarg) = (char)val; 946 fmt++; 947 } else { 948 if (fmt[1] == 'i' || fmt[1] == 'I') 949 fmt++; 950 if (flags & SETARGS) 951 *((int *)curarg) = (int)val; 952 } 953 954 return (checkfmt(fmt)); 955 956 case '~': 957 if (*argp != '\0') 958 goto nextchance; 959 if (haseql) { 960 return (BADFLAG); 961 } 962 goto callfunc; 963 964 case '&': 965 if (*argp == '\0' && fmt[1] != '_') { 966 if (*pac > 1) { 967 (*pac)--; 968 (*pav)++; 969 argp = **pav; 970 } else { 971 return (BADFLAG); 972 } 973 } 974 callfunc: 975 976 if (*fmt == '&' && fmt[1] == '_') /* To disallow -f foo for f& */ 977 fmt++; 978 else if (fmt[1] == ' ') /* To disallow -ffoo for f& */ 979 fmt++; 980 if ((val = checkfmt(fmt)) != NOTAFLAG) 981 return (val); 982 983 fmtp = sargp; 984 if (hasdash) 985 fmtp--; 986 if (doubledash) 987 fmtp--; 988 if ((flags & (SETARGS|ARGVECTOR)) == (SETARGS|ARGVECTOR)) { 989 int ret; 990 991 if (flagp->ga_funcp == NULL) 992 return (BADFMT); 993 994 ret = ((*flagp->ga_funcp) (argp, flagp->ga_arg, 995 pac, pav, fmtp)); 996 if (ret != NOTAFILE) 997 return (ret); 998 fmt++; 999 } else 1000 if (flags & SETARGS) { 1001 int ret; 1002 void *funarg = va_arg(args, void *); 1003 1004 if (curarg == NULL) 1005 return (BADFMT); 1006 ret = ((*(getpargfun)curarg) (argp, funarg, 1007 pac, pav, fmtp)); 1008 if (ret != NOTAFILE) 1009 return (ret); 1010 fmt++; 1011 } else { 1012 return (val); 1013 } 1014 /* 1015 * Called function returns NOTAFILE: try next format. 1016 */ 1017 } 1018 } 1019 } 1020 1021 1022 /* 1023 * parse args for combined single char flags 1024 */ 1025 typedef struct { 1026 void *curarg; /* The pointer to the arg to modify */ 1027 void *curfun; /* The pointer to the function to call */ 1028 char c; /* The single char flag character */ 1029 char type; /* The type of the single char flag */ 1030 char fmt; /* The format type of the single char flag */ 1031 char val; /* The value to assign for BOOL flags */ 1032 } sflags; 1033 1034 LOCAL int 1035 dosflags(argp, vfmt, pac, pav, flags, oargs) 1036 register const char *argp; 1037 void *vfmt; 1038 int *pac; 1039 char *const **pav; 1040 int flags; 1041 va_list oargs; 1042 { 1043 register const char *fmt = (const char *)vfmt; 1044 struct ga_flags *flagp = vfmt; 1045 #define MAXSF 64 1046 sflags sf[MAXSF]; 1047 char fl[256]; 1048 va_list args; 1049 register sflags *rsf = sf; 1050 register int nsf = 0; 1051 register const char *p = argp; 1052 register int i; 1053 register void *curarg = (void *)0; 1054 getpargfun curfun = 0; 1055 char type; 1056 1057 /* 1058 * Initialize 'args' to the start of the argument list. 1059 * I don't know any portable way to copy an arbitrary 1060 * C object so I use a system-specific routine 1061 * (probably a macro) from stdarg.h. (Remember that 1062 * if va_list is an array, 'args' will be a pointer 1063 * and '&args' won't be what I would need for memcpy.) 1064 * It is a system requirement for SVr4 compatibility 1065 * to be able to do this assgignement. If your system 1066 * defines va_list to be an array but does not define 1067 * va_copy() you are lost. 1068 * This is needed to make sure, that 'oargs' will not 1069 * be clobbered. 1070 */ 1071 va_copy(args, oargs); 1072 1073 if (flags & ARGVECTOR) { 1074 fmt = flagp->ga_format; 1075 if (fmt == NULL) 1076 fmt = ""; 1077 if (flags & SETARGS) { 1078 curarg = flagp->ga_arg; 1079 curfun = flagp->ga_funcp; 1080 } 1081 } else if (flags & SETARGS) { 1082 /* 1083 * We set curfun to curarg. We later get the real 1084 * curarg in case that we see a function callback 1085 * but we need curfun first in this case. 1086 */ 1087 curarg = va_arg(args, void *); 1088 curfun = (getpargfun)curarg; 1089 } 1090 1091 for (i = 0; i < sizeof (fl); i++) { 1092 fl[i] = 0; 1093 } 1094 while (*p) { 1095 for (i = 0; i < nsf; i++) { 1096 if (rsf[i].c == *p) 1097 break; 1098 } 1099 if (i >= MAXSF) { 1100 va_end(args); 1101 return (BADFLAG); 1102 } 1103 if (i == nsf) { 1104 rsf[i].curarg = (void *)0; 1105 rsf[i].curfun = (void *)0; 1106 rsf[i].c = *p; 1107 rsf[i].type = (char)-1; 1108 rsf[i].fmt = '\0'; 1109 rsf[i].val = (char)TRUE; 1110 nsf++; 1111 } 1112 fl[*p & 0xFF] = i; 1113 p++; 1114 } 1115 1116 again: 1117 while (*fmt) { 1118 if (!isfmtspec(*fmt) && 1119 (fmt[1] == ',' || fmt[1] == '+' || 1120 fmt[1] == '~' || fmt[1] == '%' || fmt[1] == '\0') && 1121 strchr(argp, *fmt)) { 1122 for (i = 0; i < nsf; i++) { 1123 if (rsf[i].c == *fmt) { 1124 if (fmt[1] == '+') { 1125 rsf[i].fmt = '+'; 1126 fmt++; 1127 if (fmt[1] == ',' || 1128 fmt[1] == '\0') { 1129 rsf[i].type = 'i'; 1130 } else if ((fmt[1] == 'l' || 1131 fmt[1] == 'L') && 1132 (fmt[2] == 'l' || 1133 fmt[2] == 'L')) { 1134 /* 1135 * Type 'Q'uad (ll) 1136 */ 1137 rsf[i].type = 'Q'; 1138 fmt++; 1139 } else { 1140 /* 1141 * Type 'l','i','s','c' 1142 */ 1143 rsf[i].type = fmt[1]; 1144 } 1145 } else if (fmt[1] == '%') { 1146 fmt++; 1147 rsf[i].fmt = '%'; 1148 if (fmt[1] == '0') 1149 rsf[i].val = (char)FALSE; 1150 else if (fmt[1] == '1') 1151 rsf[i].val = (char)TRUE; 1152 fmt++; 1153 if (fmt[1] == ',' || 1154 fmt[1] == '\0') { 1155 rsf[i].type = 'i'; 1156 } else if ((fmt[1] == 'l' || 1157 fmt[1] == 'L') && 1158 (fmt[2] == 'l' || 1159 fmt[2] == 'L')) { 1160 /* 1161 * Type 'Q'uad (ll) 1162 */ 1163 rsf[i].type = 'Q'; 1164 fmt++; 1165 } else { 1166 /* 1167 * Type 'l','i','s','c' 1168 */ 1169 rsf[i].type = fmt[1]; 1170 } 1171 } else if (fmt[1] == '~') { 1172 /* 1173 * Let fmt point to ',' to 1174 * prevent to fetch the 1175 * func arg twice. 1176 */ 1177 fmt += 2; 1178 rsf[i].fmt = '~'; 1179 rsf[i].type = '~'; 1180 rsf[i].curfun = (void *)curfun; 1181 if ((flags & (SETARGS|ARGVECTOR)) == SETARGS) 1182 curarg = va_arg(args, void *); 1183 } else { 1184 /* 1185 * ',' or '\0' for BOOL 1186 */ 1187 rsf[i].type = fmt[1]; 1188 } 1189 rsf[i].curarg = curarg; 1190 break; 1191 } 1192 } 1193 } 1194 while (*fmt != ',' && *fmt != '\0') { 1195 /* 1196 * function has extra arg on stack. The code above 1197 * prevents us from fetching this arg twice. 1198 */ 1199 if ((*fmt == '&' || *fmt == '~') && 1200 (flags & (SETARGS|ARGVECTOR)) == SETARGS) { 1201 curarg = va_arg(args, void *); 1202 } 1203 fmt++; 1204 } 1205 if (*fmt != '\0') 1206 fmt++; 1207 else 1208 break; 1209 1210 if ((flags & (SETARGS|ARGVECTOR)) == SETARGS) { 1211 /* 1212 * We set curfun to curarg. We later get the real 1213 * curarg in case that we see a function callback 1214 * but we need curfun first in this case. 1215 */ 1216 curarg = va_arg(args, void *); 1217 curfun = (getpargfun)curarg; 1218 } 1219 } 1220 if ((flags & ARGVECTOR) && flagp[1].ga_format != NULL) { 1221 flagp++; 1222 fmt = flagp->ga_format; 1223 if (flags & SETARGS) { 1224 curarg = flagp->ga_arg; 1225 curfun = flagp->ga_funcp; 1226 } 1227 goto again; 1228 } 1229 1230 for (p = argp; *p; p++) { 1231 char tfmt; 1232 1233 i = fl[*p & 0xFF]; 1234 tfmt = rsf[i].fmt; 1235 type = rsf[i].type; 1236 if (type == (char)-1) { 1237 return (BADFLAG); 1238 } 1239 1240 if ((flags & SETARGS) && 1241 (rsf[i].curfun || rsf[i].curarg)) { 1242 if (type == ',' || type == '\0') { 1243 *((int *)rsf[i].curarg) = TRUE; 1244 } else if (type == 'i' || type == 'I') { 1245 if (tfmt == '+') 1246 *((int *)rsf[i].curarg) += 1; 1247 else 1248 *((int *)rsf[i].curarg) = rsf[i].val; 1249 } else if (type == 'l' || type == 'L') { 1250 if (tfmt == '+') 1251 *((long *)rsf[i].curarg) += 1; 1252 else 1253 *((long *)rsf[i].curarg) = rsf[i].val; 1254 } else if (type == 'Q') { 1255 if (tfmt == '+') 1256 *((Llong *)rsf[i].curarg) += 1; 1257 else 1258 *((Llong *)rsf[i].curarg) = rsf[i].val; 1259 } else if (type == 's' || type == 'S') { 1260 if (tfmt == '+') 1261 *((short *)rsf[i].curarg) += 1; 1262 else 1263 *((short *)rsf[i].curarg) = rsf[i].val; 1264 } else if (type == 'c' || type == 'C') { 1265 if (tfmt == '+') 1266 *((char *)rsf[i].curarg) += 1; 1267 else 1268 *((char *)rsf[i].curarg) = rsf[i].val; 1269 } else if (type == '~') { 1270 int ret; 1271 char cfmt[3]; 1272 1273 cfmt[0] = '-'; 1274 cfmt[1] = rsf[i].c; 1275 cfmt[2] = '\0'; 1276 1277 if (rsf[i].curfun == NULL) 1278 return (BADFMT); 1279 ret = ((*(getpargfun)rsf[i].curfun) ("", 1280 rsf[i].curarg, 1281 pac, pav, cfmt)); 1282 return (ret); 1283 } else { 1284 return (BADFLAG); 1285 } 1286 } 1287 } 1288 return (NOTAFLAG); 1289 } 1290 1291 /* 1292 * If the next format character is a comma or the string delimiter, 1293 * there are no invalid format specifiers. Return success. 1294 * Otherwise raise the getarg_bad_format condition. 1295 */ 1296 LOCAL int 1297 checkfmt(fmt) 1298 const char *fmt; 1299 { 1300 char c; 1301 1302 c = *(++fmt); /* non constant expression */ 1303 1304 1305 if (c == ',' || c == '\0') { 1306 return (NOTAFLAG); 1307 } else { 1308 raisecond("getarg_bad_format", (long)fmt); 1309 return (BADFMT); 1310 } 1311 } 1312 1313 /* 1314 * Parse the string as long as valid characters can be found. 1315 * Valid flag identifiers are chosen from the set of 1316 * alphanumeric characters, '-' and '_'. 1317 * If the next character is an equal sign the string 1318 * contains a valid flag identifier. 1319 */ 1320 LOCAL int 1321 checkeql(str) 1322 register const char *str; 1323 { 1324 register unsigned char c; 1325 1326 for (c = (unsigned char)*str; 1327 isalnum(c) || c == '_' || c == '-' || c == '+'; 1328 c = *++str) 1329 /* LINTED */ 1330 ; 1331 return (c == '='); 1332 } 1333 1334 EXPORT char * 1335 getargerror(err) 1336 int err; 1337 { 1338 if (err < RETMIN || err > RETMAX) 1339 return ("Illegal arg error"); 1340 return (RNAME(err)); 1341 } 1342