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 33 tOptions * optionParseShellOptions = NULL; 34 35 /* * * * * * * * * * * * * * * * * * * * * 36 * 37 * Setup Format Strings 38 */ 39 static char const zStartMarker[] = 40 "# # # # # # # # # # -- do not modify this marker --\n#\n" 41 "# DO NOT EDIT THIS SECTION"; 42 43 static 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 48 static char const zEndPreamble[] = 49 "# From the %s option definitions\n#\n"; 50 51 static 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 61 static 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 66 static 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 */ 79 static 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 94 static 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 */ 109 static char const zLongSelection[] = 110 " --* )\n"; 111 112 static char const zFlagSelection[] = 113 " -* )\n"; 114 115 static char const zEndSelection[] = 116 " ;;\n\n"; 117 118 static char const zNoSelection[] = 119 " * )\n" 120 " OPT_PROCESS=false\n" 121 " ;;\n" 122 " esac\n\n"; 123 124 /* * * * * * * * * * * * * * * * 125 * 126 * LOOP END 127 */ 128 static 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 143 static 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 */ 152 static char const zOptionCase[] = 153 " case \"${OPT_CODE}\" in\n"; 154 155 static char const zOptionPartName[] = 156 " '%s' | \\\n"; 157 158 static char const zOptionFullName[] = 159 " '%s' )\n"; 160 161 static char const zOptionFlag[] = 162 " '%c' )\n"; 163 164 static char const zOptionEndSelect[] = 165 " ;;\n\n"; 166 167 static 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 */ 181 static char const zTextExit[] = 182 " echo \"$%s_%s_TEXT\"\n" 183 " exit 0\n"; 184 185 static char const zPagedUsageExit[] = 186 " echo \"$%s_LONGUSAGE_TEXT\" | ${PAGER-more}\n" 187 " exit 0\n"; 188 189 static char const zCmdFmt[] = 190 " %s\n"; 191 192 static 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 198 static 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 203 static 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 211 static 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 218 static 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 228 static 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 233 static char const zMustArg[] = 234 " OPT_ARG_NEEDED=YES\n"; 235 236 static 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 */ 247 static 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 255 static 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 */ 291 static 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 295 static 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 342 tSCC* pzShell = NULL; 343 static char* pzLeader = NULL; 344 static char* pzTrailer = NULL; 345 346 /* = = = START-STATIC-FORWARD = = = */ 347 static void 348 emit_var_text(char const * prog, char const * var, int fdin); 349 350 static void 351 textToVariable(tOptions * pOpts, teTextTo whichVar, tOptDesc * pOD); 352 353 static void 354 emitUsage(tOptions* pOpts); 355 356 static void 357 emitSetup(tOptions* pOpts); 358 359 static void 360 printOptionAction(tOptions* pOpts, tOptDesc* pOptDesc); 361 362 static void 363 printOptionInaction(tOptions* pOpts, tOptDesc* pOptDesc); 364 365 static void 366 emitFlag(tOptions* pOpts); 367 368 static void 369 emitMatchExpr(tCC* pzMatchName, tOptDesc* pCurOpt, tOptions* pOpts); 370 371 static void 372 emitLong(tOptions* pOpts); 373 374 static void 375 openOutput(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 =*/ 387 void 388 optionParseShell(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 485 static void 486 emit_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 526 skip_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 */ 540 static void 541 textToVariable(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 610 static void 611 emitUsage(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 687 static void 688 emitSetup(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 753 static void 754 printOptionAction(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 803 static void 804 printOptionInaction(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 822 static void 823 emitFlag(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 */ 847 static void 848 emitMatchExpr(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 */ 927 static void 928 emitLong(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 961 static void 962 openOutput(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 =*/ 1056 void 1057 genshelloptUsage(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