aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md106
-rw-r--r--doc/example.c5
-rw-r--r--doc/interface.md81
-rw-r--r--src/simple-opt.h16
4 files changed, 152 insertions, 56 deletions
diff --git a/README.md b/README.md
index c651acd..71e7fdb 100644
--- a/README.md
+++ b/README.md
@@ -22,20 +22,27 @@ you can compile and test with yourself.
int main(int argc, char **argv)
{
+ const char *set[] = { "str_a", "str_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",
+ "(str_a|str_b)", set },
{ SIMPLE_OPT_END },
};
@@ -102,22 +109,35 @@ 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",
+ options[i].string_set[options[i].val_string_set_idx]);
break;
-
+
default:
break;
}
@@ -179,20 +199,41 @@ $ ./a.out --help
Usage: ./a.out [OPTION]... [--] [NON-OPTION]...
This is where you would put an overview description of the program and its
- general functionality.
-
- -h --help print this help message and exit
- --int=INT this thing needs an integer!
- -u --uns=NON-NEG-INT this one has a custom_arg_string. normally it would say
- "UNSIGNED" rather than "NON-NEG-INT"
- -s STRING this one doesn't have a long opt version
- -b --bool[=BOOL] (optionally) takes a boolean arg!
+ general functionality.
+
+ -h --help print this help message and exit
+ -b --bool[=BOOL] (optionally) takes a boolean arg!
+ --int=INT requires an integer. has no short_name!
+ -u --uns=NON-NEG-INT this one has a custom_arg_string. normally it
+ would say "UNSIGNED" rather than "NON-NEG-INT"
+ -d --double=DOUBLE a floating point number
+ -s STRING this one doesn't have a long_name version
+ --set-choice=(str_a|str_b) a choice of one string from a NULL-terminated
+ array
```
note that the output is word-wrapped. it wraps to a maximum of 80 columns
because 80 is passed as the second argument to `simple_opt_print_usage`
(allowing printing to adapt to the user's terminal width if you want to add
-support for that, via ncurses or something).
+support for that, via ncurses or something). also note that the indentation of
+the option descriptions is dependent on the width of the `long_name` column. if
+the lengthy `set-choice` line was removed, for example, the output would
+become:
+
+```
+Usage: ./a.out [OPTION]... [--] [NON-OPTION]...
+
+ This is where you would put an overview description of the program and its
+ general functionality.
+
+ -h --help print this help message and exit
+ -b --bool[=BOOL] (optionally) takes a boolean arg!
+ --int=INT requires an integer. has no short_name!
+ -u --uns=NON-NEG-INT this one has a custom_arg_string. normally it would say
+ "UNSIGNED" rather than "NON-NEG-INT"
+ -d --double=DOUBLE a floating point number
+ -s STRING this one doesn't have a long_name version
+```
finally, if parsing was successful and usage not printed, this program prints a
quick summary of which options it accepts, which it saw, and what arguments
@@ -202,51 +243,60 @@ command itself:
```
$ ./a.out
--help, seen: no
+--bool, seen: no
--int, seen: no
--uns, seen: no
+--double, seen: no
-s, seen: no
---bool, seen: no
+--set-choice, seen: no
```
```
-$ ./a.out --int=-1 -s test_string -b -- trailing args passed
+$ ./a.out --int=-1 -s test_string -b -- trailing --args -passed
--help, seen: no
+--bool, seen: yes
--int, seen: yes, val: -1
--uns, seen: no
+--double, seen: no
-s, seen: yes, val: test_string
---bool, seen: yes
+--set-choice, seen: no
-non-options: trailing args passed
+non-options: trailing --args -passed
```
```
-$ ./a.out --bool=false -u 3
+$ ./a.out --bool=false -u 3 --set-choice "str_b"
--help, seen: no
+--bool, seen: yes, val: false
--int, seen: no
--uns, seen: yes, val: 3
+--double, seen: no
-s, seen: no
---bool, seen: yes, val: false
+--set-choice, seen: yes, val: str_b
```
```
$ ./a.out no options, just arguments
--help, seen: no
+--bool, seen: no
--int, seen: no
--uns, seen: no
+--double, seen: no
-s, seen: no
---bool, seen: no
+--set-choice, seen: no
non-options: no options, just arguments
```
```
-$ ./a.out non-options --int=+1 can -b on be --uns 0 interleaved
+$ ./a.out non-options --int=+1 can -d 3.9 be --uns 0 interleaved
--help, seen: no
+--bool, seen: no
--int, seen: yes, val: 1
--uns, seen: yes, val: 0
+--double, seen: yes, val: 3.900000
-s, seen: no
---bool, seen: yes, val: true
+--set-choice, seen: no
non-options: non-options can be interleaved
```
-
diff --git a/doc/example.c b/doc/example.c
index 1f2708c..18aec1c 100644
--- a/doc/example.c
+++ b/doc/example.c
@@ -23,8 +23,6 @@ int main(int argc, char **argv)
{ SIMPLE_OPT_STRING_SET, '\0', "set-choice", true,
"a choice of one string from a NULL-terminated array",
"(str_a|str_b)", set },
- { SIMPLE_OPT_CHAR, 'c', "char", false,
- "(optionally) takes a character argument" },
{ SIMPLE_OPT_END },
};
@@ -116,7 +114,8 @@ int main(int argc, char **argv)
break;
case SIMPLE_OPT_STRING_SET:
- printf(", val: %s", set[options[i].val_string_set_idx]);
+ printf(", val: %s",
+ options[i].string_set[options[i].val_string_set_idx]);
break;
default:
diff --git a/doc/interface.md b/doc/interface.md
index 5408332..86937f8 100644
--- a/doc/interface.md
+++ b/doc/interface.md
@@ -21,16 +21,29 @@ fields which should be defined in these elements are:
/* optional, a custom string describing the arg, used for usage printing */
const char *custom_arg_string;
+
+ /* required 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;
```
if `type` is `SIMPLE_OPT_FLAG`, this option may not accept arguments. if `type`
is `SIMPLE_OPT_END`, parsing and usage printing will return at this point when
iterating through the array and will not see any elements which may follow.
+thus, in practice, the array definition should look something like this:
+
+```
+struct simple_opt options[] = {
+ { SIMPLE_OPT_<type>, <short_name>, <long_name>, <true|false> ...},
+ { SIMPLE_OPT_<type>, <short_name>, <long_name>, <true|false> ...},
+ ...
+ { SIMPLE_OPT_END }
+};
+```
-`short_name` is optional, and may be undefined for this option by passing '\0'.
-`long_name` is also optional, and may be left undefined for this option by
-passing `NULL`. however, at fewest one of these two must be defined for every
-option.
+`short_name` is optional, and it may be left undefined for this option by
+passing '\0'. `long_name` is also optional and may be left undefined by passing
+`NULL`. however, at fewest one of these two must be defined for every option.
the fields which are set by `simple_opt_parse` are:
@@ -49,7 +62,9 @@ the fields which are set by `simple_opt_parse` are:
`was_seen` indicates if this option was encountered during parsing,
`arg_is_stored` if an argument was passed to the option, and the `val_<type>`
fields contain the value passed (with the correct field to set being determined
-by the `type` field shown above).
+by the `type` field shown above) for all but `SIMPLE_OPT_STRING_SET`, for which
+`val_string_set_idx` is set, an index into the `string_set` field's array,
+indicating which possibility was matched.
options of the following types:
@@ -57,10 +72,13 @@ options of the following types:
SIMPLE_OPT_BOOL,
SIMPLE_OPT_INT,
SIMPLE_OPT_UNSIGNED,
+ SIMPLE_OPT_DOUBLE,
+ SIMPLE_OPT_CHAR,
SIMPLE_OPT_STRING,
+ SIMPLE_OPT_STRING_SET,
```
-take arguments. if the user passes a short option on the cli, that options
+take arguments. if the user passes a short option on the cli, that option's
argument is passed as the following cli argument, like so:
```
@@ -68,8 +86,7 @@ argument is passed as the following cli argument, like so:
```
if the user passes a long option on the cli, that option's argument can be
-passed either as the following cli argument or following an `=` typed at the
-end of the argument, like so:
+passed either as the next cli argument or appended to a trailing `=`, like so:
```
./a.out --opt-x <arg_goes_here>
@@ -80,15 +97,42 @@ arguments acceptable to type `SIMPLE_OPT_BOOL` are `true`, `yes`, or `on`, all
of which result in a value of true, and `false`, `no`, or `off`, which result
in a value of false.
-arguments acceptable to type `SIMPLE_OPT_INT` must be decimal integers (that is
-digit-only strings) with an optional leading sign indicator of `-` or `+`.
+arguments acceptable to type `SIMPLE_OPT_INT` must be integers with an optional
+leading sign indicator of '-' or '+'. they are assumed decimal unless given a
+prefix to indicate otherwise ('0' for octal and '0x' for hexadecimal).
+arguments with values too large (negative or positive) to be stored in a signed
+integer `long` will also be rejected.
+
+arguments acceptable to type `SIMPLE_OPT_UNSIGNED` are the same as those
+acceptable to `SIMPLE_OPT_INT`, save that they cannot have a sign indicator and
+are limited to the size of an `unsigned long`.
-arguments acceptable to type `SIMPLE_OPT_UNSIGNED` must be decimal integers
-(that is digit-only strings).
+arguments acceptable to type `SIMPLE_OPT_DOUBLE` may be any representation of a
+floating point number that can be read by the standard library `strtod`
+function and stored in a `double` type. this includes arguments like "4.9",
+"-1.2e20", "infinity", or "nan".
+
+arguments acceptable to type `SIMPLE_OPT_CHAR` may be any single-byte
+character.
arguments acceptable to type `SIMPLE_OPT_STRING` may be any string of
characters the user passes.
+arguments acceptable to type `SIMPLE_OPT_STRING_SET` may be any character
+string which also appears in the programmer-defined NULL-terminated array of
+strings in the `string_set` field. in practice, adding an argument of this type
+would look something like this:
+
+```
+const char *set[] = { "choice_a", "choice_b", ..., NULL };
+
+struct simple_opt options[] = {
+ ...
+ { SIMPLE_OPT_STRING_SET, <short_name>, <long_name>, <true|false>,
+ [description], [custom_arg_string], set },
+ ...
+};
+```
### struct simple_opt_result
@@ -144,8 +188,9 @@ limit can also be resized by defining `SIMPLE_OPT_MAX_ARGC`
finally, `SIMPLE_OPT_RESULT_MALFORMED_OPTION_STRUCT` is returned if the
programmer has passed a `struct simple_opt` array which contains disallowed
option configurations (that is, two options share a `short_name` or
-`long_name`, an option has neither a `short_name` nor a `long_name`, or an
-option of type `SIMPLE_OPT_FLAG` is marked as requiring an argument)
+`long_name`, an option has neither a `short_name` nor a `long_name`, an option
+of type `SIMPLE_OPT_FLAG` is marked as requiring an argument, or an option of
+type `SIMPLE_OPT_STRING_SET` has a NULL `string_set` field)
functions
@@ -218,8 +263,8 @@ defined above.
*note:* usage printing's word wrap operates under the assumptions that your
language delimits words with spaces (i.e. "when i was a child..." vs.
-"子供時代に..."), that the font used is fixed-width, and that every character
+"子供時代に..."), that the font used is fixed-width and that every character
occupies one column (that is, there are no wide characters, combining
-diacritics, etc) and one byte (no multi-byte utf-8 characters). if these
-assumptions do not apply to your use case, you should use an alternative method
-for usage printing.
+diacritics, etc), and that all characters are one byte (no multi-byte utf-8
+characters). if these assumptions do not apply to your use case, you should use
+an alternative method for usage printing.
diff --git a/src/simple-opt.h b/src/simple-opt.h
index e6f329d..d250e73 100644
--- a/src/simple-opt.h
+++ b/src/simple-opt.h
@@ -53,8 +53,8 @@ struct simple_opt {
/* 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 */
+ /* required 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 */
@@ -261,7 +261,9 @@ static struct simple_opt_result simple_opt_parse(int argc, char **argv,
for (i = 0; options[i].type != SIMPLE_OPT_END; i++) {
if ( (options[i].short_name == '\0' && options[i].long_name == NULL)
|| (options[i].type == SIMPLE_OPT_FLAG &&
- options[i].arg_is_required) ) {
+ options[i].arg_is_required)
+ || (options[i].type == SIMPLE_OPT_STRING_SET &&
+ options[i].string_set == NULL) ) {
r.result_type = SIMPLE_OPT_RESULT_MALFORMED_OPTION_STRUCT;
goto end;
}
@@ -568,13 +570,13 @@ static void simple_opt_print_usage(FILE *f, unsigned width, char *usage_name,
}
}
- /* 5 for leading " -X ", 1 for trailing " " */
- if (desc_line_start < j + 5 + 1)
- desc_line_start = j + 5 + 1;
+ /* 5 for leading " -X ", 2 for trailing " " */
+ if (desc_line_start < j + 5 + 2)
+ desc_line_start = j + 5 + 2;
}
/* check for space for long_name printing */
- if (desc_line_start - 5 - 1 >= SIMPLE_OPT_USAGE_PRINT_BUFFER_WIDTH) {
+ if (desc_line_start - 5 - 2 >= SIMPLE_OPT_USAGE_PRINT_BUFFER_WIDTH) {
fprintf(f, "simple-opt internal err: usage print buffer too small\n");
return;
}