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