facil.io

#CLI Helpers

c
#define FIO_CLI
#include "fio-stl.h"

A small command-line parser with auto-generated help, typed flags, and unnamed-argument support. It is easier to set up than getopt and stays out of the way once you have your values.

Defining FIO_CLI also pulls in FIO_ATOL, FIO_RAND, and FIO_IMAP because the parser needs them internally.

Note: the module keeps a global data table. Parsing and setting values is not thread-safe. Reading values is safe after parsing is complete.


#Types

#fio_cli_arg_e

c
typedef enum {
  FIO_CLI_ARG_STRING,
  FIO_CLI_ARG_BOOL,
  FIO_CLI_ARG_INT,
  FIO_CLI_ARG_PRINT,
  FIO_CLI_ARG_PRINT_LINE,
  FIO_CLI_ARG_PRINT_HEADER,
} fio_cli_arg_e;

Argument type tags used internally and returned by fio_cli_type.

Values:

  • FIO_CLI_ARG_STRING — accepts any string value.
  • FIO_CLI_ARG_BOOL — a flag; presence means true.
  • FIO_CLI_ARG_INT — validates the value as an integer.
  • FIO_CLI_ARG_PRINT — indented help text line.
  • FIO_CLI_ARG_PRINT_LINE — unindented help text line.
  • FIO_CLI_ARG_PRINT_HEADER — section header in help output.

#Argument Definition Macros

Use these inside the fio_cli_start macro to describe each flag and help line.

#FIO_CLI_STRING

c
#define FIO_CLI_STRING(line) /* ... */

Declares a string argument.

c
FIO_CLI_STRING("-o -output (stdout) output file path")

#FIO_CLI_INT

c
#define FIO_CLI_INT(line) /* ... */

Declares an integer argument. The parser validates the value with fio_atol.

c
FIO_CLI_INT("-p -port (8080) the port number to use")

#FIO_CLI_BOOL

c
#define FIO_CLI_BOOL(line) /* ... */

Declares a boolean flag. Boolean flags cannot have default values. They can be chained, e.g. -abc is treated as -a -b -c.

c
FIO_CLI_BOOL("-v -verbose enable verbose logging")

#FIO_CLI_PRINT

c
#define FIO_CLI_PRINT(line) /* ... */

Prints an extra indented help line after the previous argument.

c
FIO_CLI_INT("-p -port (8080) the port number"),
FIO_CLI_PRINT("Set to 0 for Unix socket mode.")

#FIO_CLI_PRINT_LINE

c
#define FIO_CLI_PRINT_LINE(line) /* ... */

Prints a help line without indentation.

#FIO_CLI_PRINT_HEADER

c
#define FIO_CLI_PRINT_HEADER(line) /* ... */

Prints a section header in the help output.

c
FIO_CLI_PRINT_HEADER("Network Options:")

#Initialization and Cleanup

#fio_cli_start

c
#define fio_cli_start(argc, argv, unnamed_min, unnamed_max, description, ...)  \
  fio_cli_start((argc),                                                        \
                (argv),                                                        \
                (unnamed_min),                                                 \
                (unnamed_max),                                                 \
                (description),                                                 \
                (fio___cli_line_s[]){__VA_ARGS__, {0}})

SFUNC void fio_cli_start FIO_NOOP(int argc,
                                  char const *argv[],
                                  int unnamed_min,
                                  int unnamed_max,
                                  char const *description,
                                  fio___cli_line_s *arguments);

Parses argv into a dictionary of named and unnamed arguments, then prints help and exits on -h, -?, -help, or --help.

Named Arguments:

  • argc, argv — values passed to main.
  • unnamed_min — minimum required unnamed arguments. -1 means no limit on unnamed arguments.
  • unnamed_max — maximum allowed unnamed arguments. -1 means unlimited.
  • description — program description shown in help. The text NAME is replaced with argv[0].
  • ... — argument description lines using the macros above.

Argument names must start with -. The first non-- word begins the description. Optional defaults go in parentheses: (default) or ("literal default").

Accepted input formats:

text
app -t=1 -p3000 -a localhost
app -t 1 -p 3000 -a localhost
app --threads=1 --port=3000 --address=localhost

Note: this function is not thread-safe.

c
#define FIO_CLI
#include "fio-stl.h"

int main(int argc, char const *argv[]) {
  fio_cli_start(argc, argv, 0, -1,
                "NAME - a small CLI example.\n",
                FIO_CLI_PRINT_HEADER("Options:"),
                FIO_CLI_STRING("-s -str (hello) a string"),
                FIO_CLI_INT("-i -int (42) an integer"),
                FIO_CLI_BOOL("-b -bool a boolean flag"),
                FIO_CLI_PRINT("Boolean flags do not take values."));

  fprintf(stderr, "string: %s\n", fio_cli_get("-s"));
  fprintf(stderr, "int: %" PRId64 "\n", fio_cli_get_i("-i"));
  fprintf(stderr, "bool: %d\n", (int)fio_cli_get_bool("-b"));

  fio_cli_end();
  return 0;
}

#fio_cli_end

c
SFUNC void fio_cli_end(void);

Frees the parsed CLI dictionary.

A destructor runs this automatically at program exit, but calling it earlier lets the memory be reused.

Note: this function is not thread-safe.


#Getting Values

#fio_cli_get

c
SFUNC char const *fio_cli_get(char const *name);

Returns the argument value as a NUL-terminated C string, or NULL if the argument was not provided. If name is NULL, returns the first unnamed argument.

#fio_cli_get_str

c
SFUNC fio_buf_info_s fio_cli_get_str(char const *name);

Returns the argument value as a fio_buf_info_s with length. If name is NULL, returns the first unnamed argument.

#fio_cli_get_i

c
SFUNC int64_t fio_cli_get_i(char const *name);

Returns the argument value parsed as an integer, or 0 if it was not provided. Integer parsing accepts decimal, hex, octal, and binary prefixes.

#fio_cli_get_bool

c
#define fio_cli_get_bool(name) (fio_cli_get((name)) != NULL)

Returns non-zero if the argument was provided, including by default.

#fio_cli_type

c
SFUNC fio_cli_arg_e fio_cli_type(char const *name);

Returns the declared argument type, or FIO_CLI_ARG_NONE if the name is unknown.


#Unnamed Arguments

#fio_cli_unnamed_count

c
SFUNC unsigned int fio_cli_unnamed_count(void);

Returns the number of unnamed arguments collected.

#fio_cli_unnamed

c
SFUNC char const *fio_cli_unnamed(unsigned int index);

Returns the unnamed argument at index as a NUL-terminated string, or NULL if index is out of bounds.

#fio_cli_unnamed_str

c
SFUNC fio_buf_info_s fio_cli_unnamed_str(unsigned int index);

Returns the unnamed argument at index as a fio_buf_info_s, or an empty fio_buf_info_s if out of bounds.


#Setting Values

#fio_cli_set

c
SFUNC void fio_cli_set(char const *name, char const *value);

Sets or overwrites a named argument's value. If name is NULL, appends value as a new unnamed argument.

Note: this function is not thread-safe.

#fio_cli_set_i

c
SFUNC void fio_cli_set_i(char const *name, int64_t i);

Sets a named argument to a base-10 string representation of i.

Note: this function is not thread-safe.

#fio_cli_set_unnamed

c
SFUNC unsigned int fio_cli_set_unnamed(unsigned int index, const char *value);

Sets or appends an unnamed argument. If index is past the end, the value is appended. Returns the stored index, or (unsigned int)-1 if value is NULL or empty.

Note: this function is not thread-safe.


#Iteration

#fio_cli_each

c
SFUNC size_t fio_cli_each(int (*task)(fio_buf_info_s name,
                                      fio_buf_info_s value,
                                      fio_cli_arg_e arg_type,
                                      void *udata),
                          void *udata);

Calls task for every argument that has a value, returning the number of calls made. If task returns non-zero, iteration stops. For unnamed arguments, name.buf is NULL and name.len is 0.