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