A C99+ option parser.
- Supports both short and long options.
- Short options are POSIX-compliant (-o ARG, -oARG, -asdfoARG).
- Long options follow the GNU standard (--option ARG, --option=ARG).
- The order of options and operands (non-options) does not matter.
- Supports the "end of options" delimiter (--).
- Can set integer flags (true, false, increment, decrement).
- Can split, type-convert and store option-arguments.
- Supports subcommands and nested subcommands.
- Options and commands/subcommands can call functions ("callbacks") with or without arguments.
- Mutually exclusive options.
- A nicely-formatted, customizable help screen with word-wrapping.
- Provides functions for easy manual parsing (e.g. to implement multiple option-arguments).
- Provides function "strtox()" for manual type-conversion.
- Features can be toggled to only compile necessary code.
By now all features are implemented and supposed to work. If you like the ideas and want to help polish them further or report bugs, please create an issue at https://github.com/hippie68/optparse99/issues.
#include "optparse99.h"
#include <stdint.h> // For uint16_t
#include <stdio.h>
#include <stdlib.h> // For exit()
int verbose;
uint16_t bufsize = 4096;
char *filename;
int *array;
size_t array_size;
void check_bufsize(uint16_t bufsize)
{
if (bufsize < 4096) {
fprintf(stderr, "Buffer size too low: %u\n", bufsize);
exit(EXIT_FAILURE);
}
}
int main(int argc, char *argv[])
{
// 1. Define the command tree and its options.
struct optparse_cmd main_cmd = {
.about = "Supertool v1.00 - A really handy tool.",
.description = "This is an example program that uses a basic optparse99 setup. Variables like \"FILENAME\" serve no purpose other than to demonstrate option behavior. After parsing, the program will print remaining operands and variables' final state.",
.name = "supertool",
.operands = "OPERAND [OPERAND...]",
.options = (struct optparse_opt []) {
// Call the built-in help screen function.
{
.short_name = 'h',
.long_name = "help",
.description = "Print help information and quit.",
.function = optparse_print_help,
},
// Set a flag.
{
.long_name = "verbose",
.description = "Increase verbosity.",
.flag = &verbose,
},
// Use a user-provided option-argument to set a string.
{
.short_name = 'f',
.long_name = "file",
.description = "Set a file name.",
.arg_name = "FILENAME",
.arg_storage = &filename,
},
// Split and store a comma-separated integer list as an array.
{
.long_name = "input",
.description = "Process a list of comma-separated numbers.",
.arg_name = "LIST",
.arg_data_type = DATA_TYPE_INT,
.arg_delim = ",",
.arg_storage = &array,
.arg_storage_size = &array_size,
},
// Convert a user-provided option-argument to data type uint16_t and
// check if the converted number is inside an allowed range.
// (The check could also be done, i.e. together with other options'
// checks, by employing main_cmd's .function, or after parsing.)
{
.short_name = 'b',
.long_name = "bufsize",
.description = "Change the file buffer size. Allowed range: 4096-65535 (default: 4096).",
.arg_name = "BUFSIZE",
.arg_data_type = DATA_TYPE_UINT16,
.arg_storage = &bufsize,
.function = (void (*)(void)) check_bufsize,
},
{ END_OF_OPTIONS },
},
};
// 2. After defining the command tree, parse the command line arguments.
optparse_parse(&main_cmd, &argc, &argv);
// At this point, optparse99 is done. Variables have been set, and argc and
// argv have been altered to contain operands only.
// Print remaining operands.
if (argc > 1) {
for (int i = 0; i <= argc; i++) {
printf("argv[%d]: %s\n", i, argv[i] ? argv[i] : "NULL");
}
} else {
fprintf(stderr, "At least one operand is required.\n");
optparse_fprint_usage(stderr);
exit(EXIT_FAILURE);
}
// Print state of variables.
printf("verbose: %d\n", verbose);
printf("filename: %s\n", filename ? filename : "NULL");
printf("bufsize: %u\n", bufsize);
printf("array_size: %zu\n", array_size);
if (array != NULL) {
for (size_t i = 0; i < array_size; i++) {
printf("array[%zu]: %d\n", i, array[i]);
}
free(array);
}
}
Help screen output:
$ supertool --help
Supertool v1.00 - A really handy tool.
Usage: supertool [OPTIONS] OPERAND [OPERAND...]
This is an example program that uses a basic optparse99 setup. Variables like
"FILENAME" serve no purpose other than to demonstrate option behavior. After
parsing, the program will print remaining operands and variables' final state.
Options:
-h, --help Print help information and quit.
--verbose Increase verbosity.
-f, --file FILENAME Set a file name.
--input LIST Process a list of comma-separated numbers.
-b, --bufsize BUFSIZE Change the file buffer size. Allowed range: 4096-65535
(default: 4096).
Optparse99 is based on two types of structures: commands and options. Each command can have a set of options and a set of subcommands, the latter which can be nested tree-like. The root of the tree is always the program's command itself.
Command and option structures are to be thought of as sets of instructions. Most structure members are optional and, by making use of C99's designated initializers, do not need to be specified. Non-specified members are initialized with 0 (NULL), which for enumeration types is the default value.
Both options and subcommands are arrays that are constructed by compound literals and must end with the element { END_OF_OPTIONS } and { END_OF_SUBCOMMANDS }, respectively:
struct optparse_cmd main_cmd = {
.options = (struct optparse_opt []) {
{ ... },
{ END_OF_OPTIONS },
},
.subcommands = (struct optparse_cmd []) {
{ ... },
{ END_OF_SUBCOMMANDS },
},
};
struct optparse_cmd {
char *name;
char *about;
char *description;
char *operands;
char *usage;
void (*function)(int, char **);
struct optparse_opt *options;
struct optparse_cmd *subcommands;
struct optparse_cmd *_parent;
};
Structure member | Description |
---|---|
.name (required) |
The command line string users enter to run the command. |
.about |
A short sentence, describing the command's purpose in a nutshell. May also contain information like version number, homepage, etc. |
.description |
The command's detailed documentation. |
.operands |
The command's operands (aka "positional arguments") as to be displayed in the help screen. |
.usage |
Can be specified to override automatic usage generation, e.g. if operands depend on options. |
.function |
Once the command's options have been parsed, the command will call the specified function, using the current state of argc and argv as function arguments. |
.options |
Points to an array containing the command's options. |
.subcommands |
Points to an array containing the command's subcommands. |
Members starting with an underscore ("_") are for internal use only and should be ignored.
struct optparse_opt {
char short_name;
char *long_name;
char *arg_name;
enum optparse_data_type arg_data_type;
char *arg_delim;
void *arg_storage;
size_t *arg_storage_size;
int *flag;
enum optparse_flag_type flag_type;
void (*function)(void);
enum optparse_function_type function_type;
int group;
_Bool hidden;
char *description;
};
Structure member | Description |
---|---|
.short_name (required*) |
The short option character. |
.long_name (required*) |
The long option string (without leading "--"). |
.arg_name |
If specified, it means the option has one or more option-arguments. The string is displayed as-is in the help screen. If it begins with "[", the option-argument is regarded as optional. |
.arg_data_type |
If set, the parsed option-argument (char *) will be converted to a different data type. |
.arg_delim |
If set, the option-argument will be treated as a list whose items are separated by any of this string's characters. |
.arg_storage |
The memory location the (type-converted) option-argument is saved to. Its data type must match the one defined in .arg_data_type. If .arg_delim is set, it must be a pointer (which after parsing will point to dynamically allocated memory). |
.arg_storage_size |
The memory location the number of list items stored in *arg_storage is saved to. |
.flag |
A pointer to an integer variable that is to be used as specified by .flag_type. |
.flag_type |
Specifies what to do to with the flag variable's value. |
.function |
Points to a function that is called as specified in .function_type. The pointer can be cast to void (*)(void) to avoid compiler warnings. |
.function_type |
Specifies how the function pointed to by .function is expected to be declared and, internally, going to be called. |
.group |
Options that share the same group value are treated as mutually exclusive. |
.hidden |
If true, the option won't be displayed in the help screen. |
.description |
The option's description, whether short or in-depth. |
*At least one of them must be specified.
Value | Conversion type |
---|---|
DATA_TYPE_STR (default) |
(no conversion) |
DATA_TYPE_CHAR |
char |
DATA_TYPE_SCHAR |
signed char |
DATA_TYPE_UCHAR |
unsigned char |
DATA_TYPE_SHRT |
short |
DATA_TYPE_USHRT |
unsigned short |
DATA_TYPE_INT |
int |
DATA_TYPE_UINT |
unsigned int |
DATA_TYPE_LONG |
long |
DATA_TYPE_ULONG |
unsigned long |
DATA_TYPE_LLONG |
long long |
DATA_TYPE_ULLONG |
unsigned long long |
DATA_TYPE_FLT |
float |
DATA_TYPE_DBL |
double |
DATA_TYPE_LDBL |
long double |
DATA_TYPE_BOOL |
_Bool |
DATA_TYPE_INT8 |
int8_t |
DATA_TYPE_UINT8 |
uint8_t |
DATA_TYPE_INT16 |
int16_t |
DATA_TYPE_UINT16 |
uint16_t |
DATA_TYPE_INT32 |
int32_t |
DATA_TYPE_UINT32 |
uint32_t |
DATA_TYPE_INT64 |
int64_t |
DATA_TYPE_UINT64 |
uint64_t |
Value | Result |
---|---|
FLAG_TYPE_SET_TRUE (default) |
Set the variable to 1. |
FLAG_TYPE_SET_FALSE |
Set the variable to 0. |
FLAG_TYPE_INCREMENT |
Increase the variable's current value by 1. |
FLAG_TYPE_DECREMENT |
Decrease the variable's current value by 1. |
Value | Function declaration and internal call |
---|---|
FUNCTION_TYPE_AUTO (default) |
Automatically decide. |
FUNCTION_TYPE_TARG |
TARG means "type-converted option-argument". declaration: void f(DATA_TYPE); call: f(TARG); (DATA_TYPE is set according to .arg_data_type .) |
FUNCTION_TYPE_TARG_ARRAY |
declaration: void f(size_t, DATA_TYPE *); call: f(ARRAY_SIZE, TARG_ARRAY); |
FUNCTION_TYPE_OARG |
OARG means "original option-argument". declaration: void f(char *); call: f(OARG); |
FUNCTION_TYPE_OARG_ARRAY |
declaration: void f(size_t, char **); call: f(ARRAY_SIZE, OARG_ARRAY); |
FUNCTION_TYPE_VOID |
declaration: void f(void); call: f(); |
How automatic decision works:
- if .arg_name is set:
- if .arg_delim is set: FUNCTION_TYPE_TARG_ARRAY
- else: FUNCTION_TYPE_TARG
- else: FUNCTION_TYPE_VOID
Functions refered to by .function must be of return type void, and their declaration must match the one specified by .function_type.
The data passed pointers refer to is only guaranteed to exist while the function is running.
void optparse_parse(optparse_cmd *cmd, int *argc, char ***argv);
optparse_parse() starts the parsing process. It must be called before calling any other optparse_ function. After parsing, the main() function's argc and argv will contain only operands.
- *cmd: a pointer to the command tree's root command
- *argc: a pointer to main()'s argc variable
- ***argv: a pointer to main()'s argv variable
void optparse_print_help(void);
Prints the currently active command's help information. It can be called manually or through an option's .function member - see the quick example above. Exits with exit status EXIT_SUCCESS.
void optparse_fprint_help(FILE *stream, int exit_status);
Same as optparse_print_help(), but can only be called manually. It prints to the specified stream and exits with the provided exit status.
void optparse_fprint_usage(FILE *stream);
Prints the currently active command's usage information only.
void optparse_print_help_subcmd(int argc, char **argv);
Prints a subcommand's help information by parsing remaining operands. optparse_print_help_subcmd() must be called while the remaining command line arguments represent a valid subcommand chain, e.g. as a subcommand's function:
...
.subcommands = (struct optparse_cmd []) {
{
.about = "Print a subcommand's help information and quit.",
.name = "help",
.operands = "[COMMAND...]",
.function = optparse_print_help_subcmd,
},
...
It is possible to manually parse arguments from inside an option's callback function (.function). Inside that function, the following functions can be used:
char *optparse_shift(void);
char *optparse_unshift(void);
optparse_shift() returns the next command line argument, while optparse_unshift() puts it back. Please note that optparse_unshift() is only guaranteed to undo the most recent shift. Using this technique, you can parse multiple option-arguments while having full control over what exactly qualifies as a valid option-argument and when to stop.
The callback function must have the single parameter char *, and .function_type must be FUNCTION_TYPE_OARG. The function's argument will be the first command line argument, so it must be checked prior to using optparse_shift(). E.g. to make an option eat and print all remaining command line arguments:
void print_arg(char *arg) {
while (arg != NULL) {
printf("option-argument: %s\n", arg);
arg = optparse_shift();
}
}
Or, in general:
void print_arg(char *arg) {
if (arg == NULL) {
... // Code that is run if a required argument is missing.
return;
}
do {
... // Code that is run for each argument.
arg = optparse_shift();
} while (arg != NULL);
}
Note: when implementing multiple option-arguments, the first option-argument must not be optional unless OPTPARSE_ATTACHED_OPTION_ARGUMENTS (see Preprocessor directives) is set to false.
The function used internally for type-converting option-arguments is strtox():
int strtox(char *str, void *x, enum optparse_data_type data_type);
It can also be used manually, to convert a string to any of the basic C99 data types. For example, to convert a string (in this case a string literal, "512") to int:
int i;
int retval = strtox("512", &i, DATA_TYPE_INT);
The function's return value depends on the conversion outcome:
Return value | Meaning |
---|---|
0 | Success |
1 | Error: the string is not convertible. |
-1 | Error: converted data is out of range. |
The following macros can be defined to disable features and to customize the help screen:
Macro | Default value | Description |
---|---|---|
OPTPARSE_LONG_OPTIONS |
1 (boolean) | Enables/disables long options. |
OPTPARSE_SUBCOMMANDS |
1 (boolean) | Enables/disables subcommands. |
OPTPARSE_MUTUALLY_EXCLUSIVE_OPTIONS |
1 (boolean) | Enables/disables mutually exclusive options. |
OPTPARSE_HIDDEN_OPTIONS |
1 (boolean) | Enables/disables hidden options. |
OPTPARSE_ATTACHED_OPTION_ARGUMENTS |
1 (boolean) | Enables/disables attached option-arguments (-oarg, --option=arg). Note: if disabled, optional option-arguments can only be detected during manual parsing. |
OPTPARSE_LIST_SUPPORT |
1 (boolean) | Enables/disables support for option-arguments in list form. |
OPTPARSE_FLOATING_POINT_SUPPORT |
1 (boolean) | Enables/disables floating point support. |
OPTPARSE_C99_INTEGER_TYPES_SUPPORT |
1 (boolean) | Enables/disables C99 integer types support. |
OPTPARSE_HELP_INDENTATION_WIDTH |
2 | The help screen's indentation width, in characters. |
OPTPARSE_HELP_MAX_DIVIDER_WIDTH |
32 | Maximum distance between the help screen's left edge and option descriptions. |
OPTPARSE_HELP_MAX_LINE_WIDTH |
80 | Maximum line width for word wrapping. |
OPTPARSE_HELP_USAGE_STYLE |
0 | Style used for automatic usage generation; 0: short, 1: verbose. |
OPTPARSE_HELP_USAGE_OPTIONS_STRING |
"OPTIONS" | Placeholder string to be displayed if OPTPARSE_HELP_USAGE_STYLE is 0. |
OPTPARSE_HELP_LETTER_CASE |
0 | The help screen's letter case; 0: capitalized, 1: lower, 2: upper. |
OPTPARSE_HELP_WORD_WRAP |
1 (boolean) | Enables/disables word wrap for lines longer than OPTPARSE_HELP_MAX_LINE_WIDTH. |
OPTPARSE_HELP_FLOATING_DESCRIPTIONS |
1 (boolean) | Defines how a description is to be printed if an option is longer than OPTPARSE_HELP_MAX_DIVIDER_WIDTH; 0: print description on a separate line, 1: print description on the same line, after a single indentation. |
OPTPARSE_HELP_UNIQUE_COLUMN_FOR_LONG_OPTIONS |
1 (boolean) | Makes long options stay in a separate column even if there's no short option. |
OPTPARSE_PRINT_HELP_ON_ERROR |
1 (boolean) | Prints the currently active command's help screen if there's a parsing error. |
OPTPARSE_MUTUALLY_EXCLUSIVE_GROUPS_MAX |
8 | The maximum amount of groups for mutually exclusive options. |
OPTPARSE_PRINT_BUFFER_SIZE |
1024 | The size of the buffer used for printing functionality of optparse99 such as printing help and usage. |
By disabling a feature, related code will not be compiled and structure members that are related to that feature will no longer be recognized.
Recommended:
- When building releases, define
NDEBUG
to remove assert()-related code. That code is used to identify mistakes in command and option structures and is not needed in release builds. - When compiling with GCC or clang and compiler option -Wextra, warnings like
warning: missing initializer for field ‘long_name’ of ‘struct optparse_opt’ [-Wmissing-field-initializers]
may appear. To avoid them, the command tree's definition can be wrapped in these directives:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmissing-field-initializers"
struct optparse_cmd main_cmd = {
...
};
#pragma GCC diagnostic pop