1:
2: /**
3: * \file makeshell.c
4: *
5: * Time-stamp: "2011-04-20 11:06:57 bkorb"
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: * This file is part of AutoOpts, a companion to AutoGen.
11: * AutoOpts is free software.
12: * AutoOpts is Copyright (c) 1992-2011 by Bruce Korb - all rights reserved
13: *
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.
17: *
18: * The GNU Lesser General Public License, version 3 or later
19: * See the files "COPYING.lgplv3" and "COPYING.gplv3"
20: *
21: * The Modified Berkeley Software Distribution License
22: * See the file "COPYING.mbsd"
23: *
24: * These files have the following md5sums:
25: *
26: * 43b91e8ca915626ed3818ffb1b71248b pkg/libopts/COPYING.gplv3
27: * 06a1a2e4760c90ea5e1dad8dfaac4d39 pkg/libopts/COPYING.lgplv3
28: * 66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd
29: */
30:
31: tOptions * optionParseShellOptions = NULL;
32:
33: /* * * * * * * * * * * * * * * * * * * * *
34: *
35: * Setup Format Strings
36: */
37: static char const zStartMarker[] =
38: "# # # # # # # # # # -- do not modify this marker --\n#\n"
39: "# DO NOT EDIT THIS SECTION";
40:
41: static char const zPreamble[] =
42: "%s OF %s\n#\n"
43: "# From here to the next `-- do not modify this marker --',\n"
44: "# the text has been generated %s\n";
45:
46: static char const zEndPreamble[] =
47: "# From the %s option definitions\n#\n";
48:
49: static char const zMultiDef[] = "\n"
50: "if test -z \"${%1$s_%2$s}\"\n"
51: "then\n"
52: " %1$s_%2$s_CT=0\n"
53: "else\n"
54: " %1$s_%2$s_CT=1\n"
55: " %1$s_%2$s_1=\"${%1$s_%2$s}\"\n"
56: "fi\n"
57: "export %1$s_%2$s_CT";
58:
59: static char const zSingleDef[] = "\n"
60: "%1$s_%2$s=\"${%1$s_%2$s-'%3$s'}\"\n"
61: "%1$s_%2$s_set=false\n"
62: "export %1$s_%2$s\n";
63:
64: static char const zSingleNoDef[] = "\n"
65: "%1$s_%2$s=\"${%1$s_%2$s}\"\n"
66: "%1$s_%2$s_set=false\n"
67: "export %1$s_%2$s\n";
68:
69: /* * * * * * * * * * * * * * * * * * * * *
70: *
71: * LOOP START
72: *
73: * The loop may run in either of two modes:
74: * all options are named options (loop only)
75: * regular, marked option processing.
76: */
77: static char const zLoopCase[] = "\n"
78: "OPT_PROCESS=true\n"
79: "OPT_ARG=\"$1\"\n\n"
80: "while ${OPT_PROCESS} && [ $# -gt 0 ]\ndo\n"
81: " OPT_ELEMENT=''\n"
82: " OPT_ARG_VAL=''\n\n"
83: /*
84: * 'OPT_ARG' may or may not match the current $1
85: */
86: " case \"${OPT_ARG}\" in\n"
87: " -- )\n"
88: " OPT_PROCESS=false\n"
89: " shift\n"
90: " ;;\n\n";
91:
92: static char const zLoopOnly[] = "\n"
93: "OPT_ARG=\"$1\"\n\n"
94: "while [ $# -gt 0 ]\ndo\n"
95: " OPT_ELEMENT=''\n"
96: " OPT_ARG_VAL=''\n\n"
97: " OPT_ARG=\"${1}\"\n";
98:
99: /* * * * * * * * * * * * * * * *
100: *
101: * CASE SELECTORS
102: *
103: * If the loop runs as a regular option loop,
104: * then we must have selectors for each acceptable option
105: * type (long option, flag character and non-option)
106: */
107: static char const zLongSelection[] =
108: " --* )\n";
109:
110: static char const zFlagSelection[] =
111: " -* )\n";
112:
113: static char const zEndSelection[] =
114: " ;;\n\n";
115:
116: static char const zNoSelection[] =
117: " * )\n"
118: " OPT_PROCESS=false\n"
119: " ;;\n"
120: " esac\n\n";
121:
122: /* * * * * * * * * * * * * * * *
123: *
124: * LOOP END
125: */
126: static char const zLoopEnd[] =
127: " if [ -n \"${OPT_ARG_VAL}\" ]\n"
128: " then\n"
129: " eval %1$s_${OPT_NAME}${OPT_ELEMENT}=\"'${OPT_ARG_VAL}'\"\n"
130: " export %1$s_${OPT_NAME}${OPT_ELEMENT}\n"
131: " fi\n"
132: "done\n\n"
133: "unset OPT_PROCESS || :\n"
134: "unset OPT_ELEMENT || :\n"
135: "unset OPT_ARG || :\n"
136: "unset OPT_ARG_NEEDED || :\n"
137: "unset OPT_NAME || :\n"
138: "unset OPT_CODE || :\n"
139: "unset OPT_ARG_VAL || :\n%2$s";
140:
141: static char const zTrailerMarker[] = "\n"
142: "# # # # # # # # # #\n#\n"
143: "# END OF AUTOMATED OPTION PROCESSING\n"
144: "#\n# # # # # # # # # # -- do not modify this marker --\n";
145:
146: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
147: *
148: * OPTION SELECTION
149: */
150: static char const zOptionCase[] =
151: " case \"${OPT_CODE}\" in\n";
152:
153: static char const zOptionPartName[] =
154: " '%s' | \\\n";
155:
156: static char const zOptionFullName[] =
157: " '%s' )\n";
158:
159: static char const zOptionFlag[] =
160: " '%c' )\n";
161:
162: static char const zOptionEndSelect[] =
163: " ;;\n\n";
164:
165: static char const zOptionUnknown[] =
166: " * )\n"
167: " echo Unknown %s: \"${OPT_CODE}\" >&2\n"
168: " echo \"$%s_USAGE_TEXT\"\n"
169: " exit 1\n"
170: " ;;\n"
171: " esac\n\n";
172:
173: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
174: *
175: * OPTION PROCESSING
176: *
177: * Formats for emitting the text for handling particular options
178: */
179: static char const zTextExit[] =
180: " echo \"$%s_%s_TEXT\"\n"
181: " exit 0\n";
182:
183: static char const zPagedUsageExit[] =
184: " echo \"$%s_LONGUSAGE_TEXT\" | ${PAGER-more}\n"
185: " exit 0\n";
186:
187: static char const zCmdFmt[] =
188: " %s\n";
189:
190: static char const zCountTest[] =
191: " if [ $%1$s_%2$s_CT -ge %3$d ] ; then\n"
192: " echo Error: more than %3$d %2$s options >&2\n"
193: " echo \"$%1$s_USAGE_TEXT\"\n"
194: " exit 1 ; fi\n";
195:
196: static char const zMultiArg[] =
197: " %1$s_%2$s_CT=`expr ${%1$s_%2$s_CT} + 1`\n"
198: " OPT_ELEMENT=\"_${%1$s_%2$s_CT}\"\n"
199: " OPT_NAME='%2$s'\n";
200:
201: static char const zSingleArg[] =
202: " if [ -n \"${%1$s_%2$s}\" ] && ${%1$s_%2$s_set} ; then\n"
203: " echo Error: duplicate %2$s option >&2\n"
204: " echo \"$%1$s_USAGE_TEXT\"\n"
205: " exit 1 ; fi\n"
206: " %1$s_%2$s_set=true\n"
207: " OPT_NAME='%2$s'\n";
208:
209: static char const zNoMultiArg[] =
210: " %1$s_%2$s_CT=0\n"
211: " OPT_ELEMENT=''\n"
212: " %1$s_%2$s='%3$s'\n"
213: " export %1$s_%2$s\n"
214: " OPT_NAME='%2$s'\n";
215:
216: static char const zNoSingleArg[] =
217: " if [ -n \"${%1$s_%2$s}\" ] && ${%1$s_%2$s_set} ; then\n"
218: " echo Error: duplicate %2$s option >&2\n"
219: " echo \"$%1$s_USAGE_TEXT\"\n"
220: " exit 1 ; fi\n"
221: " %1$s_%2$s_set=true\n"
222: " %1$s_%2$s='%3$s'\n"
223: " export %1$s_%2$s\n"
224: " OPT_NAME='%2$s'\n";
225:
226: static char const zMayArg[] =
227: " eval %1$s_%2$s${OPT_ELEMENT}=true\n"
228: " export %1$s_%2$s${OPT_ELEMENT}\n"
229: " OPT_ARG_NEEDED=OK\n";
230:
231: static char const zMustArg[] =
232: " OPT_ARG_NEEDED=YES\n";
233:
234: static char const zCantArg[] =
235: " eval %1$s_%2$s${OPT_ELEMENT}=true\n"
236: " export %1$s_%2$s${OPT_ELEMENT}\n"
237: " OPT_ARG_NEEDED=NO\n";
238:
239: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
240: *
241: * LONG OPTION PROCESSING
242: *
243: * Formats for emitting the text for handling long option types
244: */
245: static char const zLongOptInit[] =
246: " OPT_CODE=`echo \"X${OPT_ARG}\"|sed 's/^X-*//'`\n"
247: " shift\n"
248: " OPT_ARG=\"$1\"\n\n"
249: " case \"${OPT_CODE}\" in *=* )\n"
250: " OPT_ARG_VAL=`echo \"${OPT_CODE}\"|sed 's/^[^=]*=//'`\n"
251: " OPT_CODE=`echo \"${OPT_CODE}\"|sed 's/=.*$//'` ;; esac\n\n";
252:
253: static char const zLongOptArg[] =
254: " case \"${OPT_ARG_NEEDED}\" in\n"
255: " NO )\n"
256: " OPT_ARG_VAL=''\n"
257: " ;;\n\n"
258: " YES )\n"
259: " if [ -z \"${OPT_ARG_VAL}\" ]\n"
260: " then\n"
261: " if [ $# -eq 0 ]\n"
262: " then\n"
263: " echo No argument provided for ${OPT_NAME} option >&2\n"
264: " echo \"$%s_USAGE_TEXT\"\n"
265: " exit 1\n"
266: " fi\n\n"
267: " OPT_ARG_VAL=\"${OPT_ARG}\"\n"
268: " shift\n"
269: " OPT_ARG=\"$1\"\n"
270: " fi\n"
271: " ;;\n\n"
272: " OK )\n"
273: " if [ -z \"${OPT_ARG_VAL}\" ] && [ $# -gt 0 ]\n"
274: " then\n"
275: " case \"${OPT_ARG}\" in -* ) ;; * )\n"
276: " OPT_ARG_VAL=\"${OPT_ARG}\"\n"
277: " shift\n"
278: " OPT_ARG=\"$1\" ;; esac\n"
279: " fi\n"
280: " ;;\n"
281: " esac\n";
282:
283: /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
284: *
285: * FLAG OPTION PROCESSING
286: *
287: * Formats for emitting the text for handling flag option types
288: */
289: static char const zFlagOptInit[] =
290: " OPT_CODE=`echo \"X${OPT_ARG}\" | sed 's/X-\\(.\\).*/\\1/'`\n"
291: " OPT_ARG=` echo \"X${OPT_ARG}\" | sed 's/X-.//'`\n\n";
292:
293: static char const zFlagOptArg[] =
294: " case \"${OPT_ARG_NEEDED}\" in\n"
295: " NO )\n"
296: " if [ -n \"${OPT_ARG}\" ]\n"
297: " then\n"
298: " OPT_ARG=-\"${OPT_ARG}\"\n"
299: " else\n"
300: " shift\n"
301: " OPT_ARG=\"$1\"\n"
302: " fi\n"
303: " ;;\n\n"
304: " YES )\n"
305: " if [ -n \"${OPT_ARG}\" ]\n"
306: " then\n"
307: " OPT_ARG_VAL=\"${OPT_ARG}\"\n\n"
308: " else\n"
309: " if [ $# -eq 0 ]\n"
310: " then\n"
311: " echo No argument provided for ${OPT_NAME} option >&2\n"
312: " echo \"$%s_USAGE_TEXT\"\n"
313: " exit 1\n"
314: " fi\n"
315: " shift\n"
316: " OPT_ARG_VAL=\"$1\"\n"
317: " fi\n\n"
318: " shift\n"
319: " OPT_ARG=\"$1\"\n"
320: " ;;\n\n"
321: " OK )\n"
322: " if [ -n \"${OPT_ARG}\" ]\n"
323: " then\n"
324: " OPT_ARG_VAL=\"${OPT_ARG}\"\n"
325: " shift\n"
326: " OPT_ARG=\"$1\"\n\n"
327: " else\n"
328: " shift\n"
329: " if [ $# -gt 0 ]\n"
330: " then\n"
331: " case \"$1\" in -* ) ;; * )\n"
332: " OPT_ARG_VAL=\"$1\"\n"
333: " shift ;; esac\n"
334: " OPT_ARG=\"$1\"\n"
335: " fi\n"
336: " fi\n"
337: " ;;\n"
338: " esac\n";
339:
340: tSCC* pzShell = NULL;
341: static char* pzLeader = NULL;
342: static char* pzTrailer = NULL;
343:
344: /* = = = START-STATIC-FORWARD = = = */
345: static void
346: emit_var_text(char const * prog, char const * var, int fdin);
347:
348: static void
349: textToVariable(tOptions * pOpts, teTextTo whichVar, tOptDesc * pOD);
350:
351: static void
352: emitUsage(tOptions* pOpts);
353:
354: static void
355: emitSetup(tOptions* pOpts);
356:
357: static void
358: printOptionAction(tOptions* pOpts, tOptDesc* pOptDesc);
359:
360: static void
361: printOptionInaction(tOptions* pOpts, tOptDesc* pOptDesc);
362:
363: static void
364: emitFlag(tOptions* pOpts);
365:
366: static void
367: emitMatchExpr(tCC* pzMatchName, tOptDesc* pCurOpt, tOptions* pOpts);
368:
369: static void
370: emitLong(tOptions* pOpts);
371:
372: static void
373: openOutput(char const* pzFile);
374: /* = = = END-STATIC-FORWARD = = = */
375:
376: /*=export_func optionParseShell
377: * private:
378: *
379: * what: Decipher a boolean value
380: * arg: + tOptions* + pOpts + program options descriptor +
381: *
382: * doc:
383: * Emit a shell script that will parse the command line options.
384: =*/
385: void
386: optionParseShell(tOptions* pOpts)
387: {
388: /*
389: * Check for our SHELL option now.
390: * IF the output file contains the "#!" magic marker,
391: * it will override anything we do here.
392: */
393: if (HAVE_GENSHELL_OPT(SHELL))
394: pzShell = GENSHELL_OPT_ARG(SHELL);
395:
396: else if (! ENABLED_GENSHELL_OPT(SHELL))
397: pzShell = NULL;
398:
399: else if ((pzShell = getenv("SHELL")),
400: pzShell == NULL)
401:
402: pzShell = POSIX_SHELL;
403:
404: /*
405: * Check for a specified output file
406: */
407: if (HAVE_GENSHELL_OPT(SCRIPT))
408: openOutput(GENSHELL_OPT_ARG(SCRIPT));
409:
410: emitUsage(pOpts);
411: emitSetup(pOpts);
412:
413: /*
414: * There are four modes of option processing.
415: */
416: switch (pOpts->fOptSet & (OPTPROC_LONGOPT|OPTPROC_SHORTOPT)) {
417: case OPTPROC_LONGOPT:
418: fputs(zLoopCase, stdout);
419:
420: fputs(zLongSelection, stdout);
421: fputs(zLongOptInit, stdout);
422: emitLong(pOpts);
423: printf(zLongOptArg, pOpts->pzPROGNAME);
424: fputs(zEndSelection, stdout);
425:
426: fputs(zNoSelection, stdout);
427: break;
428:
429: case 0:
430: fputs(zLoopOnly, stdout);
431: fputs(zLongOptInit, stdout);
432: emitLong(pOpts);
433: printf(zLongOptArg, pOpts->pzPROGNAME);
434: break;
435:
436: case OPTPROC_SHORTOPT:
437: fputs(zLoopCase, stdout);
438:
439: fputs(zFlagSelection, stdout);
440: fputs(zFlagOptInit, stdout);
441: emitFlag(pOpts);
442: printf(zFlagOptArg, pOpts->pzPROGNAME);
443: fputs(zEndSelection, stdout);
444:
445: fputs(zNoSelection, stdout);
446: break;
447:
448: case OPTPROC_LONGOPT|OPTPROC_SHORTOPT:
449: fputs(zLoopCase, stdout);
450:
451: fputs(zLongSelection, stdout);
452: fputs(zLongOptInit, stdout);
453: emitLong(pOpts);
454: printf(zLongOptArg, pOpts->pzPROGNAME);
455: fputs(zEndSelection, stdout);
456:
457: fputs(zFlagSelection, stdout);
458: fputs(zFlagOptInit, stdout);
459: emitFlag(pOpts);
460: printf(zFlagOptArg, pOpts->pzPROGNAME);
461: fputs(zEndSelection, stdout);
462:
463: fputs(zNoSelection, stdout);
464: break;
465: }
466:
467: printf(zLoopEnd, pOpts->pzPROGNAME, zTrailerMarker);
468: if ((pzTrailer != NULL) && (*pzTrailer != '\0'))
469: fputs(pzTrailer, stdout);
470: else if (ENABLED_GENSHELL_OPT(SHELL))
471: printf("\nenv | grep '^%s_'\n", pOpts->pzPROGNAME);
472:
473: fflush(stdout);
474: fchmod(STDOUT_FILENO, 0755);
475: fclose(stdout);
476: if (ferror(stdout)) {
477: fputs(zOutputFail, stderr);
478: exit(EXIT_FAILURE);
479: }
480: }
481:
482: #ifdef HAVE_WORKING_FORK
483: static void
484: emit_var_text(char const * prog, char const * var, int fdin)
485: {
486: FILE * fp = fdopen(fdin, "r" FOPEN_BINARY_FLAG);
487: int nlct = 0; /* defer newlines and skip trailing ones */
488:
489: printf("%s_%s_TEXT='", prog, var);
490: if (fp == NULL)
491: goto skip_text;
492:
493: for (;;) {
494: int ch = fgetc(fp);
495: switch (ch) {
496:
497: case '\n':
498: nlct++;
499: break;
500:
501: case '\'':
502: while (nlct > 0) {
503: fputc('\n', stdout);
504: nlct--;
505: }
506: fputs("'\\''", stdout);
507: break;
508:
509: case EOF:
510: goto endCharLoop;
511:
512: default:
513: while (nlct > 0) {
514: fputc('\n', stdout);
515: nlct--;
516: }
517: fputc(ch, stdout);
518: break;
519: }
520: } endCharLoop:;
521:
522: fclose(fp);
523:
524: skip_text:
525:
526: fputs("'\n\n", stdout);
527: }
528:
529: #endif
530:
531: /*
532: * The purpose of this function is to assign "long usage", short usage
533: * and version information to a shell variable. Rather than wind our
534: * way through all the logic necessary to emit the text directly, we
535: * fork(), have our child process emit the text the normal way and
536: * capture the output in the parent process.
537: */
538: static void
539: textToVariable(tOptions * pOpts, teTextTo whichVar, tOptDesc * pOD)
540: {
541: # define _TT_(n) static char const z ## n [] = #n;
542: TEXTTO_TABLE
543: # undef _TT_
544: # define _TT_(n) z ## n ,
545: static char const * apzTTNames[] = { TEXTTO_TABLE };
546: # undef _TT_
547:
548: #if ! defined(HAVE_WORKING_FORK)
549: printf("%1$s_%2$s_TEXT='no %2$s text'\n",
550: pOpts->pzPROGNAME, apzTTNames[ whichVar ]);
551: #else
552: int pipeFd[2];
553:
554: fflush(stdout);
555: fflush(stderr);
556:
557: if (pipe(pipeFd) != 0) {
558: fprintf(stderr, zBadPipe, errno, strerror(errno));
559: exit(EXIT_FAILURE);
560: }
561:
562: switch (fork()) {
563: case -1:
564: fprintf(stderr, zForkFail, errno, strerror(errno), pOpts->pzProgName);
565: exit(EXIT_FAILURE);
566: break;
567:
568: case 0:
569: /*
570: * Send both stderr and stdout to the pipe. No matter which
571: * descriptor is used, we capture the output on the read end.
572: */
573: dup2(pipeFd[1], STDERR_FILENO);
574: dup2(pipeFd[1], STDOUT_FILENO);
575: close(pipeFd[0]);
576:
577: switch (whichVar) {
578: case TT_LONGUSAGE:
579: (*(pOpts->pUsageProc))(pOpts, EXIT_SUCCESS);
580: /* NOTREACHED */
581:
582: case TT_USAGE:
583: (*(pOpts->pUsageProc))(pOpts, EXIT_FAILURE);
584: /* NOTREACHED */
585:
586: case TT_VERSION:
587: if (pOD->fOptState & OPTST_ALLOC_ARG) {
588: AGFREE(pOD->optArg.argString);
589: pOD->fOptState &= ~OPTST_ALLOC_ARG;
590: }
591: pOD->optArg.argString = "c";
592: optionPrintVersion(pOpts, pOD);
593: /* NOTREACHED */
594:
595: default:
596: exit(EXIT_FAILURE);
597: }
598:
599: default:
600: close(pipeFd[1]);
601: }
602:
603: emit_var_text(pOpts->pzPROGNAME, apzTTNames[whichVar], pipeFd[0]);
604: #endif
605: }
606:
607:
608: static void
609: emitUsage(tOptions* pOpts)
610: {
611: char zTimeBuf[AO_NAME_SIZE];
612:
613: /*
614: * First, switch stdout to the output file name.
615: * Then, change the program name to the one defined
616: * by the definitions (rather than the current
617: * executable name). Down case the upper cased name.
618: */
619: if (pzLeader != NULL)
620: fputs(pzLeader, stdout);
621:
622: {
623: tSCC zStdout[] = "stdout";
624: tCC* pzOutName;
625:
626: {
627: time_t curTime = time(NULL);
628: struct tm* pTime = localtime(&curTime);
629: strftime(zTimeBuf, AO_NAME_SIZE, "%A %B %e, %Y at %r %Z", pTime );
630: }
631:
632: if (HAVE_GENSHELL_OPT(SCRIPT))
633: pzOutName = GENSHELL_OPT_ARG(SCRIPT);
634: else pzOutName = zStdout;
635:
636: if ((pzLeader == NULL) && (pzShell != NULL))
637: printf("#! %s\n", pzShell);
638:
639: printf(zPreamble, zStartMarker, pzOutName, zTimeBuf);
640: }
641:
642: printf(zEndPreamble, pOpts->pzPROGNAME);
643:
644: /*
645: * Get a copy of the original program name in lower case and
646: * fill in an approximation of the program name from it.
647: */
648: {
649: char * pzPN = zTimeBuf;
650: char const * pz = pOpts->pzPROGNAME;
651: char ** pp;
652:
653: for (;;) {
654: if ((*pzPN++ = tolower(*pz++)) == '\0')
655: break;
656: }
657:
658: pp = (char **)(void *)&(pOpts->pzProgPath);
659: *pp = zTimeBuf;
660: pp = (char **)(void *)&(pOpts->pzProgName);
661: *pp = zTimeBuf;
662: }
663:
664: textToVariable(pOpts, TT_LONGUSAGE, NULL);
665: textToVariable(pOpts, TT_USAGE, NULL);
666:
667: {
668: tOptDesc* pOptDesc = pOpts->pOptDesc;
669: int optionCt = pOpts->optCt;
670:
671: for (;;) {
672: if (pOptDesc->pOptProc == optionPrintVersion) {
673: textToVariable(pOpts, TT_VERSION, pOptDesc);
674: break;
675: }
676:
677: if (--optionCt <= 0)
678: break;
679: pOptDesc++;
680: }
681: }
682: }
683:
684:
685: static void
686: emitSetup(tOptions* pOpts)
687: {
688: tOptDesc* pOptDesc = pOpts->pOptDesc;
689: int optionCt = pOpts->presetOptCt;
690: char const* pzFmt;
691: char const* pzDefault;
692:
693: for (;optionCt > 0; pOptDesc++, --optionCt) {
694: char zVal[16];
695:
696: /*
697: * Options that are either usage documentation or are compiled out
698: * are not to be processed.
699: */
700: if (SKIP_OPT(pOptDesc) || (pOptDesc->pz_NAME == NULL))
701: continue;
702:
703: if (pOptDesc->optMaxCt > 1)
704: pzFmt = zMultiDef;
705: else pzFmt = zSingleDef;
706:
707: /*
708: * IF this is an enumeration/bitmask option, then convert the value
709: * to a string before printing the default value.
710: */
711: switch (OPTST_GET_ARGTYPE(pOptDesc->fOptState)) {
712: case OPARG_TYPE_ENUMERATION:
713: (*(pOptDesc->pOptProc))(OPTPROC_EMIT_SHELL, pOptDesc );
714: pzDefault = pOptDesc->optArg.argString;
715: break;
716:
717: /*
718: * Numeric and membership bit options are just printed as a number.
719: */
720: case OPARG_TYPE_NUMERIC:
721: snprintf(zVal, sizeof(zVal), "%d",
722: (int)pOptDesc->optArg.argInt);
723: pzDefault = zVal;
724: break;
725:
726: case OPARG_TYPE_MEMBERSHIP:
727: snprintf(zVal, sizeof(zVal), "%lu",
728: (unsigned long)pOptDesc->optArg.argIntptr);
729: pzDefault = zVal;
730: break;
731:
732: case OPARG_TYPE_BOOLEAN:
733: pzDefault = (pOptDesc->optArg.argBool) ? "true" : "false";
734: break;
735:
736: default:
737: if (pOptDesc->optArg.argString == NULL) {
738: if (pzFmt == zSingleDef)
739: pzFmt = zSingleNoDef;
740: pzDefault = NULL;
741: }
742: else
743: pzDefault = pOptDesc->optArg.argString;
744: }
745:
746: printf(pzFmt, pOpts->pzPROGNAME, pOptDesc->pz_NAME, pzDefault);
747: }
748: }
749:
750:
751: static void
752: printOptionAction(tOptions* pOpts, tOptDesc* pOptDesc)
753: {
754: if (pOptDesc->pOptProc == optionPrintVersion)
755: printf(zTextExit, pOpts->pzPROGNAME, "VERSION");
756:
757: else if (pOptDesc->pOptProc == optionPagedUsage)
758: printf(zPagedUsageExit, pOpts->pzPROGNAME);
759:
760: else if (pOptDesc->pOptProc == optionLoadOpt) {
761: printf(zCmdFmt, "echo 'Warning: Cannot load options files' >&2");
762: printf(zCmdFmt, "OPT_ARG_NEEDED=YES");
763:
764: } else if (pOptDesc->pz_NAME == NULL) {
765:
766: if (pOptDesc->pOptProc == NULL) {
767: printf(zCmdFmt, "echo 'Warning: Cannot save options files' "
768: ">&2");
769: printf(zCmdFmt, "OPT_ARG_NEEDED=OK");
770: } else
771: printf(zTextExit, pOpts->pzPROGNAME, "LONGUSAGE");
772:
773: } else {
774: if (pOptDesc->optMaxCt == 1)
775: printf(zSingleArg, pOpts->pzPROGNAME, pOptDesc->pz_NAME);
776: else {
777: if ((unsigned)pOptDesc->optMaxCt < NOLIMIT)
778: printf(zCountTest, pOpts->pzPROGNAME,
779: pOptDesc->pz_NAME, pOptDesc->optMaxCt);
780:
781: printf(zMultiArg, pOpts->pzPROGNAME, pOptDesc->pz_NAME);
782: }
783:
784: /*
785: * Fix up the args.
786: */
787: if (OPTST_GET_ARGTYPE(pOptDesc->fOptState) == OPARG_TYPE_NONE) {
788: printf(zCantArg, pOpts->pzPROGNAME, pOptDesc->pz_NAME);
789:
790: } else if (pOptDesc->fOptState & OPTST_ARG_OPTIONAL) {
791: printf(zMayArg, pOpts->pzPROGNAME, pOptDesc->pz_NAME);
792:
793: } else {
794: fputs(zMustArg, stdout);
795: }
796: }
797: fputs(zOptionEndSelect, stdout);
798: }
799:
800:
801: static void
802: printOptionInaction(tOptions* pOpts, tOptDesc* pOptDesc)
803: {
804: if (pOptDesc->pOptProc == optionLoadOpt) {
805: printf(zCmdFmt, "echo 'Warning: Cannot suppress the loading of "
806: "options files' >&2");
807:
808: } else if (pOptDesc->optMaxCt == 1)
809: printf(zNoSingleArg, pOpts->pzPROGNAME,
810: pOptDesc->pz_NAME, pOptDesc->pz_DisablePfx);
811: else
812: printf(zNoMultiArg, pOpts->pzPROGNAME,
813: pOptDesc->pz_NAME, pOptDesc->pz_DisablePfx);
814:
815: printf(zCmdFmt, "OPT_ARG_NEEDED=NO");
816: fputs(zOptionEndSelect, stdout);
817: }
818:
819:
820: static void
821: emitFlag(tOptions* pOpts)
822: {
823: tOptDesc* pOptDesc = pOpts->pOptDesc;
824: int optionCt = pOpts->optCt;
825:
826: fputs(zOptionCase, stdout);
827:
828: for (;optionCt > 0; pOptDesc++, --optionCt) {
829:
830: if (SKIP_OPT(pOptDesc))
831: continue;
832:
833: if (IS_GRAPHIC_CHAR(pOptDesc->optValue)) {
834: printf(zOptionFlag, pOptDesc->optValue);
835: printOptionAction(pOpts, pOptDesc);
836: }
837: }
838: printf(zOptionUnknown, "flag", pOpts->pzPROGNAME);
839: }
840:
841:
842: /*
843: * Emit the match text for a long option
844: */
845: static void
846: emitMatchExpr(tCC* pzMatchName, tOptDesc* pCurOpt, tOptions* pOpts)
847: {
848: tOptDesc* pOD = pOpts->pOptDesc;
849: int oCt = pOpts->optCt;
850: int min = 1;
851: char zName[ 256 ];
852: char* pz = zName;
853:
854: for (;;) {
855: int matchCt = 0;
856:
857: /*
858: * Omit the current option, Documentation opts and compiled out opts.
859: */
860: if ((pOD == pCurOpt) || SKIP_OPT(pOD)){
861: if (--oCt <= 0)
862: break;
863: pOD++;
864: continue;
865: }
866:
867: /*
868: * Check each character of the name case insensitively.
869: * They must not be the same. They cannot be, because it would
870: * not compile correctly if they were.
871: */
872: while ( toupper(pOD->pz_Name[matchCt])
873: == toupper(pzMatchName[matchCt]))
874: matchCt++;
875:
876: if (matchCt > min)
877: min = matchCt;
878:
879: /*
880: * Check the disablement name, too.
881: */
882: if (pOD->pz_DisableName != NULL) {
883: matchCt = 0;
884: while ( toupper(pOD->pz_DisableName[matchCt])
885: == toupper(pzMatchName[matchCt]))
886: matchCt++;
887: if (matchCt > min)
888: min = matchCt;
889: }
890: if (--oCt <= 0)
891: break;
892: pOD++;
893: }
894:
895: /*
896: * IF the 'min' is all or one short of the name length,
897: * THEN the entire string must be matched.
898: */
899: if ( (pzMatchName[min ] == NUL)
900: || (pzMatchName[min+1] == NUL) )
901: printf(zOptionFullName, pzMatchName);
902:
903: else {
904: int matchCt = 0;
905: for (; matchCt <= min; matchCt++)
906: *pz++ = pzMatchName[matchCt];
907:
908: for (;;) {
909: *pz = NUL;
910: printf(zOptionPartName, zName);
911: *pz++ = pzMatchName[matchCt++];
912: if (pzMatchName[matchCt] == NUL) {
913: *pz = NUL;
914: printf(zOptionFullName, zName);
915: break;
916: }
917: }
918: }
919: }
920:
921:
922: /*
923: * Emit GNU-standard long option handling code
924: */
925: static void
926: emitLong(tOptions* pOpts)
927: {
928: tOptDesc* pOD = pOpts->pOptDesc;
929: int ct = pOpts->optCt;
930:
931: fputs(zOptionCase, stdout);
932:
933: /*
934: * do each option, ...
935: */
936: do {
937: /*
938: * Documentation & compiled-out options
939: */
940: if (SKIP_OPT(pOD))
941: continue;
942:
943: emitMatchExpr(pOD->pz_Name, pOD, pOpts);
944: printOptionAction(pOpts, pOD);
945:
946: /*
947: * Now, do the same thing for the disablement version of the option.
948: */
949: if (pOD->pz_DisableName != NULL) {
950: emitMatchExpr(pOD->pz_DisableName, pOD, pOpts);
951: printOptionInaction(pOpts, pOD);
952: }
953: } while (pOD++, --ct > 0);
954:
955: printf(zOptionUnknown, "option", pOpts->pzPROGNAME);
956: }
957:
958:
959: static void
960: openOutput(char const* pzFile)
961: {
962: FILE* fp;
963: char* pzData = NULL;
964: struct stat stbf;
965:
966: do {
967: char* pzScan;
968: size_t sizeLeft;
969:
970: /*
971: * IF we cannot stat the file,
972: * THEN assume we are creating a new file.
973: * Skip the loading of the old data.
974: */
975: if (stat(pzFile, &stbf) != 0)
976: break;
977:
978: /*
979: * The file must be a regular file
980: */
981: if (! S_ISREG(stbf.st_mode)) {
982: fprintf(stderr, zNotFile, pzFile);
983: exit(EXIT_FAILURE);
984: }
985:
986: pzData = AGALOC(stbf.st_size + 1, "file data");
987: fp = fopen(pzFile, "r" FOPEN_BINARY_FLAG);
988:
989: sizeLeft = (unsigned)stbf.st_size;
990: pzScan = pzData;
991:
992: /*
993: * Read in all the data as fast as our OS will let us.
994: */
995: for (;;) {
996: int inct = fread((void*)pzScan, (size_t)1, sizeLeft, fp);
997: if (inct == 0)
998: break;
999:
1000: pzScan += inct;
1001: sizeLeft -= inct;
1002:
1003: if (sizeLeft == 0)
1004: break;
1005: }
1006:
1007: /*
1008: * NUL-terminate the leader and look for the trailer
1009: */
1010: *pzScan = '\0';
1011: fclose(fp);
1012: pzScan = strstr(pzData, zStartMarker);
1013: if (pzScan == NULL) {
1014: pzTrailer = pzData;
1015: break;
1016: }
1017:
1018: *(pzScan++) = NUL;
1019: pzScan = strstr(pzScan, zTrailerMarker);
1020: if (pzScan == NULL) {
1021: pzTrailer = pzData;
1022: break;
1023: }
1024:
1025: /*
1026: * Check to see if the data contains our marker.
1027: * If it does, then we will skip over it
1028: */
1029: pzTrailer = pzScan + sizeof(zTrailerMarker) - 1;
1030: pzLeader = pzData;
1031: } while (AG_FALSE);
1032:
1033: if (freopen(pzFile, "w" FOPEN_BINARY_FLAG, stdout) != stdout) {
1034: fprintf(stderr, zFreopenFail, errno, strerror(errno));
1035: exit(EXIT_FAILURE);
1036: }
1037: }
1038:
1039:
1040: /*=export_func genshelloptUsage
1041: * private:
1042: * what: The usage function for the genshellopt generated program
1043: *
1044: * arg: + tOptions* + pOpts + program options descriptor +
1045: * arg: + int + exitCode + usage text type to produce +
1046: *
1047: * doc:
1048: * This function is used to create the usage strings for the option
1049: * processing shell script code. Two child processes are spawned
1050: * each emitting the usage text in either the short (error exit)
1051: * style or the long style. The generated program will capture this
1052: * and create shell script variables containing the two types of text.
1053: =*/
1054: void
1055: genshelloptUsage(tOptions * pOpts, int exitCode)
1056: {
1057: #if ! defined(HAVE_WORKING_FORK)
1058: optionUsage(pOpts, exitCode);
1059: #else
1060: /*
1061: * IF not EXIT_SUCCESS,
1062: * THEN emit the short form of usage.
1063: */
1064: if (exitCode != EXIT_SUCCESS)
1065: optionUsage(pOpts, exitCode);
1066: fflush(stderr);
1067: fflush(stdout);
1068: if (ferror(stdout) || ferror(stderr))
1069: exit(EXIT_FAILURE);
1070:
1071: option_usage_fp = stdout;
1072:
1073: /*
1074: * First, print our usage
1075: */
1076: switch (fork()) {
1077: case -1:
1078: optionUsage(pOpts, EXIT_FAILURE);
1079: /* NOTREACHED */
1080:
1081: case 0:
1082: pagerState = PAGER_STATE_CHILD;
1083: optionUsage(pOpts, EXIT_SUCCESS);
1084: /* NOTREACHED */
1085: _exit(EXIT_FAILURE);
1086:
1087: default:
1088: {
1089: int sts;
1090: wait(&sts);
1091: }
1092: }
1093:
1094: /*
1095: * Generate the pzProgName, since optionProcess() normally
1096: * gets it from the command line
1097: */
1098: {
1099: char * pz;
1100: char ** pp = (char **)(void *)&(optionParseShellOptions->pzProgName);
1101: AGDUPSTR(pz, optionParseShellOptions->pzPROGNAME, "program name");
1102: *pp = pz;
1103: while (*pz != NUL) {
1104: *pz = tolower(*pz);
1105: pz++;
1106: }
1107: }
1108:
1109: /*
1110: * Separate the makeshell usage from the client usage
1111: */
1112: fprintf(option_usage_fp, zGenshell, optionParseShellOptions->pzProgName);
1113: fflush(option_usage_fp);
1114:
1115: /*
1116: * Now, print the client usage.
1117: */
1118: switch (fork()) {
1119: case 0:
1120: pagerState = PAGER_STATE_CHILD;
1121: /*FALLTHROUGH*/
1122: case -1:
1123: optionUsage(optionParseShellOptions, EXIT_FAILURE);
1124:
1125: default:
1126: {
1127: int sts;
1128: wait(&sts);
1129: }
1130: }
1131:
1132: fflush(stdout);
1133: if (ferror(stdout)) {
1134: fputs(zOutputFail, stderr);
1135: exit(EXIT_FAILURE);
1136: }
1137:
1138: exit(EXIT_SUCCESS);
1139: #endif
1140: }
1141:
1142: /*
1143: * Local Variables:
1144: * mode: C
1145: * c-file-style: "stroustrup"
1146: * indent-tabs-mode: nil
1147: * End:
1148: * end of autoopts/makeshell.c */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>