C library for command-line argument parsing with descriptor-based config and automatic help generation.
Find a file
2026-04-23 12:25:02 +02:00
example feat: add cli_print_options with extensible arg type system 2026-03-29 16:35:29 +02:00
include feat: hexa parsing util 2026-04-23 12:25:02 +02:00
src feat: hexa parsing util 2026-04-23 12:25:02 +02:00
tests feat: hexa parsing util 2026-04-23 12:25:02 +02:00
.gitignore chore: ignore automake dirstamp and test result files 2026-03-29 16:35:29 +02:00
autogen.sh build: add autotools build system 2026-03-29 16:35:29 +02:00
configure.ac feat: pkgconfig and fix default CFLAGS 2026-04-22 17:55:50 +02:00
libcli.pc.in feat: pkgconfig and fix default CFLAGS 2026-04-22 17:55:50 +02:00
LICENSE chore: add license and gitignore 2026-03-29 01:10:01 +01:00
Makefile.am feat: pkgconfig and fix default CFLAGS 2026-04-22 17:55:50 +02:00
README.md feat: update README 2026-04-20 17:03:34 +02:00

libcli

A lightweight C library for parsing command-line arguments. Wraps getopt_long with a descriptor-based API, typed parse utilities, duplicate detection, and automatic help formatting.

Dependencies

  • clang
  • autoconf ≥ 2.69, automake, libtool
  • Criterion — optional, required for --enable-tests

Build

./autogen.sh
./configure
make
make install
ldconfig

To build and run the test suite:

./configure --enable-tests
make check

Quickstart

#include "cli.h"
#include "cli_parse_utils.h"
#include "compiler.h"

struct config { const char *name; };

static int handle_name(const char *arg, void *cfg) {
	((struct config *)cfg)->name = arg;
	return CLI_SUCCESS;
}

static int handle_help(const char *arg, void *cfg) {
	printf("Usage: myprog [OPTIONS]\n\n");
	cli_print_options(g_opts, NB_OPTS, cli_arg_type_to_str);
	return CLI_EXIT_SUCCESS;
}

#define OPTIONS_LIST \
	X('h', "help", no_argument,			handle_help, OPT_ARG_NONE,   "Display this help") \
	X('n', "name", required_argument,   handle_name, OPT_ARG_STRING, "Your name")

#define X(s, l, a, h, t, d) + 1
enum { NB_OPTS = 0 OPTIONS_LIST };
#undef X

#define X(s, l, a, h, t, d) + (1 + (a != no_argument ? 1 : 0))
enum { OPTSTR_LEN = 1 OPTIONS_LIST };
#undef X

#define X(s, l, a, h, t, d) { s, l, a, h, t, d },
static const struct option_descriptor g_opts[] = { OPTIONS_LIST };
#undef X

int main(int argc, char **argv) {
	struct config cfg = { .name = "world" };
	char opt_str[OPTSTR_LEN + 1];
	struct option long_opts[NB_OPTS + 1];

	cli_set_prog_name(argv[0]);
	if (CLI_SUCCESS != cli_parse(argc, argv, &cfg, g_opts, NB_OPTS, opt_str, long_opts))
		return 1;

	printf("Hello, %s!\n", cfg.name);
	return 0;
}

See example/ for a complete working example.

API

Parsing

enum cli_code cli_parse(int argc, char **argv, void *config,
	const struct option_descriptor *opts, size_t nb_opts,
	char *opt_str, struct option *long_opts);

Parses argv against the descriptor table opts. Calls each option's handler with the raw argument string and config. Returns CLI_SUCCESS, CLI_ERROR, or CLI_EXIT_SUCCESS (handler requested early exit, e.g. --help).

opt_str and long_opts are caller-allocated scratch buffers — size them with the X-macro pattern shown above.

void cli_set_prog_name(const char *name);

Sets the program name used in error messages. Call with argv[0] before cli_parse.

Option descriptor

struct option_descriptor {
	char			 short_opt;		// e.g. 'h'
	const char		*long_opt;		// e.g. "help"
	int				 has_arg;		// no_argument / required_argument / optional_argument
	t_option_handler handler;		// callback: int f(const char *arg, void *config)
	int				 arg_type;		// display hint for cli_print_options
	const char		 *description;  // displayed by cli_print_options
};

Help

void cli_print_options(const struct option_descriptor *opts, size_t nb_opts,
	t_arg_type_to_str to_str);

Prints a formatted options table to stdout. to_str maps arg_type integers to placeholder strings ("<NUM>", "<STR>", etc.).

const char *cli_arg_type_to_str(int type);

Default to_str implementation covering the built-in types. Pass directly to cli_print_options if you don't need custom types.

Return codes

Value Meaning
CLI_SUCCESS Continue — option handled successfully
CLI_ERROR Abort — invalid option or argument
CLI_EXIT_SUCCESS Abort — clean exit requested (e.g. after printing help)

Parse utilities

int cli_parse_uint64(const char *s, uint64_t *out); // non-negative integers
int cli_parse_int64(const char *s, int64_t *out);   // signed integers (use --opt=-N syntax)
int cli_parse_float(const char *s, float *out);     // floats (use --opt=-1.5 syntax)

All return 0 on success, 1 on failure. They reject NULL, empty strings, trailing characters, and out-of-range values.

Custom arg types

The built-in CLI_OPT_ARG_TYPES X-macro covers OPT_ARG_NONE, OPT_ARG_INT, OPT_ARG_UINT, OPT_ARG_FLOAT, OPT_ARG_STRING. To define custom types with their own display strings, declare your own list and generate both the enum and the to_str function from it:

#define MY_ARG_TYPES \
	X(OPT_ARG_NONE,	    "") \
	X(OPT_ARG_UINT,	    "<NUM>") \
	X(OPT_ARG_SECONDS,  "<SEC>") \
	X(OPT_ARG_STRING,   "<STR>")

#define X(name, str) name,
enum { MY_ARG_TYPES };
#undef X

static const char *my_arg_type_to_str(int type) {
	static const char *strs[] = {
		#define X(name, str) str,
		MY_ARG_TYPES
		#undef X
	};
	if ((size_t)type < COUNT_OF(strs))
		return strs[type];
	return "<ARG>";
}

Then pass my_arg_type_to_str to cli_print_options.

Limitations

  • Maximum 64 options

License

GPLv3 — see LICENSE.