add positionals to arg parser, avoid unnecessary parts in arg options

This commit is contained in:
John
2023-11-01 22:37:30 +01:00
parent b901232678
commit 3d9dd9b1c3
10 changed files with 169 additions and 126 deletions
+1 -1
View File
@@ -83,7 +83,7 @@ static vector<string>* g_integrationVars = nullptr; //!< the integration settin
* @param arg the option argument, or nullptr. * @param arg the option argument, or nullptr.
* @param state the parsing state. * @param state the parsing state.
*/ */
static int knx_parse_opt(int key, char *arg, const argParseOpt *parseOpt) { static int knx_parse_opt(int key, char *arg, const argParseOpt *parseOpt, void *userArg) {
result_t result; result_t result;
unsigned int value; unsigned int value;
switch (key) { switch (key) {
+7 -7
View File
@@ -222,8 +222,7 @@ void signalHandler(int sig) {
* @return the exit code. * @return the exit code.
*/ */
int main(int argc, char* argv[], char* envp[]) { int main(int argc, char* argv[], char* envp[]) {
int arg_index = -1; switch (parse_main_args(argc, argv, envp, &s_opt)) {
switch (parse_main_args(argc, argv, envp, &s_opt, &arg_index)) {
case 0: // OK case 0: // OK
break; break;
case '?': // help printed case '?': // help printed
@@ -236,9 +235,9 @@ int main(int argc, char* argv[], char* envp[]) {
return EINVAL; return EINVAL;
} }
if (arg_index >= 0) { if (s_opt.injectCount > 0) {
if (!s_opt.injectMessages && !(s_opt.checkConfig && s_opt.scanConfig)) { if (!s_opt.injectMessages && !(s_opt.checkConfig && s_opt.scanConfig)) {
fprintf(stderr, "invalid arguments starting with \"%s\"", argv[arg_index]); fprintf(stderr, "invalid inject arguments");
return EINVAL; return EINVAL;
} }
} }
@@ -302,10 +301,11 @@ int main(int argc, char* argv[], char* envp[]) {
if (s_opt.checkConfig) { if (s_opt.checkConfig) {
logNotice(lf_main, PACKAGE_STRING "." REVISION " performing configuration check..."); logNotice(lf_main, PACKAGE_STRING "." REVISION " performing configuration check...");
result_t result = s_scanHelper->loadConfigFiles(!s_opt.scanConfig || arg_index >= argc); result_t result = s_scanHelper->loadConfigFiles(!s_opt.scanConfig || s_opt.injectCount <= 0);
result_t overallResult = s_scanHelper->executeInstructions(nullptr); result_t overallResult = s_scanHelper->executeInstructions(nullptr);
MasterSymbolString master; MasterSymbolString master;
SlaveSymbolString slave; SlaveSymbolString slave;
int arg_index = argc - s_opt.injectCount;
while (result == RESULT_OK && s_opt.scanConfig && arg_index < argc) { while (result == RESULT_OK && s_opt.scanConfig && arg_index < argc) {
// check scan config for each passed ident message // check scan config for each passed ident message
if (!s_scanHelper->parseMessage(argv[arg_index++], true, &master, &slave)) { if (!s_scanHelper->parseMessage(argv[arg_index++], true, &master, &slave)) {
@@ -413,11 +413,11 @@ int main(int argc, char* argv[], char* envp[]) {
if (s_opt.injectMessages) { if (s_opt.injectMessages) {
int scanAdrCount = 0; int scanAdrCount = 0;
bool scanAddresses[256] = {}; bool scanAddresses[256] = {};
while (arg_index < argc) { for (int arg_index = argc - s_opt.injectCount; arg_index < argc; arg_index++) {
// add each passed message // add each passed message
MasterSymbolString master; MasterSymbolString master;
SlaveSymbolString slave; SlaveSymbolString slave;
if (!s_scanHelper->parseMessage(argv[arg_index++], false, &master, &slave)) { if (!s_scanHelper->parseMessage(argv[arg_index], false, &master, &slave)) {
continue; continue;
} }
protocol->injectMessage(master, slave); protocol->injectMessage(master, slave);
+4 -2
View File
@@ -33,6 +33,8 @@ namespace ebusd {
*/ */
/** the config path part behind the scheme (scheme without "://"). */ /** the config path part behind the scheme (scheme without "://"). */
//#define CONFIG_PATH_SUFFIX "://ebus.github.io/cfg/de/"
#define CONFIG_PATH_SUFFIX "://cfg.ebusd.eu/" #define CONFIG_PATH_SUFFIX "://cfg.ebusd.eu/"
/** A structure holding all program options. */ /** A structure holding all program options. */
@@ -62,6 +64,7 @@ typedef struct options {
unsigned int pollInterval; //!< poll interval in seconds, 0 to disable [5] unsigned int pollInterval; //!< poll interval in seconds, 0 to disable [5]
bool injectMessages; //!< inject remaining arguments as already seen messages bool injectMessages; //!< inject remaining arguments as already seen messages
bool stopAfterInject; //!< only inject messages once, then stop bool stopAfterInject; //!< only inject messages once, then stop
int injectCount; //!< number of message arguments to inject, or 0
const char* caFile; //!< the CA file to use (uses defaults if neither caFile nor caPath are set), or "#" for insecure const char* caFile; //!< the CA file to use (uses defaults if neither caFile nor caPath are set), or "#" for insecure
const char* caPath; //!< the path with CA files to use (uses defaults if neither caFile nor caPath are set) const char* caPath; //!< the path with CA files to use (uses defaults if neither caFile nor caPath are set)
@@ -107,11 +110,10 @@ typedef struct options {
* @param argv the command line arguments. * @param argv the command line arguments.
* @param envp the environment variables to parse before the args, or nullptr. * @param envp the environment variables to parse before the args, or nullptr.
* @param opt pointer to the parsed arguments (will be initialized to defaults first). * @param opt pointer to the parsed arguments (will be initialized to defaults first).
* @param argIndex optional pointer for storing the index to the first non-argument found in argv.
* @return 0 on success, '!' for an invalid argument value, ':' for a missing argument value, * @return 0 on success, '!' for an invalid argument value, ':' for a missing argument value,
* '?' when "-?" was given, or the result of the parse function if non-zero. * '?' when "-?" was given, or the result of the parse function if non-zero.
*/ */
int parse_main_args(int argc, char* argv[], char* envp[], options_t* opt, int* argIndex); int parse_main_args(int argc, char* argv[], char* envp[], options_t* opt);
} // namespace ebusd } // namespace ebusd
+17 -12
View File
@@ -56,6 +56,7 @@ static const options_t s_default_opt = {
.pollInterval = 5, .pollInterval = 5,
.injectMessages = false, .injectMessages = false,
.stopAfterInject = false, .stopAfterInject = false,
.injectCount = 0,
.caFile = nullptr, .caFile = nullptr,
.caPath = nullptr, .caPath = nullptr,
@@ -133,6 +134,7 @@ static string s_configPath = CONFIG_PATH;
#define O_DMPFIL (O_RAWSIZ-1) #define O_DMPFIL (O_RAWSIZ-1)
#define O_DMPSIZ (O_DMPFIL-1) #define O_DMPSIZ (O_DMPFIL-1)
#define O_DMPFLU (O_DMPSIZ-1) #define O_DMPFLU (O_DMPSIZ-1)
#define O_INJPOS 0x100
/** the definition of the known program arguments. */ /** the definition of the known program arguments. */
static const argDef argDefs[] = { static const argDef argDefs[] = {
@@ -169,6 +171,7 @@ static const argDef argDefs[] = {
{"pollinterval", O_POLINT, "SEC", 0, "Poll for data every SEC seconds (0=disable) [5]"}, {"pollinterval", O_POLINT, "SEC", 0, "Poll for data every SEC seconds (0=disable) [5]"},
{"inject", 'i', "stop", af_optional, "Inject remaining arguments as already seen messages (e.g. " {"inject", 'i', "stop", af_optional, "Inject remaining arguments as already seen messages (e.g. "
"\"FF08070400/0AB5454850303003277201\"), optionally stop afterwards"}, "\"FF08070400/0AB5454850303003277201\"), optionally stop afterwards"},
{nullptr, O_INJPOS, "INJECT", af_optional|af_multiple, "Message(s) to inject (if --inject was given)"},
#ifdef HAVE_SSL #ifdef HAVE_SSL
{"cafile", O_CAFILE, "FILE", 0, "Use CA FILE for checking certificates (uses defaults," {"cafile", O_CAFILE, "FILE", 0, "Use CA FILE for checking certificates (uses defaults,"
" \"#\" for insecure)"}, " \"#\" for insecure)"},
@@ -229,8 +232,7 @@ static const argDef argDefs[] = {
* @param arg the option argument, or nullptr. * @param arg the option argument, or nullptr.
* @param parseOpt the parse options. * @param parseOpt the parse options.
*/ */
static int parse_opt(int key, char *arg, const argParseOpt *parseOpt) { static int parse_opt(int key, char *arg, const argParseOpt *parseOpt, struct options *opt) {
struct options *opt = (struct options*)parseOpt->userArg;
result_t result = RESULT_OK; result_t result = RESULT_OK;
unsigned int value; unsigned int value;
@@ -590,7 +592,14 @@ static int parse_opt(int key, char *arg, const argParseOpt *parseOpt) {
break; break;
default: default:
return ESRCH; if (key >= O_INJPOS) { // INJECT
if (!opt->injectMessages || !arg || !arg[0]) {
return ESRCH;
}
opt->injectCount++;
} else {
return ESRCH;
}
} }
// check for invalid arg combinations // check for invalid arg combinations
@@ -610,18 +619,15 @@ static int parse_opt(int key, char *arg, const argParseOpt *parseOpt) {
return 0; return 0;
} }
int parse_main_args(int argc, char* argv[], char* envp[], options_t *opt, int *arg_index) { int parse_main_args(int argc, char* argv[], char* envp[], options_t *opt) {
*opt = s_default_opt; *opt = s_default_opt;
const argParseOpt parseOpt = { const argParseOpt parseOpt = {
argDefs, argDefs,
parse_opt, reinterpret_cast<parse_function_t>(parse_opt),
0, 0,
"" PACKAGE_NAME,
"[INJECT...]",
"A daemon for communication with eBUS heating systems.", "A daemon for communication with eBUS heating systems.",
"Report bugs to " PACKAGE_BUGREPORT " .", "Report bugs to " PACKAGE_BUGREPORT " .",
datahandler_getargs(), datahandler_getargs()
opt
}; };
char envname[32] = "--"; // needs to cover at least max length of any option name plus "--" char envname[32] = "--"; // needs to cover at least max length of any option name plus "--"
@@ -651,7 +657,7 @@ int parse_main_args(int argc, char* argv[], char* envp[], options_t *opt, int *a
int cnt = pos[1] ? 2 : 1; int cnt = pos[1] ? 2 : 1;
if (pos[1] && strlen(*env) < sizeof(envname)-3 if (pos[1] && strlen(*env) < sizeof(envname)-3
&& (strcmp(envopt, "scanconfig") == 0 || strcmp(envopt, "lograwdata") == 0)) { && (strcmp(envopt, "scanconfig") == 0 || strcmp(envopt, "lograwdata") == 0)) {
// only really special case: OPTION_ARG_OPTIONAL with non-empty arg needs to use "=" syntax // only really special case: af_optional with non-empty arg needs to use "=" syntax
cnt = 1; cnt = 1;
strcat(envopt, pos); strcat(envopt, pos);
} }
@@ -666,8 +672,7 @@ int parse_main_args(int argc, char* argv[], char* envp[], options_t *opt, int *a
} }
} }
*arg_index = -1; int ret = argParse(&parseOpt, argc, argv, reinterpret_cast<void*>(opt));
int ret = argParse(&parseOpt, argc, argv, arg_index);
if (ret != 0) { if (ret != 0) {
return ret; return ret;
} }
+1 -1
View File
@@ -140,7 +140,7 @@ void splitFields(const string& str, vector<string>* row);
* @param arg the option argument, or nullptr. * @param arg the option argument, or nullptr.
* @param state the parsing state. * @param state the parsing state.
*/ */
static int mqtt_parse_opt(int key, char *arg, const argParseOpt *parseOpt) { static int mqtt_parse_opt(int key, char *arg, const argParseOpt *parseOpt, void *userArg) {
result_t result = RESULT_OK; result_t result = RESULT_OK;
unsigned int value; unsigned int value;
switch (key) { switch (key) {
+87 -38
View File
@@ -28,50 +28,50 @@ namespace ebusd {
#define isAlpha(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z')) #define isAlpha(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z'))
void calcCounts(const argDef *argDefs, int &count, int &shortCharsCount, int &shortOptsCount) { void calcCounts(const argDef *argDefs, int *count, int *shortCharsCount, int *shortOptsCount) {
for (const argDef *arg = argDefs; arg && arg->help; arg++) { for (const argDef *arg = argDefs; arg && arg->help; arg++) {
if (!arg->name) { if (!arg->name) {
continue; continue;
} }
count++; (*count)++;
if (!isAlpha(arg->key)) { if (!isAlpha(arg->key)) {
continue; continue;
} }
shortCharsCount++; (*shortCharsCount)++;
shortOptsCount++; (*shortOptsCount)++;
if (arg->valueName) { if (arg->valueName) {
shortOptsCount++; (*shortOptsCount)++;
if (arg->flags & af_optional) { if (arg->flags & af_optional) {
shortOptsCount++; (*shortOptsCount)++;
} }
} }
} }
} }
void buildOpts(const argDef *argDefs, int &count, int &shortCharsCount, int &shortOptsCount, void buildOpts(const argDef *argDefs, int *count, int *shortCharsCount, int *shortOptsCount,
struct option *longOpts, char *shortChars, int *shortIndexes, char *shortOpts, int argDefIdx) { struct option *longOpts, char *shortChars, int *shortIndexes, char *shortOpts, int argDefIdx) {
struct option *opt = longOpts+count; struct option *opt = longOpts+(*count);
for (const argDef *arg = argDefs; arg && arg->help; arg++, argDefIdx++) { for (const argDef *arg = argDefs; arg && arg->help; arg++, argDefIdx++) {
if (!arg->name) { if (!arg->name) {
continue; continue;
} }
opt->name = arg->name; opt->name = arg->name;
opt->has_arg = arg->valueName ? ((arg->flags & af_optional) ? optional_argument : required_argument) : no_argument; opt->has_arg = arg->valueName ? ((arg->flags & af_optional) ? optional_argument : required_argument) : no_argument;
opt->flag = nullptr; opt->flag = NULL;
opt->val = argDefIdx; opt->val = argDefIdx;
if (isAlpha(arg->key)) { if (isAlpha(arg->key)) {
shortChars[shortCharsCount] = static_cast<char>(arg->key); shortChars[(*shortCharsCount)] = static_cast<char>(arg->key);
shortIndexes[shortCharsCount++] = count; shortIndexes[(*shortCharsCount)++] = *count;
shortOpts[shortOptsCount++] = static_cast<char>(arg->key); shortOpts[(*shortOptsCount)++] = static_cast<char>(arg->key);
if (arg->valueName) { if (arg->valueName) {
shortOpts[shortOptsCount++] = ':'; shortOpts[(*shortOptsCount)++] = ':';
if (arg->flags & af_optional) { if (arg->flags & af_optional) {
shortOpts[shortOptsCount++] = ':'; shortOpts[(*shortOptsCount)++] = ':';
} }
} }
} }
opt++; opt++;
count++; (*count)++;
} }
} }
@@ -87,17 +87,17 @@ static const argDef versionArgDefs[] = {
endArgDef endArgDef
}; };
int argParse(const argParseOpt *parseOpt, int argc, char **argv, int *argIndex) { int argParse(const argParseOpt *parseOpt, int argc, char **argv, void* userArg) {
int count = 0, shortCharsCount = 0, shortOptsCount = 0; int count = 0, shortCharsCount = 0, shortOptsCount = 0;
if (!(parseOpt->flags & af_noHelp)) { if (!(parseOpt->flags & af_noHelp)) {
calcCounts(helpArgDefs, count, shortCharsCount, shortOptsCount); calcCounts(helpArgDefs, &count, &shortCharsCount, &shortOptsCount);
} }
if (!(parseOpt->flags & af_noVersion)) { if (!(parseOpt->flags & af_noVersion)) {
calcCounts(versionArgDefs, count, shortCharsCount, shortOptsCount); calcCounts(versionArgDefs, &count, &shortCharsCount, &shortOptsCount);
} }
calcCounts(parseOpt->argDefs, count, shortCharsCount, shortOptsCount); calcCounts(parseOpt->argDefs, &count, &shortCharsCount, &shortOptsCount);
for (const argParseChildOpt *child = parseOpt->childOpts; child && child->argDefs; child++) { for (const argParseChildOpt *child = parseOpt->childOpts; child && child->argDefs; child++) {
calcCounts(child->argDefs, count, shortCharsCount, shortOptsCount); calcCounts(child->argDefs, &count, &shortCharsCount, &shortOptsCount);
} }
struct option *longOpts = (struct option*)calloc(count+1, sizeof(struct option)); // room for EOF struct option *longOpts = (struct option*)calloc(count+1, sizeof(struct option)); // room for EOF
char *shortChars = reinterpret_cast<char*>(calloc(shortCharsCount+1, sizeof(char))); // room for \0 char *shortChars = reinterpret_cast<char*>(calloc(shortCharsCount+1, sizeof(char))); // room for \0
@@ -109,18 +109,18 @@ int argParse(const argParseOpt *parseOpt, int argc, char **argv, int *argIndex)
shortOpts[shortOptsCount++] = '+'; // posix mode to stop at first non-option shortOpts[shortOptsCount++] = '+'; // posix mode to stop at first non-option
shortOpts[shortOptsCount++] = ':'; // return ':' for missing option shortOpts[shortOptsCount++] = ':'; // return ':' for missing option
if (!(parseOpt->flags & af_noHelp)) { if (!(parseOpt->flags & af_noHelp)) {
buildOpts(helpArgDefs, count, shortCharsCount, shortOptsCount, longOpts, shortChars, buildOpts(helpArgDefs, &count, &shortCharsCount, &shortOptsCount, longOpts, shortChars,
shortIndexes, shortOpts, 0xff00); shortIndexes, shortOpts, 0xff00);
} }
if (!(parseOpt->flags & af_noVersion)) { if (!(parseOpt->flags & af_noVersion)) {
buildOpts(versionArgDefs, count, shortCharsCount, shortOptsCount, longOpts, shortChars, buildOpts(versionArgDefs, &count, &shortCharsCount, &shortOptsCount, longOpts, shortChars,
shortIndexes, shortOpts, 0xff01); shortIndexes, shortOpts, 0xff01);
} }
buildOpts(parseOpt->argDefs, count, shortCharsCount, shortOptsCount, longOpts, shortChars, buildOpts(parseOpt->argDefs, &count, &shortCharsCount, &shortOptsCount, longOpts, shortChars,
shortIndexes, shortOpts, 0); shortIndexes, shortOpts, 0);
int children = 0; int children = 0;
for (const argParseChildOpt *child = parseOpt->childOpts; child && child->argDefs; child++) { for (const argParseChildOpt *child = parseOpt->childOpts; child && child->argDefs; child++) {
buildOpts(child->argDefs, count, shortCharsCount, shortOptsCount, longOpts, shortChars, buildOpts(child->argDefs, &count, &shortCharsCount, &shortOptsCount, longOpts, shortChars,
shortIndexes, shortOpts, 0x100*(++children)); shortIndexes, shortOpts, 0x100*(++children));
} }
optind = 1; // setting to 0 does not work optind = 1; // setting to 0 does not work
@@ -177,16 +177,49 @@ int argParse(const argParseOpt *parseOpt, int argc, char **argv, int *argIndex)
parser = parseOpt->parser; parser = parseOpt->parser;
} }
const argDef *arg = argDefs + (val & 0xff); const argDef *arg = argDefs + (val & 0xff);
c = parser(arg->key, optarg, parseOpt); c = parser(arg->key, optarg, parseOpt, userArg);
if (c != 0) { if (c != 0) {
ret = c; ret = c;
break; break;
} }
} }
if (ret == 0) {
// check for positionals
for (const argDef *arg = parseOpt->argDefs; arg && arg->help; arg++) {
if (arg->name || !arg->valueName) {
continue; // short/long arg or group
}
if (optind < argc) {
int key = arg->key;
do {
c = parseOpt->parser(key, argv[optind], parseOpt, userArg);
if (c != 0) {
ret = c;
break;
}
if (optind+1 >= argc || !(arg->flags & af_multiple)) {
break;
}
key++;
optind++;
} while (true);
if (ret != 0) {
break;
}
} else if (!(arg->flags & af_optional)) {
ret = ':'; // missing argument
fprintf(stderr, "missing argument\n");
break;
}
optind++;
}
if (ret == 0 && optind < argc) {
ret = '!'; // extra unexpected argument
fprintf(stderr, "extra argument %s\n", argv[optind]);
}
}
if (ret == '?') { if (ret == '?') {
argHelp(parseOpt); argHelp(argv[0], parseOpt);
} else if (argIndex && optind < argc) {
*argIndex = optind;
} }
free(longOpts); free(longOpts);
free(shortChars); free(shortChars);
@@ -270,7 +303,7 @@ size_t calcIndent(const argDef *argDefs) {
void printArgs(const argDef *argDefs, size_t indent) { void printArgs(const argDef *argDefs, size_t indent) {
for (const argDef *arg = argDefs; arg && arg->help; arg++) { for (const argDef *arg = argDefs; arg && arg->help; arg++) {
if (!arg->name) { if (!arg->name && !arg->valueName) {
if (*arg->help) { if (*arg->help) {
printf("\n %s\n", arg->help); printf("\n %s\n", arg->help);
} else { } else {
@@ -284,15 +317,21 @@ void printArgs(const argDef *argDefs, size_t indent) {
} else { } else {
printf(" "); printf(" ");
} }
printf(" --%s", arg->name); size_t taken = 2 + 3 + 3;
size_t taken = 2 + 3 + 3 + strlen(arg->name); if (arg->name) {
printf(" --%s", arg->name);
taken += strlen(arg->name);
} else {
printf(" ");
}
if (arg->valueName) { if (arg->valueName) {
taken += 1 + strlen(arg->valueName); bool multi = arg->flags & af_multiple;
taken += (arg->name ? 1 : 0) + strlen(arg->valueName) + (multi ? 3 : 0);
if (arg->flags & af_optional) { if (arg->flags & af_optional) {
printf("[=%s]", arg->valueName); printf("[%s%s%s]", arg->name ? "=" : "", arg->valueName, multi ? "..." : "");
taken += 2; taken += 2;
} else { } else {
printf("=%s", arg->valueName); printf("%s%s%s", arg->name ? "=" : "", arg->valueName, multi ? "..." : "");
} }
} }
if (taken > indent) { if (taken > indent) {
@@ -305,7 +344,7 @@ void printArgs(const argDef *argDefs, size_t indent) {
} }
} }
void argHelp(const argParseOpt *parseOpt) { void argHelp(const char* name, const argParseOpt *parseOpt) {
size_t indent = calcIndent(parseOpt->argDefs); size_t indent = calcIndent(parseOpt->argDefs);
if (indent < MAX_INDENT) { if (indent < MAX_INDENT) {
for (const argParseChildOpt *child = parseOpt->childOpts; child && child->argDefs; child++) { for (const argParseChildOpt *child = parseOpt->childOpts; child && child->argDefs; child++) {
@@ -323,9 +362,19 @@ void argHelp(const argParseOpt *parseOpt) {
} else if (indent < MIN_INDENT) { } else if (indent < MIN_INDENT) {
indent = MIN_INDENT; indent = MIN_INDENT;
} }
printf("Usage: %s [OPTION...] %s\n", printf("Usage: %s [OPTION...]", basename(name));
parseOpt->name, for (const argDef *arg = parseOpt->argDefs; arg && arg->help; arg++) {
parseOpt->positional ? parseOpt->positional : ""); if (arg->name || !arg->valueName) {
continue;
}
bool multi = arg->flags & af_multiple;
if (arg->flags & af_optional) {
printf(" [%s%s]", arg->valueName, multi ? "..." : "");
} else {
printf(" %s%s", arg->valueName, multi ? "..." : "");
}
}
printf("\n");
wrap(parseOpt->help, 0, 0); wrap(parseOpt->help, 0, 0);
printArgs(parseOpt->argDefs, indent); printArgs(parseOpt->argDefs, indent);
for (const argParseChildOpt *child = parseOpt->childOpts; child && child->argDefs; child++) { for (const argParseChildOpt *child = parseOpt->childOpts; child && child->argDefs; child++) {
+14 -14
View File
@@ -26,15 +26,16 @@ namespace ebusd {
/** the available arg flags. */ /** the available arg flags. */
enum ArgFlag { enum ArgFlag {
af_optional = 1<<0, //!< optional argument value af_optional = 1<<0, //!< optional argument value
af_noHelp = 1<<1, //!< do not include -?/--help option af_multiple = 1<<1, //!< may appear multiple times (only allowed for last positional)
af_noVersion = 1<<2, //!< do not include -V/--version option af_noHelp = 1<<2, //!< do not include -?/--help option
af_noVersion = 1<<3, //!< do not include -V/--version option
}; };
/** Definition of a single argument. */ /** Definition of a single argument. */
typedef struct argDef { typedef struct argDef {
const char* name; //!< the (long) name of the argument, or nullptr for a group header const char* name; //!< the (long) name of the argument, or nullptr for a group header or positional
int key; //!< the argument key, also used as short name if alphabetic or the question mark int key; //!< the argument key, also used as short name if alphabetic or the question mark
const char* valueName; //!< the optional argument value name const char* valueName; //!< the optional argument value name, or nullptr for group header or argument without value name
int flags; //!< flags for the argument, bit combination of @a ArgFlag int flags; //!< flags for the argument, bit combination of @a ArgFlag
const char* help; //!< help text (mandatory) const char* help; //!< help text (mandatory)
} argDef; } argDef;
@@ -43,12 +44,13 @@ struct argParseOpt;
/** /**
* Function to be called for each argument. * Function to be called for each argument.
* @param key the argument key as defined. * @param key the argument key as defined. for positional arguments with multiple flag this will be increased with each call.
* @param arg the argument value, or nullptr. * @param arg the argument value, or nullptr.
* @param parseOpt ppointer to the @a argParseOpt structure. * @param parseOpt pointer to the @a argParseOpt structure.
* @param userArg pointer to user argument.
* @return 0 on success, non-zero otherwise. * @return 0 on success, non-zero otherwise.
*/ */
typedef int (*parse_function_t)(int key, char *arg, const struct argParseOpt *parseOpt); typedef int (*parse_function_t)(int key, char *arg, const struct argParseOpt *parseOpt, void *userArg);
/** Options for child definitions. */ /** Options for child definitions. */
typedef struct argParseChildOpt { typedef struct argParseChildOpt {
@@ -61,12 +63,9 @@ typedef struct argParseOpt {
const argDef *argDefs; //!< pointer to the argument defintions (last one needs to have nullptr help as end sign) const argDef *argDefs; //!< pointer to the argument defintions (last one needs to have nullptr help as end sign)
parse_function_t parser; //!< parse function to use parse_function_t parser; //!< parse function to use
int flags; //!< flags for the parser, bit combination of @a ArgFlag int flags; //!< flags for the parser, bit combination of @a ArgFlag
const char* name; //!< name of the program parsed
const char* positional; //!< help text for optional positional argument
const char* help; //!< help text for the program (second line of help output) const char* help; //!< help text for the program (second line of help output)
const char* suffix; //!< optional help suffix text const char* suffix; //!< optional help suffix text
const argParseChildOpt *childOpts; //!< optional child definitions const argParseChildOpt *childOpts; //!< optional child definitions
void* userArg; //!< optional user argument
} argParseOpt; } argParseOpt;
/** /**
@@ -74,17 +73,18 @@ typedef struct argParseOpt {
* @param parseOpt pointer to the @a argParseOpt structure. * @param parseOpt pointer to the @a argParseOpt structure.
* @param argc the argument count (including the full program name in index 0). * @param argc the argument count (including the full program name in index 0).
* @param argv the argument values (including the full program name in index 0). * @param argv the argument values (including the full program name in index 0).
* @param argIndex optional pointer for storing the index to the first non-argument found in argv. * @param userArg pointer to user argument to pass to parser.
* @return 0 on success, '!' for an invalid argument value, ':' for a missing argument value, * @return 0 on success, '!' for an invalid argument value, ':' for a missing argument value,
* '?' when "-?" was given, or the result of the parse function if non-zero. * '?' when "-?" was given, or the result of the parse function if non-zero.
*/ */
int argParse(const argParseOpt *parseOpt, int argc, char **argv, int *argIndex); int argParse(const argParseOpt *parseOpt, int argc, char **argv, void *userArg);
/** /**
* Print the help text. * Print the help text.
* @param parseOpt ppointer to the @a argParseOpt structure. * @param name the name of the program.
* @param parseOpt pointer to the @a argParseOpt structure.
*/ */
void argHelp(const argParseOpt *parseOpt); void argHelp(const char* name, const argParseOpt *parseOpt);
/** /**
* Convenience macro to print an error message to stderr. * Convenience macro to print an error message to stderr.
+12 -17
View File
@@ -47,7 +47,6 @@ struct options {
uint16_t timeout; //!< ebusd connect/send/receive timeout uint16_t timeout; //!< ebusd connect/send/receive timeout
bool errorResponse; //!< non-zero exit on error response bool errorResponse; //!< non-zero exit on error response
char* const *args; //!< arguments to pass to ebusd
unsigned int argCount; //!< number of arguments to pass to ebusd unsigned int argCount; //!< number of arguments to pass to ebusd
}; };
@@ -58,7 +57,6 @@ static struct options opt = {
60, // timeout 60, // timeout
false, // non-zero exit on error response false, // non-zero exit on error response
nullptr, // args
0 // argCount 0 // argCount
}; };
@@ -71,6 +69,8 @@ static const argDef argDefs[] = {
", 0 for none [60]"}, ", 0 for none [60]"},
{"error", 'e', nullptr, 0, "Exit non-zero if the connection was fine but the response indicates non-success"}, {"error", 'e', nullptr, 0, "Exit non-zero if the connection was fine but the response indicates non-success"},
{nullptr, 0x100, "COMMAND", af_optional|af_multiple, "COMMAND (and arguments) to send to " PACKAGE "."},
{nullptr, 0, nullptr, 0, nullptr}, {nullptr, 0, nullptr, 0, nullptr},
}; };
@@ -80,8 +80,7 @@ static const argDef argDefs[] = {
* @param arg the option argument, or nullptr. * @param arg the option argument, or nullptr.
* @param parseOpt the parse options. * @param parseOpt the parse options.
*/ */
static int parse_opt(int key, char *arg, const argParseOpt *parseOpt) { static int parse_opt(int key, char *arg, const argParseOpt *parseOpt, struct options *opt) {
struct options *opt = (struct options*)parseOpt->userArg;
char* strEnd = nullptr; char* strEnd = nullptr;
unsigned int value; unsigned int value;
switch (key) { switch (key) {
@@ -113,7 +112,11 @@ static int parse_opt(int key, char *arg, const argParseOpt *parseOpt) {
opt->errorResponse = true; opt->errorResponse = true;
break; break;
default: default:
return ESRCH; if (key >= 0x100) {
opt->argCount++;
} else {
return ESRCH;
}
} }
return 0; return 0;
} }
@@ -344,18 +347,14 @@ bool connect(const char* host, uint16_t port, uint16_t timeout, char* const *arg
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
argParseOpt parseOpt = { argParseOpt parseOpt = {
argDefs, argDefs,
parse_opt, reinterpret_cast<parse_function_t>(parse_opt),
af_noVersion, af_noVersion,
"ebusctl",
"[COMMAND [CMDOPT...]]",
"Client for accessing " PACKAGE " via TCP.", "Client for accessing " PACKAGE " via TCP.",
"If given, send COMMAND together with CMDOPT options to " PACKAGE ".\n" "If given, send COMMAND together with arguments to " PACKAGE ".\n"
"Use 'help' as COMMAND for help on available " PACKAGE " commands.", "Use 'help' as COMMAND for help on available " PACKAGE " commands.",
nullptr, nullptr,
&opt
}; };
int arg_index = -1; switch (argParse(&parseOpt, argc, argv, &opt)) {
switch (argParse(&parseOpt, argc, argv, &arg_index)) {
case 0: // OK case 0: // OK
break; break;
case '?': // help printed case '?': // help printed
@@ -363,12 +362,8 @@ int main(int argc, char* argv[]) {
default: default:
return EINVAL; return EINVAL;
} }
if (arg_index >= 0) {
opt.args = argv + arg_index;
opt.argCount = argc - arg_index;
}
bool success = connect(opt.server, opt.port, opt.timeout, opt.args, opt.argCount); bool success = connect(opt.server, opt.port, opt.timeout, argv + argc - opt.argCount, opt.argCount);
exit(success ? EXIT_SUCCESS : EXIT_FAILURE); exit(success ? EXIT_SUCCESS : EXIT_FAILURE);
} }
+13 -23
View File
@@ -62,6 +62,7 @@ static struct options opt = {
static const ebusd::argDef argDefs[] = { static const ebusd::argDef argDefs[] = {
{"device", 'd', "DEV", 0, "Write to DEV (serial device) [/dev/ttyUSB60]"}, {"device", 'd', "DEV", 0, "Write to DEV (serial device) [/dev/ttyUSB60]"},
{"time", 't', "USEC", 0, "Delay each byte by USEC us [10000]"}, {"time", 't', "USEC", 0, "Delay each byte by USEC us [10000]"},
{nullptr, 0x100, "DUMPFILE", af_optional, "Dump file to read [/tmp/ebus_dump.bin]"},
{nullptr, 0, nullptr, 0, nullptr}, {nullptr, 0, nullptr, 0, nullptr},
}; };
@@ -72,8 +73,7 @@ static const ebusd::argDef argDefs[] = {
* @param arg the option argument, or nullptr. * @param arg the option argument, or nullptr.
* @param parseOpt the parse options. * @param parseOpt the parse options.
*/ */
static int parse_opt(int key, char *arg, const ebusd::argParseOpt *parseOpt) { static int parse_opt(int key, char *arg, const ebusd::argParseOpt *parseOpt, struct options *opt) {
struct options *opt = (struct options*)parseOpt->userArg;
char* strEnd = nullptr; char* strEnd = nullptr;
switch (key) { switch (key) {
// Device settings: // Device settings:
@@ -91,6 +91,14 @@ static int parse_opt(int key, char *arg, const ebusd::argParseOpt *parseOpt) {
return EINVAL; return EINVAL;
} }
break; break;
case 0x100: // DUMPFILE
if (arg[0] == 0 || strcmp("/", arg) == 0) {
argParseError(parseOpt, "invalid dumpfile");
return EINVAL;
}
opt->dumpFile = arg;
break;
default: default:
return ESRCH; return ESRCH;
} }
@@ -107,13 +115,9 @@ static int parse_opt(int key, char *arg, const ebusd::argParseOpt *parseOpt) {
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
ebusd::argParseOpt parseOpt = { ebusd::argParseOpt parseOpt = {
argDefs, argDefs,
parse_opt, reinterpret_cast<parse_function_t>(parse_opt),
af_noVersion, af_noVersion,
"ebusfeed",
"[DUMPFILE]",
"Feed data from an " PACKAGE " DUMPFILE to a serial device.", "Feed data from an " PACKAGE " DUMPFILE to a serial device.",
"With no DUMPFILE, /tmp/ebus_dump.bin is used.\n"
"\n"
"Example for setting up two pseudo terminals with 'socat':\n" "Example for setting up two pseudo terminals with 'socat':\n"
" 1. 'socat -d -d pty,raw,echo=0 pty,raw,echo=0'\n" " 1. 'socat -d -d pty,raw,echo=0 pty,raw,echo=0'\n"
" 2. create symbol links to appropriate devices, e.g.\n" " 2. create symbol links to appropriate devices, e.g.\n"
@@ -122,10 +126,8 @@ int main(int argc, char* argv[]) {
" 3. start " PACKAGE ": '" PACKAGE " -f -d /dev/ttyUSB20 --nodevicecheck'\n" " 3. start " PACKAGE ": '" PACKAGE " -f -d /dev/ttyUSB20 --nodevicecheck'\n"
" 4. start ebusfeed: 'ebusfeed /path/to/ebus_dump.bin'", " 4. start ebusfeed: 'ebusfeed /path/to/ebus_dump.bin'",
nullptr, nullptr,
&opt
}; };
int arg_index = -1; switch (ebusd::argParse(&parseOpt, argc, argv, &opt)) {
switch (ebusd::argParse(&parseOpt, argc, argv, &arg_index)) {
case 0: // OK case 0: // OK
break; break;
case '?': // help printed case '?': // help printed
@@ -133,20 +135,8 @@ int main(int argc, char* argv[]) {
default: default:
return EINVAL; return EINVAL;
} }
if (arg_index >= 0) {
if (argv[arg_index][0] == 0 || strcmp("/", argv[arg_index]) == 0) {
argParseError(parseOpt, "invalid dumpfile");
return EINVAL;
}
if (arg_index != argc -1) {
// more than one arg
argParseError(parseOpt, "multiple dumpfile");
return EINVAL;
}
opt.dumpFile = argv[arg_index];
}
Device* device = Device::create(opt.device, false, false, false); Device* device = Device::create(opt.device, 0, false);
if (device == nullptr) { if (device == nullptr) {
cout << "unable to create device " << opt.device << endl; cout << "unable to create device " << opt.device << endl;
return EINVAL; return EINVAL;
+13 -11
View File
@@ -65,6 +65,7 @@ static const ebusd::argDef argDefs[] = {
{nullptr, 0, nullptr, 0, "Tool options:"}, {nullptr, 0, nullptr, 0, "Tool options:"},
{"verbose", 'v', nullptr, 0, "enable verbose output"}, {"verbose", 'v', nullptr, 0, "enable verbose output"},
{"slow", 's', nullptr, 0, "low speed mode for transfer (115kBd instead of 921kBd)"}, {"slow", 's', nullptr, 0, "low speed mode for transfer (115kBd instead of 921kBd)"},
{nullptr, 0x100, "PORT", ebusd::af_optional, "port to connect to"},
{nullptr, 0, nullptr, 0, nullptr}, {nullptr, 0, nullptr, 0, nullptr},
}; };
@@ -88,6 +89,7 @@ static bool setVariantForced = false;
static char* flashFile = nullptr; static char* flashFile = nullptr;
static bool reset = false; static bool reset = false;
static bool lowSpeed = false; static bool lowSpeed = false;
static const char* portArg = nullptr;
bool parseByte(const char *arg, uint8_t minValue, uint8_t maxValue, uint8_t *result) { bool parseByte(const char *arg, uint8_t minValue, uint8_t maxValue, uint8_t *result) {
char* strEnd = nullptr; char* strEnd = nullptr;
@@ -125,7 +127,7 @@ bool parseShort(const char *arg, uint16_t minValue, uint16_t maxValue, uint16_t
* @param arg the option argument, or nullptr. * @param arg the option argument, or nullptr.
* @param parseOpt the parse options. * @param parseOpt the parse options.
*/ */
static int parse_opt(int key, char *arg, const ebusd::argParseOpt *parseOpt) { static int parse_opt(int key, char *arg, const ebusd::argParseOpt *parseOpt, void *userArg) {
char *ip = nullptr, *part = nullptr; char *ip = nullptr, *part = nullptr;
int pos = 0, sum = 0; int pos = 0, sum = 0;
struct stat st; struct stat st;
@@ -327,6 +329,9 @@ static int parse_opt(int key, char *arg, const ebusd::argParseOpt *parseOpt) {
case 's': // --slow case 's': // --slow
lowSpeed = true; lowSpeed = true;
break; break;
case 0x100: // PORT
portArg = arg;
break;
default: default:
return ESRCH; return ESRCH;
} }
@@ -1161,10 +1166,8 @@ int run(int fd);
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
ebusd::argParseOpt parseOpt = { ebusd::argParseOpt parseOpt = {
argDefs, argDefs,
parse_opt, reinterpret_cast<ebusd::parse_function_t>(parse_opt),
ebusd::af_noVersion, ebusd::af_noVersion,
"ebuspicloader",
"[PORT]",
"A tool for loading firmware to the eBUS adapter PIC and configure adjustable settings.", "A tool for loading firmware to the eBUS adapter PIC and configure adjustable settings.",
"PORT is either the serial port to use (e.g. " "PORT is either the serial port to use (e.g. "
#ifdef __CYGWIN__ #ifdef __CYGWIN__
@@ -1174,11 +1177,9 @@ int main(int argc, char* argv[]) {
#endif #endif
") that also supports a trailing wildcard '*' for testing" ") that also supports a trailing wildcard '*' for testing"
" multiple ports, or a network port as \"ip:port\" for use with e.g. socat or ebusd-esp in PIC pass-through mode.", " multiple ports, or a network port as \"ip:port\" for use with e.g. socat or ebusd-esp in PIC pass-through mode.",
nullptr,
nullptr nullptr
}; };
int arg_index = -1; switch (ebusd::argParse(&parseOpt, argc, argv, nullptr)) {
switch (ebusd::argParse(&parseOpt, argc, argv, &arg_index)) {
case 0: // OK case 0: // OK
break; break;
case '?': // help printed case '?': // help printed
@@ -1187,20 +1188,21 @@ int main(int argc, char* argv[]) {
return EINVAL; return EINVAL;
} }
bool forceHelp = false;
if (setIp != setMask || (setMacFromIp && !setIp)) { if (setIp != setMask || (setMacFromIp && !setIp)) {
std::cerr << "incomplete IP arguments" << std::endl; std::cerr << "incomplete IP arguments" << std::endl;
arg_index = argc; // force help output forceHelp = true;
} }
if (arg_index < 0 || argc-arg_index < 1) { if (forceHelp || !portArg) {
if (flashFile) { if (flashFile) {
printFileChecksum(); printFileChecksum();
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} else { } else {
ebusd::argHelp(&parseOpt); ebusd::argHelp(argv[0], &parseOpt);
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
std::string port = argv[arg_index]; std::string port = portArg;
std::string::size_type pos = port.find('*'); std::string::size_type pos = port.find('*');
if (pos == std::string::npos || pos != port.length()-1) { if (pos == std::string::npos || pos != port.length()-1) {
int fd; int fd;