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