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