From 9231b162706b991fd39f452ab4e9e728c8a933b7 Mon Sep 17 00:00:00 2001 From: katherine Date: Mon, 19 Mar 2018 13:48:25 -0700 Subject: add double, char, and string_set also clean up integer parsing and usage printing --- doc/example.c | 42 +++++++++++++++---- src/simple-opt.h | 126 ++++++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 131 insertions(+), 37 deletions(-) diff --git a/doc/example.c b/doc/example.c index fee399b..08a9d7f 100644 --- a/doc/example.c +++ b/doc/example.c @@ -2,20 +2,32 @@ int main(int argc, char **argv) { + const char *set[] = { "choice_a", "choice_b", NULL }; + /* array containing all options and their types / attributes */ struct simple_opt options[] = { { SIMPLE_OPT_FLAG, 'h', "help", false, "print this help message and exit" }, + { SIMPLE_OPT_BOOL, 'b', "bool", false, + "(optionally) takes a boolean arg!" }, { SIMPLE_OPT_INT, '\0', "int", true, - "this thing needs an integer!" }, + "requires an integer. has no short_name!" }, { SIMPLE_OPT_UNSIGNED, 'u', "uns", true, "this one has a custom_arg_string. normally it would say" " \"UNSIGNED\" rather than \"NON-NEG-INT\"", "NON-NEG-INT" }, + { SIMPLE_OPT_DOUBLE, 'd', "double", true, + "a floating point number" }, { SIMPLE_OPT_STRING, 's', NULL, true, - "this one doesn't have a long opt version" }, - { SIMPLE_OPT_BOOL, 'b', "bool", false, - "(optionally) takes a boolean arg!" }, + "this one doesn't have a long_name version" }, + { SIMPLE_OPT_STRING_SET, '\0', "set-choice", true, + "a choice of one string from a NULL-terminated array." + " note that, in order to maintain redability, the description" + " indentation does not accomodate the full width of an" + " overly-wide custom_arg_string like (choice_a|choice_b)", + "(choice_a|choice_b)", set }, + { SIMPLE_OPT_CHAR, 'c', "char", false, + "(optionally) takes a character argument" }, { SIMPLE_OPT_END }, }; @@ -82,22 +94,34 @@ int main(int argc, char **argv) if (options[i].arg_is_stored) { switch (options[i].type) { + case SIMPLE_OPT_BOOL: + printf(", val: %s", options[i].val_bool ? "true" : "false"); + break; + case SIMPLE_OPT_INT: - printf(", val: %d", options[i].val_int); + printf(", val: %ld", options[i].val_int); break; case SIMPLE_OPT_UNSIGNED: - printf(", val: %u", options[i].val_unsigned); + printf(", val: %lu", options[i].val_unsigned); + break; + + case SIMPLE_OPT_DOUBLE: + printf(", val: %lf", options[i].val_double); + break; + + case SIMPLE_OPT_CHAR: + printf(", val: %c", options[i].val_char); break; case SIMPLE_OPT_STRING: printf(", val: %s", options[i].val_string); break; - case SIMPLE_OPT_BOOL: - printf(", val: %s", options[i].val_bool ? "true" : "false"); + case SIMPLE_OPT_STRING_SET: + printf(", val: %s", set[options[i].val_string_set_idx]); break; - + default: break; } diff --git a/src/simple-opt.h b/src/simple-opt.h index 2a3564d..7bbb8b8 100644 --- a/src/simple-opt.h +++ b/src/simple-opt.h @@ -6,6 +6,7 @@ #include #include #include +#include /* the maximum number of options that can be passed on the cli */ #ifndef SIMPLE_OPT_MAX_ARGC @@ -33,7 +34,10 @@ enum simple_opt_type { SIMPLE_OPT_BOOL, SIMPLE_OPT_INT, SIMPLE_OPT_UNSIGNED, + SIMPLE_OPT_DOUBLE, + SIMPLE_OPT_CHAR, SIMPLE_OPT_STRING, + SIMPLE_OPT_STRING_SET, SIMPLE_OPT_END, }; @@ -43,21 +47,28 @@ struct simple_opt { const char *long_name; bool arg_is_required; - /* optional, used for usage printing */ + /* optional, a description of the option used for usage printing */ const char *description; /* optional, a custom string describing the arg, used for usage printing */ const char *custom_arg_string; + /* for type SIMPLE_OPT_STRING_SET, a NULL-terminated array of string + * possibilities against which an option's argument is matched */ + const char **string_set; + /* values assigned upon successful option parse */ bool was_seen; bool arg_is_stored; union { bool val_bool; - int val_int; - unsigned val_unsigned; + long val_int; + unsigned long val_unsigned; + double val_double; + char val_char; char val_string[SIMPLE_OPT_OPT_ARG_MAX_WIDTH]; + int val_string_set_idx; }; }; @@ -95,7 +106,7 @@ static void simple_opt_print_usage(FILE *f, unsigned width, char *usage_name, static bool sub_simple_opt_parse(struct simple_opt *o, char *s) { int i, j; - char *str; + char *str, *cp; bool match; switch (o->type) { @@ -148,35 +159,40 @@ strmatch_out: case SIMPLE_OPT_INT: + errno = 0; + o->val_int = strtol(s, &cp, 0); - if (s[0] == '-' || s[0] == '+') { - if (strlen(s) < 2) - return false; - j = 1; - } else { - j = 0; - } + if (cp == s || *cp != '\0' || errno) + return false; - for (i = j; s[i] != '\0'; i++) { - if ( !isdigit(s[i]) ) - return false; - } + return true; - if (s[0] == '-') - o->val_int = -atoi(s+j); - else - o->val_int = atoi(s+j); + case SIMPLE_OPT_UNSIGNED: + if (s[0] == '-' || s[0] == '+') + return false; + + errno = 0; + o->val_unsigned = strtoul(s, &cp, 0); + + if (cp == s || *cp != '\0' || errno) + return false; return true; - case SIMPLE_OPT_UNSIGNED: + case SIMPLE_OPT_DOUBLE: + errno = 0; + o->val_double = strtod(s, &cp); - for (i = 0; s[i] != '\0'; i++) { - if ( !isdigit(s[i]) ) - return false; - } + if (cp == s || *cp != '\0' || errno) + return false; - o->val_int = atoi(s); + return true; + + case SIMPLE_OPT_CHAR: + if (strlen(s) != 1) + return false; + + o->val_char = s[0]; return true; case SIMPLE_OPT_STRING: @@ -186,6 +202,16 @@ strmatch_out: strcpy(o->val_string, s); return true; + case SIMPLE_OPT_STRING_SET: + for (i = 0; o->string_set[i] != NULL; i++) { + if (!strcmp(s, o->string_set[i])) { + o->val_string_set_idx = i; + return true; + } + } + + return false; + default: return false; } @@ -398,8 +424,15 @@ static int sub_simple_opt_wrap_print(FILE *f, unsigned width, int col, add_newline = true; } - if (add_newline) + if (add_newline) { fprintf(f, "\n"); + col = 0; + + if (width > 20) { + fprintf(f, " "); + col += 2; + } + } /* print out the message, trying to wrap at words */ word_start = 0; @@ -426,12 +459,20 @@ static int sub_simple_opt_wrap_print(FILE *f, unsigned width, int col, if (width != 0 && col + (word_end - word_start) + (first_word ? 0 : 1) > width && first_word == false) { fprintf(f, "\n"); - /* buffer up to line_start with spaces */ col = 0; + + /* buffer up to line_start with spaces */ while (col < line_start) { fprintf(f, " "); col++; } + + /* newline indentation, for readability */ + if (width > 20) { + fprintf(f, " "); + col += 2; + } + first_word = true; } @@ -522,9 +563,18 @@ static void simple_opt_print_usage(FILE *f, unsigned width, char *usage_name, case SIMPLE_OPT_UNSIGNED: j += 8; break; + case SIMPLE_OPT_DOUBLE: + j += 6; + break; + case SIMPLE_OPT_CHAR: + j += 4; + break; case SIMPLE_OPT_STRING: j += 6; break; + case SIMPLE_OPT_STRING_SET: + j += 10; + break; default: break; } @@ -542,6 +592,11 @@ static void simple_opt_print_usage(FILE *f, unsigned width, char *usage_name, return; } + /* if the desc_line_start is so far over it threatens readability, move it + * back a bit and just let the offending longer args be offset */ + if (desc_line_start > (width / 2 < 30 ? width / 2 : 30)) + desc_line_start = (width / 2 < 30 ? width / 2 : 30); + /* * printing @@ -631,10 +686,22 @@ static void simple_opt_print_usage(FILE *f, unsigned width, char *usage_name, sprintf(print_buffer + print_buffer_offset, "UNSIGNED"); print_buffer_offset += 8; break; + case SIMPLE_OPT_DOUBLE: + sprintf(print_buffer + print_buffer_offset, "DOUBLE"); + print_buffer_offset += 6; + break; + case SIMPLE_OPT_CHAR: + sprintf(print_buffer + print_buffer_offset, "CHAR"); + print_buffer_offset += 4; + break; case SIMPLE_OPT_STRING: sprintf(print_buffer + print_buffer_offset, "STRING"); print_buffer_offset += 6; break; + case SIMPLE_OPT_STRING_SET: + sprintf(print_buffer + print_buffer_offset, "STRING-SET"); + print_buffer_offset += 10; + break; default: break; } @@ -649,9 +716,12 @@ static void simple_opt_print_usage(FILE *f, unsigned width, char *usage_name, col = sub_simple_opt_wrap_print(f, width, col, 5, print_buffer); /* print option description */ - if (options[i].description != NULL) + if (options[i].description != NULL) { + fprintf(f, " "); + col += 2; sub_simple_opt_wrap_print(f, width, col, desc_line_start, options[i].description); + } /* end of this option */ fprintf(f, "\n"); -- cgit v1.2.3