rockbox/apps/plugins/lib/arg_helper.c
William Wilgus a8253c57dd lib/argparse fix red
Change-Id: I7bbac6de6319bf86189f079b7330d650ea95de3c
2021-08-31 17:23:47 -04:00

310 lines
8.1 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2021 William Wilgus
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "plugin.h"
#include "arg_helper.h"
#ifndef logf
#define logf(...) {}
#endif
#define SWCHAR '-'
#define DECSEPCHAR '.'
int string_parse(const char **parameter, char* buf, size_t buf_sz)
{
/* fills buf with a string upto buf_sz, null terminates the buffer
* strings break on WS by default but can be enclosed in single or double quotes
* opening and closing quotes will not be included in the buffer but will be counted
* use alternating quotes if you really want them included '"text"' or "'text'"
* failure to close the string will result in eating all remaining args till \0
* If buffer full remaining chars are discarded till stopchar or \0 is reached */
char stopchar = ' ';
char stopchars[] = "\'\"";
int skipped = 0;
int found = 0;
const char* start = *parameter;
if (strchr(stopchars, *start))
{
logf("stop char %c\n", *start);
stopchar = *start;
skipped++;
start++;
}
while (*start && *start != stopchar)
{
if (buf_sz > 1)
{
*buf++ = *start;
buf_sz--;
}
found++;
start++;
}
if (*start == stopchar && skipped)
{
start++;
skipped++;
}
*buf = '\0';
if (found > 0)
*parameter = start;
else
skipped = 0;
return found + skipped;
}
int char_parse(const char **parameter, char* character)
{
/* passes *character a single character eats remaining non-WS characters */
char buf[2];
int ret = string_parse(parameter, buf, sizeof(buf));
if (ret && character)
*character = buf[0];
return ret;
}
int bool_parse(const char **parameter, bool *choice)
{
/* determine true false using the first character the rest are skipped/ignored */
int found = 0;
const char tf_val[]="fn0ty1";/* false chars on left f/t should be balanced fffttt */
const char* start = *parameter;
char c = tolower(*start);
const char *tfval = strchr(tf_val, c);
while(isalnum(*++start)) {;}
if (tfval)
{
found = start - (*parameter);
*parameter = start;
}
if (choice)
*choice = (tfval - tf_val) > (signed int) (sizeof(tf_val) / 2) - 1;
return found;
}
int longnum_parse(const char **parameter, long *number, long *decimal)
{
/* passes number and or decimal portion of number base 10 only..
fractional portion is scaled by ARGPARSE_FRAC_DEC_MULTIPLIER
Example (if ARGPARSE_FRAC_DEC_MULTIPLIER = 10 000)
meaning .0009 returns 9 , 9 / 10000 = .0009
.009 returns 90
.099 returns 990
.09 returns 900
.9 returns 9000
.9999 returns 9999
*/
long num = 0;
long dec = 0;
int found = 0;
int neg = 0;
int digits = 0;
//logf ("n: %s\n", *parameter);
const char *start = *parameter;
if (*start == '-')
{
neg = 1;
start++;
}
while (isdigit(*start))
{
found++;
num = num *10 + *start - '0';
start++;
}
if (*start == DECSEPCHAR)
{
start++;
while(*start == '0')
{
digits++;
start++;
}
while (isdigit(*start))
{
dec = dec *10 + *start - '0';
digits++;
start++;
}
if (decimal && digits <= ARGPARSE_MAX_FRAC_DIGITS)
{
if(digits < ARGPARSE_MAX_FRAC_DIGITS)
{
digits = ARGPARSE_MAX_FRAC_DIGITS - digits;
while (digits--)
dec *= 10;
}
}
else
dec = -1; /* error */
}
if (found > 0)
{
found = start - (*parameter);
*parameter = start;
}
if(number)
*number = neg ? -num : num;
if (decimal)
*decimal = dec;
return found;
}
int num_parse(const char **parameter, int *number, int *decimal)
{
long num, dec;
int ret = longnum_parse(parameter, &num, &dec);
if(number)
*number = num;
if (decimal)
*decimal = dec;
return ret;
}
/*
*argparse(const char *parameter, int parameter_len,
* int (*arg_callback)(char argchar, const char **parameter))
* parameter : constant char string of arguments
* parameter_len : may be set to -1 if your parameter string is NULL (\0) terminated
* arg_callback : function gets called for each SWCHAR found in the parameter string
* Note: WS at beginning is stripped, **parameter starts at the first NON WS char
* return 0 for arg_callback to quit parsing immediately
*/
void argparse(const char *parameter, int parameter_len, int (*arg_callback)(char argchar, const char **parameter))
{
bool lastchr;
char argchar;
const char *start = parameter;
while (parameter_len < 0 || (parameter - start) < parameter_len)
{
switch (*parameter++)
{
case SWCHAR:
{
if ((*parameter) == '\0')
return;
logf ("%s\n",parameter);
argchar = *parameter;
lastchr = (*(parameter + 1) == '\0');
while (*++parameter || lastchr)
{
lastchr = false;
if (isspace(*parameter))
continue; /* eat spaces at beginning */
if (!arg_callback(argchar, &parameter))
return;
break;
}
break;
}
case '\0':
{
if (parameter_len <= 0)
return;
}
}
}
}
/* EXAMPLE USAGE
argparse("-n 42 -N 9.9 -n -78.9009 -f -P /rockbox/path/f -s 'Yestest' -B false -B 0 -B true -b n -by -b 1-c ops -c s -k", -1, &arg_callback);
int arg_callback(char argchar, const char **parameter)
{
int ret;
int num, dec;
char c;
char buf[32];
bool bret;
logf ("Arg: %c\n", argchar);
switch (tolower(argchar))
{
case 'k' :
logf("Option K!");
break;
case 'c' :
ret = char_parse(parameter, &c);
if (ret)
{
logf ("Val: %c\n", c);
logf("ate %d chars\n", ret);
}
break;
case 'n' :
ret = num_parse(parameter, &num, &dec);
if (ret)
{
logf ("Val: %d.%d\n", num, dec);
logf("ate %d chars\n", ret);
}
break;
case 's' :
ret = string_parse(parameter, buf, sizeof(buf));
if (ret)
{
logf ("Val: %s\n", buf);
logf("ate %d chars\n", ret);
}
break;
case 'p' :
ret = string_parse(parameter, buf, sizeof(buf));
if (ret)
{
logf ("Path: %s\n", buf);
logf("ate %d chars\n", ret);
}
break;
case 'b' :
ret = bool_parse(parameter, &bret);
if (ret)
{
logf ("Val: %s\n", bret ? "true" : "false");
logf("ate %d chars\n", ret);
}
break;
default :
logf ("Unknown switch '%c'\n",argchar);
//return 0;
}
return 1;
}
*/