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