corrected call to gnutls_error_is_fatal().
[gnutls.git] / src / libopts / makeshell.c
bloba2b0a2e1013281b85c849d5f2e01bd33160a2f3b
2 /**
3 * \file makeshell.c
5 * Time-stamp: "2012-01-29 19:01:07 bkorb"
7 * This module will interpret the options set in the tOptions
8 * structure and create a Bourne shell script capable of parsing them.
10 * This file is part of AutoOpts, a companion to AutoGen.
11 * AutoOpts is free software.
12 * AutoOpts is Copyright (c) 1992-2012 by Bruce Korb - all rights reserved
14 * AutoOpts is available under any one of two licenses. The license
15 * in use must be one of these two and the choice is under the control
16 * of the user of the license.
18 * The GNU Lesser General Public License, version 3 or later
19 * See the files "COPYING.lgplv3" and "COPYING.gplv3"
21 * The Modified Berkeley Software Distribution License
22 * See the file "COPYING.mbsd"
24 * These files have the following md5sums:
26 * 43b91e8ca915626ed3818ffb1b71248b pkg/libopts/COPYING.gplv3
27 * 06a1a2e4760c90ea5e1dad8dfaac4d39 pkg/libopts/COPYING.lgplv3
28 * 66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd
31 tOptions * optionParseShellOptions = NULL;
33 static char const * shell_prog = NULL;
34 static char * script_leader = NULL;
35 static char * script_trailer = NULL;
37 /* = = = START-STATIC-FORWARD = = = */
38 static void
39 emit_var_text(char const * prog, char const * var, int fdin);
41 static void
42 text_to_var(tOptions * pOpts, teTextTo whichVar, tOptDesc * pOD);
44 static void
45 emit_usage(tOptions * pOpts);
47 static void
48 emit_setup(tOptions * pOpts);
50 static void
51 emit_action(tOptions * pOpts, tOptDesc* pOptDesc);
53 static void
54 emit_inaction(tOptions * pOpts, tOptDesc* pOptDesc);
56 static void
57 emit_flag(tOptions * pOpts);
59 static void
60 emit_match_expr(char const * pzMatchName, tOptDesc* pCurOpt, tOptions* pOpts);
62 static void
63 emitLong(tOptions * pOpts);
65 static void
66 open_out(char const * pzFile);
67 /* = = = END-STATIC-FORWARD = = = */
69 /*=export_func optionParseShell
70 * private:
72 * what: Decipher a boolean value
73 * arg: + tOptions* + pOpts + program options descriptor +
75 * doc:
76 * Emit a shell script that will parse the command line options.
77 =*/
78 void
79 optionParseShell(tOptions * pOpts)
82 * Check for our SHELL option now.
83 * IF the output file contains the "#!" magic marker,
84 * it will override anything we do here.
86 if (HAVE_GENSHELL_OPT(SHELL))
87 shell_prog = GENSHELL_OPT_ARG(SHELL);
89 else if (! ENABLED_GENSHELL_OPT(SHELL))
90 shell_prog = NULL;
92 else if ((shell_prog = getenv("SHELL")),
93 shell_prog == NULL)
95 shell_prog = POSIX_SHELL;
98 * Check for a specified output file
100 if (HAVE_GENSHELL_OPT(SCRIPT))
101 open_out(GENSHELL_OPT_ARG(SCRIPT));
103 emit_usage(pOpts);
104 emit_setup(pOpts);
107 * There are four modes of option processing.
109 switch (pOpts->fOptSet & (OPTPROC_LONGOPT|OPTPROC_SHORTOPT)) {
110 case OPTPROC_LONGOPT:
111 fputs(LOOP_STR, stdout);
113 fputs(LONG_OPT_MARK, stdout);
114 fputs(INIT_LOPT_STR, stdout);
115 emitLong(pOpts);
116 printf(LOPT_ARG_FMT, pOpts->pzPROGNAME);
117 fputs(END_OPT_SEL_STR, stdout);
119 fputs(NOT_FOUND_STR, stdout);
120 break;
122 case 0:
123 fputs(ONLY_OPTS_LOOP, stdout);
124 fputs(INIT_LOPT_STR, stdout);
125 emitLong(pOpts);
126 printf(LOPT_ARG_FMT, pOpts->pzPROGNAME);
127 break;
129 case OPTPROC_SHORTOPT:
130 fputs(LOOP_STR, stdout);
132 fputs(FLAG_OPT_MARK, stdout);
133 fputs(INIT_OPT_STR, stdout);
134 emit_flag(pOpts);
135 printf(OPT_ARG_FMT, pOpts->pzPROGNAME);
136 fputs(END_OPT_SEL_STR, stdout);
138 fputs(NOT_FOUND_STR, stdout);
139 break;
141 case OPTPROC_LONGOPT|OPTPROC_SHORTOPT:
142 fputs(LOOP_STR, stdout);
144 fputs(LONG_OPT_MARK, stdout);
145 fputs(INIT_LOPT_STR, stdout);
146 emitLong(pOpts);
147 printf(LOPT_ARG_FMT, pOpts->pzPROGNAME);
148 fputs(END_OPT_SEL_STR, stdout);
150 fputs(FLAG_OPT_MARK, stdout);
151 fputs(INIT_OPT_STR, stdout);
152 emit_flag(pOpts);
153 printf(OPT_ARG_FMT, pOpts->pzPROGNAME);
154 fputs(END_OPT_SEL_STR, stdout);
156 fputs(NOT_FOUND_STR, stdout);
157 break;
160 printf(zLoopEnd, pOpts->pzPROGNAME, END_MARK);
161 if ((script_trailer != NULL) && (*script_trailer != NUL))
162 fputs(script_trailer, stdout);
163 else if (ENABLED_GENSHELL_OPT(SHELL))
164 printf(SHOW_PROG_ENV, pOpts->pzPROGNAME);
166 fflush(stdout);
167 #ifdef HAVE_FCHMOD
168 fchmod(STDOUT_FILENO, 0755);
169 #endif
170 fclose(stdout);
171 if (ferror(stdout)) {
172 fputs(zOutputFail, stderr);
173 exit(EXIT_FAILURE);
177 #ifdef HAVE_WORKING_FORK
178 static void
179 emit_var_text(char const * prog, char const * var, int fdin)
181 FILE * fp = fdopen(fdin, "r" FOPEN_BINARY_FLAG);
182 int nlct = 0; /* defer newlines and skip trailing ones */
184 printf(SET_TEXT_FMT, prog, var);
185 if (fp == NULL)
186 goto skip_text;
188 for (;;) {
189 int ch = fgetc(fp);
190 switch (ch) {
192 case NL:
193 nlct++;
194 break;
196 case '\'':
197 while (nlct > 0) {
198 fputc(NL, stdout);
199 nlct--;
201 fputs(apostrophy, stdout);
202 break;
204 case EOF:
205 goto endCharLoop;
207 default:
208 while (nlct > 0) {
209 fputc(NL, stdout);
210 nlct--;
212 fputc(ch, stdout);
213 break;
215 } endCharLoop:;
217 fclose(fp);
219 skip_text:
221 fputs(END_SET_TEXT, stdout);
224 #endif
227 * The purpose of this function is to assign "long usage", short usage
228 * and version information to a shell variable. Rather than wind our
229 * way through all the logic necessary to emit the text directly, we
230 * fork(), have our child process emit the text the normal way and
231 * capture the output in the parent process.
233 static void
234 text_to_var(tOptions * pOpts, teTextTo whichVar, tOptDesc * pOD)
236 # define _TT_(n) static char const z ## n [] = #n;
237 TEXTTO_TABLE
238 # undef _TT_
239 # define _TT_(n) z ## n ,
240 static char const * apzTTNames[] = { TEXTTO_TABLE };
241 # undef _TT_
243 #if ! defined(HAVE_WORKING_FORK)
244 printf(SET_NO_TEXT_FMT, pOpts->pzPROGNAME, apzTTNames[ whichVar]);
245 #else
246 int pipeFd[2];
248 fflush(stdout);
249 fflush(stderr);
251 if (pipe(pipeFd) != 0) {
252 fprintf(stderr, zBadPipe, errno, strerror(errno));
253 exit(EXIT_FAILURE);
256 switch (fork()) {
257 case -1:
258 fprintf(stderr, zForkFail, errno, strerror(errno), pOpts->pzProgName);
259 exit(EXIT_FAILURE);
260 break;
262 case 0:
264 * Send both stderr and stdout to the pipe. No matter which
265 * descriptor is used, we capture the output on the read end.
267 dup2(pipeFd[1], STDERR_FILENO);
268 dup2(pipeFd[1], STDOUT_FILENO);
269 close(pipeFd[0]);
271 switch (whichVar) {
272 case TT_LONGUSAGE:
273 (*(pOpts->pUsageProc))(pOpts, EXIT_SUCCESS);
274 /* NOTREACHED */
276 case TT_USAGE:
277 (*(pOpts->pUsageProc))(pOpts, EXIT_FAILURE);
278 /* NOTREACHED */
280 case TT_VERSION:
281 if (pOD->fOptState & OPTST_ALLOC_ARG) {
282 AGFREE(pOD->optArg.argString);
283 pOD->fOptState &= ~OPTST_ALLOC_ARG;
285 pOD->optArg.argString = "c";
286 optionPrintVersion(pOpts, pOD);
287 /* NOTREACHED */
289 default:
290 exit(EXIT_FAILURE);
293 default:
294 close(pipeFd[1]);
297 emit_var_text(pOpts->pzPROGNAME, apzTTNames[whichVar], pipeFd[0]);
298 #endif
302 static void
303 emit_usage(tOptions * pOpts)
305 char zTimeBuf[AO_NAME_SIZE];
308 * First, switch stdout to the output file name.
309 * Then, change the program name to the one defined
310 * by the definitions (rather than the current
311 * executable name). Down case the upper cased name.
313 if (script_leader != NULL)
314 fputs(script_leader, stdout);
317 char const * out_nm;
320 time_t c_tim = time(NULL);
321 struct tm * ptm = localtime(&c_tim);
322 strftime(zTimeBuf, AO_NAME_SIZE, TIME_FMT, ptm );
325 if (HAVE_GENSHELL_OPT(SCRIPT))
326 out_nm = GENSHELL_OPT_ARG(SCRIPT);
327 else out_nm = STDOUT;
329 if ((script_leader == NULL) && (shell_prog != NULL))
330 printf(SHELL_MAGIC, shell_prog);
332 printf(PREAMBLE_FMT, START_MARK, out_nm, zTimeBuf);
335 printf(END_PRE_FMT, pOpts->pzPROGNAME);
338 * Get a copy of the original program name in lower case and
339 * fill in an approximation of the program name from it.
342 char * pzPN = zTimeBuf;
343 char const * pz = pOpts->pzPROGNAME;
344 char ** pp;
346 for (;;) {
347 if ((*pzPN++ = (char)tolower(*pz++)) == NUL)
348 break;
351 pp = (char **)(void *)&(pOpts->pzProgPath);
352 *pp = zTimeBuf;
353 pp = (char **)(void *)&(pOpts->pzProgName);
354 *pp = zTimeBuf;
357 text_to_var(pOpts, TT_LONGUSAGE, NULL);
358 text_to_var(pOpts, TT_USAGE, NULL);
361 tOptDesc* pOptDesc = pOpts->pOptDesc;
362 int optionCt = pOpts->optCt;
364 for (;;) {
365 if (pOptDesc->pOptProc == optionPrintVersion) {
366 text_to_var(pOpts, TT_VERSION, pOptDesc);
367 break;
370 if (--optionCt <= 0)
371 break;
372 pOptDesc++;
378 static void
379 emit_setup(tOptions * pOpts)
381 tOptDesc * pOptDesc = pOpts->pOptDesc;
382 int optionCt = pOpts->presetOptCt;
383 char const * pzFmt;
384 char const * pzDefault;
386 for (;optionCt > 0; pOptDesc++, --optionCt) {
387 char zVal[32];
390 * Options that are either usage documentation or are compiled out
391 * are not to be processed.
393 if (SKIP_OPT(pOptDesc) || (pOptDesc->pz_NAME == NULL))
394 continue;
396 if (pOptDesc->optMaxCt > 1)
397 pzFmt = MULTI_DEF_FMT;
398 else pzFmt = SGL_DEF_FMT;
401 * IF this is an enumeration/bitmask option, then convert the value
402 * to a string before printing the default value.
404 switch (OPTST_GET_ARGTYPE(pOptDesc->fOptState)) {
405 case OPARG_TYPE_ENUMERATION:
406 (*(pOptDesc->pOptProc))(OPTPROC_EMIT_SHELL, pOptDesc );
407 pzDefault = pOptDesc->optArg.argString;
408 break;
411 * Numeric and membership bit options are just printed as a number.
413 case OPARG_TYPE_NUMERIC:
414 snprintf(zVal, sizeof(zVal), "%d",
415 (int)pOptDesc->optArg.argInt);
416 pzDefault = zVal;
417 break;
419 case OPARG_TYPE_MEMBERSHIP:
420 snprintf(zVal, sizeof(zVal), "%lu",
421 (unsigned long)pOptDesc->optArg.argIntptr);
422 pzDefault = zVal;
423 break;
425 case OPARG_TYPE_BOOLEAN:
426 pzDefault = (pOptDesc->optArg.argBool) ? TRUE_STR : FALSE_STR;
427 break;
429 default:
430 if (pOptDesc->optArg.argString == NULL) {
431 if (pzFmt == SGL_DEF_FMT)
432 pzFmt = SGL_NO_DEF_FMT;
433 pzDefault = NULL;
435 else
436 pzDefault = pOptDesc->optArg.argString;
439 printf(pzFmt, pOpts->pzPROGNAME, pOptDesc->pz_NAME, pzDefault);
443 static void
444 emit_action(tOptions * pOpts, tOptDesc* pOptDesc)
446 if (pOptDesc->pOptProc == optionPrintVersion)
447 printf(zTextExit, pOpts->pzPROGNAME, VER_STR);
449 else if (pOptDesc->pOptProc == optionPagedUsage)
450 printf(zPagedUsageExit, pOpts->pzPROGNAME);
452 else if (pOptDesc->pOptProc == optionLoadOpt) {
453 printf(zCmdFmt, NO_LOAD_WARN);
454 printf(zCmdFmt, YES_NEED_OPT_ARG);
456 } else if (pOptDesc->pz_NAME == NULL) {
458 if (pOptDesc->pOptProc == NULL) {
459 printf(zCmdFmt, NO_SAVE_OPTS);
460 printf(zCmdFmt, OK_NEED_OPT_ARG);
461 } else
462 printf(zTextExit, pOpts->pzPROGNAME, LONG_USE_STR);
464 } else {
465 if (pOptDesc->optMaxCt == 1)
466 printf(SGL_ARG_FMT, pOpts->pzPROGNAME, pOptDesc->pz_NAME);
467 else {
468 if ((unsigned)pOptDesc->optMaxCt < NOLIMIT)
469 printf(zCountTest, pOpts->pzPROGNAME,
470 pOptDesc->pz_NAME, pOptDesc->optMaxCt);
472 printf(MULTI_ARG_FMT, pOpts->pzPROGNAME, pOptDesc->pz_NAME);
476 * Fix up the args.
478 if (OPTST_GET_ARGTYPE(pOptDesc->fOptState) == OPARG_TYPE_NONE) {
479 printf(zCantArg, pOpts->pzPROGNAME, pOptDesc->pz_NAME);
481 } else if (pOptDesc->fOptState & OPTST_ARG_OPTIONAL) {
482 printf(zMayArg, pOpts->pzPROGNAME, pOptDesc->pz_NAME);
484 } else {
485 fputs(zMustArg, stdout);
488 fputs(zOptionEndSelect, stdout);
492 static void
493 emit_inaction(tOptions * pOpts, tOptDesc* pOptDesc)
495 if (pOptDesc->pOptProc == optionLoadOpt) {
496 printf(zCmdFmt, NO_SUPPRESS_LOAD);
498 } else if (pOptDesc->optMaxCt == 1)
499 printf(NO_SGL_ARG_FMT, pOpts->pzPROGNAME,
500 pOptDesc->pz_NAME, pOptDesc->pz_DisablePfx);
501 else
502 printf(NO_MULTI_ARG_FMT, pOpts->pzPROGNAME,
503 pOptDesc->pz_NAME, pOptDesc->pz_DisablePfx);
505 printf(zCmdFmt, NO_ARG_NEEDED);
506 fputs(zOptionEndSelect, stdout);
510 static void
511 emit_flag(tOptions * pOpts)
513 tOptDesc* pOptDesc = pOpts->pOptDesc;
514 int optionCt = pOpts->optCt;
516 fputs(zOptionCase, stdout);
518 for (;optionCt > 0; pOptDesc++, --optionCt) {
520 if (SKIP_OPT(pOptDesc))
521 continue;
523 if (IS_GRAPHIC_CHAR(pOptDesc->optValue)) {
524 printf(zOptionFlag, pOptDesc->optValue);
525 emit_action(pOpts, pOptDesc);
528 printf(UNK_OPT_FMT, FLAG_STR, pOpts->pzPROGNAME);
533 * Emit the match text for a long option
535 static void
536 emit_match_expr(char const * pzMatchName, tOptDesc* pCurOpt, tOptions* pOpts)
538 tOptDesc* pOD = pOpts->pOptDesc;
539 int oCt = pOpts->optCt;
540 int min = 1;
541 char zName[ 256 ];
542 char* pz = zName;
544 for (;;) {
545 int matchCt = 0;
548 * Omit the current option, Documentation opts and compiled out opts.
550 if ((pOD == pCurOpt) || SKIP_OPT(pOD)){
551 if (--oCt <= 0)
552 break;
553 pOD++;
554 continue;
558 * Check each character of the name case insensitively.
559 * They must not be the same. They cannot be, because it would
560 * not compile correctly if they were.
562 while ( toupper(pOD->pz_Name[matchCt])
563 == toupper(pzMatchName[matchCt]))
564 matchCt++;
566 if (matchCt > min)
567 min = matchCt;
570 * Check the disablement name, too.
572 if (pOD->pz_DisableName != NULL) {
573 matchCt = 0;
574 while ( toupper(pOD->pz_DisableName[matchCt])
575 == toupper(pzMatchName[matchCt]))
576 matchCt++;
577 if (matchCt > min)
578 min = matchCt;
580 if (--oCt <= 0)
581 break;
582 pOD++;
586 * IF the 'min' is all or one short of the name length,
587 * THEN the entire string must be matched.
589 if ( (pzMatchName[min ] == NUL)
590 || (pzMatchName[min+1] == NUL) )
591 printf(zOptionFullName, pzMatchName);
593 else {
594 int matchCt = 0;
595 for (; matchCt <= min; matchCt++)
596 *pz++ = pzMatchName[matchCt];
598 for (;;) {
599 *pz = NUL;
600 printf(zOptionPartName, zName);
601 *pz++ = pzMatchName[matchCt++];
602 if (pzMatchName[matchCt] == NUL) {
603 *pz = NUL;
604 printf(zOptionFullName, zName);
605 break;
613 * Emit GNU-standard long option handling code
615 static void
616 emitLong(tOptions * pOpts)
618 tOptDesc* pOD = pOpts->pOptDesc;
619 int ct = pOpts->optCt;
621 fputs(zOptionCase, stdout);
624 * do each option, ...
626 do {
628 * Documentation & compiled-out options
630 if (SKIP_OPT(pOD))
631 continue;
633 emit_match_expr(pOD->pz_Name, pOD, pOpts);
634 emit_action(pOpts, pOD);
637 * Now, do the same thing for the disablement version of the option.
639 if (pOD->pz_DisableName != NULL) {
640 emit_match_expr(pOD->pz_DisableName, pOD, pOpts);
641 emit_inaction(pOpts, pOD);
643 } while (pOD++, --ct > 0);
645 printf(UNK_OPT_FMT, OPTION_STR, pOpts->pzPROGNAME);
649 static void
650 open_out(char const * pzFile)
652 FILE* fp;
653 char* pzData = NULL;
654 struct stat stbf;
656 do {
657 char* pzScan;
658 size_t sizeLeft;
661 * IF we cannot stat the file,
662 * THEN assume we are creating a new file.
663 * Skip the loading of the old data.
665 if (stat(pzFile, &stbf) != 0)
666 break;
669 * The file must be a regular file
671 if (! S_ISREG(stbf.st_mode)) {
672 fprintf(stderr, zNotFile, pzFile);
673 exit(EXIT_FAILURE);
676 pzData = AGALOC(stbf.st_size + 1, "f data");
677 fp = fopen(pzFile, "r" FOPEN_BINARY_FLAG);
679 sizeLeft = (unsigned)stbf.st_size;
680 pzScan = pzData;
683 * Read in all the data as fast as our OS will let us.
685 for (;;) {
686 int inct = fread((void*)pzScan, (size_t)1, sizeLeft, fp);
687 if (inct == 0)
688 break;
690 pzScan += inct;
691 sizeLeft -= inct;
693 if (sizeLeft == 0)
694 break;
698 * NUL-terminate the leader and look for the trailer
700 *pzScan = NUL;
701 fclose(fp);
702 pzScan = strstr(pzData, START_MARK);
703 if (pzScan == NULL) {
704 script_trailer = pzData;
705 break;
708 *(pzScan++) = NUL;
709 pzScan = strstr(pzScan, END_MARK);
710 if (pzScan == NULL) {
711 script_trailer = pzData;
712 break;
716 * Check to see if the data contains our marker.
717 * If it does, then we will skip over it
719 script_trailer = pzScan + END_MARK_LEN;
720 script_leader = pzData;
721 } while (AG_FALSE);
723 if (freopen(pzFile, "w" FOPEN_BINARY_FLAG, stdout) != stdout) {
724 fprintf(stderr, zFreopenFail, errno, strerror(errno));
725 exit(EXIT_FAILURE);
730 /*=export_func genshelloptUsage
731 * private:
732 * what: The usage function for the genshellopt generated program
734 * arg: + tOptions* + pOpts + program options descriptor +
735 * arg: + int + exitCode + usage text type to produce +
737 * doc:
738 * This function is used to create the usage strings for the option
739 * processing shell script code. Two child processes are spawned
740 * each emitting the usage text in either the short (error exit)
741 * style or the long style. The generated program will capture this
742 * and create shell script variables containing the two types of text.
744 void
745 genshelloptUsage(tOptions * pOpts, int exitCode)
747 #if ! defined(HAVE_WORKING_FORK)
748 optionUsage(pOpts, exitCode);
749 #else
751 * IF not EXIT_SUCCESS,
752 * THEN emit the short form of usage.
754 if (exitCode != EXIT_SUCCESS)
755 optionUsage(pOpts, exitCode);
756 fflush(stderr);
757 fflush(stdout);
758 if (ferror(stdout) || ferror(stderr))
759 exit(EXIT_FAILURE);
761 option_usage_fp = stdout;
764 * First, print our usage
766 switch (fork()) {
767 case -1:
768 optionUsage(pOpts, EXIT_FAILURE);
769 /* NOTREACHED */
771 case 0:
772 pagerState = PAGER_STATE_CHILD;
773 optionUsage(pOpts, EXIT_SUCCESS);
774 /* NOTREACHED */
775 _exit(EXIT_FAILURE);
777 default:
779 int sts;
780 wait(&sts);
785 * Generate the pzProgName, since optionProcess() normally
786 * gets it from the command line
789 char * pz;
790 char ** pp = (char **)(void *)&(optionParseShellOptions->pzProgName);
791 AGDUPSTR(pz, optionParseShellOptions->pzPROGNAME, "prog name");
792 *pp = pz;
793 while (*pz != NUL) {
794 *pz = tolower(*pz);
795 pz++;
800 * Separate the makeshell usage from the client usage
802 fprintf(option_usage_fp, zGenshell, optionParseShellOptions->pzProgName);
803 fflush(option_usage_fp);
806 * Now, print the client usage.
808 switch (fork()) {
809 case 0:
810 pagerState = PAGER_STATE_CHILD;
811 /*FALLTHROUGH*/
812 case -1:
813 optionUsage(optionParseShellOptions, EXIT_FAILURE);
815 default:
817 int sts;
818 wait(&sts);
822 fflush(stdout);
823 if (ferror(stdout)) {
824 fputs(zOutputFail, stderr);
825 exit(EXIT_FAILURE);
828 exit(EXIT_SUCCESS);
829 #endif
833 * Local Variables:
834 * mode: C
835 * c-file-style: "stroustrup"
836 * indent-tabs-mode: nil
837 * End:
838 * end of autoopts/makeshell.c */