static inline unsigned char to_uchar (char ch) { return ch; }
#define UPPER(_c) (toupper(to_uchar(_c)))
#define LOWER(_c) (tolower(to_uchar(_c)))
static void
emit_var_text(char const * prog, char const * var, int fdin);
static void
text_to_var(tOptions * opts, teTextTo which, tOptDesc * od);
static void
emit_usage(tOptions * opts);
static void
emit_wrapup(tOptions * opts);
static void
emit_setup(tOptions * opts);
static void
emit_action(tOptions * opts, tOptDesc * od);
static void
emit_inaction(tOptions * opts, tOptDesc * od);
static void
emit_flag(tOptions * opts);
static void
emit_match_expr(char const * name, tOptDesc * cod, tOptions * opts);
static void
emit_long(tOptions * opts);
static char *
load_old_output(char const * fname, char const * pname);
static void
open_out(char const * fname, char const * pname);
LOCAL noreturn void
option_exits(int exit_code)
{
if (print_exit)
printf("\nexit %d\n", exit_code);
exit(exit_code);
}
LOCAL noreturn void
ao_bug(char const * msg)
{
fprintf(stderr, zao_bug_msg, msg);
option_exits(EX_SOFTWARE);
}
LOCAL void
fserr_warn(char const * prog, char const * op, char const * fname)
{
fprintf(stderr, zfserr_fmt, prog, errno, strerror(errno),
op, fname);
}
LOCAL noreturn void
fserr_exit(char const * prog, char const * op, char const * fname)
{
fserr_warn(prog, op, fname);
option_exits(EXIT_FAILURE);
}
void
optionParseShell(tOptions * opts)
{
if (HAVE_GENSHELL_OPT(SHELL))
shell_prog = GENSHELL_OPT_ARG(SHELL);
else if (! ENABLED_GENSHELL_OPT(SHELL))
shell_prog = NULL;
else if ((shell_prog = getenv("SHELL")),
shell_prog == NULL)
shell_prog = POSIX_SHELL;
if (HAVE_GENSHELL_OPT(SCRIPT))
open_out(GENSHELL_OPT_ARG(SCRIPT), opts->pzProgName);
emit_usage(opts);
emit_setup(opts);
switch (opts->fOptSet & (OPTPROC_LONGOPT|OPTPROC_SHORTOPT)) {
case OPTPROC_LONGOPT:
fputs(LOOP_STR, stdout);
fputs(LONG_OPT_MARK, stdout);
fputs(INIT_LOPT_STR, stdout);
emit_long(opts);
printf(LOPT_ARG_FMT, opts->pzPROGNAME);
fputs(END_OPT_SEL_STR, stdout);
fputs(NOT_FOUND_STR, stdout);
break;
case 0:
fputs(ONLY_OPTS_LOOP, stdout);
fputs(INIT_LOPT_STR, stdout);
emit_long(opts);
printf(LOPT_ARG_FMT, opts->pzPROGNAME);
break;
case OPTPROC_SHORTOPT:
fputs(LOOP_STR, stdout);
fputs(FLAG_OPT_MARK, stdout);
fputs(INIT_OPT_STR, stdout);
emit_flag(opts);
printf(OPT_ARG_FMT, opts->pzPROGNAME);
fputs(END_OPT_SEL_STR, stdout);
fputs(NOT_FOUND_STR, stdout);
break;
case OPTPROC_LONGOPT|OPTPROC_SHORTOPT:
fputs(LOOP_STR, stdout);
fputs(LONG_OPT_MARK, stdout);
fputs(INIT_LOPT_STR, stdout);
emit_long(opts);
printf(LOPT_ARG_FMT, opts->pzPROGNAME);
fputs(END_OPT_SEL_STR, stdout);
fputs(FLAG_OPT_MARK, stdout);
fputs(INIT_OPT_STR, stdout);
emit_flag(opts);
printf(OPT_ARG_FMT, opts->pzPROGNAME);
fputs(END_OPT_SEL_STR, stdout);
fputs(NOT_FOUND_STR, stdout);
break;
}
emit_wrapup(opts);
if ((script_trailer != NULL) && (*script_trailer != NUL))
fputs(script_trailer, stdout);
else if (ENABLED_GENSHELL_OPT(SHELL))
printf(SHOW_PROG_ENV, opts->pzPROGNAME);
#ifdef HAVE_FCHMOD
fchmod(STDOUT_FILENO, 0755);
#endif
fclose(stdout);
if (ferror(stdout))
fserr_exit(opts->pzProgName, zwriting, zstdout_name);
AGFREE(script_text);
script_leader = NULL;
script_trailer = NULL;
script_text = NULL;
}
#ifdef HAVE_WORKING_FORK
static void
emit_var_text(char const * prog, char const * var, int fdin)
{
FILE * fp = fdopen(fdin, "r" FOPEN_BINARY_FLAG);
int nlct = 0;
printf(SET_TEXT_FMT, prog, var);
if (fp == NULL)
goto skip_text;
for (;;) {
int ch = fgetc(fp);
switch (ch) {
case NL:
nlct++;
break;
case '\'':
while (nlct > 0) {
fputc(NL, stdout);
nlct--;
}
fputs(apostrophe, stdout);
break;
case EOF:
goto done;
default:
while (nlct > 0) {
fputc(NL, stdout);
nlct--;
}
fputc(ch, stdout);
break;
}
} done:;
fclose(fp);
skip_text:
fputs(END_SET_TEXT, stdout);
}
#endif
static void
text_to_var(tOptions * opts, teTextTo which, tOptDesc * od)
{
# define _TT_(n) static char const z ## n [] = #n;
TEXTTO_TABLE
# undef _TT_
# define _TT_(n) z ## n ,
static char const * ttnames[] = { TEXTTO_TABLE };
# undef _TT_
#if ! defined(HAVE_WORKING_FORK)
printf(SET_NO_TEXT_FMT, opts->pzPROGNAME, ttnames[which]);
#else
int fdpair[2];
fflush(stdout);
fflush(stderr);
if (pipe(fdpair) != 0)
fserr_exit(opts->pzProgName, "pipe", zinter_proc_pipe);
switch (fork()) {
case -1:
fserr_exit(opts->pzProgName, "fork", opts->pzProgName);
case 0:
dup2(fdpair[1], STDERR_FILENO);
dup2(fdpair[1], STDOUT_FILENO);
close(fdpair[0]);
switch (which) {
case TT_LONGUSAGE:
(*(opts->pUsageProc))(opts, EXIT_SUCCESS);
case TT_USAGE:
(*(opts->pUsageProc))(opts, EXIT_FAILURE);
case TT_VERSION:
if (od->fOptState & OPTST_ALLOC_ARG) {
AGFREE(od->optArg.argString);
od->fOptState &= ~OPTST_ALLOC_ARG;
}
od->optArg.argString = "c";
optionPrintVersion(opts, od);
default:
option_exits(EXIT_FAILURE);
}
default:
close(fdpair[1]);
}
emit_var_text(opts->pzPROGNAME, ttnames[which], fdpair[0]);
#endif
}
static void
emit_usage(tOptions * opts)
{
char tm_nm_buf[AO_NAME_SIZE];
if (script_leader != NULL)
fputs(script_leader, stdout);
{
char const * out_nm;
{
time_t c_tim = time(NULL);
struct tm * ptm = localtime(&c_tim);
strftime(tm_nm_buf, AO_NAME_SIZE, TIME_FMT, ptm );
}
if (HAVE_GENSHELL_OPT(SCRIPT))
out_nm = GENSHELL_OPT_ARG(SCRIPT);
else out_nm = STDOUT;
if ((script_leader == NULL) && (shell_prog != NULL))
printf(SHELL_MAGIC, shell_prog);
printf(PREAMBLE_FMT, START_MARK, out_nm, tm_nm_buf);
}
printf(END_PRE_FMT, opts->pzPROGNAME);
{
char * pzPN = tm_nm_buf;
char const * pz = opts->pzPROGNAME;
char ** pp;
for (;;) {
if ((*pzPN++ = (char)tolower((unsigned char)*pz++)) == NUL)
break;
}
pp = VOIDP(&(opts->pzProgPath));
*pp = tm_nm_buf;
pp = VOIDP(&(opts->pzProgName));
*pp = tm_nm_buf;
}
text_to_var(opts, TT_LONGUSAGE, NULL);
text_to_var(opts, TT_USAGE, NULL);
{
tOptDesc * pOptDesc = opts->pOptDesc;
int optionCt = opts->optCt;
for (;;) {
if (pOptDesc->pOptProc == optionPrintVersion) {
text_to_var(opts, TT_VERSION, pOptDesc);
break;
}
if (--optionCt <= 0)
break;
pOptDesc++;
}
}
}
static void
emit_wrapup(tOptions * opts)
{
tOptDesc * od = opts->pOptDesc;
int opt_ct = opts->presetOptCt;
char const * fmt;
printf(FINISH_LOOP, opts->pzPROGNAME);
for (;opt_ct > 0; od++, --opt_ct) {
if (SKIP_OPT(od) || (od->pz_NAME == NULL))
continue;
if ((od->optMinCt == 0) && ((od->fOptState & OPTST_MUST_SET) == 0))
continue;
if (od->optMaxCt > 1)
fmt = CHK_MIN_COUNT;
else fmt = CHK_ONE_REQUIRED;
{
int min = (od->optMinCt == 0) ? 1 : od->optMinCt;
printf(fmt, opts->pzPROGNAME, od->pz_NAME, min);
}
}
fputs(END_MARK, stdout);
}
static void
emit_setup(tOptions * opts)
{
tOptDesc * od = opts->pOptDesc;
int opt_ct = opts->presetOptCt;
char const * fmt;
char const * def_val;
for (;opt_ct > 0; od++, --opt_ct) {
char int_val_buf[32];
if (SKIP_OPT(od) || (od->pz_NAME == NULL))
continue;
if (od->optMaxCt > 1)
fmt = MULTI_DEF_FMT;
else fmt = SGL_DEF_FMT;
switch (OPTST_GET_ARGTYPE(od->fOptState)) {
case OPARG_TYPE_ENUMERATION:
(*(od->pOptProc))(OPTPROC_EMIT_SHELL, od );
def_val = od->optArg.argString;
break;
case OPARG_TYPE_NUMERIC:
snprintf(int_val_buf, sizeof(int_val_buf), "%d",
(int)od->optArg.argInt);
def_val = int_val_buf;
break;
case OPARG_TYPE_MEMBERSHIP:
snprintf(int_val_buf, sizeof(int_val_buf), "%lu",
(unsigned long)od->optArg.argIntptr);
def_val = int_val_buf;
break;
case OPARG_TYPE_BOOLEAN:
def_val = (od->optArg.argBool) ? TRUE_STR : FALSE_STR;
break;
default:
if (od->optArg.argString == NULL) {
if (fmt == SGL_DEF_FMT)
fmt = SGL_NO_DEF_FMT;
def_val = NULL;
}
else
def_val = od->optArg.argString;
}
printf(fmt, opts->pzPROGNAME, od->pz_NAME, def_val);
}
}
static void
emit_action(tOptions * opts, tOptDesc * od)
{
if (od->pOptProc == optionPrintVersion)
printf(ECHO_N_EXIT, opts->pzPROGNAME, VER_STR);
else if (od->pOptProc == optionPagedUsage)
printf(PAGE_USAGE_TEXT, opts->pzPROGNAME);
else if (od->pOptProc == optionLoadOpt) {
printf(LVL3_CMD, NO_LOAD_WARN);
printf(LVL3_CMD, YES_NEED_OPT_ARG);
} else if (od->pz_NAME == NULL) {
if (od->pOptProc == NULL) {
printf(LVL3_CMD, NO_SAVE_OPTS);
printf(LVL3_CMD, OK_NEED_OPT_ARG);
} else
printf(ECHO_N_EXIT, opts->pzPROGNAME, LONG_USE_STR);
} else {
if (od->optMaxCt == 1)
printf(SGL_ARG_FMT, opts->pzPROGNAME, od->pz_NAME);
else {
if ((unsigned)od->optMaxCt < NOLIMIT)
printf(CHK_MAX_COUNT, opts->pzPROGNAME,
od->pz_NAME, od->optMaxCt);
printf(MULTI_ARG_FMT, opts->pzPROGNAME, od->pz_NAME);
}
if (OPTST_GET_ARGTYPE(od->fOptState) == OPARG_TYPE_NONE) {
printf(SET_MULTI_ARG, opts->pzPROGNAME, od->pz_NAME);
printf(LVL3_CMD, NO_ARG_NEEDED);
} else if (od->fOptState & OPTST_ARG_OPTIONAL) {
printf(SET_MULTI_ARG, opts->pzPROGNAME, od->pz_NAME);
printf(LVL3_CMD, OK_NEED_OPT_ARG);
} else {
printf(LVL3_CMD, YES_NEED_OPT_ARG);
}
}
fputs(zOptionEndSelect, stdout);
}
static void
emit_inaction(tOptions * opts, tOptDesc * od)
{
if (od->pOptProc == optionLoadOpt) {
printf(LVL3_CMD, NO_SUPPRESS_LOAD);
} else if (od->optMaxCt == 1)
printf(NO_SGL_ARG_FMT, opts->pzPROGNAME,
od->pz_NAME, od->pz_DisablePfx);
else
printf(NO_MULTI_ARG_FMT, opts->pzPROGNAME,
od->pz_NAME, od->pz_DisablePfx);
printf(LVL3_CMD, NO_ARG_NEEDED);
fputs(zOptionEndSelect, stdout);
}
static void
emit_flag(tOptions * opts)
{
tOptDesc * od = opts->pOptDesc;
int opt_ct = opts->optCt;
fputs(zOptionCase, stdout);
for (;opt_ct > 0; od++, --opt_ct) {
if (SKIP_OPT(od) || ! IS_GRAPHIC_CHAR(od->optValue))
continue;
printf(zOptionFlag, od->optValue);
emit_action(opts, od);
}
printf(UNK_OPT_FMT, FLAG_STR, opts->pzPROGNAME);
}
static void
emit_match_expr(char const * name, tOptDesc * cod, tOptions * opts)
{
char name_bf[32];
unsigned int min_match_ct = 2;
unsigned int max_match_ct = strlen(name) - 1;
if (max_match_ct >= sizeof(name_bf) - 1)
goto leave;
{
tOptDesc * od = opts->pOptDesc;
int ct = opts->optCt;
for (; ct-- > 0; od++) {
unsigned int match_ct = 0;
if ((od == cod) || SKIP_OPT(od))
continue;
while (UPPER(od->pz_Name[match_ct]) == UPPER(name[match_ct]))
match_ct++;
if (match_ct > min_match_ct)
min_match_ct = match_ct;
if (od->pz_DisableName == NULL)
continue;
match_ct = 0;
while ( toupper((unsigned char)od->pz_DisableName[match_ct])
== toupper((unsigned char)name[match_ct]))
match_ct++;
if (match_ct > min_match_ct)
min_match_ct = match_ct;
}
}
if (min_match_ct < max_match_ct) {
char * pz = name_bf + min_match_ct;
int nm_ix = min_match_ct;
memcpy(name_bf, name, min_match_ct);
for (;;) {
*pz = NUL;
printf(zOptionPartName, name_bf);
*pz++ = name[nm_ix++];
if (name[nm_ix] == NUL) {
*pz = NUL;
break;
}
}
}
leave:
printf(zOptionFullName, name);
}
static void
emit_long(tOptions * opts)
{
tOptDesc * od = opts->pOptDesc;
int ct = opts->optCt;
fputs(zOptionCase, stdout);
do {
if (SKIP_OPT(od))
continue;
emit_match_expr(od->pz_Name, od, opts);
emit_action(opts, od);
if (od->pz_DisableName != NULL) {
emit_match_expr(od->pz_DisableName, od, opts);
emit_inaction(opts, od);
}
} while (od++, --ct > 0);
printf(UNK_OPT_FMT, OPTION_STR, opts->pzPROGNAME);
}
static char *
load_old_output(char const * fname, char const * pname)
{
FILE * fp = fopen(fname, "r" FOPEN_BINARY_FLAG);
struct stat stbf;
char * text;
char * scan;
if (fp == NULL)
return NULL;
if ((fstat(fileno(fp), &stbf) != 0) || (! S_ISREG(stbf.st_mode)))
fserr_exit(pname, "fstat", fname);
scan = text = AGALOC(stbf.st_size + 1, "f data");
for (;;) {
size_t inct = fread(VOIDP(scan), 1, (size_t)stbf.st_size, fp);
if (inct == 0)
break;
stbf.st_size -= (ssize_t)inct;
if (stbf.st_size == 0)
break;
scan += inct;
}
*scan = NUL;
fclose(fp);
return text;
}
static void
open_out(char const * fname, char const * pname)
{
do {
char * txt = script_text = load_old_output(fname, pname);
char * scn;
if (txt == NULL)
break;
scn = strstr(txt, START_MARK);
if (scn == NULL) {
script_trailer = txt;
break;
}
*(scn++) = NUL;
scn = strstr(scn, END_MARK);
if (scn == NULL) {
script_trailer = txt + strlen(txt) + START_MARK_LEN + 1;
break;
}
script_trailer = scn + END_MARK_LEN;
script_leader = txt;
} while (false);
if (freopen(fname, "w" FOPEN_BINARY_FLAG, stdout) != stdout)
fserr_exit(pname, "freopen", fname);
}
void
genshelloptUsage(tOptions * opts, int exit_cd)
{
#if ! defined(HAVE_WORKING_FORK)
optionUsage(opts, exit_cd);
#else
if (exit_cd != EXIT_SUCCESS)
optionUsage(opts, exit_cd);
fflush(stderr);
fflush(stdout);
if (ferror(stdout) || ferror(stderr))
option_exits(EXIT_FAILURE);
option_usage_fp = stdout;
switch (fork()) {
case -1:
optionUsage(opts, EXIT_FAILURE);
case 0:
pagerState = PAGER_STATE_CHILD;
optionUsage(opts, EXIT_SUCCESS);
_exit(EXIT_FAILURE);
default:
{
int sts;
wait(&sts);
}
}
{
char * pz;
char ** pp = VOIDP(&(optionParseShellOptions->pzProgName));
AGDUPSTR(pz, optionParseShellOptions->pzPROGNAME, "prog name");
*pp = pz;
while (*pz != NUL) {
*pz = (char)LOWER(*pz);
pz++;
}
}
fprintf(option_usage_fp, zGenshell, optionParseShellOptions->pzProgName);
fflush(option_usage_fp);
switch (fork()) {
case 0:
pagerState = PAGER_STATE_CHILD;
case -1:
optionUsage(optionParseShellOptions, EXIT_FAILURE);
default:
{
int sts;
wait(&sts);
}
}
fflush(stdout);
if (ferror(stdout))
fserr_exit(opts->pzProgName, zwriting, zstdout_name);
option_exits(EXIT_SUCCESS);
#endif
}