1 2 /** 3 * \file makeshell.c 4 * 5 * This module will interpret the options set in the tOptions 6 * structure and create a Bourne shell script capable of parsing them. 7 * 8 * @addtogroup autoopts 9 * @{ 10 */ 11 /* 12 * This file is part of AutoOpts, a companion to AutoGen. 13 * AutoOpts is free software. 14 * AutoOpts is Copyright (C) 1992-2014 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 sha256 sums: 27 * 28 * 8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95 COPYING.gplv3 29 * 4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b COPYING.lgplv3 30 * 13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239 COPYING.mbsd 31 */ 32 33 /* = = = START-STATIC-FORWARD = = = */ 34 static void 35 emit_var_text(char const * prog, char const * var, int fdin); 36 37 static void 38 text_to_var(tOptions * opts, teTextTo which, tOptDesc * od); 39 40 static void 41 emit_usage(tOptions * opts); 42 43 static void 44 emit_wrapup(tOptions * opts); 45 46 static void 47 emit_setup(tOptions * opts); 48 49 static void 50 emit_action(tOptions * opts, tOptDesc * od); 51 52 static void 53 emit_inaction(tOptions * opts, tOptDesc * od); 54 55 static void 56 emit_flag(tOptions * opts); 57 58 static void 59 emit_match_expr(char const * name, tOptDesc * cod, tOptions * opts); 60 61 static void 62 emit_long(tOptions * opts); 63 64 static char * 65 load_old_output(char const * fname, char const * pname); 66 67 static void 68 open_out(char const * fname, char const * pname); 69 /* = = = END-STATIC-FORWARD = = = */ 70 71 LOCAL noreturn void 72 option_exits(int exit_code) 73 { 74 if (print_exit) 75 printf("\nexit %d\n", exit_code); 76 exit(exit_code); 77 } 78 79 LOCAL noreturn void 80 ao_bug(char const * msg) 81 { 82 fprintf(stderr, zao_bug_msg, msg); 83 option_exits(EX_SOFTWARE); 84 } 85 86 LOCAL void 87 fserr_warn(char const * prog, char const * op, char const * fname) 88 { 89 fprintf(stderr, zfserr_fmt, prog, errno, strerror(errno), 90 op, fname); 91 } 92 93 LOCAL noreturn void 94 fserr_exit(char const * prog, char const * op, char const * fname) 95 { 96 fserr_warn(prog, op, fname); 97 option_exits(EXIT_FAILURE); 98 } 99 100 /*=export_func optionParseShell 101 * private: 102 * 103 * what: Decipher a boolean value 104 * arg: + tOptions* + pOpts + program options descriptor + 105 * 106 * doc: 107 * Emit a shell script that will parse the command line options. 108 =*/ 109 void 110 optionParseShell(tOptions * opts) 111 { 112 /* 113 * Check for our SHELL option now. 114 * IF the output file contains the "#!" magic marker, 115 * it will override anything we do here. 116 */ 117 if (HAVE_GENSHELL_OPT(SHELL)) 118 shell_prog = GENSHELL_OPT_ARG(SHELL); 119 120 else if (! ENABLED_GENSHELL_OPT(SHELL)) 121 shell_prog = NULL; 122 123 else if ((shell_prog = getenv("SHELL")), 124 shell_prog == NULL) 125 126 shell_prog = POSIX_SHELL; 127 128 /* 129 * Check for a specified output file 130 */ 131 if (HAVE_GENSHELL_OPT(SCRIPT)) 132 open_out(GENSHELL_OPT_ARG(SCRIPT), opts->pzProgName); 133 134 emit_usage(opts); 135 emit_setup(opts); 136 137 /* 138 * There are four modes of option processing. 139 */ 140 switch (opts->fOptSet & (OPTPROC_LONGOPT|OPTPROC_SHORTOPT)) { 141 case OPTPROC_LONGOPT: 142 fputs(LOOP_STR, stdout); 143 144 fputs(LONG_OPT_MARK, stdout); 145 fputs(INIT_LOPT_STR, stdout); 146 emit_long(opts); 147 printf(LOPT_ARG_FMT, opts->pzPROGNAME); 148 fputs(END_OPT_SEL_STR, stdout); 149 150 fputs(NOT_FOUND_STR, stdout); 151 break; 152 153 case 0: 154 fputs(ONLY_OPTS_LOOP, stdout); 155 fputs(INIT_LOPT_STR, stdout); 156 emit_long(opts); 157 printf(LOPT_ARG_FMT, opts->pzPROGNAME); 158 break; 159 160 case OPTPROC_SHORTOPT: 161 fputs(LOOP_STR, stdout); 162 163 fputs(FLAG_OPT_MARK, stdout); 164 fputs(INIT_OPT_STR, stdout); 165 emit_flag(opts); 166 printf(OPT_ARG_FMT, opts->pzPROGNAME); 167 fputs(END_OPT_SEL_STR, stdout); 168 169 fputs(NOT_FOUND_STR, stdout); 170 break; 171 172 case OPTPROC_LONGOPT|OPTPROC_SHORTOPT: 173 fputs(LOOP_STR, stdout); 174 175 fputs(LONG_OPT_MARK, stdout); 176 fputs(INIT_LOPT_STR, stdout); 177 emit_long(opts); 178 printf(LOPT_ARG_FMT, opts->pzPROGNAME); 179 fputs(END_OPT_SEL_STR, stdout); 180 181 fputs(FLAG_OPT_MARK, stdout); 182 fputs(INIT_OPT_STR, stdout); 183 emit_flag(opts); 184 printf(OPT_ARG_FMT, opts->pzPROGNAME); 185 fputs(END_OPT_SEL_STR, stdout); 186 187 fputs(NOT_FOUND_STR, stdout); 188 break; 189 } 190 191 emit_wrapup(opts); 192 if ((script_trailer != NULL) && (*script_trailer != NUL)) 193 fputs(script_trailer, stdout); 194 else if (ENABLED_GENSHELL_OPT(SHELL)) 195 printf(SHOW_PROG_ENV, opts->pzPROGNAME); 196 197 #ifdef HAVE_FCHMOD 198 fchmod(STDOUT_FILENO, 0755); 199 #endif 200 fclose(stdout); 201 202 if (ferror(stdout)) 203 fserr_exit(opts->pzProgName, zwriting, zstdout_name); 204 205 AGFREE(script_text); 206 script_leader = NULL; 207 script_trailer = NULL; 208 script_text = NULL; 209 } 210 211 #ifdef HAVE_WORKING_FORK 212 /** 213 * Print the value of "var" to a file descriptor. 214 * The "fdin" is the read end of a pipe to a forked process that 215 * is writing usage text to it. We read that text in and re-emit 216 * to standard out, formatting it so that it is assigned to a 217 * shell variable. 218 * 219 * @param[in] prog The capitalized, c-variable-formatted program name 220 * @param[in] var a similarly formatted type name 221 * (LONGUSAGE, USAGE or VERSION) 222 * @param[in] fdin the input end of a pipe 223 */ 224 static void 225 emit_var_text(char const * prog, char const * var, int fdin) 226 { 227 FILE * fp = fdopen(fdin, "r" FOPEN_BINARY_FLAG); 228 int nlct = 0; /* defer newlines and skip trailing ones */ 229 230 printf(SET_TEXT_FMT, prog, var); 231 if (fp == NULL) 232 goto skip_text; 233 234 for (;;) { 235 int ch = fgetc(fp); 236 switch (ch) { 237 238 case NL: 239 nlct++; 240 break; 241 242 case '\'': 243 while (nlct > 0) { 244 fputc(NL, stdout); 245 nlct--; 246 } 247 fputs(apostrophe, stdout); 248 break; 249 250 case EOF: 251 goto done; 252 253 default: 254 while (nlct > 0) { 255 fputc(NL, stdout); 256 nlct--; 257 } 258 fputc(ch, stdout); 259 break; 260 } 261 } done:; 262 263 fclose(fp); 264 265 skip_text: 266 267 fputs(END_SET_TEXT, stdout); 268 } 269 #endif 270 271 /** 272 * The purpose of this function is to assign "long usage", short usage 273 * and version information to a shell variable. Rather than wind our 274 * way through all the logic necessary to emit the text directly, we 275 * fork(), have our child process emit the text the normal way and 276 * capture the output in the parent process. 277 * 278 * @param[in] opts the program options 279 * @param[in] which what to print: long usage, usage or version 280 * @param[in] od for TT_VERSION, it is the version option 281 */ 282 static void 283 text_to_var(tOptions * opts, teTextTo which, tOptDesc * od) 284 { 285 # define _TT_(n) static char const z ## n [] = #n; 286 TEXTTO_TABLE 287 # undef _TT_ 288 # define _TT_(n) z ## n , 289 static char const * ttnames[] = { TEXTTO_TABLE }; 290 # undef _TT_ 291 292 #if ! defined(HAVE_WORKING_FORK) 293 printf(SET_NO_TEXT_FMT, opts->pzPROGNAME, ttnames[which]); 294 #else 295 int fdpair[2]; 296 297 fflush(stdout); 298 fflush(stderr); 299 300 if (pipe(fdpair) != 0) 301 fserr_exit(opts->pzProgName, "pipe", zinter_proc_pipe); 302 303 switch (fork()) { 304 case -1: 305 fserr_exit(opts->pzProgName, "fork", opts->pzProgName); 306 /* NOTREACHED */ 307 308 case 0: 309 /* 310 * Send both stderr and stdout to the pipe. No matter which 311 * descriptor is used, we capture the output on the read end. 312 */ 313 dup2(fdpair[1], STDERR_FILENO); 314 dup2(fdpair[1], STDOUT_FILENO); 315 close(fdpair[0]); 316 317 switch (which) { 318 case TT_LONGUSAGE: 319 (*(opts->pUsageProc))(opts, EXIT_SUCCESS); 320 /* NOTREACHED */ 321 322 case TT_USAGE: 323 (*(opts->pUsageProc))(opts, EXIT_FAILURE); 324 /* NOTREACHED */ 325 326 case TT_VERSION: 327 if (od->fOptState & OPTST_ALLOC_ARG) { 328 AGFREE(od->optArg.argString); 329 od->fOptState &= ~OPTST_ALLOC_ARG; 330 } 331 od->optArg.argString = "c"; 332 optionPrintVersion(opts, od); 333 /* NOTREACHED */ 334 335 default: 336 option_exits(EXIT_FAILURE); 337 /* NOTREACHED */ 338 } 339 /* NOTREACHED */ 340 341 default: 342 close(fdpair[1]); 343 } 344 345 emit_var_text(opts->pzPROGNAME, ttnames[which], fdpair[0]); 346 #endif 347 } 348 349 /** 350 * capture usage text in shell variables. 351 * 352 */ 353 static void 354 emit_usage(tOptions * opts) 355 { 356 char tm_nm_buf[AO_NAME_SIZE]; 357 358 /* 359 * First, switch stdout to the output file name. 360 * Then, change the program name to the one defined 361 * by the definitions (rather than the current 362 * executable name). Down case the upper cased name. 363 */ 364 if (script_leader != NULL) 365 fputs(script_leader, stdout); 366 367 { 368 char const * out_nm; 369 370 { 371 time_t c_tim = time(NULL); 372 struct tm * ptm = localtime(&c_tim); 373 strftime(tm_nm_buf, AO_NAME_SIZE, TIME_FMT, ptm ); 374 } 375 376 if (HAVE_GENSHELL_OPT(SCRIPT)) 377 out_nm = GENSHELL_OPT_ARG(SCRIPT); 378 else out_nm = STDOUT; 379 380 if ((script_leader == NULL) && (shell_prog != NULL)) 381 printf(SHELL_MAGIC, shell_prog); 382 383 printf(PREAMBLE_FMT, START_MARK, out_nm, tm_nm_buf); 384 } 385 386 printf(END_PRE_FMT, opts->pzPROGNAME); 387 388 /* 389 * Get a copy of the original program name in lower case and 390 * fill in an approximation of the program name from it. 391 */ 392 { 393 char * pzPN = tm_nm_buf; 394 char const * pz = opts->pzPROGNAME; 395 char ** pp; 396 397 /* Copy the program name into the time/name buffer */ 398 for (;;) { 399 if ((*pzPN++ = (char)tolower((unsigned char)*pz++)) == NUL) 400 break; 401 } 402 403 pp = (char **)(void *)(intptr_t)&(opts->pzProgPath); 404 *pp = tm_nm_buf; 405 pp = (char **)(void *)(intptr_t)&(opts->pzProgName); 406 *pp = tm_nm_buf; 407 } 408 409 text_to_var(opts, TT_LONGUSAGE, NULL); 410 text_to_var(opts, TT_USAGE, NULL); 411 412 { 413 tOptDesc* pOptDesc = opts->pOptDesc; 414 int optionCt = opts->optCt; 415 416 for (;;) { 417 if (pOptDesc->pOptProc == optionPrintVersion) { 418 text_to_var(opts, TT_VERSION, pOptDesc); 419 break; 420 } 421 422 if (--optionCt <= 0) 423 break; 424 pOptDesc++; 425 } 426 } 427 } 428 429 static void 430 emit_wrapup(tOptions * opts) 431 { 432 tOptDesc * od = opts->pOptDesc; 433 int opt_ct = opts->presetOptCt; 434 char const * fmt; 435 436 printf(FINISH_LOOP, opts->pzPROGNAME); 437 for (;opt_ct > 0; od++, --opt_ct) { 438 /* 439 * Options that are either usage documentation or are compiled out 440 * are not to be processed. 441 */ 442 if (SKIP_OPT(od) || (od->pz_NAME == NULL)) 443 continue; 444 445 /* 446 * do not presence check if there is no minimum/must-set 447 */ 448 if ((od->optMinCt == 0) && ((od->fOptState & OPTST_MUST_SET) == 0)) 449 continue; 450 451 if (od->optMaxCt > 1) 452 fmt = CHK_MIN_COUNT; 453 else fmt = CHK_ONE_REQUIRED; 454 455 { 456 int min = (od->optMinCt == 0) ? 1 : od->optMinCt; 457 printf(fmt, opts->pzPROGNAME, od->pz_NAME, min); 458 } 459 } 460 fputs(END_MARK, stdout); 461 } 462 463 static void 464 emit_setup(tOptions * opts) 465 { 466 tOptDesc * od = opts->pOptDesc; 467 int opt_ct = opts->presetOptCt; 468 char const * fmt; 469 char const * def_val; 470 471 for (;opt_ct > 0; od++, --opt_ct) { 472 char int_val_buf[32]; 473 474 /* 475 * Options that are either usage documentation or are compiled out 476 * are not to be processed. 477 */ 478 if (SKIP_OPT(od) || (od->pz_NAME == NULL)) 479 continue; 480 481 if (od->optMaxCt > 1) 482 fmt = MULTI_DEF_FMT; 483 else fmt = SGL_DEF_FMT; 484 485 /* 486 * IF this is an enumeration/bitmask option, then convert the value 487 * to a string before printing the default value. 488 */ 489 switch (OPTST_GET_ARGTYPE(od->fOptState)) { 490 case OPARG_TYPE_ENUMERATION: 491 (*(od->pOptProc))(OPTPROC_EMIT_SHELL, od ); 492 def_val = od->optArg.argString; 493 break; 494 495 /* 496 * Numeric and membership bit options are just printed as a number. 497 */ 498 case OPARG_TYPE_NUMERIC: 499 snprintf(int_val_buf, sizeof(int_val_buf), "%d", 500 (int)od->optArg.argInt); 501 def_val = int_val_buf; 502 break; 503 504 case OPARG_TYPE_MEMBERSHIP: 505 snprintf(int_val_buf, sizeof(int_val_buf), "%lu", 506 (unsigned long)od->optArg.argIntptr); 507 def_val = int_val_buf; 508 break; 509 510 case OPARG_TYPE_BOOLEAN: 511 def_val = (od->optArg.argBool) ? TRUE_STR : FALSE_STR; 512 break; 513 514 default: 515 if (od->optArg.argString == NULL) { 516 if (fmt == SGL_DEF_FMT) 517 fmt = SGL_NO_DEF_FMT; 518 def_val = NULL; 519 } 520 else 521 def_val = od->optArg.argString; 522 } 523 524 printf(fmt, opts->pzPROGNAME, od->pz_NAME, def_val); 525 } 526 } 527 528 static void 529 emit_action(tOptions * opts, tOptDesc * od) 530 { 531 if (od->pOptProc == optionPrintVersion) 532 printf(ECHO_N_EXIT, opts->pzPROGNAME, VER_STR); 533 534 else if (od->pOptProc == optionPagedUsage) 535 printf(PAGE_USAGE_TEXT, opts->pzPROGNAME); 536 537 else if (od->pOptProc == optionLoadOpt) { 538 printf(LVL3_CMD, NO_LOAD_WARN); 539 printf(LVL3_CMD, YES_NEED_OPT_ARG); 540 541 } else if (od->pz_NAME == NULL) { 542 543 if (od->pOptProc == NULL) { 544 printf(LVL3_CMD, NO_SAVE_OPTS); 545 printf(LVL3_CMD, OK_NEED_OPT_ARG); 546 } else 547 printf(ECHO_N_EXIT, opts->pzPROGNAME, LONG_USE_STR); 548 549 } else { 550 if (od->optMaxCt == 1) 551 printf(SGL_ARG_FMT, opts->pzPROGNAME, od->pz_NAME); 552 else { 553 if ((unsigned)od->optMaxCt < NOLIMIT) 554 printf(CHK_MAX_COUNT, opts->pzPROGNAME, 555 od->pz_NAME, od->optMaxCt); 556 557 printf(MULTI_ARG_FMT, opts->pzPROGNAME, od->pz_NAME); 558 } 559 560 /* 561 * Fix up the args. 562 */ 563 if (OPTST_GET_ARGTYPE(od->fOptState) == OPARG_TYPE_NONE) { 564 printf(SET_MULTI_ARG, opts->pzPROGNAME, od->pz_NAME); 565 printf(LVL3_CMD, NO_ARG_NEEDED); 566 567 } else if (od->fOptState & OPTST_ARG_OPTIONAL) { 568 printf(SET_MULTI_ARG, opts->pzPROGNAME, od->pz_NAME); 569 printf(LVL3_CMD, OK_NEED_OPT_ARG); 570 571 } else { 572 printf(LVL3_CMD, YES_NEED_OPT_ARG); 573 } 574 } 575 fputs(zOptionEndSelect, stdout); 576 } 577 578 static void 579 emit_inaction(tOptions * opts, tOptDesc * od) 580 { 581 if (od->pOptProc == optionLoadOpt) { 582 printf(LVL3_CMD, NO_SUPPRESS_LOAD); 583 584 } else if (od->optMaxCt == 1) 585 printf(NO_SGL_ARG_FMT, opts->pzPROGNAME, 586 od->pz_NAME, od->pz_DisablePfx); 587 else 588 printf(NO_MULTI_ARG_FMT, opts->pzPROGNAME, 589 od->pz_NAME, od->pz_DisablePfx); 590 591 printf(LVL3_CMD, NO_ARG_NEEDED); 592 fputs(zOptionEndSelect, stdout); 593 } 594 595 /** 596 * recognize flag options. These go at the end. 597 * At the end, emit code to handle options we don't recognize. 598 * 599 * @param[in] opts the program options 600 */ 601 static void 602 emit_flag(tOptions * opts) 603 { 604 tOptDesc* od = opts->pOptDesc; 605 int opt_ct = opts->optCt; 606 607 fputs(zOptionCase, stdout); 608 609 for (;opt_ct > 0; od++, --opt_ct) { 610 611 if (SKIP_OPT(od) || ! IS_GRAPHIC_CHAR(od->optValue)) 612 continue; 613 614 printf(zOptionFlag, od->optValue); 615 emit_action(opts, od); 616 } 617 printf(UNK_OPT_FMT, FLAG_STR, opts->pzPROGNAME); 618 } 619 620 /** 621 * Emit the match text for a long option. The passed in \a name may be 622 * either the enablement name or the disablement name. 623 * 624 * @param[in] name The current name to check. 625 * @param[in] cod current option descriptor 626 * @param[in] opts the program options 627 */ 628 static void 629 emit_match_expr(char const * name, tOptDesc * cod, tOptions * opts) 630 { 631 char name_bf[32]; 632 unsigned int min_match_ct = 2; 633 unsigned int max_match_ct = strlen(name) - 1; 634 635 if (max_match_ct >= sizeof(name_bf) - 1) 636 goto leave; 637 638 { 639 tOptDesc * od = opts->pOptDesc; 640 int ct = opts->optCt; 641 642 for (; ct-- > 0; od++) { 643 unsigned int match_ct = 0; 644 645 /* 646 * Omit the current option, Doc opts and compiled out opts. 647 */ 648 if ((od == cod) || SKIP_OPT(od)) 649 continue; 650 651 /* 652 * Check each character of the name case insensitively. 653 * They must not be the same. They cannot be, because it would 654 * not compile correctly if they were. 655 */ 656 while (toupper((unsigned char)od->pz_Name[match_ct]) == toupper((unsigned char)name[match_ct])) 657 match_ct++; 658 659 if (match_ct > min_match_ct) 660 min_match_ct = match_ct; 661 662 /* 663 * Check the disablement name, too. 664 */ 665 if (od->pz_DisableName == NULL) 666 continue; 667 668 match_ct = 0; 669 while ( toupper((unsigned char)od->pz_DisableName[match_ct]) 670 == toupper((unsigned char)name[match_ct])) 671 match_ct++; 672 if (match_ct > min_match_ct) 673 min_match_ct = match_ct; 674 } 675 } 676 677 /* 678 * Don't bother emitting partial matches if there is only one possible 679 * partial match. 680 */ 681 if (min_match_ct < max_match_ct) { 682 char * pz = name_bf + min_match_ct; 683 int nm_ix = min_match_ct; 684 685 memcpy(name_bf, name, min_match_ct); 686 687 for (;;) { 688 *pz = NUL; 689 printf(zOptionPartName, name_bf); 690 *pz++ = name[nm_ix++]; 691 if (name[nm_ix] == NUL) { 692 *pz = NUL; 693 break; 694 } 695 } 696 } 697 698 leave: 699 printf(zOptionFullName, name); 700 } 701 702 /** 703 * Emit GNU-standard long option handling code. 704 * 705 * @param[in] opts the program options 706 */ 707 static void 708 emit_long(tOptions * opts) 709 { 710 tOptDesc * od = opts->pOptDesc; 711 int ct = opts->optCt; 712 713 fputs(zOptionCase, stdout); 714 715 /* 716 * do each option, ... 717 */ 718 do { 719 /* 720 * Documentation & compiled-out options 721 */ 722 if (SKIP_OPT(od)) 723 continue; 724 725 emit_match_expr(od->pz_Name, od, opts); 726 emit_action(opts, od); 727 728 /* 729 * Now, do the same thing for the disablement version of the option. 730 */ 731 if (od->pz_DisableName != NULL) { 732 emit_match_expr(od->pz_DisableName, od, opts); 733 emit_inaction(opts, od); 734 } 735 } while (od++, --ct > 0); 736 737 printf(UNK_OPT_FMT, OPTION_STR, opts->pzPROGNAME); 738 } 739 740 /** 741 * Load the previous shell script output file. We need to preserve any 742 * hand-edited additions outside of the START_MARK and END_MARKs. 743 * 744 * @param[in] fname the output file name 745 */ 746 static char * 747 load_old_output(char const * fname, char const * pname) 748 { 749 /* 750 * IF we cannot stat the file, 751 * THEN assume we are creating a new file. 752 * Skip the loading of the old data. 753 */ 754 FILE * fp = fopen(fname, "r" FOPEN_BINARY_FLAG); 755 struct stat stbf; 756 char * text; 757 char * scan; 758 759 if (fp == NULL) 760 return NULL; 761 762 /* 763 * If we opened it, we should be able to stat it and it needs 764 * to be a regular file 765 */ 766 if ((fstat(fileno(fp), &stbf) != 0) || (! S_ISREG(stbf.st_mode))) 767 fserr_exit(pname, "fstat", fname); 768 769 scan = text = AGALOC(stbf.st_size + 1, "f data"); 770 771 /* 772 * Read in all the data as fast as our OS will let us. 773 */ 774 for (;;) { 775 size_t inct = fread((void*)scan, 1, (size_t)stbf.st_size, fp); 776 if (inct == 0) 777 break; 778 779 stbf.st_size -= (ssize_t)inct; 780 781 if (stbf.st_size == 0) 782 break; 783 784 scan += inct; 785 } 786 787 *scan = NUL; 788 fclose(fp); 789 790 return text; 791 } 792 793 /** 794 * Open the specified output file. If it already exists, load its 795 * contents and save the non-generated (hand edited) portions. 796 * If a "start mark" is found, everything before it is preserved leader. 797 * If not, the entire thing is a trailer. Assuming the start is found, 798 * then everything after the end marker is the trailer. If the end 799 * mark is not found, the file is actually corrupt, but we take the 800 * remainder to be the trailer. 801 * 802 * @param[in] fname the output file name 803 */ 804 static void 805 open_out(char const * fname, char const * pname) 806 { 807 808 do { 809 char * txt = script_text = load_old_output(fname, pname); 810 char * scn; 811 812 if (txt == NULL) 813 break; 814 815 scn = strstr(txt, START_MARK); 816 if (scn == NULL) { 817 script_trailer = txt; 818 break; 819 } 820 821 *(scn++) = NUL; 822 scn = strstr(scn, END_MARK); 823 if (scn == NULL) { 824 /* 825 * The file is corrupt. Set the trailer to be everything 826 * after the start mark. The user will need to fix it up. 827 */ 828 script_trailer = txt + strlen(txt) + START_MARK_LEN + 1; 829 break; 830 } 831 832 /* 833 * Check to see if the data contains our marker. 834 * If it does, then we will skip over it 835 */ 836 script_trailer = scn + END_MARK_LEN; 837 script_leader = txt; 838 } while (false); 839 840 if (freopen(fname, "w" FOPEN_BINARY_FLAG, stdout) != stdout) 841 fserr_exit(pname, "freopen", fname); 842 } 843 844 /*=export_func genshelloptUsage 845 * private: 846 * what: The usage function for the genshellopt generated program 847 * 848 * arg: + tOptions* + opts + program options descriptor + 849 * arg: + int + exit_cd + usage text type to produce + 850 * 851 * doc: 852 * This function is used to create the usage strings for the option 853 * processing shell script code. Two child processes are spawned 854 * each emitting the usage text in either the short (error exit) 855 * style or the long style. The generated program will capture this 856 * and create shell script variables containing the two types of text. 857 =*/ 858 void 859 genshelloptUsage(tOptions * opts, int exit_cd) 860 { 861 #if ! defined(HAVE_WORKING_FORK) 862 optionUsage(opts, exit_cd); 863 #else 864 /* 865 * IF not EXIT_SUCCESS, 866 * THEN emit the short form of usage. 867 */ 868 if (exit_cd != EXIT_SUCCESS) 869 optionUsage(opts, exit_cd); 870 fflush(stderr); 871 fflush(stdout); 872 if (ferror(stdout) || ferror(stderr)) 873 option_exits(EXIT_FAILURE); 874 875 option_usage_fp = stdout; 876 877 /* 878 * First, print our usage 879 */ 880 switch (fork()) { 881 case -1: 882 optionUsage(opts, EXIT_FAILURE); 883 /* NOTREACHED */ 884 885 case 0: 886 pagerState = PAGER_STATE_CHILD; 887 optionUsage(opts, EXIT_SUCCESS); 888 /* NOTREACHED */ 889 _exit(EXIT_FAILURE); 890 891 default: 892 { 893 int sts; 894 wait(&sts); 895 } 896 } 897 898 /* 899 * Generate the pzProgName, since optionProcess() normally 900 * gets it from the command line 901 */ 902 { 903 char * pz; 904 char ** pp = (char **)(void *)(intptr_t)&(optionParseShellOptions->pzProgName); 905 AGDUPSTR(pz, optionParseShellOptions->pzPROGNAME, "prog name"); 906 *pp = pz; 907 while (*pz != NUL) { 908 *pz = (char)tolower((unsigned char)*pz); 909 pz++; 910 } 911 } 912 913 /* 914 * Separate the makeshell usage from the client usage 915 */ 916 fprintf(option_usage_fp, zGenshell, optionParseShellOptions->pzProgName); 917 fflush(option_usage_fp); 918 919 /* 920 * Now, print the client usage. 921 */ 922 switch (fork()) { 923 case 0: 924 pagerState = PAGER_STATE_CHILD; 925 /*FALLTHROUGH*/ 926 case -1: 927 optionUsage(optionParseShellOptions, EXIT_FAILURE); 928 929 default: 930 { 931 int sts; 932 wait(&sts); 933 } 934 } 935 936 fflush(stdout); 937 if (ferror(stdout)) 938 fserr_exit(opts->pzProgName, zwriting, zstdout_name); 939 940 option_exits(EXIT_SUCCESS); 941 #endif 942 } 943 944 /** @} 945 * 946 * Local Variables: 947 * mode: C 948 * c-file-style: "stroustrup" 949 * indent-tabs-mode: nil 950 * End: 951 * end of autoopts/makeshell.c */ 952