Argument Parsing¶
#include "libstephen/ad.h"
For practical programs, argument parsing is a large part of what you do.
So, having a simple library to help you build normal command line
interfaces can save you plenty of time. The library in
libstephen/ad.h
provides a basic argument parsing solution. I wrote
it before I knew about the existence of well established solutions like
getopt
. It’s not a perfect solution, but it does the job for most of
my use cases.
Argument Types¶
My argument processor is based on four different types of arguments:
- Short flags: these are one letter flags that can be on or off. They
are prefixed by a single hyphen. They can also be smooshed together.
For instance, with the command
ls -la
has two short flags:l
anda
. - Long flags: these are multi-letter flags that can be on or off. They
have to be prefixed by a double hyphen. So, the command
ls --all
has a long flag:all
. - Parameters: both short and long flags may have parameters provided to
them. For instance,
gcc -o code.o code.c
hascode.o
provided as an argument to the short flago
. - Bare strings: these are neither short nor long flags. They are simply
arguments provided to the program. They are typically the actual
objects that the program is to operate on (input files, or input
text). For instance,
gcc -o code.o code.c
has the bare stringcode.c
.
Initialization¶
void arg_data_init(smb_ad *data);
smb_ad *arg_data_create();
void arg_data_destroy(smb_ad *data);
void arg_data_delete(smb_ad *data);
void process_args(smb_ad *data, int argc, char **argv);
In order to start using the arg parsing library, you create a smb_ad
object. Typically you do this on the stack in your main()
function,
because why would your arguments need to last any longer than that? You
call arg_data_init()
with a pointer to the smb_ad
to initialize
it. To parse your program’s arguments, you call
process_args(smb_ad *, int argc, char **argv)
. You shouldn’t include
the program name in argc
and argv
(unless you want it to be
parsed as an argument).
So, something like this would be the basic structure of your main function:
int main(int argc, char *argv[])
{
smb_ad args;
arg_data_init(&args);
process_args(&args, argc-1, argv+1);
// Get arguments and flags
// Do program logic
arg_data_destroy(&args);
return EXIT_SUCCESS;
}
Reading Argument Data¶
int check_flag(smb_ad *data, char flag);
int check_long_flag(smb_ad *data, char *flag);
int check_bare_string(smb_ad *data, char *string);
char *get_flag_parameter(smb_ad *data, char flag);
char *get_long_flag_parameter(smb_ad *data, char *string);
Once you have processed the argument data, the smb_ad
can be queried
for each argument you’re expecting. Typically, you’ll pair short flags
with long flags (e.g. -h
and --help
). You can check for long
flags with check_long_flag()
, and you can check for short flags with
check_flag()
. You can get flag and long flag parameters with
get_flag_parameter()
and get_long_flag_parameter()
respectively.
You can also use check_bare_string()
to check if a bare string was
provided. However, that’s not usually the most convenient way to access
the bare strings (since you probably don’t already know the bare string
before it’s provided). So, you can also access the linked list directly
in the bare_strings
field of the smb_ad
struct.
Here’s a typical example of how you might use smb_ad
to parse
arguments. It continues from the outline earlier.
if (check_flag(&args, 'h') || check_long_flag(&args, "help")) {
help();
} else if (check_flag(&args, 'o') || check_long_flag(&args, "output")) {
output = get_flag_parameter(&args, 'o');
if (!output) {
output = get_long_flag_parameter(&args, "output");
}
}
// Now you can do stuff with the output file name you got!