[common] option: added shortopt support and pretty help print

This commit is contained in:
Geoffrey McRae 2019-05-21 11:31:31 +10:00
parent b9841351b4
commit 2d5f6d65ce
3 changed files with 173 additions and 37 deletions

View File

@ -1 +1 @@
a12-192-gbeed03eb33+1 a12-192-gb9841351b4+1

View File

@ -36,6 +36,7 @@ struct Option
char * module; char * module;
char * name; char * name;
char * description; char * description;
const char shortopt;
enum OptionType type; enum OptionType type;
union union

View File

@ -19,6 +19,7 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include "common/option.h" #include "common/option.h"
#include "common/debug.h" #include "common/debug.h"
#include "common/stringutils.h"
#include <stddef.h> #include <stddef.h>
#include <stdlib.h> #include <stdlib.h>
@ -90,6 +91,9 @@ static char * bool_toString(struct Option * opt)
static char * string_toString(struct Option * opt) static char * string_toString(struct Option * opt)
{ {
if (!opt->value.x_string)
return NULL;
return strdup(opt->value.x_string); return strdup(opt->value.x_string);
} }
@ -156,7 +160,10 @@ bool option_register(struct Option options[])
// ensure the string is locally allocated // ensure the string is locally allocated
if (o->type == OPTION_TYPE_STRING) if (o->type == OPTION_TYPE_STRING)
o->value.x_string = strdup(o->value.x_string); {
if (o->value.x_string)
o->value.x_string = strdup(o->value.x_string);
}
// add the option to the correct group for help printout // add the option to the correct group for help printout
bool found = false; bool found = false;
@ -237,50 +244,83 @@ bool option_parse(int argc, char * argv[])
{ {
for(int a = 1; a < argc; ++a) for(int a = 1; a < argc; ++a)
{ {
if (strcmp(argv[a], "-h") == 0 || strcmp(argv[a], "--help") == 0) struct Option * o = NULL;
{ char * value = NULL;
state.doHelp = true;
continue;
}
char * arg = strdup(argv[a]); // emulate getopt for backwards compatability
char * module = strtok(arg , ":"); if (argv[a][0] == '-')
char * name = strtok(NULL, "=");
char * value = strtok(NULL, "" );
if (!module || !name || !value)
{ {
DEBUG_WARN("Ignored invalid argument: %s", argv[a]); if (strcmp(argv[a], "-h") == 0 || strcmp(argv[a], "--help") == 0)
free(arg); {
continue; state.doHelp = true;
}
bool found = false;
struct Option * o;
for(int i = 0; i < state.oCount; ++i)
{
o = state.options[i];
if ((strcmp(o->module, module) != 0) || (strcmp(o->name, name) != 0))
continue; continue;
}
found = true; if (strlen(argv[a]) != 2)
break; {
DEBUG_WARN("Ignored invalid argvument: %s", argv[a]);
continue;
}
for(int i = 0; i < state.oCount; ++i)
{
if (state.options[i]->shortopt == argv[a][1])
{
o = state.options[i];
break;
}
}
if (o->type != OPTION_TYPE_BOOL && a < argc - 1)
{
++a;
value = strdup(argv[a]);
}
}
else
{
char * arg = strdup(argv[a]);
char * module = strtok(arg , ":");
char * name = strtok(NULL, "=");
value = strtok(NULL, "" );
if (!module || !name)
{
DEBUG_WARN("Ignored invalid argument: %s", argv[a]);
free(arg);
continue;
}
o = option_get(module, name);
if (value)
value = strdup(value);
free(arg);
} }
if (!found) if (!value)
{
if (o->type == OPTION_TYPE_BOOL)
{
option_set(o, "yes");
continue;
}
else if (o->type != OPTION_TYPE_CUSTOM)
{
DEBUG_WARN("Ignored invalid argument, missing value: %s", argv[a]);
continue;
}
}
if (!o)
{ {
DEBUG_WARN("Ignored unknown argument: %s", argv[a]); DEBUG_WARN("Ignored unknown argument: %s", argv[a]);
free(arg); free(value);
continue; continue;
} }
if (!option_set(o, value)) option_set(o, value);
{ free(value);
free(arg);
continue;
}
free(arg);
} }
return true; return true;
@ -510,13 +550,108 @@ void option_print()
for(int g = 0; g < state.gCount; ++g) for(int g = 0; g < state.gCount; ++g)
{ {
StringList lines = stringlist_new(true);
StringList values = stringlist_new(true);
int len;
int maxLen;
int valueLen = 5;
char * line;
// ensure the pad length is atleast as wide as the heading
if (state.groups[g].pad < 4)
state.groups[g].pad = 4;
// get the values and the max value length
for(int i = 0; i < state.groups[g].count; ++i) for(int i = 0; i < state.groups[g].count; ++i)
{ {
struct Option * o = state.groups[g].options[i]; struct Option * o = state.groups[g].options[i];
char * value = o->toString(o); char * value = o->toString(o);
printf(" %s:%-*s - %s [%s]\n", o->module, state.groups[g].pad, o->name, o->description, value); if (!value)
free(value); {
value = strdup("NULL");
len = 4;
}
else
len = strlen(value);
if (len > valueLen)
valueLen = len;
stringlist_push(values, value);
} }
// add the heading
maxLen = alloc_sprintf(
&line,
"%-*s | Short | %-*s | Description",
state.groups[g].pad + 4,
"Long",
valueLen,
"Value"
);
assert(maxLen > 0);
stringlist_push(lines, line);
for(int i = 0; i < state.groups[g].count; ++i)
{
struct Option * o = state.groups[g].options[i];
char * value = stringlist_at(values, i);
len = alloc_sprintf(
&line,
"%s:%-*s | %c%c | %-*s | %s",
o->module,
state.groups[g].pad,
o->name,
o->shortopt ? '-' : ' ',
o->shortopt ? o->shortopt : ' ',
valueLen,
value,
o->description
);
assert(len > 0);
stringlist_push(lines, line);
if (len > maxLen)
maxLen = len;
}
stringlist_free(&values);
// print out the lines
for(int i = 0; i < stringlist_count(lines); ++i)
{
if (i == 0)
{
// print a horizontal rule
printf(" |");
for(int i = 0; i < maxLen + 2; ++i)
putc('-', stdout);
printf("|\n");
}
char * line = stringlist_at(lines, i);
printf(" | %-*s |\n", maxLen, line);
if (i == 0)
{
// print a horizontal rule
printf(" |");
for(int i = 0; i < maxLen + 2; ++i)
putc('-', stdout);
printf("|\n");
}
}
// print a horizontal rule
printf(" |");
for(int i = 0; i < maxLen + 2; ++i)
putc('-', stdout);
printf("|\n");
stringlist_free(&lines);
printf("\n"); printf("\n");
} }
} }