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