From 960ed4584fff6749a1c81c6085d6b3238bb502e8 Mon Sep 17 00:00:00 2001 From: lohhiiccc <96543753+lohhiiccc@users.noreply.github.com> Date: Sun, 29 Mar 2026 03:46:05 +0200 Subject: [PATCH] docs: add README --- README.md | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..f973962 --- /dev/null +++ b/README.md @@ -0,0 +1,192 @@ +# 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 + +- GCC (uses `__attribute__((unused))`) +- autoconf ≥ 2.69, automake, libtool +- [Criterion](https://github.com/Snaipe/Criterion) — optional, required for + `--enable-tests` + +## Build + +```sh +./autogen.sh +./configure +make +make install +``` + +To build and run the test suite: + +```sh +./configure --enable-tests +make check +``` + +## Quickstart + +```c +#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 + +```c +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. + +```c +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 + +```c +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 + +```c +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 (`""`, `""`, etc.). + +```c +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 + +```c +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: + +```c +#define MY_ARG_TYPES \ + X(OPT_ARG_NONE, "") \ + X(OPT_ARG_UINT, "") \ + X(OPT_ARG_SECONDS, "") \ + X(OPT_ARG_STRING, "") + +#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 ""; +} +``` + +Then pass `my_arg_type_to_str` to `cli_print_options`. + +## Limitations + +- Maximum 64 options + +## License + +GPLv3 — see [LICENSE](LICENSE).