# 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`.

------------------------------------------------------------
