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 53 tOptions* pShellParseOptions = NULL; 54 55 /* * * * * * * * * * * * * * * * * * * * * 56 * 57 * Setup Format Strings 58 */ 59 tSCC zStartMarker[] = 60 "# # # # # # # # # # -- do not modify this marker --\n#\n" 61 "# DO NOT EDIT THIS SECTION"; 62 63 tSCC 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 68 tSCC zEndPreamble[] = 69 "# From the %s option definitions\n#\n"; 70 71 tSCC 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 81 tSCC 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 86 tSCC 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 */ 99 tSCC 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 114 tSCC 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 */ 129 tSCC zLongSelection[] = 130 " --* )\n"; 131 132 tSCC zFlagSelection[] = 133 " -* )\n"; 134 135 tSCC zEndSelection[] = 136 " ;;\n\n"; 137 138 tSCC zNoSelection[] = 139 " * )\n" 140 " OPT_PROCESS=false\n" 141 " ;;\n" 142 " esac\n\n"; 143 144 /* * * * * * * * * * * * * * * * 145 * 146 * LOOP END 147 */ 148 tSCC 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 163 tSCC 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 */ 172 tSCC zOptionCase[] = 173 " case \"${OPT_CODE}\" in\n"; 174 175 tSCC zOptionPartName[] = 176 " '%s' | \\\n"; 177 178 tSCC zOptionFullName[] = 179 " '%s' )\n"; 180 181 tSCC zOptionFlag[] = 182 " '%c' )\n"; 183 184 tSCC zOptionEndSelect[] = 185 " ;;\n\n"; 186 187 tSCC 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 */ 201 tSCC zTextExit[] = 202 " echo \"$%s_%s_TEXT\"\n" 203 " exit 0\n"; 204 205 tSCC zPagedUsageExit[] = 206 " echo \"$%s_LONGUSAGE_TEXT\" | ${PAGER-more}\n" 207 " exit 0\n"; 208 209 tSCC zCmdFmt[] = 210 " %s\n"; 211 212 tSCC 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 218 tSCC 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 223 tSCC 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 231 tSCC 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 238 tSCC 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 248 tSCC 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 253 tSCC zMustArg[] = 254 " OPT_ARG_NEEDED=YES\n"; 255 256 tSCC 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 */ 267 tSCC 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 275 tSCC 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 */ 311 tSCC 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 315 tSCC 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 362 tSCC* pzShell = NULL; 363 static char* pzLeader = NULL; 364 static char* pzTrailer = NULL; 365 366 /* = = = START-STATIC-FORWARD = = = */ 367 /* static forward declarations maintained by :mkfwd */ 368 static void 369 textToVariable( tOptions* pOpts, teTextTo whichVar, tOptDesc* pOD ); 370 371 static void 372 emitUsage( tOptions* pOpts ); 373 374 static void 375 emitSetup( tOptions* pOpts ); 376 377 static void 378 printOptionAction( tOptions* pOpts, tOptDesc* pOptDesc ); 379 380 static void 381 printOptionInaction( tOptions* pOpts, tOptDesc* pOptDesc ); 382 383 static void 384 emitFlag( tOptions* pOpts ); 385 386 static void 387 emitMatchExpr( tCC* pzMatchName, tOptDesc* pCurOpt, tOptions* pOpts ); 388 389 static void 390 emitLong( tOptions* pOpts ); 391 392 static void 393 openOutput( 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 =*/ 405 void 406 optionParseShell( 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 499 static void 500 textToVariable( 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 600 static void 601 emitUsage( 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 670 static void 671 emitSetup( 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 736 static void 737 printOptionAction( 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 786 static void 787 printOptionInaction( 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 805 static void 806 emitFlag( 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 */ 830 static void 831 emitMatchExpr( 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 */ 910 static void 911 emitLong( 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 944 static void 945 openOutput( 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 =*/ 1036 void 1037 genshelloptUsage( 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