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
to_uchar(char ch)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 = = = */
41 static void
42 emit_var_text(char const * prog, char const * var, int fdin);
43
44 static void
45 text_to_var(tOptions * opts, teTextTo which, tOptDesc * od);
46
47 static void
48 emit_usage(tOptions * opts);
49
50 static void
51 emit_wrapup(tOptions * opts);
52
53 static void
54 emit_setup(tOptions * opts);
55
56 static void
57 emit_action(tOptions * opts, tOptDesc * od);
58
59 static void
60 emit_inaction(tOptions * opts, tOptDesc * od);
61
62 static void
63 emit_flag(tOptions * opts);
64
65 static void
66 emit_match_expr(char const * name, tOptDesc * cod, tOptions * opts);
67
68 static void
69 emit_long(tOptions * opts);
70
71 static char *
72 load_old_output(char const * fname, char const * pname);
73
74 static void
75 open_out(char const * fname, char const * pname);
76 /* = = = END-STATIC-FORWARD = = = */
77
78 LOCAL noreturn void
option_exits(int exit_code)79 option_exits(int exit_code)
80 {
81 if (print_exit)
82 printf("\nexit %d\n", exit_code);
83 exit(exit_code);
84 }
85
86 LOCAL noreturn void
ao_bug(char const * msg)87 ao_bug(char const * msg)
88 {
89 fprintf(stderr, zao_bug_msg, msg);
90 option_exits(EX_SOFTWARE);
91 }
92
93 LOCAL void
fserr_warn(char const * prog,char const * op,char const * fname)94 fserr_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
100 LOCAL noreturn void
fserr_exit(char const * prog,char const * op,char const * fname)101 fserr_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 =*/
116 void
optionParseShell(tOptions * opts)117 optionParseShell(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 */
231 static void
emit_var_text(char const * prog,char const * var,int fdin)232 emit_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 */
289 static void
text_to_var(tOptions * opts,teTextTo which,tOptDesc * od)290 text_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 */
360 static void
emit_usage(tOptions * opts)361 emit_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
436 static void
emit_wrapup(tOptions * opts)437 emit_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
470 static void
emit_setup(tOptions * opts)471 emit_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
535 static void
emit_action(tOptions * opts,tOptDesc * od)536 emit_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
585 static void
emit_inaction(tOptions * opts,tOptDesc * od)586 emit_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 */
608 static void
emit_flag(tOptions * opts)609 emit_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 */
635 static void
emit_match_expr(char const * name,tOptDesc * cod,tOptions * opts)636 emit_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
705 leave:
706 printf(zOptionFullName, name);
707 }
708
709 /**
710 * Emit GNU-standard long option handling code.
711 *
712 * @param[in] opts the program options
713 */
714 static void
emit_long(tOptions * opts)715 emit_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 */
753 static char *
load_old_output(char const * fname,char const * pname)754 load_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 */
811 static void
open_out(char const * fname,char const * pname)812 open_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 =*/
865 void
genshelloptUsage(tOptions * opts,int exit_cd)866 genshelloptUsage(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