|
From: <kin...@us...> - 2025-09-26 11:33:58
|
Revision: 7497
http://sourceforge.net/p/teem/code/7497
Author: kindlmann
Date: 2025-09-26 11:33:53 +0000 (Fri, 26 Sep 2025)
Log Message:
-----------
still hacking
Modified Paths:
--------------
teem/trunk/src/hest/methodsHest.c
teem/trunk/src/hest/parsest.c
teem/trunk/src/hest/privateHest.h
Modified: teem/trunk/src/hest/methodsHest.c
===================================================================
--- teem/trunk/src/hest/methodsHest.c 2025-09-26 11:32:56 UTC (rev 7496)
+++ teem/trunk/src/hest/methodsHest.c 2025-09-26 11:33:53 UTC (rev 7497)
@@ -64,10 +64,133 @@
sizeof(char),
sizeof(char*),
sizeof(int),
- 0 /* we don't know anything about type "other" */
+ 0 /* we don't know anything about size of type "other" */
};
/* clang-format on */
+/* now (in 2025) that we've done all this work to preserve the command-line argv[]
+tokens, and to properly tokenize default strings and response files, we should stop using
+the airParseStrT functions that internally did airStrtok(): we have exactly one token to
+parse. These functions thus return non-zero in case of error, instead of returning the
+number of parsed values. */
+static int
+parseSingleB(void *_out, const char *str, const void *ptr) {
+ AIR_UNUSED(ptr);
+ // we got NULL, there's nothing to do
+ if (!(_out && str)) return 1;
+ int *out = (int *)_out;
+ *out = airEnumVal(airBool, str);
+ return (airEnumUnknown(airBool) /* which is -1 */ == *out);
+}
+
+#define _PARSE_1_ARGS(type) void *out, const char *str, const void *ptr
+#define _PARSE_1_BODY(format) \
+ AIR_UNUSED(ptr); \
+ /* we got NULL, there's nothing to do */ \
+ if (!(out && str)) return 1; \
+ return (1 != airSingleSscanf(str, format, out))
+
+// clang-format off
+static int parseSingleH (_PARSE_1_ARGS(short)) { _PARSE_1_BODY("%hd"); }
+static int parseSingleUH(_PARSE_1_ARGS(unsigned short)) { _PARSE_1_BODY("%hu"); }
+static int parseSingleI (_PARSE_1_ARGS(int)) { _PARSE_1_BODY("%d"); }
+static int parseSingleUI(_PARSE_1_ARGS(unsigned int)) { _PARSE_1_BODY("%u"); }
+static int parseSingleL (_PARSE_1_ARGS(long int)) { _PARSE_1_BODY("%ld"); }
+static int parseSingleUL(_PARSE_1_ARGS(unsigned long int)) { _PARSE_1_BODY("%lu"); }
+static int parseSingleZ (_PARSE_1_ARGS(size_t)) { _PARSE_1_BODY("%z"); }
+static int parseSingleF (_PARSE_1_ARGS(float)) { _PARSE_1_BODY("%f"); }
+static int parseSingleD (_PARSE_1_ARGS(double)) { _PARSE_1_BODY("%lf"); }
+// clang-format on
+static int
+parseSingleC(void *_out, const char *str, const void *ptr) {
+ AIR_UNUSED(ptr);
+ // we got NULL, there's nothing to do
+ if (!(_out && str)) return 1;
+ if (1 != strlen(str)) {
+ // really just want single char
+ return 1;
+ }
+ char *out = (char *)_out;
+ *out = str[0];
+ return 0;
+}
+static int
+parseSingleS(void *_out, const char *str, const void *ptr) {
+ AIR_UNUSED(ptr);
+ // we got NULL, there's nothing to do
+ if (!(_out && str)) return 1;
+ char **out = (char **)_out;
+ *out = airStrdup(str);
+ return !!(*out); // check that we got a non-NULL strdup
+}
+static int
+parseSingleE(void *_out, const char *str, const void *_enm) {
+ // we got NULL, there's nothing to do
+ if (!(_out && str && _enm)) return 1;
+ int *out = (int *)_out;
+ const airEnum *enm = (const airEnum *)_enm;
+ *out = airEnumVal(enm, str);
+ return (airEnumUnknown(enm) == *out);
+}
+
+int (*const _hestParseSingle[_HEST_TYPE_MAX + 1])(void *, const char *, const void *) = {
+ NULL, //
+ parseSingleB, //
+ parseSingleH, //
+ parseSingleUH, //
+ parseSingleI, //
+ parseSingleUI, //
+ parseSingleL, //
+ parseSingleUL, //
+ parseSingleZ, //
+ parseSingleF, //
+ parseSingleD, //
+ parseSingleC, //
+ parseSingleS, //
+ parseSingleE,
+ NULL // "other"
+};
+
+#define _INVERT_SCALAR(TT, ctype) \
+ static void _invertScalar##TT(void *_valP) { \
+ ctype *valP = (ctype *)_valP; \
+ ctype val = *valP; \
+ *valP = !val; \
+ }
+_INVERT_SCALAR(B, int)
+_INVERT_SCALAR(H, short)
+_INVERT_SCALAR(UH, unsigned short)
+_INVERT_SCALAR(I, int)
+_INVERT_SCALAR(UI, unsigned int)
+_INVERT_SCALAR(L, long)
+_INVERT_SCALAR(UL, unsigned long)
+_INVERT_SCALAR(Z, size_t)
+_INVERT_SCALAR(F, float)
+_INVERT_SCALAR(D, double)
+// not: C, char
+// not: S, char *
+// not: E, int
+// not: ?, "other"
+
+void (*const _hestInvertScalar[_HEST_TYPE_MAX + 1])(void *) = {
+ NULL, //
+ _invertScalarB, //
+ _invertScalarH, //
+ _invertScalarUH, //
+ _invertScalarI, //
+ _invertScalarUI, //
+ _invertScalarL, //
+ _invertScalarUL, //
+ _invertScalarZ, //
+ _invertScalarF, //
+ _invertScalarD, //
+ NULL, // not C, char
+ NULL, // not S, char *
+ NULL, // not E, int
+ NULL // not ?, "other"
+};
+
+// HEY these are sticking around just for the old implementation of hestParse
unsigned int (*const _hestParseStr[_HEST_TYPE_MAX + 1])(void *, const char *,
const char *, unsigned int)
= {NULL,
@@ -229,55 +352,47 @@
return max;
}
-/* opt_kind determines the kind (1,2,3,4, or 5) of an opt,
- from being passed its min and max fields */
+/* minmaxKind determines the kind (1,2,3,4, or 5) of an opt,
+ based on the min and max fields from the hestOpt */
static int
-opt_kind(unsigned int min, int _max) {
- int max;
-
- max = _hestMax(_max);
- if (!(AIR_INT(min) <= max)) {
+minmaxKind(unsigned int min, int _max) {
+ int ret;
+ int max = _hestMax(_max);
+ if (AIR_INT(min) > max) {
/* invalid */
- return -1;
+ ret = -1;
+ } else { // else min <= max
+ if (AIR_INT(min) == max) {
+ if (0 == min) {
+ // stand-alone flag
+ ret = 1;
+ } else if (1 == min) {
+ // single fixed parm
+ ret = 2;
+ } else { // min==max >= 2
+ // multiple fixed parms
+ ret = 3;
+ }
+ } else { // else min < max
+ if (0 == min && 1 == max) {
+ // weirdo: single optional parameter
+ ret = 4;
+ } else {
+ // multiple variadic parameters
+ ret = 5;
+ }
+ }
}
-
- if (0 == min && 0 == max) {
- /* flag */
- return 1;
- }
-
- if (1 == min && 1 == max) {
- /* single fixed parameter */
- return 2;
- }
-
- if (2 <= min && 2 <= max && AIR_INT(min) == max) {
- /* multiple fixed parameters */
- return 3;
- }
-
- if (0 == min && 1 == max) {
- /* single optional parameter */
- return 4;
- }
-
- /* else multiple variadic parameters */
- return 5;
+ return ret;
}
-/* "private" wrapper around opt_kind, taking a hestOpt pointer */
-int
-_hestKind(const hestOpt *opt) {
-
- return opt_kind(opt->min, opt->max);
-}
-
-/* opt_init initializes all of a hestOpt, even arrAlloc and arrLen */
+/* initializes all of a hestOpt, even arrAlloc and arrLen */
static void
-opt_init(hestOpt *opt) {
+optInit(hestOpt *opt) {
- opt->flag = opt->name = NULL;
- opt->type = airTypeUnknown; /* == 0 */
+ opt->flag = NULL;
+ opt->name = NULL;
+ opt->type = airTypeUnknown; /* h== 0 */
opt->min = 0;
opt->max = 0;
opt->valueP = NULL;
@@ -310,12 +425,12 @@
/* like airArrayNew: create an initial segment of the hestOpt array */
static void
-optarr_new(hestOpt **optP) {
+optarrNew(hestOpt **optP) {
unsigned int opi;
hestOpt *ret = AIR_CALLOC(INCR, hestOpt);
assert(ret);
for (opi = 0; opi < INCR; opi++) {
- opt_init(ret + opi);
+ optInit(ret + opi);
}
ret->arrAlloc = INCR;
ret->arrLen = 0;
@@ -326,7 +441,7 @@
/* line airArrayLenIncr(1): increments logical length by 1,
and returns index of newly-available element */
static unsigned int
-optarr_incr(hestOpt **optP) {
+optarrIncr(hestOpt **optP) {
unsigned int olen, nlen;
olen = (*optP)->arrLen; /* == index of new element */
nlen = olen + 1;
@@ -338,7 +453,7 @@
memcpy(nopt, *optP, olen * sizeof(hestOpt));
nopt->arrAlloc = (*optP)->arrAlloc + INCR;
for (opi = olen; opi < nopt->arrAlloc; opi++) {
- opt_init(nopt + opi);
+ optInit(nopt + opi);
}
free(*optP);
*optP = nopt;
@@ -368,7 +483,7 @@
opt->dflt = airStrdup(dflt);
opt->info = airStrdup(info);
// need to set kind now so can be used in later conditionals
- opt->kind = opt_kind(min, max);
+ opt->kind = minmaxKind(min, max);
// deal with (what used to be) var args
opt->sawP = (5 == opt->kind /* */
? sawP
@@ -382,7 +497,7 @@
// alloc set by hestParse
opt->havec = hestArgVecNew();
// leave arrAlloc, arrLen untouched: managed by caller
- // yes, redundant with opt_init()
+ // yes, redundant with optInit()
opt->source = hestSourceUnknown;
opt->parmStr = NULL;
opt->helpWanted = AIR_FALSE;
@@ -393,14 +508,14 @@
hestOptAdd_nva: A new (as of 2023) non-var-args ("_nva") version of hestOptAdd;
The per-hestOpt logic (including setting opt->kind) has now been moved to
hestOptSingleSet. The venerable var-args hestOptAdd is now a wrapper around this.
-and the 99 non-var-args hestOptAdd_* functions also all call this.
+and all the 99 non-var-args fully typed hestOptAdd_* functions also call this.
Like hestOptAdd has done since 2013: returns UINT_MAX in case of error.
NOTE that we do NOT do here ANY error checking on the validity of the arguments passed,
-e.g. enforcing that we have a non-NULL sawP if min != max (a variadic parameter option),
-or that without a flag (`flag` is NULL) we must have min > 0. All of that is done later,
-in _hestOPCheck.
+e.g. enforcing that we have a non-NULL sawP iff this is a multi-variadic parameter
+option, or that without a flag (`flag` is NULL) we must have min > 0. All of that is
+done later, in _hestOPCheck.
*/
unsigned int
hestOptAdd_nva(hestOpt **optP, const char *flag, const char *name, int type,
@@ -413,10 +528,10 @@
if (!optP) return UINT_MAX;
/* initialize hestOpt array if necessary */
if (!(*optP)) {
- optarr_new(optP);
+ optarrNew(optP);
}
/* increment logical length of hestOpt array; return index of opt being set here */
- retIdx = optarr_incr(optP);
+ retIdx = optarrIncr(optP);
/* set all elements of the opt */
hestOptSingleSet(*optP + retIdx, flag, name, type, min, max, /* */
valueP, dflt, info, /* */
@@ -436,8 +551,8 @@
* hestOptAdd_Nv_T for T=Bool, Short, UShort, Int, UInt, LongInt, ULongInt, Size_t,
* Float, Double, Char, String, Enum, or Other.
*
- * This returns the index of the option just added, to so the caller can remember it and
- * this speed up later checking the `hestOpt->source` to learn where how the option was
+ * This returns the index of the option just added, so the caller can remember it and
+ * thus speed up later checking the `hestOpt->source` to learn where how the option was
* parsed. Returns UINT_MAX in case of error.
*/
unsigned int
@@ -451,7 +566,7 @@
if (!optP) return UINT_MAX;
/* deal with var args */
- if (5 == opt_kind(min, max)) {
+ if (5 == minmaxKind(min, max)) {
va_start(ap, info);
sawP = va_arg(ap, unsigned int *);
va_end(ap);
@@ -533,10 +648,6 @@
opt[opi].type, airTypeUnknown + 1, airTypeLast - 1);
return 1;
}
- if (!(opt[opi].valueP)) {
- biffAddf(HEST, "%s%sopt[%u]'s valueP is NULL!", _ME_, opi);
- return 1;
- }
// `kind` set by hestOptSingleSet
if (-1 == opt[opi].kind) {
biffAddf(HEST, "%s%sopt[%u]'s min (%d) and max (%d) incompatible", _ME_, opi,
@@ -543,50 +654,31 @@
opt[opi].min, opt[opi].max);
return 1;
}
- if (5 == opt[opi].kind && !(opt[opi].sawP)) {
- biffAddf(HEST,
- "%s%sopt[%u] has multiple variadic parameters (min=%u,max=%d), "
- "but sawP is NULL",
- _ME_, opi, opt[opi].min, opt[opi].max);
+ if (!(opt[opi].valueP)) {
+ biffAddf(HEST, "%s%sopt[%u]'s valueP is NULL!", _ME_, opi);
return 1;
}
- if (airTypeEnum == opt[opi].type) {
- if (!(opt[opi].enm)) {
- biffAddf(HEST,
- "%s%sopt[%u] (%s) is type \"enum\", but no "
- "airEnum pointer given",
- _ME_, opi, opt[opi].flag ? opt[opi].flag : "unflagged");
+ if (1 == opt[opi].kind) {
+ if (!opt[opi].flag) {
+ biffAddf(HEST, "%s%sstand-alone flag opt[%u] must have a flag", _ME_, opi);
return 1;
}
- }
- if (airTypeOther == opt[opi].type) {
- if (!(opt[opi].CB)) {
- biffAddf(HEST,
- "%s%sopt[%u] (%s) is type \"other\", but no "
- "callbacks given",
- _ME_, opi, opt[opi].flag ? opt[opi].flag : "unflagged");
+ if (opt[opi].dflt) {
+ biffAddf(HEST, "%s%sstand-alone flag (opt[%u] %s) should not have a default",
+ _ME_, opi, opt[opi].flag);
return 1;
}
- if (!(opt[opi].CB->size > 0)) {
- biffAddf(HEST, "%s%sopt[%u]'s \"size\" (%u) invalid", _ME_, opi,
- (uint)(opt[opi].CB->size));
+ if (opt[opi].name) {
+ biffAddf(HEST, "%s%sstand-alone flag (opt[%u] %s) should not have a name", _ME_,
+ opi, opt[opi].flag);
return 1;
}
- if (!(opt[opi].CB->type)) {
- biffAddf(HEST, "%s%sopt[%u]'s \"type\" is NULL", _ME_, opi);
+ } else { // ------ end of if (1 == opt[opi].kind)
+ if (!opt[opi].name) {
+ biffAddf(HEST, "%s%sopt[%u] isn't stand-alone flag: must have \"name\"", _ME_,
+ opi);
return 1;
}
- if (!(opt[opi].CB->parse)) {
- biffAddf(HEST, "%s%sopt[%u]'s \"parse\" callback NULL", _ME_, opi);
- return 1;
- }
- if (opt[opi].CB->destroy && (sizeof(void *) != opt[opi].CB->size)) {
- biffAddf(HEST,
- "%s%sopt[%u] has a \"destroy\", but size %lu isn't "
- "sizeof(void*)",
- _ME_, opi, (unsigned long)(opt[opi].CB->size));
- return 1;
- }
}
if (opt[opi].flag) {
const char *flag = opt[opi].flag;
@@ -606,13 +698,9 @@
flag, chi, flag[chi]);
return 1;
}
- }
- if (1 == opt[opi].kind) {
- if (opt[opi].dflt) {
- biffAddf(HEST,
- "%s%sstand-alone flag (opt[%u] %s) should not give a default; will "
- "be ignored",
- _ME_, opi, opt[opi].flag);
+ if (strchr(AIR_WHITESPACE, flag[chi])) {
+ biffAddf(HEST, "%s%sopt[%u].flag \"%s\" char %u '%c' is whitespace", _ME_, opi,
+ flag, chi, flag[chi]);
return 1;
}
}
@@ -643,6 +731,7 @@
_ME_, opi, flag, MULTI_FLAG_SEP);
return (free(tbuff), 1);
}
+ free(tbuff);
} else {
if (!strlen(opt[opi].flag)) {
biffAddf(HEST, "%s%sopt[%u].flag is zero length", _ME_, opi);
@@ -662,7 +751,7 @@
"%s%sflagged single variadic parameter must "
"specify a default",
_ME_);
- return (free(tbuff), 1);
+ return 1;
}
if (!strlen(opt[opi].dflt)) {
biffAddf(HEST,
@@ -669,45 +758,100 @@
"%s%sflagged single variadic parameter default "
"must be non-zero length",
_ME_);
- return (free(tbuff), 1);
+ return 1;
}
}
- /*
- sprintf(tbuff, "-%s", opt[op].flag);
- if (1 == sscanf(tbuff, "%f", &tmpF)) {
- if (err)
- sprintf(err, "%sopt[%u].flag (\"%s\") is numeric, bad news",
- ME, op, opt[op].flag);
- return 1;
- }
- */
- free(tbuff);
} else { // ------ end of if (opt[opi].flag)
// opt[opi] is unflagged
if (!opt[opi].min) {
+ // this rules out all unflagged kind 1 and kind 4
+ // and prevents unflagged kind 5 w/ min=0
biffAddf(HEST, "%s%sunflagged opt[%u] (name %s) must have min >= 1, not 0", _ME_,
opi, opt[opi].name ? opt[opi].name : "not set");
return 1;
}
}
- if (1 == opt[opi].kind) {
- if (!opt[opi].flag) {
- biffAddf(HEST, "%s%sopt[%u] flag must have a flag", _ME_, opi);
+ if (4 == opt[opi].kind) { // single variadic parameter
+ // immediately above have ruled out unflagged kind 4
+ if (!opt[opi].dflt) {
+ biffAddf(HEST,
+ "%s%sopt[%u] -%s is single variadic parameter, but "
+ "no default set",
+ _ME_, opi, opt[opi].flag);
return 1;
}
- } else {
- if (!opt[opi].name) {
- biffAddf(HEST, "%s%sopt[%u] isn't a flag: must have \"name\"", _ME_, opi);
+ /* pre 2025, these types were allowed kind 4, but the semantics are just so weird
+ and thus hard to test + debug, that it no longer makes sense to support them */
+ if (airTypeChar == opt[opi].type || airTypeString == opt[opi].type
+ || airTypeEnum == opt[opi].type || airTypeOther == opt[opi].type) {
+ biffAddf(HEST,
+ "%s%sopt[%u] -%s is single variadic parameter, but sorry, "
+ "type %s no longer supported",
+ _ME_, opi, opt[opi].flag, _hestTypeStr[opt[opi].type]);
return 1;
}
}
- if (4 == opt[opi].kind && !opt[opi].dflt) {
+ if (5 == opt[opi].kind && !(opt[opi].sawP)) {
biffAddf(HEST,
- "%s%sopt[%u] is single variadic parameter, but "
- "no default set",
- _ME_, opi);
+ "%s%sopt[%u] has multiple variadic parameters (min=%u,max=%d), "
+ "but sawP is NULL",
+ _ME_, opi, opt[opi].min, opt[opi].max);
return 1;
}
+ if (opt[opi].sawP && 5 != opt[opi].kind) {
+ biffAddf(HEST,
+ "%s%sopt[%u] has non-NULL sawP but is not a (kind=5) "
+ "multiple variadic parm option (min=%u,max=%d)",
+ _ME_, opi, opt[opi].min, opt[opi].max);
+ return 1;
+ }
+ if (airTypeEnum == opt[opi].type && !(opt[opi].enm)) {
+ biffAddf(HEST,
+ "%s%sopt[%u] (%s) is type \"enum\", but no "
+ "airEnum pointer given",
+ _ME_, opi, opt[opi].flag ? opt[opi].flag : "unflagged");
+ return 1;
+ }
+ if (opt[opi].enm && airTypeEnum != opt[opi].type) {
+ biffAddf(HEST,
+ "%s%sopt[%u] (%s) has non-NULL airEnum pointer, but is not airTypeEnum",
+ _ME_, opi, opt[opi].flag ? opt[opi].flag : "unflagged");
+ return 1;
+ }
+ if (airTypeOther == opt[opi].type) {
+ if (!(opt[opi].CB)) {
+ biffAddf(HEST,
+ "%s%sopt[%u] (%s) is type \"other\", but no "
+ "callbacks given",
+ _ME_, opi, opt[opi].flag ? opt[opi].flag : "unflagged");
+ return 1;
+ }
+ if (!(opt[opi].CB->size > 0)) {
+ biffAddf(HEST, "%s%sopt[%u]'s \"size\" (%u) invalid", _ME_, opi,
+ (uint)(opt[opi].CB->size));
+ return 1;
+ }
+ if (!(opt[opi].CB->type)) {
+ biffAddf(HEST, "%s%sopt[%u]'s \"type\" is NULL", _ME_, opi);
+ return 1;
+ }
+ if (!(opt[opi].CB->parse)) {
+ biffAddf(HEST, "%s%sopt[%u]'s \"parse\" callback NULL", _ME_, opi);
+ return 1;
+ }
+ if (opt[opi].CB->destroy && (sizeof(void *) != opt[opi].CB->size)) {
+ biffAddf(HEST,
+ "%s%sopt[%u] has a \"destroy\", but size %lu isn't "
+ "sizeof(void*)",
+ _ME_, opi, (unsigned long)(opt[opi].CB->size));
+ return 1;
+ }
+ }
+ if (opt[opi].CB && airTypeOther != opt[opi].type) {
+ biffAddf(HEST, "%s%sopt[%u] (%s) has non-NULL callbacks, but is not airTypeOther",
+ _ME_, opi, opt[opi].flag ? opt[opi].flag : "unflagged");
+ return 1;
+ }
// kind 4 = single variadic parm; kind 5 = multiple variadic parm
ufvarNum += (opt[opi].kind > 3 && (!opt[opi].flag));
}
Modified: teem/trunk/src/hest/parsest.c
===================================================================
--- teem/trunk/src/hest/parsest.c 2025-09-26 11:32:56 UTC (rev 7496)
+++ teem/trunk/src/hest/parsest.c 2025-09-26 11:33:53 UTC (rev 7497)
@@ -800,9 +800,8 @@
/* havecExtractFlagged
Extracts the parameter args associated with all flagged options from the given
`hestArgVec *havec` (as generated by histProc()) and stores them the corresponding
-opt->havec. Also sets opt->source according to where that flag arg appeared (we don't
-notice if the parameters were weirdly split between the comamnd-line and a response file;
-only the source of the flag arg is recorded).
+opt->havec. Also sets opt->source according to where that flag arg appeared in the case
+of stand-alone flags, or (via havecTransfer) wherever the last parm arg came from.
In the case of variadic parameter options, this does the work of figuring out which args
belong with the option. In any case, this only extracts and preserves (in opt->havec) the
@@ -911,7 +910,9 @@
if (hparm->verbosity > 1) {
hestArgVecPrint(__func__, "main havec as it came", havec);
}
- // remember from whence this flagged option came
+ /* remember from whence this flagged option came, which is necessary when there are
+ zero parms. For nonzero parmNum, havecTransfer will also (overriding this) set
+ theOpt->source to every source of each arg extracted */
theOpt->source = havec->harg[argIdx]->source;
// lose the flag argument
hestArgNix(hestArgVecRemove(havec, argIdx));
@@ -924,7 +925,7 @@
}
havStr = airFree(havStr);
if (hitVPS) {
- // drop the variadic-parameter-stop flag
+ // lose the variadic-parameter-stop flag
hestArgNix(hestArgVecRemove(havec, argIdx));
}
if (hparm->verbosity) {
@@ -967,8 +968,7 @@
}
/* havecExtractUnflagged()
-
-extracts the parameter args associated with all unflagged options (of `hestOpt *opt`)
+Extracts the parameter args associated with all unflagged options (of `hestOpt *opt`)
from the given `hestArgVec *havec` and (like havecExtractFlagged) extracts those args and
saves them in the corresponding opt[].havec
@@ -1162,6 +1162,7 @@
static int
optProcessDefaults(hestOpt *opt, hestArg *tharg, hestInputStack *hist,
const hestParm *hparm) {
+ char ident[AIR_STRLEN_HUGE + 1];
uint optNum = opt->arrLen;
for (uint opi = 0; opi < optNum; opi++) {
if (hparm->verbosity) {
@@ -1181,7 +1182,6 @@
the flag was not given by user */
goto nextopt;
}
- char ident[AIR_STRLEN_HUGE + 1];
identStr(ident, opt + opi);
// should have already checked for this but just to make sure
if (!opt[opi].dflt) {
@@ -1203,7 +1203,7 @@
return 1;
}
/* havecExtractFlagged and havecExtractUnflagged have done the work of ensuring that
- the minimum number of parm args have been extracted for each option. We have to do
+ the minimum number of parm args have been extracted for each option. We should do
something analogous for args tokenized from the default strings. */
if (opt[opi].havec->len < opt[opi].min) {
biffAddf(
@@ -1218,12 +1218,32 @@
optPrint(opt + opi, opi);
}
}
+ for (uint opi = 0; opi < optNum; opi++) {
+ identStr(ident, opt + opi);
+ /* (Yes, half of this test is redundant with check above on whether the default
+ string supplied at least opt[opi].min args, but erring with doing more checks).
+ Now that we've made a opt[opi].havec array, and will soon parse strings to set
+ values, we can check that the number of args matches what the option needs */
+ int maxArg = _hestMax(opt[opi].max);
+ int haveArg = AIR_INT(opt[opi].havec->len);
+ if (!(AIR_INT(opt[opi].min) <= haveArg && haveArg <= maxArg)) {
+ biffAddf(HEST,
+ "%s%s %s[%u] got (from user or from default) %u args, but that is "
+ "outside [min,max]=[%u,%d] range",
+ _ME_, ident, opi, opt[opi].havec->len, opt[opi].min, maxArg);
+ return 1;
+ }
+ }
return 0;
}
-#if 0
+/* optSetValues
+Does the parsing of opt[opi].havec to set values in whatever opt[opi].valueP points to.
+The parent mop `pmop` is passed so that we can remember what to free up ONLY IN CASE OF
+ERROR. Otherwise, the allocations persist with the successful return of hestParse(2), and
+are freed with hestParseFree. */
static int
-optSetValues(hestOpt *opt, const hestParm *hparm) {
+optSetValues(hestOpt *opt, const hestParm *hparm, airArray *pmop) {
char ident[AIR_STRLEN_HUGE + 1];
/*
char cberr[AIR_STRLEN_HUGE + 1], *tok, *last, *optParmsCopy;
@@ -1232,6 +1252,7 @@
*/
void *valueP;
char *cvalueP;
+ int *ivalueP;
uint optNum = opt->arrLen;
for (uint opi = 0; opi < optNum; opi++) {
@@ -1244,7 +1265,7 @@
and it turns out that adding this was as simple as adding this one following
line. The inscrutability of the hest code (or really the self-reinforcing
learned fear of working with the hest code) seems to have been the barrier.
- (2025 GLK notes that the fear is justified, given how long the re-write took!) */
+ (2025 GLK notes that the fear was justified, given how long the re-write took ...) */
opt[opi].parmStr = hestArgVecSprint(opt[opi].havec, AIR_FALSE);
/* not: airStrdup(optParms[opi]); since 2025 havec adoption */
int type = opt[opi].type;
@@ -1255,10 +1276,11 @@
: _hestTypeSize[type]));
valueP = opt[opi].valueP;
cvalueP = (char *)valueP;
+ ivalueP = (int *)valueP;
if (hparm->verbosity) {
- printf("%s: opt[%u/%u]: havec|%s| |%s| --> kind=%d, type=%d, size=%u\n", __func__,
- opi, optNum, opt[opi].parmStr, ident, opt[opi].kind, type,
- (unsigned int)size);
+ printf("%s: opt[%u/%u]: havec_%c|%s| \t|%s| \t--> kind=%d, type=%d, size=%u\n",
+ __func__, opi, optNum, airEnumStr(hestSource, opt[opi].source)[0],
+ opt[opi].parmStr, ident, opt[opi].kind, type, (unsigned int)size);
}
/* we may over-write these */
opt[opi].alloc = 0;
@@ -1268,15 +1290,60 @@
switch (opt[opi].kind) {
case 1:
/* -------- parameter-less boolean flags -------- */
- /* the value pointer is always assumed to be an int* */
- if (valueP) *((int *)valueP) = hestSourceDefault != opt[opi].source;
+ /* valueP is always assumed to be an int* */
+ *ivalueP = hestSourceDefault != opt[opi].source;
+ if (hparm->verbosity) {
+ printf(" --> set value %d\n", *ivalueP);
+ }
break;
-# if 0
- case 2:
+ case 4: {
+ const char *strsrc;
+ int invert;
+ /* -------- optional single variadics -------- */
+ /* 2025 _hestOPCheck now restricts possible types; these are no longer allowed:
+ airTypeChar, airTypeString, airTypeEnum, airTypeOther. As for the semantics,
+ the old web page seems to be clear (though we now disallow unflagged kind 4):
+ - option flag does not appear
+ --> value is set from the default
+ - option flag appears, but with no parm
+ --> default is parsed, say, as value V, then the value is set to !V
+ (*this* is the "inversion" that is mentioned at places in the code)
+ - option flag appears, with single parm
+ --> value is set from parsing that parm
+ In any case, some string has to be parsed */
+ if (hestSourceDefault == opt[opi].source) {
+ // option flag does not appear
+ strsrc = opt[opi].dflt;
+ invert = AIR_FALSE;
+ } else if (hestSourceDefault != opt[opi].source && 0 == opt[opi].havec->len) {
+ // option flag appears, but with no parm
+ strsrc = opt[opi].dflt;
+ invert = AIR_TRUE;
+ } else if (hestSourceDefault != opt[opi].source && 1 == opt[opi].havec->len) {
+ // option flag appears, with single parm
+ strsrc = opt[opi].havec->harg[0]->str;
+ invert = AIR_FALSE;
+ } else {
+ biffAddf(HEST, "%s%sconfused by %s[%u] source %s and havec->len %u", _ME_, ident,
+ opi, airEnumStr(hestSource, opt[opi].source), opt[opi].havec->len);
+ return 1;
+ }
+ if (_hestParseSingle[type](valueP, strsrc, NULL /* because type simple */)) {
+ biffAddf(HEST, "%s%sfor %s[%u] could not parse |%s| as single %s", _ME_, ident,
+ opi, strsrc, _hestTypeStr[type]);
+ return 1;
+ }
+ if (invert) {
+ _hestInvertScalar[type](valueP);
+ }
+ break;
+ } // end case 4 {
+#if 0
+ case 2:
/* -------- one required parameter -------- */
- /* 2023 GLK is really curious why "if (optParms[op] && vP) {" is (repeatedly)
+ /* 2023 GLK is really curious why "if (optParms[op] && valueP) {" is (repeatedly)
guarding all the work in these blocks, and why that wasn't factored out */
- if (optParms[opi]) {
+ if (optParms[opi] && valueP) {
switch (type) {
case airTypeEnum:
if (1 != airParseStrE((int *)valueP, optParms[opi], " ", 1, opt[opi].enm)) {
@@ -1406,100 +1473,6 @@
}
}
break;
- case 4:
- /* -------- optional single variadics -------- */
- if (optParms[opi] && valueP) {
- int pret;
- switch (type) {
- case airTypeChar:
- /* no "inversion" for chars: using the flag with no parameter is the same as
- not using the flag i.e. we just parse from the default string */
- if (1 != _hestParseStr[type](valueP, optParms[opi], " ", 1)) {
- fprintf(stderr, "%scouldn't parse %s\"%s\" as %s for %s\n", ME,
- optDfltd[opi] ? "(default) " : "", optParms[opi], _hestTypeStr[type],
- ident);
- return 1;
- }
- opt[opi].alloc = 0;
- break;
- case airTypeString:
- /* this is a bizarre case: optional single string, with some kind of value
- "inversion". 2023 GLK would prefer to make this like Char, Enum, and Other: for
- which there is no attempt at "inversion". But for some reason the inversion of
- a non-empty default string to a NULL string value, when the flag is used
- without a parameter, was implemented from the early days of hest. Assuming
- that a younger GLK long ago had a reason for that, that functionality now
- persists. */
- pret = _hestParseStr[type](valueP, optParms[opi], " ",
- 1 /*, hparm->greedySingleString */);
- if (1 != pret) {
- fprintf(stderr, "%scouldn't parse %s\"%s\" as %s for %s\n", ME,
- optDfltd[opi] ? "(default) " : "", optParms[opi], _hestTypeStr[type],
- ident);
- return 1;
- }
- opt[opi].alloc = 1;
- if (opt[opi].flag && 1 == whichCase(opt, optDfltd, optParmNum, appr, opi)) {
- /* we just parsed the default, but now we want to "invert" it */
- *((char **)valueP) = (char *)airFree(*((char **)valueP));
- opt[opi].alloc = 0;
- }
- /* vP is the address of a char* (a char**), and what we
- manage with airMop is the char * */
- airMopMem(pmop, valueP, airMopOnError);
- break;
- case airTypeEnum:
- if (1 != airParseStrE((int *)valueP, optParms[opi], " ", 1, opt[opi].enm)) {
- fprintf(stderr, "%scouldn't parse %s\"%s\" as %s for %s\n", ME,
- optDfltd[opi] ? "(default) " : "", optParms[opi], opt[opi].enm->name,
- ident);
- return 1;
- }
- break;
- case airTypeOther:
- /* we're parsing an single "other". We will not perform the special flagged
- single variadic parameter games as done above, so whether this option is
- flagged or unflagged, we're going to treat it like an unflagged single variadic
- parameter option: if the parameter didn't appear, we'll parse it from the
- default, if it did appear, we'll parse it from the command line. Setting up
- optParms[op] thusly has already been done by _hestDefaults() */
- strcpy(cberr, "");
- ret = opt[opi].CB->parse(valueP, optParms[opi], cberr);
- if (ret) {
- if (strlen(cberr))
- fprintf(stderr, "%serror parsing \"%s\" as %s for %s:\n%s\n", ME,
- optParms[opi], opt[opi].CB->type, ident, cberr);
- else
- fprintf(stderr, "%serror parsing \"%s\" as %s for %s: returned %d\n", ME,
- optParms[opi], opt[opi].CB->type, ident, ret);
- return 1;
- }
- if (opt[opi].CB->destroy) {
- /* vP is the address of a void*, we manage the void* */
- opt[opi].alloc = 1;
- airMopAdd(pmop, valueP, (airMopper)airSetNull, airMopOnError);
- airMopAdd(pmop, *((void **)valueP), opt[opi].CB->destroy, airMopOnError);
- }
- break;
- default:
- if (1 != _hestParseStr[type](valueP, optParms[opi], " ", 1)) {
- fprintf(stderr, "%scouldn't parse %s\"%s\" as %s for %s\n", ME,
- optDfltd[opi] ? "(default) " : "", optParms[opi], _hestTypeStr[type],
- ident);
- return 1;
- }
- opt[opi].alloc = 0;
- /* HEY sorry about confusion about hestOpt->parmStr versus the value set
- here, due to this "inversion" */
- if (1 == whichCase(opt, optDfltd, optParmNum, appr, opi)) {
- /* we just parsed the default, but now we want to "invert" it */
- tmpD = airDLoad(valueP, type);
- airIStore(valueP, type, tmpD ? 0 : 1);
- }
- break;
- }
- }
- break;
case 5:
/* -------- multiple variadic parameters -------- */
if (optParms[opi] && valueP) {
@@ -1622,12 +1595,11 @@
}
}
break;
-# endif
- }
- }
+#endif
+ } // end switch
+ } // for opi ...
return 0;
}
-#endif
/* hestParse2
Parse the `argc`,`argv` commandline according to the hestOpt array `opt`, and as
@@ -1641,16 +1613,17 @@
0) Error checking on given `opt` array
-1) Generate internal representation of command-line that includes expanding any response
-files; this all goes into the `hestArgVec *havec`.
+1) Generate internal representation of command-line that includes expanding any
+response files; this all goes into the `hestArgVec *havec`.
-2) From `havec`, extract the args that are attributable to flagged and unflagged options,
-moving each `hestArg` out of main `havec` and into the per-hestOpt opt->havec
+2) From `havec`, extract the args that are attributable to flagged and unflagged
+options, moving each `hestArg` out of main `havec` and into the per-hestOpt
+opt->havec
3) For options not user-supplied, process the opt's `dflt` string to set opt->havec
-4) Now, every option should have a opt->havec set, regardless of where it came from. So
-parse those per-opt args to set final values for the user to see
+4) Now, every option should have a opt->havec set, regardless of where it came from.
+So parse those per-opt args to set final values for the user to see
What is allocated as result of work here should be freed by hestParseFree
*/
@@ -1737,14 +1710,13 @@
airMopError(mop);
return 1;
}
-#if 0
+
// --4--4--4--4--4-- Finally, parse the args and set values
- if (optSetValues(opt, HPARM)) {
+ if (optSetValues(opt, HPARM, mop)) {
DO_ERR("problem with setting values");
airMopError(mop);
return 1;
}
-#endif
#undef DO_ERR
#undef HPARM
Modified: teem/trunk/src/hest/privateHest.h
===================================================================
--- teem/trunk/src/hest/privateHest.h 2025-09-26 11:32:56 UTC (rev 7496)
+++ teem/trunk/src/hest/privateHest.h 2025-09-26 11:33:53 UTC (rev 7497)
@@ -71,11 +71,14 @@
#define _HEST_TYPE_MAX 14
HEST_EXPORT const char _hestTypeStr[_HEST_TYPE_MAX + 1][AIR_STRLEN_SMALL + 1];
HEST_EXPORT const size_t _hestTypeSize[_HEST_TYPE_MAX + 1];
+HEST_EXPORT void (*const _hestInvertScalar[_HEST_TYPE_MAX + 1])(void *);
+HEST_EXPORT int (*const _hestParseSingle[_HEST_TYPE_MAX + 1])(void *, const char *,
+ const void *);
+// HEY these are sticking around just for the old implementation of hestParse
HEST_EXPORT unsigned int (*const _hestParseStr[_HEST_TYPE_MAX + 1])(void *, const char *,
const char *,
unsigned int);
extern const char *const _hestBiffKey;
-extern int _hestKind(const hestOpt *opt);
extern int _hestMax(int max);
extern int _hestOPCheck(const hestOpt *opt, const hestParm *parm);
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|