Compare commits

...

9 commits

21 changed files with 921 additions and 0 deletions

5
.gitignore vendored
View file

@ -8,6 +8,11 @@
*.o *.o
*.d *.d
*.lo *.lo
.dirstamp
# Automake test results
*.log
*.trs
# Libtool # Libtool
*.la *.la

13
Makefile.am Normal file
View file

@ -0,0 +1,13 @@
ACLOCAL_AMFLAGS = -I m4
SUBDIRS = src example
if BUILD_TESTS
SUBDIRS += tests
endif
pkgincludedir = $(includedir)/cli
pkginclude_HEADERS = \
include/cli.h \
include/cli_parse_utils.h \
include/compiler.h

192
README.md Normal file
View file

@ -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 (`"<NUM>"`, `"<STR>"`, 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, "<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](LICENSE).

3
autogen.sh Executable file
View file

@ -0,0 +1,3 @@
#!/bin/sh
set -e
autoreconf --install --verbose

47
configure.ac Normal file
View file

@ -0,0 +1,47 @@
AC_PREREQ([2.69])
AC_INIT([libcli], [0.1.0], [])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIRS([m4])
AM_INIT_AUTOMAKE([foreign -Wall -Werror subdir-objects])
AC_PROG_CC
AM_PROG_AR
LT_PREREQ([2.2])
LT_INIT
# --enable-tests
AC_ARG_ENABLE([tests],
[AS_HELP_STRING([--enable-tests], [Build Criterion unit tests (default: no)])],
[enable_tests=$enableval],
[enable_tests=no])
AM_CONDITIONAL([BUILD_TESTS], [test "x$enable_tests" = "xyes"])
AS_IF([test "x$enable_tests" = "xyes"], [
PKG_CHECK_MODULES([CRITERION], [criterion], [], [
AC_MSG_NOTICE([pkg-config could not find criterion -- trying manual detection])
AC_CHECK_HEADER([criterion/criterion.h], [], [
AC_MSG_ERROR([criterion/criterion.h not found])])
AC_CHECK_LIB([criterion], [main],
[CRITERION_LIBS="-lcriterion"],
[AC_MSG_ERROR([libcriterion not found])])
])
])
AC_CONFIG_FILES([
Makefile
src/Makefile
tests/Makefile
example/Makefile
])
AC_OUTPUT
AC_MSG_NOTICE([
libcli $VERSION
---------------
prefix : $prefix
CC : $CC
CFLAGS : $CFLAGS
tests : $enable_tests
])

6
example/Makefile.am Normal file
View file

@ -0,0 +1,6 @@
noinst_PROGRAMS = greet
greet_SOURCES = main.c
greet_CPPFLAGS = -I$(top_srcdir)/include
greet_CFLAGS = -std=c99 -Wall -Wextra
greet_LDADD = $(top_builddir)/src/libcli.la

120
example/main.c Normal file
View file

@ -0,0 +1,120 @@
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include "cli.h"
#include "cli_parse_utils.h"
#include "compiler.h"
/* Config */
#define FLAG_VERBOSE (1 << 0)
struct greet_config {
const char *name;
uint64_t count;
uint8_t flags;
};
/* Forward declarations */
static int handle_help(const char *arg, void *cfg);
static int handle_version(const char *arg, void *cfg);
static int handle_verbose(const char *arg, void *cfg);
static int handle_name(const char *arg, void *cfg);
static int handle_count(const char *arg, void *cfg);
/* ------------------- */
/* Option table */
#define GREET_OPTIONS_LIST \
X('h', "help", no_argument, handle_help, OPT_ARG_NONE, "Display this help") \
X('V', "version", no_argument, handle_version, OPT_ARG_NONE, "Display version") \
X('v', "verbose", no_argument, handle_verbose, OPT_ARG_NONE, "Verbose output") \
X('n', "name", required_argument, handle_name, OPT_ARG_STRING, "Name to greet") \
X('c', "count", required_argument, handle_count, OPT_ARG_UINT, "Number of greetings")
#define X(s, l, a, h, t, d) + 1
enum { GREET_NB_OPTS = 0 GREET_OPTIONS_LIST };
#undef X
#define X(s, l, a, h, t, d) + (1 + (a != no_argument ? 1 : 0))
enum { GREET_OPTSTR_LEN = 1 GREET_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[] = { GREET_OPTIONS_LIST };
#undef X
/* Handlers */
static int
handle_help(__unused const char *arg, __unused void *cfg)
{
printf("Usage: greet [OPTIONS]\n\n");
cli_print_options(g_opts, GREET_NB_OPTS, cli_arg_type_to_str);
return CLI_EXIT_SUCCESS;
}
static int
handle_version(__unused const char *arg, __unused void *cfg)
{
printf("greet 0.1.0 (libcli example)\n");
return CLI_EXIT_SUCCESS;
}
static int
handle_verbose(__unused const char *arg, void *cfg)
{
struct greet_config *config = (struct greet_config *)cfg;
SET_FLAG(config->flags, FLAG_VERBOSE);
return CLI_SUCCESS;
}
static int
handle_name(const char *arg, void *cfg)
{
struct greet_config *config = (struct greet_config *)cfg;
config->name = arg;
return CLI_SUCCESS;
}
static int
handle_count(const char *arg, void *cfg)
{
struct greet_config *config = (struct greet_config *)cfg;
if (0 != cli_parse_uint64(arg, &config->count))
return CLI_ERROR;
return CLI_SUCCESS;
}
/* Main */
int
main(int argc, char **argv)
{
struct greet_config config = { .name = "world", .count = 1 };
char opt_str[GREET_OPTSTR_LEN + 1];
struct option long_opts[GREET_NB_OPTS + 1];
enum cli_code ret;
uint64_t i;
cli_set_prog_name(argv[0]);
ret = cli_parse(argc, argv, &config, g_opts, GREET_NB_OPTS,
opt_str, long_opts);
if (CLI_EXIT_SUCCESS == ret)
return EXIT_SUCCESS;
if (CLI_ERROR == ret)
return EXIT_FAILURE;
for (i = 0; i < config.count; i++)
{
if (HAS_FLAG(config.flags, FLAG_VERBOSE))
printf("[%llu] Hello, %s!\n", (unsigned long long)(i + 1),
config.name);
else
printf("Hello, %s!\n", config.name);
}
return EXIT_SUCCESS;
}

48
include/cli.h Normal file
View file

@ -0,0 +1,48 @@
#ifndef CLI_H
#define CLI_H
#include <stddef.h>
#include <getopt.h>
enum cli_code {
CLI_EXIT_SUCCESS = -1,
CLI_SUCCESS = 0,
CLI_ERROR = 1
};
typedef int (*t_option_handler)(const char *arg, void *config);
typedef const char *(*t_arg_type_to_str)(int type);
struct option_descriptor
{
char short_opt;
const char *long_opt;
int has_arg;
t_option_handler handler;
int arg_type;
const char *description;
};
/* Default arg types — clients may define their own */
#define CLI_OPT_ARG_TYPES \
X(OPT_ARG_NONE, "" ) \
X(OPT_ARG_INT, "<NUM>") \
X(OPT_ARG_UINT, "<NUM>") \
X(OPT_ARG_FLOAT, "<NUM>") \
X(OPT_ARG_STRING, "<STR>")
#define X(name, str) name,
enum { CLI_OPT_ARG_TYPES };
#undef X
void cli_set_prog_name(const char *name);
void cli_print_options(const struct option_descriptor *opts, size_t nb_opts,
t_arg_type_to_str to_str);
const char *cli_arg_type_to_str(int type);
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);
#endif

11
include/cli_parse_utils.h Normal file
View file

@ -0,0 +1,11 @@
#ifndef CLI_PARSE_UTILS_H
#define CLI_PARSE_UTILS_H
#include <stdint.h>
int cli_parse_uint64(const char *s, uint64_t *out);
int cli_parse_int64(const char *s, int64_t *out);
int cli_parse_float(const char *s, float *out);
int cli_parse_ufloat(const char *s, float *out);
#endif

16
include/compiler.h Normal file
View file

@ -0,0 +1,16 @@
#ifndef COMPILER_H
#define COMPILER_H
#define __unused __attribute__((unused))
#define COUNT_OF(arr) (sizeof(arr) / sizeof((arr)[0]))
#define STATIC_ARRAY_FOREACH(arr, ptr) \
for ((ptr) = (arr); (ptr) < (arr) + COUNT_OF(arr); (ptr)++)
#define HAS_FLAG(flags, flag) ((flags) & (flag))
#define SET_FLAG(flags, flag) ((flags) |= (flag))
#define CLEAR_FLAG(flags, flag) ((flags) &= ~(flag))
#define TOGGLE_FLAG(flags, flag) ((flags) ^= (flag))
#endif

15
src/Makefile.am Normal file
View file

@ -0,0 +1,15 @@
lib_LTLIBRARIES = libcli.la
libcli_la_SOURCES = \
parse.c \
help.c \
parse_utils/parse_uint.c \
parse_utils/parse_int.c \
parse_utils/parse_float.c \
parse_utils/parse_ufloat.c
libcli_la_CPPFLAGS = -I$(top_srcdir)/include
libcli_la_CFLAGS = -std=c99 -Wall -Wextra
# Libtool versioning: current:revision:age
libcli_la_LDFLAGS = -version-info 0:1:0

34
src/help.c Normal file
View file

@ -0,0 +1,34 @@
#include <stdio.h>
#include <stddef.h>
#include "cli.h"
#include "compiler.h"
const char *
cli_arg_type_to_str(int type)
{
static const char *strs[] = {
#define X(name, str) str,
CLI_OPT_ARG_TYPES
#undef X
};
if ((size_t)type < COUNT_OF(strs))
return strs[type];
return "<ARG>";
}
void
cli_print_options(const struct option_descriptor *opts, size_t nb_opts,
t_arg_type_to_str to_str)
{
printf("Options:\n");
for (size_t i = 0; i < nb_opts; i++)
{
const char *argstr = to_str(opts[i].arg_type);
printf(" -%c, --%-15s %-8s %s\n",
opts[i].short_opt,
opts[i].long_opt,
argstr,
opts[i].description);
}
}

168
src/parse.c Normal file
View file

@ -0,0 +1,168 @@
#include <stdint.h>
#include <stdio.h>
#include <getopt.h>
#include "cli.h"
/* Internal bitmask helpers */
#define HAS_FLAG(flags, flag) ((flags) & (flag))
#define SET_FLAG(flags, flag) ((flags) |= (flag))
/* Map -? to -h to support help shortcut */
#define HANDLE_QUESTION_MARK(opt) \
do { \
if (opt == '?' && (optopt) == '?') \
opt = 'h'; \
} while (0)
/* Forward declarations */
static void build_long_options(struct option *long_opts, size_t nb_opts,
const struct option_descriptor *src);
static void build_optstr(char *dest, const struct option_descriptor *opts,
size_t nb_opts);
static const struct option_descriptor *find_option_handler(int opt,
const struct option_descriptor *opts, size_t nb_opts);
static int handle_one_option(int opt, char **argv, void *config,
uint64_t *opt_tracker,
const struct option_descriptor *opts, size_t nb_opts);
static enum cli_code error_unknown_opt(const char *current_opt);
static enum cli_code error_invalid_opt(const char *current_opt);
static enum cli_code error_duplicate_opt(const char *current_opt);
static void print_suggest_help(void);
/* ------------------- */
static const char *s_prog_name = "";
void
cli_set_prog_name(const char *name)
{
if (NULL != name)
s_prog_name = name;
}
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)
{
uint64_t tracker = 0;
int opt;
int res;
build_optstr(opt_str, opts, nb_opts);
build_long_options(long_opts, nb_opts, opts);
while (-1 != (opt = getopt_long(argc, argv, opt_str, long_opts, NULL)))
{
HANDLE_QUESTION_MARK(opt);
res = handle_one_option(opt, argv, config, &tracker, opts, nb_opts);
if (CLI_SUCCESS != res)
return (enum cli_code)res;
}
return CLI_SUCCESS;
}
static void
build_optstr(char *dest, const struct option_descriptor *opts, size_t nb_opts)
{
size_t str_i = 1;
/* Mute default error messages */
dest[0] = ':';
for (size_t opt_i = 0; opt_i < nb_opts; ++opt_i)
{
dest[str_i++] = opts[opt_i].short_opt;
switch (opts[opt_i].has_arg)
{
case optional_argument: dest[str_i++] = ':'; /* fall through */
case required_argument: dest[str_i++] = ':'; break;
default: break;
}
}
dest[str_i] = '\0';
}
static int
handle_one_option(int opt, char **argv, void *config,
uint64_t *opt_tracker,
const struct option_descriptor *opts, size_t nb_opts)
{
const char *current_opt = argv[optind - 1];
const struct option_descriptor *desc;
size_t bitmask_index;
int res;
if ('?' == opt)
return error_unknown_opt(current_opt);
else if (':' == opt)
return error_invalid_opt(current_opt);
desc = find_option_handler(opt, opts, nb_opts);
if (NULL == desc)
return CLI_ERROR;
bitmask_index = (size_t)(desc - opts);
if (HAS_FLAG(*opt_tracker, (1ULL << bitmask_index)))
return error_duplicate_opt(current_opt);
SET_FLAG(*opt_tracker, (1ULL << bitmask_index));
res = desc->handler(optarg, config);
if (CLI_ERROR == res)
return error_invalid_opt(current_opt);
return res;
}
static void
build_long_options(struct option *long_opts, size_t nb_opts,
const struct option_descriptor *src)
{
for (size_t i = 0; i < nb_opts; ++i)
{
long_opts[i].name = src[i].long_opt;
long_opts[i].has_arg = src[i].has_arg;
long_opts[i].flag = NULL;
long_opts[i].val = src[i].short_opt;
}
long_opts[nb_opts] = (struct option){0};
}
static const struct option_descriptor *
find_option_handler(int opt, const struct option_descriptor *opts,
size_t nb_opts)
{
for (size_t i = 0; i < nb_opts; ++i)
{
if (opts[i].short_opt == opt)
return &opts[i];
}
return NULL;
}
static enum cli_code
error_unknown_opt(const char *current_opt)
{
fprintf(stderr, "%s: unknown option -- '%s'\n", s_prog_name, current_opt);
print_suggest_help();
return CLI_ERROR;
}
static enum cli_code
error_invalid_opt(const char *current_opt)
{
fprintf(stderr, "%s: invalid option '%s'\n", s_prog_name, current_opt);
print_suggest_help();
return CLI_ERROR;
}
static enum cli_code
error_duplicate_opt(const char *current_opt)
{
fprintf(stderr, "%s: duplicate option '%s'\n", s_prog_name, current_opt);
print_suggest_help();
return CLI_ERROR;
}
static void
print_suggest_help(void)
{
fprintf(stderr, "Try '%s --help' for more information.\n", s_prog_name);
}

View file

@ -0,0 +1,21 @@
#include <errno.h>
#include <stdlib.h>
#include <math.h>
int
cli_parse_float(const char *s, float *out)
{
char *end;
if (NULL == s || '\0' == *s)
return 1;
errno = 0;
float v = strtof(s, &end);
if (s == end || ERANGE == errno || '\0' != *end || 0 == isfinite(v))
return 1;
*out = v;
return 0;
}

View file

@ -0,0 +1,22 @@
#include <errno.h>
#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>
int
cli_parse_int64(const char *s, int64_t *out)
{
char *end;
if (NULL == s || '\0' == *s)
return 1;
errno = 0;
const intmax_t v = strtoimax(s, &end, 10);
if (s == end || ERANGE == errno || '\0' != *end || v < INT64_MIN || v > INT64_MAX)
return 1;
*out = (int64_t)v;
return 0;
}

View file

@ -0,0 +1,21 @@
#include <errno.h>
#include <stdlib.h>
#include <math.h>
int
cli_parse_ufloat(const char *s, float *out)
{
char *end;
if (NULL == s || '\0' == *s)
return 1;
errno = 0;
float v = strtof(s, &end);
if (s == end || ERANGE == errno || '\0' != *end || 0 == isfinite(v) || v < 0.0f)
return 1;
*out = v;
return 0;
}

View file

@ -0,0 +1,21 @@
#include <errno.h>
#include <inttypes.h>
#include <stddef.h>
int
cli_parse_uint64(const char *s, uint64_t *out)
{
char *end;
if ( NULL == s || '\0' == *s || '-' == *s)
return 1;
errno = 0;
const uintmax_t v = (uintmax_t)strtoumax(s, &end, 10);
if (s == end || ERANGE == errno || '\0' != *end || v > UINT64_MAX)
return 1;
*out = (uint64_t)v;
return 0;
}

15
tests/Makefile.am Normal file
View file

@ -0,0 +1,15 @@
AM_CPPFLAGS = -I$(top_srcdir)/include $(CRITERION_CFLAGS)
AM_CFLAGS = -std=c99
check_PROGRAMS = test_cli
test_cli_SOURCES = \
test_main.c \
parse_utils/test_parse_int.c \
parse_utils/test_parse_float.c
test_cli_LDADD = \
$(top_builddir)/src/libcli.la \
$(CRITERION_LIBS)
TESTS = test_cli

View file

@ -0,0 +1,69 @@
#include <criterion/criterion.h>
#include "cli_parse_utils.h"
/* Test 1: Valid simple number */
Test(parse_float, valid_number)
{
float result;
int ret = cli_parse_float("42.42", &result);
cr_assert_eq(ret, 0);
cr_assert_float_eq(result, 42.42, 0.0001);
}
/* Test 2: Zero */
Test(parse_float, zero)
{
float result;
int ret = cli_parse_float("0", &result);
cr_assert_eq(ret, 0);
cr_assert_float_eq(result, 0, 0.0001);
}
/* Test 3: NULL pointer */
Test(parse_float, null_pointer)
{
float result;
int ret = cli_parse_float(NULL, &result);
cr_assert_eq(ret, 1);
}
/* Test 4: Empty string */
Test(parse_float, empty_string)
{
float result;
int ret = cli_parse_float("", &result);
cr_assert_eq(ret, 1);
}
/* Test 5: Negative number (cli_parse_float accepts negatives; use cli_parse_ufloat to reject) */
Test(parse_float, negative_number)
{
float result;
int ret = cli_parse_float("-42", &result);
cr_assert_eq(ret, 0);
cr_assert_float_eq(result, -42.0f, 0.0001f);
}
/* Test 6: Invalid characters */
Test(parse_float, invalid_characters)
{
float result;
int ret = cli_parse_float("42abc", &result);
cr_assert_eq(ret, 1);
}
/* Test 7: Dot + number */
Test(parse_float, dot)
{
float result;
int ret = cli_parse_float(".42", &result);
cr_assert_eq(ret, 0);
cr_assert_float_eq(result, 0.42, 0.0001);
}

View file

@ -0,0 +1,68 @@
#include <criterion/criterion.h>
#include <stdint.h>
#include "cli_parse_utils.h"
/* Test 1: Valid simple number */
Test(parse_uint64, valid_number)
{
uint64_t result;
int ret = cli_parse_uint64("42", &result);
cr_assert_eq(ret, 0);
cr_assert_eq(result, 42);
}
/* Test 2: Zero */
Test(parse_uint64, zero)
{
uint64_t result;
int ret = cli_parse_uint64("0", &result);
cr_assert_eq(ret, 0);
cr_assert_eq(result, 0);
}
/* Test 3: NULL pointer */
Test(parse_uint64, null_pointer)
{
uint64_t result;
int ret = cli_parse_uint64(NULL, &result);
cr_assert_eq(ret, 1);
}
/* Test 4: Empty string */
Test(parse_uint64, empty_string)
{
uint64_t result;
int ret = cli_parse_uint64("", &result);
cr_assert_eq(ret, 1);
}
/* Test 5: Negative number */
Test(parse_uint64, negative_number)
{
uint64_t result;
int ret = cli_parse_uint64("-42", &result);
cr_assert_eq(ret, 1);
}
/* Test 6: Invalid characters */
Test(parse_uint64, invalid_characters)
{
uint64_t result;
int ret = cli_parse_uint64("42abc", &result);
cr_assert_eq(ret, 1);
}
/* Test 7: Overflow */
Test(parse_uint64, overflow)
{
uint64_t result;
int ret = cli_parse_uint64("18446744073709551616", &result);
cr_assert_eq(ret, 1);
}

6
tests/test_main.c Normal file
View file

@ -0,0 +1,6 @@
#include <criterion/criterion.h>
Test(dummy, always_pass)
{
cr_assert(1, "Hello, world!");
}