|
From: <kin...@us...> - 2025-09-25 07:47:59
|
Revision: 7485
http://sourceforge.net/p/teem/code/7485
Author: kindlmann
Date: 2025-09-25 07:47:57 +0000 (Thu, 25 Sep 2025)
Log Message:
-----------
still hacking, and changed terminology of variable number of parameters from variable to variadic
Modified Paths:
--------------
teem/trunk/src/hest/README.md
teem/trunk/src/hest/adders.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/test/tparse.c
Modified: teem/trunk/src/hest/README.md
===================================================================
--- teem/trunk/src/hest/README.md 2025-09-24 21:20:39 UTC (rev 7484)
+++ teem/trunk/src/hest/README.md 2025-09-25 07:47:57 UTC (rev 7485)
@@ -4,8 +4,10 @@
The purpose of `hest` is to bridge the `int argc`, `char *argv[]` command-line arguments and a set of C variables that need to be set for a C program to run. The variables can be of most any type (boolean, `int`, `float`, `char *` strings, or user-defined types), and the variables can hold single values (such as `float thresh`) or multiple values (such as `float RGBA[4]`).
-`hest` was created in 2002 out of frustration with how limiting other C command-line parsing libraries were, and has become essential for the utility of tools like `unu`. To the extent that `hest` bridges the interactive command-line with compiled C code, it has taken on some of the roles that in other contexts are served by scripting languages with C extensions. The `hest` code was revisited in 2023 to add long-overdue support for `--help`, and to add typed functions for specifying options like `hestOptAdd_4_Float`. Re-writing the code in 2025 finally fixed long-standing bugs with how quoted strings were handled and how response files were parsed, and to add `-{`, `}-` comments.
+`hest` was created in 2002 out of frustration with how limited other C command-line parsing libraries were, and has become essential for the utility of tools like `unu`. To the extent that `hest` bridges the interactive command-line with compiled C code, it approaches some of the roles that in other contexts are served by scripting languages with C extensions. The `hest` code was revisited in 2023 to add long-overdue support for `--help`, and to add typed functions for specifying options like `hestOptAdd_4_Float`. Re-writing the code in 2025 finally fixed long-standing bugs with how quoted strings were handled and how response files were parsed, and to add `-{`, `}-` comments.
+`hest` is powerful and not simple. This note attempts to give a technical description useful for someone thinking about using `hest`, as well as anyone trying wrap their head around the `hest` source code, including its author.
+
## Terminology and concepts
`hest` has possibly non-standard terminology for the elements of command-line parsing. Here is a bottom-up description of the command-line and what `hest` can do with it:
@@ -17,12 +19,11 @@
- Separately, and possibly confusingly, `hest`'s behavior has many knobs and controls, stored in the `hestParm` struct. The pointer-to-struct is always named `hparm` in the code, to try to distinguish it from the parameters appearing on the command-line.
- An _option_ determines how to set one C variable. In the C code, one `hestOpt` struct stores everything about how to parse one option, _and_ the results of that parsing. An array of `hestOpt` structs (not pointers to structs) is how a `hest`-using program communicates what it wants to learn from the command-line. The `hestOpt` array is usually built up by calls to one of the `hestOptAdd` functions.
- On the command-line, the option may be specified by a flag and its associated parms; this is a _flagged_ option. Options may also be _unflagged_, or what others call "positional" arguments, because which C variable is set by parsing that option is disambiguated by the option's position on the command-line, and the corresponding ordering of `hestOpt` structs in the `hestOpt` array.
-- `hest`'s goal is to process _all_ the args in the `argv` you give it. In this way `hest` is more like Python's `argparse` that tries to make sense of the entire command-line, rather than, say, POSIX `getopt` which sees some parts of `argv` as flag-prefixed options but leaves the rest as "operands" for something else downstream to interpret. `hest` doesn't know what an operand is, and tries to slot every `argv` element into an argument of some (possibly unflagged) option.
-- An option may have no parms, one parm, a fixed number of parms, or a variable number of parms. Unflagged options must have one or more parms. With `mv *.txt dir`, the `*.txt` filenames could be parsed as a variable number of parms for an unflagged option, and `dir` would be a fixed single parm for a second unflagged option. Flagged options can appear in any order on the command-line, and the same option can be repeated: later appearances over-ride earlier appearances.
-- Sometimes multiple command-line options need to be saved and re-used together, over a time span longer than one shell or any variables set it. Command-line options can thus be stored in _response files_, and the contents of response files effecively expanded into the command-line. Response files can have comments, and response files can name other response files.
+- An option may have no parms, one parm, a fixed number of parms, or a variable number of parms; `hest` calls these _variadic_ options to separate the description of the options from the information (the C _variable_) that the option describes. Unflagged options must have one or more parms. With `mv *.txt dir`, the `*.txt` filenames could be parsed as a variadic parm list for an unflagged option, and `dir` would be a fixed single parm for a second unflagged option.
+- Sometimes multiple command-line options need to be saved and re-used together, over a time span longer than any one shell. Command-line options can thus be stored in _response files_, and the contents of response files effecively expanded into the command-line. Response files can have comments (from `#` to end of line, just like shell scripts), and response files can in turn name other response files.
- The main `hest` function that does the parsing is `hestParse`. Its job is to set one variable (which may have multiple components) for every `hestOpt`. Information for setting each variable can come from the command-line, or from the default string set in the `hestOpt`, but it has to come from somewhere. Essentially, if no default string is given, then the option _must_ be set on the command-line (or a response file named there). In this sense, `hest`'s "options" are badly named, because they are not really optional.
-Note that `hest` does not attempt to follow POSIX conventions (or terminology) for command-line descriptions, because those conventions don't empower the kind of expressivity and flexibility that motivated `hest`'s creation. POSIX does not encompass the scientific computing and visualization contexts that Teem was built for.
+Pre-dating `hest` are the [POSIX conventions for command-line arguments](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html#tag_12_01), wherein the elements of `argv` can be first "options" and then "operands", where "options" are indicated by an initial `-` character, and may have zero or more "option-arguments" (what `hest` calls "parameters"). However, `hest` does not attempt to follow POSIX conventions (or terminology) for command-line usage. In particular, `hest` has no idea of an "operand" while it tries to interpret _every_ the `argv` args as some argument of some (possibly unflagged) option. Disregard for POSIX has not limited `hest`'s expressivity and flexibility, or its utility for the scientific computing and visualization contexts that it was built for.
## The different `kind`s of options, and how to `hestOptAdd` them.
@@ -39,12 +40,12 @@
| . / . /
| . / . /
| (5) multiple | (3) multiple
- 2--| variable | fixed
+ 2--| variadic | fixed
| parms | parms
| hOA_Nv_T | hOA_{2,3,4,N}_T
|.............................../
| (4) single | (2) single
- 1--| variable | fixed
+ 1--| variadic | fixed
| parm | parm
| hOA_1v_T | hOA_1_T
|...............|/
@@ -57,29 +58,36 @@
min > 0 1 2
```
-The `kind` of option is mostly independent of whether it is flagged or unflagged, and independent of being optional (due to the `hestOpt` having a default string) versus required (when no default is given). The one wrinkle is that unflagged options must have at least one parameter (i.e. `min` > 0), either by the command-line or via a default string. An unflagged option allowed to have zero parameters has no textual existence and so is unactionable. Thus for unflagged options, `kind`s 1 and 4 are ruled out; kind 5 is possible with `min` >= 1. This is likely already too much low-level information: users of `hest` will likely not ever need to worry about `kind`, and certainly `kind` is not part of the API calls to create options and parse command-lines.
+The `kind` of option is mostly independent of whether it is flagged or unflagged, and independent of being optional (due to the `hestOpt` having a default string) versus required (when no default is given). The one wrinkle is that unflagged options must have at least one parameter (i.e. `min` > 0), either by the command-line or via a default string. An unflagged option allowed to have zero parameters has no explicit textual existence, which seems out-of-bounds for a command-line parser. Thus for unflagged options, `kind`s 1 and 4 are ruled out, and kind 5 is possible with `min` >= 1. This is likely already too much low-level information: users of `hest` will probably never need to worry about `kind`, and certainly `kind` is not part of the API calls to create options and parse command-lines.
Some **concrete examples** may be helpful for understanding `hest`'s utility ... (IN PROGRESS) ...
... Give some specific examples, flagged and unflagged ...
.. show a response file being used, show -{ }- commenting
-## Limits on what may be parsed, and the `--` flag
+## The over-all process `hestParse`, its limits, and the `--` flag
-If there are no variable parameter options (i.e. limiting oneself to kinds 1, 2, and 3), then there is no limit on options may be used, including the intermixing of flagged and unflagged options, because there is then zero ambiguity about how to attribute a given command-line argument to the parameters of some option.
+Given an `argc`,`argv` command-line and a `hestOpt` array describing the options to parse, `hestParse` must first answer: **which elements of `argv` are associated with which options?** If there are no variadic options (i.e. limiting oneself to kinds 1, 2, and 3), then the answer is straight-forward, even flagged options being able to appear on the command-line in any order. The option set has some fixed number of slots. The flag arguments for the flagged options, and the position of arguments of unflagged options, implies how to put each `argv` element into each slot.
-Things became more complicated with variable parameter options. ... two unflagged variable options wouldn't make sense ...
+Things became more complicated with variadic options. Suppose ... two unflagged variadic options wouldn't make sense ...
-In [POSIX command-lines](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html#tag_12_01), the elements of `argv` can be first "options" and then "operands", where "options" are
-indicated by something starting with `-`, and may have 0 or more "option-arguments" (what `hest` calls parameters). Then, according to Guideline 10:
+Understanding how `hest` attributes arguments to options starts with knowing the main phases of `hestParse`:
-> The first -- argument that is not an option-argument should be accepted as a
-> delimiter indicating the end of options. Any following arguments should be treated
-> as operands, even if they begin with the `-` character.
-> So `--` marks the end of some "option-arguments".
+0. Validate the given `hestOpt` array
+1. Convert given `argc`,`argv`, to an internal (`hestArgVec`) representation of the argument vector (called `havec` in the code). This is the phase in which response files are expanded and `-{`,`}-` comments are interpreted.
+1. 1. Extract from `havec` all the arguments associated with flagged options: the flag args, any immediately subsequent associated parm args.
+ 1. What remains in `havec` are the concatenation of args associated with the unflagged (positional) options. As long as there is at most one unflagged variadic option, there is an unambigious assignment of arguments to options.
+1. For any options not yet processed, tokenize into arguments the option's default string, and save these per-option.
+1. Finally, parse the per-option arguments to set the value(s) of C variable pointed to by each `hestOpt`. Each `hestOpt` also remembers the source of information for setting the variable (e.g. command-line vs default string).
-Even though, as noted above, `hest` itself does not traffic in "operands", and is _not_ currently compliant with the POSIX behavior just quoted, it does make _limited_ use of the `--` flag.
+Fans of [POSIX Guidelines](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap12.html#tag_12_02) may know that Guideline 10 describes the role of `--`:
-The limits in using variable parameter options are:
+> The first -- argument that is not an option-argument should be accepted as a delimiter indicating the end of options. Any following arguments should be treated as operands, even if they begin with the `-` character. So `--` marks the end of some "option-arguments".
-- There can be only one _unflagged_ multiple variable parameter option (kind 5). Having more than one creates ambiguity about which option consumes which arguments, and even though `--` could be used for demarcating them, this seems fragile and has not been implemented.
-- ... The `--` flag indicates the explicit end of a _flagged_ variable parameter options.
+Even though, as noted above, `hest` itself does not traffic in "operands" or follow POSIX, it does borrow `--` in a limited way to help with step 2.1 above: `--` marks the end of arguments for a _flagged_ variadic option, to demarcate them from any arguments intended for _unflagged_ options (variadic or not). In this sense, the `--` mark is more about seperating steps 2.1 and 2.2 above, than it is about separating options from operands.
+
+`hest`'s limitations in option variety and processing are:
+
+- There can be only one _unflagged_ multiple variadic option (kind 5). Having more than one creates ambiguity about which option consumes which arguments, and `hest` currently attempts nothing (not even `--`) to resolve this. There can be, however, more than one _flagged_ multiple variadic options.
+- The `--` flag indicates the explicit end of arguments for a _flagged_ variadic option.
+- `hest` strives to interpret the entire `argv` argument vector; there is currently no way to tell `hest` (via `--` or otherwise): "stop processing `argv` here, and leave the as operands for something else to interpret".
+- Flagged options can appear in any order on the command-line, and the same option can be repeated: the last appearances over-rides all earlier appearances. `hest` currently cannot remember a list of occurance of repeated options (unlike, say, `sed -e ... -e ... `. )
Modified: teem/trunk/src/hest/adders.c
===================================================================
--- teem/trunk/src/hest/adders.c 2025-09-24 21:20:39 UTC (rev 7484)
+++ teem/trunk/src/hest/adders.c 2025-09-25 07:47:57 UTC (rev 7485)
@@ -104,11 +104,11 @@
_T_ = simple scalar types
_E_ = airEnum
_O_ = Other (needs hestCB)
-_1 = single parameter, either fixed (kind 2) or variable (kind 4)
+_1 = single parameter, either fixed (kind 2) or variadic (kind 4)
_M = 2, 3, or 4 = COMPILE-TIME fixed # of parameters (kind 3)
(these exist as a convenience, covering many common hest uses)
_N = RUN_TIME user-given fixed # of parameters (still kind 3)
-_V = RUN_TIME variable # of parameters (kind 5)
+_V = RUN_TIME variadic # of parameters (kind 5)
*/
@@ -122,7 +122,7 @@
_1 _1 _1 _1 _1 _1 _1 _1 _1 _1 _1 _1 _1 _1 _1 _1 _1 _1 _1 _1 _1 _1 _1 _1 _1 _1
*/
-/* "_1v_" functions (declared via _DCL_T_0) are for a single variable parm opt (kind 4),
+/* "_1v_" functions (declared via _DCL_T_0) are for a single variadic parm opt (kind 4),
"_1_" functions (declared via _DCL_T_1) are for a single fixed parm opt (kind 2)
*/
#define _DCL_T_0(ATYP, CTYP) \
Modified: teem/trunk/src/hest/hest.h
===================================================================
--- teem/trunk/src/hest/hest.h 2025-09-24 21:20:39 UTC (rev 7484)
+++ teem/trunk/src/hest/hest.h 2025-09-25 07:47:57 UTC (rev 7485)
@@ -164,7 +164,7 @@
** for when the thing you want to parse from the command-line is airTypeOther: not a
** simple boolean, number, string, or airEnum. hestParse() will not allocate anything to
** store individual things, though it may allocate an array in the case of a multiple
-** variable parameter option. If your things are actually pointers to things, then you
+** variadic parameter option. If your things are actually pointers to things, then you
** do the allocation in the parse() callback. In this case, you set destroy() to be
** your "destructor", and it will be called on the result of derefencing the argument
** to parse().
@@ -201,7 +201,7 @@
void *valueP; /* storage of parsed values */
char *dflt, /* default value(s) written out as string */
*info; /* description to be printed with "glossary" info */
- unsigned int *sawP; /* really OUTPUT: used ONLY for multiple variable parameter
+ unsigned int *sawP; /* really OUTPUT: used ONLY for multiple variadic parameter
options (min < max >= 2): storage of # of parsed values */
const airEnum *enm; /* used ONLY for airTypeEnum options */
const hestCB *CB; /* used ONLY for airTypeOther options */
@@ -217,8 +217,8 @@
1: min == max == 0 stand-alone flag; no parameters
2: min == max == 1 single fixed parameter
3: min == max >= 2 multiple fixed parameters
- 4: min == 0; max == 1; single variable parameter
- 5: min < max; max >= 2 multiple variable parameters
+ 4: min == 0; max == 1; single variadic parameter
+ 5: min < max; max >= 2 multiple variadic parameters
This is set by hest functions as part of building up an array of hestOpt,
and informs the later action of hestOptFree */
alloc; /* Information (set by hestParse) about whether flag is non-NULL, and what
@@ -258,7 +258,7 @@
string (storing zero or many parameters), from which hestParse ultimately parsed
whatever values were set in *valueP above. Internally, hest maintains an argc,argv-like
representation of the info to parse, but here it is joined back together into a
- space-delimited single string. Note that in the case of single variable parameter
+ space-delimited single string. Note that in the case of single variadic parameter
options used without a parameter, the value stored will be "inverted" from the string
here. */
char *parmStr;
@@ -418,8 +418,8 @@
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
+ 4 min == 0; max == 1 hestOptAdd_1v_T single variadic parameter
+ 5 min < max; max >= 2 hestOptAdd_Nv_T multiple variadic parameters
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
Modified: teem/trunk/src/hest/methodsHest.c
===================================================================
--- teem/trunk/src/hest/methodsHest.c 2025-09-24 21:20:39 UTC (rev 7484)
+++ teem/trunk/src/hest/methodsHest.c 2025-09-25 07:47:57 UTC (rev 7485)
@@ -261,7 +261,7 @@
return 4;
}
- /* else multiple variable parameters */
+ /* else multiple variadic parameters */
return 5;
}
@@ -398,7 +398,7 @@
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 variable parameter option),
+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.
*/
@@ -526,7 +526,7 @@
return 1;
}
uint optNum = opt->arrLen;
- uint varNum = 0; // number of variable-parameter options
+ uint varNum = 0; // number of variadic-parameter options
for (uint opi = 0; opi < optNum; opi++) {
if (!(AIR_IN_OP(airTypeUnknown, opt[opi].type, airTypeLast))) {
biffAddf(HEST, "%s%sopt[%u].type (%d) not in valid range [%d,%d]", _ME_, opi,
@@ -545,7 +545,7 @@
}
if (5 == opt[opi].kind && !(opt[opi].sawP)) {
biffAddf(HEST,
- "%s%sopt[%u] has multiple variable parameters (min=%u,max=%d), "
+ "%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;
@@ -607,6 +607,15 @@
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);
+ return 1;
+ }
+ }
char *tbuff = airStrdup(flag);
assert(tbuff);
// no mop, have to call free(tbuff) !
@@ -650,7 +659,7 @@
if (4 == opt[opi].kind) {
if (!opt[opi].dflt) {
biffAddf(HEST,
- "%s%sflagged single variable parameter must "
+ "%s%sflagged single variadic parameter must "
"specify a default",
_ME_);
return (free(tbuff), 1);
@@ -657,7 +666,7 @@
}
if (!strlen(opt[opi].dflt)) {
biffAddf(HEST,
- "%s%sflagged single variable parameter default "
+ "%s%sflagged single variadic parameter default "
"must be non-zero length",
_ME_);
return (free(tbuff), 1);
@@ -694,12 +703,12 @@
}
if (4 == opt[opi].kind && !opt[opi].dflt) {
biffAddf(HEST,
- "%s%sopt[%u] is single variable parameter, but "
+ "%s%sopt[%u] is single variadic parameter, but "
"no default set",
_ME_, opi);
return 1;
}
- // kind 4 = single variable parm; kind 5 = multiple variable parm
+ // kind 4 = single variadic parm; kind 5 = multiple variadic parm
varNum += (opt[opi].kind > 3 && (NULL == opt[opi].flag));
}
if (varNum > 1) {
Modified: teem/trunk/src/hest/parseHest.c
===================================================================
--- teem/trunk/src/hest/parseHest.c 2025-09-24 21:20:39 UTC (rev 7484)
+++ teem/trunk/src/hest/parseHest.c 2025-09-25 07:47:57 UTC (rev 7485)
@@ -137,7 +137,7 @@
But hestParse does not know or care about "operands": *every* element of the given argv
will be interpreted as the argument to some option, including an unflagged option (a
-variable unflagged option is how hest would support something like "cksum *.txt"). For
+variadic unflagged option is how hest would support something like "cksum *.txt"). For
hest to implement the expected behavior for "--", hest has to care about "--" only in the
context of collecting parameters to *flagged* options. But copyArgv() is upstream of that
awareness (of flagged vs unflagged), so we do not act on "--" here.
@@ -392,7 +392,7 @@
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
+VAR_PARM_STOP_FLAG) is used as a marker for the end of a *flagged* variadic parameter
option. AND, the "--" marker is removed from the argv.
*/
static int
@@ -528,7 +528,7 @@
recording the number of parameters in optParmNum[].
This is the function that has to handle the trickly logic of allowing there to be
-multiple unflagged options, one of which may have a variable number of parms; that
+multiple unflagged options, one of which may have a variadic number of parms; that
one has to be extracted last.
*/
static int
@@ -556,7 +556,7 @@
break;
}
}
- /* now, if there is a variable parameter unflagged opt, unflagVar is its
+ /* now, if there is a variadic parameter unflagged opt, unflagVar is its
index in opt[], or else unflagVar is optNum */
if (hparm->verbosity) {
printf("%sunflagVar %d\n", me, unflagVar);
@@ -579,9 +579,9 @@
airMopAdd(pmop, optParms[op], airFree, airMopAlways);
optParmNum[op] = np;
}
- /* we skip over the variable parameter unflagged option,
+ /* we skip over the variadic parameter unflagged option,
subtract from *argcP the number of parameters in all the opts which follow it,
- in order to get the number of parameters in the sole variable parameter option,
+ in order to get the number of parameters in the sole variadic parameter option,
store this in nvp */
nvp = *argcP;
for (op = nextUnflagged(unflagVar + 1, opt, optNum); op < optNum;
@@ -596,7 +596,7 @@
return 1;
}
/* else we had enough args for all the unflagged options following
- the sole variable parameter unflagged option, so snarf them up */
+ the sole variadic parameter unflagged option, so snarf them up */
for (op = nextUnflagged(unflagVar + 1, opt, optNum); op < optNum;
op = nextUnflagged(op + 1, opt, optNum)) {
np = opt[op].min;
@@ -605,7 +605,7 @@
optParmNum[op] = np;
}
- /* now we grab the parameters of the sole variable parameter unflagged opt,
+ /* now we grab the parameters of the sole variadic parameter unflagged opt,
if it exists (unflagVar < optNum) */
if (hparm->verbosity) {
printf("%s (still here) unflagVar %d vs optNum %d (nvp %d)\n", me, unflagVar, optNum,
@@ -675,7 +675,7 @@
optDfltd[optIdx] = opt[optIdx].flag && !optAprd[optIdx];
break;
case 4:
- /* -------- optional single variables -------- */
+ /* -------- optional single variadics -------- */
/* if the flag appeared (if there is a flag) but the parameter didn't, we'll
"invert" the default; if the flag didn't appear (or if there isn't a flag) and
the parameter also didn't appear, we'll use the default. In either case,
@@ -1036,7 +1036,7 @@
}
break;
case 4:
- /* -------- optional single variables -------- */
+ /* -------- optional single variadics -------- */
if (optParms[op] && vP) {
int pret;
switch (type) {
@@ -1087,8 +1087,8 @@
break;
case airTypeOther:
/* we're parsing an single "other". We will not perform the special flagged
- single variable parameter games as done above, so whether this option is
- flagged or unflagged, we're going to treat it like an unflagged single variable
+ 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() */
@@ -1130,7 +1130,7 @@
}
break;
case 5:
- /* -------- multiple variable parameters -------- */
+ /* -------- multiple variadic parameters -------- */
if (optParms[op] && vP) {
if (1 == whichCase(opt, optDfltd, optParmNum, appr, op)) {
*((void **)vP) = NULL;
@@ -1385,7 +1385,7 @@
} else {
fprintf(stderr,
"%sunexpected end-of-parameters flag \"%s\": "
- "not ending a flagged variable-parameter option\n",
+ "not ending a flagged variadic-parameter option\n",
ME, stops);
}
airMopError(mop);
Modified: teem/trunk/src/hest/parsest.c
===================================================================
--- teem/trunk/src/hest/parsest.c 2025-09-24 21:20:39 UTC (rev 7484)
+++ teem/trunk/src/hest/parsest.c 2025-09-25 07:47:57 UTC (rev 7485)
@@ -469,7 +469,6 @@
return 0;
}
-#if 0
static int
histPushDefault(hestInputStack *hist, const char *dflt, const hestParm *hparm) {
if (!(hist && dflt && hparm)) {
@@ -494,7 +493,6 @@
hist->hin[idx].carIdx = 0;
return 0;
}
-#endif
/* histProcess
Consumes args (tokens) from the stack `hist`, mostly just copying them into `havec`,
@@ -516,7 +514,7 @@
static int
histProcess(hestArgVec *havec, int *helpWantedP, hestArg *tharg, hestInputStack *hist,
const hestParm *hparm) {
- *helpWantedP = AIR_FALSE;
+ if (helpWantedP) *helpWantedP = AIR_FALSE;
int nast = nastUnknown;
uint iters = 0;
hestInput *topHin;
@@ -583,9 +581,14 @@
// else this arg is not in a comment and is not related to commenting
if (hparm->respectDashDashHelp && !strcmp("--help", tharg->str)) {
if (hestSourceCommandLine == topHin->source) {
- *helpWantedP = AIR_TRUE;
/* user asking for help halts further parsing work: user is not looking
for parsing results nor error messages about that process */
+ if (!helpWantedP) {
+ biffAddf(HEST, "%s%s(iter %u, on %s) saw \"--help\" but have NULL helpWantedP",
+ _ME_, iters, srcstr);
+ return 1;
+ }
+ *helpWantedP = AIR_TRUE;
return 0;
} else {
biffAddf(HEST, "%s%s(iter %u, on %s) \"--help\" not handled in this source",
@@ -760,14 +763,13 @@
}
/* havecExtractFlagged
-
-extracts the parameter args associated with all flagged options from the given
+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
+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
parameter args, not the flag arg that identified which option was being set.
@@ -774,11 +776,11 @@
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.
-For variable parameter options, the sawP information is not set here, since it is better
+For variadic parameter options, 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
+VAR_PARM_STOP_FLAG) is used as a marker for the end of a flagged variadic parameter
option. AND, the "--" marker is removed from `havec`.
*/
static int
@@ -819,7 +821,7 @@
AIR_INT(parmNum) < _hestMax(theOpt->max)
// and looking ahead by parmNum still gives us a valid index pai
&& !(hitEnd = !((pai = argIdx + 1 + parmNum) < havec->len))
- // and either this isn't a variable parm opt
+ // and either this isn't a variadic parm opt
&& (!varParm || // or, it is a varparm opt, and we aren't looking at "--"
!(hitVPS = !strcmp(VPS, havec->harg[pai]->str)))
&& UINT_MAX // and we aren't looking at start of another flagged option
@@ -846,7 +848,7 @@
parmNum);
} else if (hitVPS) {
biffAddf(HEST,
- "%s%shit \"-%c\" (variable-parameter-stop flag) before getting %u "
+ "%s%shit \"-%c\" (variadic-parameter-stop flag) before getting %u "
"parameter%s for %s (got %u)",
_ME_, VAR_PARM_STOP_FLAG, theOpt->min, theOpt->min > 1 ? "s" : "",
identStr(ident1, theOpt), parmNum);
@@ -880,7 +882,7 @@
return 1;
}
if (hitVPS) {
- // drop the variable-parameter-stop flag
+ // drop the variadic-parameter-stop flag
hestArgNix(hestArgVecRemove(havec, argIdx));
}
if (hparm->verbosity) {
@@ -937,7 +939,7 @@
saves them in the corresponding opt[].havec
This is the function that has to handle the trickly logic of allowing there to be
-multiple unflagged options, only one of which may have a variable number of parms; that
+multiple unflagged options, only one of which may have a variadic number of parms; that
one has to be extracted last.
*/
static int
@@ -957,7 +959,7 @@
printf("%s: optNum %u != unflag1st %u: have %u (of %u) unflagged options\n",
__func__, optNum, unflag1st, unflagNum, optNum);
}
- uint unflagVar; // the index of the unflagged variable parm option
+ uint unflagVar; // the index of the unflagged variadic parm option
for (unflagVar = unflag1st; //
unflagVar < optNum;
unflagVar = nextUnflagged(unflagVar + 1, opt)) {
@@ -968,14 +970,14 @@
_ME_, unflagVar);
return 1;
} else {
- // kind 5 = multiple variable parm; we allow one of these
+ // kind 5 = multiple variadic parm; we allow one of these
break;
}
}
}
- /* now, if there is a variable parameter unflagged opt (NOTE that _hestOPCheck()
+ /* now, if there is a variadic parameter unflagged opt (NOTE that _hestOPCheck()
ensured that there is at most one of these), then unflagVar is its index in opt[].
- If there is no variable parameter unflagged opt, unflagVar is optNum. */
+ If there is no variadic parameter unflagged opt, unflagVar is optNum. */
if (hparm->verbosity) {
printf("%s: unflagVar %u %s\n", __func__, unflagVar,
(unflagVar == optNum ? "==> there is no unflagged var parm opt"
@@ -995,9 +997,9 @@
return 1;
}
}
- /* we skip over the variable parameter unflagged option, subtract from havec->len the
+ /* we skip over the variadic parameter unflagged option, subtract from havec->len the
number of parameters in all the opts which follow it, in order to get the number of
- parameters in the sole variable parameter option; store this in nvp */
+ parameters in the sole variadic parameter option; store this in nvp */
int nvp = AIR_INT(havec->len);
for (uint opi = nextUnflagged(unflagVar + 1, opt); opi < optNum;
opi = nextUnflagged(opi + 1, opt)) {
@@ -1013,7 +1015,7 @@
return 1;
}
/* else we had enough args for all the unflagged options following
- the sole variable parameter unflagged option, so snarf them up */
+ the sole variadic parameter unflagged option, so snarf them up */
for (uint opi = nextUnflagged(unflagVar + 1, opt); opi < optNum;
opi = nextUnflagged(opi + 1, opt)) {
if (havecTransfer(opt + opi, havec, nvp, opt[opi].min /* min == max */, hparm)) {
@@ -1023,13 +1025,13 @@
}
}
- /* now, finally, we grab the parameters of the sole variable parameter unflagged opt,
+ /* now, finally, we grab the parameters of the sole variadic parameter unflagged opt,
if it exists (unflagVar < optNum) */
if (hparm->verbosity) {
printf("%s: (still here) unflagVar %u vs optNum %u (nvp %d)\n", __func__, unflagVar,
optNum, nvp);
}
- if (unflagVar < optNum) { // so there is a variable parameter unflagged opt
+ if (unflagVar < optNum) { // so there is a variadic parameter unflagged opt
if (hparm->verbosity) {
printf("%s: unflagVar=%u: min, nvp, max = %u %d %d\n", __func__, unflagVar,
opt[unflagVar].min, nvp, _hestMax(opt[unflagVar].max));
@@ -1081,7 +1083,6 @@
return 0;
}
-// char **optParms, int *optDfltd, unsigned int *optParmNum, const int *optAprd
/* optProcessDefaults
All the command-line arguments (and any response files invoked therein) should now be
processed (by transferring the arguments to per-option opt->havec arrays), but we need to
@@ -1088,102 +1089,70 @@
ensure that every option has information from which to set values. The per-option
opt->dflt string is what we look to now, to finish setting per-option opt->havec arrays
for all the options for which opt->havec have not already been set. We use
-`hestSourceUknown == opt->source` as the indicator of not already being set.
+`!opt->source` (aka hestSourceUnknown) as the indicator of not already being set.
*/
static int
-optProcessDefaults(hestOpt *opt, const hestParm *hparm) {
- // char *tmpS, ident[AIR_STRLEN_HUGE + 1];
- uint optNum = hestOptNum(opt);
- for (uint optIdx = 0; optIdx < optNum; optIdx++) {
+optProcessDefaults(hestOpt *opt, hestArg *tharg, hestInputStack *hist,
+ const hestParm *hparm) {
+ uint optNum = opt->arrLen;
+ for (uint opi = 0; opi < optNum; opi++) {
if (hparm->verbosity) {
- printf("%s: ", __func__);
- optPrint(opt + optIdx, optIdx);
+ printf("%s: INCOMING", __func__);
+ optPrint(opt + opi, opi);
}
-#if 0
- switch (opt[optIdx].kind) {
- case 1:
- /* -------- (no-parameter) boolean flags -------- */
- /* default is indeed always ignored for the sake of setting the option's value, but
- optDfltd is used downstream to set the option's source. The info came from
- the user if the flag appears, otherwise it is from the default. */
- optDfltd[optIdx] = !optAprd[optIdx];
- break;
- case 2:
- /* -------- one required parameter -------- */
- case 3:
- /* -------- multiple required parameters -------- */
- /* we'll used defaults if the flag didn't appear */
- optDfltd[optIdx] = opt[optIdx].flag && !optAprd[optIdx];
- break;
- case 4:
- /* -------- optional single variables -------- */
- /* if the flag appeared (if there is a flag) but the parameter didn't, we'll
- "invert" the default; if the flag didn't appear (or if there isn't a flag) and
- the parameter also didn't appear, we'll use the default. In either case,
- optParmNum[op] will be zero, and in both cases, we need to use the default
- information. */
- optDfltd[optIdx] = (0 == optParmNum[optIdx]);
- /* fprintf(stderr, "%s optParmNum[%d] = %u --> optDfltd[%d] = %d\n", me,
- * op, optParmNum[op], op, optDfltd[op]); */
- break;
- case 5:
- /* -------- multiple optional parameters -------- */
- /* we'll use the default if there is a flag and it didn't appear. Otherwise (with a
- flagged option), if optParmNum[op] is zero, we'll use the default if user has
- given zero parameters, yet the the option requires at least one. If an unflagged
- option can have zero parms, and user has given zero parms, then we don't use the
- default */
- optDfltd[optIdx]
- = (opt[optIdx].flag
- ? !optAprd[optIdx] /* option is flagged and flag didn't appear */
- /* else: option is unflagged, and there were no given parms,
- and yet the option requires at least one parm */
- : !optParmNum[optIdx] && opt[optIdx].min >= 1);
- /* fprintf(stderr,
- * "!%s: opt[%d].flag = %d; optAprd[op] = %d; optParmNum[op] = %d;
- * opt[op].min = %d "
- * "--> optDfltd[op] = %d\n",
- * me, op, !!opt[op].flag, optAprd[op], optParmNum[op], opt[op].min,
- * optDfltd[op]);
- */
- break;
+ if (opt[opi].source) {
+ /* the source is already set (to something other than hestSourceUnknown),
+ so there's no need for using the default */
+ continue;
}
- /* if not using the default, we're done with this option; continue to next one */
- if (!optDfltd[optIdx]) continue;
- optParms[optIdx] = airStrdup(opt[optIdx].dflt);
+ opt[opi].source = hestSourceDefault;
+ if (1 == opt[opi].kind) {
+ /* There is no meaningful "default" for stand-alone flags (and in fact
+ opt[opi].dflt is enforced to be NULL) so there is no default string to tokenize,
+ but we above set source to default for sake of completeness, and to signal that
+ the flag was not given by user */
+ continue;
+ }
+ 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) {
+ biffAddf(HEST, "%s%sopt[%u] %s needs default string but it is NULL", _ME_, opi,
+ ident);
+ return 1;
+ }
+ /* in some circumstances the default may be empty "", even if non-NULL, which means
+ that no args will be put into opt[opi].havec, and that's okay, but that's part of why
+ we set the source above to hestSourceDefault, so that we'd know the source even if it
+ isn't apparent in any of the (non-existant) args. */
if (hparm->verbosity) {
- printf("%soptParms[%d] = |%s|\n", me, optIdx, optParms[optIdx]);
+ printf("%s: looking at opt[%u] %s default string |%s|\n", __func__, opi, ident,
+ opt[opi].dflt);
}
- if (optParms[optIdx]) {
- airMopAdd(mop, optParms[optIdx], airFree, airMopAlways);
- airOneLinify(optParms[optIdx]);
- tmpS = airStrdup(optParms[optIdx]);
- optParmNum[optIdx] = airStrntok(tmpS, " ");
- airFree(tmpS);
+ if (histPushDefault(hist, opt[opi].dflt, hparm)
+ || histProcess(opt[opi].havec, NULL, tharg, hist, hparm)) {
+ biffAddf(HEST, "%s%sproblem tokenizing opt[%u] %s default string", _ME_, opi,
+ ident);
+ return 1;
}
- /* fprintf(stderr,
- * "!%s: after default; optParmNum[%d] = %u; varparm = %d (min %d vs max
- * %d)\n", me, op, optParmNum[op], AIR_INT(opt[op].min) < _hestMax(opt[op].max),
- * ((int)opt[op].min), _hestMax(opt[op].max)); */
- if (AIR_INT(opt[optIdx].min) < _hestMax(opt[optIdx].max)) {
- if (!AIR_IN_CL(AIR_INT(opt[optIdx].min), AIR_INT(optParmNum[optIdx]),
- _hestMax(opt[optIdx].max))) {
- if (-1 == opt[optIdx].max) {
- fprintf(stderr,
- "%s# parameters (in default) for %s is %d, but need %d or more\n", ME,
- identStr(ident, opt + optIdx, hparm, AIR_TRUE), optParmNum[optIdx],
- opt[optIdx].min);
- } else {
- fprintf(
- stderr,
- "%s# parameters (in default) for %s is %d, but need between %d and %d\n", ME,
- identStr(ident, opt + optIdx, hparm, AIR_TRUE), optParmNum[optIdx],
- opt[optIdx].min, _hestMax(opt[optIdx].max));
- }
- return 1;
- }
+ if (hparm->verbosity) {
+ printf("%s: DONE looking at opt[%u] %s default string |%s|\n", __func__, opi,
+ ident, opt[opi].dflt);
}
-#endif
+ /* 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
+ something analogous for args tokenized from the default strings. */
+ if (opt[opi].havec->len < opt[opi].min) {
+ biffAddf(
+ HEST,
+ "%s%sopt[%u] %s default string supplied %u args but option wants at least %u",
+ _ME_, opi, ident, opt[opi].havec->len, opt[opi].min);
+ return 1;
+ }
+ if (hparm->verbosity) {
+ printf("%s: OUTGOING", __func__);
+ optPrint(opt + opi, opi);
+ }
}
return 0;
}
@@ -1197,15 +1166,20 @@
- 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 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 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
+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
+
+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
+
What is allocated as result of work here should be freed by hestParseFree
*/
int
@@ -1286,7 +1260,7 @@
/* --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(opt, HPARM)) {
+ if (optProcessDefaults(opt, tharg, hist, HPARM)) {
DO_ERR("problem with processing defaults");
airMopError(mop);
return 1;
Modified: teem/trunk/src/hest/test/tparse.c
===================================================================
--- teem/trunk/src/hest/test/tparse.c 2025-09-24 21:20:39 UTC (rev 7484)
+++ teem/trunk/src/hest/test/tparse.c 2025-09-25 07:47:57 UTC (rev 7485)
@@ -34,7 +34,7 @@
hestParm *hparm = hestParmNew();
hparm->respectDashDashHelp = AIR_TRUE;
hparm->responseFileEnable = AIR_TRUE;
- hparm->verbosity = 1;
+ hparm->verbosity = 10;
int verb;
hestOptAdd_1_Int(&opt, "v", "verb", &verb, "0", "verbosity");
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|