Rework theremin script
[RRG-proxmark3.git] / client / deps / cliparser / cliparser.c
blob0ea0bd33b203cf183455f3c6a137b50bfd46f15d
1 //-----------------------------------------------------------------------------
2 // Copyright (C) 2017 Merlok
3 //
4 // This code is licensed to you under the terms of the GNU GPL, version 2 or,
5 // at your option, any later version. See the LICENSE.txt file for the text of
6 // the license.
7 //-----------------------------------------------------------------------------
8 // Command line parser core commands
9 //-----------------------------------------------------------------------------
11 #include "cliparser.h"
12 #include <string.h>
13 #include <stdlib.h>
14 #include <util.h> // color constants
15 #include <ui.h> // PrintAndLogEx
16 #include <ctype.h> // tolower
17 #include <inttypes.h> // PRIu64
19 #ifndef ARRAYLEN
20 # define ARRAYLEN(x) (sizeof(x)/sizeof((x)[0]))
21 #endif
23 // Custom Colors
24 // To default the color return s
25 #define _SectionTagColor_(s) _GREEN_(s)
26 #define _ExampleColor_(s) _YELLOW_(s)
27 #define _CommandColor_(s) _RED_(s)
28 #define _DescriptionColor_(s) _CYAN_(s)
29 #define _ArgColor_(s) s
30 #define _ArgHelpColor_(s) s
31 // End Custom Colors
32 // Option width set to 30 to allow option descriptions to align. approx line 74
33 // Example width set to 50 to allow help descriptions to align. approx line 93
35 int CLIParserInit(CLIParserContext **ctx, const char *vprogramName, const char *vprogramHint, const char *vprogramHelp) {
36 *ctx = calloc(sizeof(CLIParserContext), sizeof(uint8_t));
37 if (*ctx == NULL) {
38 PrintAndLogEx(ERR, "ERROR: Insufficient memory\n");
39 return 2;
42 (*ctx)->argtable = NULL;
43 (*ctx)->argtableLen = 0;
44 (*ctx)->programName = vprogramName;
45 (*ctx)->programHint = vprogramHint;
46 (*ctx)->programHelp = vprogramHelp;
47 memset((*ctx)->buf, 0x00, sizeof((*ctx)->buf));
49 return PM3_SUCCESS;
52 void CLIParserPrintHelp(CLIParserContext *ctx) {
53 if (ctx->programHint) {
54 PrintAndLogEx(NORMAL, "\n"_DescriptionColor_("%s"), ctx->programHint);
57 PrintAndLogEx(NORMAL, "\n"_SectionTagColor_("usage:"));
58 PrintAndLogEx(NORMAL, " "_CommandColor_("%s")NOLF, ctx->programName);
59 arg_print_syntax(stdout, ctx->argtable, "\n\n");
61 PrintAndLogEx(NORMAL, _SectionTagColor_("options:"));
62 arg_print_glossary(stdout, ctx->argtable, " "_ArgColor_("%-30s")" "_ArgHelpColor_("%s")"\n");
63 PrintAndLogEx(NORMAL, "");
65 if (ctx->programHelp) {
67 // allocate more then enough memory as we are splitting
68 char *s = calloc(strlen(ctx->programHelp) + 1, sizeof(uint8_t));
69 if (s == NULL) {
70 PrintAndLogEx(FAILED, "cannot allocate memory");
71 return;
74 PrintAndLogEx(NORMAL, _SectionTagColor_("examples/notes:"));
76 // pointer to split example from comment.
77 char *p2;
79 int idx = 0;
80 int egWidth = 30;
81 for (int i = 0; i <= strlen(ctx->programHelp); i++) { // <= so to get string terminator.
83 s[idx++] = ctx->programHelp[i];
85 if ((ctx->programHelp[i] == '\n') || (ctx->programHelp[i] == 0x00)) {
87 s[idx - 1] = 0x00;
88 p2 = strstr(s, "->"); // See if the example has a comment.
90 if (p2 != NULL) {
91 *(p2 - 1) = 0x00;
93 if (strlen(s) > 28)
94 egWidth = strlen(s) + 5;
95 else
96 egWidth = 30;
98 PrintAndLogEx(NORMAL, " "_ExampleColor_("%-*s")" %s", egWidth, s, p2);
99 } else {
100 PrintAndLogEx(NORMAL, " "_ExampleColor_("%-*s"), egWidth, s);
103 idx = 0;
106 free(s);
107 PrintAndLogEx(NORMAL, "");
109 fflush(stdout);
112 int CLIParserParseArg(CLIParserContext *ctx, int argc, char **argv, void *vargtable[], size_t vargtableLen, bool allowEmptyExec) {
113 int nerrors;
115 ctx->argtable = vargtable;
116 ctx->argtableLen = vargtableLen;
118 /* verify the argtable[] entries were allocated sucessfully */
119 if (arg_nullcheck(ctx->argtable) != 0) {
120 /* NULL entries were detected, some allocations must have failed */
121 PrintAndLogEx(ERR, "ERROR: Insufficient memory\n");
122 fflush(stdout);
123 return 2;
125 /* Parse the command line as defined by argtable[] */
126 nerrors = arg_parse(argc, argv, ctx->argtable);
128 /* special case: '--help' takes precedence over error reporting */
129 if ((argc < 2 && !allowEmptyExec) || ((struct arg_lit *)(ctx->argtable)[0])->count > 0) { // help must be the first record
130 CLIParserPrintHelp(ctx);
131 return 1;
134 /* If the parser returned any errors then display them and exit */
135 if (nerrors > 0) {
136 /* Display the error details contained in the arg_end struct.*/
137 arg_print_errors(stdout, ((struct arg_end *)(ctx->argtable)[vargtableLen - 1]), ctx->programName);
138 PrintAndLogEx(WARNING, "Try " _YELLOW_("'%s --help'") " for more information.\n", ctx->programName);
139 fflush(stdout);
140 return 3;
143 return PM3_SUCCESS;
146 enum ParserState {
147 PS_FIRST,
148 PS_ARGUMENT,
149 PS_OPTION,
150 PS_QUOTE,
153 #define isSpace(c)(c == ' ' || c == '\t')
155 int CLIParserParseString(CLIParserContext *ctx, const char *str, void *vargtable[], size_t vargtableLen, bool allowEmptyExec) {
156 return CLIParserParseStringEx(ctx, str, vargtable, vargtableLen, allowEmptyExec, false);
159 int CLIParserParseStringEx(CLIParserContext *ctx, const char *str, void *vargtable[], size_t vargtableLen, bool allowEmptyExec, bool clueData) {
160 int argc = 0;
161 char *argv[MAX_INPUT_ARG_LENGTH] = {NULL};
163 int len = strlen(str);
165 memset(ctx->buf, 0x00, ARRAYLEN(ctx->buf));
167 char *bufptr = ctx->buf;
168 char *bufptrend = ctx->buf + ARRAYLEN(ctx->buf) - 1;
169 char *spaceptr = NULL;
170 enum ParserState state = PS_FIRST;
172 argv[argc++] = bufptr;
173 // param0 = program name + with 0x00
174 memcpy(ctx->buf, ctx->programName, strlen(ctx->programName) + 1);
176 bufptr += strlen(ctx->programName) + 1;
177 if (len) {
178 argv[argc++] = bufptr;
181 // parse params
182 for (int i = 0; i < len; i++) {
183 switch (state) {
184 case PS_FIRST: // first char
185 if (!clueData || str[i] == '-') { // first char before space is '-' - next element - option OR not "clueData" for not-option fields
186 state = PS_OPTION;
188 if (spaceptr) {
189 bufptr = spaceptr;
190 *bufptr = 0x00;
191 bufptr++;
192 argv[argc++] = bufptr;
195 spaceptr = NULL;
196 case PS_ARGUMENT:
197 if (state == PS_FIRST)
198 state = PS_ARGUMENT;
199 if (str[i] == '"') {
200 state = PS_QUOTE;
201 break;
203 if (isSpace(str[i])) {
204 spaceptr = bufptr;
205 state = PS_FIRST;
207 *bufptr = str[i];
208 bufptr++;
209 break;
210 case PS_OPTION:
211 if (isSpace(str[i])) {
212 state = PS_FIRST;
214 *bufptr = 0x00;
215 bufptr++;
216 argv[argc++] = bufptr;
217 break;
220 *bufptr = str[i];
221 bufptr++;
222 break;
223 case PS_QUOTE:
224 if (str[i] == '"') {
225 *bufptr++ = 0x00;
226 state = PS_FIRST;
227 } else {
228 if (isSpace(str[i]) == false) {
229 *bufptr++ = str[i];
232 break;
234 if (bufptr > bufptrend) {
235 PrintAndLogEx(ERR, "ERROR: Line too long\n");
236 fflush(stdout);
237 return 2;
241 return CLIParserParseArg(ctx, argc, argv, vargtable, vargtableLen, allowEmptyExec);
244 // convertors
245 int CLIParamHexToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen) {
246 *datalen = 0;
248 int tmplen = 0;
249 uint8_t tmpstr[MAX_INPUT_ARG_LENGTH + 1] = {0};
251 // concat all strings in argstr into tmpstr[]
252 int res = CLIParamStrToBuf(argstr, tmpstr, sizeof(tmpstr), &tmplen);
253 if (res || (tmplen == 0)) {
254 return res;
257 res = param_gethex_to_eol((char *)tmpstr, 0, data, maxdatalen, datalen);
258 switch (res) {
259 case 1:
260 PrintAndLogEx(ERR, "Parameter error: Invalid HEX value\n");
261 break;
262 case 2:
263 PrintAndLogEx(ERR, "Parameter error: parameter too large\n");
264 break;
265 case 3:
266 PrintAndLogEx(ERR, "Parameter error: Hex string must have EVEN number of digits\n");
267 break;
269 return res;
272 int CLIParamBinToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen) {
273 *datalen = 0;
275 int tmplen = 0;
276 uint8_t tmpstr[MAX_INPUT_ARG_LENGTH + 1] = {0};
278 // concat all strings in argstr into tmpstr[]
280 int res = CLIParamStrToBuf(argstr, tmpstr, sizeof(tmpstr), &tmplen);
281 if (res || tmplen == 0) {
282 return res;
285 res = param_getbin_to_eol((char *)tmpstr, 0, data, maxdatalen, datalen);
286 switch (res) {
287 case 1:
288 PrintAndLogEx(ERR, "Parameter error: Invalid BINARY value\n");
289 break;
290 case 2:
291 PrintAndLogEx(ERR, "Parameter error: parameter too large\n");
292 break;
294 return res;
297 int CLIParamStrToBuf(struct arg_str *argstr, uint8_t *data, int maxdatalen, int *datalen) {
298 *datalen = 0;
299 if (!argstr->count)
300 return 0;
302 uint8_t tmpstr[MAX_INPUT_ARG_LENGTH + 1] = {0};
303 int ibuf = 0;
305 for (int i = 0; i < argstr->count; i++) {
307 int len = strlen(argstr->sval[i]);
309 if (len > ((sizeof(tmpstr) / 2) - ibuf)) {
310 PrintAndLogEx(ERR, "Parameter error: string too long (%i chars), expect MAX %zu chars\n", len + ibuf, (sizeof(tmpstr) / 2));
311 return 2;
314 memcpy(&tmpstr[ibuf], argstr->sval[i], len);
316 ibuf += len;
319 ibuf = MIN(ibuf, (sizeof(tmpstr) / 2));
320 tmpstr[ibuf] = 0;
322 if (ibuf == 0)
323 return 0;
325 if (ibuf > maxdatalen) {
326 PrintAndLogEx(ERR, "Parameter error: string too long (%i chars), expected MAX %i chars\n", ibuf, maxdatalen);
327 return 2;
330 memcpy(data, tmpstr, ibuf + 1);
331 *datalen = ibuf;
332 return 0;
335 int CLIGetOptionList(struct arg_str *argstr, const CLIParserOption *option_array, int *value) {
336 char data[200] = {0};
337 int datalen = 200;
338 int res = CLIParamStrToBuf(argstr, (uint8_t *)data, sizeof(data), &datalen);
339 if (res)
340 return res;
342 // no data to check - we do not touch *value, just return
343 if (datalen == 0)
344 return 0;
346 str_lower(data);
348 int val = -1;
349 int cntr = 0;
350 for (int i = 0; (i < CLI_MAX_OPTLIST_LEN) && (option_array[i].text != NULL); i++) {
351 // exact match
352 if (strcmp(option_array[i].text, data) == 0) {
353 *value = option_array[i].code;
354 return 0;
356 // partial match
357 if (strncmp(option_array[i].text, data, datalen) == 0) {
358 val = option_array[i].code;
359 cntr++;
363 // check partial match
364 if (cntr == 0) {
365 PrintAndLogEx(ERR, "Parameter error: No similar option to `%s`. Valid options: %s\n", argstr->sval[0], argstr->hdr.datatype);
366 return 20;
368 if (cntr > 1) {
369 PrintAndLogEx(ERR, "Parameter error: Several options fit to `%s`. Valid options: %s\n", argstr->sval[0], argstr->hdr.datatype);
370 return 21;
373 *value = val;
374 return 0;
377 const char *CLIGetOptionListStr(const CLIParserOption *option_array, int value) {
378 static const char *errmsg = "n/a";
380 for (int i = 0; (i < CLI_MAX_OPTLIST_LEN) && (option_array[i].text != NULL); i++) {
381 if (option_array[i].code == value) {
382 return option_array[i].text;
385 return errmsg;
389 // hexstr -> u64, w optional len input and default value fallback.
390 // 0 = failed
391 // 1 = OK
392 // 3 = optional param - not set
393 uint64_t arg_get_u64_hexstr_def(CLIParserContext *ctx, uint8_t paramnum, uint64_t def) {
394 uint64_t rv = 0;
395 uint8_t d[8];
396 int dlen = 0;
397 int res = CLIParamHexToBuf(arg_get_str(ctx, paramnum), d, sizeof(d), &dlen);
398 if (res == 0 && dlen > 0) {
399 for (int i = 0; i < dlen; i++) {
400 rv <<= 8;
401 rv |= d[i];
403 } else {
404 rv = def;
406 return rv;
409 // hexstr -> u64, w optional len input and default value fallback.
410 // 0 = failed
411 // 1 = OK
412 // 2 = wrong len param, use default
413 // 3 = optional param, if fail, use default.
414 int arg_get_u64_hexstr_def_nlen(CLIParserContext *ctx, uint8_t paramnum, uint64_t def, uint64_t *out, uint8_t nlen, bool optional) {
415 int n = 0;
416 uint8_t d[nlen];
417 int res = CLIParamHexToBuf(arg_get_str(ctx, paramnum), d, sizeof(d), &n);
418 if (res == 0 && n == nlen) {
419 uint64_t rv = 0;
420 for (uint8_t i = 0; i < n; i++) {
421 rv <<= 8;
422 rv |= d[i];
424 *out = rv;
425 return 1;
426 } else if (res == 0 && n) {
427 *out = def;
428 return 2;
429 } else if (res == 0 && n == 0 && optional) {
430 *out = def;
431 return 3;
433 return 0;
436 int arg_get_u32_hexstr_def(CLIParserContext *ctx, uint8_t paramnum, uint32_t def, uint32_t *out) {
437 return arg_get_u32_hexstr_def_nlen(ctx, paramnum, def, out, 4, false);
440 int arg_get_u32_hexstr_def_nlen(CLIParserContext *ctx, uint8_t paramnum, uint32_t def, uint32_t *out, uint8_t nlen, bool optional) {
441 int n = 0;
442 uint8_t d[nlen];
443 int res = CLIParamHexToBuf(arg_get_str(ctx, paramnum), d, sizeof(d), &n);
444 if (res == 0 && n == nlen) {
445 uint32_t rv = 0;
446 for (uint8_t i = 0; i < n; i++) {
447 rv <<= 8;
448 rv |= d[i];
450 *out = rv;
451 return 1;
452 } else if (res == 0 && n) {
453 *out = def;
454 return 2;
455 } else if (res == 0 && n == 0 && optional) {
456 *out = def;
457 return 3;
459 return 0;