aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLion Kortlepel <[email protected]>2026-01-20 23:29:43 +0100
committerLion Kortlepel <[email protected]>2026-01-20 23:29:43 +0100
commit4ed5f7e5d99885f445ec70671779e60efa1bcbcc (patch)
tree93dd195e52b61adbbc9339d337f4a6edeee4fbeb
downloadargs-4ed5f7e5d99885f445ec70671779e60efa1bcbcc.tar.zst
args-4ed5f7e5d99885f445ec70671779e60efa1bcbcc.zip
initial commit
-rw-r--r--.clang-format4
-rw-r--r--.clangd3
-rw-r--r--.gitignore5
-rw-r--r--LICENSE21
-rw-r--r--Makefile21
-rw-r--r--ls_args.h262
-rw-r--r--tests/ls_test.h288
-rw-r--r--tests/tests.c27
8 files changed, 631 insertions, 0 deletions
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..a65181b
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,4 @@
+BasedOnStyle: WebKit
+BreakBeforeBraces: Attach
+SpaceAfterTemplateKeyword: false
+ColumnLimit: 80
diff --git a/.clangd b/.clangd
new file mode 100644
index 0000000..3f26b1c
--- /dev/null
+++ b/.clangd
@@ -0,0 +1,3 @@
+CompileFlags:
+ CompilationDatabase: .
+ Add: [-x, c]
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..45b6c28
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+tests/tests
+.cache/
+*.o
+compile_commands.json
+coverage/
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..65fc554
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2026 Lion Kortlepel <[email protected]>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..9381484
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,21 @@
+# CAUTION: This Makefile builds ONLY the tests.
+# To use this library, see ls_test.h or README.md.
+
+tests/tests: ls_args.o tests/tests.c tests/ls_test.h
+ $(CC) -o $@ ls_args.o tests/tests.c -Itests -I. -ggdb
+
+# Usually you wouldn't do this, but for tests we want this compiled with the
+# most pedantic settings.
+# Dont use this.
+ls_args.o: ls_args.h
+ $(CC) -c -x c -o $@ $^ -Wall -Wextra -Wpedantic -Werror -std=c89 -ggdb \
+ -Wno-error=pragma-once-outside-header \
+ -I. \
+ -DLS_ARGS_IMPLEMENTATION \
+ -Wno-pragma-once-outside-header
+
+.PHONY: clean
+
+clean:
+ rm -f tests/tests
+ rm -f ls_args.o
diff --git a/ls_args.h b/ls_args.h
new file mode 100644
index 0000000..fc23134
--- /dev/null
+++ b/ls_args.h
@@ -0,0 +1,262 @@
+#pragma once
+
+#include <stddef.h>
+
+#ifndef LS_REALLOC
+#include <stdlib.h>
+#define LS_REALLOC realloc
+#endif
+#ifndef LS_FREE
+#include <stdlib.h>
+#define LS_FREE free
+#endif
+
+/* Yes the naming is a little inconsistent, but "arg optional" reads better than
+ * "args mode optional" */
+typedef enum ls_args_mode {
+ LS_ARG_OPTIONAL = 0,
+ LS_ARG_REQUIRED = 1
+} ls_args_mode;
+
+typedef enum ls_args_type { LS_ARGS_TYPE_BOOL = 0 } ls_args_type;
+
+typedef struct ls_args_arg {
+ const char* short_opt;
+ const char* long_opt;
+ const char* help;
+ ls_args_type type;
+ void* val_ptr;
+ ls_args_mode mode;
+} ls_args_arg;
+
+typedef struct ls_args {
+ ls_args_arg* args;
+ int args_len;
+ int args_cap;
+} ls_args;
+
+void ls_args_init(ls_args*);
+
+void ls_arg_bool(ls_args*, int* val, const char* short_opt,
+ const char* long_opt, const char* help, ls_args_mode mode);
+
+int ls_args_parse(ls_args*, int argc, char** argv);
+
+char* ls_args_help(ls_args*);
+
+void ls_args_free(ls_args*);
+
+#ifdef LS_ARGS_IMPLEMENTATION
+
+#include <assert.h>
+#include <limits.h>
+#include <string.h>
+
+/* 0 on failure, 1 on success */
+static int _lsa_add(ls_args* a, ls_args_arg** arg) {
+ /* a is already checked when this is called */
+ assert(arg != NULL);
+ if (a->args_len + 1 > a->args_cap) {
+ ls_args_arg* new_args;
+ int new_cap = a->args_cap + a->args_cap / 2 + 8;
+ if (new_cap > INT_MAX / (int)sizeof(*a->args)) {
+ /* int overflow */
+ return 0;
+ }
+ new_args = LS_REALLOC(a->args, new_cap * sizeof(*new_args));
+ if (new_args == NULL) {
+ /* allocation failure */
+ return 0;
+ }
+ a->args_cap = new_cap;
+ a->args = new_args;
+ }
+ *arg = &a->args[a->args_len++];
+ return 1;
+}
+
+void ls_args_init(ls_args* a) { memset(a, 0, sizeof(*a)); }
+
+void _lsa_register(ls_args* a, void* val, ls_args_type type,
+ const char* short_opt, const char* long_opt, const char* help,
+ ls_args_mode mode) {
+ ls_args_arg* arg;
+ assert(a != NULL);
+ assert(val != NULL);
+ /* only one can be NULL, not both, but neither have to be NULL */
+ assert(short_opt != NULL || long_opt != NULL);
+ /* if short_opt isn't null, it must be 1 char */
+ assert(short_opt == NULL || strlen(short_opt) == 1);
+ assert(_lsa_add(a, &arg));
+ /* TODO: sanity check that there are no dashes in there, because that would
+ * be a misuse of the API. */
+ /* remove preceding dashes for later matching */
+ while (*long_opt == '-')
+ long_opt++;
+ /* remove preceding dashes for later matching */
+ while (*short_opt == '-')
+ short_opt++;
+ /* the rest may be NULL */
+ arg->type = type;
+ arg->short_opt = short_opt;
+ arg->long_opt = long_opt;
+ arg->help = help;
+ arg->mode = mode;
+ arg->val_ptr = val;
+}
+
+typedef enum _lsa_parsed_type {
+ LS_ARGS_PARSED_ERROR = 0,
+ LS_ARGS_PARSED_LONG = 1,
+ LS_ARGS_PARSED_SHORT = 2,
+ LS_ARGS_PARSED_STOP = 3,
+ LS_ARGS_PARSED_POSITIONAL = 4
+} _lsa_parsed_type;
+
+typedef struct _lsa_parsed {
+ _lsa_parsed_type type;
+ union {
+ /* the full argument that caused the error */
+ const char* erroneous;
+ /* the long arg without the `--` */
+ const char* long_arg;
+ /* might be multiple, like for -abc it would be `abc` */
+ const char* short_args;
+ /* an argument provided without `--`, in full */
+ const char* positional;
+ } as;
+} _lsa_parsed;
+
+_lsa_parsed _lsa_parse(const char* s) {
+ size_t s_len = strlen(s);
+ _lsa_parsed res;
+ assert(s != NULL);
+ /* empty string or `-` */
+ if (s_len < 2) {
+ res.type = LS_ARGS_PARSED_ERROR;
+ res.as.erroneous = s;
+ goto end;
+ }
+ if (s[0] == '-') {
+ if (s[1] == '-') {
+ /* long opt */
+ size_t remaining = s_len - 2;
+ if (remaining == 0) {
+ /* special case where `--` is provided on its own to signal
+ * "everything after this is positional" */
+ res.type = LS_ARGS_PARSED_STOP;
+ goto end;
+ }
+ res.type = LS_ARGS_PARSED_LONG;
+ res.as.long_arg = &s[remaining];
+ } else {
+ /* short opt */
+ size_t remaining = s_len - 1;
+ if (remaining == 0) {
+ /* shouldn't be possible due to earlier checks */
+ res.type = LS_ARGS_PARSED_ERROR;
+ res.as.erroneous = s;
+ goto end;
+ }
+ res.type = LS_ARGS_PARSED_SHORT;
+ res.as.short_args = &s[remaining];
+ }
+ } else {
+ res.type = LS_ARGS_PARSED_POSITIONAL;
+ res.as.positional = s;
+ }
+
+end:
+ return res;
+}
+
+void ls_arg_bool(ls_args* a, int* val, const char* short_opt,
+ const char* long_opt, const char* help, ls_args_mode mode) {
+ _lsa_register(a, val, LS_ARGS_TYPE_BOOL, short_opt, long_opt, help, mode);
+}
+
+void _lsa_apply(ls_args_arg* arg, _lsa_parsed* parsed, ls_args_arg** prev_arg) {
+ (void)parsed;
+ (void)prev_arg;
+ switch (arg->type) {
+ case LS_ARGS_TYPE_BOOL:
+ *(int*)arg->val_ptr = 1;
+ break;
+ }
+ *prev_arg = arg;
+}
+
+#include <stdio.h>
+
+int ls_args_parse(ls_args* a, int argc, char** argv) {
+ int i;
+ int k;
+ ls_args_arg* prev_arg = NULL;
+ assert(a != NULL);
+ assert(argv != NULL);
+ for (i = 1; i < argc; ++i) {
+ _lsa_parsed parsed = _lsa_parse(argv[i]);
+ if (prev_arg && prev_arg->type != LS_ARGS_TYPE_BOOL) {
+ if (parsed.type != LS_ARGS_PARSED_POSITIONAL) {
+ /* argument for the previous param expected, but none given */
+ /* TODO: Error properly */
+ fprintf(stderr, "Expected argument for '--%s'\n",
+ prev_arg->long_opt);
+ return 0;
+ }
+ _lsa_apply(prev_arg, &parsed, NULL);
+ continue;
+ }
+ switch (parsed.type) {
+ case LS_ARGS_PARSED_ERROR:
+ /* TODO: Return/print/save error */
+ fprintf(stderr, "Failed to parse '%s'\n", parsed.as.erroneous);
+ return 0;
+ case LS_ARGS_PARSED_LONG:
+ for (k = 0; k < a->args_len; ++k) {
+ if (a->args[k].long_opt != NULL
+ && strcmp(argv[i], a->args[k].long_opt) == 0) {
+ /* match! */
+ _lsa_apply(&a->args[k], &parsed, &prev_arg);
+ break;
+ }
+ }
+ break;
+ case LS_ARGS_PARSED_SHORT: {
+ const char* args = parsed.as.short_args;
+ while (*args) {
+ char arg = *args++;
+ for (k = 0; k < a->args_len; ++k) {
+ const char* opt = a->args[k].short_opt;
+ if (opt != NULL && opt[0] == arg) {
+ /* match! */
+ _lsa_apply(&a->args[k], &parsed, &prev_arg);
+ break;
+ }
+ }
+ }
+ break;
+ }
+ case LS_ARGS_PARSED_STOP:
+ case LS_ARGS_PARSED_POSITIONAL:
+ assert(!"NOT IMPLEMENTED");
+ break;
+ }
+ }
+ return 1;
+}
+
+char* ls_args_help(ls_args* a) {
+ (void)a;
+ return "help!";
+}
+
+void ls_args_free(ls_args* a) {
+ if (a) {
+ LS_FREE(a->args);
+ a->args = NULL;
+ a->args_cap = 0;
+ a->args_len = 0;
+ }
+}
+#endif
diff --git a/tests/ls_test.h b/tests/ls_test.h
new file mode 100644
index 0000000..f9f4fe9
--- /dev/null
+++ b/tests/ls_test.h
@@ -0,0 +1,288 @@
+/* Lion's Standard (LS) test harness.
+ *
+ * Version: 1.0
+ * Website: https://libls.org
+ * Repo: https://github.com/libls/test
+ * SPDX-License-Identifier: MIT
+ *
+ * ==== TABLE OF CONTENTS ====
+ *
+ * 1. DESCRIPTION
+ * 2. HOW TO USE
+ * 3. LICENSE
+ *
+ * ==== 1. DESCRIPTION ====
+ *
+ * This is a super simple, minimal unit-test harness. It has auto-registering
+ * tests and some macros for easy usage.
+ *
+ * Compiles under ANSI C, the only special part is the extension __typeof__ if
+ * you use asserts other than `ASSERT` (e.g. ASSERT_EQ), and the constructor
+ * attribute __attribute__((destructor)) for automatic test registration.
+ *
+ * ==== 2. HOW TO USE ====
+ *
+ * 1. Copy this file into your project and include it:
+ *
+ * #include "ls_test.h"
+ *
+ * 2. In ONE C file, define LS_TEST_IMPLEMENTATION before including:
+ *
+ * #define LS_TEST_IMPLEMENTATION
+ * #include "ls_test.h"
+ *
+ * 3. Write tests as functions with no arguments/returns:
+ *
+ * TEST_CASE(test_add) {
+ * ASSERT_EQ(add(1, 2), 3, "%d");
+ * return 0; // needed for --failfast
+ * }
+ *
+ * Use unique names, avoid starting with ls_ or lst_.
+ *
+ * 4. Add a main:
+ *
+ * TEST_MAIN
+ *
+ * 5. Compile and run. Use --help for options.
+ *
+ * ==== 3. LICENSE ====
+ *
+ * This file is provided under the MIT license. For commercial support and
+ * maintenance, feel free to use the e-mail below to contact the author(s).
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2026 Lion Kortlepel <[email protected]>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the “Software”), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#pragma once
+
+#include <stdio.h>
+
+#define TEST_MAIN \
+ int main(int argc, char** argv) { return ls_test_main(argc, argv); }
+
+#define TEST_CASE(name) \
+ static int LS_CAT(lst_t_, name)(void); \
+ static void LS_CAT(lst_init_, name)(void) LS_CONSTRUCTOR; \
+ static void LS_CAT(lst_init_, name)(void) { \
+ lst_reg(LS_CAT(lst_t_, name)); \
+ } \
+ static int LS_CAT(lst_t_, name)(void)
+
+#define ASSERT(cond) \
+ do { \
+ if (!(cond)) { \
+ const char* _func = __func__; \
+ if (strncmp(_func, "lst_t_", 6) == 0) \
+ _func += 6; \
+ fprintf(stderr, "%s: FAILED: %s (%s:%d)\n", _func, #cond, \
+ __FILE__, __LINE__); \
+ } \
+ } while (0)
+
+/* the following macros require __typeof__ */
+
+#define ASSERT_EQ(a, b, fmt) \
+ do { \
+ __typeof__(a) _a = (a); \
+ __typeof__(b) _b = (b); \
+ if (_a != _b) { \
+ const char* _func = __func__; \
+ if (strncmp(_func, "lst_t_", 6) == 0) \
+ _func += 6; \
+ fprintf(stderr, \
+ "%s: FAILED: %s == %s (actual: " fmt " != " fmt ") (%s:%d)\n", \
+ _func, #a, #b, _a, _b, __FILE__, __LINE__); \
+ ++lst_fail; \
+ return 1; \
+ } \
+ ++lst_ok; \
+ } while (0)
+
+#define ASSERT_NEQ(a, b, fmt) \
+ do { \
+ __typeof__(a) _a = (a); \
+ __typeof__(b) _b = (b); \
+ if (_a == _b) { \
+ const char* _func = __func__; \
+ if (strncmp(_func, "lst_t_", 6) == 0) \
+ _func += 6; \
+ fprintf(stderr, \
+ "%s: FAILED: %s != %s (actual: " fmt " == " fmt ") (%s:%d)\n", \
+ _func, #a, #b, _a, _b, __FILE__, __LINE__); \
+ ++lst_fail; \
+ return 1; \
+ } \
+ ++lst_ok; \
+ } while (0)
+
+#define ASSERT_LT(a, b, fmt) \
+ do { \
+ __typeof__(a) _a = (a); \
+ __typeof__(b) _b = (b); \
+ if (!(_a < _b)) { \
+ const char* _func = __func__; \
+ if (strncmp(_func, "lst_t_", 6) == 0) \
+ _func += 6; \
+ fprintf(stderr, \
+ "%s: FAILED: %s < %s (actual: " fmt " >= " fmt ") (%s:%d)\n", \
+ _func, #a, #b, _a, _b, __FILE__, __LINE__); \
+ ++lst_fail; \
+ return 1; \
+ } \
+ ++lst_ok; \
+ } while (0)
+
+#define ASSERT_LE(a, b, fmt) \
+ do { \
+ __typeof__(a) _a = (a); \
+ __typeof__(b) _b = (b); \
+ if (!(_a <= _b)) { \
+ const char* _func = __func__; \
+ if (strncmp(_func, "lst_t_", 6) == 0) \
+ _func += 6; \
+ fprintf(stderr, \
+ "%s: FAILED: %s <= %s (actual: " fmt " > " fmt ") (%s:%d)\n", \
+ _func, #a, #b, _a, _b, __FILE__, __LINE__); \
+ ++lst_fail; \
+ return 1; \
+ } \
+ ++lst_ok; \
+ } while (0)
+
+#define ASSERT_GT(a, b, fmt) \
+ do { \
+ __typeof__(a) _a = (a); \
+ __typeof__(b) _b = (b); \
+ if (!(_a > _b)) { \
+ const char* _func = __func__; \
+ if (strncmp(_func, "lst_t_", 6) == 0) \
+ _func += 6; \
+ fprintf(stderr, \
+ "%s: FAILED: %s > %s (actual: " fmt " <= " fmt ") (%s:%d)\n", \
+ _func, #a, #b, _a, _b, __FILE__, __LINE__); \
+ ++lst_fail; \
+ return 1; \
+ } \
+ ++lst_ok; \
+ } while (0)
+
+#define ASSERT_GE(a, b, fmt) \
+ do { \
+ __typeof__(a) _a = (a); \
+ __typeof__(b) _b = (b); \
+ if (!(_a >= _b)) { \
+ const char* _func = __func__; \
+ if (strncmp(_func, "lst_t_", 6) == 0) \
+ _func += 6; \
+ fprintf(stderr, \
+ "%s: FAILED: %s >= %s (actual: " fmt " < " fmt ") (%s:%d)\n", \
+ _func, #a, #b, _a, _b, __FILE__, __LINE__); \
+ ++lst_fail; \
+ return 1; \
+ } \
+ ++lst_ok; \
+ } while (0)
+#define LS_CAT2(a, b) a##b
+#define LS_CAT(a, b) LS_CAT2(a, b)
+
+#if defined(__GNUC__) || defined(__clang__)
+#define LS_CONSTRUCTOR __attribute__((constructor))
+#else
+#error "Requires __attribute__((constructor)) support"
+#endif
+
+typedef int (*lst_func)(void);
+
+extern lst_func* lst_funcs;
+extern int lst_n;
+extern int lst_cap;
+extern int lst_fail;
+extern int lst_ok;
+
+void lst_reg(lst_func f);
+
+#ifdef LS_TEST_IMPLEMENTATION
+#include <stdlib.h>
+#include <string.h>
+
+lst_func* lst_funcs;
+int lst_n;
+int lst_cap;
+int lst_fail = 0;
+int lst_ok = 0;
+
+void lst_reg(lst_func f) {
+ if (lst_n == lst_cap) {
+ if (lst_cap == 0) {
+ lst_cap = 8;
+ } else {
+ lst_cap *= 2;
+ }
+ lst_funcs = (lst_func*)realloc(lst_funcs, lst_cap * sizeof(*lst_funcs));
+ }
+ lst_funcs[lst_n++] = f;
+}
+
+#define HELP_STR \
+ "Usage: %s [options]\n" \
+ "Options:\n" \
+ " --failfast Stop after the first failed test\n" \
+ " --help Show this help message\n"
+
+static int ls_test_main(int argc, char** argv) {
+ (void)argc;
+ (void)argv;
+
+ int failfast = 0;
+ int i;
+
+ for (i = 1; i < argc; ++i) {
+ if (strcmp(argv[i], "--failfast") == 0) {
+ failfast = 1;
+ } else if (strcmp(argv[i], "--help") == 0) {
+ fprintf(stderr, HELP_STR, argv[0]);
+ return 0;
+ } else {
+ fprintf(
+ stderr, "unknown argument: %s\n\n" HELP_STR, argv[i], argv[0]);
+ return 1;
+ }
+ }
+
+ for (i = 0; i < lst_n; ++i) {
+ if (lst_funcs[i]() != 0 && failfast) {
+ goto end;
+ }
+ }
+
+end:
+ fprintf(stderr, "%d succeeded, %d failed, %d total\n", lst_ok, lst_fail,
+ lst_ok + lst_fail);
+
+ if (lst_fail > 0) {
+ return 1;
+ }
+
+ return 0;
+}
+#endif
diff --git a/tests/tests.c b/tests/tests.c
new file mode 100644
index 0000000..0e644e8
--- /dev/null
+++ b/tests/tests.c
@@ -0,0 +1,27 @@
+#define LS_TEST_IMPLEMENTATION
+#include "ls_test.h"
+
+#include "ls_args.h"
+
+TEST_CASE(basic_args) {
+ int help = 0;
+ int test = 0;
+ int no = 0;
+ ls_args args;
+ char* argv[] = { "./hello", "-h", "--test", NULL };
+ int argc = sizeof(argv) / sizeof(*argv) - 1;
+
+ ls_args_init(&args);
+ ls_arg_bool(&args, &help, "h", "help", "Provides help", 0);
+ ls_arg_bool(&args, &test, "t", "test", "A test argument", 0);
+ ls_arg_bool(&args, &no, "n", "nope", "An argument that isn't present", 0);
+ ASSERT(ls_args_parse(&args, argc, argv));
+ ASSERT_EQ(help, 1, "%d");
+ ASSERT_EQ(test, 1, "%d");
+ ASSERT_EQ(no, 0, "%d");
+ ls_args_free(&args);
+ return 0;
+}
+
+
+TEST_MAIN