summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsanine <sanine.not@pm.me>2022-08-24 00:02:17 -0500
committersanine <sanine.not@pm.me>2022-08-24 00:02:17 -0500
commit2cb3c3df4099297b0a0554bb482e2de04fe86b5c (patch)
tree7796b4064c16460d9d603707b5256027649aa8b6
parent709e1b6e1ce86f8da4fc136747fcefbc6c6057bd (diff)
add command-line arguments
-rw-r--r--CMakeLists.txt4
-rw-r--r--demo/.honey.lua.swobin20480 -> 0 bytes
-rw-r--r--libs/cargs/.appveyor.yml29
-rw-r--r--libs/cargs/.clang-format38
-rw-r--r--libs/cargs/.codecov.yml2
-rw-r--r--libs/cargs/.gitignore13
-rw-r--r--libs/cargs/.travis.yml32
-rw-r--r--libs/cargs/CMakeLists.txt105
-rw-r--r--libs/cargs/CONTRIBUTING.md30
-rw-r--r--libs/cargs/LICENSE.md21
-rw-r--r--libs/cargs/README.md153
-rwxr-xr-xlibs/cargs/banner.pngbin0 -> 17345 bytes
-rwxr-xr-xlibs/cargs/banner.svg36
-rw-r--r--libs/cargs/cmake/CargsConfig.cmake.in5
-rw-r--r--libs/cargs/cmake/CreateTestList.cmake11
-rwxr-xr-xlibs/cargs/cmake/EnableWarnings.cmake19
-rwxr-xr-xlibs/cargs/demo/CMakeLists.txt13
-rwxr-xr-xlibs/cargs/demo/main.c91
-rw-r--r--libs/cargs/docs/_config.yml6
-rw-r--r--libs/cargs/docs/_layouts/default.html42
-rw-r--r--libs/cargs/docs/assets/css/default.css127
-rw-r--r--libs/cargs/docs/assets/css/github.css209
-rw-r--r--libs/cargs/docs/assets/css/monakai.css210
-rw-r--r--libs/cargs/docs/assets/css/trac.css210
-rw-r--r--libs/cargs/docs/assets/css/vim.css1
-rw-r--r--libs/cargs/docs/assets/img/favicon.pngbin0 -> 1584 bytes
-rw-r--r--libs/cargs/docs/assets/img/favicon.svg125
-rw-r--r--libs/cargs/docs/build.md49
-rw-r--r--libs/cargs/docs/embed.md47
-rw-r--r--libs/cargs/docs/index.md129
-rwxr-xr-xlibs/cargs/docs/reference/cag_option_fetch.md32
-rwxr-xr-xlibs/cargs/docs/reference/cag_option_get.md26
-rwxr-xr-xlibs/cargs/docs/reference/cag_option_get_index.md29
-rwxr-xr-xlibs/cargs/docs/reference/cag_option_get_value.md27
-rw-r--r--libs/cargs/docs/reference/cag_option_prepare.md30
-rwxr-xr-xlibs/cargs/docs/reference/cag_option_print.md28
-rw-r--r--libs/cargs/docs/reference/index.md30
-rw-r--r--libs/cargs/include/cargs.h162
-rw-r--r--libs/cargs/meson.build19
-rw-r--r--libs/cargs/src/cargs.c437
-rwxr-xr-xlibs/cargs/test/definitions.h9
-rwxr-xr-xlibs/cargs/test/main.c105
-rwxr-xr-xlibs/cargs/test/option_test.c665
-rw-r--r--src/gl/gl.c25
-rw-r--r--src/glm/glm.c1
-rw-r--r--src/main.c18
-rw-r--r--src/options/CMakeLists.txt5
-rw-r--r--src/options/options.c56
-rw-r--r--src/options/options.h16
49 files changed, 3474 insertions, 3 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ea8fca3..b428197 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -32,12 +32,13 @@ add_subdirectory(${LIB_ROOT}/lua-5.1.5)
add_subdirectory(${LIB_ROOT}/honeysuckle)
add_subdirectory(${LIB_ROOT}/cglm)
add_subdirectory(${LIB_ROOT}/glfw-3.3.8)
+add_subdirectory(${LIB_ROOT}/cargs)
set(HONEY_SOURCE ${SRC_ROOT}/main.c)
add_executable(honey ${HONEY_SOURCE})
-set(LIBRARIES lua5.1 honeysuckle glfw)
+set(LIBRARIES lua5.1 honeysuckle glfw cargs)
if (WIN32)
set(LIBRARIES ${LIBRARIES} opengl32)
else()
@@ -64,3 +65,4 @@ add_subdirectory(${SRC_ROOT}/image)
add_subdirectory(${SRC_ROOT}/util)
add_subdirectory(${SRC_ROOT}/test)
add_subdirectory(${SRC_ROOT}/glm)
+add_subdirectory(${SRC_ROOT}/options)
diff --git a/demo/.honey.lua.swo b/demo/.honey.lua.swo
deleted file mode 100644
index 73c5d1d..0000000
--- a/demo/.honey.lua.swo
+++ /dev/null
Binary files differ
diff --git a/libs/cargs/.appveyor.yml b/libs/cargs/.appveyor.yml
new file mode 100644
index 0000000..fa64d2d
--- /dev/null
+++ b/libs/cargs/.appveyor.yml
@@ -0,0 +1,29 @@
+version: 0.1.0.{build}
+image: Visual Studio 2017
+configuration: Release
+
+platform:
+ - Win32
+ - x64
+
+clone_folder: c:\projects\cargs
+
+init:
+ - cmd: set arch_str=
+ - cmd: if "%PLATFORM%"=="x64" (set arch_str= Win64)
+ - cmd: echo %PLATFORM%
+ - cmd: echo %APPVEYOR_BUILD_WORKER_IMAGE%
+ - cmd: if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2017" ( set GENERATOR="Visual Studio 15 2017%arch_str%" )
+ - cmd: if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2015" ( set GENERATOR="Visual Studio 14 2015%arch_str%" )
+ - cmd: if "%APPVEYOR_BUILD_WORKER_IMAGE%"=="Visual Studio 2013" ( set GENERATOR="Visual Studio 12 2013%arch_str%" )
+ - cmd: echo %GENERATOR%
+
+before_build:
+ - cmd: mkdir build.win && cd build.win && cmake --version && cmake .. -G %GENERATOR% -DENABLE_TESTS=1
+
+build:
+ project: c:\projects\cargs\build.win\cargs.sln
+ verbosity: minimal
+
+test_script:
+ - cmd: c:\projects\cargs\build.win\Release\cargstest.exe
diff --git a/libs/cargs/.clang-format b/libs/cargs/.clang-format
new file mode 100644
index 0000000..eec6156
--- /dev/null
+++ b/libs/cargs/.clang-format
@@ -0,0 +1,38 @@
+---
+BasedOnStyle: LLVM
+AlignAfterOpenBracket: DontAlign
+BinPackArguments: true
+BinPackParameters: true
+BreakBeforeBraces: Custom
+IndentWidth: 2
+ContinuationIndentWidth: 2
+AllowAllParametersOfDeclarationOnNextLine: false
+AllowAllArgumentsOnNextLine: false
+AllowShortBlocksOnASingleLine: 'Never'
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: 'None'
+AllowShortIfStatementsOnASingleLine: 'false'
+AllowShortLoopsOnASingleLine: false
+PenaltyBreakAssignment: 10000
+PenaltyBreakBeforeFirstCallParameter: 100000
+IndentExternBlock: false
+BraceWrapping:
+ AfterExternBlock: false
+ AfterCaseLabel: false
+ AfterClass: true
+ AfterControlStatement: Never
+ AfterEnum: true
+ AfterFunction: true
+ AfterNamespace: false
+ AfterObjCDeclaration: false
+ AfterStruct: true
+ AfterUnion: true
+ AfterExternBlock: true
+ BeforeCatch: false
+ BeforeElse: false
+ BeforeLambdaBody: false
+ BeforeWhile: false
+ IndentBraces: false
+ SplitEmptyFunction: true
+ SplitEmptyRecord: false
+ SplitEmptyNamespace: true
diff --git a/libs/cargs/.codecov.yml b/libs/cargs/.codecov.yml
new file mode 100644
index 0000000..2515756
--- /dev/null
+++ b/libs/cargs/.codecov.yml
@@ -0,0 +1,2 @@
+ignore:
+ - "test" \ No newline at end of file
diff --git a/libs/cargs/.gitignore b/libs/cargs/.gitignore
new file mode 100644
index 0000000..2b8f3c4
--- /dev/null
+++ b/libs/cargs/.gitignore
@@ -0,0 +1,13 @@
+test/tests.h
+.idea
+cmake-build-debug
+cmake-build-release
+build
+build.win
+builddir
+.vs
+.vscode
+docs/_site
+docs/.jekyll-cache
+demo/build
+demo/build.win
diff --git a/libs/cargs/.travis.yml b/libs/cargs/.travis.yml
new file mode 100644
index 0000000..31f1511
--- /dev/null
+++ b/libs/cargs/.travis.yml
@@ -0,0 +1,32 @@
+language: c
+
+os:
+- linux
+- osx
+- freebsd
+
+dist: focal
+
+addons:
+ apt:
+ sources:
+ - ubuntu-toolchain-r-test
+ packages:
+ - gcc-7
+ - cmake
+
+compiler:
+- gcc
+- clang
+
+before_script:
+- mkdir build
+- cd build
+- cmake -DENABLE_TESTS=1 -DENABLE_COVERAGE=1 ..
+
+script:
+ - make
+ - make test
+
+after_success:
+- bash <(curl -s https://codecov.io/bash)
diff --git a/libs/cargs/CMakeLists.txt b/libs/cargs/CMakeLists.txt
new file mode 100644
index 0000000..4cd618f
--- /dev/null
+++ b/libs/cargs/CMakeLists.txt
@@ -0,0 +1,105 @@
+cmake_minimum_required(VERSION 3.14.7)
+
+# set project name
+project(cargs
+ VERSION 1.0.3
+ DESCRIPTION "A simple argument parser library"
+ LANGUAGES C)
+
+# include utilities
+list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
+include(EnableWarnings)
+include(CTest)
+include(CreateTestList)
+include(CMakePackageConfigHelpers)
+include(GNUInstallDirs)
+
+# configure requirements
+set(CMAKE_C_STANDARD 11)
+
+# setup target and directory names
+set(LIBRARY_TARGET "cargs")
+set(TEST_TARGET "cargstest")
+set(INCLUDE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include")
+set(SOURCE_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/src")
+set(TEST_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/test")
+
+# enable coverage if requested
+if(ENABLE_COVERAGE)
+ message("-- Coverage enabled")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fprofile-arcs -ftest-coverage")
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage")
+endif()
+
+# enable sanitizer
+if(ENABLE_SANITIZER)
+ message("-- Sanitizer enabled")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer -fsanitize=${ENABLE_SANITIZER}")
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fno-omit-frame-pointer -fsanitize=${ENABLE_SANITIZER}")
+endif()
+
+# add the main library
+add_library(${LIBRARY_TARGET}
+ "${INCLUDE_DIRECTORY}/cargs.h"
+ "${SOURCE_DIRECTORY}/cargs.c")
+enable_warnings(${LIBRARY_TARGET})
+target_include_directories(${LIBRARY_TARGET} PUBLIC
+ $<BUILD_INTERFACE:${INCLUDE_DIRECTORY}>
+ $<INSTALL_INTERFACE:include>
+)
+set_target_properties(cargs PROPERTIES PUBLIC_HEADER "${INCLUDE_DIRECTORY}/cargs.h")
+set_target_properties(cargs PROPERTIES DEFINE_SYMBOL CAG_EXPORTS)
+
+# add shared library macro
+if(BUILD_SHARED_LIBS)
+ target_compile_definitions(cargs PUBLIC CAG_SHARED)
+endif()
+
+# add tests
+if(ENABLE_TESTS)
+ message("-- Tests enabled")
+ enable_testing()
+ create_test(DEFAULT option complex)
+ create_test(DEFAULT option mixed)
+ create_test(DEFAULT option ending)
+ create_test(DEFAULT option long_missing_value)
+ create_test(DEFAULT option short_missing_value)
+ create_test(DEFAULT option long_space_value)
+ create_test(DEFAULT option short_space_value)
+ create_test(DEFAULT option long_equal_value)
+ create_test(DEFAULT option short_equal_value)
+ create_test(DEFAULT option combined)
+ create_test(DEFAULT option unknown_long)
+ create_test(DEFAULT option unknown_short)
+ create_test(DEFAULT option alias)
+ create_test(DEFAULT option simple_long)
+ create_test(DEFAULT option simple)
+ create_test(DEFAULT option print)
+ create_test_list(DEFAULT "${TEST_DIRECTORY}/tests.h")
+
+ add_executable(${TEST_TARGET}
+ "${TEST_DIRECTORY}/main.c"
+ "${TEST_DIRECTORY}/option_test.c")
+ target_link_libraries(${TEST_TARGET} PUBLIC ${LIBRARY_TARGET})
+ target_include_directories(${TEST_TARGET} PUBLIC "${INCLUDE_DIRECTORY}")
+ enable_warnings(${TEST_TARGET})
+endif()
+
+# version file
+configure_package_config_file("cmake/CargsConfig.cmake.in"
+ ${CMAKE_CURRENT_BINARY_DIR}/CargsConfig.cmake
+ INSTALL_DESTINATION ${LIB_INSTALL_DIR}/cargs/cmake)
+write_basic_package_version_file(
+ ${CMAKE_CURRENT_BINARY_DIR}/CargsConfigVersion.cmake
+ COMPATIBILITY SameMajorVersion)
+
+# installing
+install(TARGETS cargs
+ EXPORT CargsTargets)
+
+install(FILES
+ ${CMAKE_CURRENT_BINARY_DIR}/CargsConfig.cmake
+ ${CMAKE_CURRENT_BINARY_DIR}/CargsConfigVersion.cmake
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cargs)
+install(EXPORT CargsTargets
+ DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/cargs)
diff --git a/libs/cargs/CONTRIBUTING.md b/libs/cargs/CONTRIBUTING.md
new file mode 100644
index 0000000..9a5c1ab
--- /dev/null
+++ b/libs/cargs/CONTRIBUTING.md
@@ -0,0 +1,30 @@
+# Contributing
+All your contributions to **cargs** are very welcome! **cargs** is especially happy to
+receive contributions like:
+
+ * general **bugfixes**
+ * simple **bug reports**
+ * **proposing new features**
+ * **questions** (there are no dumb questions)
+
+## License
+**Any contributions you make will be under the MIT Software License.**
+
+In short, when you submit code changes, your submissions are understood to be
+under the same MIT License that covers the project. Feel free to contact the
+maintainers if that's a concern.
+
+## How to report a bug?
+You can just use the issue tracker to do so.
+
+## How to propose a new feature?
+You can just use the issue tracker to do so.
+
+## How to submit a bug fix?
+Just submit a pull-request! Try to make sure that the code style fits the
+surrounding code.
+
+## How to submit a new feature?
+You probably want to create an issue first to discuss the change. All
+pull-requests will be considered though! Just try to make sure that the code
+style fits the surrounding code. \ No newline at end of file
diff --git a/libs/cargs/LICENSE.md b/libs/cargs/LICENSE.md
new file mode 100644
index 0000000..23f2eba
--- /dev/null
+++ b/libs/cargs/LICENSE.md
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 Leonard Iklé
+
+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/libs/cargs/README.md b/libs/cargs/README.md
new file mode 100644
index 0000000..e1a4260
--- /dev/null
+++ b/libs/cargs/README.md
@@ -0,0 +1,153 @@
+<img style="width:100%;" src="banner.png">
+
+[![Travis Build](https://img.shields.io/travis/com/likle/cargs/master?label=Linux%2C%20macOS%20%26%20FreeBSD)](https://app.travis-ci.com/github/likle/cargs)
+[![Appveyor Build](https://img.shields.io/appveyor/ci/likle/cargs/master.svg?label=Windows)](https://ci.appveyor.com/project/likle/cargs)
+[![codecov](https://img.shields.io/codecov/c/github/likle/cargs/master.svg?label=Coverage)](https://codecov.io/gh/likle/cargs)
+[![Language Grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/likle/cargs.svg?label=Code%20Quality)](https://lgtm.com/projects/g/likle/cargs/context:cpp)
+
+# libcargs - command line argument library for C/C++
+This is a lighweight C command line argument library. It is currently compiled and
+tested under **Windows**, **MacOS** and **Linux**.
+
+## Features
+Please have a look at the
+**[reference](https://likle.github.io/cargs/reference/)** for detailed
+information. Some features this library includes:
+
+ * **cross-platform** on windows, linux and macOS
+ * **simple interface** - just one header
+ * **one simple loop** - to iterate over the arguments
+ * **automatic help output** - showing all options available
+ * **long and short options** - giving users alternatives
+ * **option values** - for options which are more than just flags
+
+ ## Building
+ **[Building](https://likle.github.io/cargs/build.html)**,
+ **[embedding](https://likle.github.io/cargs/embed.html)** and
+ **[testing](https://likle.github.io/cargs/build.html)** instructions are
+ available in the documentation (it's very easy).
+
+ ## Docs
+ All the documentation is available in the
+ **[the github page](https://likle.github.io/cargs/)** of this repository.
+
+ ## Example
+ ```c
+#include <cargs.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+/**
+ * This is the main configuration of all options available.
+ */
+static struct cag_option options[] = {
+ {.identifier = 's',
+ .access_letters = "s",
+ .access_name = NULL,
+ .value_name = NULL,
+ .description = "Simple flag"},
+
+ {.identifier = 'm',
+ .access_letters = "mMoO",
+ .access_name = NULL,
+ .value_name = NULL,
+ .description = "Multiple access letters"},
+
+ {.identifier = 'l',
+ .access_letters = NULL,
+ .access_name = "long",
+ .value_name = NULL,
+ .description = "Long parameter name"},
+
+ {.identifier = 'k',
+ .access_letters = "k",
+ .access_name = "key",
+ .value_name = "VALUE",
+ .description = "Parameter value"},
+
+ {.identifier = 'h',
+ .access_letters = "h",
+ .access_name = "help",
+ .description = "Shows the command help"}};
+
+/**
+ * This is a custom project configuration structure where you can store the
+ * parsed information.
+ */
+struct demo_configuration
+{
+ bool simple_flag;
+ bool multiple_flag;
+ bool long_flag;
+ const char *key;
+};
+
+int main(int argc, char *argv[])
+{
+ char identifier;
+ const char *value;
+ cag_option_context context;
+ struct demo_configuration config = {false, false, false, NULL};
+
+ /**
+ * Now we just prepare the context and iterate over all options. Simple!
+ */
+ cag_option_prepare(&context, options, CAG_ARRAY_SIZE(options), argc, argv);
+ while (cag_option_fetch(&context)) {
+ identifier = cag_option_get(&context);
+ switch (identifier) {
+ case 's':
+ config.simple_flag = true;
+ break;
+ case 'm':
+ config.multiple_flag = true;
+ break;
+ case 'l':
+ config.long_flag = true;
+ break;
+ case 'k':
+ value = cag_option_get_value(&context);
+ config.key = value;
+ break;
+ case 'h':
+ printf("Usage: cargsdemo [OPTION]...\n");
+ printf("Demonstrates the cargs library.\n\n");
+ cag_option_print(options, CAG_ARRAY_SIZE(options), stdout);
+ printf("\nNote that all formatting is done by cargs.\n");
+ return EXIT_SUCCESS;
+ }
+ }
+
+ printf("simple_flag: %i, multiple_flag: %i, long_flag: %i, key: %s\n",
+ config.simple_flag, config.multiple_flag, config.long_flag,
+ config.key ? config.key : "-");
+
+ return EXIT_SUCCESS;
+}
+
+```
+
+### Example output
+```console
+foo@bar:~$ ./cargsdemo
+simple_flag: 0, multiple_flag: 0, long_flag: 0, key: -
+```
+
+```console
+foo@bar:~$ ./cargsdemo -k=test -sm --long
+simple_flag: 1, multiple_flag: 1, long_flag: 1, key: test
+```
+
+```console
+foo@bar:~$ ./cargsdemo --help
+Usage: cargsdemo [OPTION]...
+Demonstrates the cargs library.
+
+ -s Simple flag
+ -m, -M, -o, -O Multiple access letters
+ --long Long parameter name
+ -k, --key=VALUE Parameter value
+ -h, --help Shows the command help
+
+Note that all formatting is done by cargs.
+```
diff --git a/libs/cargs/banner.png b/libs/cargs/banner.png
new file mode 100755
index 0000000..7de0493
--- /dev/null
+++ b/libs/cargs/banner.png
Binary files differ
diff --git a/libs/cargs/banner.svg b/libs/cargs/banner.svg
new file mode 100755
index 0000000..404e46a
--- /dev/null
+++ b/libs/cargs/banner.svg
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg width="100%" height="100%" viewBox="0 0 1000 250" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
+ <g id="rect1060" transform="matrix(1,0,0,1,0,-872.52)">
+ <rect x="0" y="872.52" width="1000" height="250" style="fill:rgb(114,78,145);"/>
+ </g>
+ <g id="Layer3">
+ <g transform="matrix(1,0,0,1,221.118,45.991)">
+ <circle cx="278.882" cy="107.887" r="94.301" style="fill:rgb(248,198,48);"/>
+ </g>
+ <g transform="matrix(1.22797,0,0,1.22797,157.54,21.3954)">
+ <circle cx="278.882" cy="107.887" r="94.301" style="fill:rgb(248,198,48);fill-opacity:0.25;"/>
+ </g>
+ <g transform="matrix(1.42312,0,0,1.42312,103.117,0.341853)">
+ <circle cx="278.882" cy="107.887" r="94.301" style="fill:rgb(248,198,48);fill-opacity:0.25;"/>
+ </g>
+ </g>
+ <g id="Layer2">
+ <path d="M0,49.209L35.622,13.586L70.746,48.71L70.746,125L111.385,84.361L138.078,84.361L138.078,70.156L179.973,70.156L179.973,85.167L235.131,85.167L274.964,125L274.964,50.029L289.064,50.029L289.064,37.363L318.482,37.363L318.482,49.621L340.137,49.621L340.137,125L383.751,81.386L427.365,125L449.02,103.345L470.674,125L470.674,52.889L530.943,52.889L530.943,202.429L570.984,162.389L570.984,91.182L612.659,91.182L632.328,110.851L632.328,207.741L676.886,163.183L719.772,206.069L719.772,137.122L804.68,137.122L804.68,204.154L834.047,174.788L893.418,174.788L917.997,199.366L917.997,134.568L938.107,134.568L938.107,125L975.773,125L975.773,133.93L1000,133.93L1000,250L0,250L0,49.209Z" style="fill:rgb(69,31,85);"/>
+ </g>
+ <g transform="matrix(1,0,0,1,0,23.705)">
+ <g id="Layer1">
+ <path d="M0,114.047L67.255,114.047L67.255,180.88L98.177,180.88L98.177,113.049L149.05,113.049L149.05,151.952L201.918,151.952L201.918,59.577L243.512,17.984L243.512,146.965L265.477,125L286.077,145.6L287.077,182.813L398.43,182.813L397.43,73.149L434.338,73.149L476.234,115.044L476.234,139.982L525.112,139.982L605.516,59.577L605.516,138.985L658.778,138.985L658.778,69.159L728.246,138.627L728.246,201.828L785.462,201.828L785.462,142.975L804.414,142.975L804.414,125L856.662,125L856.662,138.985L892.195,138.985L892.195,203.823L931.098,203.823L931.098,150.955L1000,150.955L1000,226.295L0,226.295L0,114.047Z" style="fill:rgb(229,79,109);"/>
+ </g>
+ </g>
+ <g transform="matrix(1,0,0,1,709.162,69.265)">
+ <g id="text1122">
+ <g>
+ <g>
+ <text x="0px" y="0px" style="font-family:'Arial-Black', 'Arial', sans-serif;font-weight:900;font-size:85.333px;fill:rgb(229,79,109);">c</text>
+ <text x="56.916px" y="0px" style="font-family:'Arial-Black', 'Arial', sans-serif;font-weight:900;font-size:85.333px;fill:rgb(248,198,48);">ar<tspan x="153.833px 210.749px " y="0px 0px ">gs</tspan></text>
+ </g>
+ </g>
+ </g>
+ </g>
+</svg>
diff --git a/libs/cargs/cmake/CargsConfig.cmake.in b/libs/cargs/cmake/CargsConfig.cmake.in
new file mode 100644
index 0000000..bb11824
--- /dev/null
+++ b/libs/cargs/cmake/CargsConfig.cmake.in
@@ -0,0 +1,5 @@
+@PACKAGE_INIT@
+
+include("${CMAKE_CURRENT_LIST_DIR}/CargsTargets.cmake")
+
+check_required_components(cargs)
diff --git a/libs/cargs/cmake/CreateTestList.cmake b/libs/cargs/cmake/CreateTestList.cmake
new file mode 100644
index 0000000..2dd46b3
--- /dev/null
+++ b/libs/cargs/cmake/CreateTestList.cmake
@@ -0,0 +1,11 @@
+function(create_test_list list_name file)
+ set("TEST_LIST_FILE_${list_name}" ${file} PARENT_SCOPE)
+ file(WRITE ${file} "#define UNIT_TESTS(XX) \\\n")
+ file(APPEND ${file} ${TEST_LIST_CONTENT_${list_name}})
+ file(APPEND ${file} "\n")
+endfunction()
+
+function(create_test list_name unit_name test_name)
+ set(TEST_LIST_CONTENT_${list_name} "${TEST_LIST_CONTENT_${list_name}} XX(${unit_name},${test_name}) \\\n" PARENT_SCOPE)
+ add_test(NAME "${unit_name}_${test_name}" COMMAND ${TEST_TARGET} ${unit_name} ${test_name})
+endfunction() \ No newline at end of file
diff --git a/libs/cargs/cmake/EnableWarnings.cmake b/libs/cargs/cmake/EnableWarnings.cmake
new file mode 100755
index 0000000..bfce52f
--- /dev/null
+++ b/libs/cargs/cmake/EnableWarnings.cmake
@@ -0,0 +1,19 @@
+# enable warnings
+function(enable_warnings target)
+ if(MSVC)
+ target_compile_definitions(${target} PRIVATE _CRT_SECURE_NO_WARNINGS)
+ target_compile_options(${target} PRIVATE /W4)
+ target_compile_options(${target} PRIVATE /WX)
+ elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang")
+ target_compile_options(${target} PRIVATE -Werror)
+ target_compile_options(${target} PRIVATE -Wall)
+ target_compile_options(${target} PRIVATE -Wextra)
+ target_compile_options(${target} PRIVATE -Wpedantic)
+ target_compile_options(${target} PRIVATE -Wno-gnu-zero-variadic-macro-arguments)
+ elseif("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU")
+ target_compile_options(${target} PRIVATE -Wall)
+ target_compile_options(${target} PRIVATE -Werror)
+ target_compile_options(${target} PRIVATE -Wextra)
+ target_compile_options(${target} PRIVATE -Wpedantic)
+ endif()
+endfunction() \ No newline at end of file
diff --git a/libs/cargs/demo/CMakeLists.txt b/libs/cargs/demo/CMakeLists.txt
new file mode 100755
index 0000000..0d37d45
--- /dev/null
+++ b/libs/cargs/demo/CMakeLists.txt
@@ -0,0 +1,13 @@
+cmake_minimum_required(VERSION 3.16)
+project(cargsdemo)
+
+include(FetchContent)
+FetchContent_Declare(cargs
+ GIT_REPOSITORY git@github.com:likle/cargs.git
+ GIT_TAG stable
+)
+FetchContent_MakeAvailable(cargs)
+
+add_executable(cargsdemo main.c)
+
+target_link_libraries(cargsdemo cargs)
diff --git a/libs/cargs/demo/main.c b/libs/cargs/demo/main.c
new file mode 100755
index 0000000..8d5167e
--- /dev/null
+++ b/libs/cargs/demo/main.c
@@ -0,0 +1,91 @@
+#include <cargs.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+/**
+ * This is the main configuration of all options available.
+ */
+static struct cag_option options[] = {
+ {.identifier = 's',
+ .access_letters = "s",
+ .access_name = NULL,
+ .value_name = NULL,
+ .description = "Simple flag"},
+
+ {.identifier = 'm',
+ .access_letters = "mMoO",
+ .access_name = NULL,
+ .value_name = NULL,
+ .description = "Multiple access letters"},
+
+ {.identifier = 'l',
+ .access_letters = NULL,
+ .access_name = "long",
+ .value_name = NULL,
+ .description = "Long parameter name"},
+
+ {.identifier = 'k',
+ .access_letters = "k",
+ .access_name = "key",
+ .value_name = "VALUE",
+ .description = "Parameter value"},
+
+ {.identifier = 'h',
+ .access_letters = "h",
+ .access_name = "help",
+ .description = "Shows the command help"}};
+
+/**
+ * This is a custom project configuration structure where you can store the
+ * parsed information.
+ */
+struct demo_configuration
+{
+ bool simple_flag;
+ bool multiple_flag;
+ bool long_flag;
+ const char *key;
+};
+
+int main(int argc, char *argv[])
+{
+ char identifier;
+ const char *value;
+ cag_option_context context;
+ struct demo_configuration config = {false, false, false, NULL};
+
+ /**
+ * Now we just prepare the context and iterate over all options. Simple!
+ */
+ cag_option_prepare(&context, options, CAG_ARRAY_SIZE(options), argc, argv);
+ while (cag_option_fetch(&context)) {
+ identifier = cag_option_get(&context);
+ switch (identifier) {
+ case 's':
+ config.simple_flag = true;
+ break;
+ case 'm':
+ config.multiple_flag = true;
+ break;
+ case 'l':
+ config.long_flag = true;
+ break;
+ case 'k':
+ value = cag_option_get_value(&context);
+ config.key = value;
+ break;
+ case 'h':
+ printf("Usage: cargsdemo [OPTION]...\n");
+ printf("Demonstrates the cargs library.\n\n");
+ cag_option_print(options, CAG_ARRAY_SIZE(options), stdout);
+ printf("\nNote that all formatting is done by cargs.\n");
+ return EXIT_SUCCESS;
+ }
+ }
+
+ printf("simple_flag: %i, multiple_flag: %i, long_flag: %i, key: %s\n",
+ config.simple_flag, config.multiple_flag, config.long_flag,
+ config.key ? config.key : "-");
+
+ return EXIT_SUCCESS;
+}
diff --git a/libs/cargs/docs/_config.yml b/libs/cargs/docs/_config.yml
new file mode 100644
index 0000000..88f7fa8
--- /dev/null
+++ b/libs/cargs/docs/_config.yml
@@ -0,0 +1,6 @@
+defaults:
+ -
+ scope:
+ path: "" # an empty string here means all files in the project
+ values:
+ layout: "default" \ No newline at end of file
diff --git a/libs/cargs/docs/_layouts/default.html b/libs/cargs/docs/_layouts/default.html
new file mode 100644
index 0000000..e326afd
--- /dev/null
+++ b/libs/cargs/docs/_layouts/default.html
@@ -0,0 +1,42 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>{{ page.title }} - cargs</title>
+ <meta name="description" content="{{ page.description }}" />
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <link href="https://fonts.googleapis.com/css?family=Ubuntu+Mono" rel="stylesheet">
+ <link href="{{ site.baseurl }}/assets/css/default.css?v2" rel="stylesheet" type="text/css">
+ <link href="{{ site.baseurl }}/assets/css/monakai.css" rel="stylesheet" type="text/css">
+ <link rel="shortcut icon" href="{{ site.baseurl }}/assets/img/favicon.png">
+ <script async src="https://www.googletagmanager.com/gtag/js?id=UA-127837944-1"></script>
+ <script>
+ window.dataLayer = window.dataLayer || [];
+ function gtag(){dataLayer.push(arguments);}
+ gtag('js', new Date());
+
+ gtag('config', 'UA-127837944-1');
+ </script>
+ </head>
+ <body>
+ <header>
+ <div class="inner">
+ <a href="{{ site.baseurl }}{% link index.md %}" class="logo">cargs</a>
+ <nav>
+ <a href="{{ site.baseurl }}{% link build.md %}">build</a>
+ <a href="{{ site.baseurl }}{% link embed.md %}">embed</a>
+ <a href="{{ site.baseurl }}{% link reference/index.md %}">reference</a>
+ </nav>
+ </div>
+ </header>
+ <div class="main">
+ <div class="content">
+ <h1>{{ page.title }}</h1>
+ {{ content }}
+ </div>
+ </div>
+ </body>
+ <footer>
+ find this repository on <a href="https://github.com/likle/cargs/tree/master">github</a>
+ </footer>
+</html>
diff --git a/libs/cargs/docs/assets/css/default.css b/libs/cargs/docs/assets/css/default.css
new file mode 100644
index 0000000..9389ca0
--- /dev/null
+++ b/libs/cargs/docs/assets/css/default.css
@@ -0,0 +1,127 @@
+html,
+body {
+ height: 100%;
+}
+
+body {
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
+ margin: auto;
+ padding: 20px;
+ padding: 0;
+ display: flex;
+ flex-flow: column;
+ background: #fafafa;
+ color: #22162B;
+}
+
+header {
+ text-align: right;
+ margin-bottom: 20px;
+ padding: 20px 20px;
+ font-family: 'Ubuntu Mono';
+ background: #451F55;
+ color: #F8C630;
+ border-bottom: 10px solid #724E91;
+}
+
+header .inner {
+ max-width: 800px;
+ margin: auto;
+}
+
+header .logo {
+ float: left;
+ vertical-align: middle;
+ display: inline-block;
+ font-weight: bold;
+ font-size: 32pt;
+ padding: 0;
+ margin-left: 10px;
+ line-height: 50px;
+}
+
+header nav {
+ line-height: 50px;
+}
+
+header a {
+ padding: 10px;
+ text-decoration: none;
+ font-weight: bold;
+ font-size: 18pt;
+ overflow: auto;
+ color: #E54F6D;;
+}
+
+.main {
+ flex: 1;
+}
+
+footer {
+ text-align: center;
+ margin-top: 50px;
+ padding: 40px 20px;
+ font-family: 'Ubuntu Mono';
+ background: #22162B;
+ color: #F8C630;
+}
+
+
+a {
+ color: #E54F6D;
+}
+
+h1, h2 {
+ font-family: 'Ubuntu Mono';
+ padding-bottom: 5px;
+ border-bottom: 1px solid rgba(255,255,255,.2);
+}
+
+.content {
+ color: #49483e;
+ padding-top: 20px;
+ max-width: 800px;
+ margin: auto;
+}
+
+.highlight {
+ padding: 10px;
+ margin: 0;
+ overflow: auto;
+ white-space: pre-wrap;
+}
+
+.highlighter-rouge {
+ background: rgba(0,0,0,0.1);
+}
+
+table {
+ border-collapse: collapse;
+}
+
+table td,
+table th {
+ border: 1px solid #DDDDDD;
+ background: #ffffff;
+ padding: 5px 8px;
+}
+
+table tr:nth-child(even) td {
+ background: #f8f9fb;
+}
+
+@media (max-width: 1000px) {
+ header {
+ text-align: center;
+ }
+
+ header .logo {
+ display: block;
+ float: none;
+ margin-bottom: 10px;
+ }
+
+ .content {
+ padding: 0 20px;
+ }
+}
diff --git a/libs/cargs/docs/assets/css/github.css b/libs/cargs/docs/assets/css/github.css
new file mode 100644
index 0000000..daf76ad
--- /dev/null
+++ b/libs/cargs/docs/assets/css/github.css
@@ -0,0 +1,209 @@
+.highlight table td { padding: 5px; }
+.highlight table pre { margin: 0; }
+.highlight .cm {
+ color: #999988;
+ font-style: italic;
+}
+.highlight .cp {
+ color: #999999;
+ font-weight: bold;
+}
+.highlight .c1 {
+ color: #999988;
+ font-style: italic;
+}
+.highlight .cs {
+ color: #999999;
+ font-weight: bold;
+ font-style: italic;
+}
+.highlight .c, .highlight .cd {
+ color: #999988;
+ font-style: italic;
+}
+.highlight .err {
+ color: #a61717;
+ background-color: #e3d2d2;
+}
+.highlight .gd {
+ color: #000000;
+ background-color: #ffdddd;
+}
+.highlight .ge {
+ color: #000000;
+ font-style: italic;
+}
+.highlight .gr {
+ color: #aa0000;
+}
+.highlight .gh {
+ color: #999999;
+}
+.highlight .gi {
+ color: #000000;
+ background-color: #ddffdd;
+}
+.highlight .go {
+ color: #888888;
+}
+.highlight .gp {
+ color: #555555;
+}
+.highlight .gs {
+ font-weight: bold;
+}
+.highlight .gu {
+ color: #aaaaaa;
+}
+.highlight .gt {
+ color: #aa0000;
+}
+.highlight .kc {
+ color: #000000;
+ font-weight: bold;
+}
+.highlight .kd {
+ color: #000000;
+ font-weight: bold;
+}
+.highlight .kn {
+ color: #000000;
+ font-weight: bold;
+}
+.highlight .kp {
+ color: #000000;
+ font-weight: bold;
+}
+.highlight .kr {
+ color: #000000;
+ font-weight: bold;
+}
+.highlight .kt {
+ color: #445588;
+ font-weight: bold;
+}
+.highlight .k, .highlight .kv {
+ color: #000000;
+ font-weight: bold;
+}
+.highlight .mf {
+ color: #009999;
+}
+.highlight .mh {
+ color: #009999;
+}
+.highlight .il {
+ color: #009999;
+}
+.highlight .mi {
+ color: #009999;
+}
+.highlight .mo {
+ color: #009999;
+}
+.highlight .m, .highlight .mb, .highlight .mx {
+ color: #009999;
+}
+.highlight .sb {
+ color: #d14;
+}
+.highlight .sc {
+ color: #d14;
+}
+.highlight .sd {
+ color: #d14;
+}
+.highlight .s2 {
+ color: #d14;
+}
+.highlight .se {
+ color: #d14;
+}
+.highlight .sh {
+ color: #d14;
+}
+.highlight .si {
+ color: #d14;
+}
+.highlight .sx {
+ color: #d14;
+}
+.highlight .sr {
+ color: #009926;
+}
+.highlight .s1 {
+ color: #d14;
+}
+.highlight .ss {
+ color: #990073;
+}
+.highlight .s {
+ color: #d14;
+}
+.highlight .na {
+ color: #008080;
+}
+.highlight .bp {
+ color: #999999;
+}
+.highlight .nb {
+ color: #0086B3;
+}
+.highlight .nc {
+ color: #445588;
+ font-weight: bold;
+}
+.highlight .no {
+ color: #008080;
+}
+.highlight .nd {
+ color: #3c5d5d;
+ font-weight: bold;
+}
+.highlight .ni {
+ color: #800080;
+}
+.highlight .ne {
+ color: #990000;
+ font-weight: bold;
+}
+.highlight .nf {
+ color: #990000;
+ font-weight: bold;
+}
+.highlight .nl {
+ color: #990000;
+ font-weight: bold;
+}
+.highlight .nn {
+ color: #555555;
+}
+.highlight .nt {
+ color: #000080;
+}
+.highlight .vc {
+ color: #008080;
+}
+.highlight .vg {
+ color: #008080;
+}
+.highlight .vi {
+ color: #008080;
+}
+.highlight .nv {
+ color: #008080;
+}
+.highlight .ow {
+ color: #000000;
+ font-weight: bold;
+}
+.highlight .o {
+ color: #000000;
+ font-weight: bold;
+}
+.highlight .w {
+ color: #bbbbbb;
+}
+.highlight {
+ background-color: #f8f8f8;
+}
diff --git a/libs/cargs/docs/assets/css/monakai.css b/libs/cargs/docs/assets/css/monakai.css
new file mode 100644
index 0000000..667b3ec
--- /dev/null
+++ b/libs/cargs/docs/assets/css/monakai.css
@@ -0,0 +1,210 @@
+.highlight table td { padding: 5px; }
+.highlight table pre { margin: 0; }
+.highlight .c, .highlight .cd {
+ color: #75715e;
+ font-style: italic;
+}
+.highlight .cm {
+ color: #75715e;
+ font-style: italic;
+}
+.highlight .c1 {
+ color: #75715e;
+ font-style: italic;
+}
+.highlight .cp {
+ color: #75715e;
+ font-weight: bold;
+}
+.highlight .cs {
+ color: #75715e;
+ font-weight: bold;
+ font-style: italic;
+}
+.highlight .err {
+ color: #960050;
+ background-color: #1e0010;
+}
+.highlight .gi {
+ color: #ffffff;
+ background-color: #324932;
+}
+.highlight .gd {
+ color: #ffffff;
+ background-color: #493131;
+}
+.highlight .ge {
+ color: #000000;
+ font-style: italic;
+}
+.highlight .gr {
+ color: #aa0000;
+}
+.highlight .gt {
+ color: #aa0000;
+}
+.highlight .gh {
+ color: #999999;
+}
+.highlight .go {
+ color: #888888;
+}
+.highlight .gp {
+ color: #ee7733;
+}
+.highlight .gs {
+ font-weight: bold;
+}
+.highlight .gu {
+ color: #aaaaaa;
+}
+.highlight .k, .highlight .kv {
+ color: #66d9ef;
+ font-weight: bold;
+}
+.highlight .kc {
+ color: #66d9ef;
+ font-weight: bold;
+}
+.highlight .kd {
+ color: #66d9ef;
+ font-weight: bold;
+}
+.highlight .kp {
+ color: #66d9ef;
+ font-weight: bold;
+}
+.highlight .kr {
+ color: #66d9ef;
+ font-weight: bold;
+}
+.highlight .kt {
+ color: #66d9ef;
+ font-weight: bold;
+}
+.highlight .kn {
+ color: #f92672;
+ font-weight: bold;
+}
+.highlight .ow {
+ color: #f92672;
+ font-weight: bold;
+}
+.highlight .o {
+ color: #f92672;
+ font-weight: bold;
+}
+.highlight .mf {
+ color: #ae81ff;
+}
+.highlight .mh {
+ color: #ae81ff;
+}
+.highlight .il {
+ color: #ae81ff;
+}
+.highlight .mi {
+ color: #ae81ff;
+}
+.highlight .mo {
+ color: #ae81ff;
+}
+.highlight .m, .highlight .mb, .highlight .mx {
+ color: #ae81ff;
+}
+.highlight .se {
+ color: #ae81ff;
+}
+.highlight .sb {
+ color: #e6db74;
+}
+.highlight .sc {
+ color: #e6db74;
+}
+.highlight .sd {
+ color: #e6db74;
+}
+.highlight .s2 {
+ color: #e6db74;
+}
+.highlight .sh {
+ color: #e6db74;
+}
+.highlight .si {
+ color: #e6db74;
+}
+.highlight .sx {
+ color: #e6db74;
+}
+.highlight .sr {
+ color: #e6db74;
+}
+.highlight .s1 {
+ color: #e6db74;
+}
+.highlight .ss {
+ color: #e6db74;
+}
+.highlight .s {
+ color: #e6db74;
+}
+.highlight .na {
+ color: #a6e22e;
+}
+.highlight .nc {
+ color: #a6e22e;
+ font-weight: bold;
+}
+.highlight .nd {
+ color: #a6e22e;
+ font-weight: bold;
+}
+.highlight .ne {
+ color: #a6e22e;
+ font-weight: bold;
+}
+.highlight .nf {
+ color: #a6e22e;
+ font-weight: bold;
+}
+.highlight .no {
+ color: #66d9ef;
+}
+.highlight .bp {
+ color: #f8f8f2;
+}
+.highlight .nb {
+ color: #f8f8f2;
+}
+.highlight .ni {
+ color: #f8f8f2;
+}
+.highlight .nn {
+ color: #f8f8f2;
+}
+.highlight .vc {
+ color: #f8f8f2;
+}
+.highlight .vg {
+ color: #f8f8f2;
+}
+.highlight .vi {
+ color: #f8f8f2;
+}
+.highlight .nv {
+ color: #f8f8f2;
+}
+.highlight .w {
+ color: #f8f8f2;
+}
+.highlight .nl {
+ color: #f8f8f2;
+ font-weight: bold;
+}
+.highlight .nt {
+ color: #f92672;
+}
+.highlight {
+ color: #f8f8f2;
+ background-color: #49483e;
+}
diff --git a/libs/cargs/docs/assets/css/trac.css b/libs/cargs/docs/assets/css/trac.css
new file mode 100644
index 0000000..854cfb8
--- /dev/null
+++ b/libs/cargs/docs/assets/css/trac.css
@@ -0,0 +1,210 @@
+.highlight table td { padding: 5px; }
+.highlight table pre { margin: 0; }
+.highlight .c, .highlight .cd {
+ color: #75715e;
+ font-style: italic;
+}
+.highlight .cm {
+ color: #75715e;
+ font-style: italic;
+}
+.highlight .c1 {
+ color: #75715e;
+ font-style: italic;
+}
+.highlight .cp {
+ color: #75715e;
+ font-weight: bold;
+}
+.highlight .cs {
+ color: #75715e;
+ font-weight: bold;
+ font-style: italic;
+}
+.highlight .err {
+ color: #960050;
+ background-color: #1e0010;
+}
+.highlight .gi {
+ color: #ffffff;
+ background-color: #324932;
+}
+.highlight .gd {
+ color: #ffffff;
+ background-color: #493131;
+}
+.highlight .ge {
+ color: #000000;
+ font-style: italic;
+}
+.highlight .gr {
+ color: #aa0000;
+}
+.highlight .gt {
+ color: #aa0000;
+}
+.highlight .gh {
+ color: #999999;
+}
+.highlight .go {
+ color: #888888;
+}
+.highlight .gp {
+ color: #555555;
+}
+.highlight .gs {
+ font-weight: bold;
+}
+.highlight .gu {
+ color: #aaaaaa;
+}
+.highlight .k, .highlight .kv {
+ color: #66d9ef;
+ font-weight: bold;
+}
+.highlight .kc {
+ color: #66d9ef;
+ font-weight: bold;
+}
+.highlight .kd {
+ color: #66d9ef;
+ font-weight: bold;
+}
+.highlight .kp {
+ color: #66d9ef;
+ font-weight: bold;
+}
+.highlight .kr {
+ color: #66d9ef;
+ font-weight: bold;
+}
+.highlight .kt {
+ color: #66d9ef;
+ font-weight: bold;
+}
+.highlight .kn {
+ color: #f92672;
+ font-weight: bold;
+}
+.highlight .ow {
+ color: #f92672;
+ font-weight: bold;
+}
+.highlight .o {
+ color: #f92672;
+ font-weight: bold;
+}
+.highlight .mf {
+ color: #ae81ff;
+}
+.highlight .mh {
+ color: #ae81ff;
+}
+.highlight .il {
+ color: #ae81ff;
+}
+.highlight .mi {
+ color: #ae81ff;
+}
+.highlight .mo {
+ color: #ae81ff;
+}
+.highlight .m, .highlight .mb, .highlight .mx {
+ color: #ae81ff;
+}
+.highlight .se {
+ color: #ae81ff;
+}
+.highlight .sb {
+ color: #e6db74;
+}
+.highlight .sc {
+ color: #e6db74;
+}
+.highlight .sd {
+ color: #e6db74;
+}
+.highlight .s2 {
+ color: #e6db74;
+}
+.highlight .sh {
+ color: #e6db74;
+}
+.highlight .si {
+ color: #e6db74;
+}
+.highlight .sx {
+ color: #e6db74;
+}
+.highlight .sr {
+ color: #e6db74;
+}
+.highlight .s1 {
+ color: #e6db74;
+}
+.highlight .ss {
+ color: #e6db74;
+}
+.highlight .s {
+ color: #e6db74;
+}
+.highlight .na {
+ color: #a6e22e;
+}
+.highlight .nc {
+ color: #a6e22e;
+ font-weight: bold;
+}
+.highlight .nd {
+ color: #a6e22e;
+ font-weight: bold;
+}
+.highlight .ne {
+ color: #a6e22e;
+ font-weight: bold;
+}
+.highlight .nf {
+ color: #a6e22e;
+ font-weight: bold;
+}
+.highlight .no {
+ color: #66d9ef;
+}
+.highlight .bp {
+ color: #f8f8f2;
+}
+.highlight .nb {
+ color: #f8f8f2;
+}
+.highlight .ni {
+ color: #f8f8f2;
+}
+.highlight .nn {
+ color: #f8f8f2;
+}
+.highlight .vc {
+ color: #f8f8f2;
+}
+.highlight .vg {
+ color: #f8f8f2;
+}
+.highlight .vi {
+ color: #f8f8f2;
+}
+.highlight .nv {
+ color: #f8f8f2;
+}
+.highlight .w {
+ color: #f8f8f2;
+}
+.highlight .nl {
+ color: #f8f8f2;
+ font-weight: bold;
+}
+.highlight .nt {
+ color: #f92672;
+}
+.highlight {
+ color: #f8f8f2;
+ background-color: #49483e;
+}
diff --git a/libs/cargs/docs/assets/css/vim.css b/libs/cargs/docs/assets/css/vim.css
new file mode 100644
index 0000000..75e00ef
--- /dev/null
+++ b/libs/cargs/docs/assets/css/vim.css
@@ -0,0 +1 @@
+unknown theme: vim
diff --git a/libs/cargs/docs/assets/img/favicon.png b/libs/cargs/docs/assets/img/favicon.png
new file mode 100644
index 0000000..b4d46f6
--- /dev/null
+++ b/libs/cargs/docs/assets/img/favicon.png
Binary files differ
diff --git a/libs/cargs/docs/assets/img/favicon.svg b/libs/cargs/docs/assets/img/favicon.svg
new file mode 100644
index 0000000..acf0df6
--- /dev/null
+++ b/libs/cargs/docs/assets/img/favicon.svg
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="48"
+ height="48"
+ viewBox="0 0 12.7 12.7"
+ version="1.1"
+ id="svg8"
+ inkscape:version="0.92.3 (2405546, 2018-03-11)"
+ sodipodi:docname="favicon.svg">
+ <defs
+ id="defs2">
+ <inkscape:path-effect
+ effect="powerstroke"
+ id="path-effect845"
+ is_visible="true"
+ offset_points="0,0.13229166"
+ sort_points="true"
+ interpolator_type="CubicBezierJohan"
+ interpolator_beta="0.2"
+ start_linecap_type="zerowidth"
+ linejoin_type="extrp_arc"
+ miter_limit="4"
+ end_linecap_type="zerowidth" />
+ <inkscape:path-effect
+ effect="spiro"
+ id="path-effect843"
+ is_visible="true" />
+ <inkscape:path-effect
+ effect="simplify"
+ id="path-effect841"
+ is_visible="true"
+ steps="1"
+ threshold="0.00707113"
+ smooth_angles="360"
+ helper_size="0"
+ simplify_individual_paths="false"
+ simplify_just_coalesce="false"
+ simplifyindividualpaths="false"
+ simplifyJustCoalesce="false" />
+ <inkscape:path-effect
+ effect="spiro"
+ id="path-effect837"
+ is_visible="true" />
+ <inkscape:path-effect
+ effect="simplify"
+ id="path-effect835"
+ is_visible="true"
+ steps="1"
+ threshold="0.00707113"
+ smooth_angles="360"
+ helper_size="0"
+ simplify_individual_paths="false"
+ simplify_just_coalesce="false"
+ simplifyindividualpaths="false"
+ simplifyJustCoalesce="false" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="7.9195959"
+ inkscape:cx="40.669189"
+ inkscape:cy="15.472538"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ units="px"
+ inkscape:window-width="1600"
+ inkscape:window-height="847"
+ inkscape:window-x="-8"
+ inkscape:window-y="-8"
+ inkscape:window-maximized="1" />
+ <metadata
+ id="metadata5">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-284.3)">
+ <circle
+ style="opacity:0.98999999;fill:#646464;fill-opacity:1;stroke:none;stroke-width:0.99925655;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
+ id="path815"
+ cx="6.3499999"
+ cy="290.64999"
+ r="6.3499999" />
+ <path
+ style="opacity:0.98999999;fill:#464646;fill-opacity:1;stroke:none;stroke-width:11.34266472;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
+ d="M 45.214844 12.810547 C 39.930389 15.838245 31.937362 19.593748 21.265625 22.328125 C 13.923958 24.209271 11.586387 22.779846 5.6914062 16.884766 C 4.3495394 15.542904 2.7438777 15.778452 1.0644531 16.9375 A 24 24 0 0 0 0 24 A 24 24 0 0 0 4.546875 38.005859 C 7.0755777 33.407331 11.162743 28.341891 15.195312 34.054688 C 19.611697 40.311272 36.484577 25.785901 46.623047 16.070312 A 24 24 0 0 0 45.214844 12.810547 z "
+ transform="matrix(0.26458333,0,0,0.26458333,0,284.3)"
+ id="path821" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:3.70416665px;line-height:125%;font-family:Pacifico;-inkscape-font-specification:Pacifico;letter-spacing:0px;word-spacing:0px;fill:#f6b27f;fill-opacity:1;stroke:#f6b27f;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="2.915477"
+ y="294.59277"
+ id="text819"><tspan
+ sodipodi:role="line"
+ id="tspan817"
+ x="2.915477"
+ y="294.59277"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.69999981px;font-family:'Ubuntu Mono';-inkscape-font-specification:'Ubuntu Mono Bold';fill:#f6b27f;fill-opacity:1;stroke:#f6b27f;stroke-width:0.26458332px;stroke-opacity:1">C</tspan></text>
+ </g>
+</svg>
diff --git a/libs/cargs/docs/build.md b/libs/cargs/docs/build.md
new file mode 100644
index 0000000..599c114
--- /dev/null
+++ b/libs/cargs/docs/build.md
@@ -0,0 +1,49 @@
+---
+title: Building
+description: A guide on how to build the cargs command line arguments parser library for C/C++.
+---
+
+In order to build the source, you will have to download it. You can do so using git (or download it from [here](https://github.com/likle/cargs/archive/stable.zip)).
+```bash
+git clone -b stable git@github.com:likle/cargs.git
+```
+
+**Note**: The *stable* branch points to the latest stable version. You should
+always use a stable version in production code.
+
+## Using Windows
+Visual Studio 2017 is recommended, then you can just open the source using ``File -> Open -> CMake...``. You can use Visual Studio to compile the source and debug the code. Make sure you have the CMake and C/C++ features enabled.
+
+## Using Ubuntu
+You will need [CMake](https://cmake.org/download/) and either gcc or clang installed. On Ubuntu you can use the following to compile **cargs**:
+```bash
+sudo apt-get install build-essential cmake
+mkdir cargs/build
+cd cargs/build
+cmake ..
+make
+```
+
+## Using MacOS
+You will need [CMake](https://cmake.org/download/) and either gcc or clang installed. On MacOS you can use the following to compile **cargs**:
+```
+brew install cmake gcc
+mkdir cargs/build
+cd cargs/build
+cmake ..
+make
+```
+# Running Tests
+After building **cargs** you can run tests to ensure everything is fine. In order to do that, make sure that you are in the build folder and then execute the test program:
+
+```bash
+./cargstest
+```
+
+That's it!
+
+You can even specify which tests to execute by optionally specifying the category and test name:
+```bash
+# ./cargstest [category] [test]
+./cargstest option complex
+```
diff --git a/libs/cargs/docs/embed.md b/libs/cargs/docs/embed.md
new file mode 100644
index 0000000..ed64d4e
--- /dev/null
+++ b/libs/cargs/docs/embed.md
@@ -0,0 +1,47 @@
+---
+title: Embedding
+description: A guide on how to embed the cargs command line parser library for C/C++.
+---
+
+
+In order to embed **cargs**, you will have to download it.
+You can do so using git (or download it from [here](https://github.com/likle/cargs/archive/stable.zip)).
+
+```bash
+git clone -b stable git@github.com:likle/cargs.git
+```
+**Note**: The *stable* branch points to the latest stable version. You should
+always use a stable version in production code.
+
+## Using CMake to embed cargs
+If you are using CMake it is fairly easy to embed **cargs**.
+This only requires two lines, you don't even have to specify the include directories.
+The following example shows how to do so:
+```cmake
+# Some basics you will need in your cmake file.
+cmake_minimum_required(VERSION 3.9.2)
+project(example C)
+add_executable(example_target main.c)
+
+# Replace your_path_to_cargs with the path to your cargs copy.
+# This could be something like "${CMAKE_CURRENT_SOURCE_DIR}/lib/cargs".
+add_subdirectory(your_path_to_cargs)
+
+# Replace example_target with the target name which requires cargs.
+# After this, there is no need to specify any include directories.
+target_link_libraries(example_target cargs)
+```
+
+After that, you should be able to use cargs in your source code:
+```c
+#include <cargs.h>
+```
+
+## Directly embed cargs in your source
+If you don't use CMake and would like to embed **cargs** directly, you could
+just add the two files ``src/cargs.c`` and ``ìnclude/cargs.h`` to your project.
+The folder containing ``cargs.h`` has to be in your include directories
+([Visual Studio](https://docs.microsoft.com/en-us/cpp/ide/vcpp-directories-property-page?view=vs-2017),
+[Eclipse](https://help.eclipse.org/mars/index.jsp?topic=%2Forg.eclipse.cdt.doc.user%2Freference%2Fcdt_u_prop_general_pns_inc.htm),
+[gcc](https://www.rapidtables.com/code/linux/gcc/gcc-i.html),
+[clang](https://clang.llvm.org/docs/ClangCommandLineReference.html#include-path-management)).
diff --git a/libs/cargs/docs/index.md b/libs/cargs/docs/index.md
new file mode 100644
index 0000000..60c598e
--- /dev/null
+++ b/libs/cargs/docs/index.md
@@ -0,0 +1,129 @@
+---
+title: C/C++ Command Line Argument Parser
+description: cargs is a lightweight C/C++ command line argument parser library which can be used to parse argv and argc parameters passed to a main function.
+---
+
+## What is cargs?
+**cargs** is a lightweight C/C++ command line argument parser library which can be used to parse argv and argc parameters passed to a main function.
+
+## Example
+Here is a simple example of cargs in action.
+ ```c
+#include <cargs.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+/**
+ * This is the main configuration of all options available.
+ */
+static struct cag_option options[] = {
+ {.identifier = 's',
+ .access_letters = "s",
+ .access_name = NULL,
+ .value_name = NULL,
+ .description = "Simple flag"},
+
+ {.identifier = 'm',
+ .access_letters = "mMoO",
+ .access_name = NULL,
+ .value_name = NULL,
+ .description = "Multiple access letters"},
+
+ {.identifier = 'l',
+ .access_letters = NULL,
+ .access_name = "long",
+ .value_name = NULL,
+ .description = "Long parameter name"},
+
+ {.identifier = 'k',
+ .access_letters = "k",
+ .access_name = "key",
+ .value_name = "VALUE",
+ .description = "Parameter value"},
+
+ {.identifier = 'h',
+ .access_letters = "h",
+ .access_name = "help",
+ .description = "Shows the command help"}};
+
+/**
+ * This is a custom project configuration structure where you can store the
+ * parsed information.
+ */
+struct demo_configuration
+{
+ bool simple_flag;
+ bool multiple_flag;
+ bool long_flag;
+ const char *key;
+};
+
+int main(int argc, char *argv[])
+{
+ char identifier;
+ const char *value;
+ cag_option_context context;
+ struct demo_configuration config = {false, false, false, NULL};
+
+ /**
+ * Now we just prepare the context and iterate over all options. Simple!
+ */
+ cag_option_prepare(&context, options, CAG_ARRAY_SIZE(options), argc, argv);
+ while (cag_option_fetch(&context)) {
+ identifier = cag_option_get(&context);
+ switch (identifier) {
+ case 's':
+ config.simple_flag = true;
+ break;
+ case 'm':
+ config.multiple_flag = true;
+ break;
+ case 'l':
+ config.long_flag = true;
+ break;
+ case 'k':
+ value = cag_option_get_value(&context);
+ config.key = value;
+ break;
+ case 'h':
+ printf("Usage: cargsdemo [OPTION]...\n");
+ printf("Demonstrates the cargs library.\n\n");
+ cag_option_print(options, CAG_ARRAY_SIZE(options), stdout);
+ printf("\nNote that all formatting is done by cargs.\n");
+ return EXIT_SUCCESS;
+ }
+ }
+
+ printf("simple_flag: %i, multiple_flag: %i, long_flag: %i, key: %s\n",
+ config.simple_flag, config.multiple_flag, config.long_flag,
+ config.key ? config.key : "-");
+
+ return EXIT_SUCCESS;
+}
+
+```
+
+### Example output
+```console
+foo@bar:~$ ./cargsdemo
+simple_flag: 0, multiple_flag: 0, long_flag: 0, key: -
+```
+<br>
+```console
+foo@bar:~$ ./cargsdemo -k=test -sm --long
+simple_flag: 1, multiple_flag: 1, long_flag: 1, key: test
+```
+<br>
+```console
+foo@bar:~$ ./cargsdemo --help
+Usage: cargsdemo [OPTION]...
+Demonstrates the cargs library.
+
+ -s Simple flag
+ -m, -M, -o, -O Multiple access letters
+ --long Long parameter name
+ -k, --key=VALUE Parameter value
+ -h, --help Shows the command help
+
+Note that all formatting is done by cargs.
+```
diff --git a/libs/cargs/docs/reference/cag_option_fetch.md b/libs/cargs/docs/reference/cag_option_fetch.md
new file mode 100755
index 0000000..331ac20
--- /dev/null
+++ b/libs/cargs/docs/reference/cag_option_fetch.md
@@ -0,0 +1,32 @@
+---
+title: cag_option_fetch
+description: Fetches an option from the argument list.
+---
+
+_(since v1.0.0)_
+Fetches an option from the argument list.
+
+## Description
+```c
+bool cag_option_fetch(cag_option_context *context);
+```
+
+This function fetches a single option from the argument list. The context
+will be moved to that item. Information can be extracted from the context
+after the item has been fetched.
+The arguments will be re-ordered, which means that non-option arguments will
+be moved to the end of the argument list. After all options have been
+fetched, all non-option arguments will be positioned after the index of
+the context.
+
+## Parameters
+ * **context**: The context from which we will fetch the option.
+
+## Return Value
+Returns true if there was another option or false if the end is reached.
+
+## Changelog
+
+| Version | Description |
+|------------|--------------------------------------------------------|
+| **v1.0.0** | The function is introduced. |
diff --git a/libs/cargs/docs/reference/cag_option_get.md b/libs/cargs/docs/reference/cag_option_get.md
new file mode 100755
index 0000000..8777744
--- /dev/null
+++ b/libs/cargs/docs/reference/cag_option_get.md
@@ -0,0 +1,26 @@
+---
+title: cag_option_get
+description: Gets the identifier of the option.
+---
+
+_(since v1.0.0)_
+Gets the identifier of the option.
+
+## Description
+```c
+char cag_option_get(const cag_option_context *context);
+```
+This function gets the identifier of the option, which should be unique to
+this option and can be used to determine what kind of option this is.
+
+## Parameters
+ * **context**: The context from which the option was fetched.
+
+## Return Value
+Returns the identifier of the option.
+
+## Changelog
+
+| Version | Description |
+|------------|--------------------------------------------------------|
+| **v1.0.0** | The function is introduced. |
diff --git a/libs/cargs/docs/reference/cag_option_get_index.md b/libs/cargs/docs/reference/cag_option_get_index.md
new file mode 100755
index 0000000..ef27d88
--- /dev/null
+++ b/libs/cargs/docs/reference/cag_option_get_index.md
@@ -0,0 +1,29 @@
+---
+title: cag_option_get_index
+description: Gets the current index of the context.
+---
+
+_(since v1.0.0)_
+Gets the current index of the context.
+
+## Description
+```c
+int cag_option_get_index(const cag_option_context *context);
+```
+
+This function gets the index within the argv arguments of the context. The
+context always points to the next item which it will inspect. This is
+particularly useful to inspect the original argument array, or to get
+non-option arguments after option fetching has finished.
+
+## Parameters
+ * **context**: The context from which the option was fetched.
+
+## Return Value
+Returns the current index of the context.
+
+## Changelog
+
+| Version | Description |
+|------------|--------------------------------------------------------|
+| **v1.0.0** | The function is introduced. |
diff --git a/libs/cargs/docs/reference/cag_option_get_value.md b/libs/cargs/docs/reference/cag_option_get_value.md
new file mode 100755
index 0000000..462e2e7
--- /dev/null
+++ b/libs/cargs/docs/reference/cag_option_get_value.md
@@ -0,0 +1,27 @@
+---
+title: cag_option_get_value
+description: Gets the value from the option.
+---
+
+_(since v1.0.0)_
+Gets the value from the option.
+
+## Description
+```c
+const char *cag_option_get_value(const cag_option_context *context);
+```
+
+This function gets the value from the option, if any. If the option does not
+contain a value, this function will return NULL.
+
+## Parameters
+ * **context**: The context from which the option was fetched.
+
+## Return Value
+Returns a pointer to the value or NULL if there is no value.
+
+## Changelog
+
+| Version | Description |
+|------------|--------------------------------------------------------|
+| **v1.0.0** | The function is introduced. |
diff --git a/libs/cargs/docs/reference/cag_option_prepare.md b/libs/cargs/docs/reference/cag_option_prepare.md
new file mode 100644
index 0000000..c9aa9e5
--- /dev/null
+++ b/libs/cargs/docs/reference/cag_option_prepare.md
@@ -0,0 +1,30 @@
+---
+title: cag_option_prepare
+description: Prepare argument options context for parsing.
+---
+
+_(since v1.0.0)_
+Prepare argument options context for parsing.
+
+## Description
+```c
+void cag_option_prepare(cag_option_context *context, const cag_option *options,
+ size_t option_count, int argc, char **argv);
+```
+
+This function prepares the context for iteration and initializes the context
+with the supplied options and arguments. After the context has been prepared,
+it can be used to fetch arguments from it.
+
+## Parameters
+ * **context**: The context which will be initialized.
+ * **options**: The registered options which are available for the program.
+ * **option_count**: The amount of options which are available for the program.
+ * **argc**: The amount of arguments the user supplied in the main function.
+ * **argv**: A pointer to the arguments of the main function.
+
+## Changelog
+
+| Version | Description |
+|------------|--------------------------------------------------------|
+| **v1.0.0** | The function is introduced. |
diff --git a/libs/cargs/docs/reference/cag_option_print.md b/libs/cargs/docs/reference/cag_option_print.md
new file mode 100755
index 0000000..c4531eb
--- /dev/null
+++ b/libs/cargs/docs/reference/cag_option_print.md
@@ -0,0 +1,28 @@
+---
+title: cag_option_print
+description: Prints all options to the terminal.
+---
+
+_(since v1.0.0)_
+description: Prints all options to the terminal.
+
+## Description
+```c
+void cag_option_print(const cag_option *options, size_t option_count,
+ FILE *destination);
+```
+
+This function prints all options to the terminal. This can be used to generate
+the output for a "--help" option.
+
+## Parameters
+ * **options**: The options which will be printed.
+ * **option_count**: The option count which will be printed.
+ * **destination**: The destination where the output will be printed.
+
+
+## Changelog
+
+| Version | Description |
+|------------|--------------------------------------------------------|
+| **v1.0.0** | The function is introduced. |
diff --git a/libs/cargs/docs/reference/index.md b/libs/cargs/docs/reference/index.md
new file mode 100644
index 0000000..654d9fe
--- /dev/null
+++ b/libs/cargs/docs/reference/index.md
@@ -0,0 +1,30 @@
+---
+title: Reference
+description: A complete reference of the cargs command line arguments parser library for C/C++.
+---
+
+## Basic
+The basic functions available in cargs, which can be used to do basic command line argument parsing.
+
+### Functions
+* **[cag_option_prepare]({{ site.baseurl }}{% link reference/cag_option_prepare.md %})**
+Prepare argument options context for parsing.
+
+* **[cag_option_fetch]({{ site.baseurl }}{% link reference/cag_option_fetch.md %})**
+Fetches an option from the argument list.
+
+* **[cag_option_get]({{ site.baseurl }}{% link reference/cag_option_get.md %})**
+ Gets the identifier of the option.
+
+* **[cag_option_get_value]({{ site.baseurl }}{% link reference/cag_option_get_value.md %})**
+Gets the value from the option.
+
+* **[cag_option_get_index]({{ site.baseurl }}{% link reference/cag_option_get_index.md %})**
+Gets the current index of the context.
+
+## Output
+This section describes functions which are used to output argument information.
+
+### Functions
+* **[cag_option_print]({{ site.baseurl }}{% link reference/cag_option_print.md %})**
+Prints all options to the terminal.
diff --git a/libs/cargs/include/cargs.h b/libs/cargs/include/cargs.h
new file mode 100644
index 0000000..17cba0a
--- /dev/null
+++ b/libs/cargs/include/cargs.h
@@ -0,0 +1,162 @@
+#pragma once
+
+/**
+ * This is a simple alternative cross-platform implementation of getopt, which
+ * is used to parse argument strings submitted to the executable (argc and argv
+ * which are received in the main function).
+ */
+
+#ifndef CAG_LIBRARY_H
+#define CAG_LIBRARY_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+#define CAG_EXPORT __declspec(dllexport)
+#define CAG_IMPORT __declspec(dllimport)
+#elif __GNUC__ >= 4
+#define CAG_EXPORT __attribute__((visibility("default")))
+#define CAG_IMPORT __attribute__((visibility("default")))
+#else
+#define CAG_EXPORT
+#define CAG_IMPORT
+#endif
+
+#if defined(CAG_SHARED)
+#if defined(CAG_EXPORTS)
+#define CAG_PUBLIC CAG_EXPORT
+#else
+#define CAG_PUBLIC CAG_IMPORT
+#endif
+#else
+#define CAG_PUBLIC
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * An option is used to describe a flag/argument option submitted when the
+ * program is run.
+ */
+typedef struct cag_option
+{
+ const char identifier;
+ const char *access_letters;
+ const char *access_name;
+ const char *value_name;
+ const char *description;
+} cag_option;
+
+/**
+ * A context is used to iterate over all options provided. It stores the parsing
+ * state.
+ */
+typedef struct cag_option_context
+{
+ const struct cag_option *options;
+ size_t option_count;
+ int argc;
+ char **argv;
+ int index;
+ int inner_index;
+ bool forced_end;
+ char identifier;
+ char *value;
+} cag_option_context;
+
+/**
+ * This is just a small macro which calculates the size of an array.
+ */
+#define CAG_ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+/**
+ * @brief Prints all options to the terminal.
+ *
+ * This function prints all options to the terminal. This can be used to
+ * generate the output for a "--help" option.
+ *
+ * @param options The options which will be printed.
+ * @param option_count The option count which will be printed.
+ * @param destination The destination where the output will be printed.
+ */
+CAG_PUBLIC void cag_option_print(const cag_option *options, size_t option_count,
+ FILE *destination);
+
+/**
+ * @brief Prepare argument options context for parsing.
+ *
+ * This function prepares the context for iteration and initializes the context
+ * with the supplied options and arguments. After the context has been prepared,
+ * it can be used to fetch arguments from it.
+ *
+ * @param context The context which will be initialized.
+ * @param options The registered options which are available for the program.
+ * @param option_count The amount of options which are available for the
+ * program.
+ * @param argc The amount of arguments the user supplied in the main function.
+ * @param argv A pointer to the arguments of the main function.
+ */
+CAG_PUBLIC void cag_option_prepare(cag_option_context *context,
+ const cag_option *options, size_t option_count, int argc, char **argv);
+
+/**
+ * @brief Fetches an option from the argument list.
+ *
+ * This function fetches a single option from the argument list. The context
+ * will be moved to that item. Information can be extracted from the context
+ * after the item has been fetched.
+ * The arguments will be re-ordered, which means that non-option arguments will
+ * be moved to the end of the argument list. After all options have been
+ * fetched, all non-option arguments will be positioned after the index of
+ * the context.
+ *
+ * @param context The context from which we will fetch the option.
+ * @return Returns true if there was another option or false if the end is
+ * reached.
+ */
+CAG_PUBLIC bool cag_option_fetch(cag_option_context *context);
+
+/**
+ * @brief Gets the identifier of the option.
+ *
+ * This function gets the identifier of the option, which should be unique to
+ * this option and can be used to determine what kind of option this is.
+ *
+ * @param context The context from which the option was fetched.
+ * @return Returns the identifier of the option.
+ */
+CAG_PUBLIC char cag_option_get(const cag_option_context *context);
+
+/**
+ * @brief Gets the value from the option.
+ *
+ * This function gets the value from the option, if any. If the option does not
+ * contain a value, this function will return NULL.
+ *
+ * @param context The context from which the option was fetched.
+ * @return Returns a pointer to the value or NULL if there is no value.
+ */
+CAG_PUBLIC const char *cag_option_get_value(const cag_option_context *context);
+
+/**
+ * @brief Gets the current index of the context.
+ *
+ * This function gets the index within the argv arguments of the context. The
+ * context always points to the next item which it will inspect. This is
+ * particularly useful to inspect the original argument array, or to get
+ * non-option arguments after option fetching has finished.
+ *
+ * @param context The context from which the option was fetched.
+ * @return Returns the current index of the context.
+ */
+CAG_PUBLIC int cag_option_get_index(const cag_option_context *context);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif
diff --git a/libs/cargs/meson.build b/libs/cargs/meson.build
new file mode 100644
index 0000000..5111af1
--- /dev/null
+++ b/libs/cargs/meson.build
@@ -0,0 +1,19 @@
+project('cargs', 'c',
+ license: 'MIT',
+ meson_version: '>= 0.45.1'
+)
+
+cargs_inc = include_directories('include')
+
+cargs = library('cargs', 'src/cargs.c',
+ install: true,
+ include_directories: cargs_inc
+)
+
+install_headers('include/cargs.h')
+
+cargs_dep = declare_dependency(include_directories: 'include', link_with: cargs)
+
+if meson.version().version_compare('>= 0.54.0')
+ meson.override_dependency('cargs', cargs_dep)
+endif
diff --git a/libs/cargs/src/cargs.c b/libs/cargs/src/cargs.c
new file mode 100644
index 0000000..2d7d945
--- /dev/null
+++ b/libs/cargs/src/cargs.c
@@ -0,0 +1,437 @@
+#include <assert.h>
+#include <cargs.h>
+#include <memory.h>
+#include <stdio.h>
+#include <string.h>
+
+#define CAG_OPTION_PRINT_DISTANCE 4
+#define CAG_OPTION_PRINT_MIN_INDENTION 20
+
+static void cag_option_print_value(const cag_option *option,
+ size_t *accessor_length, FILE *destination)
+{
+ if (option->value_name != NULL) {
+ *accessor_length += fprintf(destination, "=%s", option->value_name);
+ }
+}
+
+static void cag_option_print_letters(const cag_option *option, bool *first,
+ size_t *accessor_length, FILE *destination)
+{
+ const char *access_letter;
+ access_letter = option->access_letters;
+ if (access_letter != NULL) {
+ while (*access_letter) {
+ if (*first) {
+ *accessor_length += fprintf(destination, "-%c", *access_letter);
+ *first = false;
+ } else {
+ *accessor_length += fprintf(destination, ", -%c", *access_letter);
+ }
+ ++access_letter;
+ }
+ }
+}
+
+static void cag_option_print_name(const cag_option *option, bool *first,
+ size_t *accessor_length, FILE *destination)
+{
+ if (option->access_name != NULL) {
+ if (*first) {
+ *accessor_length += fprintf(destination, "--%s", option->access_name);
+ } else {
+ *accessor_length += fprintf(destination, ", --%s", option->access_name);
+ }
+ }
+}
+
+static size_t cag_option_get_print_indention(const cag_option *options,
+ size_t option_count)
+{
+ size_t option_index, indention, result;
+ const cag_option *option;
+
+ result = CAG_OPTION_PRINT_MIN_INDENTION;
+
+ for (option_index = 0; option_index < option_count; ++option_index) {
+ indention = CAG_OPTION_PRINT_DISTANCE;
+ option = &options[option_index];
+ if (option->access_letters != NULL && *option->access_letters) {
+ indention += strlen(option->access_letters) * 4 - 2;
+ if (option->access_name != NULL) {
+ indention += strlen(option->access_name) + 4;
+ }
+ } else if (option->access_name != NULL) {
+ indention += strlen(option->access_name) + 2;
+ }
+
+ if (option->value_name != NULL) {
+ indention += strlen(option->value_name) + 1;
+ }
+
+ if (indention > result) {
+ result = indention;
+ }
+ }
+
+ return result;
+}
+
+void cag_option_print(const cag_option *options, size_t option_count,
+ FILE *destination)
+{
+ size_t option_index, indention, i, accessor_length;
+ const cag_option *option;
+ bool first;
+
+ indention = cag_option_get_print_indention(options, option_count);
+
+ for (option_index = 0; option_index < option_count; ++option_index) {
+ option = &options[option_index];
+ accessor_length = 0;
+ first = true;
+
+ fputs(" ", destination);
+
+ cag_option_print_letters(option, &first, &accessor_length, destination);
+ cag_option_print_name(option, &first, &accessor_length, destination);
+ cag_option_print_value(option, &accessor_length, destination);
+
+ for (i = accessor_length; i < indention; ++i) {
+ fputs(" ", destination);
+ }
+
+ fputs(" ", destination);
+ fputs(option->description, destination);
+
+ fprintf(destination, "\n");
+ }
+}
+
+void cag_option_prepare(cag_option_context *context, const cag_option *options,
+ size_t option_count, int argc, char **argv)
+{
+ // This just initialized the values to the beginning of all the arguments.
+ context->options = options;
+ context->option_count = option_count;
+ context->argc = argc;
+ context->argv = argv;
+ context->index = 1;
+ context->inner_index = 0;
+ context->forced_end = false;
+}
+
+static const cag_option *cag_option_find_by_name(cag_option_context *context,
+ char *name, size_t name_size)
+{
+ const cag_option *option;
+ size_t i;
+
+ // We loop over all the available options and stop as soon as we have found
+ // one. We don't use any hash map table, since there won't be that many
+ // arguments anyway.
+ for (i = 0; i < context->option_count; ++i) {
+ option = &context->options[i];
+
+ // The option might not have an item name, we can just skip those.
+ if (option->access_name == NULL) {
+ continue;
+ }
+
+ // Try to compare the name of the access name. We can use the name_size or
+ // this comparison, since we are guaranteed to have null-terminated access
+ // names.
+ if (strncmp(option->access_name, name, name_size) == 0) {
+ return option;
+ }
+ }
+
+ return NULL;
+}
+
+static const cag_option *cag_option_find_by_letter(cag_option_context *context,
+ char letter)
+{
+ const cag_option *option;
+ size_t i;
+
+ // We loop over all the available options and stop as soon as we have found
+ // one. We don't use any look up table, since there won't be that many
+ // arguments anyway.
+ for (i = 0; i < context->option_count; ++i) {
+ option = &context->options[i];
+
+ // If this option doesn't have any access letters we will skip them.
+ if (option->access_letters == NULL) {
+ continue;
+ }
+
+ // Verify whether this option has the access letter in it's access letter
+ // string. If it does, then this is our option.
+ if (strchr(option->access_letters, letter) != NULL) {
+ return option;
+ }
+ }
+
+ return NULL;
+}
+
+static void cag_option_parse_value(cag_option_context *context,
+ const cag_option *option, char **c)
+{
+ // And now let's check whether this option is supposed to have a value, which
+ // is the case if there is a value name set. The value can be either submitted
+ // with a '=' sign or a space, which means we would have to jump over to the
+ // next argv index. This is somewhat ugly, but we do it to behave the same as
+ // the other option parsers.
+ if (option->value_name != NULL) {
+ if (**c == '=') {
+ context->value = ++(*c);
+ } else {
+ // If the next index is larger or equal to the argument count, then the
+ // parameter for this option is missing. The user will know about this,
+ // since the value pointer of the context will be NULL because we don't
+ // set it here in that case.
+ if (context->argc > context->index + 1) {
+ // We consider this argv to be the value, no matter what the contents
+ // are.
+ ++context->index;
+ *c = context->argv[context->index];
+ context->value = *c;
+ }
+ }
+
+ // Move c to the end of the value, to not confuse the caller about our
+ // position.
+ while (**c) {
+ ++(*c);
+ }
+ }
+}
+
+static void cag_option_parse_access_name(cag_option_context *context, char **c)
+{
+ const cag_option *option;
+ char *n;
+
+ // Now we need to extract the access name, which is any symbol up to a '=' or
+ // a '\0'.
+ n = *c;
+ while (**c && **c != '=') {
+ ++*c;
+ }
+
+ // Now this will obviously always be true, but we are paranoid. Sometimes. It
+ // doesn't hurt to check.
+ assert(*c >= n);
+
+ // Figure out which option this name belongs to. This might return NULL if the
+ // name is not registered, which means the user supplied an unknown option. In
+ // that case we return true to indicate that we finished with this option. We
+ // have to skip the value parsing since we don't know whether the user thinks
+ // this option has one or not. Since we don't set any identifier specifically,
+ // it will remain '?' within the context.
+ option = cag_option_find_by_name(context, n, (size_t)(*c - n));
+ if (option == NULL) {
+ // Since this option is invalid, we will move on to the next index. There is
+ // nothing we can do about this.
+ ++context->index;
+ return;
+ }
+
+ // We found an option and now we can specify the identifier within the
+ // context.
+ context->identifier = option->identifier;
+
+ // And now we try to parse the value. This function will also check whether
+ // this option is actually supposed to have a value.
+ cag_option_parse_value(context, option, c);
+
+ // And finally we move on to the next index.
+ ++context->index;
+}
+
+static void cag_option_parse_access_letter(cag_option_context *context,
+ char **c)
+{
+ const cag_option *option;
+ char *n = *c;
+ char *v;
+
+ // Figure out which option this letter belongs to. This might return NULL if
+ // the letter is not registered, which means the user supplied an unknown
+ // option. In that case we return true to indicate that we finished with this
+ // option. We have to skip the value parsing since we don't know whether the
+ // user thinks this option has one or not. Since we don't set any identifier
+ // specifically, it will remain '?' within the context.
+ option = cag_option_find_by_letter(context, n[context->inner_index]);
+ if (option == NULL) {
+ ++context->index;
+ context->inner_index = 0;
+ return;
+ }
+
+ // We found an option and now we can specify the identifier within the
+ // context.
+ context->identifier = option->identifier;
+
+ // And now we try to parse the value. This function will also check whether
+ // this option is actually supposed to have a value.
+ v = &n[++context->inner_index];
+ cag_option_parse_value(context, option, &v);
+
+ // Check whether we reached the end of this option argument.
+ if (*v == '\0') {
+ ++context->index;
+ context->inner_index = 0;
+ }
+}
+
+static void cag_option_shift(cag_option_context *context, int start, int option,
+ int end)
+{
+ char *tmp;
+ int a_index, shift_index, shift_count, left_index, right_index;
+
+ shift_count = option - start;
+
+ // There is no shift is required if the start and the option have the same
+ // index.
+ if (shift_count == 0) {
+ return;
+ }
+
+ // Lets loop through the option strings first, which we will move towards the
+ // beginning.
+ for (a_index = option; a_index < end; ++a_index) {
+ // First remember the current option value, because we will have to save
+ // that later at the beginning.
+ tmp = context->argv[a_index];
+
+ // Let's loop over all option values and shift them one towards the end.
+ // This will override the option value we just stored temporarily.
+ for (shift_index = 0; shift_index < shift_count; ++shift_index) {
+ left_index = a_index - shift_index;
+ right_index = a_index - shift_index - 1;
+ context->argv[left_index] = context->argv[right_index];
+ }
+
+ // Now restore the saved option value at the beginning.
+ context->argv[a_index - shift_count] = tmp;
+ }
+
+ // The new index will be before all non-option values, in such a way that they
+ // all will be moved again in the next fetch call.
+ context->index = end - shift_count;
+}
+
+static bool cag_option_is_argument_string(const char *c)
+{
+ return *c == '-' && *(c + 1) != '\0';
+}
+
+static int cag_option_find_next(cag_option_context *context)
+{
+ int next_index, next_option_index;
+ char *c;
+
+ // Prepare to search the next option at the next index.
+ next_index = context->index;
+ next_option_index = next_index;
+
+ // Grab a pointer to the string and verify that it is not the end. If it is
+ // the end, we have to return false to indicate that we finished.
+ c = context->argv[next_option_index];
+ if (context->forced_end || c == NULL) {
+ return -1;
+ }
+
+ // Check whether it is a '-'. We need to find the next option - and an option
+ // always starts with a '-'. If there is a string "-\0", we don't consider it
+ // as an option neither.
+ while (!cag_option_is_argument_string(c)) {
+ c = context->argv[++next_option_index];
+ if (c == NULL) {
+ // We reached the end and did not find any argument anymore. Let's tell
+ // our caller that we reached the end.
+ return -1;
+ }
+ }
+
+ // Indicate that we found an option which can be processed. The index of the
+ // next option will be returned.
+ return next_option_index;
+}
+
+bool cag_option_fetch(cag_option_context *context)
+{
+ char *c;
+ int old_index, new_index;
+
+ // Reset our identifier to a question mark, which indicates an "unknown"
+ // option. The value is set to NULL, to make sure we are not carrying the
+ // parameter from the previous option to this one.
+ context->identifier = '?';
+ context->value = NULL;
+
+ // Check whether there are any options left to parse and remember the old
+ // index as well as the new index. In the end we will move the option junk to
+ // the beginning, so that non option arguments can be read.
+ old_index = context->index;
+ new_index = cag_option_find_next(context);
+ if (new_index >= 0) {
+ context->index = new_index;
+ } else {
+ return false;
+ }
+
+ // Grab a pointer to the beginning of the option. At this point, the next
+ // character must be a '-', since if it was not the prepare function would
+ // have returned false. We will skip that symbol and proceed.
+ c = context->argv[context->index];
+ assert(*c == '-');
+ ++c;
+
+ // Check whether this is a long option, starting with a double "--".
+ if (*c == '-') {
+ ++c;
+
+ // This might be a double "--" which indicates the end of options. If this
+ // is the case, we will not move to the next index. That ensures that
+ // another call to the fetch function will not skip the "--".
+ if (*c == '\0') {
+ context->forced_end = true;
+ } else {
+ // We parse now the access name. All information about it will be written
+ // to the context.
+ cag_option_parse_access_name(context, &c);
+ }
+ } else {
+ // This is no long option, so we can just parse an access letter.
+ cag_option_parse_access_letter(context, &c);
+ }
+
+ // Move the items so that the options come first followed by non-option
+ // arguments.
+ cag_option_shift(context, old_index, new_index, context->index);
+
+ return context->forced_end == false;
+}
+
+char cag_option_get(const cag_option_context *context)
+{
+ // We just return the identifier here.
+ return context->identifier;
+}
+
+const char *cag_option_get_value(const cag_option_context *context)
+{
+ // We just return the internal value pointer of the context.
+ return context->value;
+}
+
+int cag_option_get_index(const cag_option_context *context)
+{
+ // Either we point to a value item,
+ return context->index;
+}
diff --git a/libs/cargs/test/definitions.h b/libs/cargs/test/definitions.h
new file mode 100755
index 0000000..cdb820e
--- /dev/null
+++ b/libs/cargs/test/definitions.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include "tests.h"
+/**
+ * Creates prototypes for all test functions.
+ */
+#define XX(u, t) int u##_##t(void);
+UNIT_TESTS(XX)
+#undef XX
diff --git a/libs/cargs/test/main.c b/libs/cargs/test/main.c
new file mode 100755
index 0000000..229cf80
--- /dev/null
+++ b/libs/cargs/test/main.c
@@ -0,0 +1,105 @@
+#include "definitions.h"
+#include <cargs.h>
+#include <memory.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct cag_test
+{
+ const char *unit_name;
+ const char *test_name;
+ const char *full_name;
+ int (*fn)(void);
+};
+
+static struct cag_test tests[] = {
+#define XX(u, t) \
+ {.unit_name = #u, .test_name = #t, .full_name = #u "/" #t, .fn = u##_##t},
+ UNIT_TESTS(XX)
+#undef XX
+};
+
+static int call_test(struct cag_test *test)
+{
+ size_t i;
+
+ printf(" Running '%s' ", test->full_name);
+ for (i = strlen(test->full_name); i < 40; ++i) {
+ fputs(".", stdout);
+ }
+
+ if (test->fn() == EXIT_FAILURE) {
+ fputs(" FAILURE\n", stdout);
+ return EXIT_FAILURE;
+ }
+
+ fputs(" SUCCESS\n", stdout);
+ return EXIT_SUCCESS;
+}
+
+int main(int argc, char *argv[])
+{
+ size_t i, count, failed, succeeded;
+ const char *requested_unit_name, *requested_test_name;
+ struct cag_test *test;
+ double rate;
+
+ count = 0;
+ failed = 0;
+ if (argc < 2) {
+ fputs("No unit specified. Running all tests.\n\n", stdout);
+ for (i = 0; i < CAG_ARRAY_SIZE(tests); ++i) {
+ test = &tests[i];
+ ++count;
+ if (call_test(test) == EXIT_FAILURE) {
+ ++failed;
+ }
+ }
+ } else if (argc < 3) {
+ requested_unit_name = argv[1];
+ printf("Running all unit tests of '%s'.\n\n", requested_unit_name);
+ for (i = 0; i < CAG_ARRAY_SIZE(tests); ++i) {
+ test = &tests[i];
+ if (strcmp(test->unit_name, requested_unit_name) == 0) {
+ ++count;
+ if (call_test(test) == EXIT_FAILURE) {
+ ++failed;
+ }
+ }
+ }
+ } else {
+ requested_unit_name = argv[1];
+ requested_test_name = argv[2];
+ printf("Running a single test '%s/%s'.\n\n", requested_unit_name,
+ requested_test_name);
+ for (i = 0; i < CAG_ARRAY_SIZE(tests); ++i) {
+ test = &tests[i];
+ if (strcmp(test->unit_name, requested_unit_name) == 0 &&
+ strcmp(test->test_name, requested_test_name) == 0) {
+ ++count;
+ if (call_test(test) == EXIT_FAILURE) {
+ ++failed;
+ }
+ }
+ }
+ }
+
+ if (count == 1) {
+ fputs("\nThe test has been executed.\n", stdout);
+ } else if (count > 0) {
+ succeeded = count - failed;
+ rate = (double)succeeded / (double)count * 100;
+ printf("\n%zu/%zu (%.2f%%) of those tests succeeded.\n", succeeded, count,
+ rate);
+ } else {
+ printf("\nNo tests found.\n");
+ return EXIT_FAILURE;
+ }
+
+ if (failed > 0) {
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/libs/cargs/test/option_test.c b/libs/cargs/test/option_test.c
new file mode 100755
index 0000000..c2dfff4
--- /dev/null
+++ b/libs/cargs/test/option_test.c
@@ -0,0 +1,665 @@
+#include "definitions.h"
+#include <assert.h>
+#include <cargs.h>
+#include <limits.h>
+#include <memory.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static struct cag_option options[] = {
+
+ {.identifier = 's',
+ .access_letters = "s",
+ .access_name = NULL,
+ .value_name = NULL,
+ .description = "Simple flag"},
+
+ {.identifier = 'a',
+ .access_letters = "a",
+ .access_name = NULL,
+ .value_name = NULL,
+ .description = "Another simple flag"},
+
+ {.identifier = 'm',
+ .access_letters = "mMoO",
+ .access_name = NULL,
+ .value_name = NULL,
+ .description = "Multiple access letters"},
+
+ {.identifier = 'l',
+ .access_letters = NULL,
+ .access_name = "long",
+ .value_name = NULL,
+ .description = "Long parameter name"},
+
+ {.identifier = 'k',
+ .access_letters = "k",
+ .access_name = "key",
+ .value_name = "VALUE",
+ .description = "Parameter value"}};
+
+struct cag_result
+{
+ bool simple;
+ bool another;
+ bool multi_access;
+ bool long_parameter;
+ bool value_parameter;
+ bool unknown;
+ bool def;
+ const char *value;
+};
+
+static struct cag_result result;
+static char **argv;
+static int argc;
+
+static int make_args(const char *str)
+{
+ const char *c;
+ int argIndex, argStart, argLength;
+
+ argc = 0;
+ c = str;
+ do {
+ if (*c == ' ' || *c == '\0') {
+ ++argc;
+ }
+ } while (*(c++));
+
+ argv = malloc(sizeof(char *) * (argc + 1));
+ if (argv == NULL) {
+ return 1;
+ }
+
+ c = str;
+ argIndex = 0;
+ argStart = 0;
+ argLength = 0;
+ do {
+ if (*c == ' ' || *c == '\0') {
+ argv[argIndex] = malloc(argLength + 1);
+ memcpy(argv[argIndex], &str[argStart], argLength);
+ argv[argIndex][argLength] = '\0';
+ ++argIndex;
+ argStart += argLength + 1;
+ argLength = 0;
+ } else {
+ ++argLength;
+ }
+
+ } while (*(c++));
+
+ argv[argc] = NULL;
+
+ return 0;
+}
+
+static void destroy_args()
+{
+ int i;
+
+ for (i = 0; i < argc; ++i) {
+ free(argv[i]);
+ }
+
+ free(argv);
+}
+
+static int option_test_run(int currentArgc, char *currentArgv[])
+{
+ int index;
+ char identifier;
+ cag_option_context context;
+
+ cag_option_prepare(&context, options, CAG_ARRAY_SIZE(options), currentArgc,
+ currentArgv);
+
+ memset(&result, 0, sizeof(result));
+
+ while (cag_option_fetch(&context)) {
+ identifier = cag_option_get(&context);
+ switch (identifier) {
+ case 's':
+ result.simple = true;
+ break;
+ case 'a':
+ result.another = true;
+ break;
+ case 'm':
+ result.multi_access = true;
+ break;
+ case 'l':
+ result.long_parameter = true;
+ break;
+ case 'k':
+ result.value_parameter = true;
+ result.value = cag_option_get_value(&context);
+ break;
+ case '?':
+ result.unknown = true;
+ break;
+ default:
+ result.def = true;
+ break;
+ }
+ }
+
+ index = cag_option_get_index(&context);
+ if (cag_option_fetch(&context) != false) {
+ return -1;
+ }
+
+ if (cag_option_get_index(&context) != index) {
+ return -1;
+ }
+
+ return cag_option_get_index(&context);
+}
+
+int option_complex(void)
+{
+ int status;
+
+ status = make_args("test file1 -s -- -a -- a file");
+ if (status != 0) {
+ goto err_setup;
+ }
+
+ status = option_test_run(argc, argv);
+ if (status < 0) {
+ goto err_wrong;
+ }
+
+ if (!result.simple || result.another || result.multi_access ||
+ result.long_parameter || result.value_parameter || result.unknown ||
+ result.def || result.value != NULL) {
+ goto err_wrong;
+ }
+
+ destroy_args();
+
+ return EXIT_SUCCESS;
+
+err_wrong:
+ destroy_args();
+err_setup:
+ return EXIT_FAILURE;
+}
+
+int option_mixed(void)
+{
+ int status, i;
+
+ const char *values[] = {"file1", "file2", "mixed", "file3", "--", "-m",
+ "parameters", "file4"};
+
+ status = make_args(
+ "test -s file1 -k=value file2 -a mixed file3 -- -m parameters file4");
+ if (status != 0) {
+ goto err_setup;
+ }
+
+ status = option_test_run(argc, argv);
+ if (status < 0 || argc - status != 8) {
+ goto err_wrong;
+ }
+
+ for (i = 0; i < (int)CAG_ARRAY_SIZE(values); ++i) {
+ if (strcmp(argv[status + i], values[i]) != 0) {
+ goto err_wrong;
+ }
+ }
+
+ if (!result.simple || !result.another || result.multi_access ||
+ result.long_parameter || !result.value_parameter || result.unknown ||
+ result.def || result.value == NULL ||
+ strcmp(result.value, "value") != 0) {
+ goto err_wrong;
+ }
+
+ destroy_args();
+
+ return EXIT_SUCCESS;
+
+err_wrong:
+ destroy_args();
+err_setup:
+ return EXIT_FAILURE;
+}
+
+int option_ending(void)
+{
+ int status;
+
+ status = make_args("test -s -- -a");
+ if (status != 0) {
+ goto err_setup;
+ }
+
+ status = option_test_run(argc, argv);
+ if (status < 0) {
+ goto err_wrong;
+ }
+
+ if (!result.simple || result.another || result.multi_access ||
+ result.long_parameter || result.value_parameter || result.unknown ||
+ result.def || result.value != NULL) {
+ goto err_wrong;
+ }
+
+ destroy_args();
+
+ return EXIT_SUCCESS;
+
+err_wrong:
+ destroy_args();
+err_setup:
+ return EXIT_FAILURE;
+}
+
+int option_long_missing_value(void)
+{
+ int status;
+
+ status = make_args("test --key");
+ if (status != 0) {
+ goto err_setup;
+ }
+
+ status = option_test_run(argc, argv);
+ if (status < 0) {
+ goto err_wrong;
+ }
+
+ if (result.simple || result.another || result.multi_access ||
+ result.long_parameter || !result.value_parameter || result.unknown ||
+ result.def || result.value != NULL) {
+ goto err_wrong;
+ }
+
+ destroy_args();
+
+ return EXIT_SUCCESS;
+
+err_wrong:
+ destroy_args();
+err_setup:
+ return EXIT_FAILURE;
+}
+
+int option_short_missing_value(void)
+{
+ int status;
+
+ status = make_args("test -k");
+ if (status != 0) {
+ goto err_setup;
+ }
+
+ status = option_test_run(argc, argv);
+ if (status < 0) {
+ goto err_wrong;
+ }
+
+ if (result.simple || result.another || result.multi_access ||
+ result.long_parameter || !result.value_parameter || result.unknown ||
+ result.def || result.value != NULL) {
+ goto err_wrong;
+ }
+
+ destroy_args();
+
+ return EXIT_SUCCESS;
+
+err_wrong:
+ destroy_args();
+err_setup:
+ return EXIT_FAILURE;
+}
+
+int option_long_space_value(void)
+{
+ int status;
+
+ status = make_args("test --key super_value");
+ if (status != 0) {
+ goto err_setup;
+ }
+
+ status = option_test_run(argc, argv);
+ if (status < 0) {
+ goto err_wrong;
+ }
+
+ if (result.simple || result.another || result.multi_access ||
+ result.long_parameter || !result.value_parameter || result.unknown ||
+ result.def || result.value == NULL ||
+ strcmp(result.value, "super_value") != 0) {
+ goto err_wrong;
+ }
+
+ destroy_args();
+
+ return EXIT_SUCCESS;
+
+err_wrong:
+ destroy_args();
+err_setup:
+ return EXIT_FAILURE;
+}
+
+int option_short_space_value(void)
+{
+ int status;
+
+ status = make_args("test -k test_value");
+ if (status != 0) {
+ goto err_setup;
+ }
+
+ status = option_test_run(argc, argv);
+ if (status < 0) {
+ goto err_wrong;
+ }
+
+ if (result.simple || result.another || result.multi_access ||
+ result.long_parameter || !result.value_parameter || result.unknown ||
+ result.def || result.value == NULL ||
+ strcmp(result.value, "test_value") != 0) {
+ goto err_wrong;
+ }
+
+ destroy_args();
+
+ return EXIT_SUCCESS;
+
+err_wrong:
+ destroy_args();
+err_setup:
+ return EXIT_FAILURE;
+}
+
+int option_long_equal_value(void)
+{
+ int status;
+
+ status = make_args("test --key=super_value");
+ if (status != 0) {
+ goto err_setup;
+ }
+
+ status = option_test_run(argc, argv);
+ if (status < 0) {
+ goto err_wrong;
+ }
+
+ if (result.simple || result.another || result.multi_access ||
+ result.long_parameter || !result.value_parameter || result.unknown ||
+ result.def || result.value == NULL ||
+ strcmp(result.value, "super_value") != 0) {
+ goto err_wrong;
+ }
+
+ destroy_args();
+
+ return EXIT_SUCCESS;
+
+err_wrong:
+ destroy_args();
+err_setup:
+ return EXIT_FAILURE;
+}
+
+int option_short_equal_value(void)
+{
+ int status;
+
+ status = make_args("test -k=test_value");
+ if (status != 0) {
+ goto err_setup;
+ }
+
+ status = option_test_run(argc, argv);
+ if (status < 0) {
+ goto err_wrong;
+ }
+
+ if (result.simple || result.another || result.multi_access ||
+ result.long_parameter || !result.value_parameter || result.unknown ||
+ result.def || result.value == NULL ||
+ strcmp(result.value, "test_value") != 0) {
+ goto err_wrong;
+ }
+
+ destroy_args();
+
+ return EXIT_SUCCESS;
+
+err_wrong:
+ destroy_args();
+err_setup:
+ return EXIT_FAILURE;
+}
+
+int option_combined(void)
+{
+ int status;
+
+ status = make_args("test -sma");
+ if (status != 0) {
+ goto err_setup;
+ }
+
+ status = option_test_run(argc, argv);
+ if (status < 0) {
+ goto err_wrong;
+ }
+
+ if (!result.simple || !result.another || !result.multi_access ||
+ result.long_parameter || result.value_parameter || result.unknown ||
+ result.def || result.value != NULL) {
+ goto err_wrong;
+ }
+
+ destroy_args();
+
+ return EXIT_SUCCESS;
+
+err_wrong:
+ destroy_args();
+err_setup:
+ return EXIT_FAILURE;
+}
+
+int option_unknown_long(void)
+{
+ int status;
+
+ status = make_args("test --unknown");
+ if (status != 0) {
+ goto err_setup;
+ }
+
+ status = option_test_run(argc, argv);
+ if (status < 0) {
+ goto err_wrong;
+ }
+
+ if (result.simple || result.another || result.multi_access ||
+ result.long_parameter || result.value_parameter || !result.unknown ||
+ result.def || result.value != NULL) {
+ goto err_wrong;
+ }
+
+ destroy_args();
+
+ return EXIT_SUCCESS;
+
+err_wrong:
+ destroy_args();
+err_setup:
+ return EXIT_FAILURE;
+}
+
+int option_unknown_short(void)
+{
+ int status;
+
+ status = make_args("test -u");
+ if (status != 0) {
+ goto err_setup;
+ }
+
+ status = option_test_run(argc, argv);
+ if (status < 0) {
+ goto err_wrong;
+ }
+
+ if (result.simple || result.another || result.multi_access ||
+ result.long_parameter || result.value_parameter || !result.unknown ||
+ result.def || result.value != NULL) {
+ goto err_wrong;
+ }
+
+ destroy_args();
+
+ return EXIT_SUCCESS;
+
+err_wrong:
+ destroy_args();
+err_setup:
+ return EXIT_FAILURE;
+}
+
+int option_alias(void)
+{
+ int status;
+
+ status = make_args("test -O");
+ if (status != 0) {
+ goto err_setup;
+ }
+
+ status = option_test_run(argc, argv);
+ if (status < 0) {
+ goto err_wrong;
+ }
+
+ if (result.simple || result.another || !result.multi_access ||
+ result.long_parameter || result.value_parameter || result.unknown ||
+ result.def || result.value != NULL) {
+ goto err_wrong;
+ }
+
+ destroy_args();
+
+ return EXIT_SUCCESS;
+
+err_wrong:
+ destroy_args();
+err_setup:
+ return EXIT_FAILURE;
+}
+
+int option_simple_long(void)
+{
+ int status;
+
+ status = make_args("test --long");
+ if (status != 0) {
+ goto err_setup;
+ }
+
+ status = option_test_run(argc, argv);
+ if (status < 0) {
+ goto err_wrong;
+ }
+
+ if (result.simple || result.another || result.multi_access ||
+ !result.long_parameter || result.value_parameter || result.unknown ||
+ result.def || result.value != NULL) {
+ goto err_wrong;
+ }
+
+ destroy_args();
+
+ return EXIT_SUCCESS;
+
+err_wrong:
+ destroy_args();
+err_setup:
+ return EXIT_FAILURE;
+}
+
+int option_simple(void)
+{
+ int status;
+
+ status = make_args("test -s");
+ if (status != 0) {
+ goto err_setup;
+ }
+
+ status = option_test_run(argc, argv);
+ if (status < 0) {
+ goto err_wrong;
+ }
+
+ if (!result.simple || result.another || result.multi_access ||
+ result.long_parameter || result.value_parameter || result.unknown ||
+ result.def || result.value != NULL) {
+ goto err_wrong;
+ }
+
+ destroy_args();
+
+ return EXIT_SUCCESS;
+
+err_wrong:
+ destroy_args();
+err_setup:
+ return EXIT_FAILURE;
+}
+
+int option_print(void)
+{
+ char buf[255];
+ const char *expected;
+ FILE *test_file;
+
+ expected = " -s Simple flag\n"
+ " -a Another simple flag\n"
+ " -m, -M, -o, -O Multiple access letters\n"
+ " --long Long parameter name\n"
+ " -k, --key=VALUE Parameter value\n";
+
+ test_file = tmpfile();
+ if (test_file == NULL) {
+ goto err_open;
+ }
+
+ cag_option_print(options, CAG_ARRAY_SIZE(options), test_file);
+ if (fseek(test_file, 0, SEEK_SET) != 0) {
+ goto err_seek;
+ }
+
+ if (fread(buf, sizeof(buf), 1, test_file) != 1 && feof(test_file) == 0) {
+ goto err_read;
+ }
+
+ if (memcmp(buf, expected, strlen(expected)) != 0) {
+ goto err_test;
+ }
+
+ fclose(test_file);
+ return EXIT_SUCCESS;
+
+err_test:
+err_read:
+err_seek:
+ fclose(test_file);
+err_open:
+ return EXIT_FAILURE;
+}
diff --git a/src/gl/gl.c b/src/gl/gl.c
index 4639d48..106befe 100644
--- a/src/gl/gl.c
+++ b/src/gl/gl.c
@@ -17,6 +17,8 @@ int gl_init(lua_State *L);
int glad_init(lua_State *L);
int gl_terminate(lua_State *L);
int gl_get_error(lua_State *L);
+int gl_enable(lua_State *L);
+int gl_disable(lua_State *L);
void setup_gl(lua_State *L, int honey_index)
{
@@ -26,6 +28,8 @@ void setup_gl(lua_State *L, int honey_index)
hs_str_cfunc("InitGlad", glad_init),
hs_str_cfunc("Terminate", gl_terminate),
hs_str_cfunc("GetError", gl_get_error),
+ hs_str_cfunc("Enable", gl_enable),
+ hs_str_cfunc("Disable", gl_disable),
/******** enums ********/
/* data types */
@@ -41,6 +45,9 @@ void setup_gl(lua_State *L, int honey_index)
hs_str_int("INVALID_OPERATION", GL_INVALID_OPERATION),
hs_str_int("INVALID_FRAMEBUFFER_OPERATION", GL_INVALID_FRAMEBUFFER_OPERATION),
hs_str_int("OUT_OF_MEMORY", GL_OUT_OF_MEMORY),
+
+ /* opengl capabilities */
+ hs_str_int("DEPTH_TEST", GL_DEPTH_TEST),
);
setup_shader(L, gl_index);
@@ -81,3 +88,21 @@ int gl_get_error(lua_State *L)
lua_pushinteger(L, glGetError());
return 1;
}
+
+
+int gl_enable(lua_State *L)
+{
+ lua_Integer cap;
+ hs_parse_args(L, hs_int(cap));
+ glEnable(cap);
+ return 0;
+}
+
+
+int gl_disable(lua_State *L)
+{
+ lua_Integer cap;
+ hs_parse_args(L, hs_int(cap));
+ glDisable(cap);
+ return 0;
+}
diff --git a/src/glm/glm.c b/src/glm/glm.c
index 525a029..ca6238a 100644
--- a/src/glm/glm.c
+++ b/src/glm/glm.c
@@ -1,5 +1,6 @@
#include <lua.h>
#include <honeysuckle.h>
+#include "glm.h"
void setup_glm(lua_State *L, int honey_index)
diff --git a/src/main.c b/src/main.c
index 5f5d399..304d91d 100644
--- a/src/main.c
+++ b/src/main.c
@@ -5,13 +5,22 @@
#include "gl/gl.h"
#include "image/image.h"
#include "glm/glm.h"
+#include "options/options.h"
int main(int argc, char **argv)
{
+ /* parse command-line options */
+ struct honey_options options;
+ int result = parse_options(&options, argc, argv);
+ if (result == EXIT_FAILURE) return 1;
+ else if (result == EXIT_SUCCESS) return 0;
+
+ /* set up lua state */
lua_State *L = luaL_newstate();
luaL_openlibs(L);
+ /* load honey bindings */
lua_createtable(L, 0, 2);
int honey_index = lua_gettop(L);
setup_gl(L, honey_index);
@@ -20,17 +29,22 @@ int main(int argc, char **argv)
setup_glm(L, honey_index);
lua_setglobal(L, "honey");
- int err = luaL_loadfile(L, "honey.lua");
+ /* load main script */
+ int err = luaL_loadfile(L, options.script_file);
if (err != 0) {
- printf("cannot open file!\n");
+ printf("cannot open file '%s'\n", options.script_file);
lua_close(L);
return 0;
}
+
+ /* run */
err = hs_call(L, 0, 0);
if (err != 0) {
const char *err_str = lua_tostring(L, -1);
printf("failed to run: \n%s\n", err_str);
}
+
+ /* clean up */
lua_close(L);
return 0;
}
diff --git a/src/options/CMakeLists.txt b/src/options/CMakeLists.txt
new file mode 100644
index 0000000..39ed7db
--- /dev/null
+++ b/src/options/CMakeLists.txt
@@ -0,0 +1,5 @@
+project(honey_engine)
+
+target_sources(honey PUBLIC
+ ${CMAKE_CURRENT_LIST_DIR}/options.c
+)
diff --git a/src/options/options.c b/src/options/options.c
new file mode 100644
index 0000000..4488837
--- /dev/null
+++ b/src/options/options.c
@@ -0,0 +1,56 @@
+#include <stdio.h>
+#include <cargs.h>
+#include "options.h"
+
+static struct cag_option opts[] = {
+ {
+ .identifier = 's',
+ .access_letters = "s",
+ .access_name = "script",
+ .value_name = "SCRIPT_FILE",
+ .description = "The filename of the main script. (default: main.lua)"
+ },
+ {
+ .identifier = 'h',
+ .access_letters = "h",
+ .access_name = "help",
+ .value_name = NULL,
+ .description = "Shows this help message"
+ },
+};
+
+
+void print_help(char *program_name)
+{
+ printf("usage: %s [OPTIONS]\n", program_name);
+ cag_option_print(opts, CAG_ARRAY_SIZE(opts), stdout);
+}
+
+
+enum outcomes_t parse_options(struct honey_options *options, int argc, char **argv)
+{
+ /* default values */
+ options->script_file = "main.lua";
+
+ /* parse options */
+ char id;
+ const char *value;
+ cag_option_context context;
+
+ cag_option_prepare(&context, opts, CAG_ARRAY_SIZE(opts), argc, argv);
+ while(cag_option_fetch(&context)) {
+ id = cag_option_get(&context);
+ switch(id) {
+ case 's':
+ options->script_file = cag_option_get_value(&context);
+ break;
+ case 'h':
+ print_help(argv[0]);
+ return EXIT_SUCCESS;
+ default:
+ return EXIT_FAILURE;
+ }
+ }
+
+ return CONTINUE_SUCCESS;
+}
diff --git a/src/options/options.h b/src/options/options.h
new file mode 100644
index 0000000..cb07b1a
--- /dev/null
+++ b/src/options/options.h
@@ -0,0 +1,16 @@
+#ifndef HONEY_OPTIONS_H
+#define HONEY_OPTIONS_H
+
+struct honey_options {
+ const char *script_file; // main entry point
+};
+
+enum outcomes_t {
+ CONTINUE_SUCCESS,
+ EXIT_SUCCESS,
+ EXIT_FAILURE,
+};
+
+enum outcomes_t parse_options(struct honey_options *options, int argc, char **argv);
+
+#endif