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