diff options
author | sanine <sanine.not@pm.me> | 2022-08-24 00:02:17 -0500 |
---|---|---|
committer | sanine <sanine.not@pm.me> | 2022-08-24 00:02:17 -0500 |
commit | 2cb3c3df4099297b0a0554bb482e2de04fe86b5c (patch) | |
tree | 7796b4064c16460d9d603707b5256027649aa8b6 | |
parent | 709e1b6e1ce86f8da4fc136747fcefbc6c6057bd (diff) |
add command-line arguments
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 Binary files differdeleted file mode 100644 index 73c5d1d..0000000 --- a/demo/.honey.lua.swo +++ /dev/null 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 Binary files differnew file mode 100755 index 0000000..7de0493 --- /dev/null +++ b/libs/cargs/banner.png 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 Binary files differnew file mode 100644 index 0000000..b4d46f6 --- /dev/null +++ b/libs/cargs/docs/assets/img/favicon.png 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) @@ -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 |