├── .editorconfig ├── .github └── workflows │ └── main.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake └── CodeCoverage.cmake ├── docs └── doxygen │ ├── .gitignore │ ├── CMakeLists.txt │ └── Doxyfile.in ├── examples ├── CMakeLists.txt ├── README.md ├── append_clear_option │ ├── CMakeLists.txt │ ├── README.md │ └── append_clear_option.cpp ├── area │ ├── CMakeLists.txt │ ├── README.md │ └── area.cpp ├── commands │ ├── CMakeLists.txt │ ├── README.md │ └── commands.cpp ├── hello │ ├── CMakeLists.txt │ ├── README.md │ └── hello.cpp ├── list_options │ ├── CMakeLists.txt │ ├── README.md │ └── list_options.cpp ├── rot13 │ ├── CMakeLists.txt │ ├── README.md │ └── rot13.cpp ├── table │ ├── CMakeLists.txt │ ├── README.md │ └── table.cpp └── whereis │ ├── CMakeLists.txt │ ├── README.md │ └── whereis.cpp ├── include └── Argos │ ├── Argos.hpp │ ├── ArgosException.hpp │ ├── Argument.hpp │ ├── ArgumentIterator.hpp │ ├── ArgumentParser.hpp │ ├── ArgumentValue.hpp │ ├── ArgumentValueIterator.hpp │ ├── ArgumentValues.hpp │ ├── ArgumentView.hpp │ ├── Callbacks.hpp │ ├── Command.hpp │ ├── CommandView.hpp │ ├── Enums.hpp │ ├── IArgumentView.hpp │ ├── Option.hpp │ ├── OptionView.hpp │ ├── ParsedArguments.hpp │ └── ParsedArgumentsBuilder.hpp ├── single_src └── Argos │ ├── Argos.cpp │ └── Argos.hpp ├── src └── Argos │ ├── ArgosThrow.hpp │ ├── ArgosVersion.hpp.in │ ├── Argument.cpp │ ├── ArgumentCounter.cpp │ ├── ArgumentCounter.hpp │ ├── ArgumentData.hpp │ ├── ArgumentIterator.cpp │ ├── ArgumentIteratorImpl.cpp │ ├── ArgumentIteratorImpl.hpp │ ├── ArgumentParser.cpp │ ├── ArgumentValue.cpp │ ├── ArgumentValueIterator.cpp │ ├── ArgumentValues.cpp │ ├── ArgumentView.cpp │ ├── Command.cpp │ ├── CommandData.cpp │ ├── CommandData.hpp │ ├── CommandView.cpp │ ├── ConsoleWidth.cpp │ ├── ConsoleWidth.hpp │ ├── Enums.cpp │ ├── HelpText.cpp │ ├── HelpText.hpp │ ├── Option.cpp │ ├── OptionData.cpp │ ├── OptionData.hpp │ ├── OptionIterator.cpp │ ├── OptionIterator.hpp │ ├── OptionIteratorWrapper.hpp │ ├── OptionView.cpp │ ├── ParseValue.cpp │ ├── ParseValue.hpp │ ├── ParsedArguments.cpp │ ├── ParsedArgumentsBuilder.cpp │ ├── ParsedArgumentsImpl.cpp │ ├── ParsedArgumentsImpl.hpp │ ├── ParserData.cpp │ ├── ParserData.hpp │ ├── StandardOptionIterator.cpp │ ├── StandardOptionIterator.hpp │ ├── StringUtilities.cpp │ ├── StringUtilities.hpp │ ├── TextFormatter.cpp │ ├── TextFormatter.hpp │ ├── TextSource.hpp │ ├── TextWriter.cpp │ ├── TextWriter.hpp │ ├── WordSplitter.cpp │ └── WordSplitter.hpp ├── tests └── ArgosTest │ ├── Argv.hpp │ ├── CMakeLists.txt │ ├── U8Adapter.hpp │ ├── test_ArgumentCounter.cpp │ ├── test_ArgumentParser.cpp │ ├── test_ArgumentValue.cpp │ ├── test_Callbacks.cpp │ ├── test_HelpWriter.cpp │ ├── test_ParseValue.cpp │ ├── test_ParsedArguments.cpp │ ├── test_StandardOptionIterator.cpp │ ├── test_StringUtilities.cpp │ ├── test_Subcommands.cpp │ ├── test_TextFormatter.cpp │ ├── test_TextWriter.cpp │ └── test_WordSplitter.cpp └── tools ├── compiled_code_size └── codesize.py └── make_single_src ├── make_argos_cpp.cmd.in ├── make_argos_cpp.sh.in └── mergecpp.py /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | 11 | # 4 space indentation 12 | [*.{c,cpp,h,hpp,py}] 13 | indent_style = space 14 | indent_size = 4 15 | 16 | # Set charset to utf-8 for all files 17 | charset = utf-8 18 | 19 | # Trim trailing whitespace 20 | trim_trailing_whitespace = true 21 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This is a copy of https://github.com/jherico/starter-workflows/blob/master/ci/cmake.yml 2 | name: CMake 3 | 4 | on: [push] 5 | 6 | env: 7 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 8 | BUILD_TYPE: Release 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ${{ matrix.os }} 14 | strategy: 15 | matrix: 16 | os: [windows-latest, ubuntu-24.04, macOS-latest] 17 | 18 | steps: 19 | - uses: actions/checkout@v1 20 | 21 | - name: Create Build Environment 22 | # Some projects don't allow in-source building, so create a separate build directory 23 | # We'll use this as our working directory for all subsequent commands 24 | run: cmake -E make_directory ${{runner.workspace}}/build 25 | 26 | - name: Configure CMake 27 | # Use a bash shell so we can use the same syntax for environment variable 28 | # access regardless of the host operating system 29 | shell: bash 30 | working-directory: ${{runner.workspace}}/build 31 | # Note the current convention is to use the -S and -B options here to specify source 32 | # and build directories, but this is only available with CMake 3.13 and higher. 33 | # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 34 | run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE 35 | 36 | - name: Build 37 | working-directory: ${{runner.workspace}}/build 38 | shell: bash 39 | # Execute the build. You can specify a specific target with "--target " 40 | run: cmake --build . --config $BUILD_TYPE 41 | 42 | - name: Test 43 | working-directory: ${{runner.workspace}}/build 44 | shell: bash 45 | # Execute tests defined by the CMake configuration. 46 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 47 | run: ctest -V -C $BUILD_TYPE 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | *.smod 19 | 20 | # Compiled Static libraries 21 | *.lai 22 | *.la 23 | *.a 24 | *.lib 25 | 26 | # Executables 27 | *.exe 28 | *.out 29 | *.app 30 | 31 | /.idea 32 | /bin 33 | build/ 34 | /cmake-build-* 35 | .DS_Store 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2024, Jan Erik Breimo 2 | 3 | Permission to use, copy, modify, and/or distribute this software for 4 | any purpose with or without fee is hereby granted. 5 | 6 | THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL 7 | WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES 8 | OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE 9 | FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY 10 | DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 11 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT 12 | OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 13 | -------------------------------------------------------------------------------- /cmake/CodeCoverage.cmake: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | # Created by Jan Erik Breimo on 2020-03-15. 4 | # 5 | # This file is distributed under the BSD License. 6 | # License text is included with the source distribution. 7 | # =========================================================================== 8 | cmake_minimum_required(VERSION 3.13) 9 | 10 | function(TargetEnableCodeCoverage target isEnabled) 11 | if (${isEnabled}) 12 | if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|.*Clang") 13 | target_compile_options(${target} PRIVATE --coverage) 14 | target_link_options(${target} PUBLIC --coverage) 15 | endif () 16 | endif () 17 | endfunction() 18 | -------------------------------------------------------------------------------- /docs/doxygen/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jebreimo/Argos/b26e006dee7161506dcad761c1ba82ccc4654a1c/docs/doxygen/.gitignore -------------------------------------------------------------------------------- /docs/doxygen/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | # Created by Jan Erik Breimo on 2020-03-15. 4 | # 5 | # This file is distributed under the BSD License. 6 | # License text is included with the source distribution. 7 | # =========================================================================== 8 | cmake_minimum_required(VERSION 3.13) 9 | 10 | find_package(Doxygen) 11 | 12 | if(Doxygen_NOT_FOUND) 13 | return() 14 | endif() 15 | 16 | # Find all the public headers 17 | file(GLOB_RECURSE ARGOS_DOXYGEN_INPUT "${CMAKE_SOURCE_DIR}/include/Argos/*.hpp") 18 | 19 | list(APPEND ARGOS_DOXYGEN_INPUT "${CMAKE_CURRENT_BINARY_DIR}/../../ArgosVersion.hpp") 20 | 21 | # Use README.md as the main page 22 | set(ARGOS_MARKDOWN_MAIN_PAGE "${CMAKE_SOURCE_DIR}/README.md") 23 | list(INSERT ARGOS_DOXYGEN_INPUT 0 "${ARGOS_MARKDOWN_MAIN_PAGE}") 24 | 25 | string(REPLACE ";" " \\\n " DOXYGEN_INPUT "${ARGOS_DOXYGEN_INPUT}") 26 | set(DOXYGEN_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}") 27 | set(DOXYFILE_IN ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in) 28 | set(DOXYFILE_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) 29 | 30 | # Replace variables inside @@ with the current values 31 | configure_file(${DOXYFILE_IN} ${DOXYFILE_OUT} @ONLY) 32 | 33 | set(DOXYGEN_INDEX_FILE "${DOXYGEN_OUTPUT_DIR}/xml/index.xml") 34 | 35 | # Only regenerate Doxygen when the Doxyfile or public headers change 36 | add_custom_command(OUTPUT ${DOXYGEN_INDEX_FILE} 37 | DEPENDS ${ARGOS_DOXYGEN_INPUT} 38 | COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYFILE_OUT} 39 | MAIN_DEPENDENCY ${DOXYFILE_OUT} ${DOXYFILE_IN} 40 | COMMENT "Generating documentation with Doxygen") 41 | 42 | # Nice named target so we can run the job easily 43 | add_custom_target(ArgosDoxygen ALL DEPENDS ${DOXYGEN_INDEX_FILE}) 44 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # Copyright © 2021 Jan Erik Breimo. All rights reserved. 3 | # Created by Jan Erik Breimo on 2021-09-18. 4 | # 5 | # This file is distributed under the BSD License. 6 | # License text is included with the source distribution. 7 | # =========================================================================== 8 | cmake_minimum_required(VERSION 3.14) 9 | project(examples) 10 | 11 | add_subdirectory(append_clear_option) 12 | add_subdirectory(area) 13 | add_subdirectory(hello) 14 | add_subdirectory(list_options) 15 | add_subdirectory(rot13) 16 | add_subdirectory(commands) 17 | add_subdirectory(table) 18 | add_subdirectory(whereis) 19 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | Examples 2 | ======== 3 | 4 | In this folder are several small programs that demonstrates use of 5 | Argos to parse command line arguments. 6 | -------------------------------------------------------------------------------- /examples/append_clear_option/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # Copyright © 2021 Jan Erik Breimo. All rights reserved. 3 | # Created by Jan Erik Breimo on 2021-09-04. 4 | # 5 | # This file is distributed under the BSD License. 6 | # License text is included with the source distribution. 7 | # =========================================================================== 8 | cmake_minimum_required(VERSION 3.14) 9 | project(append_clear_option) 10 | 11 | set(CMAKE_CXX_STANDARD 20) 12 | 13 | set(SINGLE_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../single_src) 14 | 15 | if (EXISTS ${SINGLE_SRC_DIR}/Argos/Argos.hpp) 16 | 17 | function(add_argos TARGET) 18 | target_sources(${TARGET} 19 | PRIVATE 20 | ${SINGLE_SRC_DIR}/Argos/Argos.cpp 21 | ) 22 | 23 | target_include_directories(${TARGET} 24 | PRIVATE 25 | ${SINGLE_SRC_DIR} 26 | ) 27 | 28 | target_compile_definitions(${TARGET} 29 | PRIVATE 30 | $<$:_CRT_SECURE_NO_WARNINGS> 31 | ) 32 | endfunction() 33 | 34 | else() 35 | 36 | include(FetchContent) 37 | FetchContent_Declare(argos 38 | GIT_REPOSITORY "https://github.com/jebreimo/Argos.git" 39 | GIT_TAG "master" 40 | ) 41 | FetchContent_MakeAvailable(argos) 42 | 43 | function(add_argos TARGET) 44 | target_link_libraries(${TARGET} 45 | PRIVATE 46 | Argos::Argos 47 | ) 48 | 49 | target_compile_definitions(${TARGET} 50 | PRIVATE 51 | $<$:_CRT_SECURE_NO_WARNINGS> 52 | ) 53 | endfunction() 54 | endif() 55 | 56 | add_executable(append_clear_option append_clear_option.cpp) 57 | add_argos(append_clear_option) 58 | -------------------------------------------------------------------------------- /examples/append_clear_option/README.md: -------------------------------------------------------------------------------- 1 | Append and clear options 2 | ======================== 3 | 4 | This project demonstrates how to make an option, `--include`, 5 | where `--include=VALUE` appends `VALUE` to a list, and just `--include` 6 | on its own clears the same list. Having an option negate or clear 7 | the result of other options can make sense when a program is run via 8 | an alias or script. 9 | 10 | ~~~ 11 | $ ./append_clear_option --include=foo --include=bar 12 | foo 13 | bar 14 | $ ./append_clear_option --include=foo --include=bar --include 15 | $ ./append_clear_option --include=foo --include=bar --include --include=baz 16 | baz 17 | ~~~ 18 | -------------------------------------------------------------------------------- /examples/append_clear_option/append_clear_option.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2021 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2021-08-29. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include 9 | #include 10 | 11 | /** 12 | * @file 13 | * Demonstrates how a program can have an option, e.g. --include, where 14 | * "--include=VALUE" adds "VALUE" to a list, while just "--include" empties 15 | * the same list. 16 | */ 17 | 18 | int main(int argc, char* argv[]) 19 | { 20 | using namespace argos; 21 | auto args = ArgumentParser(argv[0]) 22 | .about("Demonstrates APPEND and CLEAR options.") 23 | .add(Opt{"-i", "--include="}.argument("VALUE") 24 | .operation(OptionOperation::APPEND) 25 | .alias("--include") 26 | .help("Appends VALUE to the list of values.")) 27 | .add(Opt{"--include"} 28 | .operation(OptionOperation::CLEAR) 29 | .help("Clears the list of values.")) 30 | .parse(argc, argv); 31 | 32 | for (const auto& str : args.values("--include").as_strings()) 33 | std::cout << str << "\n"; 34 | 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /examples/area/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # Copyright © 2021 Jan Erik Breimo. All rights reserved. 3 | # Created by Jan Erik Breimo on 2021-08-05. 4 | # 5 | # This file is distributed under the BSD License. 6 | # License text is included with the source distribution. 7 | # =========================================================================== 8 | cmake_minimum_required(VERSION 3.14) 9 | project(area) 10 | 11 | set(CMAKE_CXX_STANDARD 20) 12 | 13 | set(SINGLE_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../single_src) 14 | 15 | if (EXISTS ${SINGLE_SRC_DIR}/Argos/Argos.hpp) 16 | 17 | function(add_argos TARGET) 18 | target_sources(${TARGET} 19 | PRIVATE 20 | ${SINGLE_SRC_DIR}/Argos/Argos.cpp 21 | ) 22 | 23 | target_include_directories(${TARGET} 24 | PRIVATE 25 | ${SINGLE_SRC_DIR} 26 | ) 27 | 28 | target_compile_definitions(${TARGET} 29 | PRIVATE 30 | $<$:_CRT_SECURE_NO_WARNINGS> 31 | ) 32 | endfunction() 33 | 34 | else() 35 | 36 | include(FetchContent) 37 | FetchContent_Declare(argos 38 | GIT_REPOSITORY "https://github.com/jebreimo/Argos.git" 39 | GIT_TAG "master" 40 | ) 41 | FetchContent_MakeAvailable(argos) 42 | 43 | function(add_argos TARGET) 44 | target_link_libraries(${TARGET} 45 | PRIVATE 46 | Argos::Argos 47 | ) 48 | 49 | target_compile_definitions(${TARGET} 50 | PRIVATE 51 | $<$:_CRT_SECURE_NO_WARNINGS> 52 | ) 53 | endfunction() 54 | endif() 55 | 56 | add_executable(area area.cpp) 57 | add_argos(area) 58 | -------------------------------------------------------------------------------- /examples/area/README.md: -------------------------------------------------------------------------------- 1 | area 2 | ==== 3 | 4 | *area* calculates the area of an arbitrary triangle or quadrilateral 5 | defined by the coordinates (x,y) of their corners. 6 | 7 | Examples: 8 | 9 | ~~~ 10 | ~/Argos/examples/area/build $ ./area 1,1 5.5,2 2,6 11 | 10.75 12 | ~/Argos/examples/area/build $ ./area 1,1 5.5,2 6,4.75 2,6 13 | 16.5625 14 | ~~~ 15 | 16 | Help text: 17 | ~~~ 18 | USAGE 19 | example2 --help 20 | example2 [] 21 | 22 | Computes the area of a triangle or quadrilateral. 23 | 24 | ARGUMENTS 25 | [] 26 | The coordinates of the corners in a triangle or quadrilateral. 27 | 28 | OPTIONS 29 | -h, --help 30 | Display the help text. 31 | ~~~ 32 | -------------------------------------------------------------------------------- /examples/area/area.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2021 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2021-09-05. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include 9 | #include 10 | #include 11 | 12 | struct Point 13 | { 14 | double x, y; 15 | }; 16 | 17 | double calculate_area(Point a, Point b, Point c) 18 | { 19 | auto dx1 = b.x - a.x, dy1 = b.y - a.y; 20 | auto dx2 = c.x - a.x, dy2 = c.y - a.y; 21 | return std::abs(dx1*dy2 - dx2*dy1) / 2; 22 | } 23 | 24 | double calculate_area(Point a, Point b, Point c, Point d) 25 | { 26 | auto dx1 = c.x - a.x, dy1 = c.y - a.y; 27 | auto dx2 = d.x - b.x, dy2 = d.y - b.y; 28 | return std::abs(dx1*dy2 - dx2*dy1) / 2; 29 | } 30 | 31 | int main(int argc, char* argv[]) 32 | { 33 | using argos::ArgumentParser, argos::Arg; 34 | auto args = ArgumentParser("example2") 35 | .about("Computes the area of a triangle or quadrilateral.") 36 | .add(Arg("X,Y") 37 | .count(3, 4) 38 | .help("The coordinates of the corners in a triangle" 39 | " or quadrilateral.")) 40 | .parse(argc, argv); 41 | 42 | std::vector corners; 43 | for (auto value : args.values("X,Y")) 44 | { 45 | auto coordinates = value.split_n(',', 2).as_doubles(); 46 | corners.push_back({coordinates[0], coordinates[1]}); 47 | } 48 | 49 | if (corners.size() == 3) 50 | { 51 | std::cout << calculate_area(corners[0], corners[1], corners[2]) 52 | << "\n"; 53 | } 54 | else 55 | { 56 | std::cout << calculate_area(corners[0], corners[1], 57 | corners[2], corners[3]) 58 | << "\n"; 59 | } 60 | 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /examples/commands/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # Copyright © 2021 Jan Erik Breimo. All rights reserved. 3 | # Created by Jan Erik Breimo on 2021-08-05. 4 | # 5 | # This file is distributed under the BSD License. 6 | # License text is included with the source distribution. 7 | # =========================================================================== 8 | cmake_minimum_required(VERSION 3.14) 9 | project(hello) 10 | 11 | set(CMAKE_CXX_STANDARD 20) 12 | 13 | set(SINGLE_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../single_src) 14 | 15 | if (EXISTS ${SINGLE_SRC_DIR}/Argos/Argos.hpp) 16 | 17 | function(add_argos TARGET) 18 | target_sources(${TARGET} 19 | PRIVATE 20 | ${SINGLE_SRC_DIR}/Argos/Argos.cpp 21 | ) 22 | 23 | target_include_directories(${TARGET} 24 | PRIVATE 25 | ${SINGLE_SRC_DIR} 26 | ) 27 | 28 | target_compile_definitions(${TARGET} 29 | PRIVATE 30 | $<$:_CRT_SECURE_NO_WARNINGS> 31 | ) 32 | endfunction() 33 | 34 | else() 35 | 36 | include(FetchContent) 37 | FetchContent_Declare(argos 38 | GIT_REPOSITORY "https://github.com/jebreimo/Argos.git" 39 | GIT_TAG "master" 40 | ) 41 | FetchContent_MakeAvailable(argos) 42 | 43 | function(add_argos TARGET) 44 | target_link_libraries(${TARGET} 45 | PRIVATE 46 | Argos::Argos 47 | ) 48 | 49 | target_compile_definitions(${TARGET} 50 | PRIVATE 51 | $<$:_CRT_SECURE_NO_WARNINGS> 52 | ) 53 | endfunction() 54 | endif() 55 | 56 | add_executable(commands commands.cpp) 57 | add_argos(commands) 58 | -------------------------------------------------------------------------------- /examples/commands/README.md: -------------------------------------------------------------------------------- 1 | hello 2 | ===== 3 | 4 | A small *hello world* program. Described in the Argos tutorial. 5 | -------------------------------------------------------------------------------- /examples/commands/commands.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2021 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2021-08-29. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include 9 | #include 10 | 11 | int main(int argc, char* argv[]) 12 | { 13 | try 14 | { 15 | using namespace argos; 16 | const ParsedArguments args = ArgumentParser(argv[0]) 17 | .about("Performs an operation of some kind.") 18 | .version("1.0.0") 19 | .add(Command("greet") 20 | .about("Prints a greeting.") 21 | .add(Arg("NAME").optional(true) 22 | .help("The person or thing to greet.")) 23 | .add(Opt("-n", "--number").argument("NUM") 24 | .help("The number of times to repeat the greeting."))) 25 | .add(Command("congratulate") 26 | .about("Prints a congratulation.") 27 | .add(Arg("NAME").optional(true) 28 | .help("The person or thing to greet.")) 29 | .add(Opt("-o", "--occasion").argument("OCCASION") 30 | .help("The occasion for the congratulation.")) 31 | .add(Opt("-n", "--number").argument("NUM") 32 | .help("The number of times to repeat the congratulation."))) 33 | .parse(argc, argv); 34 | 35 | const auto subcommand = args.subcommands()[0]; 36 | std::string a; 37 | if (subcommand.name() == "greet") 38 | { 39 | a = "Hello"; 40 | } 41 | else if (subcommand.name() == "congratulate") 42 | { 43 | a = "Congratulations"; 44 | if (const auto occasion = subcommand.value("--occasion")) 45 | a += " on your " + occasion.as_string(); 46 | } 47 | 48 | const int n = subcommand.value("--number").as_int(1); 49 | const auto name = subcommand.value("NAME").as_string("world"); 50 | for (int i = 0; i < n; ++i) 51 | { 52 | std::cout << a << ", " << name; 53 | std::cout << "!\n"; 54 | } 55 | } 56 | catch (std::exception& ex) 57 | { 58 | std::cerr << "Error: " << ex.what() << "\n"; 59 | return 1; 60 | } 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /examples/hello/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # Copyright © 2021 Jan Erik Breimo. All rights reserved. 3 | # Created by Jan Erik Breimo on 2021-08-05. 4 | # 5 | # This file is distributed under the BSD License. 6 | # License text is included with the source distribution. 7 | # =========================================================================== 8 | cmake_minimum_required(VERSION 3.14) 9 | project(hello) 10 | 11 | set(CMAKE_CXX_STANDARD 20) 12 | 13 | set(SINGLE_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../single_src) 14 | 15 | if (EXISTS ${SINGLE_SRC_DIR}/Argos/Argos.hpp) 16 | 17 | function(add_argos TARGET) 18 | target_sources(${TARGET} 19 | PRIVATE 20 | ${SINGLE_SRC_DIR}/Argos/Argos.cpp 21 | ) 22 | 23 | target_include_directories(${TARGET} 24 | PRIVATE 25 | ${SINGLE_SRC_DIR} 26 | ) 27 | 28 | target_compile_definitions(${TARGET} 29 | PRIVATE 30 | $<$:_CRT_SECURE_NO_WARNINGS> 31 | ) 32 | endfunction() 33 | 34 | else() 35 | 36 | include(FetchContent) 37 | FetchContent_Declare(argos 38 | GIT_REPOSITORY "https://github.com/jebreimo/Argos.git" 39 | GIT_TAG "master" 40 | ) 41 | FetchContent_MakeAvailable(argos) 42 | 43 | function(add_argos TARGET) 44 | target_link_libraries(${TARGET} 45 | PRIVATE 46 | Argos::Argos 47 | ) 48 | 49 | target_compile_definitions(${TARGET} 50 | PRIVATE 51 | $<$:_CRT_SECURE_NO_WARNINGS> 52 | ) 53 | endfunction() 54 | endif() 55 | 56 | add_executable(hello hello.cpp) 57 | add_argos(hello) 58 | -------------------------------------------------------------------------------- /examples/hello/README.md: -------------------------------------------------------------------------------- 1 | hello 2 | ===== 3 | 4 | A small *hello world* program. Described in the Argos tutorial. 5 | -------------------------------------------------------------------------------- /examples/hello/hello.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2021 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2021-08-29. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include 9 | #include 10 | 11 | int main(int argc, char* argv[]) 12 | { 13 | const argos::ParsedArguments args = argos::ArgumentParser(argv[0]) 14 | .about("Displays a greeting to someone or something.") 15 | .add(argos::Arg("NAME").optional(true) 16 | .help("The person or thing to greet.")) 17 | .add(argos::Opt("-n", "--number").argument("NUM") 18 | .help("The number of times to repeat the greeting.")) 19 | .parse(argc, argv); 20 | 21 | int n = args.value("--number").as_int(1); 22 | for (int i = 0; i < n; ++i) 23 | { 24 | std::cout << "Hello " 25 | << args.value("NAME").as_string("world") 26 | << "!\n"; 27 | } 28 | 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /examples/list_options/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # Copyright © 2021 Jan Erik Breimo. All rights reserved. 3 | # Created by Jan Erik Breimo on 2021-09-04. 4 | # 5 | # This file is distributed under the BSD License. 6 | # License text is included with the source distribution. 7 | # =========================================================================== 8 | cmake_minimum_required(VERSION 3.14) 9 | project(list_options) 10 | 11 | set(CMAKE_CXX_STANDARD 20) 12 | 13 | set(SINGLE_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../single_src) 14 | 15 | if (EXISTS ${SINGLE_SRC_DIR}/Argos/Argos.hpp) 16 | 17 | function(add_argos TARGET) 18 | target_sources(${TARGET} 19 | PRIVATE 20 | ${SINGLE_SRC_DIR}/Argos/Argos.cpp 21 | ) 22 | 23 | target_include_directories(${TARGET} 24 | PRIVATE 25 | ${SINGLE_SRC_DIR} 26 | ) 27 | 28 | target_compile_definitions(${TARGET} 29 | PRIVATE 30 | $<$:_CRT_SECURE_NO_WARNINGS> 31 | ) 32 | endfunction() 33 | 34 | else() 35 | 36 | include(FetchContent) 37 | FetchContent_Declare(argos 38 | GIT_REPOSITORY "https://github.com/jebreimo/Argos.git" 39 | GIT_TAG "master" 40 | ) 41 | FetchContent_MakeAvailable(argos) 42 | 43 | function(add_argos TARGET) 44 | target_link_libraries(${TARGET} 45 | PRIVATE 46 | Argos::Argos 47 | ) 48 | 49 | target_compile_definitions(${TARGET} 50 | PRIVATE 51 | $<$:_CRT_SECURE_NO_WARNINGS> 52 | ) 53 | endfunction() 54 | endif() 55 | 56 | add_executable(list_options list_options.cpp) 57 | add_argos(list_options) 58 | -------------------------------------------------------------------------------- /examples/list_options/README.md: -------------------------------------------------------------------------------- 1 | list_options 2 | ============ 3 | 4 | A small program that demonstrates different ways to read values from 5 | list options. 6 | -------------------------------------------------------------------------------- /examples/list_options/list_options.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2021 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2021-09-04. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include 9 | #include 10 | 11 | int main(int argc, char* argv[]) 12 | { 13 | using namespace argos; 14 | auto args = ArgumentParser(argv[0]) 15 | .about("A small example that demonstrates different ways to work" 16 | " with options that build lists of values.") 17 | .add(Opt{"-s", "--add-single"}.argument("VALUE") 18 | .operation(OptionOperation::APPEND) 19 | .help("Add a single value to a list.")) 20 | .add(Opt{"-m", "--add-multi"}.argument("VALUE[:VALUE]...") 21 | .operation(OptionOperation::APPEND) 22 | .help("Add one or more values to a list. Use colon, ':', as" 23 | " separator when supplying more than one value.")) 24 | .parse(argc, argv); 25 | 26 | std::cout << "single (one value at a time):\n"; 27 | for (const auto value : args.values("--add-single")) 28 | std::cout << " " << value.as_string() << "\n"; 29 | 30 | std::cout << "single (all at once):\n"; 31 | for (const auto value : args.values("--add-single").as_strings()) 32 | std::cout << " " << value << "\n"; 33 | 34 | std::cout << "multi (one value at a time):\n"; 35 | for (const auto value : args.values("--add-multi").split(':')) 36 | std::cout << " " << value.as_string() << "\n"; 37 | 38 | std::cout << "multi (all at once):\n"; 39 | for (const auto value : args.values("--add-multi").split(':').as_strings()) 40 | std::cout << " " << value << "\n"; 41 | 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /examples/rot13/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # Copyright © 2021 Jan Erik Breimo. All rights reserved. 3 | # Created by Jan Erik Breimo on 2021-08-05. 4 | # 5 | # This file is distributed under the BSD License. 6 | # License text is included with the source distribution. 7 | # =========================================================================== 8 | cmake_minimum_required(VERSION 3.14) 9 | project(rot13) 10 | 11 | set(CMAKE_CXX_STANDARD 20) 12 | 13 | set(SINGLE_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../single_src) 14 | 15 | if (EXISTS ${SINGLE_SRC_DIR}/Argos/Argos.hpp) 16 | 17 | function(add_argos TARGET) 18 | target_sources(${TARGET} 19 | PRIVATE 20 | ${SINGLE_SRC_DIR}/Argos/Argos.cpp 21 | ) 22 | 23 | target_include_directories(${TARGET} 24 | PRIVATE 25 | ${SINGLE_SRC_DIR} 26 | ) 27 | 28 | target_compile_definitions(${TARGET} 29 | PRIVATE 30 | $<$:_CRT_SECURE_NO_WARNINGS> 31 | ) 32 | endfunction() 33 | 34 | else() 35 | 36 | include(FetchContent) 37 | FetchContent_Declare(argos 38 | GIT_REPOSITORY "https://github.com/jebreimo/Argos.git" 39 | GIT_TAG "master" 40 | ) 41 | FetchContent_MakeAvailable(argos) 42 | 43 | function(add_argos TARGET) 44 | target_link_libraries(${TARGET} 45 | PRIVATE 46 | Argos::Argos 47 | ) 48 | 49 | target_compile_definitions(${TARGET} 50 | PRIVATE 51 | $<$:_CRT_SECURE_NO_WARNINGS> 52 | ) 53 | endfunction() 54 | endif() 55 | 56 | add_executable(rot13 rot13.cpp) 57 | add_argos(rot13) 58 | -------------------------------------------------------------------------------- /examples/rot13/README.md: -------------------------------------------------------------------------------- 1 | rot13 2 | ===== 3 | 4 | Uses the [rot13 algorithm](https://en.wikipedia.org/wiki/ROT13) to obfuscate 5 | (or reveal) the program's arguments. 6 | 7 | Example: 8 | ~~~ 9 | ~/Argos/examples/rot13/build % ./rot13 The key is hidden behind the painting. 10 | Gur xrl vf uvqqra oruvaq gur cnvagvat. 11 | ~~~ 12 | 13 | Help text: 14 | 15 | ~~~ 16 | USAGE 17 | example2 --help 18 | example2 [-v] [-n ] [--] []... 19 | 20 | Obfuscates (or reveals) text with the rot-13 algorithm. 21 | 22 | ARGUMENTS 23 | []... 24 | One or more words. 25 | 26 | OPTIONS 27 | -v, --verbose 28 | Display additional information. 29 | -n , --number 30 | Set the number letters are rotated by. Default is 13. 31 | -- 32 | Marks the end of the options. Allows words to start with dashes 33 | ('-'). 34 | -h, --help 35 | Display the help text. 36 | ~~~ 37 | -------------------------------------------------------------------------------- /examples/rot13/rot13.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2021 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2021-08-29. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include 9 | #include 10 | #include 11 | 12 | void rotate_and_print(const std::string& word, int n) 13 | { 14 | for (auto c: word) 15 | { 16 | if ('A' <= c && c <= 'Z') 17 | c = char('A' + ((c - 'A') + n) % 26); 18 | else if ('a' <= c && c <= 'z') 19 | c = char('a' + ((c - 'a') + n) % 26); 20 | std::cout << c; 21 | } 22 | } 23 | 24 | int main(int argc, char* argv[]) 25 | { 26 | using namespace argos; 27 | const argos::ParsedArguments args = ArgumentParser("example2") 28 | .about("Obfuscates (or reveals) text with the rot-13 algorithm.") 29 | .add(Arg("WORD").count(1, INT_MAX).help("One or more words.")) 30 | .add(Opt{"-v", "--verbose"} 31 | .help("Display additional information.")) 32 | .add(Opt{"-n", "--number"}.argument("NUM") 33 | .help("Set the number letters are rotated by. Default is 13.")) 34 | .add(Opt{"--"}.type(OptionType::LAST_OPTION) 35 | .help("Marks the end of the options. Allows words to" 36 | " start with dashes ('-').")) 37 | .parse(argc, argv); 38 | 39 | const std::vector words = args.values("WORD").as_strings(); 40 | const bool verbose = args.value("--verbose").as_bool(); 41 | const int n = args.value("--number").as_int(13); 42 | 43 | if (verbose) 44 | std::cout << "n = " << n << "\n"; 45 | 46 | bool first = true; 47 | for (const auto& word : words) 48 | { 49 | if (first) 50 | first = false; 51 | else 52 | std::cout << ' '; 53 | rotate_and_print(word, n); 54 | } 55 | std::cout << std::endl; 56 | 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /examples/table/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # Copyright © 2021 Jan Erik Breimo. All rights reserved. 3 | # Created by Jan Erik Breimo on 2021-08-05. 4 | # 5 | # This file is distributed under the BSD License. 6 | # License text is included with the source distribution. 7 | # =========================================================================== 8 | cmake_minimum_required(VERSION 3.14) 9 | project(table) 10 | 11 | set(CMAKE_CXX_STANDARD 20) 12 | 13 | set(SINGLE_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../single_src) 14 | 15 | if (EXISTS ${SINGLE_SRC_DIR}/Argos/Argos.hpp) 16 | 17 | function(add_argos TARGET) 18 | target_sources(${TARGET} 19 | PRIVATE 20 | ${SINGLE_SRC_DIR}/Argos/Argos.cpp 21 | ) 22 | 23 | target_include_directories(${TARGET} 24 | PRIVATE 25 | ${SINGLE_SRC_DIR} 26 | ) 27 | 28 | target_compile_definitions(${TARGET} 29 | PRIVATE 30 | $<$:_CRT_SECURE_NO_WARNINGS> 31 | ) 32 | endfunction() 33 | 34 | else() 35 | 36 | include(FetchContent) 37 | FetchContent_Declare(argos 38 | GIT_REPOSITORY "https://github.com/jebreimo/Argos.git" 39 | GIT_TAG "master" 40 | ) 41 | FetchContent_MakeAvailable(argos) 42 | 43 | function(add_argos TARGET) 44 | target_link_libraries(${TARGET} 45 | PRIVATE 46 | Argos::Argos 47 | ) 48 | 49 | target_compile_definitions(${TARGET} 50 | PRIVATE 51 | $<$:_CRT_SECURE_NO_WARNINGS> 52 | ) 53 | endfunction() 54 | endif() 55 | 56 | add_executable(table table.cpp) 57 | add_argos(table) 58 | -------------------------------------------------------------------------------- /examples/table/README.md: -------------------------------------------------------------------------------- 1 | table 2 | ===== 3 | 4 | A command line utility that build an ASCII table from its arguments. It 5 | demonstrates how option and argument callbacks can be used with Argos. 6 | 7 | Example of use: 8 | 9 | ~~~ 10 | ~/Argos/examples/table/build % ./table foo -c bar -r boom bang -r -cc trim --borders 11 | foo | |bar 12 | ----+----+---- 13 | boom|bang| 14 | ----+----+---- 15 | | |trim 16 | ~~~ 17 | 18 | Help text: 19 | 20 | ~~~ 21 | USAGE 22 | table --help 23 | table [-o ] [-r] [-c] [--borders] []... 24 | 25 | Prints the arguments as cells in a table. The program also demonstrates how 26 | option and argument callbacks can be used. 27 | 28 | ARGUMENTS 29 | []... 30 | Text of the next table cell. 31 | 32 | OPTIONS 33 | -o , --output 34 | File name for output. stdout is used by default. 35 | -r, --row 36 | Next file will be placed at the beginning of a new row. 37 | -c, --column 38 | Skip one column forward. 39 | --borders 40 | Print borders between cells. 41 | -h, --help 42 | Display the help text. 43 | ~~~ 44 | -------------------------------------------------------------------------------- /examples/table/table.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2021 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2021-09-05. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | struct TableCell 16 | { 17 | std::string text; 18 | unsigned row; 19 | unsigned col; 20 | }; 21 | 22 | int main(int argc, char* argv[]) 23 | { 24 | unsigned row = 0, col = 0; 25 | std::vector cells; 26 | using namespace argos; 27 | auto args = ArgumentParser(argv[0]) 28 | .about("Prints the arguments as cells in a table. The program also" 29 | " demonstrates one way to use option and argument callbacks.") 30 | .add(Arg("TEXT").count(1, UINT16_MAX) 31 | .callback([&](auto& a) 32 | { 33 | cells.push_back({std::string(a.value), row, col}); 34 | ++col; 35 | }) 36 | .help("Text of the next table cell.")) 37 | .add(Opt{"-o", "--output"}.argument("FILE") 38 | .help("File name for output. stdout is used by default.")) 39 | .add(Opt{"-r", "--row"} 40 | .callback([&](auto&){++row; col = 0;}) 41 | .help("Next file will be placed at the beginning of a new row.")) 42 | .add(Opt{"-c", "--column"} 43 | .callback([&](auto&){++col;}) 44 | .help("Skip one column forward.")) 45 | .add(Opt{"--borders"}.help("Print borders between cells.")) 46 | .parse(argc, argv); 47 | 48 | if (cells.empty()) 49 | args.error("no input"); 50 | 51 | auto borders = args.value("--borders").as_bool(); 52 | 53 | // Calculate column widths. 54 | std::vector col_widths; 55 | for (const auto& cell : cells) 56 | { 57 | if (cell.col >= col_widths.size()) 58 | col_widths.resize(cell.col + 1); 59 | col_widths[cell.col] = std::max(col_widths[cell.col], cell.text.size()); 60 | } 61 | 62 | // Initialize output stream. 63 | std::ofstream file; 64 | if (auto output = args.value("--output")) 65 | file.open(output.as_string()); 66 | std::ostream& stream = file ? file : std::cout; 67 | 68 | // Print the table. 69 | auto it = cells.begin(); 70 | for (unsigned i = 0; i <= cells.back().row; ++i) 71 | { 72 | // If borders are enabled, print a line of dashes and pluses 73 | // between the rows of text. 74 | if (borders && i != 0) 75 | { 76 | stream << std::setfill('-'); 77 | for (unsigned j = 0; j < col_widths.size(); ++j) 78 | { 79 | if (j != 0) 80 | stream << '+'; 81 | stream << std::setw(std::streamsize(col_widths[j])) << ""; 82 | } 83 | stream << std::setfill(' ') << '\n'; 84 | } 85 | 86 | // Print the row of text. 87 | for (unsigned j = 0; j < col_widths.size(); ++j) 88 | { 89 | // Print a separator between cells. 90 | if (j != 0) 91 | stream << (borders ? '|' : ' '); 92 | 93 | // spaces is the width of the column minus the width of the text 94 | size_t spaces = col_widths[j]; 95 | if (it != cells.end() && it->row == i && it->col == j) 96 | { 97 | stream << it->text; 98 | spaces -= it->text.size(); 99 | ++it; 100 | } 101 | stream << std::setw(std::streamsize(spaces)) << ""; 102 | } 103 | stream << '\n'; 104 | } 105 | 106 | return 0; 107 | } 108 | -------------------------------------------------------------------------------- /examples/whereis/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # Copyright © 2021 Jan Erik Breimo. All rights reserved. 3 | # Created by Jan Erik Breimo on 2021-08-05. 4 | # 5 | # This file is distributed under the BSD License. 6 | # License text is included with the source distribution. 7 | # =========================================================================== 8 | cmake_minimum_required(VERSION 3.14) 9 | project(whereis) 10 | 11 | set(CMAKE_CXX_STANDARD 20) 12 | 13 | add_executable(whereis 14 | whereis.cpp 15 | ) 16 | 17 | set(SINGLE_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../single_src) 18 | 19 | if (EXISTS ${SINGLE_SRC_DIR}/Argos/Argos.hpp) 20 | 21 | target_sources(whereis 22 | PRIVATE 23 | ${SINGLE_SRC_DIR}/Argos/Argos.cpp) 24 | target_include_directories(whereis 25 | PRIVATE 26 | ${SINGLE_SRC_DIR}) 27 | 28 | else() 29 | 30 | include(FetchContent) 31 | FetchContent_Declare(argos 32 | GIT_REPOSITORY "https://github.com/jebreimo/Argos.git" 33 | GIT_TAG "master" 34 | ) 35 | FetchContent_MakeAvailable(argos) 36 | 37 | target_link_libraries(whereis 38 | PRIVATE 39 | Argos::Argos 40 | ) 41 | 42 | endif() 43 | 44 | target_compile_definitions(whereis 45 | PRIVATE 46 | $<$:_CRT_SECURE_NO_WARNINGS> 47 | ) 48 | 49 | 50 | include(GNUInstallDirs) 51 | 52 | install(TARGETS whereis 53 | DESTINATION ${CMAKE_INSTALL_BINDIR} 54 | ) 55 | -------------------------------------------------------------------------------- /examples/whereis/README.md: -------------------------------------------------------------------------------- 1 | whereis 2 | ======= 3 | 4 | This program demonstrates some of Argos' capabilities, including: 5 | 6 | - `--verbose` and `--quiet` options that toggle the same value 7 | - the automatic version option 8 | - use of list options and splitting strings on a separator 9 | - variable number of arguments 10 | - use of `Option::initial_value` 11 | - use of option type LAST_OPTION to allow arguments starting with dashes 12 | - use of ArgumentParser::section to set section headings for the options 13 | 14 | To build it with CMake on Unix-like operating systems (e.g Mac or Linux), cd 15 | into folder where whereis is located and run the following commands: 16 | 17 | ~~~shell 18 | mkdir build 19 | cd build 20 | cmake .. 21 | make 22 | ~~~ 23 | 24 | The result will be a program, `whereis`, which can be used to locate where 25 | programs are located among the folders in the current PATH environment 26 | variable. 27 | 28 | Running the program will yield results similar to this this: 29 | 30 | ~~~s 31 | $ ./whereis python 32 | /usr/local/bin/python 33 | /usr/bin/python 34 | ~~~ 35 | 36 | and giving the help option (either `-h` or `--help`): 37 | 38 | ~~~ 39 | $ ./whereis -h 40 | USAGE 41 | whereis --help 42 | whereis --version 43 | whereis [-p [:]...] [-e [:]...] [--] [-q] [-v] 44 | []... 45 | 46 | Searches the directories in the PATH environment variable for the given 47 | file (or files). 48 | 49 | ARGUMENTS 50 | []... 51 | The file or files to locate. 52 | 53 | MAIN OPTIONS 54 | -p [:]..., --paths [:]... 55 | Search the given path or paths rather than the ones in the PATH 56 | environment variable. Use : as separator between the different 57 | paths. 58 | -e [:]..., --extensions [:]... 59 | File name extensions to test while looking for FILE. Must include 60 | the leading '.'. This option can be used multiple times, multiple 61 | extensions can be set at once by separating them with ':'. 62 | -- 63 | Marks the end of the options. Makes it possible to look for file 64 | names starting with dashes ('-'). 65 | 66 | OTHER OPTIONS 67 | -q, --quiet 68 | Do not show additional information (negates --verbose). 69 | -v, --verbose 70 | Show additional information. 71 | -h, --help 72 | Display the help text. 73 | --version 74 | Display the program version. 75 | ~~~ 76 | -------------------------------------------------------------------------------- /examples/whereis/whereis.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2021 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2021-08-05. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include 9 | #include 10 | #include 11 | 12 | const char VERSION[] = "1.0.0"; 13 | 14 | #ifdef _WIN32 15 | #define PATH_SEPARATOR ";" 16 | const std::vector EXTENSIONS = {"", ".exe", ".com", 17 | ".bat", ".cmd"}; 18 | #else 19 | #define PATH_SEPARATOR ":" 20 | const std::vector EXTENSIONS = {""}; 21 | #endif 22 | 23 | int main(int argc, char* argv[]) 24 | { 25 | auto path_env = std::getenv("PATH"); 26 | 27 | using namespace argos; 28 | auto args = ArgumentParser("whereis") 29 | .about("Searches the directories in the PATH environment variable" 30 | " for the given file (or files).") 31 | .version(VERSION) 32 | .add(Arg("FILE").count(1, UINT_MAX) 33 | .help("The file or files to locate.")) 34 | .current_section("MAIN OPTIONS") 35 | .add(Opt{"-p", "--paths"} 36 | .argument("[" PATH_SEPARATOR "]...") 37 | .initial_value(path_env ? path_env : std::string()) 38 | .help("Search the given path or paths rather than the ones in" 39 | " the PATH environment variable. Use " PATH_SEPARATOR 40 | " as separator between the different paths.")) 41 | .add(Opt{"-e", "--extensions"} 42 | .argument("[" PATH_SEPARATOR "]...") 43 | .operation(OptionOperation::APPEND) 44 | .help("File name extensions to test while looking for FILE. Must" 45 | " include the leading '.'. This option can be used" 46 | " multiple times, multiple extensions can be set at once" 47 | " by separating them with '" PATH_SEPARATOR "'.")) 48 | .add(Opt{"--"}.type(OptionType::LAST_OPTION) 49 | .help("Marks the end of the options. Makes it possible to look" 50 | " for file names starting with dashes ('-').")) 51 | .current_section("OTHER OPTIONS") 52 | .add(Opt{"-q", "--quiet"}.alias("--verbose") 53 | .constant(false) 54 | .help("Do not show additional information (negates --verbose).")) 55 | .add(Opt{"-v", "--verbose"} 56 | .help("Show additional information.")) 57 | .parse(argc, argv); 58 | 59 | auto file_names = args.values("FILE").as_strings(); 60 | auto dirs = args.values("--paths") 61 | .split(PATH_SEPARATOR[0]).as_strings(); 62 | auto verbose = args.value("--verbose").as_bool(); 63 | auto extensions = args.values("--extensions") 64 | .split(PATH_SEPARATOR[0]).as_strings(EXTENSIONS); 65 | 66 | for (const auto& dir : dirs) 67 | { 68 | std::filesystem::path dir_path(dir); 69 | for (const auto& file_name : file_names) 70 | { 71 | bool found = false; 72 | for (const auto& extension : extensions) 73 | { 74 | auto path = dir_path / (file_name + extension); 75 | if (std::filesystem::exists(path)) 76 | { 77 | std::cout << path.string() << '\n'; 78 | found = true; 79 | } 80 | } 81 | if (!found && verbose) 82 | std::cout << "not found: " << dir_path.string() << '\n'; 83 | } 84 | } 85 | return 0; 86 | } 87 | -------------------------------------------------------------------------------- /include/Argos/Argos.hpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-05-01. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | 10 | #include "ArgosException.hpp" 11 | #include "ArgosVersion.hpp" 12 | #include "ArgumentParser.hpp" 13 | 14 | /** 15 | * @file 16 | * @brief Include this file to make all of Argos's public interface available. 17 | */ 18 | -------------------------------------------------------------------------------- /include/Argos/ArgosException.hpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-01-14. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | 10 | #include 11 | 12 | /** 13 | * @file 14 | * @brief Defines the ArgosException class. 15 | */ 16 | 17 | /** 18 | * @brief The namespace for all Argos classes and functions. 19 | */ 20 | namespace argos 21 | { 22 | /** 23 | * @brief The exception class used throughout Argos. 24 | */ 25 | class ArgosException final : public std::runtime_error 26 | { 27 | public: 28 | using std::runtime_error::runtime_error; 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /include/Argos/ArgumentIterator.hpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-01-26. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | 10 | #include "ParsedArguments.hpp" 11 | 12 | /** 13 | * @file 14 | * @brief Defines the ArgumentIterator class. 15 | */ 16 | namespace argos 17 | { 18 | class ArgumentIteratorImpl; 19 | struct ParserData; 20 | 21 | /** 22 | * @brief Iterator class created by ArgumentParser that lets client code 23 | * process one argument or option at a time. 24 | */ 25 | class ArgumentIterator 26 | { 27 | public: 28 | /** 29 | * @private 30 | * @brief Constructs a new instance of ArgumentIterator. 31 | * 32 | * Client code must use ArgumentParser::make_iterator(). 33 | */ 34 | ArgumentIterator(std::vector args, 35 | std::shared_ptr parser_data); 36 | 37 | /** 38 | * @private 39 | */ 40 | ArgumentIterator(const ArgumentIterator&) = delete; 41 | 42 | /** 43 | * @brief Moves the innards of the old object to the new one. 44 | * 45 | * Any attempt to use the old object will result in an exception. 46 | */ 47 | ArgumentIterator(ArgumentIterator&&) noexcept; 48 | 49 | ~ArgumentIterator(); 50 | 51 | /** 52 | * @private 53 | */ 54 | ArgumentIterator& operator=(const ArgumentIterator&) = delete; 55 | 56 | /** 57 | * @brief Moves the innards of the object on the right hand side 58 | * to the one on the left hand side. 59 | * 60 | * Any attempt to use the old object will result in an exception. 61 | */ 62 | ArgumentIterator& operator=(ArgumentIterator&&) noexcept; 63 | 64 | /** 65 | * @brief Process the next argument or option and return the result. 66 | * 67 | * ParsedArguments has been updated when the function returns. 68 | * 69 | * @param arg The definition (ArgumentView or OptionView) of the 70 | * processed argument or option. If ignore_undefined_arguments 71 | * or ignore_undefined_options is true, this pointer can be empty. 72 | * @param value If @a arg is an argument then this is the argument's 73 | * value. If @a arg is an option that take an argument then this 74 | * is the option's value. If @a arg is empty (i.e. this is an 75 | * undefined argument or option) then this is the unrecognized 76 | * argument value or option flag. Otherwise @a value is empty. 77 | * @return true If an argument or option was processed successfully, 78 | * false if there were no more arguments or an error was 79 | * encountered. 80 | */ 81 | bool next(std::unique_ptr& arg, 82 | std::string_view& value); 83 | 84 | /** 85 | * @brief Gives access to all the arguments and options processed 86 | * so far. 87 | * 88 | * The returned object is "live" in the sense that it is updated 89 | * behind the scenes each time next() is called. It is therefore 90 | * sufficient to only call this function once and keep a copy of 91 | * the returned object. 92 | */ 93 | [[nodiscard]] 94 | ParsedArguments parsed_arguments() const; 95 | private: 96 | ArgumentIteratorImpl& impl(); 97 | 98 | [[nodiscard]] const ArgumentIteratorImpl& impl() const; 99 | 100 | std::unique_ptr m_impl; 101 | }; 102 | } 103 | -------------------------------------------------------------------------------- /include/Argos/ArgumentValueIterator.hpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2021 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2021-07-06. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "Enums.hpp" 14 | 15 | /** 16 | * @file 17 | * Defines ArgumentValueIterator, an input iterator suitable for range-based 18 | * for loops over the values in instances of ArgumentValues. 19 | */ 20 | 21 | namespace argos 22 | { 23 | class ArgumentValue; 24 | class ParsedArgumentsImpl; 25 | 26 | /** 27 | * @brief Iterator for the values in an instance of ArgumentValues. 28 | * 29 | * Direct use of this iterator should be avoided, it is intended to 30 | * be used in range-based for loops. For algorithms etc. it is recommended 31 | * to use the vector returned by ArgumentValues::values. 32 | */ 33 | class ArgumentValueIterator 34 | { 35 | public: 36 | /** 37 | * @private 38 | */ 39 | using It = std::vector< 40 | std::pair 41 | >::const_iterator; 42 | 43 | /** 44 | * @brief Construct an empty iterator. 45 | * 46 | * The iterator has to be assigned the result of ArgumentValues::begin 47 | * or ArgumentValues::end before it can be used. 48 | */ 49 | ArgumentValueIterator(); 50 | 51 | /** 52 | * @private 53 | * Only called from ArgumentValues 54 | */ 55 | ArgumentValueIterator(const It& internal_iterator, 56 | std::shared_ptr args, 57 | ValueId value_id); 58 | 59 | /** 60 | * @brief Prefix increment operator. 61 | */ 62 | ArgumentValueIterator& operator++(); 63 | 64 | /** 65 | * @brief Postfix increment operator. 66 | */ 67 | ArgumentValueIterator operator++(int) &; 68 | 69 | /** 70 | * @brief Returns the current value. 71 | * 72 | * @note The returned value is not a reference. 73 | */ 74 | ArgumentValue operator*() const; 75 | 76 | /** 77 | * @private 78 | */ 79 | [[nodiscard]] It internal_iterator() const; 80 | private: 81 | It m_iterator = {}; 82 | std::shared_ptr m_args; 83 | ValueId m_value_id = {}; 84 | }; 85 | 86 | /** 87 | * @brief Returns true @a a and @a b point to the same argument. 88 | */ 89 | bool operator==(const ArgumentValueIterator& a, 90 | const ArgumentValueIterator& b); 91 | 92 | /** 93 | * @brief Returns false unless @a a and @a b point to the same argument. 94 | */ 95 | bool operator!=(const ArgumentValueIterator& a, 96 | const ArgumentValueIterator& b); 97 | } 98 | -------------------------------------------------------------------------------- /include/Argos/ArgumentView.hpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-01-28. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | 10 | #include "IArgumentView.hpp" 11 | 12 | /** 13 | * @file 14 | * @brief Defines the ArgumentView class. 15 | */ 16 | 17 | namespace argos 18 | { 19 | struct ArgumentData; 20 | 21 | /** 22 | * @brief Provides read-only access to an argument definition. 23 | */ 24 | class ArgumentView final : public IArgumentView 25 | { 26 | public: 27 | /** 28 | * @private 29 | * @brief For internal use only. 30 | * 31 | * Client code can only receive objects, not construct them. 32 | */ 33 | explicit ArgumentView(const ArgumentData* data); 34 | 35 | /** 36 | * @brief Returns the argument's or option's help text. 37 | */ 38 | [[nodiscard]] std::string help() const final; 39 | 40 | /** 41 | * @brief Returns the argument's section name. 42 | */ 43 | [[nodiscard]] const std::string& section() const final; 44 | 45 | /** 46 | * @brief Returns the argument's alias name. 47 | */ 48 | [[nodiscard]] const std::string& alias() const final; 49 | 50 | /** 51 | * @brief Returns the argument's visibility in 52 | * the help text and error messages. 53 | */ 54 | [[nodiscard]] Visibility visibility() const final; 55 | 56 | /** 57 | * @brief Returns the argument's custom id. 58 | */ 59 | [[nodiscard]] int id() const final; 60 | 61 | /** 62 | * @brief Returns the numeric id of the value the argument assigns 63 | * or appends to. 64 | * 65 | * This value is created internally in Argos and must not be 66 | * confused with the customizable value returned by id(). 67 | * If different options or arguments have the same value name, they 68 | * will also have the same value id. 69 | * 70 | * @return options with operation OptionOperation::NONE have 71 | * a value of 0, all other options and arguments have a value 72 | * greater than 0. 73 | */ 74 | [[nodiscard]] ValueId value_id() const final; 75 | 76 | /** 77 | * @brief Returns the argument's argument id. 78 | * 79 | * This id is assigned and used internally to uniquely identify 80 | * each argument and option. 81 | */ 82 | [[nodiscard]] ArgumentId argument_id() const final; 83 | 84 | /** 85 | * @brief Returns the argument's name. 86 | */ 87 | [[nodiscard]] const std::string& name() const; 88 | 89 | /** 90 | * @brief Returns true if the argument is optional (i.e. its minimum 91 | * count is 0). 92 | */ 93 | [[nodiscard]] bool optional() const; 94 | 95 | /** 96 | * @brief Returns the argument's minimum and maximum counts. 97 | * 98 | * Normal arguments have both set to 1. 99 | */ 100 | [[nodiscard]] std::pair count() const; 101 | private: 102 | const ArgumentData* m_argument; 103 | }; 104 | } 105 | -------------------------------------------------------------------------------- /include/Argos/Callbacks.hpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-02-22. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | #include 10 | #include "ArgumentView.hpp" 11 | #include "OptionView.hpp" 12 | #include "ParsedArgumentsBuilder.hpp" 13 | 14 | /** 15 | * @file 16 | * @brief Defines ArgumentCallback and OptionCallback. 17 | */ 18 | 19 | namespace argos 20 | { 21 | /** 22 | * @brief The parameter type for argument and option callbacks. 23 | * @tparam ViewT Either ArgumentView or OptionView. 24 | */ 25 | template 26 | struct CallbackArguments 27 | { 28 | /** 29 | * The argument or option that was encountered. 30 | * Is an instance of either ArgumentView or OptionView. 31 | */ 32 | ViewT view; 33 | 34 | /** 35 | * The value of the argument or the option's argument, if any. 36 | */ 37 | std::string_view value; 38 | 39 | /** 40 | * Gives access to the arguments and options processed so far. Can be 41 | * used to get or set the values of arguments and options. 42 | */ 43 | ParsedArgumentsBuilder builder; 44 | 45 | /** 46 | * Add new arguments to the command line that is being parsed. These 47 | * arguments are inserted immediately after the current argument 48 | * and can be anything, including options and 49 | * commands. 50 | */ 51 | std::vector new_arguments; 52 | 53 | CallbackArguments(const ViewT& view, 54 | std::string_view value, 55 | std::shared_ptr impl) 56 | : view(view), 57 | value(value), 58 | builder(std::move(impl)) 59 | { 60 | } 61 | }; 62 | 63 | /** 64 | * @brief The parameter type for argument callbacks. 65 | */ 66 | using ArgumentCallbackArguments = CallbackArguments; 67 | 68 | /** 69 | * @brief A callback that is called each time given arguments appear 70 | * on the command line. 71 | */ 72 | using ArgumentCallback = std::function; 73 | 74 | /** 75 | * @brief The parameter type for option callbacks. 76 | */ 77 | using OptionCallbackArguments = CallbackArguments; 78 | 79 | /** 80 | * @brief A callback that is called each time given options appear 81 | * on the command line. 82 | */ 83 | using OptionCallback = std::function; 84 | 85 | /** 86 | * @brief A callback that returns a part of the help text. 87 | */ 88 | using TextCallback = std::function; 89 | } 90 | -------------------------------------------------------------------------------- /include/Argos/CommandView.hpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2024 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2024-09-21. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | #include "ArgumentView.hpp" 10 | #include "OptionView.hpp" 11 | 12 | namespace argos 13 | { 14 | struct CommandData; 15 | 16 | /** 17 | * @brief Provides read-only access to a command definition. 18 | */ 19 | class CommandView : public IArgumentView 20 | { 21 | public: 22 | /** 23 | * @private 24 | * @brief For internal use only. 25 | * 26 | * Client code can only receive objects, not construct them. 27 | */ 28 | explicit CommandView(const CommandData* command); 29 | 30 | /** 31 | * @brief Returns the command's help text. 32 | * 33 | * This is the text that is displayed in the parent command's list 34 | * of sub-commands. It will also be used as the about text if the 35 | * command doesn't have an explicit about text. 36 | */ 37 | [[nodiscard]] std::string help() const override; 38 | 39 | /** 40 | * @brief Returns the command's section name. 41 | */ 42 | [[nodiscard]] const std::string& section() const override; 43 | 44 | /** 45 | * @brief Returns the command's value name. 46 | */ 47 | [[nodiscard]] const std::string& alias() const override; 48 | 49 | [[nodiscard]] Visibility visibility() const override; 50 | 51 | [[nodiscard]] int id() const override; 52 | 53 | [[nodiscard]] ValueId value_id() const override; 54 | 55 | [[nodiscard]] ArgumentId argument_id() const override; 56 | 57 | /** 58 | * @brief Returns the command's name. 59 | */ 60 | [[nodiscard]] std::string name() const; 61 | 62 | /** 63 | * @brief Returns the command's arguments. 64 | */ 65 | [[nodiscard]] std::vector arguments() const; 66 | 67 | /** 68 | * @brief Returns the command's options. 69 | */ 70 | [[nodiscard]] std::vector options() const; 71 | 72 | /** 73 | * @brief Returns the command's sub-commands. 74 | */ 75 | [[nodiscard]] std::vector subcommands() const; 76 | 77 | /** 78 | * @brief Returns true if the program requires one or more 79 | * sub-commands. 80 | */ 81 | [[nodiscard]] bool require_subcommand() const; 82 | private: 83 | const CommandData* m_command; 84 | }; 85 | } 86 | -------------------------------------------------------------------------------- /include/Argos/IArgumentView.hpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-01-26. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | 10 | #include 11 | #include "Enums.hpp" 12 | 13 | /** 14 | * @file 15 | * @brief Defines the IArgumentView interface class. 16 | */ 17 | 18 | namespace argos 19 | { 20 | /** 21 | * @brief Interface class with the functions ArgumentView and OptionView 22 | * have in common. 23 | */ 24 | class IArgumentView 25 | { 26 | public: 27 | virtual ~IArgumentView() = default; 28 | 29 | /** 30 | * @brief Returns the object's help text. 31 | */ 32 | [[nodiscard]] virtual std::string help() const = 0; 33 | 34 | /** 35 | * @brief Returns the object's section name. 36 | */ 37 | [[nodiscard]] virtual const std::string& section() const = 0; 38 | 39 | /** 40 | * @brief Returns the object's alias. 41 | */ 42 | [[nodiscard]] virtual const std::string& alias() const = 0; 43 | 44 | /** 45 | * @brief Returns the object's visibility in the help text and 46 | * error messages. 47 | */ 48 | [[nodiscard]] virtual Visibility visibility() const = 0; 49 | 50 | /** 51 | * @brief Returns the object's custom id. 52 | */ 53 | [[nodiscard]] virtual int id() const = 0; 54 | 55 | /** 56 | * @brief Returns the numeric id of the value the argument or option 57 | * assigns or appends to. 58 | * 59 | * This value is created internally in Argos and must not be 60 | * confused with the customizable value returned by id(). 61 | * If different options or arguments are aliases for each other, 62 | * they will also have the same value id. 63 | * 64 | * @return all options with operation OptionOperation::NONE have 65 | * a value of 0, all other options and arguments have a value 66 | * greater than 0. 67 | */ 68 | [[nodiscard]] virtual ValueId value_id() const = 0; 69 | 70 | /** 71 | * @brief Returns the object's argument_id(). 72 | * 73 | * This id is assigned and used internally to uniquely identify 74 | * each argument and option. 75 | */ 76 | [[nodiscard]] virtual ArgumentId argument_id() const = 0; 77 | }; 78 | } 79 | -------------------------------------------------------------------------------- /include/Argos/OptionView.hpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-01-28. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | #include "IArgumentView.hpp" 10 | #include 11 | 12 | /** 13 | * @file 14 | * @brief Defines the OptionView class. 15 | */ 16 | 17 | namespace argos 18 | { 19 | struct OptionData; 20 | 21 | /** 22 | * @brief Provides read-only access to an option definition. 23 | */ 24 | class OptionView final : public IArgumentView 25 | { 26 | public: 27 | /** 28 | * @private 29 | * @brief For internal use only. 30 | * 31 | * Client code can only receive objects, not construct them. 32 | */ 33 | explicit OptionView(const OptionData* data); 34 | 35 | /** 36 | * @brief Returns the option's or option's help text. 37 | */ 38 | [[nodiscard]] std::string help() const final; 39 | 40 | /** 41 | * @brief Returns the option's section name. 42 | */ 43 | [[nodiscard]] const std::string& section() const final; 44 | 45 | /** 46 | * @brief Returns the option's alias. 47 | */ 48 | [[nodiscard]] const std::string& alias() const final; 49 | 50 | /** 51 | * @brief Returns the option's visibility in 52 | * the help text and error messages. 53 | */ 54 | [[nodiscard]] Visibility visibility() const final; 55 | 56 | /** 57 | * @brief Returns the option's custom id. 58 | */ 59 | [[nodiscard]] int id() const final; 60 | 61 | /** 62 | * @brief Returns the numeric id of the value the argument assigns 63 | * or appends to. 64 | * 65 | * This value is created internally in Argos and must not be 66 | * confused with the customizable value returned by id(). 67 | * If different options or arguments have the same value name, they 68 | * will also have the same value id. 69 | * 70 | * @return options with operation OptionOperation::NONE have 71 | * a value of 0, all other options and arguments have a value 72 | * greater than 0. 73 | */ 74 | [[nodiscard]] ValueId value_id() const final; 75 | 76 | /** 77 | * @brief Returns the option's argument id. 78 | * 79 | * This id is assigned and used internally to uniquely identify 80 | * each argument and option. 81 | */ 82 | [[nodiscard]] ArgumentId argument_id() const final; 83 | 84 | /** 85 | * @brief Returns the option's operation. 86 | */ 87 | [[nodiscard]] OptionOperation operation() const; 88 | 89 | /** 90 | * @brief Returns the option's flags. 91 | */ 92 | [[nodiscard]] const std::vector& flags() const; 93 | 94 | /** 95 | * @brief Returns the option's argument. 96 | */ 97 | [[nodiscard]] const std::string& argument() const; 98 | 99 | /** 100 | * @brief Returns the option's initial value. 101 | */ 102 | [[nodiscard]] const std::string& initial_value() const; 103 | 104 | /** 105 | * @brief Returns the option's constant. 106 | * 107 | * @note The constant is stored as a string internally, even if the 108 | * option was assigned an integer or boolean value. 109 | */ 110 | [[nodiscard]] const std::string& constant() const; 111 | 112 | /** 113 | * @brief Returns the option's type. 114 | */ 115 | [[nodiscard]] OptionType type() const; 116 | 117 | /** 118 | * @brief Returns false if the option is mandatory. 119 | */ 120 | [[nodiscard]] bool optional() const; 121 | private: 122 | const OptionData* m_option; 123 | }; 124 | } 125 | -------------------------------------------------------------------------------- /src/Argos/ArgosThrow.hpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-01-20. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | #include "Argos/ArgosException.hpp" 10 | 11 | #define _ARGOS_THROW_3(file, line, msg) \ 12 | throw ::argos::ArgosException(file ":" #line ": " msg) 13 | 14 | #define _ARGOS_THROW_2(file, line, msg) \ 15 | _ARGOS_THROW_3(file, line, msg) 16 | 17 | #define ARGOS_THROW(msg) \ 18 | _ARGOS_THROW_2(__FILE__, __LINE__, msg) 19 | -------------------------------------------------------------------------------- /src/Argos/ArgosVersion.hpp.in: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-03-12. 4 | // 5 | // This file is distributed under the BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | 10 | /** 11 | * @file 12 | * @brief Defines the current Argos version. 13 | */ 14 | 15 | /** 16 | * @brief String representation of the complete version number. 17 | */ 18 | constexpr char ARGOS_VERSION[] = "@Argos_VERSION@"; 19 | 20 | /** 21 | * @brief Incremented when a new version contains significant changes. It 22 | * might be necessary to update code that has been compiled with previous 23 | * the version of Argos. 24 | */ 25 | #define ARGOS_VERSION_MAJOR @Argos_VERSION_MAJOR@ 26 | 27 | /** 28 | * @brief Incremented when Argos's interface is modified in ways that are 29 | * compatible with existing client code. 30 | */ 31 | #define ARGOS_VERSION_MINOR @Argos_VERSION_MINOR@ 32 | 33 | /** 34 | * @brief Incremented when the changes does not affect the interface. 35 | */ 36 | #define ARGOS_VERSION_PATCH @Argos_VERSION_PATCH@ 37 | -------------------------------------------------------------------------------- /src/Argos/Argument.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-01-07. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include "Argos/Argument.hpp" 9 | 10 | #include "ArgosThrow.hpp" 11 | #include "ArgumentData.hpp" 12 | 13 | namespace argos 14 | { 15 | Argument::Argument() 16 | : m_argument(std::make_unique()) 17 | {} 18 | 19 | Argument::Argument(const std::string& name) 20 | : m_argument(std::make_unique()) 21 | { 22 | m_argument->name = name; 23 | } 24 | 25 | Argument::Argument(const Argument& rhs) 26 | : m_argument(rhs.m_argument 27 | ? std::make_unique(*rhs.m_argument) 28 | : std::unique_ptr()) 29 | {} 30 | 31 | Argument::Argument(Argument&& rhs) noexcept 32 | : m_argument(std::move(rhs.m_argument)) 33 | {} 34 | 35 | Argument::~Argument() = default; 36 | 37 | Argument& Argument::operator=(const Argument& rhs) 38 | { 39 | if (this != &rhs) 40 | { 41 | if (rhs.m_argument) 42 | m_argument = std::make_unique(*rhs.m_argument); 43 | else 44 | m_argument = {}; 45 | } 46 | return *this; 47 | } 48 | 49 | Argument& Argument::operator=(Argument&& rhs) noexcept 50 | { 51 | m_argument = std::move(rhs.m_argument); 52 | return *this; 53 | } 54 | 55 | Argument& Argument::help(const std::string& text) 56 | { 57 | check_argument(); 58 | m_argument->help = text; 59 | return *this; 60 | } 61 | 62 | Argument& Argument::help(TextCallback callback) 63 | { 64 | check_argument(); 65 | m_argument->help = callback; 66 | return *this; 67 | } 68 | 69 | Argument& Argument::section(const std::string& name) 70 | { 71 | check_argument(); 72 | m_argument->section = name; 73 | return *this; 74 | } 75 | 76 | Argument& Argument::alias(const std::string& id) 77 | { 78 | check_argument(); 79 | m_argument->alias = id; 80 | return *this; 81 | } 82 | 83 | Argument& Argument::callback(ArgumentCallback callback) 84 | { 85 | check_argument(); 86 | m_argument->callback = std::move(callback); 87 | return *this; 88 | } 89 | 90 | Argument& Argument::visibility(Visibility visibility) 91 | { 92 | check_argument(); 93 | m_argument->visibility = visibility; 94 | return *this; 95 | } 96 | 97 | Argument& Argument::id(int id) 98 | { 99 | check_argument(); 100 | m_argument->id = id; 101 | return *this; 102 | } 103 | 104 | Argument& Argument::name(const std::string& name) 105 | { 106 | check_argument(); 107 | m_argument->name = name; 108 | return *this; 109 | } 110 | 111 | Argument& Argument::optional(bool optional) 112 | { 113 | check_argument(); 114 | if (optional) 115 | m_argument->min_count = 0; 116 | else if (m_argument->min_count == 0) 117 | m_argument->min_count = 1; 118 | return *this; 119 | } 120 | 121 | Argument& Argument::mandatory(bool mandatory) 122 | { 123 | return optional(!mandatory); 124 | } 125 | 126 | Argument& Argument::count(unsigned n) 127 | { 128 | if (n <= 0) 129 | ARGOS_THROW("Argument's count must be greater than 0."); 130 | check_argument(); 131 | m_argument->min_count = m_argument->max_count = n; 132 | return *this; 133 | } 134 | 135 | Argument& Argument::count(unsigned min_count, unsigned max_count) 136 | { 137 | if (max_count == 0) 138 | ARGOS_THROW("Argument's max count must be greater than 0."); 139 | if (max_count < min_count) 140 | ARGOS_THROW("Argument's max count cannot be less than its min count."); 141 | check_argument(); 142 | m_argument->min_count = min_count; 143 | m_argument->max_count = max_count; 144 | return *this; 145 | } 146 | 147 | std::unique_ptr Argument::release() 148 | { 149 | check_argument(); 150 | return std::move(m_argument); 151 | } 152 | 153 | void Argument::check_argument() const 154 | { 155 | if (!m_argument) 156 | ARGOS_THROW("Argument has been moved."); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/Argos/ArgumentCounter.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-01-22. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include "ArgumentCounter.hpp" 9 | 10 | namespace argos 11 | { 12 | namespace 13 | { 14 | size_t find_first_optional(const CommandData& command) 15 | { 16 | auto& arguments = command.arguments; 17 | size_t result = 0; 18 | for (size_t i = 0; i < arguments.size(); ++i) 19 | { 20 | if (arguments[i]->min_count > 0) 21 | result = i + 1; 22 | } 23 | return result; 24 | } 25 | 26 | void make_argument_counters( 27 | const CommandData& command, 28 | std::vector>& counters, 29 | size_t& first_optional) 30 | { 31 | first_optional = find_first_optional(command); 32 | for (size_t i = 0; i < command.arguments.size(); ++i) 33 | { 34 | auto& a = command.arguments[i]; 35 | if (i + 1 == first_optional && a->min_count != a->max_count) 36 | { 37 | counters.emplace_back(a->min_count, a.get()); 38 | counters.emplace_back(a->max_count - a->min_count, a.get()); 39 | } 40 | else 41 | { 42 | counters.emplace_back(a->max_count, a.get()); 43 | } 44 | } 45 | } 46 | 47 | std::vector> 48 | make_argument_counters(const CommandData& command, 49 | size_t n) 50 | { 51 | const auto [lo, hi] = ArgumentCounter::get_min_max_count(command); 52 | if (n < lo) 53 | n = 0; 54 | else if (n > hi) 55 | n = hi - lo; 56 | else 57 | n -= lo; 58 | 59 | std::vector> result; 60 | for (auto& arg : command.arguments) 61 | { 62 | if (n == 0 || arg->min_count == arg->max_count) 63 | { 64 | result.emplace_back(arg->min_count, arg.get()); 65 | } 66 | else if (arg->min_count + n <= arg->max_count) 67 | { 68 | result.emplace_back(arg->min_count + n, arg.get()); 69 | n = 0; 70 | } 71 | else 72 | { 73 | result.emplace_back(arg->max_count, arg.get()); 74 | n -= arg->max_count - arg->min_count; 75 | } 76 | } 77 | return result; 78 | } 79 | } 80 | 81 | ArgumentCounter::ArgumentCounter() 82 | : m_counters() 83 | { 84 | } 85 | 86 | ArgumentCounter::ArgumentCounter(const CommandData& command) 87 | { 88 | make_argument_counters(command, m_counters, m_first_optional); 89 | } 90 | 91 | ArgumentCounter::ArgumentCounter(const CommandData& command, 92 | size_t argument_count, 93 | size_t initial_count) 94 | : m_counters(make_argument_counters(command, argument_count)), 95 | m_first_optional(m_counters.size()) 96 | { 97 | for (size_t i = 0; i < initial_count; ++i) 98 | { 99 | if (next_argument() == nullptr) 100 | break; 101 | } 102 | } 103 | 104 | const ArgumentData* ArgumentCounter::next_argument() 105 | { 106 | while (m_index != m_counters.size() && m_counters[m_index].first == 0) 107 | ++m_index; 108 | 109 | if (m_index == m_counters.size()) 110 | return nullptr; 111 | 112 | ++m_counter; 113 | --m_counters[m_index].first; 114 | return m_counters[m_index].second; 115 | } 116 | 117 | size_t ArgumentCounter::count() const 118 | { 119 | return m_counter; 120 | } 121 | 122 | bool ArgumentCounter::is_complete() const 123 | { 124 | return m_index >= m_first_optional 125 | || (m_index + 1 == m_first_optional 126 | && m_counters[m_index].first == 0); 127 | } 128 | 129 | std::pair 130 | ArgumentCounter::get_min_max_count(const CommandData& command) 131 | { 132 | size_t lo = 0, hi = 0; 133 | for (auto& arg : command.arguments) 134 | { 135 | lo += arg->min_count; 136 | if (hi != SIZE_MAX) 137 | { 138 | if (arg->max_count > SIZE_MAX - hi) 139 | hi = SIZE_MAX; 140 | else 141 | hi += arg->max_count; 142 | } 143 | } 144 | return {lo, hi}; 145 | } 146 | 147 | bool ArgumentCounter::requires_argument_count(const CommandData& command) 148 | { 149 | bool deterministic = true; 150 | for (auto& arg : command.arguments) 151 | { 152 | if (!deterministic) 153 | return true; 154 | if (arg->min_count != arg->max_count) 155 | deterministic = false; 156 | } 157 | return false; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/Argos/ArgumentCounter.hpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-01-22. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | #include 10 | #include 11 | #include "CommandData.hpp" 12 | 13 | namespace argos 14 | { 15 | class ArgumentCounter 16 | { 17 | public: 18 | ArgumentCounter(); 19 | 20 | explicit ArgumentCounter(const CommandData& command); 21 | 22 | ArgumentCounter(const CommandData& command, 23 | size_t argument_count, 24 | size_t initial_count = 0); 25 | 26 | const ArgumentData* next_argument(); 27 | 28 | [[nodiscard]] size_t count() const; 29 | 30 | [[nodiscard]] bool is_complete() const; 31 | 32 | static std::pair 33 | get_min_max_count(const CommandData& command); 34 | 35 | static bool requires_argument_count(const CommandData& command); 36 | 37 | private: 38 | using Counter = std::pair; 39 | std::vector m_counters; 40 | size_t m_index = 0; 41 | size_t m_first_optional = 0; 42 | size_t m_counter = 0; 43 | }; 44 | } 45 | -------------------------------------------------------------------------------- /src/Argos/ArgumentData.hpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-01-09. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | #include 10 | #include "Argos/Callbacks.hpp" 11 | #include "Argos/Enums.hpp" 12 | #include "TextSource.hpp" 13 | 14 | namespace argos 15 | { 16 | struct ArgumentData 17 | { 18 | std::string name; 19 | TextSource help; 20 | std::string section; 21 | std::string alias; 22 | ArgumentCallback callback; 23 | unsigned min_count = 1; 24 | unsigned max_count = 1; 25 | Visibility visibility = Visibility::NORMAL; 26 | int id = 0; 27 | ValueId value_id = {}; 28 | ArgumentId argument_id = {}; 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /src/Argos/ArgumentIterator.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-01-26. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include "Argos/ArgumentIterator.hpp" 9 | 10 | #include "ArgosThrow.hpp" 11 | #include "ArgumentIteratorImpl.hpp" 12 | 13 | namespace argos 14 | { 15 | ArgumentIterator::ArgumentIterator(std::vector args, 16 | std::shared_ptr parser_data) 17 | : m_impl(std::make_unique(std::move(args), 18 | std::move(parser_data))) 19 | {} 20 | 21 | ArgumentIterator::ArgumentIterator(ArgumentIterator&& rhs) noexcept 22 | : m_impl(std::move(rhs.m_impl)) 23 | {} 24 | 25 | ArgumentIterator::~ArgumentIterator() = default; 26 | 27 | ArgumentIterator& 28 | ArgumentIterator::operator=(ArgumentIterator&& rhs) noexcept 29 | { 30 | m_impl = std::move(rhs.m_impl); 31 | return *this; 32 | } 33 | 34 | bool ArgumentIterator::next(std::unique_ptr& arg, 35 | std::string_view& value) 36 | { 37 | const auto res = impl().next(); 38 | switch (std::get<0>(res)) 39 | { 40 | case IteratorResultCode::ARGUMENT: 41 | arg = std::make_unique( 42 | std::get(std::get<1>(res))); 43 | value = std::get<2>(res); 44 | return true; 45 | case IteratorResultCode::OPTION: 46 | arg = std::make_unique( 47 | std::get(std::get<1>(res))); 48 | value = std::get<2>(res); 49 | return true; 50 | case IteratorResultCode::COMMAND: 51 | arg = std::make_unique( 52 | std::get(std::get<1>(res))); 53 | value = std::get<2>(res); 54 | return true; 55 | case IteratorResultCode::UNKNOWN: 56 | arg = {}; 57 | value = std::get<2>(res); 58 | return true; 59 | case IteratorResultCode::DONE: 60 | case IteratorResultCode::ERROR: 61 | break; 62 | } 63 | arg = {}; 64 | value = {}; 65 | return false; 66 | } 67 | 68 | ParsedArguments ArgumentIterator::parsed_arguments() const 69 | { 70 | return ParsedArguments(impl().parsed_arguments()); 71 | } 72 | 73 | ArgumentIteratorImpl& ArgumentIterator::impl() 74 | { 75 | if (!m_impl) 76 | ARGOS_THROW("This ArgumentIterator has been moved from."); 77 | return *m_impl; 78 | } 79 | 80 | const ArgumentIteratorImpl& ArgumentIterator::impl() const 81 | { 82 | if (!m_impl) 83 | ARGOS_THROW("This ArgumentIterator has been moved from."); 84 | return *m_impl; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Argos/ArgumentIteratorImpl.hpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-01-07. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | #include 10 | #include 11 | #include "ArgumentCounter.hpp" 12 | #include "ParserData.hpp" 13 | #include "OptionData.hpp" 14 | #include "OptionIteratorWrapper.hpp" 15 | #include "ParsedArgumentsImpl.hpp" 16 | 17 | namespace argos 18 | { 19 | enum class IteratorResultCode 20 | { 21 | ARGUMENT, 22 | OPTION, 23 | COMMAND, 24 | DONE, 25 | UNKNOWN, 26 | ERROR 27 | }; 28 | 29 | using IteratorResultData = std::variant< 30 | std::monostate, 31 | const ArgumentData*, 32 | const OptionData*, 33 | const CommandData*>; 34 | 35 | using IteratorResult = std::tuple< 36 | IteratorResultCode, 37 | IteratorResultData, 38 | std::string_view>; 39 | 40 | class ArgumentIteratorImpl 41 | { 42 | public: 43 | ArgumentIteratorImpl(std::vector args, 44 | std::shared_ptr data); 45 | 46 | IteratorResult next(); 47 | 48 | static std::shared_ptr 49 | parse(std::vector args, 50 | const std::shared_ptr& data); 51 | 52 | [[nodiscard]] const std::shared_ptr& 53 | parsed_arguments() const; 54 | 55 | [[nodiscard]] const std::shared_ptr& 56 | toplevel_parsed_arguments() const; 57 | 58 | private: 59 | enum class OptionResult 60 | { 61 | NORMAL, 62 | LAST_ARGUMENT, 63 | STOP, 64 | EXIT, 65 | ERROR 66 | }; 67 | 68 | std::pair 69 | process_option(const OptionData& opt, const std::string& flag); 70 | 71 | IteratorResult process_option(const std::string& flag); 72 | 73 | IteratorResult process_argument(const std::string& value); 74 | 75 | IteratorResult process_command(const CommandData* command); 76 | 77 | void copy_remaining_arguments_to_parser_result(); 78 | 79 | [[nodiscard]] size_t count_arguments() const; 80 | 81 | bool check_argument_and_option_counts(); 82 | 83 | [[nodiscard]] std::pair 84 | find_sibling_command(std::string_view name) const; 85 | 86 | [[nodiscard]] 87 | std::optional find_first_multi_command_parent() const; 88 | 89 | void reactivate_multi_command_parent(size_t index); 90 | 91 | void update_arguments(const std::vector& args); 92 | 93 | void error(const std::string& message = {}); 94 | 95 | bool has_all_mandatory_options(const ParsedArgumentsImpl& parsed_args, 96 | const CommandData& command); 97 | 98 | std::shared_ptr m_data; 99 | const CommandData* m_command = nullptr; 100 | std::vector> m_parsed_args; 101 | OptionIteratorWrapper m_iterator; 102 | ArgumentCounter m_argument_counter; 103 | 104 | enum class State 105 | { 106 | ARGUMENTS_AND_OPTIONS, 107 | ARGUMENTS_ONLY, 108 | DONE, 109 | ERROR 110 | }; 111 | 112 | State m_state = State::ARGUMENTS_AND_OPTIONS; 113 | }; 114 | } 115 | -------------------------------------------------------------------------------- /src/Argos/ArgumentValueIterator.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2021 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2021-07-07. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include "Argos/ArgumentValueIterator.hpp" 9 | 10 | #include "Argos/ArgumentValue.hpp" 11 | 12 | namespace argos 13 | { 14 | ArgumentValueIterator::ArgumentValueIterator() = default; 15 | 16 | ArgumentValueIterator::ArgumentValueIterator( 17 | const ArgumentValueIterator::It& internal_iterator, 18 | std::shared_ptr args, 19 | ValueId value_id) 20 | : m_iterator(internal_iterator), 21 | m_args(std::move(args)), 22 | m_value_id(value_id) 23 | {} 24 | 25 | ArgumentValueIterator& ArgumentValueIterator::operator++() 26 | { 27 | ++m_iterator; 28 | return *this; 29 | } 30 | 31 | ArgumentValueIterator ArgumentValueIterator::operator++(int) & 32 | { 33 | auto it = *this; 34 | ++m_iterator; 35 | return it; 36 | } 37 | 38 | ArgumentValue ArgumentValueIterator::operator*() const 39 | { 40 | return {m_iterator->first, m_args, 41 | m_value_id, m_iterator->second}; 42 | } 43 | 44 | ArgumentValueIterator::It ArgumentValueIterator::internal_iterator() const 45 | { 46 | return m_iterator; 47 | } 48 | 49 | bool operator==(const ArgumentValueIterator& a, const ArgumentValueIterator& b) 50 | { 51 | return a.internal_iterator() == b.internal_iterator(); 52 | } 53 | 54 | bool operator!=(const ArgumentValueIterator& a, const ArgumentValueIterator& b) 55 | { 56 | return a.internal_iterator() != b.internal_iterator(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Argos/ArgumentView.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-01-28. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include "Argos/ArgumentView.hpp" 9 | 10 | #include "ArgosThrow.hpp" 11 | #include "ArgumentData.hpp" 12 | 13 | namespace argos 14 | { 15 | ArgumentView::ArgumentView(const ArgumentData* data) 16 | : m_argument(data) 17 | { 18 | if (!data) 19 | ARGOS_THROW("data can not be null"); 20 | } 21 | 22 | std::string ArgumentView::help() const 23 | { 24 | return get_text(m_argument->help); 25 | } 26 | 27 | const std::string& ArgumentView::section() const 28 | { 29 | return m_argument->section; 30 | } 31 | 32 | const std::string& ArgumentView::alias() const 33 | { 34 | return m_argument->alias; 35 | } 36 | 37 | Visibility ArgumentView::visibility() const 38 | { 39 | return m_argument->visibility; 40 | } 41 | 42 | int ArgumentView::id() const 43 | { 44 | return m_argument->id; 45 | } 46 | 47 | const std::string& ArgumentView::name() const 48 | { 49 | return m_argument->name; 50 | } 51 | 52 | bool ArgumentView::optional() const 53 | { 54 | return m_argument->min_count == 0; 55 | } 56 | 57 | std::pair ArgumentView::count() const 58 | { 59 | return {m_argument->min_count, m_argument->max_count}; 60 | } 61 | 62 | ValueId ArgumentView::value_id() const 63 | { 64 | return m_argument->value_id; 65 | } 66 | 67 | ArgumentId ArgumentView::argument_id() const 68 | { 69 | return m_argument->argument_id; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Argos/Command.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2024 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2024-09-04. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include "Argos/Command.hpp" 9 | 10 | #include "ArgosThrow.hpp" 11 | #include "CommandData.hpp" 12 | 13 | namespace argos 14 | { 15 | Command::Command() 16 | : data_(std::make_unique()) 17 | {} 18 | 19 | Command::Command(std::string name) 20 | : data_(std::make_unique()) 21 | { 22 | data_->name = std::move(name); 23 | } 24 | 25 | Command::Command(const Command& rhs) 26 | : data_(rhs.data_ ? std::make_unique(*rhs.data_) : nullptr) 27 | { 28 | } 29 | 30 | Command::Command(Command&& rhs) noexcept 31 | : data_(std::move(rhs.data_)) 32 | { 33 | } 34 | 35 | Command::~Command() = default; 36 | 37 | Command& Command::operator=(const Command& rhs) 38 | { 39 | if (this != &rhs) 40 | { 41 | data_ = rhs.data_ 42 | ? std::make_unique(*rhs.data_) 43 | : nullptr; 44 | } 45 | return *this; 46 | } 47 | 48 | Command& Command::operator=(Command&& rhs) noexcept 49 | { 50 | data_ = std::move(rhs.data_); 51 | return *this; 52 | } 53 | 54 | Command& Command::add(Argument& argument) 55 | { 56 | return add(std::move(argument)); 57 | } 58 | 59 | Command& Command::add(Argument&& argument) 60 | { 61 | check_command(); 62 | data_->add(argument.release()); 63 | return *this; 64 | } 65 | 66 | Command& Command::add(Option& option) 67 | { 68 | return add(std::move(option)); 69 | } 70 | 71 | Command& Command::add(Option&& option) 72 | { 73 | check_command(); 74 | data_->add(option.release()); 75 | return *this; 76 | } 77 | 78 | Command& Command::add(Command& command) 79 | { 80 | return add(std::move(command)); 81 | } 82 | 83 | Command& Command::add(Command&& command) 84 | { 85 | check_command(); 86 | data_->add(command.release()); 87 | return *this; 88 | } 89 | 90 | Command& Command::name(std::string name) 91 | { 92 | check_command(); 93 | data_->name = std::move(name); 94 | return *this; 95 | } 96 | 97 | Command& Command::help(std::string text) 98 | { 99 | check_command(); 100 | data_->texts[TextId::HELP] = std::move(text); 101 | return *this; 102 | } 103 | 104 | Command& Command::about(std::string text) 105 | { 106 | check_command(); 107 | data_->texts[TextId::ABOUT] = std::move(text); 108 | return *this; 109 | } 110 | 111 | Command& Command::section(std::string name) 112 | { 113 | check_command(); 114 | data_->section = std::move(name); 115 | return *this; 116 | } 117 | 118 | Command& Command::current_section(std::string name) 119 | { 120 | check_command(); 121 | data_->current_section = std::move(name); 122 | return *this; 123 | } 124 | 125 | Command& Command::text(TextId textId, std::string text) 126 | { 127 | check_command(); 128 | data_->texts[textId] = std::move(text); 129 | return *this; 130 | } 131 | 132 | Command& Command::text(TextId textId, std::function callback) 133 | { 134 | check_command(); 135 | data_->texts[textId] = std::move(callback); 136 | return *this; 137 | } 138 | 139 | Command& Command::visibility(Visibility visibility) 140 | { 141 | check_command(); 142 | data_->visibility = visibility; 143 | return *this; 144 | } 145 | 146 | Command& Command::id(int id) 147 | { 148 | check_command(); 149 | data_->id = id; 150 | return *this; 151 | } 152 | 153 | Command& Command::allow_multiple_subcommands(bool multi_command) 154 | { 155 | check_command(); 156 | data_->multi_command = multi_command; 157 | return *this; 158 | } 159 | 160 | Command& Command::require_subcommand(bool require_subcommand) 161 | { 162 | check_command(); 163 | data_->require_subcommand = require_subcommand; 164 | return *this; 165 | } 166 | 167 | Command& Command::copy_from(Command& command) 168 | { 169 | check_command(); 170 | data_->copy_from(*command.data_); 171 | return *this; 172 | } 173 | 174 | std::unique_ptr Command::release() 175 | { 176 | return std::move(data_); 177 | } 178 | 179 | const CommandData& Command::internal_ref() const 180 | { 181 | check_command(); 182 | return *data_; 183 | } 184 | 185 | void Command::check_command() const 186 | { 187 | if (!data_) 188 | ARGOS_THROW("Command has been moved."); 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /src/Argos/CommandData.hpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2024 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2024-09-04. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | #include 10 | #include 11 | #include "ArgumentData.hpp" 12 | #include "OptionData.hpp" 13 | 14 | namespace argos 15 | { 16 | struct CommandData 17 | { 18 | CommandData(); 19 | 20 | CommandData(const CommandData&); 21 | 22 | CommandData(CommandData&&) noexcept; 23 | 24 | ~CommandData(); 25 | 26 | CommandData& operator=(const CommandData&); 27 | 28 | CommandData& operator=(CommandData&&) noexcept; 29 | 30 | void add(std::unique_ptr arg); 31 | 32 | void add(std::unique_ptr opt); 33 | 34 | void add(std::unique_ptr cmd); 35 | 36 | void copy_from(const CommandData& cmd); 37 | 38 | void build_option_index(bool case_insensitive); 39 | 40 | [[nodiscard]] const OptionData* 41 | find_option(std::string_view flag, 42 | bool allow_abbreviations, 43 | bool case_insensitive) const; 44 | 45 | [[nodiscard]] const CommandData* 46 | find_command(std::string_view cmd_name, 47 | bool case_insensitive) const; 48 | 49 | std::vector> arguments; 50 | std::vector> options; 51 | std::vector> commands; 52 | std::string current_section; 53 | 54 | std::string name; 55 | std::string full_name; 56 | std::map texts; 57 | Visibility visibility = Visibility::NORMAL; 58 | std::optional require_subcommand; 59 | bool multi_command = false; 60 | /** 61 | * The heading the command is listed under in the parent 62 | * command's help. 63 | */ 64 | std::string section; 65 | int id = 0; 66 | ArgumentId argument_id = {}; 67 | 68 | private: 69 | [[nodiscard]] const OptionData* 70 | find_option_impl(std::string_view flag, 71 | bool allow_abbreviations, 72 | bool case_insensitive) const; 73 | 74 | std::vector> option_index; 75 | }; 76 | 77 | struct ParserData; 78 | 79 | /** 80 | * Finish the initialization of this command and any subcommands, and 81 | * make them ready for parsing arguments. 82 | */ 83 | void finish_initialization(CommandData& cmd, 84 | const ParserData& data, 85 | ValueId start_id = {}, 86 | ArgumentId argument_id = {}); 87 | 88 | struct ParserSettings; 89 | 90 | bool has_flag(const CommandData& cmd, 91 | std::string_view flag, 92 | const ParserSettings& settings); 93 | } 94 | -------------------------------------------------------------------------------- /src/Argos/CommandView.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2024 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2024-09-21. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include "Argos/CommandView.hpp" 9 | 10 | #include "ArgosThrow.hpp" 11 | #include "CommandData.hpp" 12 | 13 | namespace argos 14 | { 15 | CommandView::CommandView(const CommandData* command) 16 | : m_command(command) 17 | {} 18 | 19 | std::string CommandView::help() const 20 | { 21 | auto it = m_command->texts.find(TextId::ABOUT); 22 | return it != m_command->texts.end() ? get_text(it->second) : ""; 23 | } 24 | 25 | const std::string& CommandView::section() const 26 | { 27 | return m_command->section; 28 | } 29 | 30 | const std::string& CommandView::alias() const 31 | { 32 | return m_command->name; 33 | } 34 | 35 | Visibility CommandView::visibility() const 36 | { 37 | return m_command->visibility; 38 | } 39 | 40 | int CommandView::id() const 41 | { 42 | return m_command->id; 43 | } 44 | 45 | ValueId CommandView::value_id() const 46 | { 47 | return {}; 48 | } 49 | 50 | ArgumentId CommandView::argument_id() const 51 | { 52 | return m_command->argument_id; 53 | } 54 | 55 | std::string CommandView::name() const 56 | { 57 | return m_command->name; 58 | } 59 | 60 | std::vector CommandView::arguments() const 61 | { 62 | std::vector result; 63 | for (const auto& arg : m_command->arguments) 64 | result.emplace_back(arg.get()); 65 | return result; 66 | } 67 | 68 | std::vector CommandView::options() const 69 | { 70 | std::vector result; 71 | for (const auto& opt : m_command->options) 72 | result.emplace_back(opt.get()); 73 | return result; 74 | } 75 | 76 | std::vector CommandView::subcommands() const 77 | { 78 | std::vector result; 79 | for (const auto& cmd : m_command->commands) 80 | result.emplace_back(cmd.get()); 81 | return result; 82 | } 83 | 84 | bool CommandView::require_subcommand() const 85 | { 86 | // Instances of CommandView are only created after require_subcommand 87 | // has been automatically set, the or-value should therefore never 88 | // be returned, and it doesn't matter that it might not be correct. 89 | return m_command->require_subcommand.value_or(false); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/Argos/ConsoleWidth.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-02-10. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include "ConsoleWidth.hpp" 9 | 10 | #if defined(__APPLE__) || defined(unix) || defined(__unix) || defined(__unix__) 11 | #include 12 | #include 13 | #elif defined(WIN32) 14 | #define NOMINMAX 15 | #include 16 | #endif 17 | 18 | namespace argos 19 | { 20 | #if defined(__APPLE__) || defined(unix) || defined(__unix) || defined(__unix__) 21 | 22 | unsigned get_console_width() 23 | { 24 | winsize ws = {}; 25 | if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) <= -1) 26 | return 0; 27 | return unsigned(ws.ws_col); 28 | } 29 | 30 | #elif defined(WIN32) 31 | 32 | unsigned get_console_width() 33 | { 34 | const HANDLE h_con = GetStdHandle(STD_OUTPUT_HANDLE); 35 | if (h_con == INVALID_HANDLE_VALUE) 36 | return 0; 37 | 38 | CONSOLE_SCREEN_BUFFER_INFO con_info; 39 | if (!GetConsoleScreenBufferInfo(h_con, &con_info)) 40 | return 0; 41 | 42 | return unsigned(con_info.srWindow.Right - con_info.srWindow.Left); 43 | } 44 | 45 | #else 46 | 47 | unsigned get_console_width() 48 | { 49 | return 0; 50 | } 51 | 52 | #endif 53 | 54 | unsigned get_console_width(unsigned min_width, unsigned int default_width) 55 | { 56 | const auto width = get_console_width(); 57 | if (width == 0) 58 | return default_width; 59 | return width < min_width ? min_width : width; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Argos/ConsoleWidth.hpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-02-10. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | 10 | namespace argos 11 | { 12 | unsigned get_console_width(); 13 | 14 | unsigned get_console_width(unsigned min_width, unsigned default_width = 80); 15 | } 16 | -------------------------------------------------------------------------------- /src/Argos/Enums.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2024 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2024-10-18. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include "Argos/Enums.hpp" 9 | 10 | namespace argos 11 | { 12 | #define ENUM_CASE(x) case TextId::x: return #x 13 | 14 | std::string to_string(TextId textId) 15 | { 16 | switch (textId) 17 | { 18 | ENUM_CASE(INITIAL_TEXT); 19 | ENUM_CASE(USAGE_TITLE); 20 | ENUM_CASE(USAGE); 21 | ENUM_CASE(ABOUT); 22 | ENUM_CASE(SUBCOMMANDS_TITLE); 23 | ENUM_CASE(ARGUMENTS_TITLE); 24 | ENUM_CASE(OPTIONS_TITLE); 25 | ENUM_CASE(FINAL_TEXT); 26 | ENUM_CASE(ERROR_USAGE); 27 | ENUM_CASE(HELP); 28 | default: 29 | return ""; 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Argos/HelpText.hpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-01-21. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | 10 | #include "ParserData.hpp" 11 | 12 | namespace argos 13 | { 14 | void write_help_text(const ParserData& data, const CommandData& cmd); 15 | 16 | void write_error_message(const ParserData& data, 17 | const CommandData& cmd, 18 | const std::string& msg); 19 | 20 | void write_error_message(const ParserData& data, 21 | const CommandData& cmd, 22 | const std::string& msg, 23 | ArgumentId argument_id); 24 | } 25 | -------------------------------------------------------------------------------- /src/Argos/Option.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-01-10. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include "Argos/Option.hpp" 9 | 10 | #include "ArgosThrow.hpp" 11 | #include "OptionData.hpp" 12 | 13 | namespace argos 14 | { 15 | Option::Option() 16 | : m_option(std::make_unique()) 17 | {} 18 | 19 | Option::Option(std::string flag) 20 | : m_option(std::make_unique()) 21 | { 22 | m_option->flags.emplace_back(std::move(flag)); 23 | } 24 | 25 | Option::Option(std::string flag1, std::string flag2) 26 | : m_option(std::make_unique()) 27 | { 28 | m_option->flags.emplace_back(std::move(flag1)); 29 | m_option->flags.emplace_back(std::move(flag2)); 30 | } 31 | 32 | Option::Option(std::initializer_list flags) 33 | : m_option(std::make_unique()) 34 | { 35 | m_option->flags = flags; 36 | } 37 | 38 | Option::Option(const Option& rhs) 39 | : m_option(rhs.m_option 40 | ? std::make_unique(*rhs.m_option) 41 | : std::unique_ptr()) 42 | {} 43 | 44 | Option::Option(Option&& rhs) noexcept 45 | : m_option(std::move(rhs.m_option)) 46 | {} 47 | 48 | Option::~Option() = default; 49 | 50 | Option& Option::operator=(const Option& rhs) 51 | { 52 | if (this != &rhs) 53 | { 54 | if (rhs.m_option) 55 | m_option = std::make_unique(*rhs.m_option); 56 | else 57 | m_option = {}; 58 | } 59 | return *this; 60 | } 61 | 62 | Option& Option::operator=(Option&& rhs) noexcept 63 | { 64 | m_option = std::move(rhs.m_option); 65 | return *this; 66 | } 67 | 68 | Option& Option::help(const std::string& text) 69 | { 70 | check_option(); 71 | m_option->help = text; 72 | return *this; 73 | } 74 | 75 | Option& Option::help(TextCallback callback) 76 | { 77 | check_option(); 78 | m_option->help = callback; 79 | return *this; 80 | } 81 | 82 | Option& Option::section(const std::string& name) 83 | { 84 | check_option(); 85 | m_option->section = name; 86 | return *this; 87 | } 88 | 89 | Option& Option::alias(const std::string& id) 90 | { 91 | check_option(); 92 | m_option->alias = id; 93 | return *this; 94 | } 95 | 96 | Option& Option::operation(OptionOperation operation) 97 | { 98 | check_option(); 99 | m_option->operation = operation; 100 | return *this; 101 | } 102 | 103 | Option& Option::visibility(Visibility visibility) 104 | { 105 | check_option(); 106 | m_option->visibility = visibility; 107 | return *this; 108 | } 109 | 110 | Option& Option::id(int id) 111 | { 112 | check_option(); 113 | m_option->id = id; 114 | return *this; 115 | } 116 | 117 | Option& Option::flag(const std::string& f) 118 | { 119 | check_option(); 120 | m_option->flags = {f}; 121 | return *this; 122 | } 123 | 124 | Option& Option::flags(std::vector f) 125 | { 126 | check_option(); 127 | m_option->flags = std::move(f); 128 | return *this; 129 | } 130 | 131 | Option& Option::argument(const std::string& name) 132 | { 133 | check_option(); 134 | m_option->argument = name; 135 | return *this; 136 | } 137 | 138 | Option& Option::initial_value(const std::string& value) 139 | { 140 | check_option(); 141 | m_option->initial_value = value; 142 | return *this; 143 | } 144 | 145 | Option& Option::constant(const char* value) 146 | { 147 | return this->constant(std::string(value)); 148 | } 149 | 150 | Option& Option::constant(const std::string& value) 151 | { 152 | check_option(); 153 | m_option->constant = value; 154 | return *this; 155 | } 156 | 157 | Option& Option::constant(bool value) 158 | { 159 | return this->constant(value ? 1LL : 0LL); 160 | } 161 | 162 | Option& Option::constant(int value) 163 | { 164 | return this->constant(static_cast(value)); 165 | } 166 | 167 | Option& Option::constant(long long value) 168 | { 169 | check_option(); 170 | m_option->constant = std::to_string(value); 171 | return *this; 172 | } 173 | 174 | Option& Option::callback(OptionCallback callback) 175 | { 176 | check_option(); 177 | m_option->callback = std::move(callback); 178 | return *this; 179 | } 180 | 181 | Option& Option::type(OptionType type) 182 | { 183 | check_option(); 184 | m_option->type = type; 185 | return *this; 186 | } 187 | 188 | Option& Option::optional(bool optional) 189 | { 190 | check_option(); 191 | m_option->optional = optional; 192 | return *this; 193 | } 194 | 195 | Option& Option::mandatory(bool mandatory) 196 | { 197 | return optional(!mandatory); 198 | } 199 | 200 | const OptionData& Option::data() const 201 | { 202 | check_option(); 203 | return *m_option; 204 | } 205 | 206 | std::unique_ptr Option::release() 207 | { 208 | check_option(); 209 | return std::move(m_option); 210 | } 211 | 212 | void Option::check_option() const 213 | { 214 | if (!m_option) 215 | ARGOS_THROW("Cannot use Option instance after" 216 | " release() has been called."); 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /src/Argos/OptionData.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2024 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2024-10-05. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include "OptionData.hpp" 9 | #include "ArgosThrow.hpp" 10 | 11 | namespace argos 12 | { 13 | namespace 14 | { 15 | bool check_flag_with_equal(const std::string& flag, 16 | const OptionData& od) 17 | { 18 | const auto eq_pos = flag.find('='); 19 | if (eq_pos == std::string::npos) 20 | return true; 21 | if (eq_pos != flag.size() - 1) 22 | ARGOS_THROW("Options can not have a '=' in the middle: " + flag); 23 | if (od.argument.empty()) 24 | ARGOS_THROW("Options ending with '=' must have a named argument: " + flag); 25 | return true; 26 | } 27 | 28 | bool check_standard_flag(const std::string& flag, 29 | const OptionData& od) 30 | { 31 | if (flag.find_first_of(" \t\n\r") != std::string::npos) 32 | return false; 33 | if (flag.size() < 2) 34 | return false; 35 | if (flag[0] != '-') 36 | return false; 37 | if (flag.size() == 2) 38 | return true; 39 | if (flag[1] != '-') 40 | return false; 41 | return check_flag_with_equal(flag, od); 42 | } 43 | 44 | bool check_flag(const std::string& flag, char prefix, 45 | const OptionData& od) 46 | { 47 | if (flag.size() < 2 || flag[0] != prefix) 48 | return false; 49 | if (flag.find_first_of(" \t\n\r") != std::string::npos) 50 | return false; 51 | if (flag.size() == 2) 52 | return true; 53 | return check_flag_with_equal(flag, od); 54 | } 55 | } 56 | 57 | void validate_and_update(OptionData& option, OptionStyle style) 58 | { 59 | if (option.flags.empty()) 60 | ARGOS_THROW("Option must have one or more flags."); 61 | 62 | for (auto& flag : option.flags) 63 | { 64 | bool ok = false; 65 | switch (style) 66 | { 67 | case OptionStyle::STANDARD: 68 | ok = check_standard_flag(flag, option); 69 | break; 70 | case OptionStyle::SLASH: 71 | ok = check_flag(flag, '/', option); 72 | break; 73 | case OptionStyle::DASH: 74 | ok = check_flag(flag, '-', option); 75 | break; 76 | default: 77 | break; 78 | } 79 | if (!ok) 80 | ARGOS_THROW("Invalid flag: '" + flag + "'."); 81 | } 82 | 83 | if (!option.argument.empty() && !option.constant.empty()) 84 | ARGOS_THROW("Option cannot have both argument and constant."); 85 | 86 | switch (option.operation) 87 | { 88 | case OptionOperation::NONE: 89 | if (!option.constant.empty()) 90 | ARGOS_THROW("NONE-options cannot have a constant."); 91 | if (!option.alias.empty()) 92 | ARGOS_THROW("NONE-options cannot have an alias."); 93 | break; 94 | case OptionOperation::ASSIGN: 95 | if (option.argument.empty() && option.constant.empty()) 96 | option.constant = "1"; 97 | break; 98 | case OptionOperation::APPEND: 99 | if (option.argument.empty() && option.constant.empty()) 100 | ARGOS_THROW("Options that appends must have either constant or argument."); 101 | break; 102 | case OptionOperation::CLEAR: 103 | if (!option.argument.empty() || !option.constant.empty()) 104 | option.constant = "1"; 105 | if (!option.optional) 106 | ARGOS_THROW("CLEAR-options must be optional."); 107 | break; 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/Argos/OptionData.hpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-01-09. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | #include 10 | #include 11 | #include "Argos/Callbacks.hpp" 12 | #include "Argos/Enums.hpp" 13 | #include "TextSource.hpp" 14 | 15 | namespace argos 16 | { 17 | struct OptionData 18 | { 19 | std::vector flags; 20 | TextSource help; 21 | std::string section; 22 | std::string alias; 23 | std::string argument; 24 | std::string constant; 25 | std::string initial_value; 26 | OptionCallback callback; 27 | OptionOperation operation = OptionOperation::ASSIGN; 28 | OptionType type = OptionType::NORMAL; 29 | Visibility visibility = Visibility::NORMAL; 30 | bool optional = true; 31 | int id = 0; 32 | ArgumentId argument_id = {}; 33 | ValueId value_id = {}; 34 | }; 35 | 36 | void validate_and_update(OptionData& option, OptionStyle style); 37 | } 38 | -------------------------------------------------------------------------------- /src/Argos/OptionIterator.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-02-18. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include "OptionIterator.hpp" 9 | #include "ArgosThrow.hpp" 10 | #include "StringUtilities.hpp" 11 | 12 | namespace argos 13 | { 14 | OptionIterator::OptionIterator() 15 | : m_args(m_all_args) 16 | { 17 | } 18 | 19 | OptionIterator::OptionIterator(std::vector args, char prefix) 20 | : m_all_args(args.begin(), args.end()), 21 | m_args(m_all_args), 22 | m_prefix(prefix) 23 | { 24 | } 25 | 26 | OptionIterator::OptionIterator(const OptionIterator& rhs) 27 | : m_all_args(rhs.m_all_args), 28 | m_args(m_all_args.begin() + rhs.m_all_args.size() - rhs.m_args.size(), m_all_args.end()), 29 | m_pos(rhs.m_pos), 30 | m_prefix(rhs.m_prefix) 31 | { 32 | } 33 | 34 | std::optional OptionIterator::next() 35 | { 36 | if (m_pos != 0) 37 | { 38 | m_pos = 0; 39 | pop_front(m_args); 40 | } 41 | 42 | if (m_args.empty()) 43 | return {}; 44 | 45 | if (m_args[0].size() <= 2 || m_args[0][0] != m_prefix) 46 | { 47 | m_pos = std::string_view::npos; 48 | return std::string(m_args[0]); 49 | } 50 | 51 | const auto eq = m_args[0].find('='); 52 | if (eq == std::string_view::npos) 53 | { 54 | m_pos = std::string_view::npos; 55 | return std::string(m_args[0]); 56 | } 57 | 58 | m_pos = eq + 1; 59 | return std::string(m_args[0].substr(0, m_pos)); 60 | } 61 | 62 | std::optional OptionIterator::next_value() 63 | { 64 | if (m_args.empty()) 65 | return {}; 66 | 67 | if (m_pos != std::string_view::npos) 68 | { 69 | const auto result = m_args[0].substr(m_pos); 70 | m_pos = std::string_view::npos; 71 | return std::string(result); 72 | } 73 | 74 | pop_front(m_args); 75 | if (m_args.empty()) 76 | { 77 | m_pos = 0; 78 | return {}; 79 | } 80 | 81 | m_pos = m_args[0].size(); 82 | return std::string(m_args[0]); 83 | } 84 | 85 | std::string_view OptionIterator::current() const 86 | { 87 | if (m_args.empty()) 88 | ARGOS_THROW("There is no current argument."); 89 | return m_args[0]; 90 | } 91 | 92 | std::span OptionIterator::remaining_arguments() const 93 | { 94 | return m_pos == 0 ? m_args : m_args.subspan(1); 95 | } 96 | 97 | void OptionIterator::insert(const std::vector& args) 98 | { 99 | auto args_index = m_all_args.size() - m_args.size(); 100 | auto insert_index = args_index; 101 | if (m_pos != 0) 102 | insert_index++; 103 | m_all_args.insert(m_all_args.begin() + insert_index, 104 | args.begin(), args.end()); 105 | m_args = std::span(m_all_args.begin() + args_index, m_all_args.end()); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/Argos/OptionIterator.hpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-02-18. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace argos 16 | { 17 | class OptionIterator 18 | { 19 | public: 20 | OptionIterator(); 21 | 22 | explicit OptionIterator(std::vector args, 23 | char prefix); 24 | 25 | OptionIterator(const OptionIterator& rhs); 26 | 27 | std::optional next(); 28 | 29 | std::optional next_value(); 30 | 31 | [[nodiscard]] std::string_view current() const; 32 | 33 | [[nodiscard]] std::span remaining_arguments() const; 34 | 35 | void insert(const std::vector& args); 36 | private: 37 | std::vector m_all_args; 38 | std::span m_args; 39 | size_t m_pos = 0; 40 | char m_prefix = '-'; 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /src/Argos/OptionIteratorWrapper.hpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2024 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2024-09-19. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | #include 10 | #include "OptionIterator.hpp" 11 | #include "StandardOptionIterator.hpp" 12 | 13 | namespace argos 14 | { 15 | struct OptionIteratorWrapper 16 | { 17 | std::optional next() 18 | { 19 | if (std::holds_alternative(iterator)) 20 | return std::get(iterator).next(); 21 | else 22 | return std::get(iterator).next(); 23 | } 24 | 25 | std::optional next_value() 26 | { 27 | if (std::holds_alternative(iterator)) 28 | return std::get(iterator).next_value(); 29 | else 30 | return std::get(iterator).next_value(); 31 | } 32 | 33 | [[nodiscard]] std::string_view current() const 34 | { 35 | if (std::holds_alternative(iterator)) 36 | return std::get(iterator).current(); 37 | else 38 | return std::get(iterator).current(); 39 | } 40 | 41 | [[nodiscard]] std::span remaining_arguments() 42 | { 43 | if (std::holds_alternative(iterator)) 44 | return std::get(iterator).remaining_arguments(); 45 | else 46 | return std::get(iterator).remaining_arguments(); 47 | } 48 | 49 | void insert(const std::vector& args) 50 | { 51 | if (std::holds_alternative(iterator)) 52 | std::get(iterator).insert(args); 53 | else 54 | std::get(iterator).insert(args); 55 | } 56 | 57 | std::variant iterator; 58 | }; 59 | } 60 | -------------------------------------------------------------------------------- /src/Argos/OptionView.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-01-28. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include "Argos/OptionView.hpp" 9 | 10 | #include "ArgosThrow.hpp" 11 | #include "OptionData.hpp" 12 | 13 | namespace argos 14 | { 15 | OptionView::OptionView(const OptionData* data) 16 | : m_option(data) 17 | { 18 | if (!data) 19 | ARGOS_THROW("data can not be null"); 20 | } 21 | 22 | std::string OptionView::help() const 23 | { 24 | return get_text(m_option->help); 25 | } 26 | 27 | const std::string& OptionView::section() const 28 | { 29 | return m_option->section; 30 | } 31 | 32 | const std::string& OptionView::alias() const 33 | { 34 | return m_option->alias; 35 | } 36 | 37 | OptionOperation OptionView::operation() const 38 | { 39 | return m_option->operation; 40 | } 41 | 42 | Visibility OptionView::visibility() const 43 | { 44 | return m_option->visibility; 45 | } 46 | 47 | int OptionView::id() const 48 | { 49 | return m_option->id; 50 | } 51 | 52 | ValueId OptionView::value_id() const 53 | { 54 | return m_option->value_id; 55 | } 56 | 57 | const std::vector& OptionView::flags() const 58 | { 59 | return m_option->flags; 60 | } 61 | 62 | const std::string& OptionView::argument() const 63 | { 64 | return m_option->argument; 65 | } 66 | 67 | const std::string& OptionView::initial_value() const 68 | { 69 | return m_option->initial_value; 70 | } 71 | 72 | const std::string& OptionView::constant() const 73 | { 74 | return m_option->constant; 75 | } 76 | 77 | OptionType OptionView::type() const 78 | { 79 | return m_option->type; 80 | } 81 | 82 | bool OptionView::optional() const 83 | { 84 | return m_option->optional; 85 | } 86 | 87 | ArgumentId OptionView::argument_id() const 88 | { 89 | return m_option->argument_id; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/Argos/ParseValue.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-02-13. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | 9 | #include "ParseValue.hpp" 10 | 11 | #include 12 | #include 13 | #include 14 | #include "StringUtilities.hpp" 15 | 16 | namespace argos 17 | { 18 | namespace 19 | { 20 | template 21 | T str_to_int(const char* str, char** endp, int base); 22 | 23 | template <> 24 | long str_to_int(const char* str, char** endp, int base) 25 | { 26 | return strtol(str, endp, base); 27 | } 28 | 29 | template <> 30 | long long str_to_int(const char* str, char** endp, int base) 31 | { 32 | return strtoll(str, endp, base); 33 | } 34 | 35 | template <> 36 | unsigned long 37 | str_to_int(const char* str, char** endp, int base) 38 | { 39 | return strtoul(str, endp, base); 40 | } 41 | 42 | template <> 43 | unsigned long long 44 | str_to_int(const char* str, char** endp, int base) 45 | { 46 | return strtoull(str, endp, base); 47 | } 48 | 49 | template 50 | std::optional parse_integer_impl(const std::string& str, int base) 51 | { 52 | if (str.empty()) 53 | return {}; 54 | char* endp = nullptr; 55 | errno = 0; 56 | auto value = str_to_int(str.c_str(), &endp, base); 57 | if (endp == str.c_str() + str.size() && errno == 0) 58 | return value; 59 | return {}; 60 | } 61 | } 62 | 63 | template <> 64 | std::optional parse_integer(const std::string& str, int base) 65 | { 66 | const auto n = parse_integer_impl(str, base); 67 | if (!n) 68 | return {}; 69 | 70 | if constexpr (sizeof(int) != sizeof(long)) 71 | { 72 | if (*n < INT_MIN || INT_MAX < *n) 73 | return {}; 74 | } 75 | return static_cast(*n); 76 | } 77 | 78 | template <> 79 | std::optional 80 | parse_integer(const std::string& str, int base) 81 | { 82 | auto n = parse_integer_impl(str, base); 83 | if (!n) 84 | return {}; 85 | 86 | if constexpr (sizeof(unsigned) != sizeof(unsigned long)) 87 | { 88 | if (UINT_MAX < *n) 89 | return {}; 90 | } 91 | return static_cast(*n); 92 | } 93 | 94 | template <> 95 | std::optional parse_integer(const std::string& str, int base) 96 | { 97 | return parse_integer_impl(str, base); 98 | } 99 | 100 | template <> 101 | std::optional 102 | parse_integer(const std::string& str, int base) 103 | { 104 | return parse_integer_impl(str, base); 105 | } 106 | 107 | template <> 108 | std::optional 109 | parse_integer(const std::string& str, int base) 110 | { 111 | return parse_integer_impl(str, base); 112 | } 113 | 114 | template <> 115 | std::optional 116 | parse_integer(const std::string& str, int base) 117 | { 118 | return parse_integer_impl(str, base); 119 | } 120 | 121 | namespace 122 | { 123 | template 124 | T str_to_float(const char* str, char** endp); 125 | 126 | template <> 127 | float str_to_float(const char* str, char** endp) 128 | { 129 | return strtof(str, endp); 130 | } 131 | 132 | template <> 133 | double str_to_float(const char* str, char** endp) 134 | { 135 | return strtod(str, endp); 136 | } 137 | 138 | template 139 | std::optional parse_floating_point_impl(const std::string& str) 140 | { 141 | if (str.empty()) 142 | return {}; 143 | char* endp = nullptr; 144 | errno = 0; 145 | auto value = str_to_float(str.c_str(), &endp); 146 | if (endp == str.c_str() + str.size() && errno == 0) 147 | return value; 148 | return {}; 149 | } 150 | } 151 | 152 | template <> 153 | std::optional parse_floating_point(const std::string& str) 154 | { 155 | return parse_floating_point_impl(str); 156 | } 157 | 158 | template <> 159 | std::optional parse_floating_point(const std::string& str) 160 | { 161 | return parse_floating_point_impl(str); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/Argos/ParseValue.hpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-02-03. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace argos 16 | { 17 | template 18 | std::optional parse_integer(const std::string& str, int base) = delete; 19 | 20 | template <> 21 | std::optional parse_integer(const std::string& str, int base); 22 | 23 | template <> 24 | std::optional 25 | parse_integer(const std::string& str, int base); 26 | 27 | template <> 28 | std::optional parse_integer(const std::string& str, int base); 29 | 30 | template <> 31 | std::optional 32 | parse_integer(const std::string& str, int base); 33 | 34 | template <> 35 | std::optional 36 | parse_integer(const std::string& str, int base); 37 | 38 | template <> 39 | std::optional 40 | parse_integer(const std::string& str, int base); 41 | 42 | template 43 | std::optional parse_floating_point(const std::string& str); 44 | 45 | template <> 46 | std::optional parse_floating_point(const std::string& str); 47 | 48 | template <> 49 | std::optional parse_floating_point(const std::string& str); 50 | } 51 | -------------------------------------------------------------------------------- /src/Argos/ParsedArgumentsBuilder.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-01-29. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include "Argos/ParsedArgumentsBuilder.hpp" 9 | 10 | #include 11 | #include "ParsedArgumentsImpl.hpp" 12 | 13 | namespace argos 14 | { 15 | ParsedArgumentsBuilder::ParsedArgumentsBuilder( 16 | std::shared_ptr impl) 17 | : m_impl(std::move(impl)) 18 | {} 19 | 20 | ParsedArgumentsBuilder& 21 | ParsedArgumentsBuilder::append(const std::string& name, 22 | const std::string& value) 23 | { 24 | m_impl->append_value(m_impl->get_value_id(name), value, {}); 25 | return *this; 26 | } 27 | 28 | ParsedArgumentsBuilder& 29 | ParsedArgumentsBuilder::append(const IArgumentView& arg, 30 | const std::string& value) 31 | { 32 | m_impl->append_value(arg.value_id(), value, arg.argument_id()); 33 | return *this; 34 | } 35 | 36 | ParsedArgumentsBuilder& 37 | ParsedArgumentsBuilder::assign(const std::string& name, 38 | const std::string& value) 39 | { 40 | m_impl->assign_value(m_impl->get_value_id(name), value, {}); 41 | return *this; 42 | } 43 | 44 | ParsedArgumentsBuilder& 45 | ParsedArgumentsBuilder::assign(const IArgumentView& arg, 46 | const std::string& value) 47 | { 48 | m_impl->assign_value(arg.value_id(), value, arg.argument_id()); 49 | return *this; 50 | } 51 | 52 | ParsedArgumentsBuilder& 53 | ParsedArgumentsBuilder::clear(const std::string& name) 54 | { 55 | m_impl->clear_value(m_impl->get_value_id(name)); 56 | return *this; 57 | } 58 | 59 | ParsedArgumentsBuilder& 60 | ParsedArgumentsBuilder::clear(const IArgumentView& arg) 61 | { 62 | m_impl->clear_value(arg.value_id()); 63 | return *this; 64 | } 65 | 66 | ArgumentValue ParsedArgumentsBuilder::value(const std::string& name) const 67 | { 68 | auto id = m_impl->get_value_id(name); 69 | if (auto value = m_impl->get_value(id)) 70 | return {value->first, m_impl, id, value->second}; 71 | else 72 | return {{}, m_impl, id, {}}; 73 | } 74 | 75 | ArgumentValue 76 | ParsedArgumentsBuilder::value(const IArgumentView& arg) const 77 | { 78 | if (auto value = m_impl->get_value(arg.value_id())) 79 | return {value->first, m_impl, arg.value_id(), arg.argument_id()}; 80 | else 81 | return {{}, m_impl, arg.value_id(), arg.argument_id()}; 82 | } 83 | 84 | ArgumentValues 85 | ParsedArgumentsBuilder::values(const std::string& name) const 86 | { 87 | auto id = m_impl->get_value_id(name); 88 | auto values = m_impl->get_values(id); 89 | return {std::move(values), m_impl, id}; 90 | } 91 | 92 | ArgumentValues 93 | ParsedArgumentsBuilder::values(const IArgumentView& arg) const 94 | { 95 | auto values = m_impl->get_values(arg.value_id()); 96 | return {std::move(values), m_impl, arg.value_id()}; 97 | } 98 | 99 | bool ParsedArgumentsBuilder::has(const std::string& name) const 100 | { 101 | return m_impl->has(m_impl->get_value_id(name)); 102 | } 103 | 104 | bool ParsedArgumentsBuilder::has(const IArgumentView& arg) const 105 | { 106 | return m_impl->has(arg.value_id()); 107 | } 108 | 109 | void ParsedArgumentsBuilder::error(const std::string& msg) const 110 | { 111 | m_impl->error(msg); 112 | } 113 | 114 | void ParsedArgumentsBuilder::error(const std::string& msg, 115 | const IArgumentView& arg) const 116 | { 117 | m_impl->error(msg, arg.argument_id()); 118 | } 119 | 120 | std::ostream& ParsedArgumentsBuilder::stream() const 121 | { 122 | const auto custom_stream = m_impl->parser_data()->help_settings.output_stream; 123 | return custom_stream ? *custom_stream : std::cout; 124 | } 125 | 126 | const std::string& ParsedArgumentsBuilder::program_name() const 127 | { 128 | return m_impl->parser_data()->command.name; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/Argos/ParsedArgumentsImpl.hpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-01-07. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | #include 10 | #include "Argos/IArgumentView.hpp" 11 | #include "ParserData.hpp" 12 | 13 | namespace argos 14 | { 15 | class ArgumentIteratorImpl; 16 | 17 | class ParsedArgumentsImpl 18 | { 19 | public: 20 | explicit ParsedArgumentsImpl(const CommandData* command, 21 | std::shared_ptr data); 22 | 23 | [[nodiscard]] bool has(ValueId value_id) const; 24 | 25 | [[nodiscard]] const std::vector& unprocessed_arguments() const; 26 | 27 | void add_unprocessed_argument(const std::string& arg); 28 | 29 | std::string_view assign_value(ValueId value_id, 30 | const std::string& value, 31 | ArgumentId argument_id); 32 | 33 | std::string_view append_value(ValueId value_id, 34 | const std::string& value, 35 | ArgumentId argument_id); 36 | 37 | void clear_value(ValueId value_id); 38 | 39 | [[nodiscard]] ValueId get_value_id(std::string_view value_name) const; 40 | 41 | [[nodiscard]] std::optional> 42 | get_value(ValueId value_id) const; 43 | 44 | [[nodiscard]] std::vector> 45 | get_values(ValueId value_id) const; 46 | 47 | const std::shared_ptr& 48 | add_subcommand(const CommandData* command); 49 | 50 | const std::vector>& 51 | subcommands() const; 52 | 53 | [[nodiscard]] std::vector> 54 | get_argument_views(ValueId value_id) const; 55 | 56 | [[nodiscard]] std::unique_ptr 57 | get_argument_view(ArgumentId argument_id) const; 58 | 59 | [[nodiscard]] const std::shared_ptr& parser_data() const; 60 | 61 | [[nodiscard]] const CommandData* command() const; 62 | 63 | [[nodiscard]] ParserResultCode result_code() const; 64 | 65 | void set_result_code(ParserResultCode result_code); 66 | 67 | [[nodiscard]] const OptionData* stop_option() const; 68 | 69 | void set_breaking_option(const OptionData* option); 70 | 71 | [[noreturn]] 72 | void error(const std::string& message) const; 73 | 74 | [[noreturn]] 75 | void error(const std::string& message, ArgumentId argument_id); 76 | 77 | private: 78 | /// The values received from ArgumentIteratorImpl. 79 | std::multimap> m_values; 80 | /// Index from flag, argument name or alias to ValueId and ArgumentId 81 | std::vector> m_ids; 82 | std::vector m_unprocessed_arguments; 83 | const CommandData* m_command; 84 | std::vector> m_commands; 85 | std::shared_ptr m_data; 86 | ParserResultCode m_result_code = ParserResultCode::NONE; 87 | const OptionData* m_stop_option = nullptr; 88 | }; 89 | } 90 | -------------------------------------------------------------------------------- /src/Argos/ParserData.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2024 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2024-09-11. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include "ParserData.hpp" 9 | #include "Argos/Option.hpp" 10 | 11 | #include 12 | 13 | namespace argos 14 | { 15 | namespace 16 | { 17 | void add_version_option(ParserData& data) 18 | { 19 | if (data.version.empty()) 20 | return; 21 | auto& cmd = data.command; 22 | std::string flag; 23 | switch (data.parser_settings.option_style) 24 | { 25 | case OptionStyle::STANDARD: 26 | if (!has_flag(cmd, "--version", data.parser_settings)) 27 | flag = "--version"; 28 | break; 29 | case OptionStyle::SLASH: 30 | if (!has_flag(cmd, "/VERSION", data.parser_settings)) 31 | flag = "/VERSION"; 32 | break; 33 | case OptionStyle::DASH: 34 | if (!has_flag(cmd, "-version", data.parser_settings)) 35 | flag = "-version"; 36 | break; 37 | } 38 | 39 | if (flag.empty()) 40 | return; 41 | 42 | auto stream = data.help_settings.output_stream 43 | ? data.help_settings.output_stream 44 | : &std::cout; 45 | auto opt = Option().flag(flag).type(OptionType::STOP) 46 | .help("Display the program version.") 47 | .constant("1") 48 | .callback([v = data.version, stream] 49 | (auto& a) 50 | { 51 | *stream << a.builder.program_name() << " " << v << "\n"; 52 | return true; 53 | }) 54 | .release(); 55 | opt->section = cmd.current_section; 56 | cmd.options.push_back(std::move(opt)); 57 | } 58 | } 59 | 60 | void finish_initialization(ParserData& data) 61 | { 62 | add_version_option(data); 63 | finish_initialization(data.command, data); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/Argos/ParserData.hpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-01-13. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | #include 10 | #include 11 | #include 12 | #include "Argos/Enums.hpp" 13 | #include "ArgumentData.hpp" 14 | #include "OptionData.hpp" 15 | #include "CommandData.hpp" 16 | #include "TextFormatter.hpp" 17 | 18 | #ifndef ARGOS_EX_USAGE 19 | #ifdef EX_USAGE 20 | #define ARGOS_EX_USAGE EX_USAGE 21 | #else 22 | #define ARGOS_EX_USAGE 64 23 | #endif 24 | #endif 25 | 26 | namespace argos 27 | { 28 | struct ParserSettings 29 | { 30 | ArgumentCallback argument_callback; 31 | OptionCallback option_callback; 32 | OptionStyle option_style = OptionStyle::STANDARD; 33 | bool auto_exit = true; 34 | bool allow_abbreviated_options = false; 35 | bool ignore_undefined_options = false; 36 | bool ignore_undefined_arguments = false; 37 | bool case_insensitive = false; 38 | bool generate_help_option = true; 39 | int normal_exit_code = 0; 40 | int error_exit_code = ARGOS_EX_USAGE; 41 | }; 42 | 43 | struct HelpSettings 44 | { 45 | std::ostream* output_stream = nullptr; 46 | unsigned line_width = 0; 47 | std::vector word_split_rules; 48 | }; 49 | 50 | struct ParserData 51 | { 52 | CommandData command; 53 | ParserSettings parser_settings; 54 | HelpSettings help_settings; 55 | std::string version; 56 | }; 57 | 58 | void finish_initialization(ParserData& data); 59 | } 60 | -------------------------------------------------------------------------------- /src/Argos/StandardOptionIterator.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-01-09. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include "StandardOptionIterator.hpp" 9 | 10 | #include "ArgosThrow.hpp" 11 | #include "StringUtilities.hpp" 12 | 13 | namespace argos 14 | { 15 | StandardOptionIterator::StandardOptionIterator() 16 | { 17 | } 18 | 19 | StandardOptionIterator::StandardOptionIterator(std::vector args) 20 | : m_all_args(args.begin(), args.end()), 21 | m_args(m_all_args) 22 | { 23 | } 24 | 25 | StandardOptionIterator::StandardOptionIterator(const StandardOptionIterator& rhs) 26 | : m_all_args(rhs.m_all_args), 27 | m_args(m_all_args.begin() + rhs.m_all_args.size() - rhs.m_args.size(), m_all_args.end()), 28 | m_pos(rhs.m_pos) 29 | { 30 | } 31 | 32 | std::optional StandardOptionIterator::next() 33 | { 34 | if (m_pos != 0) 35 | { 36 | // m_pos is always greater than one if we get here. 37 | if (m_pos < m_args[0].size() && m_args[0][1] != '-') 38 | { 39 | const auto c = m_args[0][m_pos++]; 40 | if (m_pos == m_args[0].size()) 41 | m_pos = std::string::npos; 42 | return std::string{'-', c}; 43 | } 44 | pop_front(m_args); 45 | m_pos = 0; 46 | } 47 | 48 | if (m_args.empty()) 49 | return {}; 50 | 51 | if (m_args[0].size() <= 2 || m_args[0][0] != '-') 52 | { 53 | m_pos = std::string::npos; 54 | return m_args[0]; 55 | } 56 | 57 | if (m_args[0][1] != '-') 58 | { 59 | m_pos = 2; 60 | return m_args[0].substr(0, 2); 61 | } 62 | 63 | m_pos = m_args[0].find('='); 64 | 65 | if (m_pos == std::string::npos) 66 | return m_args[0]; 67 | 68 | m_pos++; 69 | return m_args[0].substr(0, m_pos); 70 | } 71 | 72 | std::optional StandardOptionIterator::next_value() 73 | { 74 | if (m_args.empty()) 75 | return {}; 76 | 77 | if (m_pos != std::string::npos) 78 | { 79 | const auto pos = m_pos; 80 | m_pos = std::string::npos; 81 | return m_args[0].substr(pos); 82 | } 83 | 84 | pop_front(m_args); 85 | if (m_args.empty()) 86 | { 87 | m_pos = 0; 88 | return {}; 89 | } 90 | 91 | return m_args[0]; 92 | } 93 | 94 | std::string_view StandardOptionIterator::current() const 95 | { 96 | if (m_args.empty()) 97 | ARGOS_THROW("There is no current argument."); 98 | return m_args[0]; 99 | } 100 | 101 | std::span StandardOptionIterator::remaining_arguments() 102 | { 103 | split_concatenated_flags(); 104 | return m_pos == 0 ? m_args : m_args.subspan(1); 105 | } 106 | 107 | void StandardOptionIterator::insert(std::vector args) 108 | { 109 | split_concatenated_flags(); 110 | auto args_index = m_all_args.size() - m_args.size(); 111 | auto insert_index = args_index; 112 | if (m_pos != 0) 113 | insert_index++; 114 | m_all_args.insert(m_all_args.begin() + insert_index, 115 | args.begin(), args.end()); 116 | m_args = std::span(m_all_args.begin() + args_index, m_all_args.end()); 117 | } 118 | 119 | void StandardOptionIterator::split_concatenated_flags() 120 | { 121 | if (m_pos == 0 || m_pos >= m_args[0].size() || m_args[0][1] == '-') 122 | return; 123 | 124 | auto index = m_all_args.size() - m_args.size(); 125 | m_all_args.insert(m_all_args.begin() + index + 1, 126 | "-" + m_args[0].substr(m_pos)); 127 | m_all_args[index].resize(m_pos); 128 | m_args = std::span(m_all_args.begin() + index, m_all_args.end()); 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/Argos/StandardOptionIterator.hpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-01-09. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace argos 16 | { 17 | class StandardOptionIterator 18 | { 19 | public: 20 | StandardOptionIterator(); 21 | 22 | explicit StandardOptionIterator(std::vector args); 23 | 24 | StandardOptionIterator(const StandardOptionIterator& rhs); 25 | 26 | std::optional next(); 27 | 28 | std::optional next_value(); 29 | 30 | [[nodiscard]] std::string_view current() const; 31 | 32 | [[nodiscard]] std::span remaining_arguments(); 33 | 34 | void insert(std::vector args); 35 | private: 36 | void split_concatenated_flags(); 37 | 38 | std::vector m_all_args; 39 | std::span m_args; 40 | size_t m_pos = 0; 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /src/Argos/StringUtilities.hpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-01-14. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace argos 16 | { 17 | bool are_equal_ci(std::string_view str1, std::string_view str2); 18 | 19 | bool are_equal(std::string_view str1, std::string_view str2, 20 | bool case_insensitive); 21 | 22 | bool starts_with(std::string_view str, std::string_view prefix); 23 | 24 | bool starts_with_ci(std::string_view str, std::string_view prefix); 25 | 26 | bool starts_with(std::string_view str, std::string_view prefix, 27 | bool case_insensitive); 28 | 29 | bool is_less_ci(std::string_view str1, std::string_view str2); 30 | 31 | bool is_less(std::string_view str1, std::string_view str2, 32 | bool case_insensitive); 33 | 34 | std::vector 35 | split_string(std::string_view s, char delimiter, size_t max_split); 36 | 37 | std::string_view get_base_name(std::string_view str); 38 | 39 | size_t count_code_points(std::string_view str); 40 | 41 | size_t find_nth_code_point(std::string_view str, size_t n); 42 | 43 | char to_lower(char c); 44 | 45 | void to_lower(std::string& word); 46 | 47 | std::string to_lower(std::string_view word); 48 | 49 | bool is_lower(std::string_view word); 50 | 51 | template 52 | inline void pop_front(std::span& span) 53 | { 54 | span = span.subspan(1); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Argos/TextFormatter.hpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-02-05. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "TextWriter.hpp" 14 | #include "WordSplitter.hpp" 15 | 16 | namespace argos 17 | { 18 | class TextFormatter 19 | { 20 | public: 21 | TextFormatter(); 22 | 23 | explicit TextFormatter(std::ostream* stream); 24 | 25 | TextFormatter(std::ostream* stream, unsigned line_width); 26 | 27 | WordSplitter& word_splitter(); 28 | 29 | [[nodiscard]] std::ostream* stream() const; 30 | 31 | void set_stream(std::ostream* stream); 32 | 33 | [[nodiscard]] unsigned line_width() const; 34 | 35 | void set_line_width(unsigned line_width); 36 | 37 | [[nodiscard]] unsigned current_line_width() const; 38 | 39 | [[nodiscard]] bool is_current_line_empty() const; 40 | 41 | static constexpr unsigned CURRENT_COLUMN = UINT_MAX; 42 | 43 | void push_indentation(unsigned indent); 44 | 45 | void pop_indentation(); 46 | 47 | void write_words(std::string_view text); 48 | 49 | void write_lines(std::string_view text); 50 | 51 | void newline(); 52 | 53 | void flush(); 54 | private: 55 | void append_word(std::string_view word); 56 | 57 | void begin_alignment(); 58 | 59 | void update_alignment(const std::string_view& token); 60 | 61 | void end_alignment(); 62 | 63 | TextWriter m_writer; 64 | std::vector m_indents; 65 | WordSplitter m_word_splitter; 66 | enum class State 67 | { 68 | NO_ALIGNMENT, 69 | ALIGNMENT, 70 | UNALIGNED_MARKER, 71 | ALIGNED_MARKER 72 | }; 73 | State m_state = State::NO_ALIGNMENT; 74 | }; 75 | } 76 | -------------------------------------------------------------------------------- /src/Argos/TextSource.hpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2023 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2023-10-10. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | #include 10 | #include "Argos/Callbacks.hpp" 11 | 12 | namespace argos 13 | { 14 | using TextSource = std::variant; 15 | 16 | inline std::string get_text(const TextSource& source) 17 | { 18 | if (auto str = std::get_if(&source)) 19 | return *str; 20 | return std::get(source)(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Argos/TextWriter.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-02-27. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include "TextWriter.hpp" 9 | #include "StringUtilities.hpp" 10 | 11 | #include 12 | #include 13 | 14 | namespace argos 15 | { 16 | TextWriter::TextWriter(unsigned line_width) 17 | : m_stream(&std::cout), 18 | m_line_width(line_width) 19 | {} 20 | 21 | std::ostream* TextWriter::stream() const 22 | { 23 | return m_stream; 24 | } 25 | 26 | void TextWriter::set_stream(std::ostream* stream) 27 | { 28 | m_stream = stream; 29 | } 30 | 31 | unsigned TextWriter::indentation() const 32 | { 33 | return m_indent; 34 | } 35 | 36 | bool TextWriter::set_indentation(unsigned indent) 37 | { 38 | if (indent >= m_line_width) 39 | return false; 40 | m_indent = indent; 41 | return true; 42 | } 43 | 44 | bool TextWriter::write(std::string_view str, bool force) 45 | { 46 | const auto width = current_width(); 47 | const auto remaining = std::max(width, m_line_width) - width; 48 | const auto str_width = static_cast(count_code_points(str)); 49 | if (!force && str_width > remaining) 50 | return false; 51 | m_line.append(width - m_current_line_width, ' '); 52 | m_spaces = 0; 53 | m_line.append(str); 54 | m_current_line_width += width - m_current_line_width + str_width; 55 | return true; 56 | } 57 | 58 | void TextWriter::newline() 59 | { 60 | m_line.push_back('\n'); 61 | m_current_line_width = 0; 62 | flush(); 63 | } 64 | 65 | void TextWriter::flush() 66 | { 67 | m_stream->write(m_line.data(), std::streamsize(m_line.size())); 68 | m_line.clear(); 69 | } 70 | 71 | void TextWriter::tab() 72 | { 73 | m_spaces += m_tab_size - (current_width() % m_tab_size); 74 | } 75 | 76 | unsigned TextWriter::spaces() const 77 | { 78 | return m_spaces; 79 | } 80 | 81 | void TextWriter::set_spaces(unsigned n) 82 | { 83 | m_spaces = n; 84 | } 85 | 86 | unsigned TextWriter::current_width() const 87 | { 88 | return std::max(m_current_line_width, m_indent) + m_spaces; 89 | } 90 | 91 | unsigned TextWriter::remaining_width() const 92 | { 93 | return m_line_width - std::min(current_width(), m_line_width); 94 | } 95 | 96 | bool TextWriter::is_current_line_empty() const 97 | { 98 | return m_line.empty(); 99 | } 100 | 101 | unsigned TextWriter::line_width() const 102 | { 103 | return m_line_width; 104 | } 105 | 106 | void TextWriter::set_line_width(unsigned width) 107 | { 108 | m_line_width = width; 109 | } 110 | 111 | std::string_view TextWriter::currentLine() const 112 | { 113 | return m_line; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/Argos/TextWriter.hpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-02-27. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | #include 10 | #include 11 | #include 12 | 13 | namespace argos 14 | { 15 | class TextWriter 16 | { 17 | public: 18 | explicit TextWriter(unsigned line_width = 80); 19 | 20 | [[nodiscard]] std::ostream* stream() const; 21 | 22 | void set_stream(std::ostream* stream); 23 | 24 | [[nodiscard]] unsigned indentation() const; 25 | 26 | bool set_indentation(unsigned indent); 27 | 28 | bool write(std::string_view str, bool force = false); 29 | 30 | void newline(); 31 | 32 | void flush(); 33 | 34 | void tab(); 35 | 36 | [[nodiscard]] unsigned spaces() const; 37 | 38 | void set_spaces(unsigned n); 39 | 40 | [[nodiscard]] unsigned current_width() const; 41 | 42 | [[nodiscard]] unsigned remaining_width() const; 43 | 44 | [[nodiscard]] bool is_current_line_empty() const; 45 | 46 | [[nodiscard]] unsigned line_width() const; 47 | 48 | void set_line_width(unsigned width); 49 | 50 | [[nodiscard]] std::string_view currentLine() const; 51 | private: 52 | std::ostream* m_stream; 53 | std::string m_line; 54 | unsigned m_line_width; 55 | unsigned m_current_line_width = 0; 56 | unsigned m_tab_size = 4; 57 | unsigned m_indent = 0; 58 | unsigned m_spaces = 0; 59 | }; 60 | } 61 | -------------------------------------------------------------------------------- /src/Argos/WordSplitter.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-02-06. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include "WordSplitter.hpp" 9 | 10 | #include 11 | #include 12 | #include "ArgosThrow.hpp" 13 | #include "StringUtilities.hpp" 14 | 15 | namespace argos 16 | { 17 | namespace 18 | { 19 | bool is_utf8_continuation(char c) 20 | { 21 | return (uint8_t(c) & 0xC0u) == 0x80; 22 | } 23 | 24 | std::string_view remove_punctuation(std::string_view word) 25 | { 26 | auto last = word.find_last_not_of(".,;:!?()[]{}<>\"'`"); 27 | return word.substr(0, last + 1); 28 | } 29 | } 30 | 31 | void WordSplitter::add_word(std::string word_rule) 32 | { 33 | size_t offset = 0; 34 | std::vector splits; 35 | for (auto pos = word_rule.find_first_of(' '); pos != std::string::npos; 36 | pos = word_rule.find_first_of(' ', pos + 1)) 37 | { 38 | if (pos == 0 || word_rule[pos - 1] == ' ') 39 | ARGOS_THROW("Invalid split rule: '" + word_rule + "'"); 40 | const auto sep = word_rule[pos - 1] == '-' ? '\0' : '-'; 41 | splits.push_back({unsigned(pos - offset), sep}); 42 | ++offset; 43 | } 44 | splits.push_back({unsigned(word_rule.size() - offset), '\0'}); 45 | word_rule.erase(remove(word_rule.begin(), word_rule.end(), ' '), 46 | word_rule.end()); 47 | to_lower(word_rule); 48 | m_strings.push_back(std::move(word_rule)); 49 | m_splits.insert({std::string_view(m_strings.back()), std::move(splits)}); 50 | } 51 | 52 | void WordSplitter::add_words(std::vector word_rules) 53 | { 54 | for (auto& word_rule: word_rules) 55 | add_word(std::move(word_rule)); 56 | } 57 | 58 | std::tuple 59 | WordSplitter::split(std::string_view word, size_t start_index, 60 | size_t max_length, bool must_split) const 61 | { 62 | auto key = remove_punctuation(word); 63 | const auto it = is_lower(key) 64 | ? m_splits.find(key) 65 | : m_splits.find(to_lower(key)); 66 | if (it != m_splits.end()) 67 | { 68 | Split prev = {unsigned(start_index), '\0'}; 69 | size_t length = 0; 70 | for (const auto split: it->second) 71 | { 72 | if (split.index < start_index + 1) 73 | continue; 74 | length += count_code_points(word.substr(prev.index, split.index - prev.index)); 75 | if (length + (split.separator ? 1 : 0) > max_length) 76 | break; 77 | prev = split; 78 | } 79 | if (prev.index > start_index + 1) 80 | return { 81 | word.substr(start_index, prev.index - start_index), 82 | prev.separator, 83 | word.substr(prev.index) 84 | }; 85 | } 86 | if (must_split) 87 | return default_rule(word.substr(start_index), max_length); 88 | return {{}, '\0', word}; 89 | } 90 | 91 | std::tuple 92 | WordSplitter::default_rule(std::string_view word, size_t max_length) 93 | { 94 | if (max_length <= 2) 95 | return {{}, '\0', word}; 96 | auto max_pos = find_nth_code_point(word, max_length); 97 | if (max_pos == std::string_view::npos) 98 | return {word, '\0', {}}; 99 | const auto ignore_utf8 = max_pos == max_length; 100 | --max_pos; 101 | if (!ignore_utf8) 102 | { 103 | while (is_utf8_continuation(word[max_pos])) 104 | --max_pos; 105 | } 106 | 107 | const auto min_pos = (max_length + 2) / 3; 108 | auto index = max_pos; 109 | for (auto count = max_length - 1; count-- > min_pos;) 110 | { 111 | --index; 112 | if (!ignore_utf8) 113 | { 114 | while (is_utf8_continuation(word[index])) 115 | --index; 116 | } 117 | if (uint8_t(word[index - 1]) >= 127 || uint8_t(word[index]) >= 127) 118 | continue; 119 | if ((isalnum(word[index - 1]) == 0) != (isalnum(word[index]) == 0)) 120 | return {word.substr(0, index), '\0', word.substr(index)}; 121 | if ((isdigit(word[index - 1]) == 0) != (isdigit(word[index]) == 0)) 122 | return {word.substr(0, index), '-', word.substr(index)}; 123 | } 124 | return { 125 | word.substr(0, max_pos), 126 | '-', 127 | word.substr(max_pos) 128 | }; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/Argos/WordSplitter.hpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-02-06. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace argos 16 | { 17 | class WordSplitter 18 | { 19 | public: 20 | void add_word(std::string word_rule); 21 | 22 | void add_words(std::vector word_rules); 23 | 24 | [[nodiscard]] std::tuple 25 | split(std::string_view word, size_t start_index, size_t max_length, 26 | bool must_split) const; 27 | private: 28 | static std::tuple 29 | default_rule(std::string_view word, size_t max_length) ; 30 | 31 | struct Split 32 | { 33 | unsigned index; 34 | char separator; 35 | }; 36 | std::map> m_splits; 37 | std::list m_strings; 38 | }; 39 | } 40 | -------------------------------------------------------------------------------- /tests/ArgosTest/Argv.hpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-04-20. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | #include 10 | #include 11 | 12 | struct Argv 13 | { 14 | Argv(std::initializer_list args) : strings(args) 15 | { 16 | for (auto& arg : strings) 17 | argv.push_back(arg.data()); 18 | } 19 | 20 | [[nodiscard]] int size() const 21 | { 22 | return static_cast(argv.size()); 23 | } 24 | 25 | char** data() 26 | { 27 | return argv.data(); 28 | } 29 | 30 | std::vector strings; 31 | std::vector argv; 32 | }; 33 | -------------------------------------------------------------------------------- /tests/ArgosTest/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # =========================================================================== 2 | # Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | # Created by Jan Erik Breimo on 2020-01-07. 4 | # 5 | # This file is distributed under the BSD License. 6 | # License text is included with the source distribution. 7 | # =========================================================================== 8 | cmake_minimum_required(VERSION 3.14) 9 | 10 | find_package(catch2 3.4.0 QUIET) 11 | 12 | if(NOT TARGET Catch2::Catch2WithMain) 13 | message(STATUS "Fetching Catch2...") 14 | include(FetchContent) 15 | FetchContent_Declare(catch2 16 | GIT_REPOSITORY "https://github.com/catchorg/Catch2.git" 17 | GIT_TAG "v3.4.0" 18 | ) 19 | FetchContent_MakeAvailable(catch2) 20 | endif () 21 | 22 | add_executable(ArgosTest 23 | Argv.hpp 24 | U8Adapter.hpp 25 | test_ArgumentCounter.cpp 26 | test_ArgumentParser.cpp 27 | test_ArgumentValue.cpp 28 | test_Callbacks.cpp 29 | test_HelpWriter.cpp 30 | test_ParseValue.cpp 31 | test_ParsedArguments.cpp 32 | test_StandardOptionIterator.cpp 33 | test_StringUtilities.cpp 34 | test_Subcommands.cpp 35 | test_TextFormatter.cpp 36 | test_TextWriter.cpp 37 | test_WordSplitter.cpp 38 | ) 39 | 40 | target_link_libraries(ArgosTest 41 | PRIVATE 42 | Argos::Argos 43 | Catch2::Catch2WithMain 44 | ) 45 | 46 | target_compile_options(ArgosTest 47 | PRIVATE 48 | $<$:/utf-8> 49 | ) 50 | 51 | target_include_directories(ArgosTest 52 | PRIVATE 53 | ../../src 54 | ) 55 | 56 | TargetEnableCodeCoverage(ArgosTest "${ARGOS_CODE_COVERAGE}") 57 | TargetEnableAllWarnings(ArgosTest) 58 | 59 | add_test(NAME ArgosTest COMMAND ${CMAKE_CURRENT_BINARY_DIR}/ArgosTest) 60 | -------------------------------------------------------------------------------- /tests/ArgosTest/U8Adapter.hpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2023 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2023-11-07. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #pragma once 9 | #include 10 | 11 | #define U8(s) reinterpret_cast(u8##s) 12 | -------------------------------------------------------------------------------- /tests/ArgosTest/test_ArgumentCounter.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-01-23. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include 9 | #include "Argos/ArgumentCounter.hpp" 10 | #include "../../include/Argos/Argument.hpp" 11 | 12 | TEST_CASE("Test non-deterministic counter.") 13 | { 14 | argos::CommandData command; 15 | auto& args = command.arguments; 16 | args.push_back(argos::Argument("1").count(0, 1).release()); 17 | args.push_back(argos::Argument("2").count(2).release()); 18 | 19 | REQUIRE(argos::ArgumentCounter::requires_argument_count(command)); 20 | 21 | SECTION("Unknown number of arguments") 22 | { 23 | argos::ArgumentCounter counter(command); 24 | REQUIRE(!counter.is_complete()); 25 | REQUIRE(counter.next_argument() == args[0].get()); 26 | REQUIRE(!counter.is_complete()); 27 | REQUIRE(counter.next_argument() == args[1].get()); 28 | REQUIRE(!counter.is_complete()); 29 | REQUIRE(counter.next_argument() == args[1].get()); 30 | REQUIRE(counter.is_complete()); 31 | REQUIRE(counter.next_argument() == nullptr); 32 | } 33 | SECTION("Known number of arguments") 34 | { 35 | argos::ArgumentCounter counter(command, 2); 36 | REQUIRE(!counter.is_complete()); 37 | REQUIRE(counter.next_argument() == args[1].get()); 38 | REQUIRE(!counter.is_complete()); 39 | REQUIRE(counter.next_argument() == args[1].get()); 40 | REQUIRE(counter.is_complete()); 41 | REQUIRE(counter.next_argument() == nullptr); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/ArgosTest/test_ArgumentValue.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-03-03. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include 9 | #include "Argos/ArgumentParser.hpp" 10 | 11 | #include 12 | 13 | TEST_CASE("Test ArgumentValue split") 14 | { 15 | using namespace argos; 16 | auto args = ArgumentParser("test") 17 | .auto_exit(false) 18 | .add(Option({"--f="}).argument("M,N")) 19 | .parse({"--f=34,45"}); 20 | 21 | auto values1 = args.value("--f=").split(',', 2, 2).as_ints(); 22 | REQUIRE(values1 == std::vector{34, 45}); 23 | 24 | auto values2 = args.value("--f=").split_n(',', 2).as_ints(); 25 | REQUIRE(values2 == std::vector{34, 45}); 26 | } 27 | 28 | TEST_CASE("Test ArgumentValues split") 29 | { 30 | using namespace argos; 31 | auto args = ArgumentParser("test") 32 | .auto_exit(false) 33 | .add(Option{"--f"}.argument("PATHS").operation(OptionOperation::APPEND)) 34 | .parse({"--f", "/a/b:/a/c", "--f", "/b/c:/b/d/e:/c/a"}); 35 | auto values = args.values("--f").split(':').as_strings(); 36 | REQUIRE(values == std::vector{"/a/b", "/a/c", "/b/c", "/b/d/e", "/c/a"}); 37 | } 38 | 39 | TEST_CASE("Double values") 40 | { 41 | using namespace argos; 42 | auto args = ArgumentParser("test") 43 | .auto_exit(false) 44 | .add(Argument("ARG1")) 45 | .add(Option{"-v"}.argument("N")) 46 | .parse({"1.567"}); 47 | REQUIRE(args.value("ARG1")); 48 | REQUIRE(args.value("ARG1").as_double() == 1.567); 49 | REQUIRE(!args.value("-v")); 50 | REQUIRE(args.value("-v").as_double(1e-10) == 1e-10); 51 | } 52 | 53 | TEST_CASE("ArgumentValueIterator") 54 | { 55 | using namespace argos; 56 | auto args = ArgumentParser("test") 57 | .auto_exit(false) 58 | .add(Option{"-i"} 59 | .argument("STR[:STR]*") 60 | .operation(argos::OptionOperation::APPEND)) 61 | .parse({"-i", "A:B", "-i", "C:D:E"}); 62 | const char expected[] = "ABCDE"; 63 | auto values = args.values("-i").split(':'); 64 | REQUIRE(bool(values)); 65 | REQUIRE(values.size() == strlen(expected)); 66 | int i = 0; 67 | for (auto v : values) 68 | { 69 | CAPTURE(i); 70 | REQUIRE(v.as_string() == std::string(1, expected[i++])); 71 | } 72 | REQUIRE(i == strlen(expected)); 73 | } 74 | -------------------------------------------------------------------------------- /tests/ArgosTest/test_Callbacks.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2025 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2025-01-19. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include 9 | #include 10 | #include "Argos/ArgumentParser.hpp" 11 | #include "Argv.hpp" 12 | 13 | TEST_CASE("Test option callback") 14 | { 15 | using namespace argos; 16 | Argv argv{"test", "-a"}; 17 | auto args = ArgumentParser() 18 | .auto_exit(false) 19 | .add(Option({"-b"})) 20 | .add(Option({"-c"})) 21 | .add(Option({"-a"}).callback( 22 | [](auto& oa) -> bool 23 | { 24 | oa.builder.assign("-b", "true").assign("-c", "true"); 25 | return true; 26 | })) 27 | .parse(argv.size(), argv.data()); 28 | REQUIRE(args.result_code() == ParserResultCode::SUCCESS); 29 | REQUIRE(args.value("-a").as_bool()); 30 | REQUIRE(args.value("-b").as_bool()); 31 | REQUIRE(args.value("-c").as_bool()); 32 | } 33 | 34 | TEST_CASE("Test argument callback") 35 | { 36 | using namespace argos; 37 | Argv argv{"test", "-b", "abcd"}; 38 | auto args = ArgumentParser() 39 | .auto_exit(false) 40 | .add(Option({"-b"})) 41 | .add(Argument("arg").callback( 42 | [](auto aa) -> bool 43 | { 44 | aa.builder.assign("-b", "false"); 45 | return true; 46 | })) 47 | .parse(argv.size(), argv.data()); 48 | REQUIRE(args.result_code() == ParserResultCode::SUCCESS); 49 | REQUIRE(!args.value("-b").as_bool()); 50 | REQUIRE(args.value("arg").as_string() == "abcd"); 51 | } 52 | 53 | TEST_CASE("Callback for all options") 54 | { 55 | using namespace argos; 56 | struct Callback 57 | { 58 | Callback(size_t& i) : i(i) {} 59 | void operator()(OptionCallbackArguments& oa) 60 | { 61 | REQUIRE(i != values.size()); 62 | REQUIRE(oa.view.flags().size() == 1); 63 | REQUIRE(oa.view.flags()[0] == values[i]); 64 | ++i; 65 | } 66 | 67 | size_t& i; 68 | std::array values = {"-b", "-c"}; 69 | }; 70 | 71 | size_t i = 0; 72 | Argv argv{"test", "-b", "-c"}; 73 | auto args = ArgumentParser() 74 | .auto_exit(false) 75 | .add(Option("-b")) 76 | .add(Option("-c")) 77 | .option_callback(Callback(i)) 78 | .parse(argv.size(), argv.data()); 79 | REQUIRE(i == 2); 80 | } 81 | 82 | TEST_CASE("Callback for all arguments") 83 | { 84 | using namespace argos; 85 | struct Callback 86 | { 87 | Callback(size_t& i) 88 | : i(i) 89 | { 90 | } 91 | 92 | void operator()(ArgumentCallbackArguments& oa) 93 | { 94 | REQUIRE(i != values.size()); 95 | REQUIRE(oa.view.name() == values[i]); 96 | ++i; 97 | } 98 | 99 | size_t& i; 100 | std::array values = {"FILE", "URL"}; 101 | }; 102 | 103 | size_t i = 0; 104 | Argv argv{"test", "bbb", "ccc"}; 105 | auto args = ArgumentParser() 106 | .auto_exit(false) 107 | .add(Argument("FILE")) 108 | .add(Argument("URL")) 109 | .argument_callback(Callback(i)) 110 | .parse(argv.size(), argv.data()); 111 | REQUIRE(i == 2); 112 | } 113 | 114 | TEST_CASE("Test option callback adding new args") 115 | { 116 | using namespace argos; 117 | Argv argv{"test", "-aC"}; 118 | auto args = ArgumentParser() 119 | .auto_exit(false) 120 | .add(Option({"-b"})) 121 | .add(Option({"-B"}).alias("-b").constant(false)) 122 | .add(Option({"-c"})) 123 | .add(Option({"-C"}).alias("-c").constant(false)) 124 | .add(Option({"-d"})) 125 | .add(Option({"-D"}).alias("-d").constant(false)) 126 | .add(Option({"-a"}).callback( 127 | [](auto& oa) 128 | { 129 | oa.new_arguments = {"-bcd"}; 130 | })) 131 | .parse(argv.size(), argv.data()); 132 | REQUIRE(args.result_code() == ParserResultCode::SUCCESS); 133 | REQUIRE(args.value("-a").as_bool()); 134 | REQUIRE(args.value("-b").as_bool()); 135 | REQUIRE_FALSE(args.value("-c").as_bool()); 136 | REQUIRE(args.value("-d").as_bool()); 137 | } 138 | -------------------------------------------------------------------------------- /tests/ArgosTest/test_HelpWriter.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-03-01. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include "Argos/ArgumentParser.hpp" 9 | #include 10 | 11 | #include 12 | 13 | TEST_CASE("Separate lines for STOP options") 14 | { 15 | using namespace argos; 16 | std::stringstream ss; 17 | auto parser = ArgumentParser("prog") 18 | .add(Option({"--help"}).type(OptionType::HELP).visibility(Visibility::USAGE)) 19 | .add(Option({"--version"}).type(OptionType::STOP).visibility(Visibility::USAGE)) 20 | .add(Option({"--do"}).argument("STUFF").type(OptionType::EXIT).visibility(Visibility::USAGE)) 21 | .add(Option({"--option"}).visibility(Visibility::USAGE)) 22 | .add(Argument("arg").visibility(Visibility::USAGE)) 23 | .stream(&ss) 24 | .move(); 25 | parser.write_help_text(); 26 | REQUIRE(ss.str() == R"-(USAGE 27 | prog --help 28 | prog --version 29 | prog --do 30 | prog [--option] 31 | )-"); 32 | } 33 | 34 | TEST_CASE("No empty line after undocumented arguments and options.") 35 | { 36 | using namespace argos; 37 | std::stringstream ss; 38 | auto parser = ArgumentParser("prog") 39 | .add(Option({"--option1"}).help( 40 | "A text that is too long to fit on a single line. This makes the help text appear on the next line.")) 41 | .add(Option({"--option2"})) 42 | .add(Argument("arg")) 43 | .generate_help_option(false) 44 | .stream(&ss) 45 | .move(); 46 | parser.write_help_text(); 47 | REQUIRE(ss.str() == R"-(USAGE 48 | prog [--option1] [--option2] 49 | 50 | ARGUMENTS 51 | 52 | 53 | OPTIONS 54 | --option1 55 | A text that is too long to fit on a single line. This makes the help 56 | text appear on the next line. 57 | --option2 58 | )-"); 59 | } 60 | 61 | TEST_CASE("Callbacks for arguments and options.") 62 | { 63 | using namespace argos; 64 | std::stringstream ss; 65 | auto parser = ArgumentParser("prog") 66 | .add(Opt({"--opt"}).help([](){return "Option";})) 67 | .add(Arg("arg").help([](){return "Argument";})) 68 | .generate_help_option(false) 69 | .stream(&ss) 70 | .move(); 71 | parser.write_help_text(); 72 | REQUIRE(ss.str() == R"-(USAGE 73 | prog [--opt] 74 | 75 | ARGUMENTS 76 | Argument 77 | 78 | OPTIONS 79 | --opt Option 80 | )-"); 81 | } 82 | 83 | TEST_CASE("Include ABOUT text.") 84 | { 85 | using namespace argos; 86 | std::stringstream ss; 87 | auto parser = ArgumentParser("prog") 88 | .about("Does stuff.") 89 | .add(Arg("arg").help([](){return "Argument";})) 90 | .generate_help_option(false) 91 | .stream(&ss) 92 | .move(); 93 | parser.write_help_text(); 94 | REQUIRE(ss.str() == R"-(USAGE 95 | prog 96 | 97 | Does stuff. 98 | 99 | ARGUMENTS 100 | Argument 101 | )-"); 102 | } 103 | 104 | TEST_CASE("HELP is used if sub-commands lack ABOUT.") 105 | { 106 | using namespace argos; 107 | std::stringstream ss; 108 | auto parser = ArgumentParser("prog") 109 | .add(Command("foo").help("Does foo things.")) 110 | .add(Command("bar")) 111 | .stream(&ss) 112 | .move(); 113 | 114 | SECTION("top-level help text") 115 | { 116 | parser.write_help_text(); 117 | REQUIRE(ss.str() == R"-(USAGE 118 | prog --help 119 | prog foo|bar 120 | 121 | COMMANDS 122 | foo Does foo things. 123 | bar 124 | 125 | OPTIONS 126 | -h, --help Display this help text. 127 | )-"); 128 | } 129 | SECTION("sub-command help text") 130 | { 131 | parser.write_subcommand_help_text({"foo"}); 132 | REQUIRE(ss.str() == R"-(USAGE 133 | prog foo --help 134 | prog foo 135 | 136 | Does foo things. 137 | 138 | OPTIONS 139 | -h, --help Display this help text. 140 | )-"); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /tests/ArgosTest/test_ParseValue.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-05-19. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include "Argos/ParseValue.hpp" 9 | #include 10 | 11 | TEST_CASE("parse_integer on empty string") 12 | { 13 | REQUIRE(!argos::parse_integer({}, 0).has_value()); 14 | } 15 | 16 | TEST_CASE("parse_floating_point on empty string") 17 | { 18 | REQUIRE(!argos::parse_floating_point({}).has_value()); 19 | } 20 | -------------------------------------------------------------------------------- /tests/ArgosTest/test_ParsedArguments.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-04-20. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include 9 | #include "Argos/Argos.hpp" 10 | 11 | #include "Argv.hpp" 12 | 13 | TEST_CASE("Test filter argc/argv") 14 | { 15 | using namespace argos; 16 | auto parser = ArgumentParser("test") 17 | .auto_exit(false) 18 | .ignore_undefined_options(true) 19 | .ignore_undefined_arguments(true) 20 | .add(Argument("FILE")) 21 | .add(Option("-f")) 22 | .add(Option{"--g"}) 23 | .move(); 24 | SECTION("filter all") 25 | { 26 | Argv argv{{"test", "-f", "file"}}; 27 | int c = argv.size(); 28 | char** v = argv.data(); 29 | auto args = parser.parse(c, v); 30 | args.filter_parsed_arguments(c, v); 31 | REQUIRE(c == 1); 32 | REQUIRE(std::string(v[0]) == "test"); 33 | } 34 | SECTION("filter some") 35 | { 36 | Argv argv{{"test", "-f", "-p", "--g", "--q", "file", "text"}}; 37 | int c = argv.size(); 38 | char** v = argv.data(); 39 | auto args = parser.parse(c, v); 40 | args.filter_parsed_arguments(c, v); 41 | REQUIRE(c == 4); 42 | REQUIRE(std::string(v[0]) == "test"); 43 | REQUIRE(std::string(v[1]) == "-p"); 44 | REQUIRE(std::string(v[2]) == "--q"); 45 | REQUIRE(std::string(v[3]) == "text"); 46 | } 47 | } 48 | 49 | TEST_CASE("Check options of arguments of main command and sub-commands") 50 | { 51 | using namespace argos; 52 | auto args = ArgumentParser() 53 | .add(Opt("-f", "--flag")) 54 | .add(Cmd("cmd") 55 | .add(Opt("-o", "--option")) 56 | .add(Arg("ARG"))) 57 | .parse({"-f", "cmd", "-o", "arg"}); 58 | auto all_opts = args.all_options(); 59 | REQUIRE(all_opts.size() == 2); 60 | REQUIRE(all_opts[0]->flags().size() == 2); 61 | REQUIRE(all_opts[0]->flags()[0] == "-f"); 62 | REQUIRE(all_opts[0]->flags()[1] == "--flag"); 63 | REQUIRE(all_opts[1]->flags().size() == 2); 64 | REQUIRE(all_opts[1]->flags()[0] == "-h");; 65 | REQUIRE(all_opts[1]->flags()[1] == "--help"); 66 | REQUIRE(args.all_arguments().empty()); 67 | auto cmds = args.subcommands(); 68 | REQUIRE(cmds.size() == 1); 69 | auto cmd = cmds[0]; 70 | REQUIRE(cmd.name() == "cmd"); 71 | all_opts = cmd.all_options(); 72 | REQUIRE(all_opts.size() == 2); 73 | REQUIRE(all_opts[0]->flags().size() == 2); 74 | REQUIRE(all_opts[0]->flags()[0] == "-o"); 75 | REQUIRE(all_opts[0]->flags()[1] == "--option"); 76 | REQUIRE(all_opts[1]->flags().size() == 2); 77 | REQUIRE(all_opts[1]->flags()[0] == "-h"); 78 | REQUIRE(all_opts[1]->flags()[1] == "--help"); 79 | auto all_args = cmd.all_arguments(); 80 | REQUIRE(all_args.size() == 1); 81 | REQUIRE(all_args[0]->name() == "ARG"); 82 | } 83 | -------------------------------------------------------------------------------- /tests/ArgosTest/test_StandardOptionIterator.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-01-09. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include 9 | #include "Argos/StandardOptionIterator.hpp" 10 | 11 | std::vector make_args( 12 | const std::vector& strings) 13 | { 14 | std::vector result; 15 | result.reserve(strings.size()); 16 | for (auto& s : strings) 17 | result.emplace_back(s); 18 | return result; 19 | } 20 | 21 | TEST_CASE("Test empty list of arguments.") 22 | { 23 | std::vector strings; 24 | argos::StandardOptionIterator it(make_args(strings)); 25 | REQUIRE(!it.next()); 26 | REQUIRE(!it.next_value()); 27 | } 28 | 29 | TEST_CASE("Test empty argument.") 30 | { 31 | std::vector strings{"", "bc"}; 32 | argos::StandardOptionIterator it(make_args(strings)); 33 | auto value = it.next(); 34 | REQUIRE(value.has_value()); 35 | REQUIRE(value->empty()); 36 | value = it.next(); 37 | REQUIRE(value.has_value()); 38 | REQUIRE(*value == "bc"); 39 | } 40 | 41 | TEST_CASE("Test short options.") 42 | { 43 | std::vector strings {"-a", "-bc"}; 44 | argos::StandardOptionIterator it(make_args(strings)); 45 | auto value = it.next(); 46 | REQUIRE(value.has_value()); 47 | REQUIRE(*value == "-a"); 48 | value = it.next(); 49 | REQUIRE(value.has_value()); 50 | REQUIRE(*value == "-b"); 51 | value = it.next(); 52 | REQUIRE(value.has_value()); 53 | REQUIRE(*value == "-c"); 54 | value = it.next(); 55 | REQUIRE(!value.has_value()); 56 | } 57 | 58 | TEST_CASE("Test short options arguments.") 59 | { 60 | std::vector strings{"-abc", "-d", "-efg"}; 61 | argos::StandardOptionIterator it(make_args(strings)); 62 | auto value = it.next(); 63 | REQUIRE(value.has_value()); 64 | REQUIRE(*value == "-a"); 65 | value = it.next_value(); 66 | REQUIRE(value.has_value()); 67 | REQUIRE(*value == "bc"); 68 | value = it.next(); 69 | REQUIRE(value.has_value()); 70 | REQUIRE(*value == "-d"); 71 | value = it.next_value(); 72 | REQUIRE(value.has_value()); 73 | REQUIRE(*value == "-efg"); 74 | value = it.next(); 75 | REQUIRE(!value.has_value()); 76 | } 77 | 78 | TEST_CASE("Test long options.") 79 | { 80 | std::vector strings{"--abc", "--def=ghi", "--jklmno=", "--pq"}; 81 | argos::StandardOptionIterator it(make_args(strings)); 82 | auto value = it.next(); 83 | REQUIRE(value.has_value()); 84 | REQUIRE(*value == "--abc"); 85 | value = it.next(); 86 | REQUIRE(value.has_value()); 87 | REQUIRE(*value == "--def="); 88 | value = it.next_value(); 89 | REQUIRE(value.has_value()); 90 | REQUIRE(*value == "ghi"); 91 | value = it.next(); 92 | REQUIRE(value.has_value()); 93 | REQUIRE(*value == "--jklmno="); 94 | value = it.next_value(); 95 | REQUIRE(value.has_value()); 96 | REQUIRE(value->empty()); 97 | value = it.next(); 98 | REQUIRE(value.has_value()); 99 | REQUIRE(*value == "--pq"); 100 | value = it.next(); 101 | REQUIRE(!value.has_value()); 102 | } 103 | 104 | TEST_CASE("Test skipping value after =.") 105 | { 106 | std::vector strings{"--def=ghi", "--jkl=", "--p"}; 107 | argos::StandardOptionIterator it(make_args(strings)); 108 | auto value = it.next(); 109 | REQUIRE(value.has_value()); 110 | REQUIRE(*value == "--def="); 111 | value = it.next(); 112 | REQUIRE(value.has_value()); 113 | REQUIRE(*value == "--jkl="); 114 | value = it.next(); 115 | REQUIRE(value.has_value()); 116 | REQUIRE(*value == "--p"); 117 | value = it.next(); 118 | REQUIRE(!value.has_value()); 119 | } 120 | -------------------------------------------------------------------------------- /tests/ArgosTest/test_StringUtilities.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-01-17. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include "Argos/StringUtilities.hpp" 9 | #include 10 | #include "U8Adapter.hpp" 11 | 12 | TEST_CASE("Test starts_with") 13 | { 14 | REQUIRE(argos::starts_with("ABCDEF", "A")); 15 | REQUIRE(argos::starts_with("ABCDEF", "ABC")); 16 | REQUIRE(argos::starts_with("ABCDEF", "ABCDEF")); 17 | REQUIRE_FALSE(argos::starts_with("ABCDEF", "abc")); 18 | REQUIRE_FALSE(argos::starts_with("ABCDEF", "ABCDEFG")); 19 | } 20 | 21 | TEST_CASE("Test starts_with_ci") 22 | { 23 | REQUIRE(argos::starts_with_ci("ABCDEF", "A")); 24 | REQUIRE(argos::starts_with_ci("ABCDEF", "ABC")); 25 | REQUIRE(argos::starts_with_ci("ABCDEF", "ABcDEF")); 26 | REQUIRE(argos::starts_with_ci("ABCDEF", "abc")); 27 | REQUIRE_FALSE(argos::starts_with_ci("ABCDEF", "ABCDEFG")); 28 | REQUIRE(argos::starts_with_ci("@ABCDYZ", "@abcdyz")); 29 | REQUIRE_FALSE(argos::starts_with_ci("@ABCDYZ", "`abcdyz")); 30 | REQUIRE_FALSE(argos::starts_with_ci("{ABCDYZ", "[abcdyz")); 31 | } 32 | 33 | TEST_CASE("Test is_less_ci") 34 | { 35 | REQUIRE(argos::is_less_ci("abc", "ABD")); 36 | REQUIRE_FALSE(argos::is_less_ci("abc", "ABC")); 37 | REQUIRE(argos::is_less_ci("abc", "ABCD")); 38 | REQUIRE_FALSE(argos::is_less_ci("aBCD", "ABC")); 39 | } 40 | 41 | TEST_CASE("Test count_code_points") 42 | { 43 | REQUIRE(argos::count_code_points(U8("Bæ bæ bø må.")) == 12); 44 | } 45 | 46 | TEST_CASE("Test find_nth_code_point") 47 | { 48 | REQUIRE(argos::find_nth_code_point(U8("Bæ bæ bø må."), 8) == 11); 49 | } 50 | 51 | TEST_CASE("Test find_nth_code_point non-UTF8") 52 | { 53 | REQUIRE(argos::find_nth_code_point("Ba boo\200 boo ma.", 8) == 8); 54 | } 55 | -------------------------------------------------------------------------------- /tests/ArgosTest/test_TextFormatter.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-02-05. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include "Argos/TextFormatter.hpp" 9 | 10 | #include 11 | #include 12 | #include "U8Adapter.hpp" 13 | 14 | TEST_CASE("Basic test of TextFormatter") 15 | { 16 | std::stringstream ss; 17 | argos::TextFormatter formatter(&ss, 40); 18 | formatter.write_words("Lorem ipsum dolor sit amet, consectetur adipiscing elit."); 19 | formatter.flush(); 20 | REQUIRE(ss.str() == "Lorem ipsum dolor sit amet, consectetur\nadipiscing elit."); 21 | } 22 | 23 | TEST_CASE("Test TextFormatter with indentation") 24 | { 25 | std::stringstream ss; 26 | argos::TextFormatter formatter(&ss, 40); 27 | formatter.write_words("Lorem ipsum dolor sit amet, consectetur adipiscing elit."); 28 | formatter.push_indentation(17); 29 | formatter.write_words("Lorem ipsum dolor sit amet, consectetur adipiscing elit."); 30 | formatter.pop_indentation(); 31 | formatter.write_words(" "); 32 | formatter.write_words("Lorem ipsum dolor sit amet, consectetur adipiscing elit."); 33 | formatter.flush(); 34 | REQUIRE(ss.str() == R"*(Lorem ipsum dolor sit amet, consectetur 35 | adipiscing elit. Lorem ipsum dolor sit 36 | amet, consectetur 37 | adipiscing elit. Lorem 38 | ipsum dolor sit amet, consectetur 39 | adipiscing elit.)*"); 40 | } 41 | 42 | TEST_CASE("Text with newlines") 43 | { 44 | std::stringstream ss; 45 | argos::TextFormatter formatter(&ss, 40); 46 | formatter.write_words("Lorem ipsum dolor\nsit amet, consectetur\nadipiscing elit."); 47 | formatter.flush(); 48 | REQUIRE(ss.str() == "Lorem ipsum dolor\nsit amet, consectetur\nadipiscing elit."); 49 | } 50 | 51 | TEST_CASE("Indentation change") 52 | { 53 | std::stringstream ss; 54 | argos::TextFormatter formatter(&ss, 40); 55 | formatter.write_words("ABCDEFGHIJ"); 56 | formatter.push_indentation(20); 57 | formatter.write_words("ABCDEFGHIJ"); 58 | formatter.flush(); 59 | REQUIRE(ss.str() == "ABCDEFGHIJ ABCDEFGHIJ"); 60 | } 61 | 62 | TEST_CASE("Indentation change with preformatted text") 63 | { 64 | std::stringstream ss; 65 | argos::TextFormatter formatter(&ss, 40); 66 | formatter.write_lines("ABCDEFGHIJ"); 67 | formatter.push_indentation(20); 68 | formatter.write_lines("ABCDEFGHIJ"); 69 | formatter.flush(); 70 | REQUIRE(ss.str() == "ABCDEFGHIJ ABCDEFGHIJ"); 71 | } 72 | 73 | TEST_CASE("Keep whitespace at the start of a line.") 74 | { 75 | std::stringstream ss; 76 | argos::TextFormatter formatter(&ss, 40); 77 | formatter.write_words(" Lorem ipsum dolor\n sit amet, consectetur\n adipiscing elit."); 78 | formatter.flush(); 79 | REQUIRE(ss.str() == " Lorem ipsum dolor\n sit amet, consectetur\n adipiscing elit."); 80 | } 81 | 82 | TEST_CASE("Preformatted text across several lines") 83 | { 84 | std::stringstream ss; 85 | argos::TextFormatter formatter(&ss, 30); 86 | formatter.push_indentation(5); 87 | formatter.write_lines("[abc efg]"); 88 | formatter.write_words(" "); 89 | formatter.write_lines("[abc efg]"); 90 | formatter.write_words(" "); 91 | formatter.write_lines("[abc efg]"); 92 | formatter.write_words(" "); 93 | formatter.write_lines("[abc efg]"); 94 | formatter.write_words(" "); 95 | formatter.write_lines("[abc efg]"); 96 | formatter.flush(); 97 | REQUIRE(ss.str() == " [abc efg] [abc efg]\n [abc efg] [abc efg]\n [abc efg]"); 98 | } 99 | 100 | TEST_CASE("TextFormatter with multi-byte characters") 101 | { 102 | std::stringstream ss; 103 | argos::TextFormatter formatter(&ss, 40); 104 | formatter.write_words(U8("Lorem ipsum dålår sit åmet, consøctetur adipiscing elit.")); 105 | formatter.flush(); 106 | REQUIRE(ss.str() == U8("Lorem ipsum dålår sit åmet, consøctetur\nadipiscing elit.")); 107 | } 108 | 109 | TEST_CASE("TextFormatter splitting word, no rule") 110 | { 111 | std::stringstream ss; 112 | argos::TextFormatter formatter(&ss, 10); 113 | formatter.write_words(U8("Brønnøysundsregisteret")); 114 | formatter.flush(); 115 | REQUIRE(ss.str() == U8("Brønnøysu-\nndsregist-\neret")); 116 | } 117 | 118 | TEST_CASE("TextFormatter splitting word with rule") 119 | { 120 | std::stringstream ss; 121 | argos::TextFormatter formatter(&ss, 12); 122 | formatter.word_splitter().add_word("Brønn øy sunds registeret"); 123 | formatter.write_words(U8("Til Brønnøysundsregisteret")); 124 | formatter.flush(); 125 | REQUIRE(ss.str() == U8("Til Brønnøy-\nsunds-\nregisteret")); 126 | } 127 | 128 | TEST_CASE("Text alignment") 129 | { 130 | std::stringstream ss; 131 | argos::TextFormatter formatter(&ss, 12); 132 | formatter.write_words(" Abcd efg hij klm"); 133 | formatter.flush(); 134 | REQUIRE(ss.str() == " Abcd efg\n hij klm"); 135 | } 136 | 137 | TEST_CASE("List item alignment") 138 | { 139 | std::stringstream ss; 140 | argos::TextFormatter formatter(&ss, 17); 141 | formatter.write_words(R"-(My list: 142 | - Abcdef ghijk lmn 143 | * Abcdef ghij 144 | * Abcdef ghijk lmn 145 | - Abcdef ghijk lmn 146 | 1. Abcdef ghijk lmn 147 | 1. Abcdef ghijk lmn 148 | Abcdef ghijk lmn opqrst)-"); 149 | formatter.flush(); 150 | REQUIRE(ss.str() == R"-(My list: 151 | - Abcdef ghijk 152 | lmn 153 | * Abcdef ghij 154 | * Abcdef 155 | ghijk lmn 156 | - Abcdef ghijk 157 | lmn 158 | 1. Abcdef ghijk 159 | lmn 160 | 1. Abcdef 161 | ghijk lmn 162 | Abcdef ghijk lmn 163 | opqrst)-"); 164 | } 165 | 166 | TEST_CASE("spaces before newline") 167 | { 168 | std::stringstream ss; 169 | argos::TextFormatter formatter(&ss, 12); 170 | formatter.write_words("abcd \nefgh"); 171 | formatter.flush(); 172 | REQUIRE(ss.str() == "abcd\nefgh"); 173 | } 174 | -------------------------------------------------------------------------------- /tests/ArgosTest/test_TextWriter.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-02-27. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include "Argos/TextWriter.hpp" 9 | #include 10 | 11 | #include 12 | 13 | TEST_CASE("Basic TextWriter") 14 | { 15 | argos::TextWriter writer(20); 16 | std::stringstream ss; 17 | writer.set_stream(&ss); 18 | writer.write("Word", false); 19 | writer.set_spaces(1); 20 | writer.write("word", false); 21 | writer.write("word", false); 22 | writer.flush(); 23 | REQUIRE(ss.str() == "Word wordword"); 24 | } 25 | -------------------------------------------------------------------------------- /tests/ArgosTest/test_WordSplitter.cpp: -------------------------------------------------------------------------------- 1 | //**************************************************************************** 2 | // Copyright © 2020 Jan Erik Breimo. All rights reserved. 3 | // Created by Jan Erik Breimo on 2020-02-07. 4 | // 5 | // This file is distributed under the Zero-Clause BSD License. 6 | // License text is included with the source distribution. 7 | //**************************************************************************** 8 | #include "Argos/WordSplitter.hpp" 9 | 10 | #include 11 | #include 12 | #include "U8Adapter.hpp" 13 | 14 | namespace 15 | { 16 | void test_default_split( 17 | std::string_view word, size_t start_pos, 18 | size_t max_length, 19 | std::tuple result) 20 | { 21 | CAPTURE(word, start_pos, max_length); 22 | argos::WordSplitter splitter; 23 | auto[txt, sep, rem] = splitter.split(word, start_pos, max_length, true); 24 | REQUIRE(txt == std::get<0>(result)); 25 | REQUIRE(sep == std::get<1>(result)); 26 | REQUIRE(rem == std::get<2>(result)); 27 | } 28 | 29 | void test_split( 30 | const std::string& rule, 31 | size_t start_pos, size_t max_length, 32 | std::tuple result) 33 | { 34 | argos::WordSplitter splitter; 35 | splitter.add_word(rule); 36 | std::string word = rule; 37 | word.erase(remove(word.begin(), word.end(), ' '), word.end()); 38 | CAPTURE(word, start_pos, max_length); 39 | auto[txt, sep, rem] = splitter.split(word, start_pos, max_length, false); 40 | REQUIRE(txt == std::get<0>(result)); 41 | REQUIRE(sep == std::get<1>(result)); 42 | REQUIRE(rem == std::get<2>(result)); 43 | } 44 | } 45 | 46 | TEST_CASE("Test default splitter") 47 | { 48 | argos::WordSplitter splitter; 49 | test_default_split("decision", 0, 8, {"decision", '\0', {}}); 50 | test_default_split("decision", 0, 7, {"decisi", '-', "on"}); 51 | test_default_split("abcdef123456", 0, 9, {"abcdef", '-', "123456"}); 52 | test_default_split("bbbbbbbbbbb", 0, 7, {"bbbbbb", '-', "bbbbb"}); 53 | test_default_split("bbbbbbccccccdddddd", 6, 7, {"cccccc", '-', "dddddd"}); 54 | } 55 | 56 | TEST_CASE("Test split double-dash option") 57 | { 58 | // I'm adding this test case to highlight a behavior that I'm not 59 | // entirely sure I want. 60 | test_default_split("--help", 0, 5, {"--", '\0', "help"}); 61 | } 62 | 63 | TEST_CASE("Test default splitter with UTF-8") 64 | { 65 | argos::WordSplitter splitter; 66 | test_default_split(U8("æøå•Ω醵üıœπ˙äöfiª√˛¸ƒ∂ß"), 0, 14, 67 | {U8("æøå•Ω醵üıœπ˙"), '-', U8("äöfiª√˛¸ƒ∂ß")}); 68 | test_default_split(U8("Båidg-hølnow"), 0, 8, 69 | {U8("Båidg-"), '\0', U8("hølnow")}); 70 | } 71 | 72 | TEST_CASE("Test splitter") 73 | { 74 | argos::WordSplitter splitter; 75 | test_split("ono mato poe ti con", 2, 8, {"omato", '-', "poeticon"}); 76 | test_split("ono mato poe ti con", 0, 8, {"onomato", '-', "poeticon"}); 77 | test_split("ono mato poe ti con", 0, 7, {"ono", '-', "matopoeticon"}); 78 | test_split("ono mato poe ti con", 7, 8, {"poeticon", '\0', {}}); 79 | test_split("ono mato poe ti con", 7, 7, {"poeti", '-', "con"}); 80 | } 81 | 82 | TEST_CASE("Test splitter with hyphens") 83 | { 84 | argos::WordSplitter splitter; 85 | test_split("multi- tasking", 0, 8, {"multi-", '\0', "tasking"}); 86 | } 87 | 88 | TEST_CASE("Test splitter with UTF-8") 89 | { 90 | argos::WordSplitter splitter; 91 | test_split(U8("Brønn øy sund"), 0, 6, {U8("Brønn"), '-', U8("øysund")}); 92 | test_split(U8("Brønn øy sund"), 0, 7, {U8("Brønn"), '-', U8("øysund")}); 93 | test_split(U8("Brønn øy sund"), 0, 8, {U8("Brønnøy"), '-', U8("sund")}); 94 | test_split(U8("Brønn øy sund"), 0, 10, {U8("Brønnøy"), '-', U8("sund")}); 95 | test_split(U8("Brønn øy sund"), 0, 11, {U8("Brønnøysund"), '\0', {}}); 96 | test_split(U8("Brønn øy sund"), 4, 7, {U8("nnøy"), '-', U8("sund")}); 97 | } 98 | 99 | TEST_CASE("Test splitter handles case and punctuation.") 100 | { 101 | argos::WordSplitter splitter; 102 | splitter.add_word("in compre hensi bili ties"); 103 | auto [txt, sep, rem] = splitter.split("Incomprehensibilities.", 0, 12, false); 104 | REQUIRE(txt == "Incompre"); 105 | REQUIRE(sep == '-'); 106 | REQUIRE(rem == "hensibilities."); 107 | } 108 | -------------------------------------------------------------------------------- /tools/compiled_code_size/codesize.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: UTF-8 -*- 3 | # =========================================================================== 4 | # Copyright © 2020 Jan Erik Breimo. All rights reserved. 5 | # Created by Jan Erik Breimo on 2020-03-20. 6 | # 7 | # This file is distributed under the BSD License. 8 | # License text is included with the source distribution. 9 | # =========================================================================== 10 | 11 | # This script is used to calculate the size of compiled functions in a 12 | # binary file. It uses the nm command to extract the addresses of the 13 | # functions and then calculates the size of each function by subtracting 14 | # the address of the current function from the address of the next function. 15 | # The nm command must be available in the system's PATH. 16 | 17 | import argparse 18 | import re 19 | import subprocess 20 | import sys 21 | 22 | 23 | def make_arg_parser(): 24 | ap = argparse.ArgumentParser() 25 | ap.add_argument("FILE", help="Any file supported by the nm command") 26 | return ap 27 | 28 | 29 | def main(): 30 | args = make_arg_parser().parse_args() 31 | output = subprocess.run(["nm", "-n", "--demangle", args.FILE], 32 | capture_output=True) 33 | if output.stderr: 34 | sys.stderr.write(output.stderr.decode("utf-8")) 35 | return 1 36 | 37 | start_addr = 0 38 | func_name = None 39 | nm_re = re.compile(r"^([0-9a-f]*)\s+([a-zA-Z?])\s(.+)$") 40 | for line in output.stdout.decode("utf-8").splitlines(): 41 | m = nm_re.match(line) 42 | if not m: 43 | func_name = None 44 | print("ERROR") 45 | continue 46 | if not m.group(1): 47 | func_name = None 48 | continue 49 | addr = int(m.group(1), 16) 50 | if func_name and addr > start_addr: 51 | print("%6d %s" % (addr - start_addr, func_name)) 52 | func_name = m.group(3) if m.group(2) in "tT" else None 53 | start_addr = addr 54 | return 0 55 | 56 | 57 | if __name__ == "__main__": 58 | sys.exit(main()) 59 | -------------------------------------------------------------------------------- /tools/make_single_src/make_argos_cpp.cmd.in: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | "${Python3_EXECUTABLE}"^ 4 | "${CMAKE_CURRENT_SOURCE_DIR}/tools/make_single_src/mergecpp.py"^ 5 | "${CMAKE_CURRENT_BINARY_DIR}/ArgosVersion.hpp"^ 6 | "${CMAKE_CURRENT_SOURCE_DIR}/include/Argos/Argos.hpp"^ 7 | -i "${CMAKE_CURRENT_BINARY_DIR}"^ 8 | -o "${CMAKE_CURRENT_SOURCE_DIR}/single_src/Argos/Argos.hpp" 9 | 10 | (set \n=^^^ 11 | %=empty, do not delete this line =% 12 | ^ 13 | %=empty, do not delete this line =% 14 | ) 15 | 16 | "${Python3_EXECUTABLE}"^ 17 | "${CMAKE_CURRENT_SOURCE_DIR}/tools/make_single_src/mergecpp.py"^ 18 | "${CMAKE_CURRENT_SOURCE_DIR}/src/Argos/*.cpp"^ 19 | -i "${CMAKE_CURRENT_SOURCE_DIR}/include"^ 20 | -i "${CMAKE_CURRENT_BINARY_DIR}"^ 21 | -f "${CMAKE_CURRENT_SOURCE_DIR}/include"^ 22 | -f "${CMAKE_CURRENT_BINARY_DIR}"^ 23 | --no-pragma-once^ 24 | -p ^"#include ""Argos.hpp""%\n%^"^ 25 | -o "${CMAKE_CURRENT_SOURCE_DIR}/single_src/Argos/Argos.cpp" 26 | -------------------------------------------------------------------------------- /tools/make_single_src/make_argos_cpp.sh.in: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | "${Python3_EXECUTABLE}" \ 4 | "${CMAKE_CURRENT_SOURCE_DIR}"/tools/make_single_src/mergecpp.py \ 5 | "${CMAKE_CURRENT_BINARY_DIR}"/ArgosVersion.hpp \ 6 | "${CMAKE_CURRENT_SOURCE_DIR}"/include/Argos/Argos.hpp \ 7 | -i "${CMAKE_CURRENT_BINARY_DIR}" \ 8 | -o "${CMAKE_CURRENT_SOURCE_DIR}"/single_src/Argos/Argos.hpp 9 | 10 | "${Python3_EXECUTABLE}" \ 11 | "${CMAKE_CURRENT_SOURCE_DIR}"/tools/make_single_src/mergecpp.py \ 12 | "${CMAKE_CURRENT_SOURCE_DIR}"/src/Argos/*.cpp \ 13 | -i "${CMAKE_CURRENT_SOURCE_DIR}"/include \ 14 | -i "${CMAKE_CURRENT_BINARY_DIR}" \ 15 | -f "${CMAKE_CURRENT_SOURCE_DIR}"/include \ 16 | -f "${CMAKE_CURRENT_BINARY_DIR}" \ 17 | --no-pragma-once \ 18 | -p '#include "Argos.hpp" 19 | ' \ 20 | -o "${CMAKE_CURRENT_SOURCE_DIR}"/single_src/Argos/Argos.cpp 21 | --------------------------------------------------------------------------------