add positionals to arg parser, avoid unnecessary parts in arg options
This commit is contained in:
@@ -83,7 +83,7 @@ static vector<string>* g_integrationVars = nullptr; //!< the integration settin
|
||||
* @param arg the option argument, or nullptr.
|
||||
* @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;
|
||||
unsigned int value;
|
||||
switch (key) {
|
||||
|
||||
+7
-7
@@ -222,8 +222,7 @@ void signalHandler(int sig) {
|
||||
* @return the exit code.
|
||||
*/
|
||||
int main(int argc, char* argv[], char* envp[]) {
|
||||
int arg_index = -1;
|
||||
switch (parse_main_args(argc, argv, envp, &s_opt, &arg_index)) {
|
||||
switch (parse_main_args(argc, argv, envp, &s_opt)) {
|
||||
case 0: // OK
|
||||
break;
|
||||
case '?': // help printed
|
||||
@@ -236,9 +235,9 @@ int main(int argc, char* argv[], char* envp[]) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if (arg_index >= 0) {
|
||||
if (s_opt.injectCount > 0) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -302,10 +301,11 @@ int main(int argc, char* argv[], char* envp[]) {
|
||||
if (s_opt.checkConfig) {
|
||||
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);
|
||||
MasterSymbolString master;
|
||||
SlaveSymbolString slave;
|
||||
int arg_index = argc - s_opt.injectCount;
|
||||
while (result == RESULT_OK && s_opt.scanConfig && arg_index < argc) {
|
||||
// check scan config for each passed ident message
|
||||
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) {
|
||||
int scanAdrCount = 0;
|
||||
bool scanAddresses[256] = {};
|
||||
while (arg_index < argc) {
|
||||
for (int arg_index = argc - s_opt.injectCount; arg_index < argc; arg_index++) {
|
||||
// add each passed message
|
||||
MasterSymbolString master;
|
||||
SlaveSymbolString slave;
|
||||
if (!s_scanHelper->parseMessage(argv[arg_index++], false, &master, &slave)) {
|
||||
if (!s_scanHelper->parseMessage(argv[arg_index], false, &master, &slave)) {
|
||||
continue;
|
||||
}
|
||||
protocol->injectMessage(master, slave);
|
||||
|
||||
+4
-2
@@ -33,6 +33,8 @@ namespace ebusd {
|
||||
*/
|
||||
|
||||
/** 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/"
|
||||
|
||||
/** A structure holding all program options. */
|
||||
@@ -62,6 +64,7 @@ typedef struct options {
|
||||
unsigned int pollInterval; //!< poll interval in seconds, 0 to disable [5]
|
||||
bool injectMessages; //!< inject remaining arguments as already seen messages
|
||||
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* 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 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 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,
|
||||
* '?' 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
|
||||
|
||||
|
||||
+17
-12
@@ -56,6 +56,7 @@ static const options_t s_default_opt = {
|
||||
.pollInterval = 5,
|
||||
.injectMessages = false,
|
||||
.stopAfterInject = false,
|
||||
.injectCount = 0,
|
||||
.caFile = nullptr,
|
||||
.caPath = nullptr,
|
||||
|
||||
@@ -133,6 +134,7 @@ static string s_configPath = CONFIG_PATH;
|
||||
#define O_DMPFIL (O_RAWSIZ-1)
|
||||
#define O_DMPSIZ (O_DMPFIL-1)
|
||||
#define O_DMPFLU (O_DMPSIZ-1)
|
||||
#define O_INJPOS 0x100
|
||||
|
||||
/** the definition of the known program arguments. */
|
||||
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]"},
|
||||
{"inject", 'i', "stop", af_optional, "Inject remaining arguments as already seen messages (e.g. "
|
||||
"\"FF08070400/0AB5454850303003277201\"), optionally stop afterwards"},
|
||||
{nullptr, O_INJPOS, "INJECT", af_optional|af_multiple, "Message(s) to inject (if --inject was given)"},
|
||||
#ifdef HAVE_SSL
|
||||
{"cafile", O_CAFILE, "FILE", 0, "Use CA FILE for checking certificates (uses defaults,"
|
||||
" \"#\" for insecure)"},
|
||||
@@ -229,8 +232,7 @@ static const argDef argDefs[] = {
|
||||
* @param arg the option argument, or nullptr.
|
||||
* @param parseOpt the parse options.
|
||||
*/
|
||||
static int parse_opt(int key, char *arg, const argParseOpt *parseOpt) {
|
||||
struct options *opt = (struct options*)parseOpt->userArg;
|
||||
static int parse_opt(int key, char *arg, const argParseOpt *parseOpt, struct options *opt) {
|
||||
result_t result = RESULT_OK;
|
||||
unsigned int value;
|
||||
|
||||
@@ -590,7 +592,14 @@ static int parse_opt(int key, char *arg, const argParseOpt *parseOpt) {
|
||||
break;
|
||||
|
||||
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
|
||||
@@ -610,18 +619,15 @@ static int parse_opt(int key, char *arg, const argParseOpt *parseOpt) {
|
||||
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;
|
||||
const argParseOpt parseOpt = {
|
||||
argDefs,
|
||||
parse_opt,
|
||||
reinterpret_cast<parse_function_t>(parse_opt),
|
||||
0,
|
||||
"" PACKAGE_NAME,
|
||||
"[INJECT...]",
|
||||
"A daemon for communication with eBUS heating systems.",
|
||||
"Report bugs to " PACKAGE_BUGREPORT " .",
|
||||
datahandler_getargs(),
|
||||
opt
|
||||
datahandler_getargs()
|
||||
};
|
||||
|
||||
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;
|
||||
if (pos[1] && strlen(*env) < sizeof(envname)-3
|
||||
&& (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;
|
||||
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, arg_index);
|
||||
int ret = argParse(&parseOpt, argc, argv, reinterpret_cast<void*>(opt));
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -140,7 +140,7 @@ void splitFields(const string& str, vector<string>* row);
|
||||
* @param arg the option argument, or nullptr.
|
||||
* @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;
|
||||
unsigned int value;
|
||||
switch (key) {
|
||||
|
||||
+87
-38
@@ -28,50 +28,50 @@ namespace ebusd {
|
||||
|
||||
#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++) {
|
||||
if (!arg->name) {
|
||||
continue;
|
||||
}
|
||||
count++;
|
||||
(*count)++;
|
||||
if (!isAlpha(arg->key)) {
|
||||
continue;
|
||||
}
|
||||
shortCharsCount++;
|
||||
shortOptsCount++;
|
||||
(*shortCharsCount)++;
|
||||
(*shortOptsCount)++;
|
||||
if (arg->valueName) {
|
||||
shortOptsCount++;
|
||||
(*shortOptsCount)++;
|
||||
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 *opt = longOpts+count;
|
||||
struct option *opt = longOpts+(*count);
|
||||
for (const argDef *arg = argDefs; arg && arg->help; arg++, argDefIdx++) {
|
||||
if (!arg->name) {
|
||||
continue;
|
||||
}
|
||||
opt->name = arg->name;
|
||||
opt->has_arg = arg->valueName ? ((arg->flags & af_optional) ? optional_argument : required_argument) : no_argument;
|
||||
opt->flag = nullptr;
|
||||
opt->flag = NULL;
|
||||
opt->val = argDefIdx;
|
||||
if (isAlpha(arg->key)) {
|
||||
shortChars[shortCharsCount] = static_cast<char>(arg->key);
|
||||
shortIndexes[shortCharsCount++] = count;
|
||||
shortOpts[shortOptsCount++] = static_cast<char>(arg->key);
|
||||
shortChars[(*shortCharsCount)] = static_cast<char>(arg->key);
|
||||
shortIndexes[(*shortCharsCount)++] = *count;
|
||||
shortOpts[(*shortOptsCount)++] = static_cast<char>(arg->key);
|
||||
if (arg->valueName) {
|
||||
shortOpts[shortOptsCount++] = ':';
|
||||
shortOpts[(*shortOptsCount)++] = ':';
|
||||
if (arg->flags & af_optional) {
|
||||
shortOpts[shortOptsCount++] = ':';
|
||||
shortOpts[(*shortOptsCount)++] = ':';
|
||||
}
|
||||
}
|
||||
}
|
||||
opt++;
|
||||
count++;
|
||||
(*count)++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,17 +87,17 @@ static const argDef versionArgDefs[] = {
|
||||
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;
|
||||
if (!(parseOpt->flags & af_noHelp)) {
|
||||
calcCounts(helpArgDefs, count, shortCharsCount, shortOptsCount);
|
||||
calcCounts(helpArgDefs, &count, &shortCharsCount, &shortOptsCount);
|
||||
}
|
||||
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++) {
|
||||
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
|
||||
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++] = ':'; // return ':' for missing option
|
||||
if (!(parseOpt->flags & af_noHelp)) {
|
||||
buildOpts(helpArgDefs, count, shortCharsCount, shortOptsCount, longOpts, shortChars,
|
||||
buildOpts(helpArgDefs, &count, &shortCharsCount, &shortOptsCount, longOpts, shortChars,
|
||||
shortIndexes, shortOpts, 0xff00);
|
||||
}
|
||||
if (!(parseOpt->flags & af_noVersion)) {
|
||||
buildOpts(versionArgDefs, count, shortCharsCount, shortOptsCount, longOpts, shortChars,
|
||||
buildOpts(versionArgDefs, &count, &shortCharsCount, &shortOptsCount, longOpts, shortChars,
|
||||
shortIndexes, shortOpts, 0xff01);
|
||||
}
|
||||
buildOpts(parseOpt->argDefs, count, shortCharsCount, shortOptsCount, longOpts, shortChars,
|
||||
buildOpts(parseOpt->argDefs, &count, &shortCharsCount, &shortOptsCount, longOpts, shortChars,
|
||||
shortIndexes, shortOpts, 0);
|
||||
int children = 0;
|
||||
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));
|
||||
}
|
||||
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;
|
||||
}
|
||||
const argDef *arg = argDefs + (val & 0xff);
|
||||
c = parser(arg->key, optarg, parseOpt);
|
||||
c = parser(arg->key, optarg, parseOpt, userArg);
|
||||
if (c != 0) {
|
||||
ret = c;
|
||||
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 == '?') {
|
||||
argHelp(parseOpt);
|
||||
} else if (argIndex && optind < argc) {
|
||||
*argIndex = optind;
|
||||
argHelp(argv[0], parseOpt);
|
||||
}
|
||||
free(longOpts);
|
||||
free(shortChars);
|
||||
@@ -270,7 +303,7 @@ size_t calcIndent(const argDef *argDefs) {
|
||||
|
||||
void printArgs(const argDef *argDefs, size_t indent) {
|
||||
for (const argDef *arg = argDefs; arg && arg->help; arg++) {
|
||||
if (!arg->name) {
|
||||
if (!arg->name && !arg->valueName) {
|
||||
if (*arg->help) {
|
||||
printf("\n %s\n", arg->help);
|
||||
} else {
|
||||
@@ -284,15 +317,21 @@ void printArgs(const argDef *argDefs, size_t indent) {
|
||||
} else {
|
||||
printf(" ");
|
||||
}
|
||||
printf(" --%s", arg->name);
|
||||
size_t taken = 2 + 3 + 3 + strlen(arg->name);
|
||||
size_t taken = 2 + 3 + 3;
|
||||
if (arg->name) {
|
||||
printf(" --%s", arg->name);
|
||||
taken += strlen(arg->name);
|
||||
} else {
|
||||
printf(" ");
|
||||
}
|
||||
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) {
|
||||
printf("[=%s]", arg->valueName);
|
||||
printf("[%s%s%s]", arg->name ? "=" : "", arg->valueName, multi ? "..." : "");
|
||||
taken += 2;
|
||||
} else {
|
||||
printf("=%s", arg->valueName);
|
||||
printf("%s%s%s", arg->name ? "=" : "", arg->valueName, multi ? "..." : "");
|
||||
}
|
||||
}
|
||||
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);
|
||||
if (indent < MAX_INDENT) {
|
||||
for (const argParseChildOpt *child = parseOpt->childOpts; child && child->argDefs; child++) {
|
||||
@@ -323,9 +362,19 @@ void argHelp(const argParseOpt *parseOpt) {
|
||||
} else if (indent < MIN_INDENT) {
|
||||
indent = MIN_INDENT;
|
||||
}
|
||||
printf("Usage: %s [OPTION...] %s\n",
|
||||
parseOpt->name,
|
||||
parseOpt->positional ? parseOpt->positional : "");
|
||||
printf("Usage: %s [OPTION...]", basename(name));
|
||||
for (const argDef *arg = parseOpt->argDefs; arg && arg->help; arg++) {
|
||||
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);
|
||||
printArgs(parseOpt->argDefs, indent);
|
||||
for (const argParseChildOpt *child = parseOpt->childOpts; child && child->argDefs; child++) {
|
||||
|
||||
+14
-14
@@ -26,15 +26,16 @@ namespace ebusd {
|
||||
/** the available arg flags. */
|
||||
enum ArgFlag {
|
||||
af_optional = 1<<0, //!< optional argument value
|
||||
af_noHelp = 1<<1, //!< do not include -?/--help option
|
||||
af_noVersion = 1<<2, //!< do not include -V/--version option
|
||||
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
|
||||
};
|
||||
|
||||
/** Definition of a single argument. */
|
||||
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
|
||||
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
|
||||
const char* help; //!< help text (mandatory)
|
||||
} argDef;
|
||||
@@ -43,12 +44,13 @@ struct argParseOpt;
|
||||
|
||||
/**
|
||||
* 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 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.
|
||||
*/
|
||||
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. */
|
||||
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)
|
||||
parse_function_t parser; //!< parse function to use
|
||||
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* suffix; //!< optional help suffix text
|
||||
const argParseChildOpt *childOpts; //!< optional child definitions
|
||||
void* userArg; //!< optional user argument
|
||||
} argParseOpt;
|
||||
|
||||
/**
|
||||
@@ -74,17 +73,18 @@ typedef struct argParseOpt {
|
||||
* @param parseOpt pointer to the @a argParseOpt structure.
|
||||
* @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 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,
|
||||
* '?' 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.
|
||||
* @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.
|
||||
|
||||
+12
-17
@@ -47,7 +47,6 @@ struct options {
|
||||
uint16_t timeout; //!< ebusd connect/send/receive timeout
|
||||
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
|
||||
};
|
||||
|
||||
@@ -58,7 +57,6 @@ static struct options opt = {
|
||||
60, // timeout
|
||||
false, // non-zero exit on error response
|
||||
|
||||
nullptr, // args
|
||||
0 // argCount
|
||||
};
|
||||
|
||||
@@ -71,6 +69,8 @@ static const argDef argDefs[] = {
|
||||
", 0 for none [60]"},
|
||||
{"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},
|
||||
};
|
||||
|
||||
@@ -80,8 +80,7 @@ static const argDef argDefs[] = {
|
||||
* @param arg the option argument, or nullptr.
|
||||
* @param parseOpt the parse options.
|
||||
*/
|
||||
static int parse_opt(int key, char *arg, const argParseOpt *parseOpt) {
|
||||
struct options *opt = (struct options*)parseOpt->userArg;
|
||||
static int parse_opt(int key, char *arg, const argParseOpt *parseOpt, struct options *opt) {
|
||||
char* strEnd = nullptr;
|
||||
unsigned int value;
|
||||
switch (key) {
|
||||
@@ -113,7 +112,11 @@ static int parse_opt(int key, char *arg, const argParseOpt *parseOpt) {
|
||||
opt->errorResponse = true;
|
||||
break;
|
||||
default:
|
||||
return ESRCH;
|
||||
if (key >= 0x100) {
|
||||
opt->argCount++;
|
||||
} else {
|
||||
return ESRCH;
|
||||
}
|
||||
}
|
||||
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[]) {
|
||||
argParseOpt parseOpt = {
|
||||
argDefs,
|
||||
parse_opt,
|
||||
reinterpret_cast<parse_function_t>(parse_opt),
|
||||
af_noVersion,
|
||||
"ebusctl",
|
||||
"[COMMAND [CMDOPT...]]",
|
||||
"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.",
|
||||
nullptr,
|
||||
&opt
|
||||
};
|
||||
int arg_index = -1;
|
||||
switch (argParse(&parseOpt, argc, argv, &arg_index)) {
|
||||
switch (argParse(&parseOpt, argc, argv, &opt)) {
|
||||
case 0: // OK
|
||||
break;
|
||||
case '?': // help printed
|
||||
@@ -363,12 +362,8 @@ int main(int argc, char* argv[]) {
|
||||
default:
|
||||
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);
|
||||
}
|
||||
|
||||
+13
-23
@@ -62,6 +62,7 @@ static struct options opt = {
|
||||
static const ebusd::argDef argDefs[] = {
|
||||
{"device", 'd', "DEV", 0, "Write to DEV (serial device) [/dev/ttyUSB60]"},
|
||||
{"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},
|
||||
};
|
||||
@@ -72,8 +73,7 @@ static const ebusd::argDef argDefs[] = {
|
||||
* @param arg the option argument, or nullptr.
|
||||
* @param parseOpt the parse options.
|
||||
*/
|
||||
static int parse_opt(int key, char *arg, const ebusd::argParseOpt *parseOpt) {
|
||||
struct options *opt = (struct options*)parseOpt->userArg;
|
||||
static int parse_opt(int key, char *arg, const ebusd::argParseOpt *parseOpt, struct options *opt) {
|
||||
char* strEnd = nullptr;
|
||||
switch (key) {
|
||||
// Device settings:
|
||||
@@ -91,6 +91,14 @@ static int parse_opt(int key, char *arg, const ebusd::argParseOpt *parseOpt) {
|
||||
return EINVAL;
|
||||
}
|
||||
break;
|
||||
case 0x100: // DUMPFILE
|
||||
if (arg[0] == 0 || strcmp("/", arg) == 0) {
|
||||
argParseError(parseOpt, "invalid dumpfile");
|
||||
return EINVAL;
|
||||
}
|
||||
opt->dumpFile = arg;
|
||||
break;
|
||||
|
||||
default:
|
||||
return ESRCH;
|
||||
}
|
||||
@@ -107,13 +115,9 @@ static int parse_opt(int key, char *arg, const ebusd::argParseOpt *parseOpt) {
|
||||
int main(int argc, char* argv[]) {
|
||||
ebusd::argParseOpt parseOpt = {
|
||||
argDefs,
|
||||
parse_opt,
|
||||
reinterpret_cast<parse_function_t>(parse_opt),
|
||||
af_noVersion,
|
||||
"ebusfeed",
|
||||
"[DUMPFILE]",
|
||||
"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"
|
||||
" 1. 'socat -d -d pty,raw,echo=0 pty,raw,echo=0'\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"
|
||||
" 4. start ebusfeed: 'ebusfeed /path/to/ebus_dump.bin'",
|
||||
nullptr,
|
||||
&opt
|
||||
};
|
||||
int arg_index = -1;
|
||||
switch (ebusd::argParse(&parseOpt, argc, argv, &arg_index)) {
|
||||
switch (ebusd::argParse(&parseOpt, argc, argv, &opt)) {
|
||||
case 0: // OK
|
||||
break;
|
||||
case '?': // help printed
|
||||
@@ -133,20 +135,8 @@ int main(int argc, char* argv[]) {
|
||||
default:
|
||||
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) {
|
||||
cout << "unable to create device " << opt.device << endl;
|
||||
return EINVAL;
|
||||
|
||||
+13
-11
@@ -65,6 +65,7 @@ static const ebusd::argDef argDefs[] = {
|
||||
{nullptr, 0, nullptr, 0, "Tool options:"},
|
||||
{"verbose", 'v', nullptr, 0, "enable verbose output"},
|
||||
{"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},
|
||||
};
|
||||
|
||||
@@ -88,6 +89,7 @@ static bool setVariantForced = false;
|
||||
static char* flashFile = nullptr;
|
||||
static bool reset = false;
|
||||
static bool lowSpeed = false;
|
||||
static const char* portArg = nullptr;
|
||||
|
||||
bool parseByte(const char *arg, uint8_t minValue, uint8_t maxValue, uint8_t *result) {
|
||||
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 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;
|
||||
int pos = 0, sum = 0;
|
||||
struct stat st;
|
||||
@@ -327,6 +329,9 @@ static int parse_opt(int key, char *arg, const ebusd::argParseOpt *parseOpt) {
|
||||
case 's': // --slow
|
||||
lowSpeed = true;
|
||||
break;
|
||||
case 0x100: // PORT
|
||||
portArg = arg;
|
||||
break;
|
||||
default:
|
||||
return ESRCH;
|
||||
}
|
||||
@@ -1161,10 +1166,8 @@ int run(int fd);
|
||||
int main(int argc, char* argv[]) {
|
||||
ebusd::argParseOpt parseOpt = {
|
||||
argDefs,
|
||||
parse_opt,
|
||||
reinterpret_cast<ebusd::parse_function_t>(parse_opt),
|
||||
ebusd::af_noVersion,
|
||||
"ebuspicloader",
|
||||
"[PORT]",
|
||||
"A tool for loading firmware to the eBUS adapter PIC and configure adjustable settings.",
|
||||
"PORT is either the serial port to use (e.g. "
|
||||
#ifdef __CYGWIN__
|
||||
@@ -1174,11 +1177,9 @@ int main(int argc, char* argv[]) {
|
||||
#endif
|
||||
") 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.",
|
||||
nullptr,
|
||||
nullptr
|
||||
};
|
||||
int arg_index = -1;
|
||||
switch (ebusd::argParse(&parseOpt, argc, argv, &arg_index)) {
|
||||
switch (ebusd::argParse(&parseOpt, argc, argv, nullptr)) {
|
||||
case 0: // OK
|
||||
break;
|
||||
case '?': // help printed
|
||||
@@ -1187,20 +1188,21 @@ int main(int argc, char* argv[]) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
bool forceHelp = false;
|
||||
if (setIp != setMask || (setMacFromIp && !setIp)) {
|
||||
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) {
|
||||
printFileChecksum();
|
||||
exit(EXIT_SUCCESS);
|
||||
} else {
|
||||
ebusd::argHelp(&parseOpt);
|
||||
ebusd::argHelp(argv[0], &parseOpt);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
std::string port = argv[arg_index];
|
||||
std::string port = portArg;
|
||||
std::string::size_type pos = port.find('*');
|
||||
if (pos == std::string::npos || pos != port.length()-1) {
|
||||
int fd;
|
||||
|
||||
Reference in New Issue
Block a user