|
From: <kin...@us...> - 2025-09-23 08:36:59
|
Revision: 7470
http://sourceforge.net/p/teem/code/7470
Author: kindlmann
Date: 2025-09-23 08:36:57 +0000 (Tue, 23 Sep 2025)
Log Message:
-----------
still working on hestParse rewrite
Modified Paths:
--------------
teem/trunk/src/hest/argvHest.c
teem/trunk/src/hest/hest.h
teem/trunk/src/hest/methodsHest.c
teem/trunk/src/hest/parseHest.c
teem/trunk/src/hest/parsest.c
teem/trunk/src/hest/privateHest.h
teem/trunk/src/hest/test/argv.c
teem/trunk/src/hest/test/tparse.c
Modified: teem/trunk/src/hest/argvHest.c
===================================================================
--- teem/trunk/src/hest/argvHest.c 2025-09-22 20:54:48 UTC (rev 7469)
+++ teem/trunk/src/hest/argvHest.c 2025-09-23 08:36:57 UTC (rev 7470)
@@ -20,9 +20,6 @@
#include "hest.h"
#include "privateHest.h"
-#include <assert.h>
-#include <sys/errno.h>
-
#define INCR 32
/* to avoid strict aliasing warnings */
@@ -52,6 +49,7 @@
appu.c = &(harg->str);
harg->strArr = airArrayNew(appu.v, &(harg->len), sizeof(char), INCR);
airArrayStructCB(harg->strArr, setNul, NULL);
+ harg->source = hestSourceUnknown;
/* initialize with \0 so that harg->str is "" */
airArrayLenIncr(harg->strArr, 1);
/* now harg->str = {0:'\0'} and harg->len = 1; */
@@ -107,8 +105,9 @@
}
void
-hestArgAddString(hestArg *harg, const char *str) {
+hestArgSetString(hestArg *harg, const char *str) {
assert(harg && str);
+ hestArgReset(harg);
uint len = AIR_UINT(strlen(str));
for (uint si = 0; si < len; si++) {
hestArgAddChar(harg, str[si]);
@@ -127,27 +126,55 @@
hestPtrPtrUnion hppu;
hppu.harg = &(havec->harg);
havec->hargArr = airArrayNew(hppu.v, &(havec->len), sizeof(hestArg), INCR);
+ // underlying array havec->harg will not be reallocated if shrunk
+ havec->hargArr->noReallocWhenSmaller = AIR_TRUE;
airArrayStructCB(havec->hargArr, hargInit, hargDone);
return havec;
}
+void
+hestArgVecReset(hestArgVec *havec) {
+ if (havec) {
+ airArrayLenSet(havec->hargArr, 0);
+ }
+ return;
+}
+
hestArgVec *
hestArgVecNix(hestArgVec *havec) {
- assert(havec);
- airArrayNuke(havec->hargArr);
- free(havec);
+ if (havec) {
+ airArrayNuke(havec->hargArr);
+ free(havec);
+ }
return NULL;
}
void
+hestArgVecRemove(hestArgVec *havec, uint popIdx) {
+ // (experimented with allocating something to hold what was lost)
+ // hestArg *ret = NULL;
+ if (havec && popIdx < havec->len) { // note: this implies that havec->len >= 1
+ // ret = AIR_CALLOC(1, hestArg); // (we don't have a constructor?)
+ // memcpy(ret, havec->harg + popIdx);
+ for (uint ai = popIdx; ai < havec->len - 1; ai++) {
+ // shuffle down the hestArg elements (not pointers to them) of havec->harg
+ memcpy(havec->harg + ai, havec->harg + ai + 1, sizeof(hestArg));
+ }
+ // decrement the nominal length of havec->harg
+ airArrayLenIncr(havec->hargArr, -1);
+ }
+ return;
+}
+
+void
hestArgVecAppendString(hestArgVec *havec, const char *str) {
uint idx = airArrayLenIncr(havec->hargArr, 1);
- hestArgAddString(havec->harg + idx, str);
+ hestArgSetString(havec->harg + idx, str);
}
void
-hestArgVecPrint(const char *caller, const hestArgVec *havec) {
- printf("%s: hestArgVec %p has %u args:\n", caller, havec, havec->len);
+hestArgVecPrint(const char *caller, const char *info, const hestArgVec *havec) {
+ printf("%s: %s hestArgVec %p has %u args:\n", caller, info, havec, havec->len);
for (uint idx = 0; idx < havec->hargArr->len; idx++) {
const hestArg *harg;
harg = havec->harg + idx;
Modified: teem/trunk/src/hest/hest.h
===================================================================
--- teem/trunk/src/hest/hest.h 2025-09-22 20:54:48 UTC (rev 7469)
+++ teem/trunk/src/hest/hest.h 2025-09-23 08:36:57 UTC (rev 7470)
@@ -49,9 +49,9 @@
*
* The blissfully-type-unaware hestOptAdd() has always relied on the airTypeT enum values
* below. Since that function is not being removed, to avoid needless code breakage with
- * TeemV2, these values now live in this hest header. hest users should instead be using
- * one of the 99 properly typed hestOptAdd_X_T functions, which have no need for airType
- * pseudo-types.
+ * TeemV2, these values now live in this hest header. However, hest users should instead
+ * be using one of the 99 properly typed hestOptAdd_X_T functions from adder.c (see
+ * below), which have no need for airType pseudo-types.
*
* Other things that used to be in air, but which really only mattered to implement hest
* functions have been moved into privateHest.h, but with air --> _hest renaming:
@@ -94,6 +94,71 @@
};
/*
+The hestArg, hestArgVec, hestInput, and hestInputStack were all created for the 2025
+TeemV2 rewrite of hestParse, to fix bugs and limits on how the code previously worked:
+- Command-line arguments containing spaces were fully never correctly handled: the
+ internal representation of one argument, as one space-delineated substring of all
+ the arguments concatenated back into a single string (a bad idea), was never correctly
+ implemented. Now the internal representation of argv is with an array data structure,
+ not a single string that has to be retokenized. The old code was wary of dynamic
+ reallocation as part of the parsing process, the new code embraces it (note the many
+ airArray).
+- When parsing response files, ""-quoted strings were not correctly handled (nor was "#"
+ appearing within a string), and response files could not invoke other response files.
+- Can now support long-wanted feature: commenting out of some span of arguments, with
+ new hest-specific "-{" "}-" delimiters. As long as these are space-separated from
+ other args, these are left intact by sh, bash, and zsh (csh and tcsh get confused).
+ They must be kept as separate args to avoid brace expansion.
+*/
+
+// hestArg: for building up and representing one argument string
+typedef struct {
+ char *str; // the argument string
+ unsigned int len; // NOT strlen(); this includes '\0'-termination
+ airArray *strArr; // (manages str and len)
+ int source; // from hestSource* enum
+} hestArg;
+
+// hestArgVec: for building up a "vector" of arguments
+typedef struct {
+ hestArg *harg; // array of hestArg structs (not pointers to them)
+ unsigned int len; // number of arguments in this vector
+ airArray *hargArr; // (manages harg and len)
+} hestArgVec;
+
+// hestInput: what is the thing we're processing now to build up a hestArgVec
+typedef struct {
+ int source; // from the hestSource* enum
+ // ------ if source == hestSourceCommandLine ------
+ unsigned int argc;
+ const char **argv; // we do NOT own
+ unsigned int argIdx;
+ // ------ if source == hestSourceResponseFile ------
+ char *rfname; // we DO own
+ FILE *rfile; // user opens and closes this
+ // ------ if source == hestSourceDefault ------
+ const char *dfltStr; // we do NOT own
+ unsigned int dfltLen; // strlen(dfltStr)
+ // for both hestSourceResponseFile and hestSourceDefault
+ unsigned int carIdx; // which character are we on
+ // ------ general for all inputs ------
+ unsigned int dashBraceComment; /* not a boolean: how many -{ }- comment levels
+ deep are we currently; tracked this way to
+ permit nested commenting */
+} hestInput;
+
+/* hestInputStack: a way of remembering what more needs to be processed to build
+ up a hestArgVec. This full stack may be overkill, but is is the right tool for
+ handling the expansion of a @opts.txt response file, especially with the possibility
+ that response files can be expanded inside of other response files */
+typedef struct {
+ hestInput *hin; // array of hestInputs
+ unsigned int len; // size of stack of hestInputs
+ airArray *hinArr; // (manages hin and len)
+ int stdinRead; // while processing this stack we have read in "-" aka stdin
+} hestInputStack;
+
+/*
******** hestCB struct
**
** for when the thing you want to parse from the command-line is airTypeOther: not a
@@ -143,9 +208,10 @@
/* --------------------- end of user-defined fields
These are set by hest functions to remember state for the sake of other hest functions.
- It is probably a drawback of the simple design of hest that this internal state ends
- up in the same struct as the input parameters above, because it blocks some
- const-correctness opportunities that would otherwise make sense. */
+ It may be a drawback of the simple design of hest that this internal state ends
+ up in the same struct as the input parameters above, but it also makes sense to keep
+ all per-opt state in one place. The const-correctness we might want of hestParse is
+ thwarted by this internal state, but also by the important output fields, below. */
int kind, /* What kind of option is this, based on min and max:
0: (invalid; unset)
1: min == max == 0 stand-alone flag; no parameters
@@ -165,6 +231,7 @@
array of strings
3: free((*valueP)[i]) and free(*valueP), because it is a dynamically
allocated array of strings */
+ hestArgVec *havec; // the args attributed to this option
/* Since hest's beginning in 2002, the basic container for a set of options was an
array of hestOpt structs (not pointers to them, which rules out argv-style
NULL-termination of the array), also unfortunately with no other top-level container
@@ -253,64 +320,6 @@
unsigned int columns; /* number of printable columns in output */
} hestParm;
-/*
-The hestArg, hestArgVec, hestInput, and hestInputStack were all created for the TeemV2
-rewrite of hestParse, to fix bugs and limits on how the code previously worked:
-- Command-line arguments containing spaces were fully never correctly handled: the
- internal representation of one argument, amidst a space-seperated string of all
- arguments (why?!?) put back in ""-quoting, but it was never correctly implemented.
- Now the internal representation of argv is with an array data structure, not a single
- string that has to be retokenized.
-- When parsing response files, ""-quoted strings were never handled at all (nor was "#"
- appearing within a string), and response files could not invoke other response files.
-- Can now support long-wanted feature: commenting out of some span of arguments, with
- new hest-specific "-{" "}-" delimiters. As long as these are space-separated from
- other args, these are left intact by sh, bash, and zsh (csh and tcsh get confused).
- They must be kept as separate args to avoid brace expansion.
-*/
-
-// hestArg: for building up and representing one argument
-typedef struct {
- char *str;
- unsigned int len; // NOT strlen; this includes '\0'-termination
- airArray *strArr;
-} hestArg;
-
-// hestArgVec: for building up a "vector" of arguments
-typedef struct {
- hestArg *harg; /* array of hestArgs */
- unsigned int len;
- airArray *hargArr;
-} hestArgVec;
-
-// hestInput: what is the thing we're processing now to build up an arg vec
-typedef struct {
- int source; // from the hestSource* enum
- // ------ if source == hestSourceCommandLine ------
- unsigned int argc;
- const char **argv; // we do NOT own
- unsigned int argIdx;
- // ------ if source == hestSourceResponseFile ------
- char *rfname; // we DO own
- FILE *rfile; // user opens and closes this
- // ------ if source == hestSourceDefault ------
- const char *dfltStr; // we do NOT own
- unsigned int dfltLen; // strlen(dfltStr)
- // for both hestSourceResponseFile and hestSourceDefault
- unsigned int carIdx; // which character are we on
- // ------ general for all inputs ------
- unsigned int dashBraceComment; /* not a boolean: how many -{ }- comment levels
- deep are we currently; tracked this way to
- permit nested commenting */
-} hestInput;
-
-typedef struct {
- hestInput *hin; // array of hestInputs
- unsigned int len;
- airArray *hinArr;
- int stdinRead; // while processing this stack we have read in "-" aka stdin
-} hestInputStack;
-
// defaultsHest.c
HEST_EXPORT int hestDefaultVerbosity;
HEST_EXPORT int hestDefaultResponseFileEnable;
@@ -330,20 +339,19 @@
HEST_EXPORT hestArg *hestArgNix(hestArg *harg);
HEST_EXPORT void hestArgReset(hestArg *harg);
HEST_EXPORT void hestArgAddChar(hestArg *harg, char cc);
-HEST_EXPORT void hestArgAddString(hestArg *harg, const char *str);
+HEST_EXPORT void hestArgSetString(hestArg *harg, const char *str);
HEST_EXPORT hestArgVec *hestArgVecNew(void);
+HEST_EXPORT void hestArgVecReset(hestArgVec *havec);
HEST_EXPORT hestArgVec *hestArgVecNix(hestArgVec *havec);
+HEST_EXPORT void hestArgVecRemove(hestArgVec *havec, unsigned int popIdx);
HEST_EXPORT void hestArgVecAppendString(hestArgVec *havec, const char *str);
-HEST_EXPORT void hestArgVecPrint(const char *caller, const hestArgVec *havec);
+HEST_EXPORT void hestArgVecPrint(const char *caller, const char *info,
+ const hestArgVec *havec);
HEST_EXPORT hestInput *hestInputNew(void);
HEST_EXPORT hestInput *hestInputNix(hestInput *hin);
HEST_EXPORT hestInputStack *hestInputStackNew(void);
HEST_EXPORT hestInputStack *hestInputStackNix(hestInputStack *hist);
-// parsest.c
-HEST_EXPORT int hestParse2(hestOpt *opt, int argc, const char **argv, char **errP,
- const hestParm *hparm);
-
// methodsHest.c
HEST_EXPORT const int hestPresent;
HEST_EXPORT const airEnum *const hestSource;
@@ -361,6 +369,7 @@
int max, void *valueP, const char *dflt,
const char *info, unsigned int *sawP,
const airEnum *enm, const hestCB *CB);
+// Instead of hestOptAdd, use one of the 99 type-checked functions (from adders.c), below
HEST_EXPORT unsigned int hestOptAdd(hestOpt **optP,
const char *flag, const char *name,
int type, unsigned int min, int max,
@@ -369,7 +378,6 @@
... /* unsigned int *sawP,
const airEnum *enm,
const hestCB *CB */);
-// SEE ALSO (from adders.c) all the 99 type-checked versions of hestOptAdd_, below!
HEST_EXPORT unsigned int hestOptNum(const hestOpt *opt);
HEST_EXPORT hestOpt *hestOptFree(hestOpt *opt);
HEST_EXPORT void *hestOptFree_vp(void *opt);
@@ -384,6 +392,10 @@
hestParm *hparm, const char *me, const char *info,
int doInfo, int doUsage, int doGlossary);
+// parsest.c
+HEST_EXPORT int hestParse2(hestOpt *opt, int argc, const char **argv, char **errP,
+ const hestParm *hparm);
+
// usage.c
HEST_EXPORT void _hestPrintStr(FILE *f, unsigned int indent, unsigned int already,
unsigned int width, const char *_str, int bslash);
@@ -395,23 +407,29 @@
// adders.c
HEST_EXPORT void hestOptAddDeclsPrint(FILE *f);
-/* The 99 (!) non-var-args alternatives to hestOptAdd, also usefully type-specific for
-the type of value to be parsed in a way that hestOptAdd_nva cannot match. These capture
-all the common uses (and then some) of hest within Teem. They are named according to
-kind, and according to the type T of the parms to be parsed for each option:
+/* The 99 (!) non-var-args alternatives to hestOptAdd, enable more type checking because
+they non-var-args, but are also usefully type-specific for each possible type of value to
+be parsed in a way that hestOptAdd_nva cannot match. In fact, *all* possible ways of
+using hest are covered here, due to the enumeration over "kind" and over type T, which
+determines the function name as follows:
-min, max function family kind description
-min == max == 0 hestOptAdd_Flag 1 (stand-alone flag; no parameters)
-min == max == 1 hestOptAdd_1_T 2 single fixed parameter
-min == max >= 2 hestOptAdd_{2,3,4,N}_T 3 multiple fixed parameters
-min == 0; max == 1 hestOptAdd_1v_T 4 single variable parameter
-min < max; max >= 2 hestOptAdd_Nv_T 5 multiple variable parameters
+kind min, max function family description
+ 1 min == max == 0 hestOptAdd_Flag (stand-alone flag; no parameters)
+ 2 min == max == 1 hestOptAdd_1_T single fixed parameter
+ 3 min == max >= 2 hestOptAdd_{2,3,4,N}_T multiple fixed parameters
+ 4 min == 0; max == 1 hestOptAdd_1v_T single variable parameter
+ 5 min < max; max >= 2 hestOptAdd_Nv_T multiple variable parameters
-The type T can be: Bool, Short, UShort, Int, UInt, Long, ULong, Size_t, Float, Double,
-Char, String, Enum, or Other. An airEnum* is passed with the T=Enum functions, or a
-hestCB* is passed for the T=Other functions. The number of parameters *sawP that hestParm
-saw on the command-line is passed for the _Nv_ options.
+The type T can be (one for each airType enum value): Bool, Short, UShort, Int, UInt,
+Long, ULong, Size_t, Float, Double, Char, String, Enum, or Other. An `airEnum *enm` is
+passed with the T=Enum functions. A `hestCB *CB` is passed for the T=Other functions. The
+number of parms `int *sawP` that hestParm saw on the command-line is passed for the _Nv_
+options.
+For each of the 14 different `_T` types, there are 7 different families for `_1`
+(kind=2), `_2`,`_3`,`_4`,`_N` (kind=3),`_1v` (kind=4), and `_Nv` (kind=5).
+14 * 7 = 98, plus hestOptAdd_Flag makes 99 functions.
+
All declarations below were automatically generated via hest/test/decls (which calls
hestOptAddDeclsPrint), followed by clang-format. */
HEST_EXPORT unsigned int hestOptAdd_Flag(hestOpt **optP, const char *flag, int *valueP,
Modified: teem/trunk/src/hest/methodsHest.c
===================================================================
--- teem/trunk/src/hest/methodsHest.c 2025-09-22 20:54:48 UTC (rev 7469)
+++ teem/trunk/src/hest/methodsHest.c 2025-09-23 08:36:57 UTC (rev 7470)
@@ -19,8 +19,6 @@
#include "hest.h"
#include "privateHest.h"
-#include <limits.h>
-#include <assert.h>
#include <sys/ioctl.h> // for ioctl(), TIOCGWINSZ, struct winsize
#include <unistd.h> // for STDOUT_FILENO and friends
@@ -221,7 +219,7 @@
return ret;
}
-/* _hestMax(-1) == INT_MAX, otherwise _hestMax(m) == m */
+// _hestMax(-1) == INT_MAX, otherwise _hestMax(m) == m
int
_hestMax(int max) {
@@ -290,6 +288,7 @@
opt->sawP = NULL;
opt->kind = 0; /* means that this hestOpt has not been set */
opt->alloc = 0;
+ opt->havec = NULL;
opt->arrAlloc = opt->arrLen = 0;
opt->source = hestSourceUnknown;
opt->parmStr = NULL;
@@ -368,7 +367,6 @@
opt->valueP = valueP;
opt->dflt = airStrdup(dflt);
opt->info = airStrdup(info);
- opt->kind = opt_kind(min, max);
/* deal with (what used to be) var args */
opt->sawP = (5 == opt->kind /* */
? sawP
@@ -379,7 +377,9 @@
opt->CB = (airTypeOther == type /* */
? CB
: NULL);
+ opt->kind = opt_kind(min, max);
/* alloc set by hestParse */
+ opt->havec = hestArgVecNew();
/* leave arrAlloc, arrLen untouched: managed by caller */
/* yes, redundant with opt_init() */
opt->source = hestSourceUnknown;
@@ -475,17 +475,15 @@
opt->name = (char *)airFree(opt->name);
opt->dflt = (char *)airFree(opt->dflt);
opt->info = (char *)airFree(opt->info);
+ opt->havec = hestArgVecNix(opt->havec);
return;
}
hestOpt *
hestOptFree(hestOpt *opt) {
- int opi, num;
-
if (!opt) return NULL;
-
- num = opt->arrLen;
- for (opi = 0; opi < num; opi++) {
+ uint num = opt->arrLen;
+ for (uint opi = 0; opi < num; opi++) {
_hestOptFree(opt + opi);
}
free(opt);
@@ -503,14 +501,15 @@
* Pre-2025, hest did not depend on biff, and this instead took a 'char *err' that
* somehow magically had to be allocated for the size of any possible error message
* generated here. The 2025 re-write recognized that biff is the right way to accumulate
- * error messages, but the use of biff is internal to biff, but not (unusually for Teem)
+ * error messages, but the use of biff is internal to biff, and not (unusually for Teem)
* part of the the expected use of biff's API. Thus, public functions hestOptCheck() and
* hestOptParmCheck(), which are the expected way to access the functionality herein,
* take a `char **errP` arg into which a message is sprintf'ed, after allocation.
*
- * The shift to using biff removed how this function used to fprintf(stderr) some message
- * like "panic 0.5" which as completely uninformative. Now, hestOptCheck() and
- * hestOptParmCheck() fprintf(stderr) the biff message.
+ * The shift to using biff removed how this function used to fprintf(stderr) some
+ * messages like "panic 0.5" which were totally uninformative. Now, hestOptCheck() and
+ * hestOptParmCheck(), which both call _hestOPCheck, will fprintf(stderr) the informative
+ * biff message.
*
* Prior to 2023 code revisit: this used to set the "kind" in all the opts, but now that
* is more appropriately done at the time the option is added.
Modified: teem/trunk/src/hest/parseHest.c
===================================================================
--- teem/trunk/src/hest/parseHest.c 2025-09-22 20:54:48 UTC (rev 7469)
+++ teem/trunk/src/hest/parseHest.c 2025-09-23 08:36:57 UTC (rev 7470)
@@ -1450,29 +1450,22 @@
**
** free()s whatever was allocated by hestParse()
**
-** returns NULL only to facilitate use with the airMop functions.
-** You should probably just ignore this quirk.
+** ignore-able quirk: returns NULL, to facilitate use with the airMop functions
*/
void *
hestParseFree(hestOpt *opt) {
- int op, i, optNum;
- unsigned int ui;
- void **vP;
- void ***vAP;
- char **str;
- char ***strP;
-
- optNum = hestOptNum(opt);
- for (op = 0; op < optNum; op++) {
+ uint optNum = opt->arrLen;
+ for (uint op = 0; op < optNum; op++) {
+ hestArgVecNix(opt[op].havec);
opt[op].parmStr = airFree(opt[op].parmStr);
/*
printf("!hestParseFree: op = %d/%d -> kind = %d; type = %d; alloc = %d\n",
op, optNum-1, opt[op].kind, opt[op].type, opt[op].alloc);
*/
- vP = (void **)opt[op].valueP;
- vAP = (void ***)opt[op].valueP;
- str = (char **)opt[op].valueP;
- strP = (char ***)opt[op].valueP;
+ void **vP = (void **)opt[op].valueP;
+ void ***vAP = (void ***)opt[op].valueP;
+ char **str = (char **)opt[op].valueP;
+ char ***strP = (char ***)opt[op].valueP;
switch (opt[op].alloc) {
case 0:
/* nothing was allocated */
@@ -1493,11 +1486,11 @@
break;
case 2:
if (airTypeString == opt[op].type) {
- for (i = 0; i < (int)opt[op].min; i++) { /* HEY scrutinize casts */
+ for (int i = 0; i < (int)opt[op].min; i++) { /* HEY scrutinize casts */
str[i] = (char *)airFree(str[i]);
}
} else {
- for (i = 0; i < (int)opt[op].min; i++) { /* HEY scrutinize casts */
+ for (int i = 0; i < (int)opt[op].min; i++) { /* HEY scrutinize casts */
vP[i] = opt[op].CB->destroy(vP[i]);
}
}
@@ -1504,12 +1497,12 @@
break;
case 3:
if (airTypeString == opt[op].type) {
- for (ui = 0; ui < *(opt[op].sawP); ui++) {
+ for (uint ui = 0; ui < *(opt[op].sawP); ui++) {
(*strP)[ui] = (char *)airFree((*strP)[ui]);
}
*strP = (char **)airFree(*strP);
} else {
- for (ui = 0; ui < *(opt[op].sawP); ui++) {
+ for (uint ui = 0; ui < *(opt[op].sawP); ui++) {
(*vAP)[ui] = opt[op].CB->destroy((*vAP)[ui]);
}
*vAP = (void **)airFree(*vAP);
Modified: teem/trunk/src/hest/parsest.c
===================================================================
--- teem/trunk/src/hest/parsest.c 2025-09-22 20:54:48 UTC (rev 7469)
+++ teem/trunk/src/hest/parsest.c 2025-09-23 08:36:57 UTC (rev 7470)
@@ -22,9 +22,6 @@
/* parse, Parser, PARSEST: please let this be the final implementation of hestParse */
-#include <assert.h>
-#include <sys/errno.h>
-
/* variable name conventions:
harg = hestArg
havec = hestArgVec
@@ -296,7 +293,7 @@
// printf("!%s: argi %u vs argc %u\n", __func__, argi, hin->argc);
if (argi < hin->argc) {
// there are args left to parse
- hestArgAddString(tharg, hin->argv[argi]);
+ hestArgSetString(tharg, hin->argv[argi]);
// printf("!%s: now tharg->str=|%s|\n", __func__, tharg->str);
*nastP = nastBehold;
hin->argIdx++;
@@ -516,6 +513,8 @@
uint iters = 0;
hestInput *topHin;
// printf("!%s: hello hist->len %u\n", __func__, hist->len);
+ // initialize destination havec
+ airArrayLenSet(havec->hargArr, 0);
/* We `return` directly from this loop ONLY when we MUST stop processing the stack,
because of an error, or because of user asking for help.
Otherwise, we loop again. */
@@ -534,7 +533,7 @@
return 1;
}
if (nastEmpty == nast) {
- // the stack has no more tokens to give, stop looped requests for mre
+ // the stack has no more tokens to give, stop looped requests for more
if (hparm->verbosity) {
printf("%s: (iter %u, on %s) empty!\n", __func__, iters, srcstr);
}
@@ -598,7 +597,7 @@
__func__, iters, srcstr, tharg->str);
return 1;
}
- // have just added response file to stack, next iter will read from it
+ // have just added response file to stack, next iter will start reading from it
continue;
}
// this arg is not specially handled by us; add it to the arg vec
@@ -607,6 +606,8 @@
printf("%s: (iter %u, on %s) added |%s| to havec, now len %u\n", __func__, iters,
srcstr, tharg->str, havec->len);
}
+ // set source in the hestArg we just appended
+ (havec->harg + havec->len - 1)->source = topHin->source;
}
if (hist->len && nast == nastEmpty) {
biffAddf(HEST, "%s: non-empty stack (depth %u) can't generate args???", __func__,
@@ -617,18 +618,240 @@
}
/*
-hestParse(2): parse the `argc`,`argv` commandline according to the hestOpt array `opt`.
+whichOptFlag(): for which option (by index) is this the flag?
+
+Given an arg string `flarg` (which may be an flag arg like "-size" or parm arg like
+"512"), this finds which one, of the options in the given hestOpt array `opt` is
+identified by `flarg`. Returns the index of the matching option, if there is a match.
+
+If there is no match, returns UINT_MAX.
+*/
+static uint
+whichOptFlag(const hestOpt *opt, const char *flarg, const hestParm *hparm) {
+ uint optNum = opt->arrLen;
+ if (hparm->verbosity)
+ printf("%s: looking for maybe-is-flag |%s| in optNum=%u options\n", __func__, flarg,
+ optNum);
+ for (uint optIdx = 0; optIdx < optNum; optIdx++) {
+ if (hparm->verbosity)
+ printf("%s: optIdx %u |%s| ?\n", __func__, optIdx,
+ opt[optIdx].flag ? opt[optIdx].flag : "(nullflag)");
+ const char *optFlag = opt[optIdx].flag;
+ if (!optFlag) continue; // it can't be for this unflagged option
+ if (strchr(optFlag, MULTI_FLAG_SEP)) {
+ // look for both long and short versions
+ char *buff = AIR_CALLOC(strlen("--") + strlen(flarg) + strlen(optFlag) + 1, char);
+ char *ofboth = airStrdup(optFlag);
+ char *sep = strchr(ofboth, MULTI_FLAG_SEP);
+ *sep = '\0'; // break short and long into separate strings
+ /* first try the short version */
+ sprintf(buff, "-%s", ofboth);
+ if (!strcmp(flarg, buff)) return (free(buff), free(ofboth), optIdx);
+ /* else try the long version */
+ sprintf(buff, "--%s", sep + 1);
+ if (!strcmp(flarg, buff)) return (free(buff), free(ofboth), optIdx);
+ } else {
+ /* flag only comes in short version */
+ char *buff = AIR_CALLOC(strlen("-") + strlen(optFlag) + 1, char);
+ sprintf(buff, "-%s", optFlag);
+ if (!strcmp(flarg, buff)) return (free(buff), optIdx);
+ }
+ }
+ if (hparm->verbosity) printf("%s: no match, returning UINT_MAX\n", __func__);
+ return UINT_MAX;
+}
+
+/* identStr sprints into `ident` (and returns same `ident`)
+ a way to identify `opt` in error and usage messages */
+static char *
+identStr(char *ident, const hestOpt *opt) {
+ if (opt->flag) {
+ if (strchr(opt->flag, MULTI_FLAG_SEP)) {
+ char *fcopy = airStrdup(opt->flag);
+ char *sep = strchr(fcopy, MULTI_FLAG_SEP);
+ *sep = '\0';
+ sprintf(ident, "\"-%s%c--%s\" option", fcopy, MULTI_FLAG_SEP, sep + 1);
+ free(fcopy);
+ } else {
+ sprintf(ident, "\"-%s\" option", opt->flag);
+ }
+ } else {
+ sprintf(ident, "\"<%s>\" option", opt->name);
+ }
+ return ident;
+}
+
+/*
+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).
+
+In the case of variable 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
+parameter args, not the flag arg that identified which option was being set.
+
+As a result of this work, the passed `havec` is shortened: all args associated with
+flagged opts are removed, so that later work can extract args for unflagged opts.
+
+The sawP information is not set here, since it is better set at the final value parsing
+time, which happens after defaults are enstated.
+
+This is where, thanks to the action of whichOptFlag(), "--" (and only "--" due to
+VAR_PARM_STOP_FLAG) is used as a marker for the end of a flagged variable parameter
+option. AND, the "--" marker is removed from `havec`.
+*/
+static int
+havecExtractFlagged(hestOpt *opt, hestArgVec *havec, const hestParm *hparm) {
+ char ident1[AIR_STRLEN_HUGE + 1], ident2[AIR_STRLEN_HUGE + 1];
+ uint argIdx = 0;
+ hestOpt *theOpt = NULL;
+ while (argIdx < havec->len) { // NOTE: havec->len may decrease within an interation!
+ hestArg *theArg = havec->harg + argIdx;
+ if (hparm->verbosity) {
+ printf("%s: ------------- argIdx = %u (of %u) -> argv[argIdx] = |%s|\n", __func__,
+ argIdx, havec->len, theArg->str);
+ }
+ uint optIdx = whichOptFlag(opt, theArg->str, hparm);
+ if (UINT_MAX == optIdx) {
+ // theArg->str is not a flag for any option, move on to next arg
+ argIdx++;
+ if (hparm->verbosity)
+ printf("%s: |%s| not a flag arg, continue\n", __func__, theArg->str);
+ continue;
+ }
+ // else theArg->str is a flag for option with index optIdx aka theOpt
+ theOpt = opt + optIdx;
+ if (hparm->verbosity)
+ printf("%s: argv[%u]=|%s| is flag of opt %u \"%s\"\n", __func__, argIdx,
+ theArg->str, optIdx, theOpt->flag);
+ /* see if we can associate some parameters with the flag */
+ if (hparm->verbosity) printf("%s: any associated parms?\n", __func__);
+ uint parmNum = 0;
+ int hitEnd = AIR_FALSE;
+ int varParm = (5 == opt[optIdx].kind);
+ char VPS[3] = {'-', VAR_PARM_STOP_FLAG, '\0'};
+ int hitVPS = AIR_FALSE;
+ uint nextOptIdx = 0; // what is index of option who's flag we see next
+ while (AIR_INT(parmNum) < _hestMax(theOpt->max) // parmNum is plausible # parms
+ && !(hitEnd = !(argIdx + 1 + parmNum < havec->len)) // and have valid index
+ && (!varParm // and either this isn't a variable parm opt
+ || // or, it is a varparm opt and we aren't looking at "--"
+ !(hitVPS = !strcmp(VPS, (theArg + 1 + parmNum)->str)))
+ && UINT_MAX // and we aren't looking at start of another flagged option
+ == (nextOptIdx = whichOptFlag(opt, (theArg + 1 + parmNum)->str,
+ hparm))) {
+ parmNum++;
+ if (hparm->verbosity)
+ printf("%s: optIdx %d |%s|: |%s| --> parmNum --> %d\n", __func__, optIdx,
+ theOpt->flag, (theArg + 1 + parmNum - 1)->str, parmNum);
+ }
+ /* we stopped because we got the max number of parameters, or
+ we hitEnd, or
+ varParm and we hitVPS, or
+ we hit the start of another flagged option (indicated by nextOptIdx) */
+ if (hparm->verbosity)
+ printf("%s: optIdx %d |%s|: stopped w/ "
+ "parmNum=%u hitEnd=%d hitVPS=%d nextOptIdx=%u\n",
+ __func__, optIdx, theOpt->flag, parmNum, hitEnd, hitVPS, nextOptIdx);
+ if (parmNum < theOpt->min) { // didn't get required min # parameters
+ if (hitEnd) {
+ biffAddf(HEST,
+ "%s: hit end of args before getting %u parameter%s "
+ "for %s (got %u)",
+ __func__, theOpt->min, theOpt->min > 1 ? "s" : "",
+ identStr(ident1, theOpt), parmNum);
+ } else if (hitVPS) {
+ biffAddf(HEST,
+ "%s: hit \"-%c\" (variable-parameter-stop flag) before getting %u "
+ "parameter%s for %s (got %u)",
+ __func__, VAR_PARM_STOP_FLAG, theOpt->min, theOpt->min > 1 ? "s" : "",
+ identStr(ident1, theOpt), parmNum);
+ } else if (UINT_MAX != nextOptIdx) {
+ biffAddf(HEST, "%s: saw %s before getting %u parameter%s for %s (got %d)",
+ __func__, identStr(ident2, opt + nextOptIdx), theOpt->min,
+ theOpt->min > 1 ? "s" : "", identStr(ident1, theOpt), parmNum);
+ } else {
+ biffAddf(HEST,
+ "%s: sorry, confused about not getting %u "
+ "parameter%s for %s (got %d)",
+ __func__, theOpt->min, theOpt->min > 1 ? "s" : "",
+ identStr(ident1, theOpt), parmNum);
+ }
+ return 1;
+ }
+ if (hparm->verbosity) {
+ printf("%s: ________ argv[%d]=|%s|: optIdx %u |%s| followed by %u parms\n",
+ __func__, argIdx, theArg->str, optIdx, theOpt->flag, parmNum);
+ }
+ // remember from whence this option came
+ theOpt->source = theArg->source;
+ // lose the flag argument
+ hestArgVecRemove(havec, argIdx);
+ // empty any pior parm args learned for this option
+ hestArgVecReset(theOpt->havec);
+ for (uint pidx = 0; pidx < parmNum; pidx++) {
+ // theArg still points to the next arg (at index argIdx) for this option
+ hestArgVecAppendString(theOpt->havec, theArg->str);
+ hestArgVecRemove(havec, argIdx);
+ }
+ if (hitVPS) {
+ // drop the variable-parameter-stop flag
+ hestArgVecRemove(havec, argIdx);
+ }
+ if (hparm->verbosity) {
+ char info[AIR_STRLEN_HUGE + 1];
+ sprintf(info, "main havec after extracting optIdx %u |%s| and %u parms", optIdx,
+ theOpt->flag, parmNum);
+ hestArgVecPrint(__func__, info, havec);
+ sprintf(info, "optIdx %u |%s|'s own havec", optIdx, theOpt->flag);
+ hestArgVecPrint(__func__, info, theOpt->havec);
+ }
+ // do NOT increment argIdx
+ }
+
+ /* make sure that flagged options without default were given */
+ uint optNum = opt->arrLen;
+ for (uint opi = 0; opi < optNum; opi++) {
+ theOpt = opt + opi;
+ if (1 != theOpt->kind // this kind of option can take a parm
+ && theOpt->flag // and this is a flagged option we should have handled above
+ && theOpt->dflt // and this option has no default
+ && (hestSourceUnknown == theOpt->source)) { // but this option hasn't been set
+ biffAddf(HEST, "%s: didn't get required %s\n", __func__, identStr(ident1, theOpt));
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/*
+hestParse(2): parse the `argc`,`argv` commandline according to the hestOpt array `opt`,
+and as tweaked by settings in (if non-NULL) the given `hestParm *_hparm`. If there is
+an error, an error message string describing it in detail is generated and
+ - if errP: *errP is set to the newly allocated error message string
+ NOTE: it is the caller's responsibility to free() it later
+ - if !errP: the error message is fprintf'ed to stderr
The basic phases of parsing are:
0) Error checking on given `opt` array
+1) Generate internal representation of command-line that includes expanding any response
+ files; this is the `hestArgVec *havec`.
+2) From `havec`, extract the args that are attributable to flagged and unflagged options,
+ moving each `hestArg` instead into 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
*/
int
hestParse2(hestOpt *opt, int argc, const char **argv, char **errP,
const hestParm *_hparm) {
+ airArray *mop = airMopNew(); // initialize the mop
- // -------- initialize the mop
- airArray *mop = airMopNew();
-
- // -------- make exactly one of (given) _hparm and (our) hparm non-NULL
+ // make exactly one of (given) _hparm and (our) hparm non-NULL
hestParm *hparm = NULL;
if (!_hparm) {
hparm = hestParmNew();
@@ -661,7 +884,7 @@
printf("%s: _hestOPCheck passed\n", __func__);
}
- // -------- allocate the state we use during parsing
+ // allocate the state we use during parsing
hestInputStack *hist = hestInputStackNew();
airMopAdd(mop, hist, (airMopper)hestInputStackNix, airMopAlways);
hestArgVec *havec = hestArgVecNew();
@@ -672,7 +895,7 @@
printf("%s: parsing state allocated\n", __func__);
}
- // -------- initialize input stack w/ given argc,argv, then process it
+ // --1--1--1--1--1-- initialize input stack w/ given argc,argv, process it
if (histPushCommandLine(hist, argc, argv, HPARM)
|| histProcess(havec, &(opt->helpWanted), tharg, hist, HPARM)) {
DO_ERR("problem with initial processing of command-line");
@@ -681,7 +904,7 @@
}
if (HPARM->verbosity > 1) {
// have finished input stack, what argvec did it leave us with?
- hestArgVecPrint(__func__, havec);
+ hestArgVecPrint(__func__, "after histProcess", havec);
}
if (opt->helpWanted) {
// once the call for help is made, we respect it: clean up and return
@@ -689,12 +912,32 @@
return 0;
}
- // ( extract, process: make little argvec for each opt )
- // extract given flagged options
- // extract given unflagged options
- // process default strings of not-given options
- // set value(s) from per-opt argvec
+ // --2--2--2--2--2-- extract args associated with flagged and unflagged opt
+ // HEY initialize internal working fields of each opt
+ if (havecExtractFlagged(opt, havec, HPARM) /*
+ || havecExtractUnflagged(opt, havec, HPARM) */) {
+ DO_ERR("problem extracting args for options");
+ airMopError(mop);
+ return 1;
+ }
+ // HEY: verify that there were no errant extra args?
+#if 0
+ /* --3--3--3--3--3-- process defaults strings for opts that weren't user-supplied
+ Like havecExtract{,Un}Flagged, this builds up opt->havec, but does not parse it */
+ if (optProcessDefaults) {
+ DO_ERR("problem with processing defaults");
+ airMopError(mop);
+ return 1;
+ }
+
+ // --4--4--4--4--4-- Finally, parse the args and set values
+ if (optSetValues) {
+ DO_ERR("problem with setting values");
+ airMopError(mop);
+ return 1;
+ }
+#endif
airMopOkay(mop);
return 0;
}
Modified: teem/trunk/src/hest/privateHest.h
===================================================================
--- teem/trunk/src/hest/privateHest.h 2025-09-22 20:54:48 UTC (rev 7469)
+++ teem/trunk/src/hest/privateHest.h 2025-09-23 08:36:57 UTC (rev 7470)
@@ -27,6 +27,10 @@
It is still the case, however, the hest users do not need to call into biff */
#include <teem/biff.h>
+#include <limits.h> // for UINT_MAX
+#include <assert.h>
+#include <sys/errno.h>
+
typedef unsigned int uint;
// pre-TeemV2, these used to be change-able defaults in defaultsHest.c:
Modified: teem/trunk/src/hest/test/argv.c
===================================================================
--- teem/trunk/src/hest/test/argv.c 2025-09-22 20:54:48 UTC (rev 7469)
+++ teem/trunk/src/hest/test/argv.c 2025-09-23 08:36:57 UTC (rev 7470)
@@ -51,22 +51,16 @@
printf("%s: |%s|\n", argv[0], harg->str);
hestArgAddChar(harg, '!');
printf("%s: |%s|\n", argv[0], harg->str);
- hestArgAddString(harg, "bingo bob lives\n");
+ hestArgSetString(harg, "bingo bob lives\n");
printf("%s: |%s|\n", argv[0], harg->str);
hestArgNix(harg);
hestArgVec *havec = hestArgVecNew();
- hestArgVecPrint(argv[0], havec);
- hestArgVecAppendString(havec, "this");
- hestArgVecPrint(argv[0], havec);
- hestArgVecAppendString(havec, "is");
- hestArgVecPrint(argv[0], havec);
- hestArgVecAppendString(havec, "totally");
- hestArgVecPrint(argv[0], havec);
- hestArgVecAppendString(havec, "");
- hestArgVecPrint(argv[0], havec);
+ hestArgVecPrint(argv[0], "step 0", havec);
+ hestArgVecAppendString(havec, "banana");
+ hestArgVecPrint(argv[0], "step 1", havec);
hestArgVecAppendString(havec, "bonkers");
- hestArgVecPrint(argv[0], havec);
+ hestArgVecPrint(argv[0], "step 2", havec);
hestArgVecNix(havec);
exit(0);
Modified: teem/trunk/src/hest/test/tparse.c
===================================================================
--- teem/trunk/src/hest/test/tparse.c 2025-09-22 20:54:48 UTC (rev 7469)
+++ teem/trunk/src/hest/test/tparse.c 2025-09-23 08:36:57 UTC (rev 7470)
@@ -36,8 +36,10 @@
hparm->responseFileEnable = AIR_TRUE;
hparm->verbosity = 10;
+ int verb;
+ hestOptAdd_1_Int(&opt, "v", "verb", &verb, "0", "verbosity");
int res[2];
- hestOptAdd_2_Int(&opt, "res", "sx sy", res, NULL, "image resolution");
+ hestOptAdd_2_Int(&opt, "s,size", "sx sy", res, NULL, "image resolution");
int flag;
hestOptAdd_Flag(&opt, "b,bingo", &flag, "a flag");
char *err = NULL;
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|