#define OPTPROC_L_N_S (OPTPROC_LONGOPT | OPTPROC_SHORTOPT)
static arg_types_t argTypes;
FILE* option_usage_fp = NULL;
static char zOptFmtLine[ 16 ];
static ag_bool displayEnum;
static ag_bool
checkGNUUsage( tOptions* pOpts );
static void
printExtendedUsage(
tOptions* pOptions,
tOptDesc* pOD,
arg_types_t* pAT );
static void
printInitList(
tCC* const* papz,
ag_bool* pInitIntro,
tCC* pzRc,
tCC* pzPN );
static void
printOneUsage(
tOptions* pOptions,
tOptDesc* pOD,
arg_types_t* pAT );
static void
printOptionUsage(
tOptions* pOpts,
int ex_code,
tCC* pOptTitle );
static void
printProgramDetails( tOptions* pOptions );
static int
setGnuOptFmts( tOptions* pOpts, tCC** ppT );
static int
setStdOptFmts( tOptions* pOpts, tCC** ppT );
static ag_bool
checkGNUUsage( tOptions* pOpts )
{
char* pz = getenv( "AUTOOPTS_USAGE" );
if (pz == NULL)
;
else if (streqvcmp( pz, "gnu" ) == 0)
pOpts->fOptSet |= OPTPROC_GNUUSAGE;
else if (streqvcmp( pz, "autoopts" ) == 0)
pOpts->fOptSet &= ~OPTPROC_GNUUSAGE;
return (pOpts->fOptSet & OPTPROC_GNUUSAGE) ? AG_TRUE : AG_FALSE;
}
void
optionOnlyUsage(
tOptions* pOpts,
int ex_code )
{
tCC* pOptTitle = NULL;
if (checkGNUUsage(pOpts)) {
(void)setGnuOptFmts( pOpts, &pOptTitle );
}
else {
(void)setStdOptFmts( pOpts, &pOptTitle );
}
printOptionUsage( pOpts, ex_code, pOptTitle );
}
void
optionUsage(
tOptions* pOptions,
int usage_exit_code )
{
int actual_exit_code =
(usage_exit_code == EX_USAGE) ? EXIT_SUCCESS : usage_exit_code;
displayEnum = AG_FALSE;
if (option_usage_fp == NULL)
option_usage_fp = (actual_exit_code != EXIT_SUCCESS) ? stderr : stdout;
fprintf( option_usage_fp, pOptions->pzUsageTitle, pOptions->pzProgName );
{
tCC* pOptTitle = NULL;
if (checkGNUUsage(pOptions)) {
int flen = setGnuOptFmts( pOptions, &pOptTitle );
sprintf( zOptFmtLine, zFmtFmt, flen );
fputc( '\n', option_usage_fp );
}
else {
int flen = setStdOptFmts( pOptions, &pOptTitle );
sprintf( zOptFmtLine, zFmtFmt, flen );
if ( (usage_exit_code != EXIT_SUCCESS)
|| ((pOptions->pOptDesc->fOptState & OPTST_DOCUMENT) == 0) )
fputs( pOptTitle, option_usage_fp );
}
printOptionUsage( pOptions, usage_exit_code, pOptTitle );
}
switch (pOptions->fOptSet & OPTPROC_L_N_S) {
case OPTPROC_L_N_S: fputs( zFlagOkay, option_usage_fp ); break;
case OPTPROC_SHORTOPT: break;
case OPTPROC_LONGOPT: fputs( zNoFlags, option_usage_fp ); break;
case 0: fputs( zOptsOnly, option_usage_fp ); break;
}
if ((pOptions->fOptSet & OPTPROC_NUM_OPT) != 0) {
fputs( zNumberOpt, option_usage_fp );
}
if ((pOptions->fOptSet & OPTPROC_REORDER) != 0) {
fputs( zReorder, option_usage_fp );
}
if (pOptions->pzExplain != NULL)
fputs( pOptions->pzExplain, option_usage_fp );
if (usage_exit_code == EXIT_SUCCESS)
printProgramDetails( pOptions );
if (pOptions->pzBugAddr != NULL)
fprintf( option_usage_fp, zPlsSendBugs, pOptions->pzBugAddr );
fflush( option_usage_fp );
exit( actual_exit_code );
}
static void
printExtendedUsage(
tOptions* pOptions,
tOptDesc* pOD,
arg_types_t* pAT )
{
if ( (pOD->pOptMust != NULL)
|| (pOD->pOptCant != NULL) ) {
fputs( zTabHyp, option_usage_fp );
if (pOD->pOptMust != NULL) {
const int* pOptNo = pOD->pOptMust;
fputs( zReqThese, option_usage_fp );
for (;;) {
fprintf( option_usage_fp, zTabout, pOptions->pOptDesc[
*pOptNo ].pz_Name );
if (*++pOptNo == NO_EQUIVALENT)
break;
}
if (pOD->pOptCant != NULL)
fputs( zTabHypAnd, option_usage_fp );
}
if (pOD->pOptCant != NULL) {
const int* pOptNo = pOD->pOptCant;
fputs( zProhib, option_usage_fp );
for (;;) {
fprintf( option_usage_fp, zTabout, pOptions->pOptDesc[
*pOptNo ].pz_Name );
if (*++pOptNo == NO_EQUIVALENT)
break;
}
}
}
if (pOD->pz_DisableName != NULL )
fprintf( option_usage_fp, zDis, pOD->pz_DisableName );
if ( (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_NUMERIC)
&& (pOD->pOptProc != NULL)
&& (pOD->pOptProc != optionNumericVal) ) {
(*(pOD->pOptProc))( pOptions, NULL );
}
if (pOD->fOptState & OPTST_INITENABLED)
fputs( zEnab, option_usage_fp );
if ( (pOD->optEquivIndex != NO_EQUIVALENT)
&& (pOD->optEquivIndex != pOD->optActualIndex ) ) {
fprintf( option_usage_fp, zAlt,
pOptions->pOptDesc[ pOD->optEquivIndex ].pz_Name );
return;
}
if ( ((pOD->fOptState & OPTST_NO_INIT) != 0)
&& ( (pOptions->papzHomeList != NULL)
|| (pOptions->pzPROGNAME != NULL)
)
&& (pOD->optIndex < pOptions->presetOptCt)
)
fputs( zNoPreset, option_usage_fp );
if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_MEMBERSHIP)
fputs( zMembers, option_usage_fp );
else switch (pOD->optMinCt) {
case 1:
case 0:
switch (pOD->optMaxCt) {
case 0: fputs( zPreset, option_usage_fp ); break;
case NOLIMIT: fputs( zNoLim, option_usage_fp ); break;
case 1: break;
default: fprintf( option_usage_fp, zUpTo, pOD->optMaxCt ); break;
}
break;
default:
fprintf( option_usage_fp, zMust, pOD->optMinCt, pOD->optMaxCt );
}
if ( NAMED_OPTS( pOptions )
&& (pOptions->specOptIdx.default_opt == pOD->optIndex))
fputs( zDefaultOpt, option_usage_fp );
}
static void
printInitList(
tCC* const* papz,
ag_bool* pInitIntro,
tCC* pzRc,
tCC* pzPN )
{
char zPath[ AG_PATH_MAX+1 ];
if (papz == NULL)
return;
fputs( zPresetIntro, option_usage_fp );
*pInitIntro = AG_FALSE;
for (;;) {
char const* pzPath = *(papz++);
if (pzPath == NULL)
break;
if (optionMakePath(zPath, (int)sizeof( zPath ), pzPath, pzPN))
pzPath = zPath;
fprintf( option_usage_fp, zPathFmt, pzPath );
if (*pzRc != NUL) {
struct stat sb;
if ( (stat( pzPath, &sb ) == 0)
&& S_ISDIR( sb.st_mode ) ) {
fputc( DIRCH, option_usage_fp );
fputs( pzRc, option_usage_fp );
}
}
fputc( '\n', option_usage_fp );
}
}
static void
printOneUsage(
tOptions* pOptions,
tOptDesc* pOD,
arg_types_t* pAT )
{
if ((pOptions->fOptSet & OPTPROC_SHORTOPT) == 0)
fputs( pAT->pzSpc, option_usage_fp );
else if (! isgraph( pOD->optValue)) {
if ( (pOptions->fOptSet & (OPTPROC_GNUUSAGE|OPTPROC_LONGOPT))
== (OPTPROC_GNUUSAGE|OPTPROC_LONGOPT))
fputc( ' ', option_usage_fp );
fputs( pAT->pzNoF, option_usage_fp );
} else {
fprintf( option_usage_fp, " -%c", pOD->optValue );
if ( (pOptions->fOptSet & (OPTPROC_GNUUSAGE|OPTPROC_LONGOPT))
== (OPTPROC_GNUUSAGE|OPTPROC_LONGOPT))
fputs( ", ", option_usage_fp );
}
{
char z[ 80 ];
tCC* pzArgType;
if (OPTST_GET_ARGTYPE(pOD->fOptState) == OPARG_TYPE_NONE) {
pzArgType = pAT->pzNo;
} else if (pOD->fOptState & OPTST_ARG_OPTIONAL) {
pzArgType = pAT->pzOpt;
} else switch (OPTST_GET_ARGTYPE(pOD->fOptState)) {
case OPARG_TYPE_ENUMERATION: pzArgType = pAT->pzKey; break;
case OPARG_TYPE_MEMBERSHIP: pzArgType = pAT->pzKeyL; break;
case OPARG_TYPE_BOOLEAN: pzArgType = pAT->pzBool; break;
case OPARG_TYPE_NUMERIC: pzArgType = pAT->pzNum; break;
case OPARG_TYPE_HIERARCHY: pzArgType = pAT->pzNest; break;
case OPARG_TYPE_STRING: pzArgType = pAT->pzStr; break;
default: goto bogus_desc; break;
}
snprintf( z, sizeof(z), pAT->pzOptFmt, pzArgType, pOD->pz_Name,
(pOD->optMinCt != 0) ? pAT->pzReq : pAT->pzOpt );
fprintf( option_usage_fp, zOptFmtLine, z, pOD->pzText );
switch (OPTST_GET_ARGTYPE(pOD->fOptState)) {
case OPARG_TYPE_ENUMERATION:
case OPARG_TYPE_MEMBERSHIP:
displayEnum = (pOD->pOptProc != NULL) ? AG_TRUE : displayEnum;
}
}
return;
bogus_desc:
fprintf( stderr, zInvalOptDesc, pOD->pz_Name );
exit( EX_SOFTWARE );
}
static void
printOptionUsage(
tOptions* pOpts,
int ex_code,
tCC* pOptTitle )
{
int ct = pOpts->optCt;
int optNo = 0;
tOptDesc* pOD = pOpts->pOptDesc;
int docCt = 0;
do {
if ((pOD->fOptState & OPTST_OMITTED) != 0)
continue;
if ((pOD->fOptState & OPTST_DOCUMENT) != 0) {
if (ex_code == EXIT_SUCCESS) {
fprintf(option_usage_fp, argTypes.pzBrk, pOD->pzText,
pOptTitle);
docCt++;
}
continue;
}
if ( (pOpts->presetOptCt == optNo)
&& (ex_code == EXIT_SUCCESS)
&& (docCt > 0)
&& ((pOD[-1].fOptState & OPTST_DOCUMENT) == 0) )
fprintf( option_usage_fp, argTypes.pzBrk, zAuto, pOptTitle );
printOneUsage( pOpts, pOD, &argTypes );
if (ex_code == EXIT_SUCCESS)
printExtendedUsage( pOpts, pOD, &argTypes );
} while (pOD++, optNo++, (--ct > 0));
fputc( '\n', option_usage_fp );
}
static void
printProgramDetails( tOptions* pOptions )
{
ag_bool initIntro = AG_TRUE;
printInitList( pOptions->papzHomeList, &initIntro,
pOptions->pzRcName, pOptions->pzProgPath );
if ((pOptions->fOptSet & OPTPROC_ENVIRON) != 0) {
if (initIntro)
fputs( zPresetIntro, option_usage_fp );
fprintf( option_usage_fp, zExamineFmt, pOptions->pzPROGNAME );
}
if (displayEnum) {
int ct = pOptions->optCt;
int optNo = 0;
tOptDesc* pOD = pOptions->pOptDesc;
fputc( '\n', option_usage_fp );
fflush( option_usage_fp );
do {
switch (OPTST_GET_ARGTYPE(pOD->fOptState)) {
case OPARG_TYPE_ENUMERATION:
case OPARG_TYPE_MEMBERSHIP:
(*(pOD->pOptProc))( NULL, pOD );
}
} while (pOD++, optNo++, (--ct > 0));
}
if (pOptions->pzDetail != NULL)
fputs( pOptions->pzDetail, option_usage_fp );
}
static int
setGnuOptFmts( tOptions* pOpts, tCC** ppT )
{
int flen = 22;
*ppT = zNoRq_ShrtTtl;
argTypes.pzStr = zGnuStrArg;
argTypes.pzReq = zOneSpace;
argTypes.pzNum = zGnuNumArg;
argTypes.pzKey = zGnuKeyArg;
argTypes.pzKeyL = zGnuKeyLArg;
argTypes.pzBool = zGnuBoolArg;
argTypes.pzNest = zGnuNestArg;
argTypes.pzOpt = zGnuOptArg;
argTypes.pzNo = zOneSpace;
argTypes.pzBrk = zGnuBreak;
argTypes.pzNoF = zSixSpaces;
argTypes.pzSpc = zThreeSpaces;
switch (pOpts->fOptSet & OPTPROC_L_N_S) {
case OPTPROC_L_N_S: argTypes.pzOptFmt = zGnuOptFmt; break;
case OPTPROC_LONGOPT: argTypes.pzOptFmt = zGnuOptFmt; break;
case 0: argTypes.pzOptFmt = zGnuOptFmt + 2; break;
case OPTPROC_SHORTOPT:
argTypes.pzOptFmt = zShrtGnuOptFmt;
zGnuStrArg[0] = zGnuNumArg[0] = zGnuKeyArg[0] = zGnuBoolArg[0] = ' ';
argTypes.pzOpt = " [arg]";
flen = 8;
break;
}
return flen;
}
static int
setStdOptFmts( tOptions* pOpts, tCC** ppT )
{
int flen = 0;
argTypes.pzStr = zStdStrArg;
argTypes.pzReq = zStdReqArg;
argTypes.pzNum = zStdNumArg;
argTypes.pzKey = zStdKeyArg;
argTypes.pzKeyL = zStdKeyLArg;
argTypes.pzBool = zStdBoolArg;
argTypes.pzNest = zStdNestArg;
argTypes.pzOpt = zStdOptArg;
argTypes.pzNo = zStdNoArg;
argTypes.pzBrk = zStdBreak;
argTypes.pzNoF = zFiveSpaces;
argTypes.pzSpc = zTwoSpaces;
switch (pOpts->fOptSet & (OPTPROC_NO_REQ_OPT | OPTPROC_SHORTOPT)) {
case (OPTPROC_NO_REQ_OPT | OPTPROC_SHORTOPT):
*ppT = zNoRq_ShrtTtl;
argTypes.pzOptFmt = zNrmOptFmt;
flen = 19;
break;
case OPTPROC_NO_REQ_OPT:
*ppT = zNoRq_NoShrtTtl;
argTypes.pzOptFmt = zNrmOptFmt;
flen = 19;
break;
case OPTPROC_SHORTOPT:
*ppT = zReq_ShrtTtl;
argTypes.pzOptFmt = zReqOptFmt;
flen = 24;
break;
case 0:
*ppT = zReq_NoShrtTtl;
argTypes.pzOptFmt = zReqOptFmt;
flen = 24;
}
return flen;
}