Command line flag parsing library in C
Heavily inspired by Go's flag
package
https://golang.org/pkg/flag/
cflags is a header only library. To use it, simply copy cflags.h
or cflags.hpp
to your project, or add it to your include path.
You may also install it using cmake, like so:
cmake path/to/source
sudo make install
This will install both CMake and pkg-config configuration files.
- The first argument is stored in
program
- The following arguments are parsed left to right
- If an argument does not start with
-
, it is placed in the additional arguments list stored inargs/argv
- If the special
--
argument appears, all following arguments are treated as positional
e.g.-c 4 -- --name hello
would parse the-c
, but place--name
andhello
intoargs/argv
- Arguments starting with
--
are long name flags, e.g.--example
- The list of flags is searched for one with
long_name
equal to the argument name (after the--
), e.g.long_name == example
- If a flag is not found with that name, an error is printed and
parse()
returns false
- The list of flags is searched for one with
- Arguments starting with just
-
are short name flags, e.g.-xvf
- These can be grouped together, so they are searched one at a time from left to right, e.g.
x
,v
, thenf
- If any of these fail to match a flag, an error is printed and
parse()
returns false
- These can be grouped together, so they are searched one at a time from left to right, e.g.
- Once a flag is found, it attempts to find a value
- Arguments with long names can also come in the forms
--name
,--name=value
, or--name value
- Arguemnts with short names can come in the forms
-n
, or-n value
- Note: Only the last short flag of a group can have a value, e.g.
-xvf file
will work, but-xfv file
will fail
- Note: Only the last short flag of a group can have a value, e.g.
- If the flag is of type
[c]string
,int
, orfloat
then a value is required, and if one is not found an error is printed andparse()
returns false- Arguments of type
bool
can have a value, e.g.--debug=false
, but one is not required
- Arguments of type
- Each time a flag is encountered, the
count
member is incremented - The value for a flag is overwritten each time the flag is processed, the last argument parsed wins, e.g.
-c 4 -c 10
will result in-c
being 10- If you want to capture each argument separately, use
add_*_callback
instead
- If you want to capture each argument separately, use
- Arguments with long names can also come in the forms
#include <cflags.h>
void process_string(const char * str)
{
printf("processing %s\n", str);
}
void process_bool(bool b)
{
printf("processing %d\n", b);
}
void process_int(int i)
{
printf("processing %d\n", i);
}
void process_float(float f)
{
printf("processing %f\n", f);
}
int main(int argc, char** argv)
{
// Create a cflags object
cflags_t * flags = cflags_init();
// Add a bool flag, which will be callable with -d or --debug
// The value will be true if it exists, and can bet set to false
// by saying -d false or --debug=false
bool debug = false;
cflags_add_bool(flags, 'd', "debug", &debug, "enable debug mode");
// Add a similar help flag, which will be callable with just --help
bool help = false;
cflags_add_bool(flags, '\0', "help", &help, "print this text and exit");
// Add a string flag
const char * string = NULL;
cflags_add_string(flags, 's', "string", &string, "enter a string");
// Add an int flag
int count = 0;
cflags_add_int(flags, 'c', "count", &count, "enter a number");
// Add a float flag
float amount = 0.f;
cflags_add_float(flags, 'a', "amount", &amount, "enter a float");
// Add a string callback flag. This will call the supplied function with the value
// when it is parsed
cflags_add_string_callback(flags, 'f', "file", &process_string, "process a file");
cflags_add_bool_callback(flags, 'q', "bool-flag", &process_bool, "process a bool");
cflags_add_int_callback(flags, 'w', "int-flag", &process_int, "process a int");
cflags_add_float_callback(flags, 'e', "float-flag", &process_float, "process a float");
// Add a flag that can be called multiple times
cflags_flag_t * verbose = cflags_add_bool(flags, 'v', "verbose", NULL, "enables verbose output, repeat up to 4 times for more verbosity");
// Parse the command arguments
if (!cflags_parse(flags, argc, argv) || help || flags->argc == 0) {
cflags_print_usage(flags,
"[OPTION]... [ARG]...",
"Tests the cflags library.",
"Additional information about this library can be found by at:\n"
" https://github.com/WhoBrokeTheBuild/cflags");
}
printf("debug: %d\n", debug);
printf("string: %s\n", string);
printf("count: %d\n", count);
printf("amount: %f\n", amount);
// Print the number of times verbose was added
printf("verbosity: %d\n", verbose->count);
// Print any additional arguments, in the order they were parsed
for (int i = 1; i < flags->argc; ++i) {
printf("positional arg %d: %s\n", i, flags->argv[i]);
}
// Cleanup
cflags_free(flags);
return 0;
}
#include <cflags.hpp>
void process_string(std::string str)
{
printf("processing %s\n", str.c_str());
}
void process_cstring(const char * str)
{
printf("processing %s\n", str);
}
void process_bool(bool b)
{
printf("processing %d\n", b);
}
void process_int(int i)
{
printf("processing %d\n", i);
}
void process_float(float f)
{
printf("processing %f\n", f);
}
int main(int argc, char * argv[])
{
// Create a cflags object
cflags::cflags flags;
// Add a bool flag, which will be callable with -d or --debug
// The value will be true if it exists, and can bet set to false
// by saying -d false or --debug=false
bool debug = false;
flags.add_bool('d', "debug", &debug, "enable debug mode");
// Add a similar help flag, which will be callable with just --help
bool help = false;
flags.add_bool('\0', "help", &help, "print this text and exit");
// Add a string flag
std::string string;
flags.add_string('s', "string", &string, "enter a string");
// Add a cstring flag
const char * cstring = NULL;
flags.add_cstring('\0', "cstring", &cstring, "enter a string (cstring)");
// Add an int flag
int count = 0;
flags.add_int('c', "count", &count, "enter a number");
// Add a float flag
float amount = 0.f;
flags.add_float('a', "amount", &amount, "enter a float");
// Add a string callback flag. This will call the supplied function with the value
// when it is parsed
flags.add_string_callback('f', "file", &process_string, "process a file");
flags.add_cstring_callback('\0', "cfile", &process_cstring, "process a file (cstring)");
flags.add_bool_callback('q', "bool-flag", &process_bool, "process a bool");
flags.add_int_callback('w', "int-flag", &process_int, "process a int");
flags.add_float_callback('e', "float-flag", &process_float, "process a float");
// You can also use lambdas
flags.add_string_callback('l', "lambda",
[](std::string value) {
printf("Hello %s\n", value.c_str());
},
"use a lambda function"
);
// Add a flag that can be called multiple times
auto verbose = flags.add_bool('v', "verbose", NULL, "enables verbose output, repeat up to 4 times for more verbosity");
// Parse the command arguments
if (!flags.parse(argc, argv) || help || flags.argc == 0) {
flags.print_usage(
"[OPTION]... [ARG]...",
"Tests the cflags library.",
"Additional information about this library can be found by at:\n"
" https://github.com/WhoBrokeTheBuild/cflags");
}
printf("debug: %d\n", debug);
printf("string: %s\n", string.c_str());
printf("cstring: %s\n", cstring);
printf("count: %d\n", count);
printf("amount: %f\n", amount);
// Print the number of times verbose was added
printf("verbosity: %d\n", verbose->count);
// Print any additional arguments, in the order they were parsed
for (auto& arg : flags.args) {
printf("positional arg %s\n", arg.data());
}
// For backwards compatability, the additional arguments are also exposed in argc/argv
for (int i = 0; i < flags.argc; ++i) {
printf("positional arg %d: %s\n", i, flags.argv[i]);
}
return 0;
}
For example:
flags.add_string('f', "file", parse_filename, "parse a filename");
flags.add_bool('d', "debug", &debug, "enable debug mode");
// These will work
test -df test.txt
test -df test.txt -f test2.txt
// And these will fail
test -fd test.txt
test -ff test.txt test2.txt
Call the following code as program -s hello
int main(int argc, char ** argv)
{
for (int i = 1; i < argc; ++i) {
printf("argv[%d] @ %p: %s\n", i, argv[i], argv[i]);
}
cflags_t * flags = cflags_init();
const char * string = NULL;
cflags_add_string(flags, 's', "string", &string, "enter a string");
cflags_parse(flags, argc, argv);
// The address pointed to by `string` is the same memory pointed to by argv
printf("string @ %p: %s\n", string, string);
cflags_free(flags);
return 0;
}
Example output
argv[1] @ 0000024F6BDD665F: -s
argv[2] @ 0000024F6BDD6662: hello
string @ 0000024F6BDD6662: hello
When parsing arguments in the form --name=value
, the memory pointed to by argv
is altered and the =
is replaced by a \0
.
When using the C++ version, arguments as std::string
do not point at argv
as their memory gets copied.