1 2/* 3 * $Id: makeshell.c,v 4.20 2007/02/04 17:44:12 bkorb Exp $ 4 * Time-stamp: "2007-01-27 06:05:45 bkorb" 5 * 6 * This module will interpret the options set in the tOptions 7 * structure and create a Bourne shell script capable of parsing them. 8 */ 9 10/* 11 * Automated Options copyright 1992-2007 Bruce Korb 12 * 13 * Automated Options is free software. 14 * You may redistribute it and/or modify it under the terms of the 15 * GNU General Public License, as published by the Free Software 16 * Foundation; either version 2, or (at your option) any later version. 17 * 18 * Automated Options is distributed in the hope that it will be useful, 19 * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 * GNU General Public License for more details. 22 * 23 * You should have received a copy of the GNU General Public License 24 * along with Automated Options. See the file "COPYING". If not, 25 * write to: The Free Software Foundation, Inc., 26 * 51 Franklin Street, Fifth Floor, 27 * Boston, MA 02110-1301, USA. 28 * 29 * As a special exception, Bruce Korb gives permission for additional 30 * uses of the text contained in his release of AutoOpts. 31 * 32 * The exception is that, if you link the AutoOpts library with other 33 * files to produce an executable, this does not by itself cause the 34 * resulting executable to be covered by the GNU General Public License. 35 * Your use of that executable is in no way restricted on account of 36 * linking the AutoOpts library code into it. 37 * 38 * This exception does not however invalidate any other reasons why 39 * the executable file might be covered by the GNU General Public License. 40 * 41 * This exception applies only to the code released by Bruce Korb under 42 * the name AutoOpts. If you copy code from other sources under the 43 * General Public License into a copy of AutoOpts, as the General Public 44 * License permits, the exception does not apply to the code that you add 45 * in this way. To avoid misleading anyone as to the status of such 46 * modified files, you must delete this exception notice from them. 47 * 48 * If you write modifications of your own for AutoOpts, it is your choice 49 * whether to permit this exception to apply to your modifications. 50 * If you do not wish that, delete this exception notice. 51 */ 52 53tOptions* pShellParseOptions = NULL; 54 55/* * * * * * * * * * * * * * * * * * * * * 56 * 57 * Setup Format Strings 58 */ 59tSCC zStartMarker[] = 60"# # # # # # # # # # -- do not modify this marker --\n#\n" 61"# DO NOT EDIT THIS SECTION"; 62 63tSCC zPreamble[] = 64"%s OF %s\n#\n" 65"# From here to the next `-- do not modify this marker --',\n" 66"# the text has been generated %s\n"; 67 68tSCC zEndPreamble[] = 69"# From the %s option definitions\n#\n"; 70 71tSCC zMultiDef[] = "\n" 72"if test -z \"${%1$s_%2$s}\"\n" 73"then\n" 74" %1$s_%2$s_CT=0\n" 75"else\n" 76" %1$s_%2$s_CT=1\n" 77" %1$s_%2$s_1=\"${%1$s_%2$s}\"\n" 78"fi\n" 79"export %1$s_%2$s_CT"; 80 81tSCC zSingleDef[] = "\n" 82"%1$s_%2$s=\"${%1$s_%2$s-'%3$s'}\"\n" 83"%1$s_%2$s_set=false\n" 84"export %1$s_%2$s\n"; 85 86tSCC zSingleNoDef[] = "\n" 87"%1$s_%2$s=\"${%1$s_%2$s}\"\n" 88"%1$s_%2$s_set=false\n" 89"export %1$s_%2$s\n"; 90 91/* * * * * * * * * * * * * * * * * * * * * 92 * 93 * LOOP START 94 * 95 * The loop may run in either of two modes: 96 * all options are named options (loop only) 97 * regular, marked option processing. 98 */ 99tSCC zLoopCase[] = "\n" 100"OPT_PROCESS=true\n" 101"OPT_ARG=\"$1\"\n\n" 102"while ${OPT_PROCESS} && [ $# -gt 0 ]\ndo\n" 103" OPT_ELEMENT=''\n" 104" OPT_ARG_VAL=''\n\n" 105 /* 106 * 'OPT_ARG' may or may not match the current $1 107 */ 108" case \"${OPT_ARG}\" in\n" 109" -- )\n" 110" OPT_PROCESS=false\n" 111" shift\n" 112" ;;\n\n"; 113 114tSCC zLoopOnly[] = "\n" 115"OPT_ARG=\"$1\"\n\n" 116"while [ $# -gt 0 ]\ndo\n" 117" OPT_ELEMENT=''\n" 118" OPT_ARG_VAL=''\n\n" 119" OPT_ARG=\"${1}\"\n"; 120 121/* * * * * * * * * * * * * * * * 122 * 123 * CASE SELECTORS 124 * 125 * If the loop runs as a regular option loop, 126 * then we must have selectors for each acceptable option 127 * type (long option, flag character and non-option) 128 */ 129tSCC zLongSelection[] = 130" --* )\n"; 131 132tSCC zFlagSelection[] = 133" -* )\n"; 134 135tSCC zEndSelection[] = 136" ;;\n\n"; 137 138tSCC zNoSelection[] = 139" * )\n" 140" OPT_PROCESS=false\n" 141" ;;\n" 142" esac\n\n"; 143 144/* * * * * * * * * * * * * * * * 145 * 146 * LOOP END 147 */ 148tSCC zLoopEnd[] = 149" if [ -n \"${OPT_ARG_VAL}\" ]\n" 150" then\n" 151" eval %1$s_${OPT_NAME}${OPT_ELEMENT}=\"'${OPT_ARG_VAL}'\"\n" 152" export %1$s_${OPT_NAME}${OPT_ELEMENT}\n" 153" fi\n" 154"done\n\n" 155"unset OPT_PROCESS || :\n" 156"unset OPT_ELEMENT || :\n" 157"unset OPT_ARG || :\n" 158"unset OPT_ARG_NEEDED || :\n" 159"unset OPT_NAME || :\n" 160"unset OPT_CODE || :\n" 161"unset OPT_ARG_VAL || :\n%2$s"; 162 163tSCC zTrailerMarker[] = "\n" 164"# # # # # # # # # #\n#\n" 165"# END OF AUTOMATED OPTION PROCESSING\n" 166"#\n# # # # # # # # # # -- do not modify this marker --\n"; 167 168/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 169 * 170 * OPTION SELECTION 171 */ 172tSCC zOptionCase[] = 173" case \"${OPT_CODE}\" in\n"; 174 175tSCC zOptionPartName[] = 176" '%s' | \\\n"; 177 178tSCC zOptionFullName[] = 179" '%s' )\n"; 180 181tSCC zOptionFlag[] = 182" '%c' )\n"; 183 184tSCC zOptionEndSelect[] = 185" ;;\n\n"; 186 187tSCC zOptionUnknown[] = 188" * )\n" 189" echo Unknown %s: \"${OPT_CODE}\" >&2\n" 190" echo \"$%s_USAGE_TEXT\"\n" 191" exit 1\n" 192" ;;\n" 193" esac\n\n"; 194 195/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 196 * 197 * OPTION PROCESSING 198 * 199 * Formats for emitting the text for handling particular options 200 */ 201tSCC zTextExit[] = 202" echo \"$%s_%s_TEXT\"\n" 203" exit 0\n"; 204 205tSCC zPagedUsageExit[] = 206" echo \"$%s_LONGUSAGE_TEXT\" | ${PAGER-more}\n" 207" exit 0\n"; 208 209tSCC zCmdFmt[] = 210" %s\n"; 211 212tSCC zCountTest[] = 213" if [ $%1$s_%2$s_CT -ge %3$d ] ; then\n" 214" echo Error: more than %3$d %2$s options >&2\n" 215" echo \"$%1$s_USAGE_TEXT\"\n" 216" exit 1 ; fi\n"; 217 218tSCC zMultiArg[] = 219" %1$s_%2$s_CT=`expr ${%1$s_%2$s_CT} + 1`\n" 220" OPT_ELEMENT=\"_${%1$s_%2$s_CT}\"\n" 221" OPT_NAME='%2$s'\n"; 222 223tSCC zSingleArg[] = 224" if [ -n \"${%1$s_%2$s}\" ] && ${%1$s_%2$s_set} ; then\n" 225" echo Error: duplicate %2$s option >&2\n" 226" echo \"$%1$s_USAGE_TEXT\"\n" 227" exit 1 ; fi\n" 228" %1$s_%2$s_set=true\n" 229" OPT_NAME='%2$s'\n"; 230 231tSCC zNoMultiArg[] = 232" %1$s_%2$s_CT=0\n" 233" OPT_ELEMENT=''\n" 234" %1$s_%2$s='%3$s'\n" 235" export %1$s_%2$s\n" 236" OPT_NAME='%2$s'\n"; 237 238tSCC zNoSingleArg[] = 239" if [ -n \"${%1$s_%2$s}\" ] && ${%1$s_%2$s_set} ; then\n" 240" echo Error: duplicate %2$s option >&2\n" 241" echo \"$%1$s_USAGE_TEXT\"\n" 242" exit 1 ; fi\n" 243" %1$s_%2$s_set=true\n" 244" %1$s_%2$s='%3$s'\n" 245" export %1$s_%2$s\n" 246" OPT_NAME='%2$s'\n"; 247 248tSCC zMayArg[] = 249" eval %1$s_%2$s${OPT_ELEMENT}=true\n" 250" export %1$s_%2$s${OPT_ELEMENT}\n" 251" OPT_ARG_NEEDED=OK\n"; 252 253tSCC zMustArg[] = 254" OPT_ARG_NEEDED=YES\n"; 255 256tSCC zCantArg[] = 257" eval %1$s_%2$s${OPT_ELEMENT}=true\n" 258" export %1$s_%2$s${OPT_ELEMENT}\n" 259" OPT_ARG_NEEDED=NO\n"; 260 261/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 262 * 263 * LONG OPTION PROCESSING 264 * 265 * Formats for emitting the text for handling long option types 266 */ 267tSCC zLongOptInit[] = 268" OPT_CODE=`echo \"X${OPT_ARG}\"|sed 's/^X-*//'`\n" 269" shift\n" 270" OPT_ARG=\"$1\"\n\n" 271" case \"${OPT_CODE}\" in *=* )\n" 272" OPT_ARG_VAL=`echo \"${OPT_CODE}\"|sed 's/^[^=]*=//'`\n" 273" OPT_CODE=`echo \"${OPT_CODE}\"|sed 's/=.*$//'` ;; esac\n\n"; 274 275tSCC zLongOptArg[] = 276" case \"${OPT_ARG_NEEDED}\" in\n" 277" NO )\n" 278" OPT_ARG_VAL=''\n" 279" ;;\n\n" 280" YES )\n" 281" if [ -z \"${OPT_ARG_VAL}\" ]\n" 282" then\n" 283" if [ $# -eq 0 ]\n" 284" then\n" 285" echo No argument provided for ${OPT_NAME} option >&2\n" 286" echo \"$%s_USAGE_TEXT\"\n" 287" exit 1\n" 288" fi\n\n" 289" OPT_ARG_VAL=\"${OPT_ARG}\"\n" 290" shift\n" 291" OPT_ARG=\"$1\"\n" 292" fi\n" 293" ;;\n\n" 294" OK )\n" 295" if [ -z \"${OPT_ARG_VAL}\" ] && [ $# -gt 0 ]\n" 296" then\n" 297" case \"${OPT_ARG}\" in -* ) ;; * )\n" 298" OPT_ARG_VAL=\"${OPT_ARG}\"\n" 299" shift\n" 300" OPT_ARG=\"$1\" ;; esac\n" 301" fi\n" 302" ;;\n" 303" esac\n"; 304 305/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 306 * 307 * FLAG OPTION PROCESSING 308 * 309 * Formats for emitting the text for handling flag option types 310 */ 311tSCC zFlagOptInit[] = 312" OPT_CODE=`echo \"X${OPT_ARG}\" | sed 's/X-\\(.\\).*/\\1/'`\n" 313" OPT_ARG=` echo \"X${OPT_ARG}\" | sed 's/X-.//'`\n\n"; 314 315tSCC zFlagOptArg[] = 316" case \"${OPT_ARG_NEEDED}\" in\n" 317" NO )\n" 318" if [ -n \"${OPT_ARG}\" ]\n" 319" then\n" 320" OPT_ARG=-\"${OPT_ARG}\"\n" 321" else\n" 322" shift\n" 323" OPT_ARG=\"$1\"\n" 324" fi\n" 325" ;;\n\n" 326" YES )\n" 327" if [ -n \"${OPT_ARG}\" ]\n" 328" then\n" 329" OPT_ARG_VAL=\"${OPT_ARG}\"\n\n" 330" else\n" 331" if [ $# -eq 0 ]\n" 332" then\n" 333" echo No argument provided for ${OPT_NAME} option >&2\n" 334" echo \"$%s_USAGE_TEXT\"\n" 335" exit 1\n" 336" fi\n" 337" shift\n" 338" OPT_ARG_VAL=\"$1\"\n" 339" fi\n\n" 340" shift\n" 341" OPT_ARG=\"$1\"\n" 342" ;;\n\n" 343" OK )\n" 344" if [ -n \"${OPT_ARG}\" ]\n" 345" then\n" 346" OPT_ARG_VAL=\"${OPT_ARG}\"\n" 347" shift\n" 348" OPT_ARG=\"$1\"\n\n" 349" else\n" 350" shift\n" 351" if [ $# -gt 0 ]\n" 352" then\n" 353" case \"$1\" in -* ) ;; * )\n" 354" OPT_ARG_VAL=\"$1\"\n" 355" shift ;; esac\n" 356" OPT_ARG=\"$1\"\n" 357" fi\n" 358" fi\n" 359" ;;\n" 360" esac\n"; 361 362tSCC* pzShell = NULL; 363static char* pzLeader = NULL; 364static char* pzTrailer = NULL; 365 366/* = = = START-STATIC-FORWARD = = = */ 367/* static forward declarations maintained by :mkfwd */ 368static void 369textToVariable( tOptions* pOpts, teTextTo whichVar, tOptDesc* pOD ); 370 371static void 372emitUsage( tOptions* pOpts ); 373 374static void 375emitSetup( tOptions* pOpts ); 376 377static void 378printOptionAction( tOptions* pOpts, tOptDesc* pOptDesc ); 379 380static void 381printOptionInaction( tOptions* pOpts, tOptDesc* pOptDesc ); 382 383static void 384emitFlag( tOptions* pOpts ); 385 386static void 387emitMatchExpr( tCC* pzMatchName, tOptDesc* pCurOpt, tOptions* pOpts ); 388 389static void 390emitLong( tOptions* pOpts ); 391 392static void 393openOutput( char const* pzFile ); 394/* = = = END-STATIC-FORWARD = = = */ 395 396/*=export_func optionParseShell 397 * private: 398 * 399 * what: Decipher a boolean value 400 * arg: + tOptions* + pOpts + program options descriptor + 401 * 402 * doc: 403 * Emit a shell script that will parse the command line options. 404=*/ 405void 406optionParseShell( tOptions* pOpts ) 407{ 408 /* 409 * Check for our SHELL option now. 410 * IF the output file contains the "#!" magic marker, 411 * it will override anything we do here. 412 */ 413 if (HAVE_OPT( SHELL )) 414 pzShell = OPT_ARG( SHELL ); 415 416 else if (! ENABLED_OPT( SHELL )) 417 pzShell = NULL; 418 419 else if ((pzShell = getenv( "SHELL" )), 420 pzShell == NULL) 421 422 pzShell = "/bin/sh"; 423 424 /* 425 * Check for a specified output file 426 */ 427 if (HAVE_OPT( SCRIPT )) 428 openOutput( OPT_ARG( SCRIPT )); 429 430 emitUsage( pOpts ); 431 emitSetup( pOpts ); 432 433 /* 434 * There are four modes of option processing. 435 */ 436 switch (pOpts->fOptSet & (OPTPROC_LONGOPT|OPTPROC_SHORTOPT)) { 437 case OPTPROC_LONGOPT: 438 fputs( zLoopCase, stdout ); 439 440 fputs( zLongSelection, stdout ); 441 fputs( zLongOptInit, stdout ); 442 emitLong( pOpts ); 443 printf( zLongOptArg, pOpts->pzPROGNAME ); 444 fputs( zEndSelection, stdout ); 445 446 fputs( zNoSelection, stdout ); 447 break; 448 449 case 0: 450 fputs( zLoopOnly, stdout ); 451 fputs( zLongOptInit, stdout ); 452 emitLong( pOpts ); 453 printf( zLongOptArg, pOpts->pzPROGNAME ); 454 break; 455 456 case OPTPROC_SHORTOPT: 457 fputs( zLoopCase, stdout ); 458 459 fputs( zFlagSelection, stdout ); 460 fputs( zFlagOptInit, stdout ); 461 emitFlag( pOpts ); 462 printf( zFlagOptArg, pOpts->pzPROGNAME ); 463 fputs( zEndSelection, stdout ); 464 465 fputs( zNoSelection, stdout ); 466 break; 467 468 case OPTPROC_LONGOPT|OPTPROC_SHORTOPT: 469 fputs( zLoopCase, stdout ); 470 471 fputs( zLongSelection, stdout ); 472 fputs( zLongOptInit, stdout ); 473 emitLong( pOpts ); 474 printf( zLongOptArg, pOpts->pzPROGNAME ); 475 fputs( zEndSelection, stdout ); 476 477 fputs( zFlagSelection, stdout ); 478 fputs( zFlagOptInit, stdout ); 479 emitFlag( pOpts ); 480 printf( zFlagOptArg, pOpts->pzPROGNAME ); 481 fputs( zEndSelection, stdout ); 482 483 fputs( zNoSelection, stdout ); 484 break; 485 } 486 487 printf( zLoopEnd, pOpts->pzPROGNAME, zTrailerMarker ); 488 if ((pzTrailer != NULL) && (*pzTrailer != '\0')) 489 fputs( pzTrailer, stdout ); 490 else if (ENABLED_OPT( SHELL )) 491 printf( "\nenv | grep '^%s_'\n", pOpts->pzPROGNAME ); 492 493 fflush( stdout ); 494 fchmod( STDOUT_FILENO, 0755 ); 495 fclose( stdout ); 496} 497 498 499static void 500textToVariable( tOptions* pOpts, teTextTo whichVar, tOptDesc* pOD ) 501{ 502# define _TT_(n) tSCC z ## n [] = #n; 503 TEXTTO_TABLE 504# undef _TT_ 505# define _TT_(n) z ## n , 506 static char const* apzTTNames[] = { TEXTTO_TABLE }; 507# undef _TT_ 508 509#if defined(__windows__) && !defined(__CYGWIN__) 510 printf( "%1$s_%2$s_TEXT='no %2$s text'\n", 511 pOpts->pzPROGNAME, apzTTNames[ whichVar ]); 512#else 513 int nlHoldCt = 0; 514 int pipeFd[2]; 515 FILE* fp; 516 517 printf( "%s_%s_TEXT='", pOpts->pzPROGNAME, apzTTNames[ whichVar ]); 518 fflush( stdout ); 519 520 if (pipe( pipeFd ) != 0) { 521 fprintf( stderr, zBadPipe, errno, strerror( errno )); 522 exit( EXIT_FAILURE ); 523 } 524 525 switch (fork()) { 526 case -1: 527 fprintf( stderr, zForkFail, errno, strerror(errno), pOpts->pzProgName); 528 exit( EXIT_FAILURE ); 529 break; 530 531 case 0: 532 dup2( pipeFd[1], STDERR_FILENO ); 533 dup2( pipeFd[1], STDOUT_FILENO ); 534 close( pipeFd[0] ); 535 536 switch (whichVar) { 537 case TT_LONGUSAGE: 538 (*(pOpts->pUsageProc))( pOpts, EXIT_SUCCESS ); 539 /* NOTREACHED */ 540 exit( EXIT_FAILURE ); 541 542 case TT_USAGE: 543 (*(pOpts->pUsageProc))( pOpts, EXIT_FAILURE ); 544 /* NOTREACHED */ 545 exit( EXIT_FAILURE ); 546 547 case TT_VERSION: 548 if (pOD->fOptState & OPTST_ALLOC_ARG) { 549 AGFREE(pOD->optArg.argString); 550 pOD->fOptState &= ~OPTST_ALLOC_ARG; 551 } 552 pOD->optArg.argString = "c"; 553 optionPrintVersion( pOpts, pOD ); 554 /* NOTREACHED */ 555 556 default: 557 exit( EXIT_FAILURE ); 558 } 559 560 default: 561 close( pipeFd[1] ); 562 fp = fdopen( pipeFd[0], "r" FOPEN_BINARY_FLAG ); 563 } 564 565 for (;;) { 566 int ch = fgetc( fp ); 567 switch (ch) { 568 569 case '\n': 570 nlHoldCt++; 571 break; 572 573 case '\'': 574 while (nlHoldCt > 0) { 575 fputc( '\n', stdout ); 576 nlHoldCt--; 577 } 578 fputs( "'\\''", stdout ); 579 break; 580 581 case EOF: 582 goto endCharLoop; 583 584 default: 585 while (nlHoldCt > 0) { 586 fputc( '\n', stdout ); 587 nlHoldCt--; 588 } 589 fputc( ch, stdout ); 590 break; 591 } 592 } endCharLoop:; 593 594 fputs( "'\n\n", stdout ); 595 close( pipeFd[0] ); 596#endif 597} 598 599 600static void 601emitUsage( tOptions* pOpts ) 602{ 603 char zTimeBuf[ AO_NAME_SIZE ]; 604 605 /* 606 * First, switch stdout to the output file name. 607 * Then, change the program name to the one defined 608 * by the definitions (rather than the current 609 * executable name). Down case the upper cased name. 610 */ 611 if (pzLeader != NULL) 612 fputs( pzLeader, stdout ); 613 614 { 615 tSCC zStdout[] = "stdout"; 616 tCC* pzOutName; 617 618 { 619 time_t curTime = time( NULL ); 620 struct tm* pTime = localtime( &curTime ); 621 strftime(zTimeBuf, AO_NAME_SIZE, "%A %B %e, %Y at %r %Z", pTime ); 622 } 623 624 if (HAVE_OPT( SCRIPT )) 625 pzOutName = OPT_ARG( SCRIPT ); 626 else pzOutName = zStdout; 627 628 if ((pzLeader == NULL) && (pzShell != NULL)) 629 printf( "#! %s\n", pzShell ); 630 631 printf( zPreamble, zStartMarker, pzOutName, zTimeBuf ); 632 } 633 634 /* 635 * Get a copy of the original program name in lower case 636 */ 637 { 638 char* pzPN = zTimeBuf; 639 tCC* pz = pOpts->pzPROGNAME; 640 for (;;) { 641 if ((*pzPN++ = tolower( *pz++ )) == '\0') 642 break; 643 } 644 } 645 646 printf( zEndPreamble, pOpts->pzPROGNAME ); 647 648 pOpts->pzProgPath = pOpts->pzProgName = zTimeBuf; 649 textToVariable( pOpts, TT_LONGUSAGE, NULL ); 650 textToVariable( pOpts, TT_USAGE, NULL ); 651 652 { 653 tOptDesc* pOptDesc = pOpts->pOptDesc; 654 int optionCt = pOpts->optCt; 655 656 for (;;) { 657 if (pOptDesc->pOptProc == optionPrintVersion) { 658 textToVariable( pOpts, TT_VERSION, pOptDesc ); 659 break; 660 } 661 662 if (--optionCt <= 0) 663 break; 664 pOptDesc++; 665 } 666 } 667} 668 669 670static void 671emitSetup( tOptions* pOpts ) 672{ 673 tOptDesc* pOptDesc = pOpts->pOptDesc; 674 int optionCt = pOpts->presetOptCt; 675 char const* pzFmt; 676 char const* pzDefault; 677 678 for (;optionCt > 0; pOptDesc++, --optionCt) { 679 char zVal[16]; 680 681 /* 682 * Options that are either usage documentation or are compiled out 683 * are not to be processed. 684 */ 685 if (SKIP_OPT(pOptDesc) || (pOptDesc->pz_NAME == NULL)) 686 continue; 687 688 if (pOptDesc->optMaxCt > 1) 689 pzFmt = zMultiDef; 690 else pzFmt = zSingleDef; 691 692 /* 693 * IF this is an enumeration/bitmask option, then convert the value 694 * to a string before printing the default value. 695 */ 696 switch (OPTST_GET_ARGTYPE(pOptDesc->fOptState)) { 697 case OPARG_TYPE_ENUMERATION: 698 (*(pOptDesc->pOptProc))( (tOptions*)2UL, pOptDesc ); 699 pzDefault = pOptDesc->optArg.argString; 700 break; 701 702 /* 703 * Numeric and membership bit options are just printed as a number. 704 */ 705 case OPARG_TYPE_NUMERIC: 706 snprintf( zVal, sizeof( zVal ), "%d", 707 (int)pOptDesc->optArg.argInt ); 708 pzDefault = zVal; 709 break; 710 711 case OPARG_TYPE_MEMBERSHIP: 712 snprintf( zVal, sizeof( zVal ), "%lu", 713 (unsigned long)pOptDesc->optArg.argIntptr ); 714 pzDefault = zVal; 715 break; 716 717 case OPARG_TYPE_BOOLEAN: 718 pzDefault = (pOptDesc->optArg.argBool) ? "true" : "false"; 719 break; 720 721 default: 722 if (pOptDesc->optArg.argString == NULL) { 723 if (pzFmt == zSingleDef) 724 pzFmt = zSingleNoDef; 725 pzDefault = NULL; 726 } 727 else 728 pzDefault = pOptDesc->optArg.argString; 729 } 730 731 printf( pzFmt, pOpts->pzPROGNAME, pOptDesc->pz_NAME, pzDefault ); 732 } 733} 734 735 736static void 737printOptionAction( tOptions* pOpts, tOptDesc* pOptDesc ) 738{ 739 if (pOptDesc->pOptProc == optionPrintVersion) 740 printf( zTextExit, pOpts->pzPROGNAME, "VERSION" ); 741 742 else if (pOptDesc->pOptProc == optionPagedUsage) 743 printf( zPagedUsageExit, pOpts->pzPROGNAME ); 744 745 else if (pOptDesc->pOptProc == optionLoadOpt) { 746 printf( zCmdFmt, "echo 'Warning: Cannot load options files' >&2" ); 747 printf( zCmdFmt, "OPT_ARG_NEEDED=YES" ); 748 749 } else if (pOptDesc->pz_NAME == NULL) { 750 751 if (pOptDesc->pOptProc == NULL) { 752 printf( zCmdFmt, "echo 'Warning: Cannot save options files' " 753 ">&2" ); 754 printf( zCmdFmt, "OPT_ARG_NEEDED=OK" ); 755 } else 756 printf( zTextExit, pOpts->pzPROGNAME, "LONGUSAGE" ); 757 758 } else { 759 if (pOptDesc->optMaxCt == 1) 760 printf( zSingleArg, pOpts->pzPROGNAME, pOptDesc->pz_NAME ); 761 else { 762 if ((unsigned)pOptDesc->optMaxCt < NOLIMIT) 763 printf( zCountTest, pOpts->pzPROGNAME, 764 pOptDesc->pz_NAME, pOptDesc->optMaxCt ); 765 766 printf( zMultiArg, pOpts->pzPROGNAME, pOptDesc->pz_NAME ); 767 } 768 769 /* 770 * Fix up the args. 771 */ 772 if (OPTST_GET_ARGTYPE(pOptDesc->fOptState) == OPARG_TYPE_NONE) { 773 printf( zCantArg, pOpts->pzPROGNAME, pOptDesc->pz_NAME ); 774 775 } else if (pOptDesc->fOptState & OPTST_ARG_OPTIONAL) { 776 printf( zMayArg, pOpts->pzPROGNAME, pOptDesc->pz_NAME ); 777 778 } else { 779 fputs( zMustArg, stdout ); 780 } 781 } 782 fputs( zOptionEndSelect, stdout ); 783} 784 785 786static void 787printOptionInaction( tOptions* pOpts, tOptDesc* pOptDesc ) 788{ 789 if (pOptDesc->pOptProc == optionLoadOpt) { 790 printf( zCmdFmt, "echo 'Warning: Cannot suppress the loading of " 791 "options files' >&2" ); 792 793 } else if (pOptDesc->optMaxCt == 1) 794 printf( zNoSingleArg, pOpts->pzPROGNAME, 795 pOptDesc->pz_NAME, pOptDesc->pz_DisablePfx ); 796 else 797 printf( zNoMultiArg, pOpts->pzPROGNAME, 798 pOptDesc->pz_NAME, pOptDesc->pz_DisablePfx ); 799 800 printf( zCmdFmt, "OPT_ARG_NEEDED=NO" ); 801 fputs( zOptionEndSelect, stdout ); 802} 803 804 805static void 806emitFlag( tOptions* pOpts ) 807{ 808 tOptDesc* pOptDesc = pOpts->pOptDesc; 809 int optionCt = pOpts->optCt; 810 811 fputs( zOptionCase, stdout ); 812 813 for (;optionCt > 0; pOptDesc++, --optionCt) { 814 815 if (SKIP_OPT(pOptDesc)) 816 continue; 817 818 if (isprint( pOptDesc->optValue )) { 819 printf( zOptionFlag, pOptDesc->optValue ); 820 printOptionAction( pOpts, pOptDesc ); 821 } 822 } 823 printf( zOptionUnknown, "flag", pOpts->pzPROGNAME ); 824} 825 826 827/* 828 * Emit the match text for a long option 829 */ 830static void 831emitMatchExpr( tCC* pzMatchName, tOptDesc* pCurOpt, tOptions* pOpts ) 832{ 833 tOptDesc* pOD = pOpts->pOptDesc; 834 int oCt = pOpts->optCt; 835 int min = 1; 836 char zName[ 256 ]; 837 char* pz = zName; 838 839 for (;;) { 840 int matchCt = 0; 841 842 /* 843 * Omit the current option, Documentation opts and compiled out opts. 844 */ 845 if ((pOD == pCurOpt) || SKIP_OPT(pOD)){ 846 if (--oCt <= 0) 847 break; 848 pOD++; 849 continue; 850 } 851 852 /* 853 * Check each character of the name case insensitively. 854 * They must not be the same. They cannot be, because it would 855 * not compile correctly if they were. 856 */ 857 while ( toupper( pOD->pz_Name[matchCt] ) 858 == toupper( pzMatchName[matchCt] )) 859 matchCt++; 860 861 if (matchCt > min) 862 min = matchCt; 863 864 /* 865 * Check the disablement name, too. 866 */ 867 if (pOD->pz_DisableName != NULL) { 868 matchCt = 0; 869 while ( toupper( pOD->pz_DisableName[matchCt] ) 870 == toupper( pzMatchName[matchCt] )) 871 matchCt++; 872 if (matchCt > min) 873 min = matchCt; 874 } 875 if (--oCt <= 0) 876 break; 877 pOD++; 878 } 879 880 /* 881 * IF the 'min' is all or one short of the name length, 882 * THEN the entire string must be matched. 883 */ 884 if ( (pzMatchName[min ] == NUL) 885 || (pzMatchName[min+1] == NUL) ) 886 printf( zOptionFullName, pzMatchName ); 887 888 else { 889 int matchCt = 0; 890 for (; matchCt <= min; matchCt++) 891 *pz++ = pzMatchName[matchCt]; 892 893 for (;;) { 894 *pz = NUL; 895 printf( zOptionPartName, zName ); 896 *pz++ = pzMatchName[matchCt++]; 897 if (pzMatchName[matchCt] == NUL) { 898 *pz = NUL; 899 printf( zOptionFullName, zName ); 900 break; 901 } 902 } 903 } 904} 905 906 907/* 908 * Emit GNU-standard long option handling code 909 */ 910static void 911emitLong( tOptions* pOpts ) 912{ 913 tOptDesc* pOD = pOpts->pOptDesc; 914 int ct = pOpts->optCt; 915 916 fputs( zOptionCase, stdout ); 917 918 /* 919 * do each option, ... 920 */ 921 do { 922 /* 923 * Documentation & compiled-out options 924 */ 925 if (SKIP_OPT(pOD)) 926 continue; 927 928 emitMatchExpr( pOD->pz_Name, pOD, pOpts ); 929 printOptionAction( pOpts, pOD ); 930 931 /* 932 * Now, do the same thing for the disablement version of the option. 933 */ 934 if (pOD->pz_DisableName != NULL) { 935 emitMatchExpr( pOD->pz_DisableName, pOD, pOpts ); 936 printOptionInaction( pOpts, pOD ); 937 } 938 } while (pOD++, --ct > 0); 939 940 printf( zOptionUnknown, "option", pOpts->pzPROGNAME ); 941} 942 943 944static void 945openOutput( char const* pzFile ) 946{ 947 FILE* fp; 948 char* pzData = NULL; 949 struct stat stbf; 950 951 do { 952 char* pzScan; 953 size_t sizeLeft; 954 955 /* 956 * IF we cannot stat the file, 957 * THEN assume we are creating a new file. 958 * Skip the loading of the old data. 959 */ 960 if (stat( pzFile, &stbf ) != 0) 961 break; 962 963 /* 964 * The file must be a regular file 965 */ 966 if (! S_ISREG( stbf.st_mode )) { 967 fprintf( stderr, zNotFile, pzFile ); 968 exit( EXIT_FAILURE ); 969 } 970 971 pzData = AGALOC(stbf.st_size + 1, "file data"); 972 fp = fopen( pzFile, "r" FOPEN_BINARY_FLAG ); 973 974 sizeLeft = (unsigned)stbf.st_size; 975 pzScan = pzData; 976 977 /* 978 * Read in all the data as fast as our OS will let us. 979 */ 980 for (;;) { 981 int inct = fread( (void*)pzScan, (size_t)1, sizeLeft, fp); 982 if (inct == 0) 983 break; 984 985 pzScan += inct; 986 sizeLeft -= inct; 987 988 if (sizeLeft == 0) 989 break; 990 } 991 992 /* 993 * NUL-terminate the leader and look for the trailer 994 */ 995 *pzScan = '\0'; 996 fclose( fp ); 997 pzScan = strstr( pzData, zStartMarker ); 998 if (pzScan == NULL) { 999 pzTrailer = pzData; 1000 break; 1001 } 1002 1003 *(pzScan++) = NUL; 1004 pzScan = strstr( pzScan, zTrailerMarker ); 1005 if (pzScan == NULL) { 1006 pzTrailer = pzData; 1007 break; 1008 } 1009 1010 /* 1011 * Check to see if the data contains 1012 * our marker. If it does, then we will skip over it 1013 */ 1014 pzTrailer = pzScan + sizeof( zTrailerMarker ) - 1; 1015 pzLeader = pzData; 1016 } while (AG_FALSE); 1017 1018 freopen( pzFile, "w" FOPEN_BINARY_FLAG, stdout ); 1019} 1020 1021 1022/*=export_func genshelloptUsage 1023 * private: 1024 * what: The usage function for the genshellopt generated program 1025 * 1026 * arg: + tOptions* + pOpts + program options descriptor + 1027 * arg: + int + exitCode + usage text type to produce + 1028 * 1029 * doc: 1030 * This function is used to create the usage strings for the option 1031 * processing shell script code. Two child processes are spawned 1032 * each emitting the usage text in either the short (error exit) 1033 * style or the long style. The generated program will capture this 1034 * and create shell script variables containing the two types of text. 1035=*/ 1036void 1037genshelloptUsage( tOptions* pOpts, int exitCode ) 1038{ 1039#if defined(__windows__) && !defined(__CYGWIN__) 1040 optionUsage( pOpts, exitCode ); 1041#else 1042 /* 1043 * IF not EXIT_SUCCESS, 1044 * THEN emit the short form of usage. 1045 */ 1046 if (exitCode != EXIT_SUCCESS) 1047 optionUsage( pOpts, exitCode ); 1048 fflush( stderr ); 1049 fflush( stdout ); 1050 1051 option_usage_fp = stdout; 1052 1053 /* 1054 * First, print our usage 1055 */ 1056 switch (fork()) { 1057 case -1: 1058 optionUsage( pOpts, EXIT_FAILURE ); 1059 /*NOTREACHED*/ 1060 _exit( EXIT_FAILURE ); 1061 1062 case 0: 1063 pagerState = PAGER_STATE_CHILD; 1064 optionUsage( pOpts, EXIT_SUCCESS ); 1065 /*NOTREACHED*/ 1066 _exit( EXIT_FAILURE ); 1067 1068 default: 1069 { 1070 int sts; 1071 wait( &sts ); 1072 } 1073 } 1074 1075 /* 1076 * Generate the pzProgName, since optionProcess() normally 1077 * gets it from the command line 1078 */ 1079 { 1080 char* pz; 1081 AGDUPSTR( pz, pShellParseOptions->pzPROGNAME, "program name" ); 1082 pShellParseOptions->pzProgName = pz; 1083 while (*pz != NUL) { 1084 *pz = tolower( *pz ); 1085 pz++; 1086 } 1087 } 1088 1089 /* 1090 * Separate the makeshell usage from the client usage 1091 */ 1092 fprintf( option_usage_fp, zGenshell, pShellParseOptions->pzProgName ); 1093 fflush( option_usage_fp ); 1094 1095 /* 1096 * Now, print the client usage. 1097 */ 1098 switch (fork()) { 1099 case 0: 1100 pagerState = PAGER_STATE_CHILD; 1101 /*FALLTHROUGH*/ 1102 case -1: 1103 optionUsage( pShellParseOptions, EXIT_FAILURE ); 1104 1105 default: 1106 { 1107 int sts; 1108 wait( &sts ); 1109 } 1110 } 1111 1112 exit( EXIT_SUCCESS ); 1113#endif 1114} 1115 1116/* 1117 * Local Variables: 1118 * mode: C 1119 * c-file-style: "stroustrup" 1120 * indent-tabs-mode: nil 1121 * End: 1122 * end of autoopts/makeshell.c */ 1123