|
From: <kin...@us...> - 2025-09-28 07:44:44
|
Revision: 7502
http://sourceforge.net/p/teem/code/7502
Author: kindlmann
Date: 2025-09-28 07:44:41 +0000 (Sun, 28 Sep 2025)
Log Message:
-----------
still hacking
Modified Paths:
--------------
teem/trunk/src/hest/README.md
teem/trunk/src/hest/argvHest.c
teem/trunk/src/hest/hest.h
teem/trunk/src/hest/methodsHest.c
teem/trunk/src/hest/parsest.c
teem/trunk/src/hest/privateHest.h
teem/trunk/src/hest/test/ex6.c
teem/trunk/src/hest/test/tparse.c
Modified: teem/trunk/src/hest/README.md
===================================================================
--- teem/trunk/src/hest/README.md 2025-09-26 11:50:43 UTC (rev 7501)
+++ teem/trunk/src/hest/README.md 2025-09-28 07:44:41 UTC (rev 7502)
@@ -8,6 +8,8 @@
`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.
+First, some examples ...
+
## 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:
@@ -15,11 +17,11 @@
- What `main()` gets as `char *argv[]` is the vector of _arguments_ or _args_; each one is a `char*` string. An arg can contain spaces and other arbitrary characters if the user quoted strings or escaped characters; that is between the user and shell (the shell is responsible for taking the command-line and tokenizing it into `char *argv[]`).
- Arguments like `-v` and `-size`, which identify the variable to be set, are called _flags_.
- Some flags are really just flags; no further information is given beyond their presence or absence. Other flags introduce subsequent arguments that together supply information for setting one variable.
-- The set of arguments that logically belong together (often following a flag) in the service of setting a variable are called _parameters_ (or _parms_). There is some slippage of terminology between the `char *` string that communicates the parameter, and the value (such an `int`) parsed from the parameter string.
+- The sub-sequence of arguments that logically belong together (often following a flag) in the service of setting a variable are called _parameters_ (or _parms_). There is some slippage of terminology between the `char *` string that communicates the parameter, and the value (such an `int`) parsed from the parameter string.
- 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.
-- 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.
+- One _option_ determines how to set one C variable. In the C code, one `hestOpt` struct stores everything about how to parse one option, _and_ intermediate state during the parsing process, _and_ the final results of that parsing. An _array_ of `hestOpt` structs (which we can call an _option list_) is how a `hest`-using program communicates what it wants to learn from the command-line, and how to interpret the contents of 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 communicated by a flag (e.g. `-sz`) and its associated parms (e.g. `3 640 480`); this is a _flagged_ option. Options may also be _unflagged_, or what others call "positional" arguments, because determining which option a given argument belongs to is disambiguated by where that option's `hestOpt` struct occurs with the option list.
+- 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 the variadic parms 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.
@@ -60,34 +62,36 @@
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 only 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) ...
+More examples may help show `hest`'s utility ... (IN PROGRESS) ...
... Give some specific examples, flagged and unflagged ...
.. show a response file being used, show -{ }- commenting
## The over-all process `hestParse`, its limits, and the `--` flag
-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.
+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 with 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 variadic options. Suppose ... two unflagged variadic options wouldn't make sense ...
Understanding how `hest` attributes arguments to options starts with knowing the main phases of `hestParse`:
-0. Validate the given `hestOpt` array
+1. 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).
+1. Extract from `havec` all the arguments associated with flagged options: the flag args, plus any immediately subsequent associated parm args. Arguments are transferred to the per-option arg vector within the `hestOpt` struct.
+1. What remains in the command-line `havec` should be 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, so transfer these args to their corresponding option.
+1. For any option that did not receive any args from the command-line, tokenize the option's default string and save into its per-option arg vector. Whether by command-line, response file, or default, every option should have the information it needs.
+1. Parse the per-option arg vectors (of strings) 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 (command-line vs response-file vs default string).
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 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".
-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.
+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 phase 3 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 phases 3 and 4 above, than it is about separating options from operands.
-`hest`'s limitations in option variety and processing are:
+With this context, some further details and limitations of `hest`' processing can be outlined:
-- 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 ... `. )
+- There can be only one _unflagged_ multiple variadic option (kind 5). Having more than one could create 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. But the `--` is not necessary: any option flag also marks the end of variadic args, as does the end of the command-line.
+- `hestProc` strives to interpret the entire `argc`,`argv` argument vector you give it; there is currently no way to tell `hest` (via `--` or otherwise): "stop processing `argv` here, and leave the rest as operands for something else to interpret".
+- Arguments for options (flagged or unflagged) can only come from the user (the `argc`,`argv` command-line or a response file it invokes) or from the option's default string, but not from any mix of both: there is no way to supplement arguments from the user with those from the default string. Though it will probably be more confusing than helpful, options can get arguments from a mix of the command-line and response files, since response files are effectively expanded into the command-line prior to any per-option processing.
+- Flagged options can appear in any order on the command-line, and the same option can be repeated: the last appearance over-rides all earlier appearances. `hest` currently cannot remember a list of occurance of repeated options (unlike, say, `sed -e ... -e ... `. )
+- By their nature, unflagged options can appear at most once on the command-line, or not at all if they have a default. The attribution of arguments to unflagged options depends on the ordering of the options in the option list (later arguments are always attributed to later options), but arguments are not extracted from the command-line (and moved to the per-option arg vec) if the option has a default. .... forward and back order ...
Modified: teem/trunk/src/hest/argvHest.c
===================================================================
--- teem/trunk/src/hest/argvHest.c 2025-09-26 11:50:43 UTC (rev 7501)
+++ teem/trunk/src/hest/argvHest.c 2025-09-28 07:44:41 UTC (rev 7502)
@@ -180,7 +180,8 @@
/* hestArgVecSprint goes is opposite of the shell-style tokenization of
parsest.c/argstGo: generate a single human-friendly string that could be tokenized to
recover the hestArgVec we started with.
-ChatGPT helped with prototyping.
+ChatGPT helped with prototyping plainWord and argAddQuotedString
+(and in this file, only those two functions)
Here are instructive examples of the same kind of argv pretty-printing:
https://github.com/git/git/blob/master/quote.c
and here https://www.opencoverage.net/coreutils/index_html/source_213.html
Modified: teem/trunk/src/hest/hest.h
===================================================================
--- teem/trunk/src/hest/hest.h 2025-09-26 11:50:43 UTC (rev 7501)
+++ teem/trunk/src/hest/hest.h 2025-09-28 07:44:41 UTC (rev 7502)
@@ -221,8 +221,7 @@
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
- parameters were used, that determines whether or not memory was allocated
+ alloc; /* Information (set by hestParse) about how (if at all) memory was allocated
by hestParse(). Informs later action of hestParseFree():
0: no free()ing needed
1: free(*valueP), either because it is a single string, or because was a
Modified: teem/trunk/src/hest/methodsHest.c
===================================================================
--- teem/trunk/src/hest/methodsHest.c 2025-09-26 11:50:43 UTC (rev 7501)
+++ teem/trunk/src/hest/methodsHest.c 2025-09-28 07:44:41 UTC (rev 7502)
@@ -70,70 +70,121 @@
/* 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
+the airParseStrT functions that internally use 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;
+parseSingleB(void *_out, const char *str, _hestPPack *hpp) {
+ if (!(_out && str && hpp)) return 1;
int *out = (int *)_out;
*out = airEnumVal(airBool, str);
- return (airEnumUnknown(airBool) /* which is -1 */ == *out);
+ int ret = airEnumUnknown(airBool) /* which is -1 */ == *out;
+ if (ret) {
+ snprintf(hpp->err, AIR_STRLEN_HUGE + 1, "couldnt parse \"%s\" as %s", str,
+ airBool->name);
+ } else {
+ hpp->err[0] = '\0';
+ }
+ return ret;
}
-#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))
+#define _PARSE_1_ARGS void *out, const char *str, _hestPPack *hpp
+#define _PARSE_1_BODY(typstr, format) \
+ if (!(out && str && hpp)) return 1; \
+ int ret = (1 != airSingleSscanf(str, format, out)); \
+ if (!strcmp("parseSingleI", __func__)) { \
+ printf("!%s: sscanf(|%s|, %s, %p)-> (ret=%d) %d\n", __func__, str, format, out, \
+ ret, *((int *)out)); \
+ } \
+ if (ret) { \
+ snprintf(hpp->err, AIR_STRLEN_HUGE + 1, "couldn't parse \"%s\" as", typstr); \
+ } else { \
+ hpp->err[0] = '\0'; \
+ } \
+ return ret
// 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"); }
+static int parseSingleH (_PARSE_1_ARGS) { _PARSE_1_BODY("short", "%hd"); }
+static int parseSingleUH(_PARSE_1_ARGS) { _PARSE_1_BODY("unsigned short", "%hu"); }
+static int parseSingleI (_PARSE_1_ARGS) { _PARSE_1_BODY("int", "%d" ); }
+static int parseSingleUI(_PARSE_1_ARGS) { _PARSE_1_BODY("unsigned int", "%u" ); }
+static int parseSingleL (_PARSE_1_ARGS) { _PARSE_1_BODY("long", "%ld"); }
+static int parseSingleUL(_PARSE_1_ARGS) { _PARSE_1_BODY("unsigned long", "%lu"); }
+static int parseSingleZ (_PARSE_1_ARGS) { _PARSE_1_BODY("size_t", "%z" ); }
+static int parseSingleF (_PARSE_1_ARGS) { _PARSE_1_BODY("float", "%f" ); }
+static int parseSingleD (_PARSE_1_ARGS) { _PARSE_1_BODY("double", "%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;
+parseSingleC(void *_out, const char *str, _hestPPack *hpp) {
+ if (!(_out && str && hpp)) return 1;
+ size_t slen = strlen(str);
+ int ret;
+ if (1 != slen) {
+ snprintf(hpp->err, AIR_STRLEN_HUGE + 1,
+ "expected single char but got string length %u", AIR_UINT(slen));
+ ret = 1;
+ } else {
+ char *out = (char *)_out;
+ *out = str[0];
+ hpp->err[0] = '\0';
+ ret = 0;
}
- char *out = (char *)_out;
- *out = str[0];
- return 0;
+ return ret;
}
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;
+parseSingleS(void *_out, const char *str, _hestPPack *hpp) {
+ if (!(_out && str && hpp)) return 1;
char **out = (char **)_out;
*out = airStrdup(str);
- return !!(*out); // check that we got a non-NULL strdup
+ int ret = !(*out); // a NULL pointer result of strdup is a problem
+ if (ret) {
+ snprintf(hpp->err, AIR_STRLEN_HUGE + 1, "airStrdup failed!");
+ } else {
+ hpp->alloc = 1;
+ airMopMem(hpp->cmop, *out, airMopOnError);
+ hpp->err[0] = '\0';
+ }
+ return ret;
}
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;
+parseSingleE(void *_out, const char *str, _hestPPack *hpp) {
+ if (!(_out && str && hpp)) return 1;
int *out = (int *)_out;
- const airEnum *enm = (const airEnum *)_enm;
- *out = airEnumVal(enm, str);
- return (airEnumUnknown(enm) == *out);
+ *out = airEnumVal(hpp->enm, str);
+ int ret = (airEnumUnknown(hpp->enm) == *out);
+ if (ret) {
+ snprintf(hpp->err, AIR_STRLEN_HUGE + 1, "couldn't parse \"%s\" as %s", str,
+ hpp->enm->name);
+ } else {
+ hpp->err[0] = '\0';
+ }
+ return ret;
}
+static int
+parseSingleO(void *out, const char *str, _hestPPack *hpp) {
+ if (!(out && str && hpp)) return 1;
+ char myerr[AIR_STRLEN_HUGE + 1];
+ int ret = hpp->CB->parse(out, str, myerr);
+ if (ret) {
+ if (strlen(myerr)) {
+ snprintf(hpp->err, AIR_STRLEN_HUGE + 1, "error parsing \"%s\" as %s:\n%s\n", str,
+ hpp->CB->type, myerr);
+ } else {
+ snprintf(hpp->err, AIR_STRLEN_HUGE + 1,
+ "error parsing \"%s\" as %s: returned %d\n", str, hpp->CB->type, ret);
+ }
+ } else {
+ if (hpp->CB->destroy) {
+ /* out is the address of a void*, we manage the void* */
+ hpp->alloc = 1;
+ airMopAdd(hpp->cmop, (void **)out, (airMopper)airSetNull, airMopOnError);
+ airMopAdd(hpp->cmop, *((void **)out), hpp->CB->destroy, airMopOnError);
+ }
+ }
+ return ret;
+}
-int (*const _hestParseSingle[_HEST_TYPE_MAX + 1])(void *, const char *, const void *) = {
+int (*const _hestParseSingle[_HEST_TYPE_MAX + 1])(void *, const char *, _hestPPack *) = {
NULL, //
parseSingleB, //
parseSingleH, //
@@ -147,8 +198,8 @@
parseSingleD, //
parseSingleC, //
parseSingleS, //
- parseSingleE,
- NULL // "other"
+ parseSingleE, //
+ parseSingleO //
};
#define _INVERT_SCALAR(TT, ctype) \
@@ -501,6 +552,10 @@
opt->source = hestSourceUnknown;
opt->parmStr = NULL;
opt->helpWanted = AIR_FALSE;
+
+ if (airTypeInt == type && 1 == min && 1 == max) {
+ printf("!something like %s: got valueP %p\n", "hestOptAdd_1_Int", AIR_VOIDP(valueP));
+ }
return;
}
Modified: teem/trunk/src/hest/parsest.c
===================================================================
--- teem/trunk/src/hest/parsest.c 2025-09-26 11:50:43 UTC (rev 7501)
+++ teem/trunk/src/hest/parsest.c 2025-09-28 07:44:41 UTC (rev 7502)
@@ -88,7 +88,7 @@
(although hest does not do rule 5 about parameter expansion, command substitution,
or arithmetic expansion), and here are the details about quoting:
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02
-ChatGPT helped with prototyping.
+ChatGPT helped with prototyping argstGo (and in this file, only that function)
Here is instructive example code https://github.com/nyuichi/dash.git
in src/parser.c see the readtoken1() function and the DFA there.
*/
@@ -670,11 +670,9 @@
/* 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.
+Given an arg string `flarg` (which may be an flag arg (like "-size") or not (like "512"),
+this finds which one, of the options in the given hestOpt array `opt`, is identified by
+`flarg`. If there is a match, returns the index of that option, else returns UINT_MAX.
*/
static uint
whichOptFlag(const hestOpt *opt, const char *flarg, const hestParm *hparm) {
@@ -735,13 +733,11 @@
}
/* havecTransfer
-(if `num`) moves `num` args from `hvsrc` (starting at `srcIdx`) to `opt->havec`
-This takes `hestOpt *opt` instead of `opt->havec` so that we can also take this
-time to set `opt->source` according to the incoming `hvsrc->harg[]->source`. To
-minimize cleverness, we set `opt->source` with every transferred argument, which
-means that the per-option source remembered is the source of the *last* argument of the
-option.
-*/
+(if `num`) moves `num` args from `hvsrc` (starting at `srcIdx`) to `opt->havec`. This
+takes `hestOpt *opt` instead of `opt->havec` so that we can also set `opt->source`
+according to the incoming `hvsrc->harg[]->source`. To minimize cleverness, we set
+`opt->source` with every transferred argument, which means that the per-option source
+remembered is the source of the *last* argument of the option. */
static int
havecTransfer(hestOpt *opt, hestArgVec *hvsrc, uint srcIdx, uint num,
const hestParm *hparm) {
@@ -761,7 +757,7 @@
hvsrc->len, num, srcIdx);
return 1;
}
- // okay now do the work, starting with empty destination havec
+ // okay now do the work, starting with emptying destination havec
hestArgVecReset(opt->havec);
for (uint ai = 0; ai < num; ai++) {
hestArg *harg = hestArgVecRemove(hvsrc, srcIdx);
@@ -856,7 +852,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 variadic parm opt
+ // and either this isn't a flagged variadic 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
@@ -990,7 +986,7 @@
with a little Nx2 array ufOpi2 of option indices */
uint *ufOpi2 = NULL;
if (!ufOptNum) {
- /* no unflagged options; we're ~done */
+ /* no unflagged options; we're done-ish */
goto finishingup;
}
ufOpi2 = AIR_CALLOC(2 * ufOptNum, uint);
@@ -1043,10 +1039,10 @@
if (hparm->verbosity) {
printf("%s: looking at opi = %u kind %d\n", __func__, opi, opt[opi].kind);
}
- /* Either we're not using the defaults because we know we have enough args,
- else we know we do not have enough args and yet we also don't have a default.
- Either way, we try extracting the args; in the later case just to generate a
- descriptive error message about the situation */
+ /* Either we have enough args to satisfy this option (and thus don't care if it has a
+ default), or, we do *not* have enough args and hence want to use the default. In the
+ later case, we don't have a default, that's a problem that needs an error message.
+ So, either way, we try extracting the args and report any problems encountered. */
if (opt[opi].min /* == max */ < havec->len || !opt[opi].dflt) {
havStr = hestArgVecSprint(havec, AIR_TRUE);
if (havecTransfer(opt + opi, havec, 0, opt[opi].min, hparm)) {
@@ -1075,7 +1071,7 @@
}
// same logic as above
if (opt[opi].min /* == max */ < havec->len || !opt[opi].dflt) {
- uint idx0 = (opt[opi].min < havec->len //
+ uint idx0 = (opt[opi].min < havec->len // index of first arg for this option
? havec->len - opt[opi].min //
: 0);
havStr = hestArgVecSprint(havec, AIR_TRUE);
@@ -1089,8 +1085,7 @@
}
}
- /* now, finally, we grab the parameters of the sole variadic parameter unflagged opt;
- the one with index ufVarOpi < optNum (and we're only here because it exists) */
+ // We're here because there is an unflagged variadic option (index ufVarOpi < optNum)
if (hparm->verbosity) {
printf("%s: ufVarOpi=%u min, have, max = %u %u %d\n", __func__, ufVarOpi,
opt[ufVarOpi].min, havec->len, _hestMax(opt[ufVarOpi].max));
@@ -1109,35 +1104,33 @@
if (minArg <= havec->len) {
// can satisfy option from havec, no need to use default
uint getArg = havec->len; // want to grab as many args as possible
- if (-1 != opt[ufVarOpi].max) { // but no more than needed
+ if (-1 != opt[ufVarOpi].max) { // but no more than the option asks for
getArg = AIR_MIN(getArg, AIR_UINT(opt[ufVarOpi].max));
}
if (havecTransfer(opt + ufVarOpi, havec, 0, getArg, hparm)) {
havStr = hestArgVecSprint(havec, AIR_TRUE);
biffAddf(HEST, "%s%sgiven (labeled) argv=|%s|", _ME_, havStr);
- biffAddf(HEST, "%s%strouble getting args for variadic unflagged %s[%u]", _ME_,
+ biffAddf(HEST, "%s%strouble getting args for unflagged variadic %s[%u]", _ME_,
identStr(ident, opt + ufVarOpi), ufVarOpi);
return (free(havStr), free(ufOpi2), 1);
}
}
- // else minArg > havec->len so can't satisfy from havec,
- // but its ok since we do have default
+ /* else minArg > havec->len so can't satisfy from havec, but that's ok for
+ opt[ufVarOpi] since it has default. The erroneous presence of extraneous args will be
+ caught next */
- /* make sure that unflagged options without default were given */
+finishingup:
+
+ // make sure that unflagged options without default were given
for (upii = 0; upii < ufOptNum; upii++) {
uint opi = ufOpi2[2 * upii + 0];
if (!(opt[opi].dflt) && hestSourceUnknown == opt[opi].source) {
- biffAddf(HEST, "%s%sdidn't see required (default-less) unflagged %s[%u]", _ME_,
+ biffAddf(HEST, "%s%sdidn't get required (default-less) unflagged %s[%u]", _ME_,
identStr(ident, opt + opi), opi);
return (free(ufOpi2), 1);
}
}
-
-finishingup:
- if (hparm->verbosity) {
- optAllPrint(__func__, "end of havecExtractUnflagged", opt);
- hestArgVecPrint(__func__, "end of havecExtractUnflagged", havec);
- }
+ // currently it's an error to have un-accounted-for args left overå
if (havec->len) {
havStr = hestArgVecSprint(havec, AIR_TRUE);
biffAddf(HEST, "%s%sgiven (labeled) argv=|%s|", _ME_, havStr);
@@ -1148,6 +1141,11 @@
havec->len > 1 ? "starting with " : "", havec->harg[0]->str);
return (free(havStr), airFree(ufOpi2), 1);
}
+
+ if (hparm->verbosity) {
+ optAllPrint(__func__, "end of havecExtractUnflagged", opt);
+ hestArgVecPrint(__func__, "end of havecExtractUnflagged", havec);
+ }
return (airFree(havStr), airFree(ufOpi2), 0);
}
@@ -1238,12 +1236,12 @@
}
/* 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. */
+Finally: 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 (pointed to by fields in hestOpt) with the
+successful return of hestParse(2), and are freed with hestParseFree. */
static int
-optSetValues(hestOpt *opt, const hestParm *hparm, airArray *pmop) {
+optSetValues(hestOpt *opt, const hestParm *hparm, airArray *cmop) {
char ident[AIR_STRLEN_HUGE + 1];
/*
char cberr[AIR_STRLEN_HUGE + 1], *tok, *last, *optParmsCopy;
@@ -1253,7 +1251,6 @@
void *valueP;
char *cvalueP;
int *ivalueP;
-
uint optNum = opt->arrLen;
for (uint opi = 0; opi < optNum; opi++) {
identStr(ident, opt + opi);
@@ -1287,9 +1284,20 @@
if (opt[opi].sawP) {
*(opt[opi].sawP) = 0;
}
+ _hestPPack _hpp;
+ _hestPPack *hpp;
+ if (1 == opt[opi].kind) {
+ hpp = NULL;
+ } else {
+ hpp = &_hpp;
+ hpp->cmop = cmop;
+ hpp->enm = opt[opi].enm; // will be non-NULL when needed
+ hpp->CB = opt[opi].CB;
+ hpp->alloc = 0; // may get over-written
+ // hpp->err set by parseSingleT
+ }
switch (opt[opi].kind) {
- case 1:
- /* -------- parameter-less boolean flags -------- */
+ case 1: // -------- parameter-less boolean flags --------
/* valueP is always assumed to be an int* */
*ivalueP = hestSourceDefault != opt[opi].source;
if (hparm->verbosity) {
@@ -1296,10 +1304,9 @@
printf(" --> set value %d\n", *ivalueP);
}
break;
- case 4: {
+ case 4: { // -------- optional single variadics --------
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):
@@ -1310,7 +1317,7 @@
(*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 */
+ In any case, some string has to be parsed; we call it `strsrc` */
if (hestSourceDefault == opt[opi].source) {
// option flag does not appear
strsrc = opt[opi].dflt;
@@ -1328,7 +1335,7 @@
opi, airEnumStr(hestSource, opt[opi].source), opt[opi].havec->len);
return 1;
}
- if (_hestParseSingle[type](valueP, strsrc, NULL /* because type simple */)) {
+ if (_hestParseSingle[type](valueP, strsrc, hpp)) {
biffAddf(HEST, "%s%sfor %s[%u] could not parse |%s| as single %s", _ME_, ident,
opi, strsrc, _hestTypeStr[type]);
return 1;
@@ -1338,67 +1345,14 @@
}
break;
} // end case 4 {
-#if 0
- case 2:
- /* -------- one required parameter -------- */
- /* 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] && valueP) {
- switch (type) {
- 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:
- 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 ret;
- }
- if (opt[opi].CB->destroy) {
- /* vP is the address of a void*, we manage the void * */
- opt[opi].alloc = 1;
- airMopAdd(pmop, (void **)valueP, (airMopper)airSetNull, airMopOnError);
- airMopAdd(pmop, *((void **)valueP), opt[opi].CB->destroy, airMopOnError);
- }
- break;
- case airTypeString:
- if (1
- != airParseStrS((char **)valueP, optParms[opi], " ", 1
- /*, hparm->greedySingleString */)) {
- fprintf(stderr, "%scouldn't parse %s\"%s\" as %s for %s\n", ME,
- optDfltd[opi] ? "(default) " : "", optParms[opi], _hestTypeStr[type],
- ident);
- return 1;
- }
- /* vP is the address of a char* (a char **), but what we
- manage with airMop is the char * */
- opt[opi].alloc = 1;
- airMopMem(pmop, valueP, airMopOnError);
- break;
- default:
- /* type isn't string or enum, so no last arg to hestParseStr[type] */
- 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;
- }
- break;
- }
+ case 2: // -------- one required parameter --------
+ if (_hestParseSingle[type](valueP, opt[opi].havec->harg[0]->str, hpp)) {
+ biffAddf(HEST, "%s%sproblem parsing for %s[%u]", _ME_, ident, opi);
+ return 1;
}
+ opt[opi].alloc = hpp->alloc;
break;
+#if 0
case 3:
/* -------- multiple required parameters -------- */
if (optParms[opi] && valueP) {
Modified: teem/trunk/src/hest/privateHest.h
===================================================================
--- teem/trunk/src/hest/privateHest.h 2025-09-26 11:50:43 UTC (rev 7501)
+++ teem/trunk/src/hest/privateHest.h 2025-09-28 07:44:41 UTC (rev 7502)
@@ -72,8 +72,15 @@
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 *);
+typedef struct {
+ airArray *cmop; // caller's mop to clean up things if airMopError
+ const airEnum *enm; // for parsing an airTypeEnum value
+ const hestCB *CB; // for parsing an airTypeOther
+ int alloc; // our single-arg parsing work allocated something
+ char err[AIR_STRLEN_HUGE + 1]; // error message can go for any type
+} _hestPPack;
HEST_EXPORT int (*const _hestParseSingle[_HEST_TYPE_MAX + 1])(void *, const char *,
- const void *);
+ _hestPPack *);
// 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 *,
Modified: teem/trunk/src/hest/test/ex6.c
===================================================================
--- teem/trunk/src/hest/test/ex6.c 2025-09-26 11:50:43 UTC (rev 7501)
+++ teem/trunk/src/hest/test/ex6.c 2025-09-28 07:44:41 UTC (rev 7502)
@@ -47,7 +47,7 @@
ptrP = _ptr;
ptr = (*ptrP) = AIR_MALLOC(1, Quat);
- /* printf("%s: ptrP = %p ---malloc--> ptr = *ptrP = %p\n", __func__, ptrP, *ptrP); */
+ printf("%s: ptrP = %p ---malloc--> ptr = *ptrP = %p\n", __func__, ptrP, *ptrP);
ptr->str = NULL;
if (1 != sscanf(str, "%lf", &(ptr->val))) {
sprintf(err, "didn't parse a double from %s", str);
@@ -84,7 +84,7 @@
hparm->respectDashDashHelp = AIR_TRUE;
hparm->noArgsIsNoProblem = AIR_TRUE;
hparm->dieLessVerbose = AIR_TRUE;
- hparm->verbosity = 1;
+ hparm->verbosity = 0;
opt = NULL;
/* going past C89 to have declarations here */
@@ -91,38 +91,6 @@
int flag;
hestOptAdd_Flag(&opt, "f,flag", &flag, "a flag created via hestOptAdd_Flag");
- int b1;
- hestOptAdd_1_Bool(&opt, "b1", "bool1", &b1, "false", "test of hestOptAdd_1_Bool");
- int i1;
- hestOptAdd_1_Int(&opt, "i1", "int1", &i1, "42", "test of hestOptAdd_1_Int");
- unsigned int ui1;
- hestOptAdd_1_UInt(&opt, "ui1", "uint1", &ui1, "42", "test of hestOptAdd_1_UInt");
- long int li1;
- hestOptAdd_1_Long(&opt, "li1", "lint1", &li1, "42", "test of hestOptAdd_1_Long");
- unsigned long int uli1;
- hestOptAdd_1_ULong(&opt, "uli1", "ulint1", &uli1, "42", "test of hestOptAdd_1_ULong");
- size_t sz1;
- hestOptAdd_1_Size_t(&opt, "sz1", "size1", &sz1, "42", "test of hestOptAdd_1_Size_t");
- float fl1;
- hestOptAdd_1_Float(&opt, "fl1", "float1", &fl1, "4.2", "test of hestOptAdd_1_Float");
- double db1;
- hestOptAdd_1_Double(&opt, "db1", "double1", &db1, "4.2",
- "test of hestOptAdd_1_Double");
- char c1;
- hestOptAdd_1_Char(&opt, "c1", "char1", &c1, "x", "test of hestOptAdd_1_Char");
- char *s1;
- hestOptAdd_1_String(&opt, "s1", "string1", &s1, "\"bingo bob\"",
- "test of hestOptAdd_1_String");
- int e1;
- hestOptAdd_1_Enum(&opt, "e1", "enum1", &e1, "little", "test of hestOptAdd_1_Enum",
- airEndian);
- double p1[2];
- hestOptAdd_1_Other(&opt, "p1", "pos", &p1, "1.5,5.25", "test of hestOptAdd_1_Other A",
- &posCB);
- Quat *q1;
- hestOptAdd_1_Other(&opt, "q1", "quat", &q1, "12.34", "test of hestOptAdd_1_Other B",
- &quatCB);
-
int b1v;
hestOptAdd_1v_Bool(&opt, "b1v", "bool1", &b1v, "false", "test of hestOptAdd_1v_Bool");
int i1v;
@@ -159,6 +127,42 @@
hestOptAdd_1v_Other(&opt, "q1v", "quat", &q1v, "12.34",
"test of hestOptAdd_1v_Other B", &quatCB);
*/
+ int b1;
+ hestOptAdd_1_Bool(&opt, "b1", "bool1", &b1, "false", "test of hestOptAdd_1_Bool");
+ int i1;
+ printf("!%s: &i1 = %p (passed to hestOptAdd_1_Int)\n", __func__, AIR_VOIDP(&i1));
+ hestOptAdd_1_Int(&opt, "i1", "int1", &i1, "42", "test of hestOptAdd_1_Int");
+ unsigned int ui1;
+ hestOptAdd_1_UInt(&opt, "ui1", "uint1", &ui1, "42", "test of hestOptAdd_1_UInt");
+ long int li1;
+ hestOptAdd_1_Long(&opt, "li1", "lint1", &li1, "42", "test of hestOptAdd_1_Long");
+ unsigned long int uli1;
+ hestOptAdd_1_ULong(&opt, "uli1", "ulint1", &uli1, "42", "test of hestOptAdd_1_ULong");
+ size_t sz1;
+ hestOptAdd_1_Size_t(&opt, "sz1", "size1", &sz1, "42", "test of hestOptAdd_1_Size_t");
+ float fl1;
+ hestOptAdd_1_Float(&opt, "fl1", "float1", &fl1, "4.2", "test of hestOptAdd_1_Float");
+ double db1;
+ hestOptAdd_1_Double(&opt, "db1", "double1", &db1, "4.2",
+ "test of hestOptAdd_1_Double");
+ char c1;
+ hestOptAdd_1_Char(&opt, "c1", "char1", &c1, "x", "test of hestOptAdd_1_Char");
+ /*
+ char *s1;
+ hestOptAdd_1_String(&opt, "s1", "string1", &s1, "\"bingo bob\"",
+ "test of hestOptAdd_1_String");
+ int e1;
+ hestOptAdd_1_Enum(&opt, "e1", "enum1", &e1, "little", "test of hestOptAdd_1_Enum",
+ airEndian);
+ double p1[2];
+ hestOptAdd_1_Other(&opt, "p1", "pos", &p1, "1.5,5.25", "test of hestOptAdd_1_Other A",
+ &posCB);
+ Quat *q1;
+ hestOptAdd_1_Other(&opt, "q1", "quat", &q1, "12.34", "test of hestOptAdd_1_Other B",
+ &quatCB);
+ */
+
+#if 0
int b2[2];
hestOptAdd_2_Bool(&opt, "b2", "bool1 bool2", b2, "true false",
"test of hestOptAdd_2_Bool");
@@ -375,6 +379,7 @@
unsigned int qvSaw;
hestOptAdd_Nv_Other(&opt, "qv", "quat1", 1, -1, &qv, "12.34 43.21",
"test of hestOptAdd_Nv_Other B", &qvSaw, &quatCB);
+#endif
hestParse2(opt, argc - 1, argv + 1, NULL, hparm);
/*
hestParseOrDie(opt, argc - 1, argv + 1, hparm, argv[0], info, AIR_TRUE, AIR_TRUE,
@@ -414,7 +419,6 @@
*/
printf("\n");
-#if 0
printf("b1 = %d\n", b1);
printf("i1 = %d\n", i1);
printf("ui1 = %u\n", ui1);
@@ -424,12 +428,15 @@
printf("fl1 = %g\n", fl1);
printf("db1 = %g\n", db1);
printf("c1 = |%c| (%d)\n", c1, c1);
+ /*
printf("s1 = |%s|\n", s1);
printf("e1 = %d\n", e1);
printf("p1 = %g,%g\n", p1[0], p1[1]);
printf("q1 (@ %p) = %g(%s)\n", q1, q1->val, q1->str);
+ */
printf("\n");
+#if 0
printf("b2 = %d %d\n", b2[0], b2[1]);
printf("i2 = %d %d\n", i2[0], i2[1]);
printf("ui2 = %u %u\n", ui2[0], ui2[1]);
Modified: teem/trunk/src/hest/test/tparse.c
===================================================================
--- teem/trunk/src/hest/test/tparse.c 2025-09-26 11:50:43 UTC (rev 7501)
+++ teem/trunk/src/hest/test/tparse.c 2025-09-28 07:44:41 UTC (rev 7502)
@@ -47,11 +47,12 @@
unsigned int slen;
hestOptAdd_Nv_Int(&opt, "s,sizes", "sx sy", 2, -1, &size, NULL, "image resolutions",
&slen);
-
+#if 0
int *unpB;
unsigned int sawB;
hestOptAdd_Nv_Int(&opt, NULL, "B B", 2, -1, &unpB, /* "BBBB" */ NULL, "unflagged B",
&sawB);
+#endif
/* int unpB[2];
hestOptAdd_2_Int(&opt, NULL, "B B", unpB, NULL, "unflagged B"); */
int unpC[2];
@@ -68,7 +69,7 @@
ret = 1;
}
if (opt->helpWanted) {
- printf("%s: help wanted!\n", argv[0]);
+ printf("\n\n%s: help wanted!\n\n\n", argv[0]);
}
hestOptFree(opt);
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|