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
to_uchar(char ch)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
option_exits(int exit_code)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
ao_bug(char const * msg)47 ao_bug(char const * msg)
48 {
49 fprintf(stderr, zao_bug_msg, msg);
50 option_exits(EX_SOFTWARE);
51 }
52
53 static void
fserr_warn(char const * prog,char const * op,char const * fname)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
fserr_exit(char const * prog,char const * op,char const * fname)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
optionParseShell(tOptions * opts)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
emit_var_text(char const * prog,char const * var,int fdin)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
text_to_var(tOptions * opts,teTextTo which,tOptDesc * od)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
emit_usage(tOptions * opts)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
emit_wrapup(tOptions * opts)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
emit_setup(tOptions * opts)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
emit_action(tOptions * opts,tOptDesc * od)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
emit_inaction(tOptions * opts,tOptDesc * od)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
emit_flag(tOptions * opts)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
emit_match_expr(char const * name,tOptDesc * cod,tOptions * opts)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
emit_long(tOptions * opts)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 *
load_old_output(char const * fname,char const * pname)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
open_out(char const * fname,char const * pname)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
genshelloptUsage(tOptions * opts,int exit_cd)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