1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285 |
- /*
- * \file usage.c
- *
- * This module implements the default usage procedure for
- * Automated Options. It may be overridden, of course.
- *
- * @addtogroup autoopts
- * @{
- */
- /*
- * Sort options:
- --start=END-[S]TATIC-FORWARD --patt='^/\*($|[^:])' \
- --out=xx.c key='^[a-zA-Z0-9_]+\(' --trail='^/\*:' \
- --spac=2 --input=usage.c
- */
- /*
- * This file is part of AutoOpts, a companion to AutoGen.
- * AutoOpts is free software.
- * AutoOpts is Copyright (C) 1992-2018 by Bruce Korb - all rights reserved
- *
- * AutoOpts is available under any one of two licenses. The license
- * in use must be one of these two and the choice is under the control
- * of the user of the license.
- *
- * The GNU Lesser General Public License, version 3 or later
- * See the files "COPYING.lgplv3" and "COPYING.gplv3"
- *
- * The Modified Berkeley Software Distribution License
- * See the file "COPYING.mbsd"
- *
- * These files have the following sha256 sums:
- *
- * 8584710e9b04216a394078dc156b781d0b47e1729104d666658aecef8ee32e95 COPYING.gplv3
- * 4379e7444a0e2ce2b12dd6f5a52a27a4d02d39d247901d3285c88cf0d37f477b COPYING.lgplv3
- * 13aa749a5b0a454917a944ed8fffc530b784f5ead522b1aacaf4ec8aa55a6239 COPYING.mbsd
- */
- #define GRAPH_CH(_ch) \
- ((((unsigned)_ch) <= 0x7E) && (((unsigned)_ch) > ' '))
- /**
- * Parse the option usage flags string. Any parsing problems yield
- * a zero (no flags set) result. This function is internal to
- * set_usage_flags().
- *
- * @param[in] fnt Flag Name Table - maps a name to a mask
- * @param[in] txt the text to process. If NULL, then
- * getenv("AUTOOPTS_USAGE") is used.
- * @returns a bit mask indicating which \a fnt entries were found.
- */
- static unsigned int
- parse_usage_flags(ao_flag_names_t const * fnt, char const * txt)
- {
- unsigned int res = 0;
- /*
- * The text may be passed in. If not, use the environment variable.
- */
- if (txt == NULL) {
- txt = getenv("AUTOOPTS_USAGE");
- if (txt == NULL)
- return 0;
- }
- txt = SPN_WHITESPACE_CHARS(txt);
- if (*txt == NUL)
- return 0;
- /*
- * search the string for table entries. We must understand everything
- * we see in the string, or we give up on it.
- */
- for (;;) {
- int ix = 0;
- for (;;) {
- if (strneqvcmp(txt, fnt[ix].fnm_name, (int)fnt[ix].fnm_len) == 0)
- break;
- if (++ix >= AOUF_COUNT)
- return 0;
- }
- /*
- * Make sure we have a full match. Look for whitespace,
- * a comma, or a NUL byte.
- */
- if (! IS_END_LIST_ENTRY_CHAR(txt[fnt[ix].fnm_len]))
- return 0;
- res |= 1U << ix;
- txt = SPN_WHITESPACE_CHARS(txt + fnt[ix].fnm_len);
- switch (*txt) {
- case NUL:
- return res;
- case ',':
- txt = SPN_WHITESPACE_CHARS(txt + 1);
- /* Something must follow the comma */
- /* FALLTHROUGH */
- default:
- continue;
- }
- }
- }
- /**
- * Set option usage flags. Any parsing problems yield no changes to options.
- * Three different bits may be fiddled: \a OPTPROC_GNUUSAGE, \a OPTPROC_MISUSE
- * and \a OPTPROC_COMPUTE.
- *
- * @param[in] flg_txt text to parse. If NULL, then the AUTOOPTS_USAGE
- * environment variable is parsed.
- * @param[in,out] opts the program option descriptor
- */
- static void
- set_usage_flags(tOptions * opts, char const * flg_txt)
- {
- # define _aof_(_n, _f) { sizeof(#_n)-1, _f, #_n },
- static ao_flag_names_t const fn_table[AOUF_COUNT] = {
- AOFLAG_TABLE
- };
- # undef _aof_
- /*
- * the flag word holds a bit for each selected table entry.
- */
- unsigned int flg = parse_usage_flags(fn_table, flg_txt);
- if (flg == 0) return;
- /*
- * Ensure we do not have conflicting selections
- */
- {
- static unsigned int const form_mask =
- AOUF_gnu | AOUF_autoopts;
- static unsigned int const misuse_mask =
- AOUF_no_misuse_usage | AOUF_misuse_usage;
- if ( ((flg & form_mask) == form_mask)
- || ((flg & misuse_mask) == misuse_mask) )
- return;
- }
- /*
- * Now fiddle the fOptSet bits, based on settings.
- * The OPTPROC_LONGOPT bit is immutable, thus if it is set,
- * then fnm points to a mask off mask.
- */
- {
- ao_flag_names_t const * fnm = fn_table;
- for (;;) {
- if ((flg & 1) != 0) {
- if ((fnm->fnm_mask & OPTPROC_LONGOPT) != 0)
- opts->fOptSet &= fnm->fnm_mask;
- else opts->fOptSet |= fnm->fnm_mask;
- }
- flg >>= 1;
- if (flg == 0)
- break;
- fnm++;
- }
- }
- }
- /*
- * Figure out if we should try to format usage text sort-of like
- * the way many GNU programs do.
- */
- static inline bool
- do_gnu_usage(tOptions * pOpts)
- {
- return (pOpts->fOptSet & OPTPROC_GNUUSAGE) ? true : false;
- }
- /*
- * Figure out if we should try to format usage text sort-of like
- * the way many GNU programs do.
- */
- static inline bool
- skip_misuse_usage(tOptions * pOpts)
- {
- return (pOpts->fOptSet & OPTPROC_MISUSE) ? true : false;
- }
- /*=export_func optionOnlyUsage
- *
- * what: Print usage text for just the options
- * arg: + tOptions * + pOpts + program options descriptor +
- * arg: + int + ex_code + exit code for calling exit(3) +
- *
- * doc:
- * This routine will print only the usage for each option.
- * This function may be used when the emitted usage must incorporate
- * information not available to AutoOpts.
- =*/
- void
- optionOnlyUsage(tOptions * pOpts, int ex_code)
- {
- char const * pOptTitle = NULL;
- set_usage_flags(pOpts, NULL);
- if ((ex_code != EXIT_SUCCESS) &&
- skip_misuse_usage(pOpts))
- return;
- /*
- * Determine which header and which option formatting strings to use
- */
- if (do_gnu_usage(pOpts))
- (void)setGnuOptFmts(pOpts, &pOptTitle);
- else
- (void)setStdOptFmts(pOpts, &pOptTitle);
- prt_opt_usage(pOpts, ex_code, pOptTitle);
- fflush(option_usage_fp);
- if (ferror(option_usage_fp) != 0)
- fserr_exit(pOpts->pzProgName, zwriting, (option_usage_fp == stderr)
- ? zstderr_name : zstdout_name);
- }
- /**
- * Print a message suggesting how to get help.
- *
- * @param[in] opts the program options
- */
- static void
- print_offer_usage(tOptions * opts)
- {
- char help[24];
- if (HAS_opt_usage_t(opts)) {
- int ix = opts->presetOptCt;
- tOptDesc * od = opts->pOptDesc + ix;
- while (od->optUsage != AOUSE_HELP) {
- if (++ix >= opts->optCt)
- ao_bug(zmissing_help_msg);
- od++;
- }
- switch (opts->fOptSet & (OPTPROC_LONGOPT | OPTPROC_SHORTOPT)) {
- case OPTPROC_SHORTOPT:
- help[0] = '-';
- help[1] = od->optValue;
- help[2] = NUL;
- break;
- case OPTPROC_LONGOPT:
- case (OPTPROC_LONGOPT | OPTPROC_SHORTOPT):
- help[0] = help[1] = '-';
- strncpy(help + 2, od->pz_Name, 20);
- break;
- case 0:
- strncpy(help, od->pz_Name, 20);
- break;
- }
- } else {
- switch (opts->fOptSet & (OPTPROC_LONGOPT | OPTPROC_SHORTOPT)) {
- case OPTPROC_SHORTOPT:
- strcpy(help, "-h");
- break;
- case OPTPROC_LONGOPT:
- case (OPTPROC_LONGOPT | OPTPROC_SHORTOPT):
- strcpy(help, "--help");
- break;
- case 0:
- strcpy(help, "help");
- break;
- }
- }
- fprintf(option_usage_fp, zoffer_usage_fmt, opts->pzProgName, help);
- }
- /**
- * Print information about each option.
- *
- * @param[in] opts the program options
- * @param[in] exit_code whether or not there was a usage error reported.
- * used to select full usage versus abbreviated.
- */
- static void
- print_usage_details(tOptions * opts, int exit_code)
- {
- {
- char const * pOptTitle = NULL;
- int flen;
- /*
- * Determine which header and which option formatting strings to use
- */
- if (do_gnu_usage(opts)) {
- flen = setGnuOptFmts(opts, &pOptTitle);
- sprintf(line_fmt_buf, zFmtFmt, flen);
- fputc(NL, option_usage_fp);
- } else {
- flen = setStdOptFmts(opts, &pOptTitle);
- sprintf(line_fmt_buf, zFmtFmt, flen);
- /*
- * When we exit with EXIT_SUCCESS and the first option is a doc
- * option, we do *NOT* want to emit the column headers.
- * Otherwise, we do.
- */
- if ( (exit_code != EXIT_SUCCESS)
- || ((opts->pOptDesc->fOptState & OPTST_DOCUMENT) == 0) )
- fputs(pOptTitle, option_usage_fp);
- }
- flen = 4 - ((flen + 15) / 8);
- if (flen > 0)
- tab_skip_ct = flen;
- prt_opt_usage(opts, exit_code, pOptTitle);
- }
- /*
- * Describe the mechanics of denoting the options
- */
- switch (opts->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 ((opts->fOptSet & OPTPROC_NUM_OPT) != 0)
- fputs(zNumberOpt, option_usage_fp);
- if ((opts->fOptSet & OPTPROC_REORDER) != 0)
- fputs(zReorder, option_usage_fp);
- if (opts->pzExplain != NULL)
- fputs(opts->pzExplain, option_usage_fp);
- /*
- * IF the user is asking for help (thus exiting with SUCCESS),
- * THEN see what additional information we can provide.
- */
- if (exit_code == EXIT_SUCCESS)
- prt_prog_detail(opts);
- /*
- * Give bug notification preference to the packager information
- */
- if (HAS_pzPkgDataDir(opts) && (opts->pzPackager != NULL))
- fputs(opts->pzPackager, option_usage_fp);
- else if (opts->pzBugAddr != NULL)
- fprintf(option_usage_fp, zPlsSendBugs, opts->pzBugAddr);
- fflush(option_usage_fp);
- if (ferror(option_usage_fp) != 0)
- fserr_exit(opts->pzProgName, zwriting, (option_usage_fp == stderr)
- ? zstderr_name : zstdout_name);
- }
- static void
- print_one_paragraph(char const * text, bool plain, FILE * fp)
- {
- if (plain) {
- #ifdef ENABLE_NLS
- #ifdef HAVE_LIBINTL_H
- #ifdef DEBUG_ENABLED
- #undef gettext
- #endif
- char * buf = dgettext("libopts", text);
- if (buf == text)
- text = gettext(text);
- #endif /* HAVE_LIBINTL_H */
- #endif /* ENABLE_NLS */
- fputs(text, fp);
- }
- else {
- char const * t = optionQuoteString(text, LINE_SPLICE);
- fprintf(fp, PUTS_FMT, t);
- AGFREE(t);
- }
- }
- /*=export_func optionPrintParagraphs
- * private:
- *
- * what: Print a paragraph of usage text
- * arg: + char const * + text + a block of text that has bee i18n-ed +
- * arg: + bool + plain + false -> wrap text in fputs() +
- * arg: + FILE * + fp + the stream file pointer for output +
- *
- * doc:
- * This procedure is called in two contexts: when a full or short usage text
- * has been provided for display, and when autogen is assembling a list of
- * translatable texts in the optmain.tlib template. In the former case, \a
- * plain is set to \a true, otherwise \a false.
- *
- * Anything less than 256 characters in size is printed as a single unit.
- * Otherwise, paragraphs are detected. A paragraph break is defined as just
- * before a non-empty line preceded by two newlines or a line that starts
- * with at least one space character but fewer than 8 space characters.
- * Lines indented with tabs or more than 7 spaces are considered continuation
- * lines.
- *
- * If 'plain' is true, we are emitting text for a user to see. So, if it is
- * true and NLS is not enabled, then just write the whole thing at once.
- =*/
- void
- optionPrintParagraphs(char const * text, bool plain, FILE * fp)
- {
- size_t len = strlen(text);
- char * buf;
- #ifndef ENABLE_NLS
- if (plain || (len < 256))
- #else
- if (len < 256)
- #endif
- {
- print_one_paragraph(text, plain, fp);
- return;
- }
- AGDUPSTR(buf, text, "ppara");
- text = buf;
- for (;;) {
- char * scan;
- if (len < 256) {
- done:
- print_one_paragraph(buf, plain, fp);
- break;
- }
- scan = buf;
- try_longer:
- scan = strchr(scan, NL);
- if (scan == NULL)
- goto done;
- if ((scan - buf) < 40) {
- scan++;
- goto try_longer;
- }
- scan++;
- if ((! isspace((int)*scan)) || (*scan == HT))
- /*
- * line starts with tab or non-whitespace --> continuation
- */
- goto try_longer;
- if (*scan == NL) {
- /*
- * Double newline -> paragraph break
- * Include all newlines in current paragraph.
- */
- while (*++scan == NL) /*continue*/;
- } else {
- char * p = scan;
- int sp_ct = 0;
- while (*p == ' ') {
- if (++sp_ct >= 8) {
- /*
- * Too many spaces --> continuation line
- */
- scan = p;
- goto try_longer;
- }
- p++;
- }
- }
- /*
- * "scan" points to the first character of a paragraph or the
- * terminating NUL byte.
- */
- {
- char svch = *scan;
- *scan = NUL;
- print_one_paragraph(buf, plain, fp);
- len -= scan - buf;
- if (len <= 0)
- break;
- *scan = svch;
- buf = scan;
- }
- }
- AGFREE(text);
- }
- /*=export_func optionUsage
- * private:
- *
- * what: Print usage text
- * arg: + tOptions * + opts + program options descriptor +
- * arg: + int + exitCode + exit code for calling exit(3) +
- *
- * doc:
- * This routine will print usage in both GNU-standard and AutoOpts-expanded
- * formats. The descriptor specifies the default, but AUTOOPTS_USAGE will
- * over-ride this, providing the value of it is set to either "gnu" or
- * "autoopts". This routine will @strong{not} return.
- *
- * If "exitCode" is "AO_EXIT_REQ_USAGE" (normally 64), then output will to
- * to stdout and the actual exit code will be "EXIT_SUCCESS".
- =*/
- noreturn void
- optionUsage(tOptions * opts, int usage_exit_code)
- {
- int exit_code = (usage_exit_code == AO_EXIT_REQ_USAGE)
- ? EXIT_SUCCESS : usage_exit_code;
- displayEnum = false;
- set_usage_flags(opts, NULL);
- /*
- * Paged usage will preset option_usage_fp to an output file.
- * If it hasn't already been set, then set it to standard output
- * on successful exit (help was requested), otherwise error out.
- *
- * Test the version before obtaining pzFullUsage or pzShortUsage.
- * These fields do not exist before revision 30.
- */
- {
- char const * pz;
- if (exit_code == EXIT_SUCCESS) {
- pz = (opts->structVersion >= 30 * 4096)
- ? opts->pzFullUsage : NULL;
- if (option_usage_fp == NULL)
- option_usage_fp = print_exit ? stderr : stdout;
- } else {
- pz = (opts->structVersion >= 30 * 4096)
- ? opts->pzShortUsage : NULL;
- if (option_usage_fp == NULL)
- option_usage_fp = stderr;
- }
- if (((opts->fOptSet & OPTPROC_COMPUTE) == 0) && (pz != NULL)) {
- if ((opts->fOptSet & OPTPROC_TRANSLATE) != 0)
- optionPrintParagraphs(pz, true, option_usage_fp);
- else
- fputs(pz, option_usage_fp);
- goto flush_and_exit;
- }
- }
- fprintf(option_usage_fp, opts->pzUsageTitle, opts->pzProgName);
- if ((exit_code == EXIT_SUCCESS) ||
- (! skip_misuse_usage(opts)))
- print_usage_details(opts, usage_exit_code);
- else
- print_offer_usage(opts);
- flush_and_exit:
- fflush(option_usage_fp);
- if (ferror(option_usage_fp) != 0)
- fserr_exit(opts->pzProgName, zwriting, (option_usage_fp == stdout)
- ? zstdout_name : zstderr_name);
- option_exits(exit_code);
- }
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * PER OPTION TYPE USAGE INFORMATION
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- /**
- * print option conflicts.
- *
- * @param opts the program option descriptor
- * @param od the option descriptor
- */
- static void
- prt_conflicts(tOptions * opts, tOptDesc * od)
- {
- const int * opt_no;
- fputs(zTabHyp + tab_skip_ct, option_usage_fp);
- /*
- * REQUIRED:
- */
- if (od->pOptMust != NULL) {
- opt_no = od->pOptMust;
- if (opt_no[1] == NO_EQUIVALENT) {
- fprintf(option_usage_fp, zReqOne,
- opts->pOptDesc[*opt_no].pz_Name);
- } else {
- fputs(zReqThese, option_usage_fp);
- for (;;) {
- fprintf(option_usage_fp, zTabout + tab_skip_ct,
- opts->pOptDesc[*opt_no].pz_Name);
- if (*++opt_no == NO_EQUIVALENT)
- break;
- }
- }
- if (od->pOptCant != NULL)
- fputs(zTabHypAnd + tab_skip_ct, option_usage_fp);
- }
- /*
- * CONFLICTS:
- */
- if (od->pOptCant == NULL)
- return;
- opt_no = od->pOptCant;
- if (opt_no[1] == NO_EQUIVALENT) {
- fprintf(option_usage_fp, zProhibOne,
- opts->pOptDesc[*opt_no].pz_Name);
- return;
- }
- fputs(zProhib, option_usage_fp);
- for (;;) {
- fprintf(option_usage_fp, zTabout + tab_skip_ct,
- opts->pOptDesc[*opt_no].pz_Name);
- if (*++opt_no == NO_EQUIVALENT)
- break;
- }
- }
- /**
- * Print the usage information for a single vendor option.
- *
- * @param[in] opts the program option descriptor
- * @param[in] od the option descriptor
- * @param[in] argtp names of the option argument types
- * @param[in] usefmt format for primary usage line
- */
- static void
- prt_one_vendor(tOptions * opts, tOptDesc * od,
- arg_types_t * argtp, char const * usefmt)
- {
- prt_preamble(opts, od, argtp);
- {
- char z[ 80 ];
- char const * pzArgType;
- /*
- * Determine the argument type string first on its usage, then,
- * when the option argument is required, base the type string on the
- * argument type.
- */
- if (od->fOptState & OPTST_ARG_OPTIONAL) {
- pzArgType = argtp->pzOpt;
- } else switch (OPTST_GET_ARGTYPE(od->fOptState)) {
- case OPARG_TYPE_NONE: pzArgType = argtp->pzNo; break;
- case OPARG_TYPE_ENUMERATION: pzArgType = argtp->pzKey; break;
- case OPARG_TYPE_FILE: pzArgType = argtp->pzFile; break;
- case OPARG_TYPE_MEMBERSHIP: pzArgType = argtp->pzKeyL; break;
- case OPARG_TYPE_BOOLEAN: pzArgType = argtp->pzBool; break;
- case OPARG_TYPE_NUMERIC: pzArgType = argtp->pzNum; break;
- case OPARG_TYPE_HIERARCHY: pzArgType = argtp->pzNest; break;
- case OPARG_TYPE_STRING: pzArgType = argtp->pzStr; break;
- case OPARG_TYPE_TIME: pzArgType = argtp->pzTime; break;
- default: goto bogus_desc;
- }
- pzArgType = SPN_WHITESPACE_CHARS(pzArgType);
- if (*pzArgType == NUL)
- snprintf(z, sizeof(z), "%s", od->pz_Name);
- else
- snprintf(z, sizeof(z), "%s=%s", od->pz_Name, pzArgType);
- fprintf(option_usage_fp, usefmt, z, od->pzText);
- switch (OPTST_GET_ARGTYPE(od->fOptState)) {
- case OPARG_TYPE_ENUMERATION:
- case OPARG_TYPE_MEMBERSHIP:
- displayEnum = (od->pOptProc != NULL) ? true : displayEnum;
- }
- }
- return;
- bogus_desc:
- fprintf(stderr, zbad_od, opts->pzProgName, od->pz_Name);
- ao_bug(zbad_arg_type_msg);
- }
- /**
- * Print the long options processed with "-W". These options will be the
- * ones that do *not* have flag characters.
- *
- * @param opts the program option descriptor
- * @param title the title for the options
- */
- static void
- prt_vendor_opts(tOptions * opts, char const * title)
- {
- static unsigned int const not_vended_mask =
- OPTST_NO_USAGE_MASK | OPTST_DOCUMENT;
- static char const vfmtfmt[] = "%%-%us %%s\n";
- char vfmt[sizeof(vfmtfmt)];
- /*
- * Only handle client specified options. The "vendor option" follows
- * "presetOptCt", so we won't loop/recurse indefinitely.
- */
- int ct = opts->presetOptCt;
- tOptDesc * od = opts->pOptDesc;
- fprintf(option_usage_fp, zTabout + tab_skip_ct, zVendOptsAre);
- {
- size_t nmlen = 0;
- do {
- size_t l;
- if ( ((od->fOptState & not_vended_mask) != 0)
- || GRAPH_CH(od->optValue))
- continue;
- l = strlen(od->pz_Name);
- if (l > nmlen) nmlen = l;
- } while (od++, (--ct > 0));
- snprintf(vfmt, sizeof(vfmt), vfmtfmt, (unsigned int)nmlen + 4);
- }
- if (tab_skip_ct > 0)
- tab_skip_ct--;
- ct = opts->presetOptCt;
- od = opts->pOptDesc;
- do {
- if ( ((od->fOptState & not_vended_mask) != 0)
- || GRAPH_CH(od->optValue))
- continue;
- prt_one_vendor(opts, od, &argTypes, vfmt);
- prt_extd_usage(opts, od, title);
- } while (od++, (--ct > 0));
- /* no need to restore "tab_skip_ct" - options are done now */
- }
- /**
- * Print extended usage. Usage/help was requested.
- *
- * @param opts the program option descriptor
- * @param od the option descriptor
- * @param title the title for the options
- */
- static void
- prt_extd_usage(tOptions * opts, tOptDesc * od, char const * title)
- {
- if ( ((opts->fOptSet & OPTPROC_VENDOR_OPT) != 0)
- && (od->optActualValue == VENDOR_OPTION_VALUE)) {
- prt_vendor_opts(opts, title);
- return;
- }
- /*
- * IF there are option conflicts or dependencies,
- * THEN print them here.
- */
- if ((od->pOptMust != NULL) || (od->pOptCant != NULL))
- prt_conflicts(opts, od);
- /*
- * IF there is a disablement string
- * THEN print the disablement info
- */
- if (od->pz_DisableName != NULL )
- fprintf(option_usage_fp, zDis + tab_skip_ct, od->pz_DisableName);
- /*
- * Check for argument types that have callbacks with magical properties
- */
- switch (OPTST_GET_ARGTYPE(od->fOptState)) {
- case OPARG_TYPE_NUMERIC:
- /*
- * IF the numeric option has a special callback,
- * THEN call it, requesting the range or other special info
- */
- if ( (od->pOptProc != NULL)
- && (od->pOptProc != optionNumericVal) ) {
- (*(od->pOptProc))(OPTPROC_EMIT_USAGE, od);
- }
- break;
- case OPARG_TYPE_FILE:
- (*(od->pOptProc))(OPTPROC_EMIT_USAGE, od);
- break;
- }
- /*
- * IF the option defaults to being enabled,
- * THEN print that out
- */
- if (od->fOptState & OPTST_INITENABLED)
- fputs(zEnab + tab_skip_ct, option_usage_fp);
- /*
- * IF the option is in an equivalence class
- * AND not the designated lead
- * THEN print equivalence and leave it at that.
- */
- if ( (od->optEquivIndex != NO_EQUIVALENT)
- && (od->optEquivIndex != od->optActualIndex ) ) {
- fprintf(option_usage_fp, zalt_opt + tab_skip_ct,
- opts->pOptDesc[ od->optEquivIndex ].pz_Name);
- return;
- }
- /*
- * IF this particular option can NOT be preset
- * AND some form of presetting IS allowed,
- * AND it is not an auto-managed option (e.g. --help, et al.)
- * THEN advise that this option may not be preset.
- */
- if ( ((od->fOptState & OPTST_NO_INIT) != 0)
- && ( (opts->papzHomeList != NULL)
- || (opts->pzPROGNAME != NULL)
- )
- && (od->optIndex < opts->presetOptCt)
- )
- fputs(zNoPreset + tab_skip_ct, option_usage_fp);
- /*
- * Print the appearance requirements.
- */
- if (OPTST_GET_ARGTYPE(od->fOptState) == OPARG_TYPE_MEMBERSHIP)
- fputs(zMembers + tab_skip_ct, option_usage_fp);
- else switch (od->optMinCt) {
- case 1:
- case 0:
- switch (od->optMaxCt) {
- case 0: fputs(zPreset + tab_skip_ct, option_usage_fp); break;
- case NOLIMIT: fputs(zNoLim + tab_skip_ct, option_usage_fp); break;
- case 1: break;
- /*
- * IF the max is more than one but limited, print "UP TO" message
- */
- default:
- fprintf(option_usage_fp, zUpTo + tab_skip_ct, od->optMaxCt); break;
- }
- break;
- default:
- /*
- * More than one is required. Print the range.
- */
- fprintf(option_usage_fp, zMust + tab_skip_ct,
- od->optMinCt, od->optMaxCt);
- }
- if ( NAMED_OPTS(opts)
- && (opts->specOptIdx.default_opt == od->optIndex))
- fputs(zDefaultOpt + tab_skip_ct, option_usage_fp);
- }
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- /**
- * Figure out where all the initialization files might live. This requires
- * translating some environment variables and testing to see if a name is a
- * directory or a file. It's squishy, but important to tell users how to
- * find these files.
- *
- * @param[in] papz search path
- * @param[out] ini_file an output buffer of AG_PATH_MAX+1 bytes
- * @param[in] path_nm the name of the file we're hunting for
- */
- static void
- prt_ini_list(char const * const * papz, char const * ini_file,
- char const * path_nm)
- {
- char pth_buf[AG_PATH_MAX+1];
- fputs(zPresetIntro, option_usage_fp);
- for (;;) {
- char const * path = *(papz++);
- char const * nm_buf = pth_buf;
- if (path == NULL)
- break;
- /*
- * Ignore any invalid paths
- */
- if (! optionMakePath(pth_buf, (int)sizeof(pth_buf), path, path_nm))
- nm_buf = path;
- /*
- * Expand paths that are relative to the executable or installation
- * directories. Leave alone paths that use environment variables.
- */
- else if ((*path == '$')
- && ((path[1] == '$') || (path[1] == '@')))
- path = nm_buf;
- /*
- * Print the name of the "homerc" file. If the "rcfile" name is
- * not empty, we may or may not print that, too...
- */
- fprintf(option_usage_fp, zPathFmt, path);
- if (*ini_file != NUL) {
- struct stat sb;
- /*
- * IF the "homerc" file is a directory,
- * then append the "rcfile" name.
- */
- if ((stat(nm_buf, &sb) == 0) && S_ISDIR(sb.st_mode)) {
- fputc(DIRCH, option_usage_fp);
- fputs(ini_file, option_usage_fp);
- }
- }
- fputc(NL, option_usage_fp);
- }
- }
- /**
- * Print the usage line preamble text
- *
- * @param opts the program option descriptor
- * @param od the option descriptor
- * @param at names of the option argument types
- */
- static void
- prt_preamble(tOptions * opts, tOptDesc * od, arg_types_t * at)
- {
- /*
- * Flag prefix: IF no flags at all, then omit it. If not printable
- * (not allowed for this option), then blank, else print it.
- * Follow it with a comma if we are doing GNU usage and long
- * opts are to be printed too.
- */
- if ((opts->fOptSet & OPTPROC_SHORTOPT) == 0)
- fputs(at->pzSpc, option_usage_fp);
- else if (! GRAPH_CH(od->optValue)) {
- if ( (opts->fOptSet & (OPTPROC_GNUUSAGE|OPTPROC_LONGOPT))
- == (OPTPROC_GNUUSAGE|OPTPROC_LONGOPT))
- fputc(' ', option_usage_fp);
- fputs(at->pzNoF, option_usage_fp);
- } else {
- fprintf(option_usage_fp, " -%c", od->optValue);
- if ( (opts->fOptSet & (OPTPROC_GNUUSAGE|OPTPROC_LONGOPT))
- == (OPTPROC_GNUUSAGE|OPTPROC_LONGOPT))
- fputs(", ", option_usage_fp);
- }
- }
- /**
- * Print the usage information for a single option.
- *
- * @param opts the program option descriptor
- * @param od the option descriptor
- * @param at names of the option argument types
- */
- static void
- prt_one_usage(tOptions * opts, tOptDesc * od, arg_types_t * at)
- {
- prt_preamble(opts, od, at);
- {
- char z[80];
- char const * atyp;
- /*
- * Determine the argument type string first on its usage, then,
- * when the option argument is required, base the type string on the
- * argument type.
- */
- if (od->fOptState & OPTST_ARG_OPTIONAL) {
- atyp = at->pzOpt;
- } else switch (OPTST_GET_ARGTYPE(od->fOptState)) {
- case OPARG_TYPE_NONE: atyp = at->pzNo; break;
- case OPARG_TYPE_ENUMERATION: atyp = at->pzKey; break;
- case OPARG_TYPE_FILE: atyp = at->pzFile; break;
- case OPARG_TYPE_MEMBERSHIP: atyp = at->pzKeyL; break;
- case OPARG_TYPE_BOOLEAN: atyp = at->pzBool; break;
- case OPARG_TYPE_NUMERIC: atyp = at->pzNum; break;
- case OPARG_TYPE_HIERARCHY: atyp = at->pzNest; break;
- case OPARG_TYPE_STRING: atyp = at->pzStr; break;
- case OPARG_TYPE_TIME: atyp = at->pzTime; break;
- default: goto bogus_desc;
- }
- #ifdef _WIN32
- if (at->pzOptFmt == zGnuOptFmt)
- snprintf(z, sizeof(z), "--%s%s", od->pz_Name, atyp);
- else if (at->pzOptFmt == zGnuOptFmt + 2)
- snprintf(z, sizeof(z), "%s%s", od->pz_Name, atyp);
- else
- #endif
- snprintf(z, sizeof(z), at->pzOptFmt, atyp, od->pz_Name,
- (od->optMinCt != 0) ? at->pzReq : at->pzOpt);
- fprintf(option_usage_fp, line_fmt_buf, z, od->pzText);
- switch (OPTST_GET_ARGTYPE(od->fOptState)) {
- case OPARG_TYPE_ENUMERATION:
- case OPARG_TYPE_MEMBERSHIP:
- displayEnum = (od->pOptProc != NULL) ? true : displayEnum;
- }
- }
- return;
- bogus_desc:
- fprintf(stderr, zbad_od, opts->pzProgName, od->pz_Name);
- option_exits(EX_SOFTWARE);
- }
- /**
- * Print out the usage information for just the options.
- */
- static void
- prt_opt_usage(tOptions * opts, int ex_code, char const * title)
- {
- int ct = opts->optCt;
- int optNo = 0;
- tOptDesc * od = opts->pOptDesc;
- int docCt = 0;
- do {
- /*
- * no usage --> disallowed on command line (OPTST_NO_COMMAND), or
- * deprecated -- strongly discouraged (OPTST_DEPRECATED), or
- * compiled out of current object code (OPTST_OMITTED)
- */
- if ((od->fOptState & OPTST_NO_USAGE_MASK) != 0) {
- /*
- * IF this is a compiled-out option
- * *AND* usage was requested with "omitted-usage"
- * *AND* this is NOT abbreviated usage
- * THEN display this option.
- */
- if ( (od->fOptState == (OPTST_OMITTED | OPTST_NO_INIT))
- && (od->pz_Name != NULL)
- && (ex_code == EXIT_SUCCESS)) {
- char const * why_pz =
- (od->pzText == NULL) ? zDisabledWhy : od->pzText;
- prt_preamble(opts, od, &argTypes);
- fprintf(option_usage_fp, zDisabledOpt, od->pz_Name, why_pz);
- }
- continue;
- }
- if ((od->fOptState & OPTST_DOCUMENT) != 0) {
- if (ex_code == EXIT_SUCCESS) {
- fprintf(option_usage_fp, argTypes.pzBrk, od->pzText,
- title);
- docCt++;
- }
- continue;
- }
- /* Skip name only options when we have a vendor option */
- if ( ((opts->fOptSet & OPTPROC_VENDOR_OPT) != 0)
- && (! GRAPH_CH(od->optValue)))
- continue;
- /*
- * IF this is the first auto-opt maintained option
- * *AND* we are doing a full help
- * *AND* there are documentation options
- * *AND* the last one was not a doc option,
- * THEN document that the remaining options are not user opts
- */
- if ((docCt > 0) && (ex_code == EXIT_SUCCESS)) {
- if (opts->presetOptCt == optNo) {
- if ((od[-1].fOptState & OPTST_DOCUMENT) == 0)
- fprintf(option_usage_fp, argTypes.pzBrk, zAuto, title);
- } else if ((ct == 1) &&
- (opts->fOptSet & OPTPROC_VENDOR_OPT))
- fprintf(option_usage_fp, argTypes.pzBrk, zVendIntro, title);
- }
- prt_one_usage(opts, od, &argTypes);
- /*
- * IF we were invoked because of the --help option,
- * THEN print all the extra info
- */
- if (ex_code == EXIT_SUCCESS)
- prt_extd_usage(opts, od, title);
- } while (od++, optNo++, (--ct > 0));
- fputc(NL, option_usage_fp);
- }
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- /**
- * Print program details.
- * @param[in] opts the program option descriptor
- */
- static void
- prt_prog_detail(tOptions * opts)
- {
- bool need_intro = (opts->papzHomeList == NULL);
- /*
- * Display all the places we look for config files, if we have
- * a list of directories to search.
- */
- if (! need_intro)
- prt_ini_list(opts->papzHomeList, opts->pzRcName, opts->pzProgPath);
- /*
- * Let the user know about environment variable settings
- */
- if ((opts->fOptSet & OPTPROC_ENVIRON) != 0) {
- if (need_intro)
- fputs(zPresetIntro, option_usage_fp);
- fprintf(option_usage_fp, zExamineFmt, opts->pzPROGNAME);
- }
- /*
- * IF we found an enumeration,
- * THEN hunt for it again. Call the handler proc with a NULL
- * option struct pointer. That tells it to display the keywords.
- */
- if (displayEnum) {
- int ct = opts->optCt;
- int optNo = 0;
- tOptDesc * od = opts->pOptDesc;
- fputc(NL, option_usage_fp);
- fflush(option_usage_fp);
- do {
- switch (OPTST_GET_ARGTYPE(od->fOptState)) {
- case OPARG_TYPE_ENUMERATION:
- case OPARG_TYPE_MEMBERSHIP:
- (*(od->pOptProc))(OPTPROC_EMIT_USAGE, od);
- }
- } while (od++, optNo++, (--ct > 0));
- }
- /*
- * If there is a detail string, now is the time for that.
- */
- if (opts->pzDetail != NULL)
- fputs(opts->pzDetail, option_usage_fp);
- }
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- *
- * OPTION LINE FORMATTING SETUP
- *
- * The "OptFmt" formats receive three arguments:
- * 1. the type of the option's argument
- * 2. the long name of the option
- * 3. "YES" or "no ", depending on whether or not the option must appear
- * on the command line.
- * These formats are used immediately after the option flag (if used) has
- * been printed.
- *
- * Set up the formatting for GNU-style output
- */
- static int
- setGnuOptFmts(tOptions * opts, char const ** ptxt)
- {
- static char const zOneSpace[] = " ";
- int flen = 22;
- *ptxt = zNoRq_ShrtTtl;
- argTypes.pzStr = zGnuStrArg;
- argTypes.pzReq = zOneSpace;
- argTypes.pzNum = zGnuNumArg;
- argTypes.pzKey = zGnuKeyArg;
- argTypes.pzKeyL = zGnuKeyLArg;
- argTypes.pzTime = zGnuTimeArg;
- argTypes.pzFile = zGnuFileArg;
- argTypes.pzBool = zGnuBoolArg;
- argTypes.pzNest = zGnuNestArg;
- argTypes.pzOpt = zGnuOptArg;
- argTypes.pzNo = zOneSpace;
- argTypes.pzBrk = zGnuBreak;
- argTypes.pzNoF = zSixSpaces;
- argTypes.pzSpc = zThreeSpaces;
- switch (opts->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;
- }
- /*
- * Standard (AutoOpts normal) option line formatting
- */
- static int
- setStdOptFmts(tOptions * opts, char const ** ptxt)
- {
- int flen = 0;
- argTypes.pzStr = zStdStrArg;
- argTypes.pzReq = zStdReqArg;
- argTypes.pzNum = zStdNumArg;
- argTypes.pzKey = zStdKeyArg;
- argTypes.pzKeyL = zStdKeyLArg;
- argTypes.pzTime = zStdTimeArg;
- argTypes.pzFile = zStdFileArg;
- argTypes.pzBool = zStdBoolArg;
- argTypes.pzNest = zStdNestArg;
- argTypes.pzOpt = zStdOptArg;
- argTypes.pzNo = zStdNoArg;
- argTypes.pzBrk = zStdBreak;
- argTypes.pzNoF = zFiveSpaces;
- argTypes.pzSpc = zTwoSpaces;
- switch (opts->fOptSet & (OPTPROC_NO_REQ_OPT | OPTPROC_SHORTOPT)) {
- case (OPTPROC_NO_REQ_OPT | OPTPROC_SHORTOPT):
- *ptxt = zNoRq_ShrtTtl;
- argTypes.pzOptFmt = zNrmOptFmt;
- flen = 19;
- break;
- case OPTPROC_NO_REQ_OPT:
- *ptxt = zNoRq_NoShrtTtl;
- argTypes.pzOptFmt = zNrmOptFmt;
- flen = 19;
- break;
- case OPTPROC_SHORTOPT:
- *ptxt = zReq_ShrtTtl;
- argTypes.pzOptFmt = zReqOptFmt;
- flen = 24;
- break;
- case 0:
- *ptxt = zReq_NoShrtTtl;
- argTypes.pzOptFmt = zReqOptFmt;
- flen = 24;
- }
- return flen;
- }
- /** @}
- *
- * Local Variables:
- * mode: C
- * c-file-style: "stroustrup"
- * indent-tabs-mode: nil
- * End:
- * end of autoopts/usage.c */
|