Updated gnulib and added hash-pjw-bare
[gnutls.git] / src / libopts / makeshell.c
blobd2ce6615b8af715e41ea38d48172551df2a16dbe
2 /**
3 * \file makeshell.c
5 * Time-stamp: "2012-04-07 09:03:16 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 #include <config.h>
33 /* Work around problem reported in
34 <http://permalink.gmane.org/gmane.comp.lib.gnulib.bugs/15755>.*/
35 #if GETTIMEOFDAY_CLOBBERS_LOCALTIME
36 #undef localtime
37 #endif
39 tOptions * optionParseShellOptions = NULL;
41 static char const * shell_prog = NULL;
42 static char * script_leader = NULL;
43 static char * script_trailer = NULL;
45 /* = = = START-STATIC-FORWARD = = = */
46 static void
47 emit_var_text(char const * prog, char const * var, int fdin);
49 static void
50 text_to_var(tOptions * pOpts, teTextTo whichVar, tOptDesc * pOD);
52 static void
53 emit_usage(tOptions * pOpts);
55 static void
56 emit_setup(tOptions * pOpts);
58 static void
59 emit_action(tOptions * pOpts, tOptDesc* pOptDesc);
61 static void
62 emit_inaction(tOptions * pOpts, tOptDesc* pOptDesc);
64 static void
65 emit_flag(tOptions * pOpts);
67 static void
68 emit_match_expr(char const * pzMatchName, tOptDesc* pCurOpt, tOptions* pOpts);
70 static void
71 emitLong(tOptions * pOpts);
73 static void
74 open_out(char const * pzFile);
75 /* = = = END-STATIC-FORWARD = = = */
77 /*=export_func optionParseShell
78 * private:
80 * what: Decipher a boolean value
81 * arg: + tOptions* + pOpts + program options descriptor +
83 * doc:
84 * Emit a shell script that will parse the command line options.
85 =*/
86 void
87 optionParseShell(tOptions * pOpts)
90 * Check for our SHELL option now.
91 * IF the output file contains the "#!" magic marker,
92 * it will override anything we do here.
94 if (HAVE_GENSHELL_OPT(SHELL))
95 shell_prog = GENSHELL_OPT_ARG(SHELL);
97 else if (! ENABLED_GENSHELL_OPT(SHELL))
98 shell_prog = NULL;
100 else if ((shell_prog = getenv("SHELL")),
101 shell_prog == NULL)
103 shell_prog = POSIX_SHELL;
106 * Check for a specified output file
108 if (HAVE_GENSHELL_OPT(SCRIPT))
109 open_out(GENSHELL_OPT_ARG(SCRIPT));
111 emit_usage(pOpts);
112 emit_setup(pOpts);
115 * There are four modes of option processing.
117 switch (pOpts->fOptSet & (OPTPROC_LONGOPT|OPTPROC_SHORTOPT)) {
118 case OPTPROC_LONGOPT:
119 fputs(LOOP_STR, stdout);
121 fputs(LONG_OPT_MARK, stdout);
122 fputs(INIT_LOPT_STR, stdout);
123 emitLong(pOpts);
124 printf(LOPT_ARG_FMT, pOpts->pzPROGNAME);
125 fputs(END_OPT_SEL_STR, stdout);
127 fputs(NOT_FOUND_STR, stdout);
128 break;
130 case 0:
131 fputs(ONLY_OPTS_LOOP, stdout);
132 fputs(INIT_LOPT_STR, stdout);
133 emitLong(pOpts);
134 printf(LOPT_ARG_FMT, pOpts->pzPROGNAME);
135 break;
137 case OPTPROC_SHORTOPT:
138 fputs(LOOP_STR, stdout);
140 fputs(FLAG_OPT_MARK, stdout);
141 fputs(INIT_OPT_STR, stdout);
142 emit_flag(pOpts);
143 printf(OPT_ARG_FMT, pOpts->pzPROGNAME);
144 fputs(END_OPT_SEL_STR, stdout);
146 fputs(NOT_FOUND_STR, stdout);
147 break;
149 case OPTPROC_LONGOPT|OPTPROC_SHORTOPT:
150 fputs(LOOP_STR, stdout);
152 fputs(LONG_OPT_MARK, stdout);
153 fputs(INIT_LOPT_STR, stdout);
154 emitLong(pOpts);
155 printf(LOPT_ARG_FMT, pOpts->pzPROGNAME);
156 fputs(END_OPT_SEL_STR, stdout);
158 fputs(FLAG_OPT_MARK, stdout);
159 fputs(INIT_OPT_STR, stdout);
160 emit_flag(pOpts);
161 printf(OPT_ARG_FMT, pOpts->pzPROGNAME);
162 fputs(END_OPT_SEL_STR, stdout);
164 fputs(NOT_FOUND_STR, stdout);
165 break;
168 printf(zLoopEnd, pOpts->pzPROGNAME, END_MARK);
169 if ((script_trailer != NULL) && (*script_trailer != NUL))
170 fputs(script_trailer, stdout);
171 else if (ENABLED_GENSHELL_OPT(SHELL))
172 printf(SHOW_PROG_ENV, pOpts->pzPROGNAME);
174 #ifdef HAVE_FCHMOD
175 fchmod(STDOUT_FILENO, 0755);
176 #endif
177 fclose(stdout);
179 if (ferror(stdout)) {
180 fputs(zOutputFail, stderr);
181 exit(EXIT_FAILURE);
185 #ifdef HAVE_WORKING_FORK
186 static void
187 emit_var_text(char const * prog, char const * var, int fdin)
189 FILE * fp = fdopen(fdin, "r" FOPEN_BINARY_FLAG);
190 int nlct = 0; /* defer newlines and skip trailing ones */
192 printf(SET_TEXT_FMT, prog, var);
193 if (fp == NULL)
194 goto skip_text;
196 for (;;) {
197 int ch = fgetc(fp);
198 switch (ch) {
200 case NL:
201 nlct++;
202 break;
204 case '\'':
205 while (nlct > 0) {
206 fputc(NL, stdout);
207 nlct--;
209 fputs(apostrophy, stdout);
210 break;
212 case EOF:
213 goto endCharLoop;
215 default:
216 while (nlct > 0) {
217 fputc(NL, stdout);
218 nlct--;
220 fputc(ch, stdout);
221 break;
223 } endCharLoop:;
225 fclose(fp);
227 skip_text:
229 fputs(END_SET_TEXT, stdout);
232 #endif
235 * The purpose of this function is to assign "long usage", short usage
236 * and version information to a shell variable. Rather than wind our
237 * way through all the logic necessary to emit the text directly, we
238 * fork(), have our child process emit the text the normal way and
239 * capture the output in the parent process.
241 static void
242 text_to_var(tOptions * pOpts, teTextTo whichVar, tOptDesc * pOD)
244 # define _TT_(n) static char const z ## n [] = #n;
245 TEXTTO_TABLE
246 # undef _TT_
247 # define _TT_(n) z ## n ,
248 static char const * apzTTNames[] = { TEXTTO_TABLE };
249 # undef _TT_
251 #if ! defined(HAVE_WORKING_FORK)
252 printf(SET_NO_TEXT_FMT, pOpts->pzPROGNAME, apzTTNames[ whichVar]);
253 #else
254 int pipeFd[2];
256 fflush(stdout);
257 fflush(stderr);
259 if (pipe(pipeFd) != 0) {
260 fprintf(stderr, zBadPipe, errno, strerror(errno));
261 exit(EXIT_FAILURE);
264 switch (fork()) {
265 case -1:
266 fprintf(stderr, zForkFail, errno, strerror(errno), pOpts->pzProgName);
267 exit(EXIT_FAILURE);
268 break;
270 case 0:
272 * Send both stderr and stdout to the pipe. No matter which
273 * descriptor is used, we capture the output on the read end.
275 dup2(pipeFd[1], STDERR_FILENO);
276 dup2(pipeFd[1], STDOUT_FILENO);
277 close(pipeFd[0]);
279 switch (whichVar) {
280 case TT_LONGUSAGE:
281 (*(pOpts->pUsageProc))(pOpts, EXIT_SUCCESS);
282 /* NOTREACHED */
284 case TT_USAGE:
285 (*(pOpts->pUsageProc))(pOpts, EXIT_FAILURE);
286 /* NOTREACHED */
288 case TT_VERSION:
289 if (pOD->fOptState & OPTST_ALLOC_ARG) {
290 AGFREE(pOD->optArg.argString);
291 pOD->fOptState &= ~OPTST_ALLOC_ARG;
293 pOD->optArg.argString = "c";
294 optionPrintVersion(pOpts, pOD);
295 /* NOTREACHED */
297 default:
298 exit(EXIT_FAILURE);
301 default:
302 close(pipeFd[1]);
305 emit_var_text(pOpts->pzPROGNAME, apzTTNames[whichVar], pipeFd[0]);
306 #endif
310 static void
311 emit_usage(tOptions * pOpts)
313 char zTimeBuf[AO_NAME_SIZE];
316 * First, switch stdout to the output file name.
317 * Then, change the program name to the one defined
318 * by the definitions (rather than the current
319 * executable name). Down case the upper cased name.
321 if (script_leader != NULL)
322 fputs(script_leader, stdout);
325 char const * out_nm;
328 time_t c_tim = time(NULL);
329 struct tm * ptm = localtime(&c_tim);
330 strftime(zTimeBuf, AO_NAME_SIZE, TIME_FMT, ptm );
333 if (HAVE_GENSHELL_OPT(SCRIPT))
334 out_nm = GENSHELL_OPT_ARG(SCRIPT);
335 else out_nm = STDOUT;
337 if ((script_leader == NULL) && (shell_prog != NULL))
338 printf(SHELL_MAGIC, shell_prog);
340 printf(PREAMBLE_FMT, START_MARK, out_nm, zTimeBuf);
343 printf(END_PRE_FMT, pOpts->pzPROGNAME);
346 * Get a copy of the original program name in lower case and
347 * fill in an approximation of the program name from it.
350 char * pzPN = zTimeBuf;
351 char const * pz = pOpts->pzPROGNAME;
352 char ** pp;
354 for (;;) {
355 if ((*pzPN++ = (char)tolower(*pz++)) == NUL)
356 break;
359 pp = (char **)(void *)&(pOpts->pzProgPath);
360 *pp = zTimeBuf;
361 pp = (char **)(void *)&(pOpts->pzProgName);
362 *pp = zTimeBuf;
365 text_to_var(pOpts, TT_LONGUSAGE, NULL);
366 text_to_var(pOpts, TT_USAGE, NULL);
369 tOptDesc* pOptDesc = pOpts->pOptDesc;
370 int optionCt = pOpts->optCt;
372 for (;;) {
373 if (pOptDesc->pOptProc == optionPrintVersion) {
374 text_to_var(pOpts, TT_VERSION, pOptDesc);
375 break;
378 if (--optionCt <= 0)
379 break;
380 pOptDesc++;
386 static void
387 emit_setup(tOptions * pOpts)
389 tOptDesc * pOptDesc = pOpts->pOptDesc;
390 int optionCt = pOpts->presetOptCt;
391 char const * pzFmt;
392 char const * pzDefault;
394 for (;optionCt > 0; pOptDesc++, --optionCt) {
395 char zVal[32];
398 * Options that are either usage documentation or are compiled out
399 * are not to be processed.
401 if (SKIP_OPT(pOptDesc) || (pOptDesc->pz_NAME == NULL))
402 continue;
404 if (pOptDesc->optMaxCt > 1)
405 pzFmt = MULTI_DEF_FMT;
406 else pzFmt = SGL_DEF_FMT;
409 * IF this is an enumeration/bitmask option, then convert the value
410 * to a string before printing the default value.
412 switch (OPTST_GET_ARGTYPE(pOptDesc->fOptState)) {
413 case OPARG_TYPE_ENUMERATION:
414 (*(pOptDesc->pOptProc))(OPTPROC_EMIT_SHELL, pOptDesc );
415 pzDefault = pOptDesc->optArg.argString;
416 break;
419 * Numeric and membership bit options are just printed as a number.
421 case OPARG_TYPE_NUMERIC:
422 snprintf(zVal, sizeof(zVal), "%d",
423 (int)pOptDesc->optArg.argInt);
424 pzDefault = zVal;
425 break;
427 case OPARG_TYPE_MEMBERSHIP:
428 snprintf(zVal, sizeof(zVal), "%lu",
429 (unsigned long)pOptDesc->optArg.argIntptr);
430 pzDefault = zVal;
431 break;
433 case OPARG_TYPE_BOOLEAN:
434 pzDefault = (pOptDesc->optArg.argBool) ? TRUE_STR : FALSE_STR;
435 break;
437 default:
438 if (pOptDesc->optArg.argString == NULL) {
439 if (pzFmt == SGL_DEF_FMT)
440 pzFmt = SGL_NO_DEF_FMT;
441 pzDefault = NULL;
443 else
444 pzDefault = pOptDesc->optArg.argString;
447 printf(pzFmt, pOpts->pzPROGNAME, pOptDesc->pz_NAME, pzDefault);
451 static void
452 emit_action(tOptions * pOpts, tOptDesc* pOptDesc)
454 if (pOptDesc->pOptProc == optionPrintVersion)
455 printf(zTextExit, pOpts->pzPROGNAME, VER_STR);
457 else if (pOptDesc->pOptProc == optionPagedUsage)
458 printf(zPagedUsageExit, pOpts->pzPROGNAME);
460 else if (pOptDesc->pOptProc == optionLoadOpt) {
461 printf(zCmdFmt, NO_LOAD_WARN);
462 printf(zCmdFmt, YES_NEED_OPT_ARG);
464 } else if (pOptDesc->pz_NAME == NULL) {
466 if (pOptDesc->pOptProc == NULL) {
467 printf(zCmdFmt, NO_SAVE_OPTS);
468 printf(zCmdFmt, OK_NEED_OPT_ARG);
469 } else
470 printf(zTextExit, pOpts->pzPROGNAME, LONG_USE_STR);
472 } else {
473 if (pOptDesc->optMaxCt == 1)
474 printf(SGL_ARG_FMT, pOpts->pzPROGNAME, pOptDesc->pz_NAME);
475 else {
476 if ((unsigned)pOptDesc->optMaxCt < NOLIMIT)
477 printf(zCountTest, pOpts->pzPROGNAME,
478 pOptDesc->pz_NAME, pOptDesc->optMaxCt);
480 printf(MULTI_ARG_FMT, pOpts->pzPROGNAME, pOptDesc->pz_NAME);
484 * Fix up the args.
486 if (OPTST_GET_ARGTYPE(pOptDesc->fOptState) == OPARG_TYPE_NONE) {
487 printf(zCantArg, pOpts->pzPROGNAME, pOptDesc->pz_NAME);
489 } else if (pOptDesc->fOptState & OPTST_ARG_OPTIONAL) {
490 printf(zMayArg, pOpts->pzPROGNAME, pOptDesc->pz_NAME);
492 } else {
493 fputs(zMustArg, stdout);
496 fputs(zOptionEndSelect, stdout);
500 static void
501 emit_inaction(tOptions * pOpts, tOptDesc* pOptDesc)
503 if (pOptDesc->pOptProc == optionLoadOpt) {
504 printf(zCmdFmt, NO_SUPPRESS_LOAD);
506 } else if (pOptDesc->optMaxCt == 1)
507 printf(NO_SGL_ARG_FMT, pOpts->pzPROGNAME,
508 pOptDesc->pz_NAME, pOptDesc->pz_DisablePfx);
509 else
510 printf(NO_MULTI_ARG_FMT, pOpts->pzPROGNAME,
511 pOptDesc->pz_NAME, pOptDesc->pz_DisablePfx);
513 printf(zCmdFmt, NO_ARG_NEEDED);
514 fputs(zOptionEndSelect, stdout);
518 static void
519 emit_flag(tOptions * pOpts)
521 tOptDesc* pOptDesc = pOpts->pOptDesc;
522 int optionCt = pOpts->optCt;
524 fputs(zOptionCase, stdout);
526 for (;optionCt > 0; pOptDesc++, --optionCt) {
528 if (SKIP_OPT(pOptDesc))
529 continue;
531 if (IS_GRAPHIC_CHAR(pOptDesc->optValue)) {
532 printf(zOptionFlag, pOptDesc->optValue);
533 emit_action(pOpts, pOptDesc);
536 printf(UNK_OPT_FMT, FLAG_STR, pOpts->pzPROGNAME);
541 * Emit the match text for a long option
543 static void
544 emit_match_expr(char const * pzMatchName, tOptDesc* pCurOpt, tOptions* pOpts)
546 tOptDesc* pOD = pOpts->pOptDesc;
547 int oCt = pOpts->optCt;
548 int min = 1;
549 char zName[ 256 ];
550 char* pz = zName;
552 for (;;) {
553 int matchCt = 0;
556 * Omit the current option, Documentation opts and compiled out opts.
558 if ((pOD == pCurOpt) || SKIP_OPT(pOD)){
559 if (--oCt <= 0)
560 break;
561 pOD++;
562 continue;
566 * Check each character of the name case insensitively.
567 * They must not be the same. They cannot be, because it would
568 * not compile correctly if they were.
570 while ( toupper(pOD->pz_Name[matchCt])
571 == toupper(pzMatchName[matchCt]))
572 matchCt++;
574 if (matchCt > min)
575 min = matchCt;
578 * Check the disablement name, too.
580 if (pOD->pz_DisableName != NULL) {
581 matchCt = 0;
582 while ( toupper(pOD->pz_DisableName[matchCt])
583 == toupper(pzMatchName[matchCt]))
584 matchCt++;
585 if (matchCt > min)
586 min = matchCt;
588 if (--oCt <= 0)
589 break;
590 pOD++;
594 * IF the 'min' is all or one short of the name length,
595 * THEN the entire string must be matched.
597 if ( (pzMatchName[min ] == NUL)
598 || (pzMatchName[min+1] == NUL) )
599 printf(zOptionFullName, pzMatchName);
601 else {
602 int matchCt = 0;
603 for (; matchCt <= min; matchCt++)
604 *pz++ = pzMatchName[matchCt];
606 for (;;) {
607 *pz = NUL;
608 printf(zOptionPartName, zName);
609 *pz++ = pzMatchName[matchCt++];
610 if (pzMatchName[matchCt] == NUL) {
611 *pz = NUL;
612 printf(zOptionFullName, zName);
613 break;
621 * Emit GNU-standard long option handling code
623 static void
624 emitLong(tOptions * pOpts)
626 tOptDesc* pOD = pOpts->pOptDesc;
627 int ct = pOpts->optCt;
629 fputs(zOptionCase, stdout);
632 * do each option, ...
634 do {
636 * Documentation & compiled-out options
638 if (SKIP_OPT(pOD))
639 continue;
641 emit_match_expr(pOD->pz_Name, pOD, pOpts);
642 emit_action(pOpts, pOD);
645 * Now, do the same thing for the disablement version of the option.
647 if (pOD->pz_DisableName != NULL) {
648 emit_match_expr(pOD->pz_DisableName, pOD, pOpts);
649 emit_inaction(pOpts, pOD);
651 } while (pOD++, --ct > 0);
653 printf(UNK_OPT_FMT, OPTION_STR, pOpts->pzPROGNAME);
657 static void
658 open_out(char const * pzFile)
660 FILE* fp;
661 char* pzData = NULL;
662 struct stat stbf;
664 do {
665 char* pzScan;
666 size_t sizeLeft;
669 * IF we cannot stat the file,
670 * THEN assume we are creating a new file.
671 * Skip the loading of the old data.
673 if (stat(pzFile, &stbf) != 0)
674 break;
677 * The file must be a regular file
679 if (! S_ISREG(stbf.st_mode)) {
680 fprintf(stderr, zNotFile, pzFile);
681 exit(EXIT_FAILURE);
684 pzData = AGALOC(stbf.st_size + 1, "f data");
685 fp = fopen(pzFile, "r" FOPEN_BINARY_FLAG);
687 sizeLeft = (unsigned)stbf.st_size;
688 pzScan = pzData;
691 * Read in all the data as fast as our OS will let us.
693 for (;;) {
694 int inct = fread((void*)pzScan, (size_t)1, sizeLeft, fp);
695 if (inct == 0)
696 break;
698 pzScan += inct;
699 sizeLeft -= inct;
701 if (sizeLeft == 0)
702 break;
706 * NUL-terminate the leader and look for the trailer
708 *pzScan = NUL;
709 fclose(fp);
710 pzScan = strstr(pzData, START_MARK);
711 if (pzScan == NULL) {
712 script_trailer = pzData;
713 break;
716 *(pzScan++) = NUL;
717 pzScan = strstr(pzScan, END_MARK);
718 if (pzScan == NULL) {
719 script_trailer = pzData;
720 break;
724 * Check to see if the data contains our marker.
725 * If it does, then we will skip over it
727 script_trailer = pzScan + END_MARK_LEN;
728 script_leader = pzData;
729 } while (false);
731 if (freopen(pzFile, "w" FOPEN_BINARY_FLAG, stdout) != stdout) {
732 fprintf(stderr, zFreopenFail, errno, strerror(errno));
733 exit(EXIT_FAILURE);
738 /*=export_func genshelloptUsage
739 * private:
740 * what: The usage function for the genshellopt generated program
742 * arg: + tOptions* + pOpts + program options descriptor +
743 * arg: + int + exitCode + usage text type to produce +
745 * doc:
746 * This function is used to create the usage strings for the option
747 * processing shell script code. Two child processes are spawned
748 * each emitting the usage text in either the short (error exit)
749 * style or the long style. The generated program will capture this
750 * and create shell script variables containing the two types of text.
752 void
753 genshelloptUsage(tOptions * pOpts, int exitCode)
755 #if ! defined(HAVE_WORKING_FORK)
756 optionUsage(pOpts, exitCode);
757 #else
759 * IF not EXIT_SUCCESS,
760 * THEN emit the short form of usage.
762 if (exitCode != EXIT_SUCCESS)
763 optionUsage(pOpts, exitCode);
764 fflush(stderr);
765 fflush(stdout);
766 if (ferror(stdout) || ferror(stderr))
767 exit(EXIT_FAILURE);
769 option_usage_fp = stdout;
772 * First, print our usage
774 switch (fork()) {
775 case -1:
776 optionUsage(pOpts, EXIT_FAILURE);
777 /* NOTREACHED */
779 case 0:
780 pagerState = PAGER_STATE_CHILD;
781 optionUsage(pOpts, EXIT_SUCCESS);
782 /* NOTREACHED */
783 _exit(EXIT_FAILURE);
785 default:
787 int sts;
788 wait(&sts);
793 * Generate the pzProgName, since optionProcess() normally
794 * gets it from the command line
797 char * pz;
798 char ** pp = (char **)(void *)&(optionParseShellOptions->pzProgName);
799 AGDUPSTR(pz, optionParseShellOptions->pzPROGNAME, "prog name");
800 *pp = pz;
801 while (*pz != NUL) {
802 *pz = tolower(*pz);
803 pz++;
808 * Separate the makeshell usage from the client usage
810 fprintf(option_usage_fp, zGenshell, optionParseShellOptions->pzProgName);
811 fflush(option_usage_fp);
814 * Now, print the client usage.
816 switch (fork()) {
817 case 0:
818 pagerState = PAGER_STATE_CHILD;
819 /*FALLTHROUGH*/
820 case -1:
821 optionUsage(optionParseShellOptions, EXIT_FAILURE);
823 default:
825 int sts;
826 wait(&sts);
830 fflush(stdout);
831 if (ferror(stdout)) {
832 fputs(zOutputFail, stderr);
833 exit(EXIT_FAILURE);
836 exit(EXIT_SUCCESS);
837 #endif
841 * Local Variables:
842 * mode: C
843 * c-file-style: "stroustrup"
844 * indent-tabs-mode: nil
845 * End:
846 * end of autoopts/makeshell.c */