From 692ed8a6102ed86fdf6d525fbaf44aa182d5a37b Mon Sep 17 00:00:00 2001 From: John Date: Wed, 25 Dec 2024 10:38:44 +0100 Subject: [PATCH] add argFind and use it for more generic special args handling from env, improve help text --- src/ebusd/main_args.cpp | 25 +++++++++++++++---------- src/lib/utils/arg.cpp | 26 ++++++++++++++++++++++++++ src/lib/utils/arg.h | 15 ++++++++++++--- 3 files changed, 53 insertions(+), 13 deletions(-) diff --git a/src/ebusd/main_args.cpp b/src/ebusd/main_args.cpp index caf5e4bb..250fb7d6 100755 --- a/src/ebusd/main_args.cpp +++ b/src/ebusd/main_args.cpp @@ -130,6 +130,8 @@ static string s_configPath = CONFIG_PATH; #define O_DMPFLU (O_DMPSIZ-1) #define O_INJPOS 0x100 +#define ARG_NO_ENV (af_max << 1) + /** the definition of the known program arguments. */ static const argDef argDefs[] = { {nullptr, 0, nullptr, 0, "Device options:"}, @@ -156,17 +158,17 @@ static const argDef argDefs[] = { "\"full\" for full scan, " "a single hex address to scan, or " "\"off\" for not picking CSV files by scan result (default when configpath is given).\n" - "If combined with --checkconfig, you can add scan message data as " + "If combined with --checkconfig and --inject, you can add scan message data as " "arguments for checking a particular scan configuration, e.g. \"FF08070400/0AB5454850303003277201\"."}, {"scanretries", O_SCNRET, "COUNT", 0, "Retry scanning devices COUNT times [5]"}, {"configlang", O_CFGLNG, "LANG", 0, "Prefer LANG in multilingual configuration files [system default language, DE as fallback]"}, - {"checkconfig", O_CHKCFG, nullptr, 0, "Check config files, then stop"}, - {"dumpconfig", O_DMPCFG, "FORMAT", af_optional, + {"checkconfig", O_CHKCFG, nullptr, ARG_NO_ENV, "Check config files, then stop"}, + {"dumpconfig", O_DMPCFG, "FORMAT", af_optional|ARG_NO_ENV, "Check and dump config files in FORMAT (\"json\" or \"csv\"), then stop"}, {"dumpconfigto", O_DMPCTO, "FILE", 0, "Dump config files to FILE"}, {"pollinterval", O_POLINT, "SEC", 0, "Poll for data every SEC seconds (0=disable) [5]"}, - {"inject", 'i', "stop", af_optional, "Inject remaining arguments as commands or already seen messages " + {"inject", 'i', "stop", af_optional|ARG_NO_ENV, "Inject remaining arguments as commands or already seen messages " "(e.g. \"FF08070400/0AB5454850303003277201\"), optionally stop afterwards"}, {nullptr, O_INJPOS, "INJECT", af_optional|af_multiple, "Commands and/or messages to inject " "(if --inject was given)"}, @@ -642,18 +644,21 @@ int parse_main_args(int argc, char* argv[], char* envp[], options_t *opt) { } envopt[len] = 0; if (strcmp(envopt, "version") == 0 || strcmp(envopt, "image") == 0 || strcmp(envopt, "arch") == 0 - || strcmp(envopt, "opts") == 0 || strcmp(envopt, "inject") == 0 - || strcmp(envopt, "checkconfig") == 0 || strncmp(envopt, "dumpconfig", 10) == 0 + || strcmp(envopt, "opts") == 0 ) { - // ignore those defined in Dockerfile, EBUSD_OPTS, those with final args, and interactive ones + // ignore those defined in Dockerfile, EBUSD_OPTS + continue; + } + const argDef* found = argFind(&parseOpt, envopt); + if (found && found->flags & ARG_NO_ENV) { + // ignore those with final args and interactive ones continue; } char* envargv[] = {argv[0], envname, pos+1}; int cnt = pos[1] ? 2 : 1; if (pos[1] && strlen(*env) < sizeof(envname)-3 - && (strcmp(envopt, "scanconfig") == 0 || strcmp(envopt, "lograwdata") == 0 - || strcmp(envopt, "mqttjson") == 0 || strcmp(envopt, "knxurl") == 0 - )) { + && found && found->flags & af_optional + ) { // only really special case: af_optional with non-empty arg needs to use "=" syntax cnt = 1; strcat(envopt, pos); diff --git a/src/lib/utils/arg.cpp b/src/lib/utils/arg.cpp index afb972b8..53e52ecf 100755 --- a/src/lib/utils/arg.cpp +++ b/src/lib/utils/arg.cpp @@ -398,4 +398,30 @@ void argHelp(const char* name, const argParseOpt *parseOpt) { fflush(stdout); } +const argDef* argFindIn(const argDef *argDefs, const char* name) { + for (const argDef *arg = argDefs; arg && arg->help; arg++) { + if (arg->name && strcmp(name, arg->name) == 0) { // long option + return arg; + } + if (name[2] == 0 && arg->key >= '?' && name[0] == arg->key) { // short option + return arg; + } + } + return nullptr; +} + +const argDef* argFind(const argParseOpt *parseOpt, const char* name) { + const argDef* found = argFindIn(parseOpt->argDefs, name); + if (found) { + return found; + } + for (const argParseChildOpt *child = parseOpt->childOpts; child && child->argDefs; child++) { + found = argFindIn(child->argDefs, name); + if (found) { + return found; + } + } + return nullptr; +} + } // namespace ebusd diff --git a/src/lib/utils/arg.h b/src/lib/utils/arg.h index bb3d4e22..c7f136dd 100755 --- a/src/lib/utils/arg.h +++ b/src/lib/utils/arg.h @@ -25,10 +25,11 @@ namespace ebusd { /** the available arg flags. */ enum ArgFlag { - af_optional = 1<<0, //!< optional argument value - af_multiple = 1<<1, //!< may appear multiple times (only allowed for last positional) - af_noHelp = 1<<2, //!< do not include -?/--help option + af_optional = 1<<0, //!< optional argument value + af_multiple = 1<<1, //!< may appear multiple times (only allowed for last positional) + af_noHelp = 1<<2, //!< do not include -?/--help option af_noVersion = 1<<3, //!< do not include -V/--version option + af_max = 1<<3, //!< maximum defined flag value }; /** Definition of a single argument. */ @@ -87,6 +88,14 @@ int argParse(const argParseOpt *parseOpt, int argc, char **argv, void *userArg); */ void argHelp(const char* name, const argParseOpt *parseOpt); +/** + * Find the argument with the given name. + * @param parseOpt pointer to the @a argParseOpt structure. + * @param name the name of the argument, either short or long. + * @return a pointer to the found @a argDef, or nullptr. + */ +const argDef* argFind(const argParseOpt *parseOpt, const char* name); + /** * Convenience macro to print an error message to stderr. */