2018-10-11 16:48:46 +08:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Copyright (C) 2017 Merlok
|
|
|
|
//
|
|
|
|
// This code is licensed to you under the terms of the GNU GPL, version 2 or,
|
|
|
|
// at your option, any later version. See the LICENSE.txt file for the text of
|
|
|
|
// the license.
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// Command line parser core commands
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
#include "cliparser.h"
|
|
|
|
#include <string.h>
|
2020-06-01 23:30:33 +08:00
|
|
|
#include <stdlib.h>
|
2020-10-21 14:42:33 +08:00
|
|
|
#include <util.h> // Get color constants
|
|
|
|
#include <ui.h> // get PrintAndLogEx
|
|
|
|
#include <ctype.h> // tolower
|
|
|
|
#include <inttypes.h> // PRIu64
|
2020-09-29 18:21:34 +08:00
|
|
|
|
2020-06-10 17:33:15 +08:00
|
|
|
#ifndef ARRAYLEN
|
|
|
|
# define ARRAYLEN(x) (sizeof(x)/sizeof((x)[0]))
|
|
|
|
#endif
|
2018-10-11 16:48:46 +08:00
|
|
|
|
2020-09-29 18:21:34 +08:00
|
|
|
// Custom Colors
|
|
|
|
// To default the color return s
|
|
|
|
#define _SectionTagColor_(s) _GREEN_(s)
|
|
|
|
#define _ExampleColor_(s) _YELLOW_(s)
|
|
|
|
#define _CommandColor_(s) _RED_(s)
|
|
|
|
#define _DescriptionColor_(s) _CYAN_(s)
|
2020-09-30 05:28:05 +08:00
|
|
|
#define _ArgColor_(s) s
|
|
|
|
#define _ArgHelpColor_(s) s
|
2020-09-29 18:21:34 +08:00
|
|
|
// End Custom Colors
|
|
|
|
// Option width set to 30 to allow option descriptions to align. approx line 74
|
|
|
|
// Example width set to 50 to allow help descriptions to align. approx line 93
|
|
|
|
|
2020-06-01 23:30:33 +08:00
|
|
|
int CLIParserInit(CLIParserContext **ctx, const char *vprogramName, const char *vprogramHint, const char *vprogramHelp) {
|
|
|
|
*ctx = malloc(sizeof(CLIParserContext));
|
|
|
|
if (!*ctx) {
|
2020-11-13 23:05:02 +08:00
|
|
|
PrintAndLogEx(ERR, "ERROR: Insufficient memory\n");
|
2020-06-01 23:30:33 +08:00
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
(*ctx)->argtable = NULL;
|
|
|
|
(*ctx)->argtableLen = 0;
|
|
|
|
(*ctx)->programName = vprogramName;
|
|
|
|
(*ctx)->programHint = vprogramHint;
|
|
|
|
(*ctx)->programHelp = vprogramHelp;
|
|
|
|
memset((*ctx)->buf, 0x00, sizeof((*ctx)->buf));
|
2019-03-10 06:35:06 +08:00
|
|
|
return 0;
|
2018-10-11 16:48:46 +08:00
|
|
|
}
|
|
|
|
|
2020-06-01 23:30:33 +08:00
|
|
|
int CLIParserParseArg(CLIParserContext *ctx, int argc, char **argv, void *vargtable[], size_t vargtableLen, bool allowEmptyExec) {
|
2019-03-10 06:35:06 +08:00
|
|
|
int nerrors;
|
|
|
|
|
2020-06-01 23:30:33 +08:00
|
|
|
ctx->argtable = vargtable;
|
|
|
|
ctx->argtableLen = vargtableLen;
|
2019-03-10 06:35:06 +08:00
|
|
|
|
|
|
|
/* verify the argtable[] entries were allocated sucessfully */
|
2020-06-01 23:30:33 +08:00
|
|
|
if (arg_nullcheck(ctx->argtable) != 0) {
|
2019-03-10 06:35:06 +08:00
|
|
|
/* NULL entries were detected, some allocations must have failed */
|
2020-09-30 20:27:19 +08:00
|
|
|
PrintAndLogEx(ERR, "ERROR: Insufficient memory\n");
|
2020-04-12 02:41:05 +08:00
|
|
|
fflush(stdout);
|
2019-03-10 06:35:06 +08:00
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
/* Parse the command line as defined by argtable[] */
|
2020-06-01 23:30:33 +08:00
|
|
|
nerrors = arg_parse(argc, argv, ctx->argtable);
|
2019-03-10 06:35:06 +08:00
|
|
|
|
|
|
|
/* special case: '--help' takes precedence over error reporting */
|
2020-06-01 23:30:33 +08:00
|
|
|
if ((argc < 2 && !allowEmptyExec) || ((struct arg_lit *)(ctx->argtable)[0])->count > 0) { // help must be the first record
|
|
|
|
if (ctx->programHint)
|
2020-09-30 20:27:19 +08:00
|
|
|
PrintAndLogEx(NORMAL, "\n"_DescriptionColor_("%s"), ctx->programHint);
|
2020-09-29 18:21:34 +08:00
|
|
|
|
2020-09-30 20:27:19 +08:00
|
|
|
PrintAndLogEx(NORMAL, "\n"_SectionTagColor_("usage:"));
|
|
|
|
PrintAndLogEx(NORMAL, " "_CommandColor_("%s")NOLF, ctx->programName);
|
2020-10-03 17:48:52 +08:00
|
|
|
arg_print_syntax(stdout, ctx->argtable, "\n\n");
|
2020-10-07 00:00:00 +08:00
|
|
|
|
2020-09-30 20:27:19 +08:00
|
|
|
PrintAndLogEx(NORMAL, _SectionTagColor_("options:"));
|
2020-09-29 18:21:34 +08:00
|
|
|
|
|
|
|
arg_print_glossary(stdout, ctx->argtable, " "_ArgColor_("%-30s")" "_ArgHelpColor_("%s")"\n");
|
|
|
|
|
2020-09-30 20:27:19 +08:00
|
|
|
PrintAndLogEx(NORMAL, "");
|
2020-09-29 18:21:34 +08:00
|
|
|
if (ctx->programHelp) {
|
2020-09-30 20:27:19 +08:00
|
|
|
PrintAndLogEx(NORMAL, _SectionTagColor_("examples/notes:"));
|
2020-09-29 18:21:34 +08:00
|
|
|
char *buf = NULL;
|
|
|
|
int idx = 0;
|
2020-09-30 20:27:19 +08:00
|
|
|
buf = realloc(buf, strlen(ctx->programHelp) + 1); // more then enough as we are splitting
|
2020-09-29 18:21:34 +08:00
|
|
|
|
|
|
|
char *p2; // pointer to split example from comment.
|
2020-09-30 14:39:08 +08:00
|
|
|
int egWidth = 30;
|
2020-09-30 20:27:19 +08:00
|
|
|
for (int i = 0; i <= strlen(ctx->programHelp); i++) { // <= so to get string terminator.
|
2020-09-29 18:21:34 +08:00
|
|
|
buf[idx++] = ctx->programHelp[i];
|
|
|
|
if ((ctx->programHelp[i] == '\n') || (ctx->programHelp[i] == 0x00)) {
|
2020-09-30 20:27:19 +08:00
|
|
|
buf[idx - 1] = 0x00;
|
|
|
|
p2 = strstr(buf, "->"); // See if the example has a comment.
|
2020-09-29 18:21:34 +08:00
|
|
|
if (p2 != NULL) {
|
2020-09-30 20:27:19 +08:00
|
|
|
*(p2 - 1) = 0x00;
|
2020-09-30 14:39:08 +08:00
|
|
|
|
|
|
|
if (strlen(buf) > 28)
|
|
|
|
egWidth = strlen(buf) + 5;
|
|
|
|
else
|
|
|
|
egWidth = 30;
|
|
|
|
|
2020-09-30 20:27:19 +08:00
|
|
|
PrintAndLogEx(NORMAL, " "_ExampleColor_("%-*s")" %s", egWidth, buf, p2);
|
2020-09-29 18:21:34 +08:00
|
|
|
} else {
|
2020-09-30 20:27:19 +08:00
|
|
|
PrintAndLogEx(NORMAL, " "_ExampleColor_("%-*s"), egWidth, buf);
|
2020-09-29 18:21:34 +08:00
|
|
|
}
|
|
|
|
idx = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-30 20:27:19 +08:00
|
|
|
PrintAndLogEx(NORMAL, "");
|
|
|
|
free(buf);
|
2020-09-29 18:21:34 +08:00
|
|
|
}
|
2019-03-10 06:35:06 +08:00
|
|
|
|
2020-04-12 02:41:05 +08:00
|
|
|
fflush(stdout);
|
2019-03-10 06:35:06 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If the parser returned any errors then display them and exit */
|
|
|
|
if (nerrors > 0) {
|
|
|
|
/* Display the error details contained in the arg_end struct.*/
|
2020-06-01 23:30:33 +08:00
|
|
|
arg_print_errors(stdout, ((struct arg_end *)(ctx->argtable)[vargtableLen - 1]), ctx->programName);
|
2020-09-30 20:27:19 +08:00
|
|
|
PrintAndLogEx(WARNING, "Try '%s --help' for more information.\n", ctx->programName);
|
2020-04-12 02:41:05 +08:00
|
|
|
fflush(stdout);
|
2019-03-10 06:35:06 +08:00
|
|
|
return 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
2018-10-11 16:48:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
enum ParserState {
|
2019-03-10 06:35:06 +08:00
|
|
|
PS_FIRST,
|
|
|
|
PS_ARGUMENT,
|
|
|
|
PS_OPTION,
|
2018-10-11 16:48:46 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
#define isSpace(c)(c == ' ' || c == '\t')
|
|
|
|
|
2020-06-01 23:30:33 +08:00
|
|
|
int CLIParserParseString(CLIParserContext *ctx, const char *str, void *vargtable[], size_t vargtableLen, bool allowEmptyExec) {
|
|
|
|
return CLIParserParseStringEx(ctx, str, vargtable, vargtableLen, allowEmptyExec, false);
|
2018-10-11 16:48:46 +08:00
|
|
|
}
|
|
|
|
|
2020-06-01 23:30:33 +08:00
|
|
|
int CLIParserParseStringEx(CLIParserContext *ctx, const char *str, void *vargtable[], size_t vargtableLen, bool allowEmptyExec, bool clueData) {
|
2019-03-10 06:35:06 +08:00
|
|
|
int argc = 0;
|
|
|
|
char *argv[200] = {NULL};
|
|
|
|
|
|
|
|
int len = strlen(str);
|
2020-06-10 17:33:15 +08:00
|
|
|
memset(ctx->buf, 0x00, ARRAYLEN(ctx->buf));
|
|
|
|
char *bufptr = ctx->buf;
|
2019-03-10 06:35:06 +08:00
|
|
|
char *spaceptr = NULL;
|
|
|
|
enum ParserState state = PS_FIRST;
|
|
|
|
|
|
|
|
argv[argc++] = bufptr;
|
|
|
|
// param0 = program name
|
2020-06-10 17:33:15 +08:00
|
|
|
memcpy(ctx->buf, ctx->programName, strlen(ctx->programName) + 1); // with 0x00
|
2020-06-01 23:30:33 +08:00
|
|
|
bufptr += strlen(ctx->programName) + 1;
|
2019-03-10 06:35:06 +08:00
|
|
|
if (len)
|
|
|
|
argv[argc++] = bufptr;
|
|
|
|
|
|
|
|
// parse params
|
|
|
|
for (int i = 0; i < len; i++) {
|
2019-03-10 07:00:59 +08:00
|
|
|
switch (state) {
|
2019-03-10 06:35:06 +08:00
|
|
|
case PS_FIRST: // first char
|
2019-03-10 07:00:59 +08:00
|
|
|
if (!clueData || str[i] == '-') { // first char before space is '-' - next element - option OR not "clueData" for not-option fields
|
2019-03-10 06:35:06 +08:00
|
|
|
state = PS_OPTION;
|
|
|
|
|
|
|
|
if (spaceptr) {
|
|
|
|
bufptr = spaceptr;
|
|
|
|
*bufptr = 0x00;
|
|
|
|
bufptr++;
|
|
|
|
argv[argc++] = bufptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
spaceptr = NULL;
|
|
|
|
case PS_ARGUMENT:
|
|
|
|
if (state == PS_FIRST)
|
|
|
|
state = PS_ARGUMENT;
|
|
|
|
if (isSpace(str[i])) {
|
|
|
|
spaceptr = bufptr;
|
|
|
|
state = PS_FIRST;
|
|
|
|
}
|
|
|
|
*bufptr = str[i];
|
|
|
|
bufptr++;
|
|
|
|
break;
|
|
|
|
case PS_OPTION:
|
2019-03-10 07:00:59 +08:00
|
|
|
if (isSpace(str[i])) {
|
2019-03-10 06:35:06 +08:00
|
|
|
state = PS_FIRST;
|
|
|
|
|
|
|
|
*bufptr = 0x00;
|
|
|
|
bufptr++;
|
|
|
|
argv[argc++] = bufptr;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
*bufptr = str[i];
|
|
|
|
bufptr++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-01 23:30:33 +08:00
|
|
|
return CLIParserParseArg(ctx, argc, argv, vargtable, vargtableLen, allowEmptyExec);
|
2018-10-11 16:48:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// convertors
|
2019-03-10 18:20:22 +08:00
|
|
|
int CLIParamHexToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen) {
|
2019-03-10 06:35:06 +08:00
|
|
|
*datalen = 0;
|
|
|
|
|
2020-10-17 22:16:07 +08:00
|
|
|
int tmplen = 0;
|
|
|
|
uint8_t tmpstr[(256 * 2) + 1] = {0};
|
2020-10-20 07:00:23 +08:00
|
|
|
|
2020-10-17 22:16:07 +08:00
|
|
|
// concat all strings in argstr into tmpstr[]
|
2020-10-20 07:00:23 +08:00
|
|
|
//
|
2020-10-17 22:16:07 +08:00
|
|
|
int res = CLIParamStrToBuf(argstr, tmpstr, sizeof(tmpstr), &tmplen);
|
2020-04-13 03:23:52 +08:00
|
|
|
if (res) {
|
2019-03-10 06:35:06 +08:00
|
|
|
return res;
|
2020-04-12 02:41:05 +08:00
|
|
|
}
|
2020-10-17 22:16:07 +08:00
|
|
|
if (tmplen == 0) {
|
2020-04-13 03:23:52 +08:00
|
|
|
return res;
|
|
|
|
}
|
2020-04-16 15:01:14 +08:00
|
|
|
|
2020-10-20 07:00:23 +08:00
|
|
|
res = param_gethex_to_eol((char *)tmpstr, 0, data, maxdatalen, datalen);
|
2020-10-17 22:16:07 +08:00
|
|
|
switch (res) {
|
2019-03-10 07:00:59 +08:00
|
|
|
case 1:
|
2020-11-13 23:05:02 +08:00
|
|
|
PrintAndLogEx(ERR, "Parameter error: Invalid HEX value\n");
|
2020-10-17 22:16:07 +08:00
|
|
|
break;
|
2019-03-10 07:00:59 +08:00
|
|
|
case 2:
|
2020-11-13 23:05:02 +08:00
|
|
|
PrintAndLogEx(ERR, "Parameter error: parameter too large\n");
|
2020-10-17 22:16:07 +08:00
|
|
|
break;
|
2019-03-10 07:00:59 +08:00
|
|
|
case 3:
|
2020-11-13 23:05:02 +08:00
|
|
|
PrintAndLogEx(ERR, "Parameter error: Hex string must have EVEN number of digits\n");
|
2020-10-17 22:16:07 +08:00
|
|
|
break;
|
2019-03-10 06:35:06 +08:00
|
|
|
}
|
2020-10-17 22:16:07 +08:00
|
|
|
return res;
|
2018-10-24 23:18:05 +08:00
|
|
|
}
|
|
|
|
|
2019-03-10 18:20:22 +08:00
|
|
|
int CLIParamStrToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen) {
|
2019-03-10 06:35:06 +08:00
|
|
|
*datalen = 0;
|
|
|
|
if (!argstr->count)
|
|
|
|
return 0;
|
2019-03-09 15:59:13 +08:00
|
|
|
|
2020-10-17 22:16:07 +08:00
|
|
|
uint8_t tmpstr[(256 * 2) + 1] = {0};
|
2019-03-10 06:35:06 +08:00
|
|
|
int ibuf = 0;
|
2019-03-09 15:59:13 +08:00
|
|
|
|
2019-03-10 06:35:06 +08:00
|
|
|
for (int i = 0; i < argstr->count; i++) {
|
2020-10-20 07:00:23 +08:00
|
|
|
|
2019-03-10 06:35:06 +08:00
|
|
|
int len = strlen(argstr->sval[i]);
|
2020-10-20 07:00:23 +08:00
|
|
|
|
|
|
|
if (len > ((sizeof(tmpstr) / 2) - ibuf)) {
|
2020-11-13 23:05:02 +08:00
|
|
|
PrintAndLogEx(ERR, "Parameter error: string too long (%i chars), expect MAX %zu chars\n", len + ibuf, (sizeof(tmpstr) / 2));
|
2020-10-17 22:16:07 +08:00
|
|
|
return 2;
|
|
|
|
}
|
2020-10-20 07:00:23 +08:00
|
|
|
|
2020-10-17 22:16:07 +08:00
|
|
|
memcpy(&tmpstr[ibuf], argstr->sval[i], len);
|
|
|
|
|
2019-03-10 06:35:06 +08:00
|
|
|
ibuf += len;
|
|
|
|
}
|
2020-10-20 07:00:23 +08:00
|
|
|
|
2020-10-17 22:16:07 +08:00
|
|
|
ibuf = MIN(ibuf, (sizeof(tmpstr) / 2));
|
|
|
|
tmpstr[ibuf] = 0;
|
2019-03-09 15:59:13 +08:00
|
|
|
|
2020-10-17 22:16:07 +08:00
|
|
|
if (ibuf == 0)
|
2019-03-10 06:35:06 +08:00
|
|
|
return 0;
|
2018-10-24 23:18:05 +08:00
|
|
|
|
2020-10-17 22:16:07 +08:00
|
|
|
if (ibuf > maxdatalen) {
|
2020-11-13 23:05:02 +08:00
|
|
|
PrintAndLogEx(ERR, "Parameter error: string too long (%i chars), expected MAX %i chars\n", ibuf, maxdatalen);
|
2019-03-10 06:35:06 +08:00
|
|
|
return 2;
|
2020-04-13 03:23:52 +08:00
|
|
|
}
|
2019-03-09 15:59:13 +08:00
|
|
|
|
2020-10-17 22:16:07 +08:00
|
|
|
memcpy(data, tmpstr, ibuf + 1);
|
2019-03-10 06:35:06 +08:00
|
|
|
*datalen = ibuf;
|
|
|
|
return 0;
|
2018-10-11 16:48:46 +08:00
|
|
|
}
|
|
|
|
|
2020-10-21 14:42:33 +08:00
|
|
|
uint64_t arg_get_u64_hexstr_def(CLIParserContext *ctx, uint8_t paramnum, uint64_t def) {
|
|
|
|
uint64_t rv = 0;
|
|
|
|
uint8_t data[8];
|
|
|
|
int datalen = 0;
|
|
|
|
int res = CLIParamHexToBuf(arg_get_str(ctx, paramnum), data, sizeof(data), &datalen);
|
2020-10-21 19:25:29 +08:00
|
|
|
if (res == 0 && datalen > 0) {
|
2020-10-21 14:42:33 +08:00
|
|
|
for (uint8_t i = 0; i < datalen; i++) {
|
|
|
|
rv <<= 8;
|
|
|
|
rv |= data[i];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
rv = def;
|
2020-11-02 08:46:47 +08:00
|
|
|
}
|
2020-10-21 14:42:33 +08:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2018-10-11 16:48:46 +08:00
|
|
|
|