|
From: <kin...@us...> - 2025-09-18 07:22:12
|
Revision: 7450
http://sourceforge.net/p/teem/code/7450
Author: kindlmann
Date: 2025-09-18 07:22:10 +0000 (Thu, 18 Sep 2025)
Log Message:
-----------
some more progress on hestParse re-write
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/test/tparse.c
Modified: teem/trunk/src/hest/argvHest.c
===================================================================
--- teem/trunk/src/hest/argvHest.c 2025-09-18 05:52:43 UTC (rev 7449)
+++ teem/trunk/src/hest/argvHest.c 2025-09-18 07:22:10 UTC (rev 7450)
@@ -54,6 +54,7 @@
/* initialize with \0 so that harg->str is "" */
airArrayLenIncr(harg->strArr, 1);
/* now harg->str = {0:'\0'} and harg->len = 1; */
+ harg->finished = AIR_FALSE;
return;
}
@@ -103,6 +104,9 @@
for (uint si = 0; si < len; si++) {
hestArgAddChar(harg, str[si]);
}
+ /* The assumption is that if you have a string to put here; then you know that the
+ string is finished. User can modify this if that's not the case. */
+ harg->finished = AIR_TRUE;
return;
}
@@ -141,7 +145,7 @@
for (uint idx = 0; idx < havec->hargArr->len; idx++) {
const hestArg *harg;
harg = havec->harg + idx;
- printf(" %u:<%s>", idx, harg->str);
+ printf(" %u:<%s>%c", idx, harg->str, harg->finished ? '.' : '~');
}
printf("\n");
}
@@ -158,6 +162,7 @@
hin->argIdx = 0;
hin->fname = NULL;
hin->file = NULL;
+ hin->dashBraceComment = 0;
return;
}
@@ -206,49 +211,39 @@
return NULL;
}
-void
-hestInputStackPushCommandLine(hestInputStack *hist, int argc, const char **argv) {
+int
+hestInputStackPushCommandLine(hestInputStack *hist, int argc, const char **argv,
+ char *err, const hestParm *hparm) {
assert(hist);
+ AIR_UNUSED(err);
+ if (hparm->verbosity) {
+ printf("%s: changing stack height: %u --> %u with argc=%d,argv=%p; "
+ "setting argIdx to 0\n",
+ __func__, hist->hinArr->len, hist->hinArr->len + 1, argc, AIR_VOIDP(argv));
+ }
uint idx = airArrayLenIncr(hist->hinArr, 1);
+ if (hparm->verbosity > 1) {
+ printf("%s: new hinTop = %p\n", __func__, AIR_VOIDP(hist->hin + idx));
+ }
hist->hin[idx].source = hestSourceCommandLine;
hist->hin[idx].argc = argc;
hist->hin[idx].argv = argv;
hist->hin[idx].argIdx = 0;
- return;
+ return 0;
}
-static int
-histProc(hestArgVec *havec, hestInputStack *hist) {
- hestInput *hinTop = hist->hin + (hist->len - 1);
- int done = AIR_FALSE;
- switch (hinTop->source) {
- case hestSourceDefault:
- fprintf(stderr, "%s: sorry hestSourceDefault not implemented\n", __func__);
- done = AIR_TRUE;
- break;
- case hestSourceCommandLine:
- /* argv[] 0 1 2 3 (argc=4) */
- /* cmd arg1 arg2 arg3 */
- if (hinTop->argIdx < hinTop->argc) {
- /* there are args left to parse */
- hestArgVecAppendString(havec, hinTop->argv[hinTop->argIdx]);
- hinTop->argIdx++;
- }
- done = (hinTop->argIdx == hinTop->argc);
- break;
- case hestSourceResponseFile:
- fprintf(stderr, "%s: sorry hestSourceResponseFile not implemented\n", __func__);
- done = AIR_TRUE;
- break;
+int
+hestInputStackPop(hestInputStack *hist, char *err, const hestParm *hparm) {
+ assert(hist);
+ uint len = hist->hinArr->len;
+ if (!len) {
+ sprintf(err, "%s: cannot pop from stack height 0", __func__);
+ return 1;
}
- return done;
-}
-
-int
-hestInputStackProcess(hestArgVec *havec, hestInputStack *hist) {
- int done;
- do {
- done = histProc(havec, hist);
- } while (!done);
+ if (hparm->verbosity) {
+ printf("%s: changing stack height: %u --> %u; popping %s source\n", __func__, len,
+ len - 1, airEnumStr(hestSource, hist->hin[len - 1].source));
+ }
+ airArrayLenIncr(hist->hinArr, -1);
return 0;
-}
+}
\ No newline at end of file
Modified: teem/trunk/src/hest/hest.h
===================================================================
--- teem/trunk/src/hest/hest.h 2025-09-18 05:52:43 UTC (rev 7449)
+++ teem/trunk/src/hest/hest.h 2025-09-18 07:22:10 UTC (rev 7450)
@@ -44,8 +44,7 @@
/*
******** hestSource* enum
**
-** records whether the info to satisfy a particular option came from the default or from
-** the user: command-line or response file.
+** way of identifying where the info to satisfy a particular option came from.
*/
enum {
hestSourceUnknown, /* 0 */
@@ -189,15 +188,21 @@
elideSingleEmptyStringDefault, /* if default for a single string is empty
(""), then don't display default */
elideMultipleEmptyStringDefault,
- respectDashDashHelp, /* hestParse interprets seeing "--help" as not an
- error, but as a request to print usage info,
- so sets helpWanted in the (first) hestOpt */
- noArgsIsNoProblem, /* if non-zero, having no arguments to parse is not in and
- of itself a problem; this means that if all options have
- defaults, it would be *ok* to invoke the problem without
- any further command-line options. This is counter to
- pre-Teem-1.11 behavior (for which no arguments *always*
- meant "show me usage info"). */
+ respectDashDashHelp, /* (new with TeemV2) hestParse interprets seeing "--help" as not
+ an error, but as a request to print usage info, so sets
+ helpWanted in the (first) hestOpt */
+ respectDashBraceComments, /* (new with TeemV2) Sometimes in a huge command-line
+ invocation you want to comment part of it out. With this
+ set, hestParse recognizes hest-specific "-{" and "}-"
+ args as comment delimiters: these args and all args they
+ enclose are ignored. These must be stand-alone args, and
+ cannot be touching anything else, lest brace expansion
+ kick in. */
+ noArgsIsNoProblem, /* if non-zero, having no arguments to parse is not in and of
+ itself a problem; this means that if all options have defaults, it
+ would be *ok* to invoke the problem without any further
+ command-line options. This is counter to pre-Teem-1.11 behavior
+ (for which no arguments *always* meant "show me usage info"). */
greedySingleString, /* when parsing a single string, whether or not to be greedy
(as per airParseStrS) */
cleverPluralizeOtherY, /* when printing the type for airTypeOther, when the min
@@ -221,14 +226,31 @@
disable this behavior entirely. */
} hestParm;
-/* for building up and representing one argument */
+/*
+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 */
+ unsigned int len; // NOT strlen; this includes '\0'-termination
airArray *strArr;
+ int finished; // we have finished building up this string
} hestArg;
-/* for building up a "vector" of arguments */
+// hestArgVec: for building up a "vector" of arguments
typedef struct {
hestArg *harg; /* array of hestArgs */
unsigned int len;
@@ -235,27 +257,31 @@
airArray *hargArr;
} hestArgVec;
-/* what is the thing we're currently processing to build up the arg vec */
+// 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 == hestSourceDefault ------ */
- const char *dflt; /* we do NOT own*/
- /* ------ if source == hestSourceCommandLine ------ */
+ int source; // from the hestSource* enum
+ // ------ if source == hestSourceDefault ------
+ const char *dflt; // we do NOT own
+ // ------ if source == hestSourceCommandLine ------
unsigned int argc;
- const char **argv; /* we do NOT own */
+ const char **argv; // we do NOT own
unsigned int argIdx;
- /* ------ if source == hestSourceResponseFile ------ */
- char *fname; /* we do NOT own */
- FILE *file; /* someone opened this for us */
+ // ------ if source == hestSourceResponseFile ------
+ char *fname; // we do NOT own
+ FILE *file; // someone opened this for us
+ // ------ 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 */
+ hestInput *hin; // array of hestInputs
unsigned int len;
airArray *hinArr;
} hestInputStack;
-/* defaultsHest.c */
+// defaultsHest.c
HEST_EXPORT int hestDefaultVerbosity;
HEST_EXPORT int hestDefaultRespFileEnable;
HEST_EXPORT int hestDefaultElideSingleEnumType;
@@ -274,7 +300,7 @@
HEST_EXPORT char hestDefaultVarParamStopFlag;
HEST_EXPORT char hestDefaultMultiFlagSep;
-/* argvHest.c */
+// argvHest.c
HEST_EXPORT hestArg *hestArgNew(void);
HEST_EXPORT hestArg *hestArgNix(hestArg *harg);
HEST_EXPORT void hestArgAddChar(hestArg *harg, char cc);
@@ -287,16 +313,19 @@
HEST_EXPORT hestInput *hestInputNix(hestInput *hin);
HEST_EXPORT hestInputStack *hestInputStackNew(void);
HEST_EXPORT hestInputStack *hestInputStackNix(hestInputStack *hist);
-HEST_EXPORT void hestInputStackPushCommandLine(hestInputStack *hist, int argc,
- const char **argv);
-HEST_EXPORT int hestInputStackProcess(hestArgVec *havec, hestInputStack *hist);
+HEST_EXPORT int hestInputStackPushCommandLine(hestInputStack *hist, int argc,
+ const char **argv, char *err,
+ const hestParm *hparm);
+HEST_EXPORT int hestInputStackPop(hestInputStack *hist, char *err,
+ const hestParm *hparm);
-/* parsest.c */
+// parsest.c
HEST_EXPORT int hestParse2(hestOpt *opt, int argc, const char **argv, char **errP,
const hestParm *hparm);
-/* methodsHest.c */
+// methodsHest.c
HEST_EXPORT const int hestPresent;
+HEST_EXPORT const airEnum *const hestSource;
HEST_EXPORT int hestSourceUser(int src);
HEST_EXPORT hestParm *hestParmNew(void);
HEST_EXPORT hestParm *hestParmFree(hestParm *hparm);
@@ -319,13 +348,13 @@
... /* unsigned int *sawP,
const airEnum *enm,
const hestCB *CB */);
-/* see also all the special-purpose and type-checked versions in adders.c, below */
+// SEE ALSO all the special-purpose and type-checked versions in adders.c, below
HEST_EXPORT unsigned int hestOptNum(const hestOpt *opt);
HEST_EXPORT hestOpt *hestOptFree(hestOpt *opt);
HEST_EXPORT void *hestOptFree_vp(void *opt);
HEST_EXPORT int hestOptCheck(hestOpt *opt, char **errP);
-/* parseHest.c */
+// parseHest.c
HEST_EXPORT int hestParse(hestOpt *opt, int argc, const char **argv, char **errP,
const hestParm *hparm);
HEST_EXPORT void *hestParseFree(hestOpt *opt);
@@ -333,7 +362,7 @@
hestParm *hparm, const char *me, const char *info,
int doInfo, int doUsage, int doGlossary);
-/* usage.c */
+// usage.c
HEST_EXPORT void _hestPrintStr(FILE *f, unsigned int indent, unsigned int already,
unsigned int width, const char *_str, int bslash);
HEST_EXPORT int hestMinNumArgs(const hestOpt *opt);
@@ -343,7 +372,7 @@
HEST_EXPORT void hestInfo(FILE *file, const char *argv0, const char *info,
const hestParm *hparm);
-/* adders.c */
+// adders.c
HEST_EXPORT void hestOptAddDeclsPrint(FILE *f);
/* Many many 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
Modified: teem/trunk/src/hest/methodsHest.c
===================================================================
--- teem/trunk/src/hest/methodsHest.c 2025-09-18 05:52:43 UTC (rev 7449)
+++ teem/trunk/src/hest/methodsHest.c 2025-09-18 07:22:10 UTC (rev 7450)
@@ -22,11 +22,33 @@
#include <limits.h>
#include <assert.h>
-#include <sys/ioctl.h> /* for ioctl(), TIOCGWINSZ, struct winsize */
-#include <unistd.h> /* for STDOUT_FILENO and friends */
+#include <sys/ioctl.h> // for ioctl(), TIOCGWINSZ, struct winsize
+#include <unistd.h> // for STDOUT_FILENO and friends
const int hestPresent = 42;
+// enjoying how C99 greatly simplifies creating an airEnum at compile-time
+static const airEnum _hestSource
+ = {.name = "source",
+ .M = 3,
+ .str = (const char *[]){"(unknown_source)", //
+ "default", //
+ "command-line", //
+ "response-file"},
+ .val = NULL,
+ .desc = (const char *[]){"unknown source", //
+ "default string in hestOpt", //
+ "argc/argv command-line", //
+ "a response file"},
+ .strEqv = (const char *[]){"default", //
+ "command-line", "cmdline", //
+ "response-file", "respfile"},
+ .valEqv = (const int[]){hestSourceDefault, //
+ hestSourceCommandLine, hestSourceCommandLine, //
+ hestSourceResponseFile, hestSourceResponseFile},
+ .sense = AIR_FALSE};
+const airEnum *const hestSource = &_hestSource;
+
int
hestSourceUser(int src) {
return (hestSourceCommandLine == src || hestSourceResponseFile == src);
@@ -70,11 +92,13 @@
can be a different top-level parser function that turns on parm->respectDashDashHelp
and knows how to check the results */
hparm->respectDashDashHelp = AIR_FALSE;
+ /* for these most recent addition to the hestParm,
+ abstaining from adding yet another default global variable */
+ hparm->respectDashBraceComments = AIR_TRUE;
hparm->noArgsIsNoProblem = hestDefaultNoArgsIsNoProblem;
hparm->greedySingleString = hestDefaultGreedySingleString;
hparm->cleverPluralizeOtherY = hestDefaultCleverPluralizeOtherY;
- /* for these most recent addition to the hestParm,
- abstaining from added yet another default global variable */
+ /* here too: newer addition to hestParm avoid adding another default global */
hparm->dieLessVerbose = AIR_FALSE;
hparm->noBlankLineBeforeUsage = AIR_FALSE;
hparm->columns = hestDefaultColumns;
Modified: teem/trunk/src/hest/parseHest.c
===================================================================
--- teem/trunk/src/hest/parseHest.c 2025-09-18 05:52:43 UTC (rev 7449)
+++ teem/trunk/src/hest/parseHest.c 2025-09-18 07:22:10 UTC (rev 7450)
@@ -248,7 +248,7 @@
for (opi = 0; opi < optNum; opi++) {
if (!(AIR_IN_OP(airTypeUnknown, opt[opi].type, airTypeLast))) {
if (err)
- sprintf(err, "%s!!!!!! opt[%d].type (%d) not in valid range [%d,%d]", ME, opi,
+ sprintf(err, "%sopt[%d].type (%d) not in valid range [%d,%d]", ME, opi,
opt[opi].type, airTypeUnknown + 1, airTypeLast - 1);
else
fprintf(stderr, "%s: panic 0\n", me);
@@ -256,7 +256,7 @@
}
if (!(opt[opi].valueP)) {
if (err)
- sprintf(err, "%s!!!!!! opt[%d]'s valueP is NULL!", ME, opi);
+ sprintf(err, "%sopt[%d]'s valueP is NULL!", ME, opi);
else
fprintf(stderr, "%s: panic 0.5\n", me);
return 1;
@@ -263,7 +263,7 @@
}
if (-1 == opt[opi].kind) {
if (err)
- sprintf(err, "%s!!!!!! opt[%d]'s min (%d) and max (%d) incompatible", ME, opi,
+ sprintf(err, "%sopt[%d]'s min (%d) and max (%d) incompatible", ME, opi,
opt[opi].min, opt[opi].max);
else
fprintf(stderr, "%s: panic 1\n", me);
@@ -272,7 +272,7 @@
if (5 == opt[opi].kind && !(opt[opi].sawP)) {
if (err)
sprintf(err,
- "%s!!!!!! have multiple variable parameters, "
+ "%shave multiple variable parameters, "
"but sawP is NULL",
ME);
else
@@ -283,7 +283,7 @@
if (!(opt[opi].enm)) {
if (err) {
sprintf(err,
- "%s!!!!!! opt[%d] (%s) is type \"enum\", but no "
+ "%sopt[%d] (%s) is type \"enum\", but no "
"airEnum pointer given",
ME, opi, opt[opi].flag ? opt[opi].flag : "?");
} else {
@@ -296,7 +296,7 @@
if (!(opt[opi].CB)) {
if (err) {
sprintf(err,
- "%s!!!!!! opt[%d] (%s) is type \"other\", but no "
+ "%sopt[%d] (%s) is type \"other\", but no "
"callbacks given",
ME, opi, opt[opi].flag ? opt[opi].flag : "?");
} else {
@@ -306,7 +306,7 @@
}
if (!(opt[opi].CB->size > 0)) {
if (err)
- sprintf(err, "%s!!!!!! opt[%d]'s \"size\" (%d) invalid", ME, opi,
+ sprintf(err, "%sopt[%d]'s \"size\" (%d) invalid", ME, opi,
(int)(opt[opi].CB->size));
else
fprintf(stderr, "%s: panic 5\n", me);
@@ -314,7 +314,7 @@
}
if (!(opt[opi].CB->type)) {
if (err)
- sprintf(err, "%s!!!!!! opt[%d]'s \"type\" is NULL", ME, opi);
+ sprintf(err, "%sopt[%d]'s \"type\" is NULL", ME, opi);
else
fprintf(stderr, "%s: panic 6\n", me);
return 1;
@@ -321,7 +321,7 @@
}
if (!(opt[opi].CB->parse)) {
if (err)
- sprintf(err, "%s!!!!!! opt[%d]'s \"parse\" callback NULL", ME, opi);
+ sprintf(err, "%sopt[%d]'s \"parse\" callback NULL", ME, opi);
else
fprintf(stderr, "%s: panic 7\n", me);
return 1;
@@ -329,7 +329,7 @@
if (opt[opi].CB->destroy && (sizeof(void *) != opt[opi].CB->size)) {
if (err)
sprintf(err,
- "%s!!!!!! opt[%d] has a \"destroy\", but size %lu isn't "
+ "%sopt[%d] has a \"destroy\", but size %lu isn't "
"sizeof(void*)",
ME, opi, (unsigned long)(opt[opi].CB->size));
else
@@ -344,7 +344,7 @@
if (!(strlen(tbuff) && strlen(sep + 1))) {
if (err)
sprintf(err,
- "%s!!!!!! either short (\"%s\") or long (\"%s\") flag"
+ "%seither short (\"%s\") or long (\"%s\") flag"
" of opt[%d] is zero length",
ME, tbuff, sep + 1, opi);
else
@@ -354,7 +354,7 @@
if (hparm->respectDashDashHelp && !strcmp("help", sep + 1)) {
if (err)
sprintf(err,
- "%s!!!!!! long \"--%s\" flag of opt[%d] is same as \"--help\" "
+ "%slong \"--%s\" flag of opt[%d] is same as \"--help\" "
"that requested hparm->respectDashDashHelp handles separately",
ME, sep + 1, opi);
else
@@ -364,17 +364,28 @@
} else {
if (!strlen(opt[opi].flag)) {
if (err)
- sprintf(err, "%s!!!!!! opt[%d].flag is zero length", ME, opi);
+ sprintf(err, "%sopt[%d].flag is zero length", ME, opi);
else
fprintf(stderr, "%s: panic 10\n", me);
return 1;
}
}
+ if (hparm->respectDashBraceComments
+ && (strchr(opt[opi].flag, '{') || strchr(opt[opi].flag, '}'))) {
+ if (err)
+ sprintf(err,
+ "%srequested hparm->respectDashBraceComments but opt[%d]'s flag "
+ "\"%s\" confusingly contains '{' or '}'",
+ ME, opi, opt[opi].flag);
+ else
+ fprintf(stderr, "%s: panic 10.5\n", me);
+ return 1;
+ }
if (4 == opt[opi].kind) {
if (!opt[opi].dflt) {
if (err)
sprintf(err,
- "%s!!!!!! flagged single variable parameter must "
+ "%sflagged single variable parameter must "
"specify a default",
ME);
else
@@ -384,7 +395,7 @@
if (!strlen(opt[opi].dflt)) {
if (err)
sprintf(err,
- "%s!!!!!! flagged single variable parameter default "
+ "%sflagged single variable parameter default "
"must be non-zero length",
ME);
else
@@ -396,7 +407,7 @@
sprintf(tbuff, "-%s", opt[op].flag);
if (1 == sscanf(tbuff, "%f", &tmpF)) {
if (err)
- sprintf(err, "%s!!!!!! opt[%d].flag (\"%s\") is numeric, bad news",
+ sprintf(err, "%sopt[%d].flag (\"%s\") is numeric, bad news",
ME, op, opt[op].flag);
return 1;
}
@@ -405,7 +416,7 @@
if (1 == opt[opi].kind) {
if (!opt[opi].flag) {
if (err)
- sprintf(err, "%s!!!!!! flags must have flags", ME);
+ sprintf(err, "%sflags must have flags", ME);
else
fprintf(stderr, "%s: panic 13\n", me);
return 1;
@@ -413,7 +424,7 @@
} else {
if (!opt[opi].name) {
if (err)
- sprintf(err, "%s!!!!!! opt[%d] isn't a flag: must have \"name\"", ME, opi);
+ sprintf(err, "%sopt[%d] isn't a flag: must have \"name\"", ME, opi);
else
fprintf(stderr, "%s: panic 14\n", me);
return 1;
@@ -422,7 +433,7 @@
if (4 == opt[opi].kind && !opt[opi].dflt) {
if (err)
sprintf(err,
- "%s!!!!!! opt[%d] is single variable parameter, but "
+ "%sopt[%d] is single variable parameter, but "
"no default set",
ME, opi);
else
@@ -434,8 +445,7 @@
}
if (numvar > 1) {
if (err)
- sprintf(err, "%s!!!!!! can't have %d unflagged min<max opts, only one", ME,
- numvar);
+ sprintf(err, "%scan't have %d unflagged min<max opts, only one", ME, numvar);
else
fprintf(stderr, "%s: panic 16\n", me);
return 1;
Modified: teem/trunk/src/hest/parsest.c
===================================================================
--- teem/trunk/src/hest/parsest.c 2025-09-18 05:52:43 UTC (rev 7449)
+++ teem/trunk/src/hest/parsest.c 2025-09-18 07:22:10 UTC (rev 7450)
@@ -20,7 +20,7 @@
#include "hest.h"
#include "privateHest.h"
-/* (parse, parser, parsest) */
+/* parse, parser, parsest: this aims to be the final implmentation of hestParse */
/* A little trickery for error reporting. For many of the functions here, if they hit an
error and hparm->verbosity is set, then we should reveal the current function name (set
@@ -30,33 +30,115 @@
to `functionname: ` (NOTE the `: `) so that all of that goes away without verbosity. And,
that means that error message generation here should also defy convention and instead of
being "%s: what happened" it should just be "%swhat happened" */
-#define ME ((hparm && hparm->verbosity) ? me : "")
+// how to const-correctly use either given (const) _hparm or own (non-const) hparm
+#define HPARM (_hparm ? _hparm : hparm)
+#define HMME (HPARM->verbosity ? me : "")
+#define ME ((hparm && hparm->verbosity) ? me : "")
+static int
+histProc(hestArgVec *havec, int *helpWantedP, hestInputStack *hist, char *err,
+ const hestParm *hparm) {
+ static const char me[] = "histProc: ";
+ int ret = 0;
+ int popAtEnd = AIR_FALSE;
+ *helpWantedP = AIR_FALSE; // may over-write later
+ hestInput *hinTop = hist->hin + (hist->len - 1);
+ switch (hinTop->source) {
+ case hestSourceDefault: // ---------------------------------
+ sprintf(err, "%ssorry hestSourceDefault not implemented\n", ME);
+ ret = 1;
+ break;
+ case hestSourceCommandLine: // ---------------------------------
+ /* argv[] 0 1 2 3 (argc=4) */
+ /* cmd arg1 arg2 arg3 */
+ if (hparm->verbosity > 1) {
+ printf("%shist->len=%u -> hinTop=%p\n", me, hist->len, AIR_VOIDP(hinTop));
+ }
+ if (hinTop->argIdx < hinTop->argc) {
+ /* there are args left to parse */
+ const char *thisArgv = hinTop->argv[hinTop->argIdx];
+ if (hparm->verbosity > 1) {
+ printf("%slooking at argv[%u] |%s|\n", me, hinTop->argIdx, thisArgv);
+ }
+ hinTop->argIdx++;
+ if (hparm->respectDashBraceComments && !strcmp("-{", thisArgv)) {
+ // start of -{ }- commenting (or increase in nesting level)
+ hinTop->dashBraceComment += 1;
+ }
+ if (!hinTop->dashBraceComment) {
+ hestArgVecAppendString(havec, thisArgv);
+ }
+ if (hparm->respectDashBraceComments && !strcmp("}-", thisArgv)) {
+ if (hinTop->dashBraceComment) {
+ hinTop->dashBraceComment -= 1;
+ } else {
+ sprintf(err, "%send comment arg \"}-\" not balanced by prior \"-{\"", ME);
+ ret = 1;
+ }
+ }
+ }
+ if (hinTop->argIdx == hinTop->argc) {
+ // we have gotten to the end of the given argv array */
+ if (hinTop->dashBraceComment) {
+ sprintf(err, "%sstart comment arg \"-{\" not balanced by later \"}-\"", ME);
+ ret = 1;
+ } else {
+ popAtEnd = AIR_TRUE;
+ // but don't pop now because we need to check for --help
+ }
+ }
+ break;
+ case hestSourceResponseFile: // ---------------------------------
+ sprintf(err, "%ssorry hestSourceResponseFile not implemented\n", ME);
+ ret = 1;
+ break;
+ }
+ /* when processing command-line or response file, check for --help
+ (it makes no sense for --help to appear in a default string) */
+ if (hestSourceResponseFile == hinTop->source
+ || hestSourceCommandLine == hinTop->source) {
+ const hestArg *hlast;
+ if (hparm->respectDashDashHelp // watching for "--help"
+ && havec->len // have at least one arg
+ && (hlast = havec->harg + havec->len - 1)->finished // latest arg is finished
+ && !strcmp("--help", hlast->str)) { // and it equals "--help"
+ *helpWantedP = AIR_TRUE;
+ }
+ }
+ if (popAtEnd) {
+ if (hestInputStackPop(hist, err, hparm)) {
+ ret = 1;
+ }
+ }
+ return ret;
+}
+
int
hestParse2(hestOpt *opt, int argc, const char **argv, char **_errP,
const hestParm *_hparm) {
- /* see note on ME (at top) for why me[] ends with ": " */
- // static const char me[] = "hestParse2: ";
+ /* see note on HMME (at top) for why me[] ends with ": " */
+ static const char me[] = "hestParse2: ";
- /* -------- initialize the mop */
+ // -------- initialize the mop
airArray *mop = airMopNew();
- /* -------- exactly one of (given) _hparm and (our) hparm is non-NULL */
+ // -------- make exactly one of (given) _hparm and (our) hparm non-NULL
hestParm *hparm = NULL;
if (!_hparm) {
hparm = hestParmNew();
airMopAdd(mop, hparm, (airMopper)hestParmFree, airMopAlways);
}
- /* how to const-correctly use hparm or _hparm in an expression */
-#define HPARM (_hparm ? _hparm : hparm)
+ if (HPARM->verbosity > 1) {
+ printf("%shparm->verbosity %d\n", HMME, HPARM->verbosity);
+ }
- /* -------- allocate the err string. We do it a dumb way for now.
- TODO: make this smarter */
+ // -------- allocate the err string. We do it a dumb way for now.
+ // TODO: make this smarter
uint eslen = 2 * AIR_STRLEN_HUGE;
char *err = AIR_CALLOC(eslen + 1, char);
assert(err);
if (_errP) {
- /* they care about the error string, so mop it only when there is _not_ an error */
+ // they care about the error string, so mop it only when there is _not_ an error
*_errP = err;
airMopAdd(mop, _errP, (airMopper)airSetNull, airMopOnOkay);
airMopAdd(mop, err, airFree, airMopOnOkay);
@@ -65,20 +147,47 @@
so we always clean it up on exit */
airMopAdd(mop, err, airFree, airMopAlways);
}
+ if (HPARM->verbosity > 1) {
+ printf("%serr %p\n", HMME, AIR_VOIDP(err));
+ }
- /* -------- check on validity of the hestOpt array */
+ // -------- check on validity of the hestOpt array
if (_hestOptCheck(opt, err, HPARM)) {
+ // error message has been sprinted into err
airMopError(mop);
return 1;
}
+ if (HPARM->verbosity > 1) {
+ printf("%s_hestOptCheck passed\n", HMME);
+ }
- /* -------- 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();
airMopAdd(mop, havec, (airMopper)hestArgVecNix, airMopAlways);
- hestInputStackPushCommandLine(hist, argc, argv);
- hestInputStackProcess(havec, hist);
+ if (HPARM->verbosity > 1) {
+ printf("%shavec and hist allocated\n", HMME);
+ }
+
+ // -------- initialize input stack w/ given argc,argv, then process it
+ if (hestInputStackPushCommandLine(hist, argc, argv, err, HPARM)) {
+ airMopError(mop);
+ return 1;
+ }
+ do {
+ /* Every iteration of this will work on one argv[] element, or, one character of a
+ response file. As long as we avoid giving ourselves infinite work, eventually,
+ bird by bird, we will finish. */
+ if (histProc(havec, &(opt->helpWanted), hist, err, HPARM)) {
+ // error message in err
+ airMopError(mop);
+ return 1;
+ }
+ /* keep going while there's something on stack and no calls for help have been seen
+ */
+ } while (hist->len && !(opt->helpWanted));
+
hestArgVecPrint(__func__, havec);
airMopOkay(mop);
Modified: teem/trunk/src/hest/test/tparse.c
===================================================================
--- teem/trunk/src/hest/test/tparse.c 2025-09-18 05:52:43 UTC (rev 7449)
+++ teem/trunk/src/hest/test/tparse.c 2025-09-18 07:22:10 UTC (rev 7450)
@@ -21,12 +21,25 @@
int
main(int argc, const char **argv) {
-
+ int ret = 0;
hestOpt *opt = NULL;
+ hestParm *hparm = hestParmNew();
+ hparm->respectDashDashHelp = AIR_TRUE;
int res[2];
- hestOptAdd(&opt, "res", "sx sy", airTypeInt, 2, 2, res, NULL, "image resolution");
- hestParse2(opt, argc - 1, argv + 1, NULL, NULL);
+ hestOptAdd_2_Int(&opt, "res", "sx sy", res, NULL, "image resolution");
+ int flag;
+ hestOptAdd_Flag(&opt, "b,bingo", &flag, "a flag");
+ char *err;
+ hparm->verbosity = 0;
+ if (hestParse2(opt, argc - 1, argv + 1, &err, hparm)) {
+ fprintf(stderr, "%s: problem:\n%s\n", argv[0], err);
+ ret = 1;
+ }
+ if (opt->helpWanted) {
+ printf("%s: help wanted!\n", argv[0]);
+ }
hestOptFree(opt);
- exit(0);
+ hestParmFree(hparm);
+ exit(ret);
}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|