1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1985-2012 AT&T Intellectual Property * 5 * and is licensed under the * 6 * Eclipse Public License, Version 1.0 * 7 * by AT&T Intellectual Property * 8 * * 9 * A copy of the License is available at * 10 * http://www.eclipse.org/org/documents/epl-v10.html * 11 * (with md5 checksum b35adb5213ca9657e911e9befb180842) * 12 * * 13 * Information and Software Systems Research * 14 * AT&T Research * 15 * Florham Park NJ * 16 * * 17 * Glenn Fowler <gsf@research.att.com> * 18 * David Korn <dgk@research.att.com> * 19 * Phong Vo <kpv@research.att.com> * 20 * * 21 ***********************************************************************/ 22 #pragma prototyped 23 /* 24 * Glenn Fowler 25 * AT&T Research 26 * 27 * command line option parser and usage formatter 28 * its a monster but its all in one place 29 * widen your window while you're at it 30 */ 31 32 #include <optlib.h> 33 #include <debug.h> 34 #include <ccode.h> 35 #include <ctype.h> 36 #include <errno.h> 37 38 #define KEEP "*[A-Za-z][A-Za-z]*" 39 #define OMIT "*@(\\[[-+]*\\?*\\]|\\@\\(#\\)|Copyright \\(c\\)|\\$\\I\\d\\: )*" 40 41 #define GO '{' /* group nest open */ 42 #define OG '}' /* group nest close */ 43 44 #define OPT_WIDTH 80 /* default help text width */ 45 #define OPT_MARGIN 10 /* default help text margin */ 46 #define OPT_USAGE 7 /* usage continuation indent */ 47 48 #define OPT_flag 0x001 /* flag ( 0 or 1 ) */ 49 #define OPT_hidden 0x002 /* remaining are hidden */ 50 #define OPT_ignorecase 0x004 /* arg match ignores case */ 51 #define OPT_invert 0x008 /* flag inverts long sense */ 52 #define OPT_listof 0x010 /* arg is ' ' or ',' list */ 53 #define OPT_number 0x020 /* arg is strtonll() number */ 54 #define OPT_oneof 0x040 /* arg may be set once */ 55 #define OPT_optional 0x080 /* arg is optional */ 56 #define OPT_string 0x100 /* arg is string */ 57 58 #define OPT_preformat 0001 /* output preformat string */ 59 #define OPT_proprietary 0002 /* proprietary docs */ 60 61 #define OPT_TYPE (OPT_flag|OPT_number|OPT_string) 62 63 #define STYLE_posix 0 /* posix getopt usage */ 64 #define STYLE_short 1 /* [default] short usage */ 65 #define STYLE_long 2 /* long usage */ 66 #define STYLE_match 3 /* long description of matches */ 67 #define STYLE_options 4 /* short and long descriptions */ 68 #define STYLE_man 5 /* pretty details */ 69 #define STYLE_html 6 /* html details */ 70 #define STYLE_nroff 7 /* nroff details */ 71 #define STYLE_api 8 /* program details */ 72 #define STYLE_keys 9 /* translation key strings */ 73 #define STYLE_usage 10 /* escaped usage string */ 74 75 #define FONT_BOLD 1 76 #define FONT_ITALIC 2 77 #define FONT_LITERAL 4 78 79 #define HELP_head 0x01 80 #define HELP_index 0x02 81 82 #define TAG_NONE 0 83 #define TAG_DIV 1 84 #define TAG_DL 2 85 86 #define SEP(c) ((c)=='-'||(c)=='_') 87 88 typedef struct Attr_s 89 { 90 const char* name; 91 int flag; 92 } Attr_t; 93 94 typedef struct Help_s 95 { 96 const char* match; /* builtin help match name */ 97 const char* name; /* builtin help name */ 98 int style; /* STYLE_* */ 99 const char* text; /* --? text */ 100 unsigned int size; /* strlen text */ 101 } Help_t; 102 103 typedef struct Font_s 104 { 105 const char* html[2]; 106 const char* nroff[2]; 107 const char* term[2]; 108 } Font_t; 109 110 typedef struct List_s 111 { 112 int type; /* { - + : } */ 113 const char* name; /* list name */ 114 const char* text; /* help text */ 115 } List_t; 116 117 typedef struct Msg_s 118 { 119 const char* text; /* default message text */ 120 Dtlink_t link; /* cdt link */ 121 } Msg_t; 122 123 typedef struct Save_s 124 { 125 Dtlink_t link; /* cdt link */ 126 char text[1]; /* saved text text */ 127 } Save_t; 128 129 typedef struct Push_s 130 { 131 struct Push_s* next; /* next string */ 132 char* ob; /* next char in old string */ 133 char* oe; /* end of old string */ 134 char* nb; /* next char in new string */ 135 char* ne; /* end of new string */ 136 int ch; /* localize() translation */ 137 } Push_t; 138 139 typedef struct Tag_s 140 { 141 unsigned char level; /* indent level */ 142 unsigned char id; /* TAG_* id */ 143 } Tag_t; 144 145 typedef struct Indent_s 146 { 147 int stop; /* tab column position */ 148 } Indent_t; 149 150 static Indent_t indent[] = 151 { 152 0,2, 4,10, 12,18, 20,26, 28,34, 36,42, 44,50, 0,0 153 }; 154 155 static const char* end[] = 156 { 157 "", "</DIV>\n", "</DL>\n" 158 }; 159 160 static const char term_off[] = {CC_esc,'[','0','m',0}; 161 static const char term_B_on[] = {CC_esc,'[','1','m',0}; 162 static const char term_I_on[] = {CC_esc,'[','1',';','4','m',0}; 163 164 static const Font_t fonts[] = 165 { 166 "", "", "", "", "", "", 167 "</B>", "<B>", "\\fP", "\\fB", &term_off[0], &term_B_on[0], 168 "</I>", "<I>", "\\fP", "\\fI", &term_off[0], &term_I_on[0], 169 "", "", "", "", "", "", 170 "</TT>","<TT>","\\fP", "\\f5", "", "", 171 }; 172 173 static char native[] = ""; 174 175 static unsigned char map[UCHAR_MAX]; 176 177 static Optstate_t state; 178 179 #if !_PACKAGE_astsa 180 181 #define ID ast.id 182 183 #define C(s) ERROR_catalog(s) 184 #define D(s) (state.msgdict && dtmatch(state.msgdict, (s))) 185 #define T(i,c,m) (X(c)?translate(i,c,C(m)):(m)) 186 #define X(c) (ERROR_translating()&&(c)!=native) 187 #define Z(x) C(x),sizeof(x)-1 188 189 /* 190 * translate with C_LC_MESSAGES_libast[] check 191 */ 192 193 static char* 194 translate(const char* cmd, const char* cat, const char* msg) 195 { 196 if (!X(cat)) 197 return (char*)msg; 198 if (cat != (const char*)ID && D(msg)) 199 cat = (const char*)ID; 200 return errorx(NiL, cmd, cat, msg); 201 } 202 203 #else 204 205 static char ID[] = "ast"; 206 207 #define C(s) s 208 #define D(s) (state.msgdict && dtmatch(state.msgdict, (s))) 209 #define T(i,c,m) m 210 #define X(c) 0 211 #define Z(x) C(x),sizeof(x)-1 212 213 #endif 214 215 static const List_t help_head[] = 216 { 217 '-', 0, 218 0, 219 '+', C("NAME"), 220 C("options available to all \bast\b commands"), 221 '+', C("DESCRIPTION"), 222 C("\b-?\b and \b--?\b* options are the same \ 223 for all \bast\b commands. For any \aitem\a below, if \b--\b\aitem\a is not \ 224 supported by a given command then it is equivalent to \b--\?\?\b\aitem\a. The \ 225 \b--\?\?\b form should be used for portability. All output is written to the \ 226 standard error."), 227 }; 228 229 static const Help_t styles[] = 230 { 231 C("about"), "-", STYLE_match, 232 Z("List all implementation info."), 233 C("api"), "?api", STYLE_api, 234 Z("List detailed info in program readable form."), 235 C("help"), "", -1, 236 Z("List detailed help option info."), 237 C("html"), "?html", STYLE_html, 238 Z("List detailed info in html."), 239 C("keys"), "?keys", STYLE_keys, 240 Z("List the usage translation key strings with C style escapes."), 241 C("long"), "?long", STYLE_long, 242 Z("List long option usage."), 243 C("man"), "?man", STYLE_man, 244 Z("List detailed info in displayed man page form."), 245 C("nroff"), "?nroff", STYLE_nroff, 246 Z("List detailed info in nroff."), 247 C("options"), "?options", STYLE_options, 248 Z("List short and long option details."), 249 C("posix"), "?posix", STYLE_posix, 250 Z("List posix getopt usage."), 251 C("short"), "?short", STYLE_short, 252 Z("List short option usage."), 253 C("usage"), "?usage", STYLE_usage, 254 Z("List the usage string with C style escapes."), 255 }; 256 257 static const List_t help_tail[] = 258 { 259 ':', C("\?\?-\alabel\a"), 260 C("List implementation info matching \alabel\a*."), 261 ':', C("\?\?\aname\a"), 262 C("Equivalent to \b--help=\b\aname\a."), 263 ':', C("\?\?"), 264 C("Equivalent to \b--\?\?options\b."), 265 ':', C("\?\?\?\?"), 266 C("Equivalent to \b--\?\?man\b."), 267 ':', C("\?\?\?\?\?\?"), 268 C("Equivalent to \b--\?\?help\b."), 269 ':', C("\?\?\?\?\?\?\aitem\a"), 270 C("If the next argument is \b--\b\aoption\a then list \ 271 the \aoption\a output in the \aitem\a style. Otherwise print \ 272 \bversion=\b\an\a where \an\a>0 if \b--\?\?\b\aitem\a is supported, \b0\b \ 273 if not."), 274 ':', C("\?\?\?\?\?\?ESC"), 275 C("Emit escape codes even if output is not a terminal."), 276 ':', C("\?\?\?\?\?\?MAN[=\asection\a]]"), 277 C("List the \bman\b(1) section title for \asection\a [the \ 278 current command]]."), 279 ':', C("\?\?\?\?\?\?SECTION"), 280 C("List the \bman\b(1) section number for the current command."), 281 ':', C("\?\?\?\?\?\?TEST"), 282 C("Massage the output for regression testing."), 283 }; 284 285 static const Attr_t attrs[] = 286 { 287 "flag", OPT_flag, 288 "hidden", OPT_hidden, 289 "ignorecase", OPT_ignorecase, 290 "invert", OPT_invert, 291 "listof", OPT_listof, 292 "number", OPT_number, 293 "oneof", OPT_oneof, 294 "optional", OPT_optional, 295 "string", OPT_string, 296 }; 297 298 static const char unknown[] = C("unknown option or attribute"); 299 300 static const char* heading[] = 301 { 302 C("INDEX"), 303 C("USER COMMANDS"), 304 C("SYSTEM LIBRARY"), 305 C("USER LIBRARY"), 306 C("FILE FORMATS"), 307 C("MISCELLANEOUS"), 308 C("GAMES and DEMOS"), 309 C("SPECIAL FILES"), 310 C("ADMINISTRATIVE COMMANDS"), 311 C("GUIs"), 312 }; 313 314 /* 315 * list of common man page strings 316 * NOTE: add but do not delete from this table 317 */ 318 319 static Msg_t C_LC_MESSAGES_libast[] = 320 { 321 { C("APPLICATION USAGE") }, 322 { C("ASYNCHRONOUS EVENTS") }, 323 { C("BUGS") }, 324 { C("CAVEATS") }, 325 { C("CONSEQUENCES OF ERRORS") }, 326 { C("DESCRIPTION") }, 327 { C("ENVIRONMENT VARIABLES") }, 328 { C("EXAMPLES") }, 329 { C("EXIT STATUS") }, 330 { C("EXTENDED DESCRIPTION") }, 331 { C("INPUT FILES") }, 332 { C("LIBRARY") }, 333 { C("NAME") }, 334 { C("OPERANDS") }, 335 { C("OPTIONS") }, 336 { C("OUTPUT FILES") }, 337 { C("PLUGIN") }, 338 { C("SEE ALSO") }, 339 { C("STDERR") }, 340 { C("STDIN") }, 341 { C("STDOUT") }, 342 { C("SYNOPSIS") }, 343 { C("author") }, 344 { C("copyright") }, 345 { C("license") }, 346 { C("name") }, 347 { C("path") }, 348 { C("version") }, 349 }; 350 351 /* 352 * 2007-03-19 move opt_info from _opt_info_ to (*_opt_data_) 353 * to allow future Opt_t growth 354 * by 2009 _opt_info_ can be static 355 */ 356 357 #if _BLD_ast && defined(__EXPORT__) 358 #define extern extern __EXPORT__ 359 #endif 360 361 extern Opt_t _opt_info_; 362 363 Opt_t _opt_info_ = { 0,0,0,0,0,0,0,{0},{0},0,0,0,{0},{0},&state }; 364 365 #undef extern 366 367 __EXTERN__(Opt_t, _opt_info_); 368 369 __EXTERN__(Opt_t*, _opt_infop_); 370 371 Opt_t* _opt_infop_ = &_opt_info_; 372 373 Optstate_t* 374 optstate(Opt_t* p) 375 { 376 return &state; 377 } 378 379 #if DEBUG || _BLD_DEBUG 380 381 /* 382 * debug usage string segment format 383 */ 384 385 static char* 386 show(register char* s) 387 { 388 register int c; 389 register char* t; 390 register char* e; 391 392 static char buf[32]; 393 394 if (!s) 395 return "(null)"; 396 t = buf; 397 e = buf + sizeof(buf) - 2; 398 while (t < e) 399 { 400 switch (c = *s++) 401 { 402 case 0: 403 goto done; 404 case '\a': 405 *t++ = '\\'; 406 c = 'a'; 407 break; 408 case '\b': 409 *t++ = '\\'; 410 c = 'b'; 411 break; 412 case '\f': 413 *t++ = '\\'; 414 c = 'f'; 415 break; 416 case '\n': 417 *t++ = '\\'; 418 c = 'n'; 419 break; 420 case '\t': 421 *t++ = '\\'; 422 c = 't'; 423 break; 424 case '\v': 425 *t++ = '\\'; 426 c = 'v'; 427 break; 428 } 429 *t++ = c; 430 } 431 done: 432 *t = 0; 433 return buf; 434 } 435 436 #endif 437 438 typedef struct Section_s 439 { 440 const char section[4]; 441 const char* name; 442 } Section_t; 443 444 static const Section_t sections[] = 445 { 446 "1M", "MAKE ASSERTION OPERATORS AND RULES", 447 "1", "USER COMMANDS", 448 "2", "SYSTEM CALLS", 449 "3F", "FORTRAN LIBRARY ROUTINES", 450 "3K", "KERNEL VM LIBRARY FUNCTIONS", 451 "3L", "LIGHTWEIGHT PROCESSES LIBRARY", 452 "3M", "MATHEMATICAL LIBRARY", 453 "3N", "NETWORK FUNCTIONS", 454 "3R", "RPC SERVICES LIBRARY", 455 "3S", "STANDARD I/O FUNCTIONS", 456 "3V", "SYSTEM V LIBRARY", 457 "3", "C LIBRARY FUNCTIONS", 458 "4F", "PROTOCOL FAMILIES", 459 "4P", "PROTOCOLS", 460 "4", "DEVICES AND NETWORK INTERFACES", 461 "5P", "PLUGINS", 462 "5", "FILE FORMATS", 463 "6", "GAMES AND DEMOS", 464 "7", "PUBLIC FILES AND TABLES", 465 "8", "ADMINISTRATIVE COMMANDS", 466 "L", "LOCAL COMMANDS", 467 }; 468 469 /* 470 * return section name given abbreviation 471 */ 472 473 static char* 474 secname(char* section) 475 { 476 int i; 477 char* b; 478 char* t; 479 const char* s; 480 481 b = t = fmtbuf(64); 482 if (section[1]) 483 { 484 switch (section[2] ? section[2] : section[1]) 485 { 486 case 'C': 487 s = "COMPATIBILITY "; 488 break; 489 case 'U': 490 s = "UWIN "; 491 break; 492 case 'X': 493 s = "MISCELLANEOUS "; 494 break; 495 default: 496 s = 0; 497 break; 498 } 499 if (s) 500 t = strcopy(t, s); 501 } 502 s = 0; 503 for (i = 0; i < elementsof(sections); i++) 504 if (section[0] == sections[i].section[0] && (section[1] == sections[i].section[1] || !sections[i].section[1])) 505 { 506 s = sections[i].name; 507 break; 508 } 509 if (!s) 510 { 511 t = strcopy(t, "SECTION "); 512 s = section; 513 } 514 strcopy(t, s); 515 return b; 516 } 517 518 /* 519 * pop the push stack 520 */ 521 522 static Push_t* 523 pop(register Push_t* psp) 524 { 525 register Push_t* tsp; 526 527 while (tsp = psp) 528 { 529 psp = psp->next; 530 free(tsp); 531 } 532 return 0; 533 } 534 535 /* 536 * skip over line space to the next token 537 */ 538 539 static char* 540 next(register char* s, int version) 541 { 542 register char* b; 543 544 while (*s == '\t' || *s == '\r' || version >= 1 && *s == ' ') 545 s++; 546 if (*s == '\n') 547 { 548 b = s; 549 while (*++s == ' ' || *s == '\t' || *s == '\r'); 550 if (*s == '\n') 551 return b; 552 } 553 return s; 554 } 555 556 /* 557 * skip to t1 or t2 or t3, whichever first, in s 558 * n==0 outside [...] 559 * n==1 inside [...] before ? 560 * n==2 inside [...] after ? 561 * b==0 outside {...} 562 * b==1 inside {...} 563 * past skips past the terminator to the next token 564 * otherwise a pointer to the terminator is returned 565 * 566 * ]] for ] inside [...] 567 * ?? for ? inside [...] before ? 568 * :: for : inside [...] before ? 569 */ 570 571 static char* 572 skip(register char* s, register int t1, register int t2, register int t3, register int n, register int b, int past, int version) 573 { 574 register int c; 575 register int on = n; 576 register int ob = b; 577 578 if (version < 1) 579 { 580 n = n >= 1; 581 for (;;) 582 { 583 switch (*s++) 584 { 585 case 0: 586 break; 587 case '[': 588 n++; 589 continue; 590 case ']': 591 if (--n <= 0) 592 break; 593 continue; 594 default: 595 continue; 596 } 597 break; 598 } 599 } 600 else while (c = *s++) 601 { 602 message((-22, "optget: skip t1=%c t2=%c t3=%c n=%d b=%d `%s'", t1 ? t1 : '@', t2 ? t2 : '@', t3 ? t3 : '@', n, b, show(s - 1))); 603 if (c == '[') 604 { 605 if (!n) 606 n = 1; 607 } 608 else if (c == ']') 609 { 610 if (n) 611 { 612 if (*s == ']') 613 s++; 614 else if (on == 1) 615 break; 616 else 617 n = 0; 618 } 619 } 620 else if (c == GO) 621 { 622 if (n == 0) 623 b++; 624 } 625 else if (c == OG) 626 { 627 if (n == 0 && b-- == ob) 628 break; 629 } 630 else if (c == '?') 631 { 632 if (n == 1) 633 { 634 if (*s == '?') 635 s++; 636 else 637 { 638 if (n == on && (c == t1 || c == t2 || c == t3)) 639 break; 640 n = 2; 641 } 642 } 643 } 644 else if (n == on && (c == t1 || c == t2 || c == t3)) 645 { 646 if (n == 1 && c == ':' && *s == c) 647 s++; 648 else 649 break; 650 } 651 } 652 return past && *(s - 1) ? next(s, version) : s - 1; 653 } 654 655 /* 656 * *s points to '(' on input 657 * return is one past matching ')' 658 */ 659 660 static char* 661 nest(register char* s) 662 { 663 int n; 664 665 n = 0; 666 for (;;) 667 { 668 switch (*s++) 669 { 670 case '(': 671 n++; 672 continue; 673 case ')': 674 if (!--n) 675 break; 676 continue; 677 default: 678 continue; 679 } 680 break; 681 } 682 return s; 683 } 684 685 /* 686 * match s with t 687 * t translated if possible 688 * embedded { - _ ' } ignored 689 * * separates required prefix from optional suffix 690 * otherwise prefix match 691 */ 692 693 static int 694 match(char* s, char* t, int version, const char* id, const char* catalog) 695 { 696 register char* w; 697 register char* x; 698 char* xw; 699 char* ww; 700 int n; 701 int v; 702 int j; 703 704 for (n = 0; n < 2; n++) 705 { 706 if (n) 707 x = t; 708 else 709 { 710 if (catalog) 711 { 712 w = skip(t, ':', '?', 0, 1, 0, 0, version); 713 w = sfprints("%-.*s", w - t, t); 714 x = T(id, catalog, w); 715 if (x == w) 716 continue; 717 } 718 x = T(NiL, ID, t); 719 if (x == t) 720 continue; 721 } 722 do 723 { 724 v = 0; 725 xw = x; 726 w = ww = s; 727 while (*x && *w) 728 { 729 if (isupper(*x)) 730 xw = x; 731 if (isupper(*w)) 732 ww = w; 733 if (*x == '*' && !v++ || *x == '\a') 734 { 735 if (*x == '\a') 736 do 737 { 738 if (!*++x) 739 { 740 x--; 741 break; 742 } 743 } while (*x != '\a'); 744 j = *(x + 1); 745 if (j == ':' || j == '|' || j == '?' || j == ']' || j == 0) 746 while (*w) 747 w++; 748 } 749 else if (SEP(*x)) 750 xw = ++x; 751 else if (SEP(*w) && w != s) 752 ww = ++w; 753 else if (*x == *w) 754 { 755 x++; 756 w++; 757 } 758 else if (w == ww && x == xw) 759 break; 760 else 761 { 762 if (x != xw) 763 { 764 while (*x && !SEP(*x) && !isupper(*x)) 765 x++; 766 if (!*x) 767 break; 768 if (SEP(*x)) 769 x++; 770 xw = x; 771 } 772 while (w > ww && *w != *x) 773 w--; 774 } 775 } 776 if (!*w) 777 { 778 if (!v) 779 { 780 for (;;) 781 { 782 switch (*x++) 783 { 784 case 0: 785 case ':': 786 case '|': 787 case '?': 788 case ']': 789 return 1; 790 case '*': 791 break; 792 default: 793 continue; 794 } 795 break; 796 } 797 break; 798 } 799 return 1; 800 } 801 } while (*(x = skip(x, '|', 0, 0, 1, 0, 0, version)) == '|' && x++); 802 } 803 return 0; 804 } 805 806 /* 807 * prefix search for s in tab with num elements of size 808 * with optional translation 809 */ 810 811 static void* 812 search(const void* tab, size_t num, size_t siz, char* s) 813 { 814 register char* p; 815 register char* e; 816 817 for (e = (p = (char*)tab) + num * siz; p < e; p += siz) 818 if (match(s, *((char**)p), -1, NiL, NiL)) 819 return (void*)p; 820 return 0; 821 } 822 823 /* 824 * save ap+bp+cp and return the saved pointer 825 */ 826 827 static char* 828 save(const char* ap, size_t az, const char* bp, size_t bz, const char* cp, size_t cz) 829 { 830 char* b; 831 char* e; 832 const char* ep; 833 Save_t* p; 834 Dtdisc_t* d; 835 char buf[1024]; 836 837 static Dt_t* dict; 838 839 if (!dict) 840 { 841 if (!(d = newof(0, Dtdisc_t, 1, 0))) 842 return (char*)ap; 843 d->key = offsetof(Save_t, text); 844 if (!(dict = dtopen(d, Dtset))) 845 return (char*)ap; 846 } 847 b = buf; 848 e = b + sizeof(buf) - 1; 849 for (ep = ap + az; b < e && ap < ep; *b++ = *ap++); 850 if (bp) 851 { 852 for (ep = bp + bz; b < e && bp < ep; *b++ = *bp++); 853 if (cp) 854 for (ep = cp + cz; b < e && cp < ep; *b++ = *cp++); 855 } 856 *b = 0; 857 if (!(p = (Save_t*)dtmatch(dict, buf))) 858 { 859 if (!(p = newof(0, Save_t, 1, b - buf))) 860 return (char*)ap; 861 strcpy(p->text, buf); 862 dtinsert(dict, p); 863 } 864 return p->text; 865 } 866 867 /* 868 * expand \f...\f info 869 * *p set to next char after second \f 870 * expanded value returned 871 */ 872 873 static char* 874 expand(register char* s, register char* e, char** p, Sfio_t* ip, char* id) 875 { 876 register int c; 877 register char* b = s; 878 int n; 879 880 n = sfstrtell(ip); 881 c = 1; 882 while ((!e || s < e) && (c = *s++) && c != '\f'); 883 sfwrite(ip, b, s - b - 1); 884 sfputc(ip, 0); 885 b = sfstrbase(ip) + n; 886 n = sfstrtell(ip); 887 if (!c) 888 s--; 889 if (*b == '?') 890 { 891 if (!*++b || streq(b, "NAME")) 892 { 893 if (!(b = id)) 894 b = "command"; 895 sfstrseek(ip, 0, SEEK_SET); 896 sfputr(ip, b, -1); 897 n = 0; 898 } 899 else 900 n = 1; 901 } 902 else if (!opt_info.disc || !opt_info.disc->infof || (*opt_info.disc->infof)(&opt_info, ip, b, opt_info.disc) < 0) 903 n = 0; 904 *p = s; 905 if (s = sfstruse(ip)) 906 s += n; 907 else 908 s = "error"; 909 return s; 910 } 911 912 /* 913 * initialize the translation dictionary and flag maps 914 */ 915 916 static void 917 initdict(void) 918 { 919 register int n; 920 921 state.vp = sfstropen(); 922 state.msgdisc.key = offsetof(Msg_t, text); 923 state.msgdisc.size = -1; 924 state.msgdisc.link = offsetof(Msg_t, link); 925 if (state.msgdict = dtopen(&state.msgdisc, Dtset)) 926 for (n = 0; n < elementsof(C_LC_MESSAGES_libast); n++) 927 dtinsert(state.msgdict, C_LC_MESSAGES_libast + n); 928 } 929 930 /* 931 * initialize the attributes for pass p from opt string s 932 */ 933 934 static int 935 init(register char* s, Optpass_t* p) 936 { 937 register char* t; 938 register char* u; 939 register int c; 940 register int a; 941 register int n; 942 char* e; 943 int l; 944 945 if (!state.localized) 946 { 947 state.localized = 1; 948 #if !_PACKAGE_astsa 949 if (!ast.locale.serial) 950 setlocale(LC_ALL, ""); 951 #endif 952 state.xp = sfstropen(); 953 if (!map[OPT_FLAGS[0]]) 954 for (n = 0, t = OPT_FLAGS; *t; t++) 955 map[*t] = ++n; 956 } 957 #if _BLD_DEBUG 958 error(-2, "optget debug"); 959 #endif 960 p->oopts = s; 961 p->version = 0; 962 p->prefix = 2; 963 p->section[0] = '1'; 964 p->section[1] = 0; 965 p->flags = 0; 966 p->id = error_info.id; 967 p->catalog = 0; 968 s = next(s, 0); 969 if (*s == ':') 970 s++; 971 if (*s == '+') 972 s++; 973 s = next(s, 0); 974 if (*s++ == '[') 975 { 976 if (*s == '+') 977 p->version = 1; 978 else if (*s++ == '-') 979 { 980 if (*s == '?' || *s == ']') 981 p->version = 1; 982 else 983 { 984 if (!isdigit(*s)) 985 p->version = 1; 986 else 987 while (isdigit(*s)) 988 p->version = p->version * 10 + (*s++ - '0'); 989 while (*s && *s != ']') 990 { 991 if ((c = *s++) == '?') 992 { 993 p->release = s; 994 while (*s && *s != ']') 995 if (isspace(*s++)) 996 p->release = s; 997 break; 998 } 999 else if (!isdigit(*s)) 1000 n = 1; 1001 else 1002 { 1003 n = 0; 1004 while (isdigit(*s)) 1005 n = n * 10 + (*s++ - '0'); 1006 } 1007 switch (c) 1008 { 1009 case '+': 1010 p->flags |= OPT_plus; 1011 break; 1012 case 'a': 1013 p->flags |= OPT_append; 1014 break; 1015 case 'c': 1016 p->flags |= OPT_cache; 1017 break; 1018 case 'i': 1019 p->flags |= OPT_ignore; 1020 break; 1021 case 'l': 1022 p->flags |= OPT_long; 1023 break; 1024 case 'm': 1025 p->flags |= OPT_module; 1026 break; 1027 case 'n': 1028 p->flags |= OPT_numeric; 1029 break; 1030 case 'o': 1031 p->flags |= OPT_old; 1032 break; 1033 case 'p': 1034 p->prefix = n; 1035 break; 1036 case 's': 1037 if (n > 1 && n < 5) 1038 { 1039 p->flags |= OPT_functions; 1040 p->prefix = 0; 1041 } 1042 p->section[0] = '0' + (n % 10); 1043 n = 1; 1044 if (isupper(*s)) 1045 p->section[n++] = *s++; 1046 if (isupper(*s)) 1047 p->section[n++] = *s++; 1048 p->section[n] = 0; 1049 break; 1050 } 1051 } 1052 } 1053 } 1054 while (*s) 1055 if (*s++ == ']') 1056 { 1057 while (isspace(*s)) 1058 s++; 1059 if (*s++ == '[') 1060 { 1061 if (*s++ != '-') 1062 { 1063 l = 0; 1064 if (strneq(s - 1, "+NAME?", 6) && (s += 5) || strneq(s - 1, "+LIBRARY?", 9) && (s += 8) && (l = 1) || strneq(s - 1, "+PLUGIN?", 8) && (s += 7) && (l = 1)) 1065 { 1066 for (; *s == '\a' || *s == '\b' || *s == '\v' || *s == ' '; s++); 1067 if (*s == '\f') 1068 { 1069 if (*(s + 1) == '?' && *(s + 2) == '\f') 1070 break; 1071 s = expand(s + 1, NiL, &e, state.xp, p->id); 1072 } 1073 for (t = s; *t && *t != ' ' && *t != ']'; t++); 1074 if (t > s) 1075 { 1076 u = t; 1077 if (*(t - 1) == '\a' || *(t - 1) == '\b' || *(t - 1) == '\v') 1078 t--; 1079 if (t > s) 1080 { 1081 while (*u == ' ' || *u == '\\') 1082 u++; 1083 if (*u == '-' || *u == ']') 1084 { 1085 if (!l) 1086 p->id = save(s, t - s, 0, 0, 0, 0); 1087 else if ((a = strlen(p->id)) <= (n = t - s) || strncmp(p->id + a - n, s, n) || *(p->id + a - n - 1) != ':') 1088 p->id = save(p->id, strlen(p->id), "::", 2, s, t - s); 1089 } 1090 } 1091 } 1092 } 1093 break; 1094 } 1095 if (*s == '-') 1096 s++; 1097 if (strneq(s, "catalog?", 8)) 1098 p->catalog = s += 8; 1099 } 1100 } 1101 } 1102 if (!error_info.id) 1103 { 1104 if (!(error_info.id = p->id)) 1105 p->id = "command"; 1106 } 1107 else if (p->id == error_info.id) 1108 p->id = save(p->id, strlen(p->id), 0, 0, 0, 0); 1109 if (s = p->catalog) 1110 p->catalog = ((t = strchr(s, ']')) && (!p->id || (t - s) != strlen(p->id) || !strneq(s, p->id, t - s))) ? save(s, t - s, 0, 0, 0, 0) : (char*)0; 1111 if (!p->catalog) 1112 { 1113 if (opt_info.disc && opt_info.disc->catalog && (!p->id || !streq(opt_info.disc->catalog, p->id))) 1114 p->catalog = opt_info.disc->catalog; 1115 else 1116 p->catalog = ID; 1117 } 1118 s = p->oopts; 1119 if (*s == ':') 1120 s++; 1121 if (*s == '+') 1122 { 1123 s++; 1124 p->flags |= OPT_plus; 1125 } 1126 s = next(s, 0); 1127 if (*s != '[') 1128 for (t = s, a = 0; *t; t++) 1129 if (!a && *t == '-') 1130 { 1131 p->flags |= OPT_minus; 1132 break; 1133 } 1134 else if (*t == '[') 1135 a++; 1136 else if (*t == ']') 1137 a--; 1138 if (!p->version && (t = strchr(s, '(')) && strchr(t, ')') && (state.cp || (state.cp = sfstropen()))) 1139 { 1140 /* 1141 * solaris long option compatibility 1142 */ 1143 1144 p->version = 1; 1145 for (t = p->oopts; t < s; t++) 1146 sfputc(state.cp, *t); 1147 n = t - p->oopts; 1148 sfputc(state.cp, '['); 1149 sfputc(state.cp, '-'); 1150 sfputc(state.cp, ']'); 1151 c = *s++; 1152 while (c) 1153 { 1154 sfputc(state.cp, '['); 1155 sfputc(state.cp, c); 1156 if (a = (c = *s++) == ':') 1157 c = *s++; 1158 if (c == '(') 1159 { 1160 sfputc(state.cp, ':'); 1161 for (;;) 1162 { 1163 while ((c = *s++) && c != ')') 1164 sfputc(state.cp, c); 1165 if (!c || (c = *s++) != '(') 1166 break; 1167 sfputc(state.cp, '|'); 1168 } 1169 } 1170 sfputc(state.cp, ']'); 1171 if (a) 1172 sfputr(state.cp, ":[string]", -1); 1173 } 1174 if (!(p->oopts = s = sfstruse(state.cp))) 1175 return -1; 1176 s += n; 1177 } 1178 p->opts = s; 1179 message((-2, "version=%d prefix=%d section=%s flags=%04x id=%s catalog=%s oopts=%p", p->version, p->prefix, p->section, p->flags, p->id, p->catalog, p->oopts)); 1180 return 0; 1181 } 1182 1183 /* 1184 * return the bold set/unset sequence for style 1185 */ 1186 1187 static const char* 1188 font(int f, int style, int set) 1189 { 1190 switch (style) 1191 { 1192 case STYLE_html: 1193 return fonts[f].html[set]; 1194 case STYLE_nroff: 1195 return fonts[f].nroff[set]; 1196 case STYLE_short: 1197 case STYLE_long: 1198 case STYLE_posix: 1199 case STYLE_api: 1200 break; 1201 default: 1202 if (state.emphasis > 0) 1203 return fonts[f].term[set]; 1204 break; 1205 } 1206 return ""; 1207 } 1208 1209 /* 1210 * push \f...\f info 1211 */ 1212 1213 static Push_t* 1214 info(Push_t* psp, char* s, char* e, Sfio_t* ip, char* id) 1215 { 1216 register char* b; 1217 int n; 1218 Push_t* tsp; 1219 1220 static Push_t push; 1221 1222 b = expand(s, e, &s, ip, id); 1223 n = strlen(b); 1224 if (tsp = newof(0, Push_t, 1, n + 1)) 1225 { 1226 tsp->nb = (char*)(tsp + 1); 1227 tsp->ne = tsp->nb + n; 1228 strcpy(tsp->nb, b); 1229 } 1230 else 1231 tsp = &push; 1232 tsp->next = psp; 1233 tsp->ob = s; 1234 tsp->oe = e; 1235 return tsp; 1236 } 1237 1238 /* 1239 * push translation 1240 */ 1241 1242 static Push_t* 1243 localize(Push_t* psp, char* s, char* e, int term, int n, Sfio_t* ip, int version, char* id, char* catalog) 1244 { 1245 char* t; 1246 char* u; 1247 Push_t* tsp; 1248 int c; 1249 1250 t = skip(s, term, 0, 0, n, 0, 0, version); 1251 if (e && t > e) 1252 t = e; 1253 while (s < t) 1254 { 1255 switch (c = *s++) 1256 { 1257 case ':': 1258 case '?': 1259 if (term && *s == c) 1260 s++; 1261 break; 1262 case ']': 1263 if (*s == c) 1264 s++; 1265 break; 1266 } 1267 sfputc(ip, c); 1268 } 1269 if (!(s = sfstruse(ip)) || (u = T(id, catalog, s)) == s) 1270 return 0; 1271 n = strlen(u); 1272 if (tsp = newof(0, Push_t, 1, n + 1)) 1273 { 1274 tsp->nb = (char*)(tsp + 1); 1275 tsp->ne = tsp->nb + n; 1276 strcpy(tsp->nb, u); 1277 tsp->ob = t; 1278 tsp->oe = e; 1279 tsp->ch = 1; 1280 } 1281 tsp->next = psp; 1282 return tsp; 1283 } 1284 1285 /* 1286 * output label s from [ ...label...[?...] ] to sp 1287 * 1 returned if the label was translated 1288 */ 1289 1290 static int 1291 label(register Sfio_t* sp, int sep, register char* s, int about, int z, int level, int style, int f, Sfio_t* ip, int version, char* id, char* catalog) 1292 { 1293 register int c; 1294 register char* t; 1295 register char* e; 1296 int ostyle; 1297 int a; 1298 int i; 1299 char* p; 1300 char* q; 1301 char* w; 1302 char* y; 1303 int va; 1304 Push_t* tsp; 1305 1306 int r = 0; 1307 int n = 1; 1308 Push_t* psp = 0; 1309 1310 if ((ostyle = style) > (STYLE_nroff - (sep <= 0)) && f != FONT_LITERAL && f >= 0) 1311 style = 0; 1312 if (z < 0) 1313 e = s + strlen(s); 1314 else 1315 e = s + z; 1316 if (sep > 0) 1317 { 1318 if (sep == ' ' && style == STYLE_nroff) 1319 sfputc(sp, '\\'); 1320 sfputc(sp, sep); 1321 } 1322 sep = !sep || z < 0; 1323 va = 0; 1324 y = 0; 1325 if (about) 1326 sfputc(sp, '('); 1327 if (version < 1) 1328 { 1329 a = 0; 1330 for (;;) 1331 { 1332 if (s >= e) 1333 return r; 1334 switch (c = *s++) 1335 { 1336 case '[': 1337 a++; 1338 break; 1339 case ']': 1340 if (--a < 0) 1341 return r; 1342 break; 1343 } 1344 sfputc(sp, c); 1345 } 1346 } 1347 else if (level && (*(p = skip(s, 0, 0, 0, 1, level, 1, version)) == ':' || *p == '#')) 1348 { 1349 va = 0; 1350 if (*++p == '?' || *p == *(p - 1)) 1351 { 1352 p++; 1353 va |= OPT_optional; 1354 } 1355 if (*(p = next(p, version)) == '[') 1356 y = p + 1; 1357 } 1358 if (X(catalog) && (!level || *s == '\a' || *(s - 1) != '+') && 1359 (tsp = localize(psp, s, e, (sep || level) ? '?' : 0, sep || level, ip, version, id, catalog))) 1360 { 1361 psp = tsp; 1362 s = psp->nb; 1363 e = psp->ne; 1364 r = psp->ch > 0; 1365 } 1366 switch (*s) 1367 { 1368 case '\a': 1369 if (f == FONT_ITALIC || f < 0) 1370 s++; 1371 if (f > 0) 1372 f = 0; 1373 break; 1374 case '\b': 1375 if (f == FONT_BOLD || f < 0) 1376 s++; 1377 if (f > 0) 1378 f = 0; 1379 break; 1380 case '\v': 1381 if (f == FONT_LITERAL || f < 0) 1382 s++; 1383 if (f > 0) 1384 f = 0; 1385 break; 1386 default: 1387 if (f > 0) 1388 sfputr(sp, font(f, style, 1), -1); 1389 break; 1390 } 1391 for (;;) 1392 { 1393 if (s >= e) 1394 { 1395 if (!(tsp = psp)) 1396 goto restore; 1397 s = psp->ob; 1398 e = psp->oe; 1399 psp = psp->next; 1400 free(tsp); 1401 continue; 1402 } 1403 switch (c = *s++) 1404 { 1405 case '(': 1406 if (n) 1407 { 1408 n = 0; 1409 if (f > 0) 1410 { 1411 sfputr(sp, font(f, style, 0), -1); 1412 f = 0; 1413 } 1414 } 1415 break; 1416 case '?': 1417 case ':': 1418 case ']': 1419 if (psp && psp->ch) 1420 break; 1421 if (y) 1422 { 1423 if (va & OPT_optional) 1424 sfputc(sp, '['); 1425 sfputc(sp, '='); 1426 label(sp, 0, y, 0, -1, 0, style, f >= 0 ? FONT_ITALIC : f, ip, version, id, catalog); 1427 if (va & OPT_optional) 1428 sfputc(sp, ']'); 1429 y = 0; 1430 } 1431 switch (c) 1432 { 1433 case '?': 1434 if (*s == '?') 1435 s++; 1436 else if (*s == ']' && *(s + 1) != ']') 1437 continue; 1438 else if (sep) 1439 goto restore; 1440 else if (X(catalog) && (tsp = localize(psp, s, e, 0, 1, ip, version, id, catalog))) 1441 { 1442 psp = tsp; 1443 s = psp->nb; 1444 e = psp->ne; 1445 } 1446 break; 1447 case ']': 1448 if (sep && *s++ != ']') 1449 goto restore; 1450 break; 1451 case ':': 1452 if (sep && *s++ != ':') 1453 goto restore; 1454 break; 1455 } 1456 break; 1457 case '\a': 1458 a = FONT_ITALIC; 1459 setfont: 1460 if (f >= 0) 1461 { 1462 if (f & ~a) 1463 { 1464 sfputr(sp, font(f, style, 0), -1); 1465 f = 0; 1466 } 1467 if (!f && style == STYLE_html) 1468 { 1469 for (t = s; t < e && !isspace(*t) && !iscntrl(*t); t++); 1470 if (*t == c && *++t == '(') 1471 { 1472 w = t; 1473 if (++t < e && isdigit(*t)) 1474 while (++t < e && isupper(*t)); 1475 if (t < e && *t == ')' && t > w + 1) 1476 { 1477 sfprintf(sp, "<NOBR><A href=\"../man%-.*s/" 1478 , t - w - 1, w + 1 1479 ); 1480 for (q = s; q < w - 1; q++) 1481 if (*q == ':' && q < w - 2 && *(q + 1) == ':') 1482 { 1483 sfputc(sp, '-'); 1484 q++; 1485 } 1486 else 1487 sfputc(sp, *q); 1488 sfprintf(sp, ".html\">%s%-.*s%s</A>%-.*s</NOBR>" 1489 , font(a, style, 1) 1490 , w - s - 1, s 1491 , font(a, style, 0) 1492 , t - w + 1, w 1493 ); 1494 s = t + 1; 1495 continue; 1496 } 1497 } 1498 } 1499 sfputr(sp, font(a, style, !!(f ^= a)), -1); 1500 } 1501 continue; 1502 case '\b': 1503 a = FONT_BOLD; 1504 goto setfont; 1505 case '\f': 1506 psp = info(psp, s, e, ip, id); 1507 if (psp->nb) 1508 { 1509 s = psp->nb; 1510 e = psp->ne; 1511 } 1512 else 1513 { 1514 s = psp->ob; 1515 psp = psp->next; 1516 } 1517 continue; 1518 case '\n': 1519 sfputc(sp, c); 1520 for (i = 0; i < level; i++) 1521 sfputc(sp, '\t'); 1522 continue; 1523 case '\v': 1524 a = FONT_LITERAL; 1525 goto setfont; 1526 case '<': 1527 if (style == STYLE_html) 1528 { 1529 sfputr(sp, "<", -1); 1530 c = 0; 1531 for (t = s; t < e; t++) 1532 if (!isalnum(*t) && *t != '_' && *t != '.' && *t != '-') 1533 { 1534 if (*t == '@') 1535 { 1536 if (c) 1537 break; 1538 c = 1; 1539 } 1540 else if (*t == '>') 1541 { 1542 if (c) 1543 { 1544 sfprintf(sp, "<A href=\"mailto:%-.*s>%-.*s</A>>", t - s, s, t - s, s); 1545 s = t + 1; 1546 } 1547 break; 1548 } 1549 else 1550 break; 1551 } 1552 continue; 1553 } 1554 break; 1555 case '>': 1556 if (style == STYLE_html) 1557 { 1558 sfputr(sp, ">", -1); 1559 continue; 1560 } 1561 break; 1562 case '&': 1563 if (style == STYLE_html) 1564 { 1565 sfputr(sp, "&", -1); 1566 continue; 1567 } 1568 break; 1569 case '"': 1570 if (style == STYLE_html) 1571 { 1572 sfputr(sp, """, -1); 1573 continue; 1574 } 1575 break; 1576 case '-': 1577 if (ostyle == STYLE_nroff) 1578 sfputc(sp, '\\'); 1579 break; 1580 case '.': 1581 if (ostyle == STYLE_nroff) 1582 { 1583 sfputc(sp, '\\'); 1584 sfputc(sp, '&'); 1585 } 1586 break; 1587 case '\\': 1588 if (ostyle == STYLE_nroff) 1589 { 1590 c = 'e'; 1591 sfputc(sp, '\\'); 1592 } 1593 break; 1594 case ' ': 1595 if (ostyle == STYLE_nroff) 1596 sfputc(sp, '\\'); 1597 break; 1598 } 1599 sfputc(sp, c); 1600 } 1601 restore: 1602 if (f > 0) 1603 sfputr(sp, font(f, style, 0), -1); 1604 if (about) 1605 sfputc(sp, ')'); 1606 if (psp) 1607 pop(psp); 1608 return r; 1609 } 1610 1611 /* 1612 * output args description to sp from p of length n 1613 */ 1614 1615 static void 1616 args(register Sfio_t* sp, register char* p, register int n, int flags, int style, Sfio_t* ip, int version, char* id, char* catalog) 1617 { 1618 register int i; 1619 register char* t; 1620 register char* o; 1621 register char* a = 0; 1622 char* b; 1623 int sep; 1624 1625 if (flags & OPT_functions) 1626 sep = '\t'; 1627 else 1628 { 1629 sep = ' '; 1630 o = T(NiL, ID, "options"); 1631 b = style == STYLE_nroff ? "\\ " : " "; 1632 for (;;) 1633 { 1634 t = (char*)memchr(p, '\n', n); 1635 if (style >= STYLE_man) 1636 { 1637 if (!(a = id)) 1638 a = "..."; 1639 sfprintf(sp, "\t%s%s%s%s[%s%s%s%s%s]", font(FONT_BOLD, style, 1), a, font(FONT_BOLD, style, 0), b, b, font(FONT_ITALIC, style, 1), o, font(FONT_ITALIC, style, 0), b); 1640 } 1641 else if (a) 1642 sfprintf(sp, "%*.*s%s%s%s[%s%s%s]", OPT_USAGE - 1, OPT_USAGE - 1, T(NiL, ID, "Or:"), b, a, b, b, o, b); 1643 else 1644 { 1645 if (!(a = error_info.id) && !(a = id)) 1646 a = "..."; 1647 if (!sfstrtell(sp)) 1648 sfprintf(sp, "[%s%s%s]", b, o, b); 1649 } 1650 if (!t) 1651 break; 1652 i = ++t - p; 1653 if (i) 1654 { 1655 sfputr(sp, b, -1); 1656 if (X(catalog)) 1657 { 1658 sfwrite(ip, p, i); 1659 if (b = sfstruse(ip)) 1660 sfputr(sp, T(id, catalog, b), -1); 1661 else 1662 sfwrite(sp, p, i); 1663 } 1664 else 1665 sfwrite(sp, p, i); 1666 } 1667 if (style == STYLE_html) 1668 sfputr(sp, "<BR>", '\n'); 1669 else if (style == STYLE_nroff) 1670 sfputr(sp, ".br", '\n'); 1671 else if (style == STYLE_api) 1672 sfputr(sp, ".BR", '\n'); 1673 p = t; 1674 n -= i; 1675 while (n > 0 && (*p == ' ' || *p == '\t')) 1676 { 1677 p++; 1678 n--; 1679 } 1680 } 1681 } 1682 if (n) 1683 label(sp, sep, p, 0, n, 0, style, 0, ip, version, id, catalog); 1684 } 1685 1686 /* 1687 * output [+-...label...?...] label s to sp 1688 * according to {...} level and style 1689 * return 0:header 1:paragraph 1690 */ 1691 1692 static int 1693 item(Sfio_t* sp, char* s, int about, int level, int style, Sfio_t* ip, int version, char* id, char* catalog, int* hflags) 1694 { 1695 register char* t; 1696 int n; 1697 int par; 1698 1699 sfputc(sp, '\n'); 1700 if (*s == '\n') 1701 { 1702 par = 0; 1703 if (style >= STYLE_nroff) 1704 sfprintf(sp, ".DS\n"); 1705 else 1706 { 1707 if (style == STYLE_html) 1708 sfprintf(sp, "<PRE>\n"); 1709 else 1710 sfputc(sp, '\n'); 1711 for (n = 0; n < level; n++) 1712 sfputc(sp, '\t'); 1713 } 1714 label(sp, 0, s + 1, about, -1, level, style, FONT_LITERAL, ip, version, id, catalog); 1715 sfputc(sp, '\n'); 1716 if (style >= STYLE_nroff) 1717 sfprintf(sp, ".DE"); 1718 else if (style == STYLE_html) 1719 sfprintf(sp, "</PRE>"); 1720 } 1721 else if (*s != ']' && (*s != '?' || *(s + 1) == '?')) 1722 { 1723 par = 0; 1724 if (level) 1725 { 1726 if (style >= STYLE_nroff) 1727 sfprintf(sp, ".H%d ", (level - (level > 2)) / 2); 1728 else 1729 for (n = 0; n < level; n++) 1730 sfputc(sp, '\t'); 1731 } 1732 if (style == STYLE_html) 1733 { 1734 if (!level) 1735 { 1736 if (*hflags & HELP_head) 1737 sfputr(sp, "</DIV>", '\n'); 1738 else 1739 *hflags |= HELP_head; 1740 sfputr(sp, "<H4>", -1); 1741 } 1742 sfputr(sp, "<A name=\"", -1); 1743 if (s[-1] == '-' && s[0] == 'l' && s[1] == 'i' && s[2] == 'c' && s[3] == 'e' && s[4] == 'n' && s[5] == 's' && s[6] == 'e' && s[7] == '?') 1744 for (t = s + 8; *t && *t != ']'; t++) 1745 if (t[0] == 'p' && (!strncmp(t, "proprietary", 11) || !strncmp(t, "private", 7)) || t[0] == 'n' && !strncmp(t, "noncommercial", 13)) 1746 { 1747 state.flags |= OPT_proprietary; 1748 break; 1749 } 1750 label(sp, 0, s, about, -1, level, style, -1, ip, version, id, catalog); 1751 sfputr(sp, "\">", -1); 1752 label(sp, 0, s, about, -1, level, style, level ? FONT_BOLD : 0, ip, version, id, catalog); 1753 sfputr(sp, "</A>", -1); 1754 if (!level) 1755 { 1756 if (!strncmp(s, C("SYNOPSIS"), strlen(C("SYNOPSIS")))) 1757 sfputr(sp, "</H4>\n<DIV class=SY>", -1); 1758 else 1759 { 1760 sfputr(sp, "</H4>\n<DIV class=SH>", -1); 1761 if (!strncmp(s, C("NAME"), strlen(C("NAME"))) || !strncmp(s, C("PLUGIN"), strlen(C("PLUGIN")))) 1762 *hflags |= HELP_index; 1763 } 1764 } 1765 } 1766 else 1767 { 1768 if (!level) 1769 { 1770 if (style >= STYLE_nroff) 1771 sfprintf(sp, ".SH "); 1772 else if (style == STYLE_man) 1773 sfputc(sp, '\n'); 1774 else if (style != STYLE_options && style != STYLE_match || *s == '-' || *s == '+') 1775 sfputc(sp, '\t'); 1776 } 1777 label(sp, 0, s, about, -1, level, style, FONT_BOLD, ip, version, id, catalog); 1778 } 1779 } 1780 else 1781 { 1782 par = 1; 1783 if (style >= STYLE_nroff) 1784 sfputr(sp, level ? ".SP" : ".PP", -1); 1785 } 1786 if (style >= STYLE_nroff || !level) 1787 sfputc(sp, '\n'); 1788 if (par && style < STYLE_nroff) 1789 for (n = 0; n < level; n++) 1790 sfputc(sp, '\t'); 1791 return par; 1792 } 1793 1794 /* 1795 * output text to sp from p according to style 1796 */ 1797 1798 #if _BLD_DEBUG 1799 1800 static char* textout(Sfio_t*, char*, char*, int, int, int, int, Sfio_t*, int, char*, char*, int*); 1801 1802 static char* 1803 trace_textout(Sfio_t* sp, register char* p, char* conform, int conformlen, int style, int level, int bump, Sfio_t* ip, int version, char* id, char* catalog, int* hflags, int line) 1804 { 1805 static int depth = 0; 1806 1807 message((-21, "opthelp: txt#%d +++ %2d \"%s\" style=%d level=%d bump=%d", line, ++depth, show(p), style, level, bump)); 1808 p = textout(sp, p, conform, conformlen, style, level, bump, ip, version, id, catalog, hflags); 1809 message((-21, "opthelp: txt#%d --- %2d \"%s\"", line, depth--, show(p))); 1810 return p; 1811 } 1812 1813 #endif 1814 1815 static char* 1816 textout(Sfio_t* sp, register char* s, char* conform, int conformlen, int style, int level, int bump, Sfio_t* ip, int version, char* id, char* catalog, int* hflags) 1817 { 1818 #if _BLD_DEBUG 1819 #define textout(sp,s,conform,conformlen,style,level,bump,ip,version,id,catalog,hflags) trace_textout(sp,s,conform,conformlen,style,level,bump,ip,version,id,catalog,hflags,__LINE__) 1820 #endif 1821 register char* t; 1822 register int c; 1823 register int n; 1824 char* w; 1825 char* q; 1826 int a; 1827 int f; 1828 int par; 1829 int about; 1830 Push_t* tsp; 1831 1832 int ident = 0; 1833 int lev = level; 1834 Push_t* psp = 0; 1835 1836 again: 1837 about = 0; 1838 if ((c = *s) == GO) 1839 { 1840 for (;;) 1841 { 1842 while (*(s = next(s + 1, version)) == '\n'); 1843 if (*s == GO) 1844 { 1845 if (level > 1) 1846 level++; 1847 level++; 1848 } 1849 else if (*s != OG) 1850 { 1851 if (level <= 1 || *s != '[' || *(s + 1) != '-' || style == STYLE_man && *(s + 2) == '?' || isalpha(*(s + 2))) 1852 break; 1853 s = skip(s, 0, 0, 0, 1, level, 0, version); 1854 } 1855 else if ((level -= 2) <= lev) 1856 return s + 1; 1857 } 1858 if (*s == '\f') 1859 { 1860 psp = info(psp, s + 1, NiL, ip, id); 1861 if (psp->nb) 1862 s = psp->nb; 1863 else 1864 { 1865 s = psp->ob; 1866 psp = psp->next; 1867 } 1868 } 1869 if (*s != '[') 1870 return s; 1871 c = *++s; 1872 if (level > 1) 1873 level++; 1874 level++; 1875 } 1876 if (c == '-' && level > 1) 1877 { 1878 if (style == STYLE_man) 1879 { 1880 about = 1; 1881 if (*(s + 1) == '-') 1882 s++; 1883 } 1884 else 1885 for (;;) 1886 { 1887 s = skip(s, 0, 0, 0, 1, level, 0, version); 1888 while (*(s = next(s + 1, version)) == '\n'); 1889 if (*s == '[') 1890 { 1891 if ((c = *++s) != '-') 1892 break; 1893 } 1894 else if (*s == GO) 1895 goto again; 1896 else if (*s == OG) 1897 return s + 1; 1898 } 1899 } 1900 if (c == '+' || c == '-' && (bump = 3) || c != ' ' && level > 1) 1901 { 1902 s = skip(t = s + 1, '?', 0, 0, 1, level, 0, version); 1903 if (c == '-' && (*t == '?' || isdigit(*t) || *s == '?' && *(s + 1) == '\n')) 1904 { 1905 if ((c = *s) != '?') 1906 return skip(s, 0, 0, 0, 1, level, 1, version); 1907 w = C("version"); 1908 par = item(sp, w, about, level, style, ip, version, id, ID, hflags); 1909 for (;;) 1910 { 1911 while (isspace(*(s + 1))) 1912 s++; 1913 w = s; 1914 if (w[1] == '@' && w[2] == '(' && w[3] == '#' && w[4] == ')') 1915 s = w + 4; 1916 else if (w[1] == '$' && w[2] == 'I' && w[3] == 'd' && w[4] == ':' && w[5] == ' ') 1917 { 1918 s = w + 5; 1919 ident = 1; 1920 } 1921 else 1922 break; 1923 } 1924 } 1925 else 1926 { 1927 if (isdigit(c) && isdigit(*t)) 1928 { 1929 while (isdigit(*t)) 1930 t++; 1931 if (*t == ':') 1932 t++; 1933 } 1934 else if (isalnum(c) && *t-- == ':') 1935 { 1936 if (X(catalog) || *t == *(t + 2)) 1937 t += 2; 1938 else 1939 { 1940 sfprintf(ip, "%s", t); 1941 if (w = sfstruse(ip)) 1942 *((t = w) + 1) = '|'; 1943 } 1944 } 1945 par = item(sp, t, about, level, style, ip, version, id, catalog, hflags); 1946 c = *s; 1947 } 1948 if (!about && level) 1949 par = 0; 1950 } 1951 else 1952 { 1953 if (style >= STYLE_nroff) 1954 sfputc(sp, '\n'); 1955 else if (c == '?') 1956 for (n = 0; n < level; n++) 1957 sfputc(sp, '\t'); 1958 par = 0; 1959 } 1960 if (c == ':') 1961 c = *(s = skip(s, '?', 0, 0, 1, 0, 0, version)); 1962 if ((c == ']' || c == '?' && *(s + 1) == ']' && *(s + 2) != ']' && s++) && (c = *(s = next(s + 1, version))) == GO) 1963 { 1964 s = textout(sp, s, conform, conformlen, style, level + bump + par + 1, 0, ip, version, id, catalog, hflags); 1965 if (level > lev && *s && *(s = next(s, version)) == '[') 1966 { 1967 s++; 1968 message((-21, "textout#%d s=%s", __LINE__, show(s))); 1969 goto again; 1970 } 1971 } 1972 else if (c == '?' || c == ' ') 1973 { 1974 s++; 1975 if (c == ' ') 1976 sfputc(sp, c); 1977 else 1978 { 1979 if (X(catalog) && (tsp = localize(psp, s, NiL, 0, 1, ip, version, id, catalog))) 1980 { 1981 psp = tsp; 1982 s = psp->nb; 1983 } 1984 if (style < STYLE_nroff) 1985 for (n = 0; n < bump + 1; n++) 1986 sfputc(sp, '\t'); 1987 } 1988 if (conform) 1989 { 1990 sfprintf(sp, "[%-.*s %s] ", conformlen, conform, T(NiL, ID, "conformance")); 1991 conform = 0; 1992 } 1993 if (*hflags & HELP_index) 1994 { 1995 *hflags &= ~HELP_index; 1996 sfputr(sp, "<!--MAN-INDEX-->", -1); 1997 } 1998 f = 0; 1999 for (;;) 2000 { 2001 switch (c = *s++) 2002 { 2003 case 0: 2004 if (!(tsp = psp)) 2005 { 2006 if (f) 2007 sfputr(sp, font(f, style, 0), -1); 2008 return s - 1; 2009 } 2010 s = psp->ob; 2011 psp = psp->next; 2012 free(tsp); 2013 continue; 2014 case ']': 2015 if (psp && psp->ch) 2016 break; 2017 if (*s != ']') 2018 { 2019 if (f) 2020 { 2021 sfputr(sp, font(f, style, 0), -1); 2022 f = 0; 2023 } 2024 for (;;) 2025 { 2026 if ((*s == '#' || *s == ':') && level > lev) 2027 { 2028 char* o; 2029 char* v; 2030 int j; 2031 int m; 2032 int ol; 2033 int vl; 2034 2035 a = 0; 2036 o = 0; 2037 v = 0; 2038 if (*++s == '?' || *s == *(s - 1)) 2039 { 2040 s++; 2041 a |= OPT_optional; 2042 } 2043 if (*(s = next(s, version)) == '[') 2044 { 2045 s = skip(s + 1, ':', '?', 0, 1, 0, 0, version); 2046 while (*s == ':') 2047 { 2048 s = skip(t = s + 1, ':', '?', 0, 1, 0, 0, version); 2049 m = s - t; 2050 if (*t == '!') 2051 { 2052 o = t + 1; 2053 ol = m - 1; 2054 } 2055 else if (*t == '=') 2056 { 2057 v = t + 1; 2058 vl = m - 1; 2059 } 2060 else 2061 for (j = 0; j < elementsof(attrs); j++) 2062 if (strneq(t, attrs[j].name, m)) 2063 { 2064 a |= attrs[j].flag; 2065 break; 2066 } 2067 } 2068 } 2069 if (a & OPT_optional) 2070 { 2071 if (o) 2072 { 2073 sfprintf(sp, " %s ", T(NiL, ID, "If the option value is omitted then")); 2074 sfputr(sp, font(FONT_BOLD, style, 1), -1); 2075 t = o + ol; 2076 while (o < t) 2077 { 2078 if (((c = *o++) == ':' || c == '?') && *o == c) 2079 o++; 2080 sfputc(sp, c); 2081 } 2082 sfputr(sp, font(FONT_BOLD, style, 0), -1); 2083 sfprintf(sp, " %s.", T(NiL, ID, "is assumed")); 2084 } 2085 else 2086 sfprintf(sp, " %s", T(NiL, ID, "The option value may be omitted.")); 2087 } 2088 if (v) 2089 { 2090 sfprintf(sp, " %s ", T(NiL, ID, "The default value is")); 2091 sfputr(sp, font(FONT_BOLD, style, 1), -1); 2092 t = v + vl; 2093 while (v < t) 2094 { 2095 if (((c = *v++) == ':' || c == '?') && *v == c) 2096 v++; 2097 sfputc(sp, c); 2098 } 2099 sfputr(sp, font(FONT_BOLD, style, 0), -1); 2100 sfputc(sp, '.'); 2101 } 2102 s = skip(s, 0, 0, 0, 1, 0, 1, version); 2103 } 2104 if (*(s = next(s, version)) == GO) 2105 { 2106 s = textout(sp, s, 0, 0, style, level + bump + !level, 0, ip, version, id, catalog, hflags); 2107 if (*s && *(s = next(s, version)) == '[' && !isalnum(*(s + 1))) 2108 { 2109 s++; 2110 message((-21, "textout#%d s=%s", __LINE__, show(s))); 2111 goto again; 2112 } 2113 } 2114 else if (*s == '[' && level > lev) 2115 { 2116 s++; 2117 goto again; 2118 } 2119 else if (*s == '\f') 2120 { 2121 s++; 2122 if (style != STYLE_keys) 2123 { 2124 psp = info(psp, s, NiL, ip, id); 2125 if (psp->nb) 2126 s = psp->nb; 2127 else 2128 { 2129 s = psp->ob; 2130 psp = psp->next; 2131 } 2132 } 2133 } 2134 else if (!*s) 2135 { 2136 if (!(tsp = psp)) 2137 break; 2138 s = psp->ob; 2139 psp = psp->next; 2140 free(tsp); 2141 } 2142 else if (*s != OG) 2143 break; 2144 else 2145 { 2146 s++; 2147 if ((level -= 2) <= lev) 2148 break; 2149 } 2150 } 2151 return s; 2152 } 2153 s++; 2154 break; 2155 case '\a': 2156 a = FONT_ITALIC; 2157 setfont: 2158 if (f & ~a) 2159 { 2160 sfputr(sp, font(f, style, 0), -1); 2161 f = 0; 2162 } 2163 if (!f && style == STYLE_html) 2164 { 2165 for (t = s; *t && !isspace(*t) && !iscntrl(*t); t++); 2166 if (*t == c && *++t == '(') 2167 { 2168 w = t; 2169 if (isdigit(*++t)) 2170 while (isupper(*++t)); 2171 if (*t == ')' && t > w + 1) 2172 { 2173 sfprintf(sp, "<NOBR><A href=\"../man%-.*s/" 2174 , t - w - 1, w + 1 2175 ); 2176 for (q = s; q < w - 1; q++) 2177 if (*q == ':' && q < w - 2 && *(q + 1) == ':') 2178 { 2179 sfputc(sp, '-'); 2180 q++; 2181 } 2182 else 2183 sfputc(sp, *q); 2184 sfprintf(sp, ".html\">%s%-.*s%s</A>%-.*s</NOBR>" 2185 , font(a, style, 1) 2186 , w - s - 1, s 2187 , font(a, style, 0) 2188 , t - w + 1, w 2189 ); 2190 s = t + 1; 2191 continue; 2192 } 2193 } 2194 } 2195 sfputr(sp, font(a, style, !!(f ^= a)), -1); 2196 continue; 2197 case '\b': 2198 a = FONT_BOLD; 2199 goto setfont; 2200 case '\f': 2201 if (style != STYLE_keys) 2202 { 2203 psp = info(psp, s, NiL, ip, id); 2204 if (psp->nb) 2205 s = psp->nb; 2206 else 2207 { 2208 s = psp->ob; 2209 psp = psp->next; 2210 } 2211 } 2212 continue; 2213 case '\v': 2214 a = FONT_LITERAL; 2215 goto setfont; 2216 case ' ': 2217 if (ident && *s == '$') 2218 { 2219 while (*++s) 2220 if (*s == ']') 2221 { 2222 if (*(s + 1) != ']') 2223 break; 2224 s++; 2225 } 2226 continue; 2227 } 2228 case '\n': 2229 case '\r': 2230 case '\t': 2231 while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n') 2232 s++; 2233 if (*s == ']' && *(s + 1) != ']' && (!psp || !psp->ch)) 2234 continue; 2235 c = ' '; 2236 break; 2237 case '<': 2238 if (style == STYLE_html) 2239 { 2240 sfputr(sp, "<", -1); 2241 c = 0; 2242 for (t = s; *t; t++) 2243 if (!isalnum(*t) && *t != '_' && *t != '.' && *t != '-') 2244 { 2245 if (*t == '@') 2246 { 2247 if (c) 2248 break; 2249 c = 1; 2250 } 2251 else if (*t == '>') 2252 { 2253 if (c) 2254 { 2255 sfprintf(sp, "<A href=\"mailto:%-.*s\">%-.*s</A>>", t - s, s, t - s, s); 2256 s = t + 1; 2257 } 2258 break; 2259 } 2260 else 2261 break; 2262 } 2263 continue; 2264 } 2265 break; 2266 case '>': 2267 if (style == STYLE_html) 2268 { 2269 sfputr(sp, ">", -1); 2270 continue; 2271 } 2272 break; 2273 case '&': 2274 if (style == STYLE_html) 2275 { 2276 sfputr(sp, "&", -1); 2277 continue; 2278 } 2279 break; 2280 case '-': 2281 if (style == STYLE_nroff) 2282 sfputc(sp, '\\'); 2283 break; 2284 case '.': 2285 if (style == STYLE_nroff) 2286 { 2287 sfputc(sp, '\\'); 2288 sfputc(sp, '&'); 2289 } 2290 break; 2291 case '\\': 2292 if (style == STYLE_nroff) 2293 { 2294 sfputc(sp, c); 2295 c = 'e'; 2296 } 2297 break; 2298 } 2299 sfputc(sp, c); 2300 } 2301 } 2302 else if (c == '[' && level > lev) 2303 { 2304 s++; 2305 goto again; 2306 } 2307 return s; 2308 } 2309 2310 /* 2311 * generate optget() help [...] list from lp 2312 */ 2313 2314 static void 2315 list(Sfio_t* sp, register const List_t* lp) 2316 { 2317 sfprintf(sp, "[%c", lp->type); 2318 if (lp->name) 2319 { 2320 sfprintf(sp, "%s", lp->name); 2321 if (lp->text) 2322 sfprintf(sp, "?%s", lp->text); 2323 } 2324 sfputc(sp, ']'); 2325 } 2326 2327 /* 2328 * return pointer to help message sans `Usage: command' 2329 * if oopts is 0 then state.pass is used 2330 * what: 2331 * 0 ?short by default, ?long if any long options used 2332 * * otherwise see help_text[] (--???) 2333 * external formatter: 2334 * \a...\a italic 2335 * \b...\b bold 2336 * \f...\f discipline infof callback on ... 2337 * \v...\v literal 2338 * internal formatter: 2339 * \t indent 2340 * \n newline 2341 * margin flush pops to previous indent 2342 */ 2343 2344 char* 2345 opthelp(const char* oopts, const char* what) 2346 { 2347 register Sfio_t* sp; 2348 register Sfio_t* mp; 2349 register int c; 2350 register char* p; 2351 register Indent_t* ip; 2352 char* t; 2353 char* x; 2354 char* w; 2355 char* u; 2356 char* y; 2357 char* s; 2358 char* d; 2359 char* v; 2360 char* cb; 2361 char* dt; 2362 char* ov; 2363 char* pp; 2364 char* rb; 2365 char* re; 2366 int f; 2367 int i; 2368 int j; 2369 int m; 2370 int n; 2371 int a; 2372 int cl; 2373 int sl; 2374 int vl; 2375 int ol; 2376 int wl; 2377 int xl; 2378 int rm; 2379 int ts; 2380 int co; 2381 int z; 2382 int style; 2383 int head; 2384 int margin; 2385 int mode; 2386 int mutex; 2387 int prefix; 2388 int version; 2389 long tp; 2390 char* id; 2391 char* catalog; 2392 Optpass_t* o; 2393 Optpass_t* q; 2394 Optpass_t* e; 2395 Optpass_t one; 2396 Optpass_t top; 2397 Help_t* hp; 2398 Tag_t ptstk[elementsof(indent) + 2]; 2399 Tag_t* pt; 2400 Sfio_t* vp; 2401 Push_t* tsp; 2402 2403 char* opts = (char*)oopts; 2404 char* section = "1"; 2405 int flags = 0; 2406 int bflags = 0; 2407 int dflags = 0; 2408 int hflags = 0; 2409 int matched = 0; 2410 int paragraph = 0; 2411 Push_t* psp = 0; 2412 Sfio_t* sp_help = 0; 2413 Sfio_t* sp_text = 0; 2414 Sfio_t* sp_plus = 0; 2415 Sfio_t* sp_head = 0; 2416 Sfio_t* sp_body = 0; 2417 Sfio_t* sp_info = 0; 2418 Sfio_t* sp_misc = 0; 2419 2420 if (!(mp = state.mp) && !(mp = state.mp = sfstropen())) 2421 goto nospace; 2422 if (!what) 2423 style = state.style; 2424 else if (!*what) 2425 style = STYLE_options; 2426 else if (*what != '?') 2427 style = STYLE_match; 2428 else if (!*(what + 1)) 2429 style = STYLE_man; 2430 else if ((hp = (Help_t*)search(styles, elementsof(styles), sizeof(styles[0]), (char*)what + 1)) && hp->style >= 0) 2431 { 2432 style = hp->style; 2433 if (*hp->name != '?') 2434 what = hp->name; 2435 } 2436 else 2437 { 2438 if ((style = state.force) < STYLE_man) 2439 style = STYLE_man; 2440 if (!(sp_help = sfstropen())) 2441 goto nospace; 2442 for (i = 0; i < elementsof(help_head); i++) 2443 list(sp_help, &help_head[i]); 2444 for (i = 0; i < elementsof(styles); i++) 2445 sfprintf(sp_help, "[:%s?%s]", styles[i].match, styles[i].text); 2446 for (i = 0; i < elementsof(help_tail); i++) 2447 list(sp_help, &help_tail[i]); 2448 if (!(opts = sfstruse(sp_help))) 2449 goto nospace; 2450 } 2451 2452 /* 2453 * this is a workaround for static optjoin() data 2454 * clobbered by plugins/builtins that may be called 2455 * evaluating \f...\f -- it would be good to hide 2456 * optjoin() interactions a bit more ... 2457 */ 2458 2459 top = state.pass[0]; 2460 again: 2461 if (opts) 2462 { 2463 for (i = 0; i < state.npass; i++) 2464 if (state.pass[i].oopts == opts) 2465 { 2466 o = &state.pass[i]; 2467 break; 2468 } 2469 if (i >= state.npass) 2470 { 2471 o = &one; 2472 if (init((char*)opts, o)) 2473 goto nospace; 2474 } 2475 e = o + 1; 2476 } 2477 else 2478 { 2479 if (state.npass > 0) 2480 { 2481 o = state.pass; 2482 e = o + state.npass; 2483 } 2484 else if (state.npass < 0) 2485 { 2486 o = &state.cache->pass; 2487 e = o + 1; 2488 } 2489 else 2490 return T(NiL, ID, "[* call optget() before opthelp() *]"); 2491 oopts = (const char*)state.pass[0].oopts; 2492 } 2493 if (style <= STYLE_usage) 2494 { 2495 if (!(sp_text = sfstropen()) || !(sp_info = sfstropen())) 2496 goto nospace; 2497 if (style >= STYLE_match && style < STYLE_keys && !(sp_body = sfstropen())) 2498 goto nospace; 2499 } 2500 switch (style) 2501 { 2502 case STYLE_api: 2503 case STYLE_html: 2504 case STYLE_nroff: 2505 state.emphasis = 0; 2506 break; 2507 case STYLE_usage: 2508 case STYLE_keys: 2509 for (q = o; q < e; q++) 2510 if (!(q->flags & OPT_ignore) && !streq(q->catalog, o->catalog)) 2511 o = q; 2512 /*FALLTHROUGH*/ 2513 case STYLE_posix: 2514 sfputc(mp, '\f'); 2515 break; 2516 default: 2517 if (!state.emphasis) 2518 { 2519 if (x = getenv("ERROR_OPTIONS")) 2520 { 2521 if (strmatch(x, "*noemphasi*")) 2522 break; 2523 if (strmatch(x, "*emphasi*")) 2524 { 2525 state.emphasis = 1; 2526 break; 2527 } 2528 } 2529 if ((x = getenv("TERM")) && strmatch(x, "(ansi|vt100|xterm)*") && isatty(sffileno(sfstderr))) 2530 state.emphasis = 1; 2531 } 2532 break; 2533 } 2534 x = ""; 2535 xl = 0; 2536 for (q = o; q < e; q++) 2537 { 2538 if (q->flags & OPT_ignore) 2539 continue; 2540 section = q->section; 2541 flags |= q->flags; 2542 p = q->opts; 2543 prefix = q->prefix; 2544 version = q->version; 2545 id = q->id; 2546 catalog = q->catalog; 2547 switch (style) 2548 { 2549 case STYLE_usage: 2550 if (xl) 2551 sfputc(mp, '\n'); 2552 else 2553 xl = 1; 2554 psp = 0; 2555 for (;;) 2556 { 2557 switch (c = *p++) 2558 { 2559 case 0: 2560 if (!(tsp = psp)) 2561 goto style_usage; 2562 p = psp->ob; 2563 psp = psp->next; 2564 free(tsp); 2565 continue; 2566 case '\a': 2567 c = 'a'; 2568 break; 2569 case '\b': 2570 c = 'b'; 2571 break; 2572 case '\f': 2573 psp = info(psp, p, NiL, sp_info, id); 2574 if (psp->nb) 2575 p = psp->nb; 2576 else 2577 { 2578 p = psp->ob; 2579 psp = psp->next; 2580 } 2581 continue; 2582 case '\n': 2583 c = 'n'; 2584 break; 2585 case '\r': 2586 c = 'r'; 2587 break; 2588 case '\t': 2589 c = 't'; 2590 break; 2591 case '\v': 2592 c = 'v'; 2593 break; 2594 case '"': 2595 c = '"'; 2596 break; 2597 case '\'': 2598 c = '\''; 2599 break; 2600 case '\\': 2601 c = '\\'; 2602 break; 2603 default: 2604 sfputc(mp, c); 2605 continue; 2606 } 2607 sfputc(mp, '\\'); 2608 sfputc(mp, c); 2609 } 2610 style_usage: 2611 continue; 2612 case STYLE_keys: 2613 a = 0; 2614 psp = 0; 2615 vl = 0; 2616 for (;;) 2617 { 2618 if (!(c = *p++)) 2619 { 2620 if (!(tsp = psp)) 2621 break; 2622 p = psp->ob; 2623 psp = psp->next; 2624 free(tsp); 2625 continue; 2626 } 2627 if (c == '\f') 2628 { 2629 psp = info(psp, p, NiL, sp_info, id); 2630 if (psp->nb) 2631 p = psp->nb; 2632 else 2633 { 2634 p = psp->ob; 2635 psp = psp->next; 2636 } 2637 continue; 2638 } 2639 f = z = 1; 2640 t = 0; 2641 if (a == 0 && (c == ' ' || c == '\n' && *p == '\n')) 2642 { 2643 if (c == ' ' && *p == ']') 2644 { 2645 p++; 2646 continue; 2647 } 2648 if (*p == '\n') 2649 p++; 2650 a = c; 2651 } 2652 else if (c == '\n') 2653 { 2654 if (a == ' ') 2655 a = -1; 2656 else if (a == '\n' || *p == '\n') 2657 { 2658 a = -1; 2659 p++; 2660 } 2661 continue; 2662 } 2663 else if ((c == ':' || c == '#') && (*p == '[' || *p == '?' && *(p + 1) == '[' && p++)) 2664 p++; 2665 else if (c != '[') 2666 { 2667 if (c == GO) 2668 vl++; 2669 else if (c == OG) 2670 vl--; 2671 continue; 2672 } 2673 else if (*p == ' ') 2674 { 2675 p++; 2676 continue; 2677 } 2678 else if (*p == '-') 2679 { 2680 z = 0; 2681 if (*++p == '-') 2682 { 2683 p = skip(p, 0, 0, 0, 1, 0, 1, version); 2684 continue; 2685 } 2686 } 2687 else if (*p == '+') 2688 { 2689 p++; 2690 if (vl > 0 && *p != '\a') 2691 { 2692 f = 0; 2693 p = skip(p, '?', 0, 0, 1, 0, 0, version); 2694 if (*p == '?') 2695 p++; 2696 } 2697 } 2698 else 2699 { 2700 if (*(p + 1) == '\f' && (vp = state.vp)) 2701 p = expand(p + 2, NiL, &t, vp, id); 2702 p = skip(p, ':', '?', 0, 1, 0, 0, version); 2703 if (*p == ':') 2704 p++; 2705 } 2706 if (f && *p == '?' && *(p + 1) != '?') 2707 { 2708 f = 0; 2709 if (z) 2710 p++; 2711 else 2712 p = skip(p, 0, 0, 0, 1, 0, 0, version); 2713 } 2714 if (*p == ']' && *(p + 1) != ']') 2715 { 2716 p++; 2717 continue; 2718 } 2719 if (!*p) 2720 { 2721 if (!t) 2722 break; 2723 p = t; 2724 t = 0; 2725 } 2726 m = sfstrtell(mp); 2727 sfputc(mp, '"'); 2728 xl = 1; 2729 /*UNDENT...*/ 2730 2731 for (;;) 2732 { 2733 if (!(c = *p++)) 2734 { 2735 if (t) 2736 { 2737 p = t; 2738 t = 0; 2739 } 2740 if (!(tsp = psp)) 2741 { 2742 p--; 2743 break; 2744 } 2745 p = psp->ob; 2746 psp = psp->next; 2747 free(tsp); 2748 continue; 2749 } 2750 if (a > 0) 2751 { 2752 if (c == '\n') 2753 { 2754 if (a == ' ') 2755 { 2756 a = -1; 2757 break; 2758 } 2759 if (a == '\n' || *p == '\n') 2760 { 2761 a = -1; 2762 p++; 2763 break; 2764 } 2765 } 2766 } 2767 else if (c == ']') 2768 { 2769 if (*p != ']') 2770 { 2771 sfputc(mp, 0); 2772 y = sfstrbase(mp) + m + 1; 2773 if (D(y) || !strmatch(y, KEEP) || strmatch(y, OMIT)) 2774 { 2775 sfstrseek(mp, m, SEEK_SET); 2776 xl = 0; 2777 } 2778 else 2779 sfstrseek(mp, -1, SEEK_CUR); 2780 break; 2781 } 2782 sfputc(mp, *p++); 2783 continue; 2784 } 2785 switch (c) 2786 { 2787 case '?': 2788 if (f) 2789 { 2790 if (*p == '?') 2791 { 2792 p++; 2793 sfputc(mp, c); 2794 } 2795 else 2796 { 2797 f = 0; 2798 sfputc(mp, 0); 2799 y = sfstrbase(mp) + m + 1; 2800 if (D(y) || !strmatch(y, KEEP) || strmatch(y, OMIT)) 2801 { 2802 sfstrseek(mp, m, SEEK_SET); 2803 xl = 0; 2804 } 2805 else 2806 sfstrseek(mp, -1, SEEK_CUR); 2807 if (z && (*p != ']' || *(p + 1) == ']')) 2808 { 2809 if (xl) 2810 { 2811 sfputc(mp, '"'); 2812 sfputc(mp, '\n'); 2813 } 2814 m = sfstrtell(mp); 2815 sfputc(mp, '"'); 2816 xl = 1; 2817 } 2818 else 2819 { 2820 p = skip(p, 0, 0, 0, 1, 0, 0, version); 2821 if (*p == '?') 2822 p++; 2823 } 2824 } 2825 } 2826 else 2827 sfputc(mp, c); 2828 continue; 2829 case ':': 2830 if (f && *p == ':') 2831 p++; 2832 sfputc(mp, c); 2833 continue; 2834 case '\a': 2835 c = 'a'; 2836 break; 2837 case '\b': 2838 c = 'b'; 2839 break; 2840 case '\f': 2841 c = 'f'; 2842 break; 2843 case '\n': 2844 c = 'n'; 2845 break; 2846 case '\r': 2847 c = 'r'; 2848 break; 2849 case '\t': 2850 c = 't'; 2851 break; 2852 case '\v': 2853 c = 'v'; 2854 break; 2855 case '"': 2856 c = '"'; 2857 break; 2858 case '\\': 2859 c = '\\'; 2860 break; 2861 case CC_esc: 2862 c = 'E'; 2863 break; 2864 default: 2865 sfputc(mp, c); 2866 continue; 2867 } 2868 sfputc(mp, '\\'); 2869 sfputc(mp, c); 2870 } 2871 2872 /*...INDENT*/ 2873 if (xl) 2874 { 2875 sfputc(mp, '"'); 2876 sfputc(mp, '\n'); 2877 } 2878 } 2879 continue; 2880 } 2881 z = 0; 2882 head = 0; 2883 mode = 0; 2884 mutex = 0; 2885 if (style > STYLE_short && style < STYLE_nroff && version < 1) 2886 { 2887 style = STYLE_short; 2888 if (sp_body) 2889 { 2890 sfclose(sp_body); 2891 sp_body = 0; 2892 } 2893 } 2894 else if (style == STYLE_short && prefix < 2) 2895 style = STYLE_long; 2896 if (*p == ':') 2897 p++; 2898 if (*p == '+') 2899 { 2900 p++; 2901 if (!(sp = sp_plus) && !(sp = sp_plus = sfstropen())) 2902 goto nospace; 2903 } 2904 else if (style >= STYLE_match) 2905 sp = sp_body; 2906 else 2907 sp = sp_text; 2908 psp = 0; 2909 for (;;) 2910 { 2911 if (!(*(p = next(p, version)))) 2912 { 2913 if (!(tsp = psp)) 2914 break; 2915 p = psp->ob; 2916 psp = psp->next; 2917 free(tsp); 2918 continue; 2919 } 2920 if (*p == '\f') 2921 { 2922 psp = info(psp, p + 1, NiL, sp_info, id); 2923 if (psp->nb) 2924 p = psp->nb; 2925 else 2926 { 2927 p = psp->ob; 2928 psp = psp->next; 2929 } 2930 continue; 2931 } 2932 if (*p == '\n' || *p == ' ') 2933 { 2934 if (*(x = p = next(p + 1, version))) 2935 while (*++p) 2936 if (*p == '\n') 2937 { 2938 while (*++p == ' ' || *p == '\t' || *p == '\r'); 2939 if (*p == '\n') 2940 break; 2941 } 2942 xl = p - x; 2943 if (!*p) 2944 break; 2945 continue; 2946 } 2947 if (*p == OG) 2948 { 2949 p++; 2950 continue; 2951 } 2952 message((-20, "opthelp: opt %s", show(p))); 2953 if (z < 0) 2954 z = 0; 2955 a = 0; 2956 f = 0; 2957 w = 0; 2958 d = 0; 2959 s = 0; 2960 rb = re = 0; 2961 sl = 0; 2962 vl = 0; 2963 if (*p == '[') 2964 { 2965 if ((c = *(p = next(p + 1, version))) == '(') 2966 { 2967 p = nest(cb = p); 2968 cl = p - cb; 2969 c = *p; 2970 } 2971 else 2972 cb = 0; 2973 if (c == '-') 2974 { 2975 if (style >= STYLE_man) 2976 { 2977 if (*(p + 1) != '-') 2978 { 2979 if (!sp_misc && !(sp_misc = sfstropen())) 2980 goto nospace; 2981 else 2982 p = textout(sp_misc, p, cb, cl, style, 1, 3, sp_info, version, id, catalog, &hflags); 2983 continue; 2984 } 2985 } 2986 else if (style == STYLE_match && *what == '-') 2987 { 2988 if (*(p + 1) == '?' || *(s = skip(p + 1, ':', '?', 0, 1, 0, 0, version)) == '?' && isspace(*(s + 1))) 2989 s = C("version"); 2990 else 2991 s = p + 1; 2992 w = (char*)what; 2993 if (*s != '-' || *(w + 1) == '-') 2994 { 2995 if (*s == '-') 2996 s++; 2997 if (*(w + 1) == '-') 2998 w++; 2999 if (match(w + 1, s, version, id, catalog)) 3000 { 3001 if (*(p + 1) == '-') 3002 p++; 3003 p = textout(sp, p, cb, cl, style, 1, 3, sp_info, version, id, catalog, &hflags); 3004 matched = -1; 3005 continue; 3006 } 3007 } 3008 } 3009 if (!z) 3010 z = -1; 3011 } 3012 else if (c == '+') 3013 { 3014 if (style >= STYLE_man) 3015 { 3016 p = textout(sp_body, p, cb, cl, style, 0, 0, sp_info, version, id, catalog, &bflags); 3017 if (!sp_head) 3018 { 3019 sp_head = sp_body; 3020 hflags = dflags = bflags; 3021 if (!(sp_body = sfstropen())) 3022 goto nospace; 3023 } 3024 continue; 3025 } 3026 else if (style == STYLE_match && *what == '+') 3027 { 3028 if (paragraph) 3029 { 3030 if (p[1] == '?') 3031 { 3032 p = textout(sp, p, cb, cl, style, 1, 3, sp_info, version, id, catalog, &hflags); 3033 continue; 3034 } 3035 paragraph = 0; 3036 } 3037 if (match((char*)what + 1, p + 1, version, id, catalog)) 3038 { 3039 p = textout(sp, p, cb, cl, style, 1, 3, sp_info, version, id, catalog, &hflags); 3040 matched = -1; 3041 paragraph = 1; 3042 continue; 3043 } 3044 } 3045 if (!z) 3046 z = -1; 3047 } 3048 else if (c == '[' || version < 1) 3049 { 3050 mutex++; 3051 continue; 3052 } 3053 else 3054 { 3055 if (c == '!') 3056 { 3057 a |= OPT_invert; 3058 p++; 3059 } 3060 rb = p; 3061 if (*p != ':') 3062 { 3063 s = p; 3064 if (*(p + 1) == '|') 3065 { 3066 while (*++p && *p != '=' && *p != '!' && *p != ':' && *p != '?'); 3067 if ((p - s) > 1) 3068 sl = p - s; 3069 if (*p == '!') 3070 a |= OPT_invert; 3071 } 3072 if (*(p + 1) == '\f') 3073 p++; 3074 else 3075 p = skip(p, ':', '?', 0, 1, 0, 0, version); 3076 if (sl || (p - s) == 1 || *(s + 1) == '=' || *(s + 1) == '!' && (a |= OPT_invert) || *(s + 1) == '|') 3077 f = *s; 3078 } 3079 re = p; 3080 if (style <= STYLE_short) 3081 { 3082 if (!z && !f) 3083 z = -1; 3084 } 3085 else 3086 { 3087 if (*p == '\f' && (vp = state.vp)) 3088 p = expand(p + 1, NiL, &t, vp, id); 3089 else 3090 t = 0; 3091 if (*p == ':') 3092 { 3093 p = skip(w = p + 1, ':', '?', 0, 1, 0, 0, version); 3094 if (!(wl = p - w)) 3095 w = 0; 3096 } 3097 else 3098 wl = 0; 3099 if (*p == ':' || *p == '?') 3100 { 3101 d = p; 3102 p = skip(p, 0, 0, 0, 1, 0, 0, version); 3103 } 3104 else 3105 d = 0; 3106 if (style == STYLE_match) 3107 { 3108 if (wl && !match((char*)what, w, version, id, catalog)) 3109 wl = 0; 3110 if ((!wl || *w == ':' || *w == '?') && (what[1] || sl && !memchr(s, what[0], sl) || !sl && what[0] != f)) 3111 { 3112 w = 0; 3113 if (!z) 3114 z = -1; 3115 } 3116 else 3117 matched = 1; 3118 } 3119 if (t) 3120 { 3121 p = t; 3122 if (*p == ':' || *p == '?') 3123 { 3124 d = p; 3125 p = skip(p, 0, 0, 0, 1, 0, 0, version); 3126 } 3127 } 3128 } 3129 } 3130 p = skip(p, 0, 0, 0, 1, 0, 1, version); 3131 if (*p == GO) 3132 p = skip(p + 1, 0, 0, 0, 0, 1, 1, version); 3133 } 3134 else if (*p == ']') 3135 { 3136 if (mutex) 3137 { 3138 if (style >= STYLE_nroff) 3139 sfputr(sp_body, "\n.OP - - anyof", '\n'); 3140 if (!(mutex & 1)) 3141 { 3142 mutex--; 3143 if (style <= STYLE_long) 3144 { 3145 sfputc(sp_body, ' '); 3146 sfputc(sp_body, ']'); 3147 } 3148 } 3149 mutex--; 3150 } 3151 p++; 3152 continue; 3153 } 3154 else if (*p == '?') 3155 { 3156 if (style < STYLE_match) 3157 z = 1; 3158 mode |= OPT_hidden; 3159 p++; 3160 continue; 3161 } 3162 else if (*p == '\\' && style==STYLE_posix) 3163 { 3164 if (*++p) 3165 p++; 3166 continue; 3167 } 3168 else 3169 { 3170 f = *p++; 3171 s = 0; 3172 if (style == STYLE_match && !z) 3173 z = -1; 3174 } 3175 if (!z) 3176 { 3177 if (style == STYLE_long || prefix < 2 || (q->flags & OPT_long)) 3178 f = 0; 3179 else if (style <= STYLE_short) 3180 w = 0; 3181 if (!f && !w) 3182 z = -1; 3183 } 3184 ov = 0; 3185 u = v = y = 0; 3186 if (*p == ':' && (a |= OPT_string) || *p == '#' && (a |= OPT_number)) 3187 { 3188 message((-21, "opthelp: arg %s", show(p))); 3189 if (*++p == '?' || *p == *(p - 1)) 3190 { 3191 p++; 3192 a |= OPT_optional; 3193 } 3194 if (*(p = next(p, version)) == '[') 3195 { 3196 if (!z) 3197 { 3198 p = skip(y = p + 1, ':', '?', 0, 1, 0, 0, version); 3199 while (*p == ':') 3200 { 3201 p = skip(t = p + 1, ':', '?', 0, 1, 0, 0, version); 3202 m = p - t; 3203 if (*t == '!') 3204 { 3205 ov = t + 1; 3206 ol = m - 1; 3207 } 3208 else if (*t == '=') 3209 { 3210 v = t + 1; 3211 vl = m - 1; 3212 } 3213 else 3214 for (j = 0; j < elementsof(attrs); j++) 3215 if (strneq(t, attrs[j].name, m)) 3216 { 3217 a |= attrs[j].flag; 3218 break; 3219 } 3220 } 3221 if (*p == '?') 3222 u = p; 3223 p = skip(p, 0, 0, 0, 1, 0, 1, version); 3224 } 3225 else 3226 p = skip(p + 1, 0, 0, 0, 1, 0, 1, version); 3227 } 3228 else 3229 y = (a & OPT_number) ? T(NiL, ID, "#") : T(NiL, ID, "arg"); 3230 } 3231 else 3232 a |= OPT_flag; 3233 if (!z) 3234 { 3235 if (style <= STYLE_short && !y && !mutex || style == STYLE_posix) 3236 { 3237 if (style != STYLE_posix && !sfstrtell(sp)) 3238 { 3239 sfputc(sp, '['); 3240 if (sp == sp_plus) 3241 sfputc(sp, '+'); 3242 sfputc(sp, '-'); 3243 } 3244 if (!sl) 3245 sfputc(sp, f); 3246 else 3247 for (c = 0; c < sl; c++) 3248 if (s[c] != '|') 3249 sfputc(sp, s[c]); 3250 if (style == STYLE_posix && y) 3251 sfputc(sp, ':'); 3252 } 3253 else 3254 { 3255 if (style >= STYLE_match) 3256 { 3257 sfputc(sp_body, '\n'); 3258 if (!head) 3259 { 3260 head = 1; 3261 item(sp_body, (flags & OPT_functions) ? C("FUNCTIONS") : C("OPTIONS"), 0, 0, style, sp_info, version, id, ID, &bflags); 3262 } 3263 if (style >= STYLE_nroff) 3264 { 3265 if (mutex & 1) 3266 { 3267 mutex++; 3268 sfputr(sp_body, "\n.OP - - oneof", '\n'); 3269 } 3270 } 3271 else 3272 sfputc(sp_body, '\t'); 3273 } 3274 else 3275 { 3276 if (sp_body) 3277 sfputc(sp_body, ' '); 3278 else if (!(sp_body = sfstropen())) 3279 goto nospace; 3280 if (mutex) 3281 { 3282 if (mutex & 1) 3283 { 3284 mutex++; 3285 sfputc(sp_body, '['); 3286 } 3287 else 3288 sfputc(sp_body, '|'); 3289 sfputc(sp_body, ' '); 3290 } 3291 else 3292 sfputc(sp_body, '['); 3293 } 3294 if (style >= STYLE_nroff) 3295 { 3296 if (flags & OPT_functions) 3297 { 3298 sfputr(sp_body, ".FN", ' '); 3299 if (re > rb) 3300 sfwrite(sp_body, rb, re - rb); 3301 else 3302 sfputr(sp, "void", -1); 3303 if (w) 3304 label(sp_body, ' ', w, 0, -1, 0, style, FONT_BOLD, sp_info, version, id, catalog); 3305 } 3306 else 3307 { 3308 sfputr(sp_body, ".OP", ' '); 3309 if (sl) 3310 sfwrite(sp_body, s, sl); 3311 else 3312 sfputc(sp_body, f ? f : '-'); 3313 sfputc(sp_body, ' '); 3314 if (w) 3315 { 3316 if (label(sp_body, 0, w, 0, -1, 0, style, 0, sp_info, version, id, catalog)) 3317 { 3318 sfputc(sp_body, '|'); 3319 label(sp_body, 0, w, 0, -1, 0, style, 0, sp_info, version, id, native); 3320 } 3321 } 3322 else 3323 sfputc(sp_body, '-'); 3324 sfputc(sp_body, ' '); 3325 m = a & OPT_TYPE; 3326 for (j = 0; j < elementsof(attrs); j++) 3327 if (m & attrs[j].flag) 3328 { 3329 sfputr(sp_body, attrs[j].name, -1); 3330 break; 3331 } 3332 if (m = (a & ~m) | mode) 3333 for (j = 0; j < elementsof(attrs); j++) 3334 if (m & attrs[j].flag) 3335 { 3336 sfputc(sp_body, ':'); 3337 sfputr(sp_body, attrs[j].name, -1); 3338 } 3339 sfputc(sp_body, ' '); 3340 if (y) 3341 label(sp_body, 0, y, 0, -1, 0, style, 0, sp_info, version, id, catalog); 3342 else 3343 sfputc(sp_body, '-'); 3344 if (v) 3345 sfprintf(sp_body, " %-.*s", vl, v); 3346 } 3347 } 3348 else 3349 { 3350 if (f) 3351 { 3352 if (sp_body == sp_plus) 3353 sfputc(sp_body, '+'); 3354 sfputc(sp_body, '-'); 3355 sfputr(sp_body, font(FONT_BOLD, style, 1), -1); 3356 if (!sl) 3357 { 3358 sfputc(sp_body, f); 3359 if (f == '-' && y) 3360 { 3361 y = 0; 3362 sfputr(sp_body, C("long-option[=value]"), -1); 3363 } 3364 } 3365 else 3366 sfwrite(sp_body, s, sl); 3367 sfputr(sp_body, font(FONT_BOLD, style, 0), -1); 3368 if (w) 3369 { 3370 sfputc(sp_body, ','); 3371 sfputc(sp_body, ' '); 3372 } 3373 } 3374 else if ((flags & OPT_functions) && re > rb) 3375 { 3376 sfwrite(sp_body, rb, re - rb); 3377 sfputc(sp_body, ' '); 3378 } 3379 if (w) 3380 { 3381 if (prefix > 0) 3382 { 3383 sfputc(sp_body, '-'); 3384 if (prefix > 1) 3385 sfputc(sp_body, '-'); 3386 } 3387 if (label(sp_body, 0, w, 0, -1, 0, style, FONT_BOLD, sp_info, version, id, catalog)) 3388 { 3389 sfputc(sp_body, '|'); 3390 label(sp_body, 0, w, 0, -1, 0, style, FONT_BOLD, sp_info, version, id, native); 3391 } 3392 } 3393 if (y) 3394 { 3395 if (a & OPT_optional) 3396 sfputc(sp_body, '['); 3397 else if (!w) 3398 sfputc(sp_body, ' '); 3399 if (w) 3400 sfputc(sp_body, prefix == 1 ? ' ' : '='); 3401 label(sp_body, 0, y, 0, -1, 0, style, FONT_ITALIC, sp_info, version, id, catalog); 3402 if (a & OPT_optional) 3403 sfputc(sp_body, ']'); 3404 } 3405 } 3406 if (style >= STYLE_match) 3407 { 3408 if (d) 3409 { 3410 textout(sp_body, d, cb, cl, style, 0, 3, sp_info, version, id, catalog, &bflags); 3411 cb = 0; 3412 } 3413 if (u) 3414 textout(sp_body, u, cb, cl, style, 0, 3, sp_info, version, id, catalog, &bflags); 3415 if ((a & OPT_invert) && w && (d || u)) 3416 { 3417 u = skip(w, ':', '?', 0, 1, 0, 0, version); 3418 if (f) 3419 sfprintf(sp_info, " %s; -\b%c\b %s --\bno%-.*s\b.", T(NiL, ID, "On by default"), f, T(NiL, ID, "means"), u - w, w); 3420 else 3421 sfprintf(sp_info, " %s %s\bno%-.*s\b %s.", T(NiL, ID, "On by default; use"), "--"+2-prefix, u - w, w, T(NiL, ID, "to turn off")); 3422 if (!(t = sfstruse(sp_info))) 3423 goto nospace; 3424 textout(sp_body, t, 0, 0, style, 0, 0, sp_info, version, NiL, NiL, &bflags); 3425 } 3426 if (*p == GO) 3427 { 3428 p = u ? skip(p + 1, 0, 0, 0, 0, 1, 1, version) : textout(sp_body, p, 0, 0, style, 4, 0, sp_info, version, id, catalog, &bflags); 3429 y = "+?"; 3430 } 3431 else 3432 y = " "; 3433 if (a & OPT_optional) 3434 { 3435 if (ov) 3436 { 3437 sfprintf(sp_info, "%s%s \b", y, T(NiL, ID, "If the option value is omitted then")); 3438 t = ov + ol; 3439 while (ov < t) 3440 { 3441 if (((c = *ov++) == ':' || c == '?') && *ov == c) 3442 ov++; 3443 sfputc(sp_info, c); 3444 } 3445 sfprintf(sp_info, "\b %s.", T(NiL, ID, "is assumed")); 3446 } 3447 else 3448 sfprintf(sp_info, "%s%s", y, T(NiL, ID, "The option value may be omitted.")); 3449 if (!(t = sfstruse(sp_info))) 3450 goto nospace; 3451 textout(sp_body, t, 0, 0, style, 4, 0, sp_info, version, NiL, NiL, &bflags); 3452 y = " "; 3453 } 3454 if (v) 3455 { 3456 sfprintf(sp_info, "%s%s \b", y, T(NiL, ID, "The default value is")); 3457 t = v + vl; 3458 while (v < t) 3459 { 3460 if (((c = *v++) == ':' || c == '?') && *v == c) 3461 v++; 3462 sfputc(sp_info, c); 3463 } 3464 sfputc(sp_info, '\b'); 3465 sfputc(sp_info, '.'); 3466 if (!(t = sfstruse(sp_info))) 3467 goto nospace; 3468 textout(sp_body, t, 0, 0, style, 4, 0, sp_info, version, NiL, NiL, &bflags); 3469 } 3470 } 3471 else if (!mutex) 3472 sfputc(sp_body, ']'); 3473 } 3474 if (*p == GO) 3475 { 3476 if (style >= STYLE_match) 3477 p = textout(sp_body, p, 0, 0, style, 4, 0, sp_info, version, id, catalog, &bflags); 3478 else 3479 p = skip(p + 1, 0, 0, 0, 0, 1, 1, version); 3480 } 3481 } 3482 else if (*p == GO) 3483 p = skip(p + 1, 0, 0, 0, 0, 1, 1, version); 3484 } 3485 psp = pop(psp); 3486 if (sp_misc) 3487 { 3488 if (!(p = sfstruse(sp_misc))) 3489 goto nospace; 3490 for (t = p; *t == '\t' || *t == '\n'; t++); 3491 if (*t) 3492 { 3493 item(sp_body, C("IMPLEMENTATION"), 0, 0, style, sp_info, version, id, ID, &bflags); 3494 sfputr(sp_body, p, -1); 3495 } 3496 } 3497 } 3498 if (oopts != o->oopts && oopts == top.oopts) 3499 state.pass[0] = top; 3500 version = o->version; 3501 id = o->id; 3502 catalog = o->catalog; 3503 if (style >= STYLE_keys) 3504 { 3505 if (sp_info) 3506 sfclose(sp_info); 3507 if (style == STYLE_keys && sfstrtell(mp) > 1) 3508 sfstrseek(mp, -1, SEEK_CUR); 3509 if (!(p = sfstruse(mp))) 3510 goto nospace; 3511 return opt_info.msg = p; 3512 } 3513 sp = sp_text; 3514 if (sfstrtell(sp) && style != STYLE_posix) 3515 sfputc(sp, ']'); 3516 if (style == STYLE_nroff) 3517 { 3518 char rd[64]; 3519 char ud[64]; 3520 3521 s = o->id; 3522 t = ud; 3523 while (t < &ud[sizeof(ud)-2] && (c = *s++)) 3524 { 3525 if (islower(c)) 3526 c = toupper(c); 3527 *t++ = c; 3528 } 3529 *t = 0; 3530 t = rd; 3531 if (s = o->release) 3532 { 3533 *t++ = ' '; 3534 while (t < &rd[sizeof(rd)-2] && (c = *s++) && c != ']') 3535 *t++ = c; 3536 } 3537 *t = 0; 3538 sfprintf(sp, "\ 3539 .\\\" format with nroff|troff|groff -man\n\ 3540 .TH %s %s%s\n\ 3541 .fp 5 CW\n\ 3542 .nr mH 5\n\ 3543 .de H0\n\ 3544 .nr mH 0\n\ 3545 .in 5n\n\ 3546 \\fB\\\\$1\\fP\n\ 3547 .in 7n\n\ 3548 ..\n\ 3549 .de H1\n\ 3550 .nr mH 1\n\ 3551 .in 7n\n\ 3552 \\fB\\\\$1\\fP\n\ 3553 .in 9n\n\ 3554 ..\n\ 3555 .de H2\n\ 3556 .nr mH 2\n\ 3557 .in 11n\n\ 3558 \\fB\\\\$1\\fP\n\ 3559 .in 13n\n\ 3560 ..\n\ 3561 .de H3\n\ 3562 .nr mH 3\n\ 3563 .in 15n\n\ 3564 \\fB\\\\$1\\fP\n\ 3565 .in 17n\n\ 3566 ..\n\ 3567 .de H4\n\ 3568 .nr mH 4\n\ 3569 .in 19n\n\ 3570 \\fB\\\\$1\\fP\n\ 3571 .in 21n\n\ 3572 ..\n\ 3573 .de OP\n\ 3574 .nr mH 0\n\ 3575 .ie !'\\\\$1'-' \\{\n\ 3576 .ds mO \\\\fB\\\\-\\\\$1\\\\fP\n\ 3577 .ds mS ,\\\\0\n\ 3578 .\\}\n\ 3579 .el \\{\n\ 3580 .ds mO \\\\&\n\ 3581 .ds mS \\\\&\n\ 3582 .\\}\n\ 3583 .ie '\\\\$2'-' \\{\n\ 3584 .if !'\\\\$4'-' .as mO \\\\0\\\\fI\\\\$4\\\\fP\n\ 3585 .\\}\n\ 3586 .el \\{\n\ 3587 .as mO \\\\*(mS\\\\fB%s\\\\$2\\\\fP\n\ 3588 .if !'\\\\$4'-' .as mO =\\\\fI\\\\$4\\\\fP\n\ 3589 .\\}\n\ 3590 .in 5n\n\ 3591 \\\\*(mO\n\ 3592 .in 9n\n\ 3593 ..\n\ 3594 .de SP\n\ 3595 .if \\\\n(mH==2 .in 9n\n\ 3596 .if \\\\n(mH==3 .in 13n\n\ 3597 .if \\\\n(mH==4 .in 17n\n\ 3598 ..\n\ 3599 .de FN\n\ 3600 .nr mH 0\n\ 3601 .in 5n\n\ 3602 \\\\$1 \\\\$2\n\ 3603 .in 9n\n\ 3604 ..\n\ 3605 .de DS\n\ 3606 .in +3n\n\ 3607 .ft 5\n\ 3608 .nf\n\ 3609 ..\n\ 3610 .de DE\n\ 3611 .fi\n\ 3612 .ft R\n\ 3613 .in -3n\n\ 3614 ..\n\ 3615 " 3616 , ud 3617 , section 3618 , rd 3619 , o->prefix == 2 ? "\\\\-\\\\-" : o->prefix == 1 ? "\\\\-" : "" 3620 ); 3621 } 3622 if (style == STYLE_match) 3623 { 3624 if (!matched) 3625 { 3626 if (hp = (Help_t*)search(styles, elementsof(styles), sizeof(styles[0]), (char*)what)) 3627 { 3628 if (!sp_help && !(sp_help = sfstropen())) 3629 goto nospace; 3630 sfprintf(sp_help, "[-][:%s?%s]", hp->match, hp->text); 3631 if (!(opts = sfstruse(sp_help))) 3632 goto nospace; 3633 goto again; 3634 } 3635 s = (char*)unknown; 3636 goto nope; 3637 } 3638 else if (matched < 0) 3639 x = 0; 3640 } 3641 if (sp_plus) 3642 { 3643 if (sfstrtell(sp_plus)) 3644 { 3645 if (sfstrtell(sp)) 3646 sfputc(sp, ' '); 3647 if (!(t = sfstruse(sp_plus))) 3648 goto nospace; 3649 sfputr(sp, t, ']'); 3650 } 3651 sfclose(sp_plus); 3652 } 3653 if (style >= STYLE_man) 3654 { 3655 if (sp_head) 3656 { 3657 if (!(t = sfstruse(sp_head))) 3658 goto nospace; 3659 for (; *t == '\n'; t++); 3660 sfputr(sp, t, '\n'); 3661 sfclose(sp_head); 3662 sp_head = 0; 3663 } 3664 if (x) 3665 item(sp, C("SYNOPSIS"), 0, 0, style, sp_info, version, id, ID, &hflags); 3666 } 3667 if (x) 3668 { 3669 for (t = x + xl; t > x && (*(t - 1) == '\n' || *(t - 1) == '\r'); t--); 3670 xl = t - x; 3671 if (style >= STYLE_match) 3672 { 3673 u = id; 3674 if (o->flags & OPT_functions) 3675 t = 0; 3676 else if (t = strchr(u, ':')) 3677 { 3678 if ((o->flags & OPT_module) && *(t + 1) == ':' && *(t + 2)) 3679 { 3680 u = t + 2; 3681 t = 0; 3682 } 3683 else 3684 *t = 0; 3685 } 3686 args(sp, x, xl, o->flags, style, sp_info, version, u, catalog); 3687 if (t) 3688 *t = ':'; 3689 x = 0; 3690 } 3691 } 3692 if (sp_body) 3693 { 3694 if (sfstrtell(sp_body)) 3695 { 3696 if (style < STYLE_match && sfstrtell(sp)) 3697 sfputc(sp, ' '); 3698 if (!(t = sfstruse(sp_body))) 3699 goto nospace; 3700 if (style == STYLE_html && !(dflags & HELP_head) && (bflags & HELP_head)) 3701 sfputr(sp, "\n</DIV>", '\n'); 3702 sfputr(sp, t, -1); 3703 } 3704 sfclose(sp_body); 3705 sp_body = 0; 3706 } 3707 if (x && style != STYLE_posix) 3708 args(sp, x, xl, flags, style, sp_info, version, id, catalog); 3709 if (sp_info) 3710 { 3711 sfclose(sp_info); 3712 sp_info = 0; 3713 } 3714 if (sp_misc) 3715 { 3716 sfclose(sp_misc); 3717 sp_misc = 0; 3718 } 3719 if (!(p = sfstruse(sp))) 3720 goto nospace; 3721 astwinsize(1, NiL, &state.width); 3722 if (state.width < 20) 3723 state.width = OPT_WIDTH; 3724 m = strlen((style <= STYLE_long && error_info.id && !strchr(error_info.id, '/')) ? error_info.id : id) + 1; 3725 margin = style == STYLE_api ? (8 * 1024) : (state.width - 1); 3726 if (!(state.flags & OPT_preformat)) 3727 { 3728 if (style >= STYLE_man || matched < 0) 3729 { 3730 sfputc(mp, '\f'); 3731 ts = 0; 3732 } 3733 else 3734 ts = OPT_USAGE + m; 3735 if (style == STYLE_html) 3736 { 3737 char ud[64]; 3738 3739 s = id; 3740 t = ud; 3741 while (t < &ud[sizeof(ud)-2] && (c = *s++)) 3742 { 3743 if (islower(c)) 3744 c = toupper(c); 3745 *t++ = c; 3746 } 3747 *t = 0; 3748 sfprintf(mp, "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">\n<HTML>\n<HEAD>\n<META name=\"generator\" content=\"optget (AT&T Research) 2011-11-11\">\n%s<TITLE>%s man document</TITLE>\n<STYLE type=\"text/css\">\ndiv.SH { padding-left:2em; text-indent:0em; }\ndiv.SY { padding-left:4em; text-indent:-2em; }\ndt { float:left; clear:both; }\ndd { margin-left:3em; }\n</STYLE>\n</HEAD>\n<BODY bgcolor=white>\n", (state.flags & OPT_proprietary) ? "<!--INTERNAL-->\n" : "", id); 3749 sfprintf(mp, "<H4><TABLE width=100%%><TR><TH align=left>%s ( %s ) <TH align=center><A href=\".\" title=\"Index\">%s</A><TH align=right>%s ( %s )</TR></TABLE></H4>\n<HR>\n", ud, section, T(NiL, ID, secname(section)), ud, section); 3750 co = 2; 3751 pt = ptstk; 3752 pt->level = 0; 3753 pt->id = TAG_DIV; 3754 } 3755 else 3756 co = 0; 3757 if ((rm = margin - ts) < OPT_MARGIN) 3758 rm = OPT_MARGIN; 3759 ip = indent; 3760 ip->stop = (ip+1)->stop = style >= STYLE_html ? 0 : 2; 3761 tp = 0; 3762 n = 0; 3763 head = 1; 3764 while (*p == '\n') 3765 p++; 3766 while (c = *p++) 3767 { 3768 if (c == '\n') 3769 { 3770 ip = indent; 3771 n = 0; 3772 tp = 0; 3773 sfputc(mp, '\n'); 3774 co = 0; 3775 rm = margin; 3776 ts = ip->stop; 3777 if (*p == '\n') 3778 { 3779 while (*++p == '\n'); 3780 if ((style == STYLE_man || style == STYLE_html) && (!head || *p != ' ' && *p != '\t')) 3781 { 3782 if (style == STYLE_man) 3783 p--; 3784 else 3785 sfprintf(mp, "<P>\n"); 3786 } 3787 } 3788 head = *p != ' ' && *p != '\t'; 3789 if (style == STYLE_html && (*p != '<' || !strneq(p, "<BR>", 4) && !strneq(p, "<P>", 3))) 3790 { 3791 y = p; 3792 while (*p == '\t') 3793 p++; 3794 if (*p == '\n') 3795 continue; 3796 j = p - y; 3797 if (j > pt->level) 3798 { 3799 pt++; 3800 pt->level = j; 3801 pt->id = TAG_NONE; 3802 for (y = p; *y && *y != '\n'; y++) 3803 if (*y == '\t') 3804 { 3805 pt->id = TAG_DL; 3806 sfprintf(mp, "<DL>\n"); 3807 break; 3808 } 3809 } 3810 else 3811 while (j < pt->level && pt > ptstk) 3812 { 3813 sfprintf(mp, "%s", end[pt->id]); 3814 pt--; 3815 } 3816 if (pt->id == TAG_DL) 3817 { 3818 dt = p; 3819 sfprintf(mp, "<DT>"); 3820 } 3821 else 3822 dt = 0; 3823 } 3824 } 3825 else if (c == '\t') 3826 { 3827 if (style == STYLE_html) 3828 { 3829 while (*p == '\t') 3830 p++; 3831 if (*p != '\n') 3832 { 3833 co += sfprintf(mp, "<DD>"); 3834 if (dt) 3835 { 3836 c = 0; 3837 m = 0; 3838 for (;;) 3839 { 3840 switch (*dt++) 3841 { 3842 case '\t': 3843 break; 3844 case '<': 3845 c = '>'; 3846 continue; 3847 case '>': 3848 if (c == '>') 3849 c = 0; 3850 else 3851 m++; 3852 continue; 3853 case '&': 3854 c = ';'; 3855 continue; 3856 case ';': 3857 if (c == ';') 3858 c = 0; 3859 m++; 3860 continue; 3861 default: 3862 if (!c) 3863 m++; 3864 continue; 3865 } 3866 break; 3867 } 3868 if (m >= 5) 3869 co += sfprintf(mp, "<BR>"); 3870 } 3871 } 3872 } 3873 else 3874 { 3875 if ((ip+1)->stop) 3876 { 3877 do 3878 { 3879 ip++; 3880 if (*p != '\t') 3881 break; 3882 p++; 3883 } while ((ip+1)->stop); 3884 if (*p == '\n') 3885 continue; 3886 ts = ip->stop; 3887 if (co >= ts) 3888 { 3889 sfputc(mp, '\n'); 3890 co = 0; 3891 rm = margin; 3892 ts = ip->stop; 3893 } 3894 } 3895 while (co < ts) 3896 { 3897 sfputc(mp, ' '); 3898 co++; 3899 } 3900 } 3901 } 3902 else 3903 { 3904 if (c == ' ' && !n) 3905 { 3906 if (co >= rm) 3907 tp = 0; 3908 else 3909 { 3910 tp = sfstrtell(mp); 3911 pp = p; 3912 } 3913 if (style == STYLE_nroff && !co) 3914 continue; 3915 } 3916 else if (style == STYLE_html) 3917 { 3918 if (c == '<') 3919 { 3920 if (strneq(p, "NOBR>", 5)) 3921 n++; 3922 else if (n && strneq(p, "/NOBR>", 6) && !--n) 3923 { 3924 for (y = p += 6; (c = *p) && c != ' ' && c != '\t' && c != '\n' && c != '<'; p++) 3925 if (c == '[') 3926 sfputr(mp, "[", -1); 3927 else if (c == ']') 3928 sfputr(mp, "]", -1); 3929 else 3930 sfputc(mp, c); 3931 sfwrite(mp, "</NOBR", 6); 3932 c = '>'; 3933 co += p - y + 6; 3934 } 3935 } 3936 else if (c == '>' && !n) 3937 { 3938 for (y = --p; (c = *p) && c != ' ' && c != '\t' && c != '\n' && c != '<'; p++) 3939 if (c == '[') 3940 sfputr(mp, "[", -1); 3941 else if (c == ']') 3942 sfputr(mp, "]", -1); 3943 else 3944 sfputc(mp, c); 3945 c = *sfstrseek(mp, -1, SEEK_CUR); 3946 if (p > y + 1) 3947 { 3948 tp = 0; 3949 co += p - y - 1; 3950 } 3951 if (co >= rm) 3952 tp = 0; 3953 else 3954 { 3955 tp = sfstrtell(mp); 3956 pp = p; 3957 } 3958 } 3959 else if (c == '[') 3960 { 3961 sfputr(mp, "[", -1); 3962 c = ';'; 3963 } 3964 else if (c == ']') 3965 { 3966 sfputr(mp, "]", -1); 3967 c = ';'; 3968 } 3969 else if (c == 'h') 3970 { 3971 y = p; 3972 if (*y++ == 't' && *y++ == 't' && *y++ == 'p' && (*y == ':' || *y++ == 's' && *y == ':') && *y++ == ':' && *y++ == '/' && *y++ == '/') 3973 { 3974 while (isalnum(*y) || *y == '_' || *y == '/' || *y == '-' || *y == '.') 3975 y++; 3976 if (*y == '?') 3977 while (isalnum(*y) || *y == '_' || *y == '/' || *y == '-' || *y == '.' || *y == '?' || *y == '=' || *y == '%' || *y == '&' || *y == ';' || *y == '#') 3978 y++; 3979 if (*(y - 1) == '.') 3980 y--; 3981 p--; 3982 sfprintf(mp, "<A href=\"%-.*s\">%-.*s</A", y - p, p, y - p, p); 3983 p = y; 3984 c = '>'; 3985 } 3986 } 3987 else if (c == 'C') 3988 { 3989 y = p; 3990 if (*y++ == 'o' && *y++ == 'p' && *y++ == 'y' && *y++ == 'r' && *y++ == 'i' && *y++ == 'g' && *y++ == 'h' && *y++ == 't' && *y++ == ' ' && *y++ == '(' && (*y++ == 'c' || *(y - 1) == 'C') && *y++ == ')') 3991 { 3992 sfputr(mp, "Copyright ©", -1); 3993 p = y; 3994 c = ';'; 3995 } 3996 } 3997 } 3998 else if (c == ']') 3999 { 4000 if (n) 4001 n--; 4002 } 4003 else if (c == '[') 4004 n++; 4005 if (c == CC_esc) 4006 { 4007 sfputc(mp, c); 4008 do 4009 { 4010 if (!(c = *p++)) 4011 { 4012 p--; 4013 break; 4014 } 4015 sfputc(mp, c); 4016 } while (c < 'a' || c > 'z'); 4017 } 4018 else if (co++ >= rm && !n) 4019 { 4020 if (tp) 4021 { 4022 if (*sfstrseek(mp, tp, SEEK_SET) != ' ') 4023 sfstrseek(mp, 1, SEEK_CUR); 4024 tp = 0; 4025 p = pp; 4026 n = 0; 4027 } 4028 else if (c != ' ' && c != '\n') 4029 sfputc(mp, c); 4030 if (*p == ' ') 4031 p++; 4032 if (*p != '\n') 4033 { 4034 sfputc(mp, '\n'); 4035 for (co = 0; co < ts; co++) 4036 sfputc(mp, ' '); 4037 rm = margin; 4038 } 4039 } 4040 else 4041 sfputc(mp, c); 4042 } 4043 } 4044 for (d = sfstrbase(mp), t = sfstrseek(mp, 0, SEEK_CUR); t > d && ((c = *(t - 1)) == '\n' || c == '\r' || c == ' ' || c == '\t'); t--); 4045 sfstrseek(mp, t - d, SEEK_SET); 4046 if (style == STYLE_html) 4047 { 4048 sfprintf(mp, "\n"); 4049 while (pt > ptstk) 4050 { 4051 sfprintf(mp, "%s", end[pt->id]); 4052 pt--; 4053 } 4054 sfprintf(mp, "</DIV>\n</BODY>\n</HTML>"); 4055 } 4056 } 4057 else 4058 sfputr(mp, p, 0); 4059 if (!(p = sfstruse(mp))) 4060 goto nospace; 4061 if (sp) 4062 sfclose(sp); 4063 return opt_info.msg = p; 4064 nospace: 4065 s = T(NiL, ID, "[* out of space *]"); 4066 nope: 4067 if (psp) 4068 pop(psp); 4069 if (sp_help) 4070 sfclose(sp_help); 4071 if (sp_text) 4072 sfclose(sp_text); 4073 if (sp_plus) 4074 sfclose(sp_plus); 4075 if (sp_info) 4076 sfclose(sp_info); 4077 if (sp_head) 4078 sfclose(sp_head); 4079 if (sp_body) 4080 sfclose(sp_body); 4081 if (sp_misc) 4082 sfclose(sp_misc); 4083 return s; 4084 } 4085 4086 /* 4087 * compatibility wrapper to opthelp() 4088 */ 4089 4090 char* 4091 optusage(const char* opts) 4092 { 4093 return opthelp(opts, NiL); 4094 } 4095 4096 /* 4097 * convert number using strtonll() *except* that 4098 * 0*[[:digit:]].* is treated as [[:digit:]].* 4099 * i.e., it looks octal but isn't, to meet 4100 * posix Utility Argument Syntax -- use 4101 * 0x.* or <base>#* for alternate bases 4102 */ 4103 4104 static intmax_t 4105 optnumber(const char* s, char** t, int* e) 4106 { 4107 intmax_t n; 4108 int oerrno; 4109 4110 while (*s == '0' && isdigit(*(s + 1))) 4111 s++; 4112 oerrno = errno; 4113 errno = 0; 4114 n = strtonll(s, t, NiL, 0); 4115 if (e) 4116 *e = errno; 4117 errno = oerrno; 4118 return n; 4119 } 4120 4121 /* 4122 * point opt_info.arg to an error/info message for opt_info.name 4123 * p points to opts location for opt_info.name 4124 * optget() return value is returned 4125 */ 4126 4127 static int 4128 opterror(register char* p, int err, int version, char* id, char* catalog) 4129 { 4130 register Sfio_t* mp; 4131 register Sfio_t* tp; 4132 register char* s; 4133 register int c; 4134 4135 if (opt_info.num != LONG_MIN) 4136 opt_info.num = (long)(opt_info.number = 0); 4137 if (!p || !(mp = state.mp) && !(mp = state.mp = sfstropen())) 4138 goto nospace; 4139 s = *p == '-' ? p : opt_info.name; 4140 if (*p == '!') 4141 { 4142 while (*s == '-') 4143 sfputc(mp, *s++); 4144 sfputc(mp, 'n'); 4145 sfputc(mp, 'o'); 4146 } 4147 sfputr(mp, s, ':'); 4148 sfputc(mp, ' '); 4149 if (*p == '#' || *p == ':') 4150 { 4151 if (*p == '#') 4152 { 4153 s = T(NiL, ID, "numeric"); 4154 sfputr(mp, s, ' '); 4155 } 4156 if (*(p = next(p + 1, version)) == '[') 4157 { 4158 p = skip(s = p + 1, ':', '?', 0, 1, 0, 0, version); 4159 tp = X(catalog) ? state.xp : mp; 4160 while (s < p) 4161 { 4162 if ((c = *s++) == '?' || c == ']') 4163 s++; 4164 sfputc(tp, c); 4165 } 4166 if (!X(catalog)) 4167 sfputc(mp, ' '); 4168 else if (p = sfstruse(tp)) 4169 sfputr(mp, T(id, catalog, p), ' '); 4170 else 4171 goto nospace; 4172 } 4173 p = opt_info.name[2] ? C("value expected") : C("argument expected"); 4174 } 4175 else if (*p == '*' || *p == '&') 4176 { 4177 sfputr(mp, opt_info.arg, ':'); 4178 sfputc(mp, ' '); 4179 p = *p == '&' ? C("ambiguous option argument value") : C("unknown option argument value"); 4180 } 4181 else if (*p == '=' || *p == '!') 4182 p = C("value not expected"); 4183 else if (*p == '?') 4184 p = *(p + 1) == '?' ? C("optget: option not supported") : C("ambiguous option"); 4185 else if (*p == '+') 4186 p = C("section not found"); 4187 else 4188 { 4189 if (opt_info.option[0] != '?' && opt_info.option[0] != '-' || opt_info.option[1] != '?' && opt_info.option[1] != '-') 4190 opt_info.option[0] = 0; 4191 p = C("unknown option"); 4192 } 4193 p = T(NiL, ID, p); 4194 sfputr(mp, p, -1); 4195 if (err) 4196 sfputr(mp, " -- out of range", -1); 4197 if (opt_info.arg = sfstruse(mp)) 4198 return ':'; 4199 nospace: 4200 opt_info.arg = T(NiL, ID, "[* out of space *]"); 4201 return ':'; 4202 } 4203 4204 /* 4205 * argv: command line argv where argv[0] is command name 4206 * 4207 * opts: option control string 4208 * 4209 * '[' [flag][=][index][:<long-name>[|<alias-name>...]['?'description]] ']' 4210 * long option name, index, description; -index returned 4211 * ':' option takes string arg 4212 * '#' option takes numeric arg (concat option may follow) 4213 * '?' (option) following options not in usage 4214 * (following # or :) optional arg 4215 * '[' '[' ... ] ... '[' ... ']' ']' 4216 * mutually exclusive option grouping 4217 * '[' name [:attr]* [?description] ']' 4218 * (following # or :) optional option arg description 4219 * '\n'[' '|'\t']* ignored for legibility 4220 * ' ' ... optional argument(s) description (to end of string) 4221 * or after blank line 4222 * ']]' literal ']' within '[' ... ']' 4223 * 4224 * return: 4225 * 0 no more options 4226 * '?' usage: opt_info.arg points to message sans 4227 * `Usage: command ' 4228 * ':' error: opt_info.arg points to message sans `command: ' 4229 * 4230 * ':' '#' ' ' '[' ']' 4231 * invalid option chars 4232 * 4233 * -- terminates option list and returns 0 4234 * 4235 * + as first opts char makes + equivalent to - 4236 * 4237 * if any # option is specified then numeric options (e.g., -123) 4238 * are associated with the leftmost # option in opts 4239 * 4240 * usage info in placed opt_info.arg when '?' returned 4241 * see help_text[] (--???) for more info 4242 */ 4243 4244 int 4245 optget(register char** argv, const char* oopts) 4246 { 4247 register int c; 4248 register char* s; 4249 char* a; 4250 char* b; 4251 char* e; 4252 char* f; 4253 char* g; 4254 char* v; 4255 char* w; 4256 char* p; 4257 char* q; 4258 char* t; 4259 char* y; 4260 char* numopt; 4261 char* opts; 4262 char* id; 4263 char* catalog; 4264 int n; 4265 int m; 4266 int k; 4267 int j; 4268 int x; 4269 int err; 4270 int no; 4271 int nov; 4272 int num; 4273 int numchr; 4274 int prefix; 4275 int version; 4276 Help_t* hp; 4277 Push_t* psp; 4278 Push_t* tsp; 4279 Sfio_t* vp; 4280 Sfio_t* xp; 4281 Optcache_t* cache; 4282 Optcache_t* pcache; 4283 Optpass_t* pass; 4284 4285 #if !_PACKAGE_astsa && !_YOU_FIGURED_OUT_HOW_TO_GET_ALL_DLLS_TO_DO_THIS_ 4286 /* 4287 * these are not initialized by all dlls! 4288 */ 4289 4290 extern Error_info_t _error_info_; 4291 extern Opt_t _opt_info_; 4292 4293 if (!_error_infop_) 4294 _error_infop_ = &_error_info_; 4295 if (!_opt_infop_) 4296 _opt_infop_ = &_opt_info_; 4297 #endif 4298 if (!oopts) 4299 return 0; 4300 state.pindex = opt_info.index; 4301 state.poffset = opt_info.offset; 4302 if (!opt_info.index) 4303 { 4304 opt_info.index = 1; 4305 opt_info.offset = 0; 4306 if (state.npass) 4307 { 4308 state.npass = 0; 4309 state.join = 0; 4310 } 4311 } 4312 if (!argv) 4313 cache = 0; 4314 else 4315 for (pcache = 0, cache = state.cache; cache; pcache = cache, cache = cache->next) 4316 if (cache->pass.oopts == (char*)oopts) 4317 break; 4318 if (cache) 4319 { 4320 if (pcache) 4321 { 4322 pcache->next = cache->next; 4323 cache->next = state.cache; 4324 state.cache = cache; 4325 } 4326 pass = &cache->pass; 4327 state.npass = -1; 4328 } 4329 else 4330 { 4331 if (!argv) 4332 n = state.npass ? state.npass : 1; 4333 else if ((n = state.join - 1) < 0) 4334 n = 0; 4335 if (n >= state.npass || state.pass[n].oopts != (char*)oopts) 4336 { 4337 for (m = 0; m < state.npass && state.pass[m].oopts != (char*)oopts; m++); 4338 if (m < state.npass) 4339 n = m; 4340 else 4341 { 4342 if (n >= elementsof(state.pass)) 4343 n = elementsof(state.pass) - 1; 4344 init((char*)oopts, &state.pass[n]); 4345 if (state.npass <= n) 4346 state.npass = n + 1; 4347 } 4348 } 4349 if (!argv) 4350 return 0; 4351 pass = &state.pass[n]; 4352 } 4353 opts = pass->opts; 4354 prefix = pass->prefix; 4355 version = pass->version; 4356 id = pass->id; 4357 if (!(xp = state.xp) || (catalog = pass->catalog) && !X(catalog)) 4358 catalog = 0; 4359 else /* if (!error_info.catalog) */ 4360 error_info.catalog = catalog; 4361 again: 4362 psp = 0; 4363 4364 /* 4365 * check if any options remain and determine if the 4366 * next option is short or long 4367 */ 4368 4369 opt_info.assignment = 0; 4370 num = 1; 4371 w = v = 0; 4372 x = 0; 4373 for (;;) 4374 { 4375 if (!opt_info.offset) 4376 { 4377 /* 4378 * finished with the previous arg 4379 */ 4380 4381 if (opt_info.index == 1 && opt_info.argv != state.strv) 4382 { 4383 opt_info.argv = 0; 4384 state.argv[0] = 0; 4385 if (argv[0] && (state.argv[0] = save(argv[0], strlen(argv[0]), 0, 0, 0, 0))) 4386 opt_info.argv = state.argv; 4387 state.style = STYLE_short; 4388 } 4389 if (!(s = argv[opt_info.index])) 4390 return 0; 4391 if (!prefix) 4392 { 4393 /* 4394 * long with no prefix (dd style) 4395 */ 4396 4397 n = 2; 4398 if ((c = *s) != '-' && c != '+') 4399 c = '-'; 4400 else if (*++s == c) 4401 { 4402 if (!*++s) 4403 { 4404 opt_info.index++; 4405 return 0; 4406 } 4407 else if (*s == c) 4408 return 0; 4409 } 4410 else if (*s == '?') 4411 n = 1; 4412 } 4413 else if ((c = *s++) != '-' && (c != '+' || !(pass->flags & OPT_plus) && (!(pass->flags & OPT_numeric) || !isdigit(*s)))) 4414 { 4415 if (!(pass->flags & OPT_old) || !isalpha(c)) 4416 return 0; 4417 s--; 4418 n = 1; 4419 opt_info.offset--; 4420 } 4421 else if (*s == c) 4422 { 4423 if (!*++s) 4424 { 4425 /* 4426 * -- or ++ end of options 4427 */ 4428 4429 opt_info.index++; 4430 return 0; 4431 } 4432 else if (*s == c) 4433 { 4434 /* 4435 * ---* or +++* are operands 4436 */ 4437 4438 return 0; 4439 } 4440 if (version || *s == '?' || !(pass->flags & OPT_minus)) 4441 { 4442 /* 4443 * long with double prefix 4444 */ 4445 4446 n = 2; 4447 } 4448 else 4449 { 4450 /* 4451 * short option char '-' 4452 */ 4453 4454 s--; 4455 n = 1; 4456 } 4457 } 4458 else if (prefix == 1 && *s != '?') 4459 { 4460 /* 4461 * long with single prefix (find style) 4462 */ 4463 4464 n = 2; 4465 } 4466 else 4467 { 4468 /* 4469 * short (always with single prefix) 4470 */ 4471 4472 n = 1; 4473 } 4474 4475 /* 4476 * just a prefix is an option (e.g., `-' == stdin) 4477 */ 4478 4479 if (!*s) 4480 return 0; 4481 if (c == '+') 4482 opt_info.arg = 0; 4483 if (n == 2) 4484 { 4485 x = 0; 4486 state.style = STYLE_long; 4487 opt_info.option[0] = opt_info.name[0] = opt_info.name[1] = c; 4488 w = &opt_info.name[prefix]; 4489 if ((*s == 'n' || *s == 'N') && (*(s + 1) == 'o' || *(s + 1) == 'O') && *(s + 2) && *(s + 2) != '=') 4490 no = *(s + 2) == '-' ? 3 : 2; 4491 else 4492 no = 0; 4493 for (c = *s; *s; s++) 4494 { 4495 if (*s == '=') 4496 { 4497 if (*(s + 1) == '=') 4498 s++; 4499 if (!isalnum(*(s - 1)) && *(w - 1) == (opt_info.assignment = *(s - 1))) 4500 w--; 4501 v = ++s; 4502 break; 4503 } 4504 if (w < &opt_info.name[elementsof(opt_info.name) - 1] && *s != ':' && *s != '|' && *s != '[' && *s != ']') 4505 *w++ = *s; 4506 } 4507 *w = 0; 4508 w = &opt_info.name[prefix]; 4509 c = *w; 4510 opt_info.offset = 0; 4511 opt_info.index++; 4512 break; 4513 } 4514 opt_info.offset++; 4515 } 4516 if (!argv[opt_info.index]) 4517 return 0; 4518 if (c = argv[opt_info.index][opt_info.offset++]) 4519 { 4520 if ((k = argv[opt_info.index][0]) != '-' && k != '+') 4521 k = '-'; 4522 opt_info.option[0] = opt_info.name[0] = k; 4523 opt_info.option[1] = opt_info.name[1] = c; 4524 opt_info.option[2] = opt_info.name[2] = 0; 4525 break; 4526 } 4527 opt_info.offset = 0; 4528 opt_info.index++; 4529 } 4530 4531 /* 4532 * at this point: 4533 * 4534 * c the first character of the option 4535 * w long option name if != 0, otherwise short 4536 * v long option value (via =) if w != 0 4537 */ 4538 4539 if (c == '?') 4540 { 4541 /* 4542 * ? always triggers internal help 4543 */ 4544 4545 if (!state.msgdict) 4546 initdict(); 4547 if (w) 4548 { 4549 if (!v && (*(w + 1) || !(v = argv[opt_info.index]) || !++opt_info.index)) 4550 v = w + 1; 4551 else if (w[0] != '?' || w[1]) 4552 { 4553 s = w; 4554 w = v; 4555 v = s + 1; 4556 } 4557 } 4558 opt_info.option[1] = c; 4559 opt_info.option[2] = 0; 4560 if (!w) 4561 { 4562 opt_info.name[1] = c; 4563 opt_info.name[2] = 0; 4564 } 4565 goto help; 4566 } 4567 else if (w && !state.msgdict) 4568 initdict(); 4569 numopt = 0; 4570 f = 0; 4571 s = opts; 4572 4573 /* 4574 * no option can start with these characters 4575 */ 4576 4577 if (c == ':' || c == '#' || c == ' ' || c == '[' || c == ']') 4578 { 4579 if (c != *s) 4580 s = ""; 4581 } 4582 else 4583 { 4584 a = 0; 4585 if (!w && (pass->flags & OPT_cache)) 4586 { 4587 if (cache) 4588 { 4589 if (k = cache->flags[map[c]]) 4590 { 4591 opt_info.arg = 0; 4592 4593 /* 4594 * this is a ksh getopts workaround 4595 */ 4596 4597 if (opt_info.num != LONG_MIN) 4598 opt_info.num = (long)(opt_info.number = !(k & OPT_cache_invert)); 4599 if (!(k & (OPT_cache_string|OPT_cache_numeric))) 4600 return c; 4601 if (*(opt_info.arg = &argv[opt_info.index++][opt_info.offset])) 4602 { 4603 if (!(k & OPT_cache_numeric)) 4604 { 4605 opt_info.offset = 0; 4606 return c; 4607 } 4608 opt_info.num = (long)(opt_info.number = optnumber(opt_info.arg, &e, &err)); 4609 if (err || e == opt_info.arg) 4610 { 4611 opt_info.num = (long)(opt_info.number = 0); 4612 if (!err && (k & OPT_cache_optional)) 4613 { 4614 opt_info.arg = 0; 4615 opt_info.index--; 4616 return c; 4617 } 4618 } 4619 else if (*e) 4620 { 4621 opt_info.offset += e - opt_info.arg; 4622 opt_info.index--; 4623 return c; 4624 } 4625 else 4626 { 4627 opt_info.offset = 0; 4628 return c; 4629 } 4630 } 4631 else if (opt_info.arg = argv[opt_info.index]) 4632 { 4633 opt_info.index++; 4634 if ((k & OPT_cache_optional) && (*opt_info.arg == '-' || (pass->flags & OPT_plus) && *opt_info.arg == '+') && *(opt_info.arg + 1)) 4635 { 4636 opt_info.arg = 0; 4637 opt_info.index--; 4638 opt_info.offset = 0; 4639 opt_info.num = (long)(opt_info.number = 0); 4640 return c; 4641 } 4642 if (k & OPT_cache_string) 4643 { 4644 opt_info.offset = 0; 4645 return c; 4646 } 4647 opt_info.num = (long)(opt_info.number = optnumber(opt_info.arg, &e, &err)); 4648 if (!err) 4649 { 4650 if (!*e) 4651 { 4652 opt_info.offset = 0; 4653 return c; 4654 } 4655 if (k & OPT_cache_optional) 4656 { 4657 opt_info.arg = 0; 4658 opt_info.index--; 4659 opt_info.offset = 0; 4660 return c; 4661 } 4662 } 4663 } 4664 else if (k & OPT_cache_optional) 4665 { 4666 opt_info.offset = 0; 4667 return c; 4668 } 4669 opt_info.index--; 4670 } 4671 cache = 0; 4672 } 4673 else if (cache = newof(0, Optcache_t, 1, 0)) 4674 { 4675 cache->caching = c; 4676 c = 0; 4677 cache->pass = *pass; 4678 cache->next = state.cache; 4679 state.cache = cache; 4680 } 4681 } 4682 else 4683 cache = 0; 4684 for (;;) 4685 { 4686 if (!(*(s = next(s, version))) || *s == '\n' || *s == ' ') 4687 { 4688 if (!(tsp = psp)) 4689 { 4690 if (cache) 4691 { 4692 /* 4693 * the first loop pass 4694 * initialized the cache 4695 * so one more pass to 4696 * check the cache or 4697 * bail for a full scan 4698 */ 4699 4700 cache->flags[0] = 0; 4701 c = cache->caching; 4702 cache->caching = 0; 4703 cache = 0; 4704 s = opts; 4705 continue; 4706 } 4707 if (!x && catalog) 4708 { 4709 /* 4710 * the first loop pass 4711 * translated long 4712 * options and there 4713 * were no matches so 4714 * one more pass for C 4715 * locale 4716 */ 4717 4718 catalog = 0; 4719 s = opts; 4720 continue; 4721 } 4722 s = ""; 4723 break; 4724 } 4725 s = psp->ob; 4726 psp = psp->next; 4727 free(tsp); 4728 continue; 4729 } 4730 if (*s == '\f') 4731 { 4732 psp = info(psp, s + 1, NiL, xp, id); 4733 if (psp->nb) 4734 s = psp->nb; 4735 else 4736 { 4737 s = psp->ob; 4738 psp = psp->next; 4739 } 4740 continue; 4741 } 4742 message((-20, "optget: opt %s c %c w %s num %ld", show(s), c, w, num)); 4743 if (*s == c && !w) 4744 break; 4745 else if (*s == '[') 4746 { 4747 s = next(s + 1, version); 4748 if (*s == '(') 4749 { 4750 s = nest(f = s); 4751 if (!conformance(f, s - f)) 4752 goto disable; 4753 } 4754 k = *(f = s); 4755 if (k == '+' || k == '-') 4756 /* ignore */; 4757 else if (k == '[' || version < 1) 4758 continue; 4759 else if (w && !cache) 4760 { 4761 nov = no; 4762 if (*(s + 1) == '\f' && (vp = state.vp)) 4763 { 4764 sfputc(vp, k); 4765 s = expand(s + 2, NiL, &t, vp, id); 4766 if (*s) 4767 *(f = s - 1) = k; 4768 else 4769 { 4770 f = sfstrbase(vp); 4771 if (s = strrchr(f, ':')) 4772 f = s - 1; 4773 else 4774 s = f + 1; 4775 } 4776 } 4777 else 4778 t = 0; 4779 if (*s != ':') 4780 s = skip(s, ':', '?', 0, 1, 0, 0, version); 4781 if (*s == ':') 4782 { 4783 if (catalog) 4784 { 4785 p = skip(s + 1, '?', 0, 0, 1, 0, 0, version); 4786 e = sfprints("%-.*s", p - (s + 1), s + 1); 4787 g = T(id, catalog, e); 4788 if (g == e) 4789 p = 0; 4790 else 4791 { 4792 sfprintf(xp, ":%s|%s?", g, e); 4793 if (!(s = sfstruse(xp))) 4794 goto nospace; 4795 } 4796 } 4797 else 4798 p = 0; 4799 y = w; 4800 for (;;) 4801 { 4802 n = m = 0; 4803 e = s + 1; 4804 while (*++s) 4805 { 4806 if (*s == '*' || *s == '\a') 4807 { 4808 if (*s == '\a') 4809 do 4810 { 4811 if (!*++s) 4812 { 4813 s--; 4814 break; 4815 } 4816 } while (*s != '\a'); 4817 j = *(s + 1); 4818 if (j == ':' || j == '|' || j == '?' || j == ']' || j == 0) 4819 { 4820 while (*w) 4821 w++; 4822 m = 0; 4823 break; 4824 } 4825 m = 1; 4826 } 4827 else if (*s == *w || SEP(*s) && SEP(*w)) 4828 w++; 4829 else if (*w == 0) 4830 break; 4831 else if (!SEP(*s)) 4832 { 4833 if (SEP(*w)) 4834 { 4835 if (*++w == *s) 4836 { 4837 w++; 4838 continue; 4839 } 4840 } 4841 else if (w == y || SEP(*(w - 1)) || isupper(*(w - 1)) && islower(*w)) 4842 break; 4843 for (q = s; *q && !SEP(*q) && *q != '|' && *q != '?' && *q != ']'; q++); 4844 if (!SEP(*q)) 4845 break; 4846 for (s = q; w > y && *w != *(s + 1); w--); 4847 } 4848 else if (*w != *(s + 1)) 4849 break; 4850 } 4851 if (!*w) 4852 { 4853 nov = 0; 4854 break; 4855 } 4856 if (n = no) 4857 { 4858 m = 0; 4859 s = e - 1; 4860 w = y + n; 4861 while (*++s) 4862 { 4863 if (*s == '*' || *s == '\a') 4864 { 4865 if (*s == '\a') 4866 do 4867 { 4868 if (!*++s) 4869 { 4870 s--; 4871 break; 4872 } 4873 } while (*s != '\a'); 4874 j = *(s + 1); 4875 if (j == ':' || j == '|' || j == '?' || j == ']' || j == 0) 4876 { 4877 while (*w) 4878 w++; 4879 m = 0; 4880 break; 4881 } 4882 m = 1; 4883 } 4884 else if (*s == *w || SEP(*s) && SEP(*w)) 4885 w++; 4886 else if (*w == 0) 4887 break; 4888 else if (!SEP(*s)) 4889 { 4890 if (SEP(*w)) 4891 { 4892 if (*++w == *s) 4893 { 4894 w++; 4895 continue; 4896 } 4897 } 4898 else if (w == y || SEP(*(w - 1)) || isupper(*(w - 1)) && islower(*w)) 4899 break; 4900 for (q = s; *q && !SEP(*q) && *q != '|' && *q != '?' && *q != ']'; q++); 4901 if (!SEP(*q)) 4902 break; 4903 for (s = q; w > y && *w != *(s + 1); w--); 4904 } 4905 else if (*w != *(s + 1)) 4906 break; 4907 } 4908 if (!*w) 4909 break; 4910 } 4911 if (*(s = skip(s, ':', '|', '?', 1, 0, 0, version)) != '|') 4912 break; 4913 w = y; 4914 } 4915 if (p) 4916 s = p; 4917 if (!*w) 4918 { 4919 if (n) 4920 num = 0; 4921 if (!(n = (m || *s == ':' || *s == '|' || *s == '?' || *s == ']' || *s == 0)) && x) 4922 { 4923 psp = pop(psp); 4924 return opterror("?", 0, version, id, catalog); 4925 } 4926 for (x = k; *(f + 1) == '|' && (j = *(f + 2)) && j != '!' && j != '=' && j != ':' && j != '?' && j != ']'; f += 2); 4927 if (*f == ':') 4928 { 4929 x = -1; 4930 opt_info.option[1] = '-'; 4931 opt_info.option[2] = 0; 4932 } 4933 else if (*(f + 1) == ':' || *(f + 1) == '!' && *(f + 2) == ':') 4934 { 4935 opt_info.option[1] = x; 4936 opt_info.option[2] = 0; 4937 } 4938 else 4939 { 4940 a = f; 4941 if (*a == '=') 4942 a++; 4943 else 4944 { 4945 if (*(a + 1) == '!') 4946 a++; 4947 if (*(a + 1) == '=') 4948 a += 2; 4949 } 4950 x = -strtol(a, &b, 0); 4951 if ((b - a) > sizeof(opt_info.option) - 2) 4952 b = a + sizeof(opt_info.option) - 2; 4953 memcpy(&opt_info.option[1], a, b - a); 4954 opt_info.option[b - a + 1] = 0; 4955 } 4956 b = e; 4957 if (t) 4958 { 4959 s = t; 4960 t = 0; 4961 } 4962 a = s = skip(s, 0, 0, 0, 1, 0, 0, version); 4963 if (n) 4964 { 4965 w = y; 4966 break; 4967 } 4968 } 4969 w = y; 4970 } 4971 else if (k == c && prefix == 1) 4972 { 4973 w = 0; 4974 opt_info.name[1] = c; 4975 opt_info.name[2] = 0; 4976 opt_info.offset = 2; 4977 opt_info.index--; 4978 break; 4979 } 4980 if (t) 4981 { 4982 s = t; 4983 if (a) 4984 a = t; 4985 } 4986 } 4987 disable: 4988 s = skip(s, 0, 0, 0, 1, 0, 1, version); 4989 if (*s == GO) 4990 s = skip(s + 1, 0, 0, 0, 0, 1, 1, version); 4991 if (cache) 4992 { 4993 m = OPT_cache_flag; 4994 v = s; 4995 if (*v == '#') 4996 { 4997 v++; 4998 m |= OPT_cache_numeric; 4999 } 5000 else if (*v == ':') 5001 { 5002 v++; 5003 m |= OPT_cache_string; 5004 } 5005 if (*v == '?') 5006 { 5007 v++; 5008 m |= OPT_cache_optional; 5009 } 5010 else if (*v == *(v - 1)) 5011 v++; 5012 if (*(v = next(v, version)) == '[') 5013 v = skip(v + 1, 0, 0, 0, 1, 0, 1, version); 5014 if (*v != GO) 5015 { 5016 v = f; 5017 for (;;) 5018 { 5019 if (isdigit(*f) && isdigit(*(f + 1))) 5020 while (isdigit(*(f + 1))) 5021 f++; 5022 else if (*(f + 1) == '=') 5023 break; 5024 else 5025 cache->flags[map[*f]] = m; 5026 j = 0; 5027 while (*(f + 1) == '|') 5028 { 5029 f += 2; 5030 if (!(j = *f) || j == '!' || j == '=' || j == ':' || j == '?' || j == ']') 5031 break; 5032 cache->flags[map[j]] = m; 5033 } 5034 if (j != '!' || (m & OPT_cache_invert)) 5035 break; 5036 f = v; 5037 m |= OPT_cache_invert; 5038 } 5039 } 5040 } 5041 else 5042 { 5043 m = 0; 5044 if (!w) 5045 { 5046 if (isdigit(*f) && isdigit(*(f + 1))) 5047 k = -1; 5048 if (c == k) 5049 m = 1; 5050 while (*(f + 1) == '|') 5051 { 5052 f += 2; 5053 if (!(j = *f)) 5054 { 5055 m = 0; 5056 break; 5057 } 5058 else if (j == c) 5059 m = 1; 5060 else if (j == '!' || j == '=' || j == ':' || j == '?' || j == ']') 5061 break; 5062 } 5063 } 5064 if (m) 5065 { 5066 s--; 5067 if (*++f == '!') 5068 { 5069 f++; 5070 num = 0; 5071 } 5072 if (*f == '=') 5073 { 5074 c = -strtol(++f, &b, 0); 5075 if ((b - f) > sizeof(opt_info.option) - 2) 5076 b = f + sizeof(opt_info.option) - 2; 5077 memcpy(&opt_info.option[1], f, b - f); 5078 opt_info.option[b - f + 1] = 0; 5079 } 5080 else 5081 c = k; 5082 break; 5083 } 5084 } 5085 if (*s == '#') 5086 { 5087 if (!numopt && s > opts) 5088 { 5089 numopt = s - 1; 5090 numchr = k; 5091 if (*f == ':') 5092 numchr = -1; 5093 else if (*(f + 1) != ':' && *(f + 1) != '!' && *(f + 1) != ']') 5094 { 5095 a = f; 5096 if (*a == '=') 5097 a++; 5098 else 5099 { 5100 if (*(a + 1) == '!') 5101 a++; 5102 if (*(a + 1) == '=') 5103 a += 2; 5104 } 5105 numchr = -strtol(a, NiL, 0); 5106 } 5107 } 5108 } 5109 else if (*s != ':') 5110 continue; 5111 } 5112 else if (*s == ']') 5113 { 5114 s++; 5115 continue; 5116 } 5117 else if (*s == '#') 5118 { 5119 if (!numopt && s > opts) 5120 numchr = *(numopt = s - 1); 5121 } 5122 else if (*s != ':') 5123 { 5124 if (cache) 5125 { 5126 m = OPT_cache_flag; 5127 if (*(s + 1) == '#') 5128 { 5129 m |= OPT_cache_numeric; 5130 if (*(s + 2) == '?') 5131 m |= OPT_cache_optional; 5132 } 5133 else if (*(s + 1) == ':') 5134 { 5135 m |= OPT_cache_string; 5136 if (*(s + 2) == '?') 5137 m |= OPT_cache_optional; 5138 } 5139 cache->flags[map[*s]] = m; 5140 } 5141 s++; 5142 continue; 5143 } 5144 message((-21, "optget: opt %s", show(s))); 5145 if (*++s == '?' || *s == *(s - 1)) 5146 s++; 5147 if (*(s = next(s, version)) == '[') 5148 { 5149 s = skip(s + 1, 0, 0, 0, 1, 0, 1, version); 5150 if (*s == GO) 5151 s = skip(s + 1, 0, 0, 0, 0, 1, 1, version); 5152 } 5153 message((-21, "optget: opt %s", show(s))); 5154 } 5155 if (w && x) 5156 { 5157 s = skip(b, '|', '?', 0, 1, 0, 0, version); 5158 if (v && (a == 0 || *a == 0 || *(a + 1) != ':' && *(a + 1) != '#') && (*v == '0' || *v == '1') && !*(v + 1)) 5159 { 5160 if (*v == '0') 5161 num = !num; 5162 v = 0; 5163 } 5164 if ((s - b) >= elementsof(opt_info.name)) 5165 s = b + elementsof(opt_info.name) - 1; 5166 for (;;) 5167 { 5168 if (b >= s) 5169 { 5170 *w = 0; 5171 break; 5172 } 5173 if (*b == '*') 5174 break; 5175 *w++ = *b++; 5176 } 5177 if (!num && v) 5178 return opterror(no ? "!" : "=", 0, version, id, catalog); 5179 w = &opt_info.name[prefix]; 5180 c = x; 5181 s = a; 5182 } 5183 } 5184 if (!*s) 5185 { 5186 if (w) 5187 { 5188 if (hp = (Help_t*)search(styles, elementsof(styles), sizeof(styles[0]), w)) 5189 { 5190 if (!v) 5191 v = (char*)hp->name; 5192 goto help; 5193 } 5194 if (!v) 5195 { 5196 v = opt_info.name; 5197 goto help; 5198 } 5199 } 5200 if (w || !isdigit(c) || !numopt || !(pass->flags & OPT_numeric)) 5201 { 5202 pop(psp); 5203 return opterror("", 0, version, id, catalog); 5204 } 5205 s = numopt; 5206 c = opt_info.option[1] = numchr; 5207 opt_info.offset--; 5208 } 5209 opt_info.arg = 0; 5210 5211 /* 5212 * this is a ksh getopts workaround 5213 */ 5214 5215 if (opt_info.num != LONG_MIN) 5216 opt_info.num = (long)(opt_info.number = num); 5217 if ((n = *++s == '#') || *s == ':' || w && !nov && v && (optnumber(v, &e, NiL), n = !*e)) 5218 { 5219 if (w) 5220 { 5221 if (nov) 5222 { 5223 if (v) 5224 { 5225 pop(psp); 5226 return opterror("!", 0, version, id, catalog); 5227 } 5228 opt_info.num = (long)(opt_info.number = 0); 5229 } 5230 else 5231 { 5232 if (!v && *(s + 1) != '?' && (v = argv[opt_info.index])) 5233 { 5234 opt_info.index++; 5235 opt_info.offset = 0; 5236 } 5237 if (!(opt_info.arg = v) || (*v == '0' || *v == '1') && !*(v + 1)) 5238 { 5239 if (*(s + 1) != '?') 5240 { 5241 if (!opt_info.arg) 5242 { 5243 pop(psp); 5244 return opterror(s, 0, version, id, catalog); 5245 } 5246 } 5247 else if (*(t = next(s + 2, version)) == '[') 5248 while (*(t = skip(t, ':', 0, 0, 1, 0, 0, version)) == ':') 5249 if (*++t == '!') 5250 { 5251 if (!v || *v == '1') 5252 { 5253 e = skip(t, ':', '?', ']', 1, 0, 0, version); 5254 opt_info.arg = sfprints("%-.*s", e - t - 1, t + 1); 5255 } 5256 else 5257 { 5258 opt_info.arg = 0; 5259 opt_info.num = (long)(opt_info.number = 0); 5260 } 5261 break; 5262 } 5263 } 5264 if (opt_info.arg && n) 5265 { 5266 opt_info.num = (long)(opt_info.number = optnumber(opt_info.arg, &e, &err)); 5267 if (err || e == opt_info.arg) 5268 { 5269 pop(psp); 5270 return opterror(s, err, version, id, catalog); 5271 } 5272 } 5273 } 5274 goto optarg; 5275 } 5276 else if (*(opt_info.arg = &argv[opt_info.index++][opt_info.offset])) 5277 { 5278 if (*s == '#') 5279 { 5280 opt_info.num = (long)(opt_info.number = optnumber(opt_info.arg, &e, &err)); 5281 if (err || e == opt_info.arg) 5282 { 5283 if (!err && *(s + 1) == '?') 5284 { 5285 opt_info.arg = 0; 5286 opt_info.index--; 5287 } 5288 else 5289 { 5290 opt_info.offset = 0; 5291 c = opterror(s, err, version, id, catalog); 5292 } 5293 pop(psp); 5294 return c; 5295 } 5296 else if (*e) 5297 { 5298 opt_info.offset += e - opt_info.arg; 5299 opt_info.index--; 5300 pop(psp); 5301 return c; 5302 } 5303 } 5304 } 5305 else if (opt_info.arg = argv[opt_info.index]) 5306 { 5307 opt_info.index++; 5308 if (*(s + 1) == '?' && (*opt_info.arg == '-' || (pass->flags & OPT_plus) && *opt_info.arg == '+') && *(opt_info.arg + 1)) 5309 { 5310 opt_info.num = (long)(opt_info.number = 0); 5311 opt_info.index--; 5312 opt_info.arg = 0; 5313 } 5314 else if (*s == '#') 5315 { 5316 opt_info.num = (long)(opt_info.number = optnumber(opt_info.arg, &e, &err)); 5317 if (err || *e) 5318 { 5319 if (!err && *(s + 1) == '?') 5320 { 5321 opt_info.arg = 0; 5322 opt_info.index--; 5323 } 5324 else 5325 { 5326 pop(psp); 5327 opt_info.offset = 0; 5328 return opterror(s, err, version, id, catalog); 5329 } 5330 } 5331 } 5332 } 5333 else if (*(s + 1) != '?') 5334 { 5335 opt_info.index--; 5336 pop(psp); 5337 return opterror(s, 0, version, id, catalog); 5338 } 5339 opt_info.offset = 0; 5340 optarg: 5341 if (*s == ':' && *(s = skip(s, 0, 0, 0, 1, 0, 1, version)) == GO && *(s = next(s + 1, version)) == '[' && isalnum(*(s + 1))) 5342 { 5343 x = 0; 5344 if (opt_info.arg) 5345 { 5346 do 5347 { 5348 w = y = opt_info.arg; 5349 f = s = next(s + 1, version); 5350 k = *f; 5351 if (k == *w && isalpha(k) && !*(w + 1)) 5352 { 5353 x = k; 5354 break; 5355 } 5356 if (*s == '+' || *s == '-') 5357 continue; 5358 else if (*s == '[' || version < 1) 5359 continue; 5360 else 5361 { 5362 if (*s != ':') 5363 s = skip(s, ':', '?', 0, 1, 0, 0, version); 5364 if (*s == ':') 5365 { 5366 if (catalog) 5367 { 5368 p = skip(s + 1, '?', 0, 0, 1, 0, 0, version); 5369 e = sfprints("%-.*s", p - (s + 1), s + 1); 5370 b = T(id, catalog, e); 5371 if (b == e) 5372 p = 0; 5373 else 5374 { 5375 sfprintf(xp, ":%s|%s?", b, e); 5376 if (!(s = sfstruse(xp))) 5377 goto nospace; 5378 } 5379 } 5380 else 5381 p = 0; 5382 for (;;) 5383 { 5384 n = m = 0; 5385 e = s + 1; 5386 while (*++s) 5387 { 5388 if (*s == '*' || *s == '\a') 5389 { 5390 if (*s == '\a') 5391 do 5392 { 5393 if (!*++s) 5394 { 5395 s--; 5396 break; 5397 } 5398 } while (*s != '\a'); 5399 j = *(s + 1); 5400 if (j == ':' || j == '|' || j == '?' || j == ']' || j == 0) 5401 { 5402 while (*w) 5403 w++; 5404 m = 0; 5405 break; 5406 } 5407 m = 1; 5408 } 5409 else if (*s == *w || SEP(*s) && SEP(*w)) 5410 w++; 5411 else if (*w == 0) 5412 break; 5413 else if (!SEP(*s)) 5414 { 5415 if (SEP(*w)) 5416 { 5417 if (*++w == *s) 5418 { 5419 w++; 5420 continue; 5421 } 5422 } 5423 else if (w == y || SEP(*(w - 1)) || isupper(*(w - 1)) && islower(*w)) 5424 break; 5425 for (q = s; *q && !SEP(*q) && *q != '|' && *q != '?' && *q != ']'; q++); 5426 if (!SEP(*q)) 5427 break; 5428 for (s = q; w > y && *w != *(s + 1); w--); 5429 } 5430 else if (*w != *(s + 1)) 5431 break; 5432 } 5433 if (!*w) 5434 { 5435 nov = 0; 5436 break; 5437 } 5438 if (*(s = skip(s, ':', '|', '?', 1, 0, 0, version)) != '|') 5439 break; 5440 w = y; 5441 } 5442 if (p) 5443 s = p; 5444 if (!*w) 5445 { 5446 if (n) 5447 num = 0; 5448 if (!(n = (m || *s == ':' || *s == '|' || *s == '?' || *s == ']')) && x) 5449 { 5450 pop(psp); 5451 return opterror("&", 0, version, id, catalog); 5452 } 5453 for (x = k; *(f + 1) == '|' && (j = *(f + 2)) && j != '!' && j != '=' && j != ':' && j != '?' && j != ']'; f += 2); 5454 if (*f == ':') 5455 x = -1; 5456 else if (*(f + 1) == ':' || *(f + 1) == '!' && *(f + 2) == ':') 5457 /* ok */; 5458 else 5459 { 5460 a = f; 5461 if (*a == '=') 5462 a++; 5463 else 5464 { 5465 if (*(a + 1) == '!') 5466 a++; 5467 if (*(a + 1) == '=') 5468 a += 2; 5469 } 5470 x = -strtol(a, &b, 0); 5471 } 5472 b = e; 5473 a = s = skip(s, 0, 0, 0, 1, 0, 0, version); 5474 if (n) 5475 break; 5476 } 5477 } 5478 } 5479 } while (*(s = skip(s, 0, 0, 0, 1, 0, 1, version)) == '['); 5480 if (!(opt_info.num = (long)(opt_info.number = x))) 5481 { 5482 pop(psp); 5483 return opterror("*", 0, version, id, catalog); 5484 } 5485 } 5486 } 5487 } 5488 else if (w && v) 5489 { 5490 pop(psp); 5491 return opterror("=", 0, version, id, catalog); 5492 } 5493 else 5494 { 5495 opt_info.num = (long)(opt_info.number = num); 5496 if (!w && !argv[opt_info.index][opt_info.offset]) 5497 { 5498 opt_info.offset = 0; 5499 opt_info.index++; 5500 } 5501 } 5502 pop(psp); 5503 return c; 5504 help: 5505 if (v && *v == '?' && *(v + 1) == '?' && *(v + 2)) 5506 { 5507 s = v + 2; 5508 if ((s[0] == 'n' || s[0] == 'N') && (s[1] == 'o' || s[1] == 'O')) 5509 { 5510 s += 2; 5511 n = -1; 5512 } 5513 else 5514 n = 1; 5515 if (hp = (Help_t*)search(styles, elementsof(styles), sizeof(styles[0]), s)) 5516 { 5517 if (hp->style < STYLE_man || !(s = argv[opt_info.index]) || s[0] != '-' || s[1] != '-' || !s[2]) 5518 { 5519 opt_info.arg = sfprints("\fversion=%d", version); 5520 pop(psp); 5521 return '?'; 5522 } 5523 state.force = hp->style; 5524 } 5525 else if (match(s, "CONFORMANCE", -1, ID, NiL)) 5526 { 5527 opt_info.arg = sfprints("\f%s", conformance(w, 0)); 5528 pop(psp); 5529 return '?'; 5530 } 5531 else if (match(s, "ESC", -1, ID, NiL) || match(s, "EMPHASIS", -1, ID, NiL)) 5532 state.emphasis = n; 5533 else if (match(s, "MAN", -1, ID, NiL)) 5534 { 5535 opt_info.arg = sfprints("\f%s", secname(*w != '?' ? w : pass->section)); 5536 pop(psp); 5537 return '?'; 5538 } 5539 else if (match(s, "PREFORMAT", -1, ID, NiL)) 5540 state.flags |= OPT_preformat; 5541 else if (match(s, "SECTION", -1, ID, NiL)) 5542 { 5543 opt_info.arg = sfprints("\f%s", pass->section); 5544 pop(psp); 5545 return '?'; 5546 } 5547 else if (match(s, "TEST", -1, ID, NiL)) 5548 { 5549 state.width = OPT_WIDTH; 5550 state.emphasis = 1; 5551 } 5552 else 5553 { 5554 pop(psp); 5555 return opterror(v, 0, version, id, catalog); 5556 } 5557 psp = pop(psp); 5558 if (argv == state.strv) 5559 return '#'; 5560 goto again; 5561 } 5562 if ((opt_info.arg = opthelp(NiL, v)) == (char*)unknown) 5563 { 5564 pop(psp); 5565 return opterror(v, 0, version, id, catalog); 5566 } 5567 pop(psp); 5568 return '?'; 5569 nospace: 5570 pop(psp); 5571 return opterror(NiL, 0, 0, NiL, NiL); 5572 } 5573 5574 /* 5575 * parse long options with 0,1,2 leading '-' or '+' from string and pass to optget() 5576 * syntax is the unquoted 5577 * 5578 * <length> [-|+|--|++]<name>[[-+:|&=]=<value>\n (or \0 for the last) 5579 * 5580 * or the quoted 5581 * 5582 * [-|+|--|++][no]name[[-+:|&=]=['"{(]value[)}"']][, ]... 5583 * 5584 * with \x escapes passed to chresc() 5585 * 5586 * return '#' for `label:', with opt_info.name==label 5587 * str[opt_info.offset] next arg 5588 * 5589 * optstr(s, 0) 5590 * return '-' if arg, 0 otherwise 5591 * optstr(0, opts) 5592 * use previous parsed str 5593 */ 5594 5595 int 5596 optstr(const char* str, const char* opts) 5597 { 5598 register char* s = (char*)str; 5599 register Sfio_t* mp; 5600 register int c; 5601 register int ql; 5602 register int qr; 5603 register int qc; 5604 int v; 5605 char* e; 5606 5607 again: 5608 if (s) 5609 { 5610 if (!(mp = state.strp) && !(mp = state.strp = sfstropen())) 5611 return 0; 5612 if (state.str != s) 5613 state.str = s; 5614 else if (opt_info.index == 1) 5615 s += opt_info.offset; 5616 while (*s == ',' || *s == ' ' || *s == '\t' || *s == '\n' || *s == '\r') 5617 s++; 5618 if (!*s) 5619 { 5620 state.str = 0; 5621 return 0; 5622 } 5623 if (*s == '-' || *s == '+') 5624 { 5625 c = *s++; 5626 sfputc(mp, c); 5627 if (*s == c) 5628 { 5629 sfputc(mp, c); 5630 s++; 5631 } 5632 } 5633 else 5634 { 5635 sfputc(mp, '-'); 5636 sfputc(mp, '-'); 5637 } 5638 if (isdigit(*s) && (v = (int)strtol(s, &e, 10)) > 1 && isspace(*e) && --v <= strlen(s) && (s[v] == 0 || s[v] == '\n')) 5639 { 5640 s += v; 5641 while (isspace(*++e)); 5642 sfwrite(mp, e, s - e); 5643 } 5644 else 5645 { 5646 while (*s && *s != ',' && *s != ' ' && *s != '\t' && *s != '\n' && *s != '\r' && *s != '=' && *s != ':') 5647 sfputc(mp, *s++); 5648 if ((c = *s) == ':' && *(s + 1) != '=') 5649 { 5650 opt_info.index = 1; 5651 opt_info.offset = ++s - (char*)str; 5652 if (!(s = sfstruse(mp))) 5653 goto nospace; 5654 s += 2; 5655 e = opt_info.name; 5656 while (e < &opt_info.name[sizeof(opt_info.name)-1] && (*e++ = *s++)); 5657 opt_info.arg = 0; 5658 opt_info.num = (long)(opt_info.number = 0); 5659 opt_info.option[0] = ':'; 5660 opt_info.option[1] = 0; 5661 return '#'; 5662 } 5663 if (c == ':' || c == '=') 5664 { 5665 sfputc(mp, c); 5666 ql = qr = 0; 5667 while (c = *++s) 5668 { 5669 if (c == '\\') 5670 { 5671 sfputc(mp, chresc(s, &e)); 5672 s = e - 1; 5673 } 5674 else if (c == qr) 5675 { 5676 if (qr != ql) 5677 sfputc(mp, c); 5678 if (--qc <= 0) 5679 qr = ql = 0; 5680 } 5681 else if (c == ql) 5682 { 5683 sfputc(mp, c); 5684 qc++; 5685 } 5686 else if (qr) 5687 sfputc(mp, c); 5688 else if (c == ',' || c == ' ' || c == '\t' || c == '\n' || c == '\r') 5689 break; 5690 else if (c == '"' || c == '\'') 5691 { 5692 ql = qr = c; 5693 qc = 1; 5694 } 5695 else 5696 { 5697 sfputc(mp, c); 5698 if (c == GO) 5699 { 5700 ql = c; 5701 qr = OG; 5702 qc = 1; 5703 } 5704 else if (c == '(') 5705 { 5706 ql = c; 5707 qr = ')'; 5708 qc = 1; 5709 } 5710 } 5711 } 5712 } 5713 } 5714 opt_info.argv = state.strv; 5715 state.strv[0] = T(NiL, ID, "option"); 5716 if (!(state.strv[1] = sfstruse(mp))) 5717 goto nospace; 5718 state.strv[2] = 0; 5719 opt_info.offset = s - (char*)str; 5720 } 5721 if (opts) 5722 { 5723 if (!state.strv[1]) 5724 { 5725 state.str = 0; 5726 return 0; 5727 } 5728 opt_info.index = 1; 5729 v = opt_info.offset; 5730 opt_info.offset = 0; 5731 c = optget(state.strv, opts); 5732 opt_info.index = 1; 5733 opt_info.offset = v; 5734 if (c == '#') 5735 { 5736 s = state.str; 5737 goto again; 5738 } 5739 if ((c == '?' || c == ':') && (opt_info.arg[0] == '-' && opt_info.arg[1] == '-')) 5740 opt_info.arg += 2; 5741 s = opt_info.name; 5742 if (*s++ == '-' && *s++ == '-' && *s) 5743 { 5744 e = opt_info.name; 5745 while (*e++ = *s++); 5746 } 5747 } 5748 else 5749 c = '-'; 5750 return c; 5751 nospace: 5752 return opterror(NiL, 0, 0, NiL, NiL); 5753 } 5754