aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/example.c42
-rw-r--r--src/simple-opt.h126
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 <string.h>
#include <stdio.h>
#include <ctype.h>
+#include <errno.h>
/* 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");