«
Previous
|
Next
»
Revision 8a4a274f
| CMakeLists.txt | ||
|---|---|---|
|
CMAKE_MINIMUM_REQUIRED(VERSION 3.16)
|
||
|
PROJECT("Photo Compress Archiver")
|
||
|
|
||
|
################################################################################
|
||
|
# Check GCC version
|
||
|
################################################################################
|
||
|
IF ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||
|
# Require at least gcc 10.3 for C++20 support
|
||
|
IF (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.3)
|
||
|
MESSAGE(FATAL_ERROR "GCC version must be at least 10.3!")
|
||
|
ENDIF()
|
||
|
ENDIF()
|
||
|
|
||
|
# This is a neat trick to prevent in source builds
|
||
|
IF (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
|
||
|
MESSAGE(FATAL_ERROR "Prevented in-tree build. This is bad practice; you "
|
||
|
"should be running cmake from inside a build directory.")
|
||
|
ENDIF()
|
||
|
|
||
|
|
||
|
################################################################################
|
||
|
# Build configuration options
|
||
|
################################################################################
|
||
|
|
||
|
IF (NOT CMAKE_BUILD_TYPE)
|
||
|
SET(CMAKE_BUILD_TYPE ReleaseWithDebug CACHE STRING
|
||
|
"Choose the type of build, options are: None Debug ReceiverRelease ReleaseWithDebug."
|
||
|
FORCE )
|
||
|
ENDIF()
|
||
|
|
||
|
MESSAGE(STATUS "Build Type: " ${CMAKE_BUILD_TYPE})
|
||
|
|
||
|
ADD_CUSTOM_TARGET(Debug
|
||
|
COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Debug ${CMAKE_SOURCE_DIR}
|
||
|
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target all
|
||
|
COMMENT "Switch CMAKE_BUILD_TYPE to Debug"
|
||
|
)
|
||
|
|
||
|
ADD_CUSTOM_TARGET(ReceiverRelease
|
||
|
COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=PCARelease ${CMAKE_SOURCE_DIR}
|
||
|
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target all
|
||
|
COMMENT "Switch CMAKE_BUILD_TYPE to ReceiverRelease"
|
||
|
)
|
||
|
|
||
|
ADD_CUSTOM_TARGET(ReleaseWithDebug
|
||
|
COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=ReleaseWithDebug ${CMAKE_SOURCE_DIR}
|
||
|
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target all
|
||
|
COMMENT "Switch CMAKE_BUILD_TYPE to ReleaseWithDebug"
|
||
|
)
|
||
|
|
||
|
|
||
|
# Use /etc/os-release to determine Linux flavor and version
|
||
|
EXECUTE_PROCESS(COMMAND bash -c "grep \"^NAME=\" /etc/os-release | cut -c7- | rev | cut -c2- | rev"
|
||
|
OUTPUT_VARIABLE DISTRIB_ID
|
||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||
|
)
|
||
|
|
||
|
EXECUTE_PROCESS(COMMAND bash -c "grep \"^VERSION_ID=\" /etc/os-release | cut -c13- | rev | cut -c2- | rev"
|
||
|
OUTPUT_VARIABLE DISTRIB_RELEASE
|
||
|
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||
|
)
|
||
|
|
||
|
# Output the parsed ID and release version
|
||
|
MESSAGE(STATUS "DISTRIB_ID: " ${DISTRIB_ID})
|
||
|
MESSAGE(STATUS "DISTRIB_RELEASE: " ${DISTRIB_RELEASE})
|
||
|
|
||
|
# Do not let CMake add the build directory path to RPATH. This means that CL/SL
|
||
|
# cannot be run from the build directory and must be properly installed.
|
||
|
SET(CMAKE_SKIP_BUILD_RPATH TRUE)
|
||
|
|
||
|
# Uncomment this line to enable AddressSanitizer
|
||
|
# SET(CMAKE_CXX_FLAGS "-fsanitize=address -fno-omit-frame-pointer")
|
||
|
|
||
|
# Detect presence of mold linker and use it if found
|
||
|
IF (EXISTS "/usr/bin/mold")
|
||
|
MESSAGE(STATUS "Using mold linker!")
|
||
|
# Note this only works on gcc >= 12.1
|
||
|
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=mold")
|
||
|
SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fuse-ld=mold")
|
||
|
ELSE()
|
||
|
MESSAGE(WARNING "Mold linker not detected. Consider installing mold (add BL APT repo: "
|
||
|
"https://athena.blacklynx.tech/apt/): sudo apt install mold")
|
||
|
ENDIF()
|
||
|
|
||
|
#
|
||
|
# Set the compile flags
|
||
|
#
|
||
|
SET(CMAKE_CXX_STANDARD 20)
|
||
|
SET(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||
|
SET(CMAKE_CXX_EXTENSIONS OFF)
|
||
|
# SET(CMAKE_CXX_VISIBILITY_PRESET hidden)
|
||
|
|
||
|
# This length is used along with the __FILE__ macro for logging
|
||
|
STRING(LENGTH ${CMAKE_CURRENT_SOURCE_DIR} BPLEN)
|
||
|
ADD_DEFINITIONS("-DBUILD_PATH_LEN=${BPLEN}")
|
||
|
|
||
|
# Ensure symbols from statically linked libraries are hidden
|
||
|
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--exclude-libs=ALL")
|
||
|
|
||
|
# Enable warnings
|
||
|
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-unused-parameter")
|
||
|
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter")
|
||
|
|
||
|
IF (CMAKE_BUILD_TYPE STREQUAL "PCARelease")
|
||
|
|
||
|
# Tune for Skylake minimum
|
||
|
SET(COMPILE_FLAGS "-O3 -flto=auto -march=skylake")
|
||
|
|
||
|
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMPILE_FLAGS}")
|
||
|
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMPILE_FLAGS}")
|
||
|
|
||
|
ELSEIF (CMAKE_BUILD_TYPE STREQUAL "ReleaseWithDebug")
|
||
|
|
||
|
# Tune for Skylake minimum
|
||
|
SET(COMPILE_FLAGS "-g -O3 -flto=auto -march=skylake ")
|
||
|
|
||
|
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMPILE_FLAGS}")
|
||
|
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMPILE_FLAGS}")
|
||
|
|
||
|
ELSEIF (CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||
|
|
||
|
SET(COMPILE_FLAGS "-g -O0 -march=skylake -mtune=generic")
|
||
|
|
||
|
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMPILE_FLAGS}")
|
||
|
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMPILE_FLAGS}")
|
||
|
|
||
|
ENDIF()
|
||
|
|
||
|
# Set the library install path depending on the Linux flavor
|
||
|
SET(LIBRARY_INSTALL_PATH "")
|
||
|
IF ((DISTRIB_ID STREQUAL "Ubuntu") OR (DISTRIB_ID STREQUAL "Linux Mint"))
|
||
|
SET(LIBRARY_INSTALL_PATH "usr/lib/x86_64-linux-gnu/")
|
||
|
ELSE()
|
||
|
# CentOS/RedHat/Scientific
|
||
|
SET(LIBRARY_INSTALL_PATH "usr/lib64/")
|
||
|
ENDIF()
|
||
|
|
||
|
IF(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
|
||
|
set(CMAKE_INSTALL_PREFIX "/" CACHE PATH "..." FORCE)
|
||
|
ENDIF()
|
||
|
|
||
|
|
||
|
################################################################################
|
||
|
# Library Dependencies
|
||
|
################################################################################
|
||
|
SET(EXTERNAL_LIBS)
|
||
|
|
||
|
FIND_PACKAGE(Threads)
|
||
|
|
||
|
|
||
|
################################################################################
|
||
|
# External projects
|
||
|
################################################################################
|
||
|
INCLUDE(ExternalProject)
|
||
|
|
||
|
#
|
||
|
# libpackjpg
|
||
|
#
|
||
|
SET(PACKJPG_PREFIX ${CMAKE_BINARY_DIR}/libpackjpg)
|
||
|
|
||
|
# TODO: only build/install libpackjpg.a
|
||
|
ExternalProject_Add(libpackjpg_local
|
||
|
GIT_REPOSITORY "https://brutus.gleeze.com/git/main"
|
||
|
PREFIX ${PACKJPG_PREFIX}
|
||
|
INSTALL_DIR ${PACKJPG_PREFIX}/install
|
||
|
SOURCE_SUBDIR software/libpackjpg/lib_src
|
||
|
CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${PACKJPG_PREFIX}/install -DCMAKE_BUILD_TYPE=Release)
|
||
|
|
||
|
|
||
|
INCLUDE_DIRECTORIES(${PACKJPG_PREFIX}/install/usr/include)
|
||
|
|
||
|
SET(libpackjpg_static_lib ${PACKJPG_PREFIX}/install/${LIBRARY_INSTALL_PATH}/libpackjpg.a)
|
||
|
MESSAGE(STATUS "libpackjpg path: ${libpackjpg_static_lib}")
|
||
|
|
||
|
SET_SOURCE_FILES_PROPERTIES(
|
||
|
${libpackjpg_static_lib} PROPERTIES
|
||
|
EXTERNAL_OBJECT TRUE # Identifies this as an object file (static lib)
|
||
|
GENERATED TRUE # Avoids need for file to exist at configure-time
|
||
|
)
|
||
|
|
||
|
|
||
|
################################################################################
|
||
|
# Source
|
||
|
################################################################################
|
||
|
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/src)
|
||
|
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/external)
|
||
|
|
||
|
SET(pca_sources
|
||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/PhotoCompressArchiver.cc
|
||
|
)
|
||
|
|
||
|
|
||
|
################################################################################
|
||
|
# Target: pca executable
|
||
|
################################################################################
|
||
|
|
||
|
ADD_EXECUTABLE(pca
|
||
|
${pca_sources}
|
||
|
${CMAKE_CURRENT_SOURCE_DIR}/src/main.cc)
|
||
|
|
||
|
ADD_DEPENDENCIES(pca libpackjpg_local)
|
||
|
|
||
|
TARGET_LINK_LIBRARIES(pca ${EXTERNAL_LIBS} ${libpackjpg_static_lib})
|
||
| external/cli11/CLI11.hpp | ||
|---|---|---|
|
// CLI11: Version 2.6.1
|
||
|
// Originally designed by Henry Schreiner
|
||
|
// https://github.com/CLIUtils/CLI11
|
||
|
//
|
||
|
// This is a standalone header file generated by MakeSingleHeader.py in CLI11/scripts
|
||
|
// from: v2.6.1
|
||
|
//
|
||
|
// CLI11 2.6.1 Copyright (c) 2017-2025 University of Cincinnati, developed by Henry
|
||
|
// Schreiner under NSF AWARD 1414736. All rights reserved.
|
||
|
//
|
||
|
// Redistribution and use in source and binary forms of CLI11, with or without
|
||
|
// modification, are permitted provided that the following conditions are met:
|
||
|
//
|
||
|
// 1. Redistributions of source code must retain the above copyright notice, this
|
||
|
// list of conditions and the following disclaimer.
|
||
|
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||
|
// this list of conditions and the following disclaimer in the documentation
|
||
|
// and/or other materials provided with the distribution.
|
||
|
// 3. Neither the name of the copyright holder nor the names of its contributors
|
||
|
// may be used to endorse or promote products derived from this software without
|
||
|
// specific prior written permission.
|
||
|
//
|
||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||
|
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||
|
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||
|
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||
|
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||
|
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||
|
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||
|
// ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||
|
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||
|
|
||
|
#pragma once
|
||
|
|
||
|
// Standard combined includes:
|
||
|
#include <algorithm>
|
||
|
#include <array>
|
||
|
#include <cctype>
|
||
|
#include <clocale>
|
||
|
#include <cmath>
|
||
|
#include <cstdint>
|
||
|
#include <cstdlib>
|
||
|
#include <cstring>
|
||
|
#include <cwchar>
|
||
|
#include <exception>
|
||
|
#include <fstream>
|
||
|
#include <functional>
|
||
|
#include <iomanip>
|
||
|
#include <iostream>
|
||
|
#include <iterator>
|
||
|
#include <limits>
|
||
|
#include <locale>
|
||
|
#include <map>
|
||
|
#include <memory>
|
||
|
#include <numeric>
|
||
|
#include <set>
|
||
|
#include <sstream>
|
||
|
#include <stdexcept>
|
||
|
#include <string>
|
||
|
#include <tuple>
|
||
|
#include <type_traits>
|
||
|
#include <utility>
|
||
|
#include <vector>
|
||
|
|
||
|
|
||
|
#define CLI11_VERSION_MAJOR 2
|
||
|
#define CLI11_VERSION_MINOR 6
|
||
|
#define CLI11_VERSION_PATCH 1
|
||
|
#define CLI11_VERSION "2.6.1"
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// The following version macro is very similar to the one in pybind11
|
||
|
#if !(defined(_MSC_VER) && __cplusplus == 199711L) && !defined(__INTEL_COMPILER)
|
||
|
#if __cplusplus >= 201402L
|
||
|
#define CLI11_CPP14
|
||
|
#if __cplusplus >= 201703L
|
||
|
#define CLI11_CPP17
|
||
|
#if __cplusplus > 201703L
|
||
|
#define CLI11_CPP20
|
||
|
#if __cplusplus > 202002L
|
||
|
#define CLI11_CPP23
|
||
|
#if __cplusplus > 202302L
|
||
|
#define CLI11_CPP26
|
||
|
#endif
|
||
|
#endif
|
||
|
#endif
|
||
|
#endif
|
||
|
#endif
|
||
|
#elif defined(_MSC_VER) && __cplusplus == 199711L
|
||
|
// MSVC sets _MSVC_LANG rather than __cplusplus (supposedly until the standard was fully implemented)
|
||
|
// Unless you use the /Zc:__cplusplus flag on Visual Studio 2017 15.7 Preview 3 or newer
|
||
|
#if _MSVC_LANG >= 201402L
|
||
|
#define CLI11_CPP14
|
||
|
#if _MSVC_LANG > 201402L && _MSC_VER >= 1910
|
||
|
#define CLI11_CPP17
|
||
|
#if _MSVC_LANG > 201703L && _MSC_VER >= 1910
|
||
|
#define CLI11_CPP20
|
||
|
#if _MSVC_LANG > 202002L && _MSC_VER >= 1922
|
||
|
#define CLI11_CPP23
|
||
|
#endif
|
||
|
#endif
|
||
|
#endif
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
#if defined(CLI11_CPP14)
|
||
|
#define CLI11_DEPRECATED(reason) [[deprecated(reason)]]
|
||
|
#elif defined(_MSC_VER)
|
||
|
#define CLI11_DEPRECATED(reason) __declspec(deprecated(reason))
|
||
|
#else
|
||
|
#define CLI11_DEPRECATED(reason) __attribute__((deprecated(reason)))
|
||
|
#endif
|
||
|
|
||
|
// GCC < 10 doesn't ignore this in unevaluated contexts
|
||
|
#if !defined(CLI11_CPP17) || \
|
||
|
(defined(__GNUC__) && !defined(__llvm__) && !defined(__INTEL_COMPILER) && __GNUC__ < 10 && __GNUC__ > 4)
|
||
|
#define CLI11_NODISCARD
|
||
|
#else
|
||
|
#define CLI11_NODISCARD [[nodiscard]]
|
||
|
#endif
|
||
|
|
||
|
/** detection of rtti */
|
||
|
#ifndef CLI11_USE_STATIC_RTTI
|
||
|
#if (defined(_HAS_STATIC_RTTI) && _HAS_STATIC_RTTI)
|
||
|
#define CLI11_USE_STATIC_RTTI 1
|
||
|
#elif defined(__cpp_rtti)
|
||
|
#if (defined(_CPPRTTI) && _CPPRTTI == 0)
|
||
|
#define CLI11_USE_STATIC_RTTI 1
|
||
|
#else
|
||
|
#define CLI11_USE_STATIC_RTTI 0
|
||
|
#endif
|
||
|
#elif (defined(__GCC_RTTI) && __GXX_RTTI)
|
||
|
#define CLI11_USE_STATIC_RTTI 0
|
||
|
#else
|
||
|
#define CLI11_USE_STATIC_RTTI 1
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
/** <filesystem> availability */
|
||
|
#if defined CLI11_CPP17 && defined __has_include && !defined CLI11_HAS_FILESYSTEM
|
||
|
#if __has_include(<filesystem>)
|
||
|
// Filesystem cannot be used if targeting macOS < 10.15
|
||
|
#if defined __MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500
|
||
|
#define CLI11_HAS_FILESYSTEM 0
|
||
|
#elif defined(__wasi__)
|
||
|
// As of wasi-sdk-14, filesystem is not implemented
|
||
|
#define CLI11_HAS_FILESYSTEM 0
|
||
|
#else
|
||
|
#include <filesystem>
|
||
|
#if defined __cpp_lib_filesystem && __cpp_lib_filesystem >= 201703
|
||
|
#if defined _GLIBCXX_RELEASE && _GLIBCXX_RELEASE >= 9
|
||
|
#define CLI11_HAS_FILESYSTEM 1
|
||
|
#elif defined(__GLIBCXX__)
|
||
|
// if we are using gcc and Version <9 default to no filesystem
|
||
|
#define CLI11_HAS_FILESYSTEM 0
|
||
|
#else
|
||
|
#define CLI11_HAS_FILESYSTEM 1
|
||
|
#endif
|
||
|
#else
|
||
|
#define CLI11_HAS_FILESYSTEM 0
|
||
|
#endif
|
||
|
#endif
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
/** <codecvt> availability */
|
||
|
#if !defined(CLI11_CPP26) && !defined(CLI11_HAS_CODECVT)
|
||
|
#if defined(__GNUC__) && !defined(__llvm__) && !defined(__INTEL_COMPILER) && __GNUC__ < 5
|
||
|
#define CLI11_HAS_CODECVT 0
|
||
|
#else
|
||
|
#define CLI11_HAS_CODECVT 1
|
||
|
#include <codecvt>
|
||
|
#endif
|
||
|
#else
|
||
|
#if defined(CLI11_HAS_CODECVT)
|
||
|
#if CLI11_HAS_CODECVT > 0
|
||
|
#include <codecvt>
|
||
|
#endif
|
||
|
#else
|
||
|
#define CLI11_HAS_CODECVT 0
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
/** rtti enabled */
|
||
|
#ifndef CLI11_HAS_RTTI
|
||
|
#if defined(__GXX_RTTI) && __GXX_RTTI == 1
|
||
|
// gcc
|
||
|
#define CLI11_HAS_RTTI 1
|
||
|
#elif defined(_CPPRTTI) && _CPPRTTI == 1
|
||
|
// msvc
|
||
|
#define CLI11_HAS_RTTI 1
|
||
|
#elif defined(__NO_RTTI__) && __NO_RTTI__ == 1
|
||
|
// intel
|
||
|
#define CLI11_HAS_RTTI 0
|
||
|
#elif defined(__has_feature)
|
||
|
// clang and other newer compilers
|
||
|
#if __has_feature(cxx_rtti)
|
||
|
#define CLI11_HAS_RTTI 1
|
||
|
#else
|
||
|
#define CLI11_HAS_RTTI 0
|
||
|
#endif
|
||
|
#elif defined(__RTTI) || defined(__INTEL_RTTI__)
|
||
|
// more intel and some other compilers
|
||
|
#define CLI11_HAS_RTTI 1
|
||
|
#else
|
||
|
#define CLI11_HAS_RTTI 0
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
/** disable deprecations */
|
||
|
#if defined(__GNUC__) // GCC or clang
|
||
|
#define CLI11_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push")
|
||
|
#define CLI11_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop")
|
||
|
|
||
|
#define CLI11_DIAGNOSTIC_IGNORE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
|
||
|
|
||
|
#elif defined(_MSC_VER)
|
||
|
#define CLI11_DIAGNOSTIC_PUSH __pragma(warning(push))
|
||
|
#define CLI11_DIAGNOSTIC_POP __pragma(warning(pop))
|
||
|
|
||
|
#define CLI11_DIAGNOSTIC_IGNORE_DEPRECATED __pragma(warning(disable : 4996))
|
||
|
|
||
|
#else
|
||
|
#define CLI11_DIAGNOSTIC_PUSH
|
||
|
#define CLI11_DIAGNOSTIC_POP
|
||
|
|
||
|
#define CLI11_DIAGNOSTIC_IGNORE_DEPRECATED
|
||
|
|
||
|
#endif
|
||
|
|
||
|
/** Inline macro **/
|
||
|
#ifdef CLI11_COMPILE
|
||
|
#define CLI11_INLINE
|
||
|
#else
|
||
|
#define CLI11_INLINE inline
|
||
|
#endif
|
||
|
|
||
|
|
||
|
|
||
|
#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
|
||
|
#include <filesystem> // NOLINT(build/include)
|
||
|
#else
|
||
|
#include <sys/stat.h>
|
||
|
#include <sys/types.h>
|
||
|
#endif
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
#ifdef CLI11_CPP17
|
||
|
#include <string_view>
|
||
|
#endif // CLI11_CPP17
|
||
|
|
||
|
#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
|
||
|
#include <filesystem>
|
||
|
#include <string_view> // NOLINT(build/include)
|
||
|
#endif // CLI11_HAS_FILESYSTEM
|
||
|
|
||
|
|
||
|
|
||
|
#if defined(_WIN32)
|
||
|
#if !(defined(_AMD64_) || defined(_X86_) || defined(_ARM_))
|
||
|
#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || \
|
||
|
defined(_M_AMD64)
|
||
|
#define _AMD64_
|
||
|
#elif defined(i386) || defined(__i386) || defined(__i386__) || defined(__i386__) || defined(_M_IX86)
|
||
|
#define _X86_
|
||
|
#elif defined(__arm__) || defined(_M_ARM) || defined(_M_ARMT)
|
||
|
#define _ARM_
|
||
|
#elif defined(__aarch64__) || defined(_M_ARM64)
|
||
|
#define _ARM64_
|
||
|
#elif defined(_M_ARM64EC)
|
||
|
#define _ARM64EC_
|
||
|
#endif
|
||
|
#endif
|
||
|
|
||
|
// first
|
||
|
#ifndef NOMINMAX
|
||
|
// if NOMINMAX is already defined we don't want to mess with that either way
|
||
|
#define NOMINMAX
|
||
|
#include <windef.h>
|
||
|
#undef NOMINMAX
|
||
|
#else
|
||
|
#include <windef.h>
|
||
|
#endif
|
||
|
|
||
|
// second
|
||
|
#include <winbase.h>
|
||
|
// third
|
||
|
#include <processthreadsapi.h>
|
||
|
#include <shellapi.h>
|
||
|
#endif
|
||
|
|
||
|
|
||
|
namespace CLI {
|
||
|
|
||
|
|
||
|
/// Convert a wide string to a narrow string.
|
||
|
CLI11_INLINE std::string narrow(const std::wstring &str);
|
||
|
CLI11_INLINE std::string narrow(const wchar_t *str);
|
||
|
CLI11_INLINE std::string narrow(const wchar_t *str, std::size_t size);
|
||
|
|
||
|
/// Convert a narrow string to a wide string.
|
||
|
CLI11_INLINE std::wstring widen(const std::string &str);
|
||
|
CLI11_INLINE std::wstring widen(const char *str);
|
||
|
CLI11_INLINE std::wstring widen(const char *str, std::size_t size);
|
||
|
|
||
|
#ifdef CLI11_CPP17
|
||
|
CLI11_INLINE std::string narrow(std::wstring_view str);
|
||
|
CLI11_INLINE std::wstring widen(std::string_view str);
|
||
|
#endif // CLI11_CPP17
|
||
|
|
||
|
#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
|
||
|
/// Convert a char-string to a native path correctly.
|
||
|
CLI11_INLINE std::filesystem::path to_path(std::string_view str);
|
||
|
#endif // CLI11_HAS_FILESYSTEM
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
namespace detail {
|
||
|
|
||
|
#if !CLI11_HAS_CODECVT
|
||
|
/// Attempt to set one of the acceptable unicode locales for conversion
|
||
|
CLI11_INLINE void set_unicode_locale() {
|
||
|
static const std::array<const char *, 3> unicode_locales{{"C.UTF-8", "en_US.UTF-8", ".UTF-8"}};
|
||
|
|
||
|
for(const auto &locale_name : unicode_locales) {
|
||
|
if(std::setlocale(LC_ALL, locale_name) != nullptr) {
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
throw std::runtime_error("CLI::narrow: could not set locale to C.UTF-8");
|
||
|
}
|
||
|
|
||
|
template <typename F> struct scope_guard_t {
|
||
|
F closure;
|
||
|
|
||
|
explicit scope_guard_t(F closure_) : closure(closure_) {}
|
||
|
~scope_guard_t() { closure(); }
|
||
|
};
|
||
|
|
||
|
template <typename F> CLI11_NODISCARD CLI11_INLINE scope_guard_t<F> scope_guard(F &&closure) {
|
||
|
return scope_guard_t<F>{std::forward<F>(closure)};
|
||
|
}
|
||
|
|
||
|
#endif // !CLI11_HAS_CODECVT
|
||
|
|
||
|
CLI11_DIAGNOSTIC_PUSH
|
||
|
CLI11_DIAGNOSTIC_IGNORE_DEPRECATED
|
||
|
|
||
|
CLI11_INLINE std::string narrow_impl(const wchar_t *str, std::size_t str_size) {
|
||
|
#if CLI11_HAS_CODECVT
|
||
|
#ifdef _WIN32
|
||
|
return std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>().to_bytes(str, str + str_size);
|
||
|
|
||
|
#else
|
||
|
return std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(str, str + str_size);
|
||
|
|
||
|
#endif // _WIN32
|
||
|
#else // CLI11_HAS_CODECVT
|
||
|
(void)str_size;
|
||
|
std::mbstate_t state = std::mbstate_t();
|
||
|
const wchar_t *it = str;
|
||
|
|
||
|
std::string old_locale = std::setlocale(LC_ALL, nullptr);
|
||
|
auto sg = scope_guard([&] { std::setlocale(LC_ALL, old_locale.c_str()); });
|
||
|
set_unicode_locale();
|
||
|
|
||
|
std::size_t new_size = std::wcsrtombs(nullptr, &it, 0, &state);
|
||
|
if(new_size == static_cast<std::size_t>(-1)) {
|
||
|
throw std::runtime_error("CLI::narrow: conversion error in std::wcsrtombs at offset " +
|
||
|
std::to_string(it - str));
|
||
|
}
|
||
|
std::string result(new_size, '\0');
|
||
|
std::wcsrtombs(const_cast<char *>(result.data()), &str, new_size, &state);
|
||
|
|
||
|
return result;
|
||
|
|
||
|
#endif // CLI11_HAS_CODECVT
|
||
|
}
|
||
|
|
||
|
CLI11_INLINE std::wstring widen_impl(const char *str, std::size_t str_size) {
|
||
|
#if CLI11_HAS_CODECVT
|
||
|
#ifdef _WIN32
|
||
|
return std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>().from_bytes(str, str + str_size);
|
||
|
|
||
|
#else
|
||
|
return std::wstring_convert<std::codecvt_utf8<wchar_t>>().from_bytes(str, str + str_size);
|
||
|
|
||
|
#endif // _WIN32
|
||
|
#else // CLI11_HAS_CODECVT
|
||
|
(void)str_size;
|
||
|
std::mbstate_t state = std::mbstate_t();
|
||
|
const char *it = str;
|
||
|
|
||
|
std::string old_locale = std::setlocale(LC_ALL, nullptr);
|
||
|
auto sg = scope_guard([&] { std::setlocale(LC_ALL, old_locale.c_str()); });
|
||
|
set_unicode_locale();
|
||
|
|
||
|
std::size_t new_size = std::mbsrtowcs(nullptr, &it, 0, &state);
|
||
|
if(new_size == static_cast<std::size_t>(-1)) {
|
||
|
throw std::runtime_error("CLI::widen: conversion error in std::mbsrtowcs at offset " +
|
||
|
std::to_string(it - str));
|
||
|
}
|
||
|
std::wstring result(new_size, L'\0');
|
||
|
std::mbsrtowcs(const_cast<wchar_t *>(result.data()), &str, new_size, &state);
|
||
|
|
||
|
return result;
|
||
|
|
||
|
#endif // CLI11_HAS_CODECVT
|
||
|
}
|
||
|
|
||
|
CLI11_DIAGNOSTIC_POP
|
||
|
|
||
|
} // namespace detail
|
||
|
|
||
|
CLI11_INLINE std::string narrow(const wchar_t *str, std::size_t str_size) { return detail::narrow_impl(str, str_size); }
|
||
|
CLI11_INLINE std::string narrow(const std::wstring &str) { return detail::narrow_impl(str.data(), str.size()); }
|
||
|
// Flawfinder: ignore
|
||
|
CLI11_INLINE std::string narrow(const wchar_t *str) { return detail::narrow_impl(str, std::wcslen(str)); }
|
||
|
|
||
|
CLI11_INLINE std::wstring widen(const char *str, std::size_t str_size) { return detail::widen_impl(str, str_size); }
|
||
|
CLI11_INLINE std::wstring widen(const std::string &str) { return detail::widen_impl(str.data(), str.size()); }
|
||
|
// Flawfinder: ignore
|
||
|
CLI11_INLINE std::wstring widen(const char *str) { return detail::widen_impl(str, std::strlen(str)); }
|
||
|
|
||
|
#ifdef CLI11_CPP17
|
||
|
CLI11_INLINE std::string narrow(std::wstring_view str) { return detail::narrow_impl(str.data(), str.size()); }
|
||
|
CLI11_INLINE std::wstring widen(std::string_view str) { return detail::widen_impl(str.data(), str.size()); }
|
||
|
#endif // CLI11_CPP17
|
||
|
|
||
|
#if defined CLI11_HAS_FILESYSTEM && CLI11_HAS_FILESYSTEM > 0
|
||
|
CLI11_INLINE std::filesystem::path to_path(std::string_view str) {
|
||
|
return std::filesystem::path{
|
||
|
#ifdef _WIN32
|
||
|
widen(str)
|
||
|
#else
|
||
|
str
|
||
|
#endif // _WIN32
|
||
|
};
|
||
|
}
|
||
|
#endif // CLI11_HAS_FILESYSTEM
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
namespace detail {
|
||
|
#ifdef _WIN32
|
||
|
/// Decode and return UTF-8 argv from GetCommandLineW.
|
||
|
CLI11_INLINE std::vector<std::string> compute_win32_argv();
|
||
|
#endif
|
||
|
} // namespace detail
|
||
|
|
||
|
|
||
|
|
||
|
namespace detail {
|
||
|
|
||
|
#ifdef _WIN32
|
||
|
CLI11_INLINE std::vector<std::string> compute_win32_argv() {
|
||
|
std::vector<std::string> result;
|
||
|
int argc = 0;
|
||
|
|
||
|
auto deleter = [](wchar_t **ptr) { LocalFree(ptr); };
|
||
|
// NOLINTBEGIN(*-avoid-c-arrays)
|
||
|
auto wargv = std::unique_ptr<wchar_t *[], decltype(deleter)>(CommandLineToArgvW(GetCommandLineW(), &argc), deleter);
|
||
|
// NOLINTEND(*-avoid-c-arrays)
|
||
|
|
||
|
if(wargv == nullptr) {
|
||
|
throw std::runtime_error("CommandLineToArgvW failed with code " + std::to_string(GetLastError()));
|
||
|
}
|
||
|
|
||
|
result.reserve(static_cast<size_t>(argc));
|
||
|
for(size_t i = 0; i < static_cast<size_t>(argc); ++i) {
|
||
|
result.push_back(narrow(wargv[i]));
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
} // namespace detail
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/// Include the items in this namespace to get free conversion of enums to/from streams.
|
||
|
/// (This is available inside CLI as well, so CLI11 will use this without a using statement).
|
||
|
namespace enums {
|
||
|
|
||
|
/// output streaming for enumerations
|
||
|
template <typename T, typename = typename std::enable_if<std::is_enum<T>::value>::type>
|
||
|
std::ostream &operator<<(std::ostream &in, const T &item) {
|
||
|
// make sure this is out of the detail namespace otherwise it won't be found when needed
|
||
|
// https://isocpp.org/wiki/faq/input-output#print-char-or-ptr-as-number
|
||
|
return in << +static_cast<typename std::underlying_type<T>::type>(item);
|
||
|
}
|
||
|
|
||
|
} // namespace enums
|
||
|
|
||
|
/// Export to CLI namespace
|
||
|
using enums::operator<<;
|
||
|
|
||
|
namespace detail {
|
||
|
/// a constant defining an expected max vector size defined to be a big number that could be multiplied by 4 and not
|
||
|
/// produce overflow for some expected uses
|
||
|
constexpr int expected_max_vector_size{1 << 29};
|
||
|
// Based on http://stackoverflow.com/questions/236129/split-a-string-in-c
|
||
|
/// Split a string by a delim
|
||
|
CLI11_INLINE std::vector<std::string> split(const std::string &s, char delim);
|
||
|
|
||
|
/// Simple function to join a string
|
||
|
template <typename T> std::string join(const T &v, std::string delim = ",") {
|
||
|
std::ostringstream s;
|
||
|
auto beg = std::begin(v);
|
||
|
auto end = std::end(v);
|
||
|
if(beg != end)
|
||
|
s << *beg++;
|
||
|
while(beg != end) {
|
||
|
s << delim << *beg++;
|
||
|
}
|
||
|
auto rval = s.str();
|
||
|
if(!rval.empty() && delim.size() == 1 && rval.back() == delim[0]) {
|
||
|
// remove trailing delimiter if the last entry was empty
|
||
|
rval.pop_back();
|
||
|
}
|
||
|
return rval;
|
||
|
}
|
||
|
|
||
|
/// Simple function to join a string from processed elements
|
||
|
template <typename T,
|
||
|
typename Callable,
|
||
|
typename = typename std::enable_if<!std::is_constructible<std::string, Callable>::value>::type>
|
||
|
std::string join(const T &v, Callable func, std::string delim = ",") {
|
||
|
std::ostringstream s;
|
||
|
auto beg = std::begin(v);
|
||
|
auto end = std::end(v);
|
||
|
auto loc = s.tellp();
|
||
|
while(beg != end) {
|
||
|
auto nloc = s.tellp();
|
||
|
if(nloc > loc) {
|
||
|
s << delim;
|
||
|
loc = nloc;
|
||
|
}
|
||
|
s << func(*beg++);
|
||
|
}
|
||
|
return s.str();
|
||
|
}
|
||
|
|
||
|
/// Join a string in reverse order
|
||
|
template <typename T> std::string rjoin(const T &v, std::string delim = ",") {
|
||
|
std::ostringstream s;
|
||
|
for(std::size_t start = 0; start < v.size(); start++) {
|
||
|
if(start > 0)
|
||
|
s << delim;
|
||
|
s << v[v.size() - start - 1];
|
||
|
}
|
||
|
return s.str();
|
||
|
}
|
||
|
|
||
|
// Based roughly on http://stackoverflow.com/questions/25829143/c-trim-whitespace-from-a-string
|
||
|
|
||
|
/// Trim whitespace from left of string
|
||
|
CLI11_INLINE std::string <rim(std::string &str);
|
||
|
|
||
|
/// Trim anything from left of string
|
||
|
CLI11_INLINE std::string <rim(std::string &str, const std::string &filter);
|
||
|
|
||
|
/// Trim whitespace from right of string
|
||
|
CLI11_INLINE std::string &rtrim(std::string &str);
|
||
|
|
||
|
/// Trim anything from right of string
|
||
|
CLI11_INLINE std::string &rtrim(std::string &str, const std::string &filter);
|
||
|
|
||
|
/// Trim whitespace from string
|
||
|
inline std::string &trim(std::string &str) { return ltrim(rtrim(str)); }
|
||
|
|
||
|
/// Trim anything from string
|
||
|
inline std::string &trim(std::string &str, const std::string filter) { return ltrim(rtrim(str, filter), filter); }
|
||
|
|
||
|
/// Make a copy of the string and then trim it
|
||
|
inline std::string trim_copy(const std::string &str) {
|
||
|
std::string s = str;
|
||
|
return trim(s);
|
||
|
}
|
||
|
|
||
|
/// remove quotes at the front and back of a string either '"' or '\''
|
||
|
CLI11_INLINE std::string &remove_quotes(std::string &str);
|
||
|
|
||
|
/// remove quotes from all elements of a string vector and process escaped components
|
||
|
CLI11_INLINE void remove_quotes(std::vector<std::string> &args);
|
||
|
|
||
|
/// Add a leader to the beginning of all new lines (nothing is added
|
||
|
/// at the start of the first line). `"; "` would be for ini files
|
||
|
///
|
||
|
/// Can't use Regex, or this would be a subs.
|
||
|
CLI11_INLINE std::string fix_newlines(const std::string &leader, std::string input);
|
||
|
|
||
|
/// Make a copy of the string and then trim it, any filter string can be used (any char in string is filtered)
|
||
|
inline std::string trim_copy(const std::string &str, const std::string &filter) {
|
||
|
std::string s = str;
|
||
|
return trim(s, filter);
|
||
|
}
|
||
|
|
||
|
/// Print subcommand aliases
|
||
|
CLI11_INLINE std::ostream &format_aliases(std::ostream &out, const std::vector<std::string> &aliases, std::size_t wid);
|
||
|
|
||
|
/// Verify the first character of an option
|
||
|
/// - is a trigger character, ! has special meaning and new lines would just be annoying to deal with
|
||
|
template <typename T> bool valid_first_char(T c) {
|
||
|
return ((c != '-') && (static_cast<unsigned char>(c) > 33)); // space and '!' not allowed
|
||
|
}
|
||
|
|
||
|
/// Verify following characters of an option
|
||
|
template <typename T> bool valid_later_char(T c) {
|
||
|
// = and : are value separators, { has special meaning for option defaults,
|
||
|
// and control codes other than tab would just be annoying to deal with in many places allowing space here has too
|
||
|
// much potential for inadvertent entry errors and bugs
|
||
|
return ((c != '=') && (c != ':') && (c != '{') && ((static_cast<unsigned char>(c) > 32) || c == '\t'));
|
||
|
}
|
||
|
|
||
|
/// Verify an option/subcommand name
|
||
|
CLI11_INLINE bool valid_name_string(const std::string &str);
|
||
|
|
||
|
/// Verify an app name
|
||
|
inline bool valid_alias_name_string(const std::string &str) {
|
||
|
return ((str.find_first_of('\n') == std::string::npos) && (str.find_first_of('\0') == std::string::npos));
|
||
|
}
|
||
|
|
||
|
/// check if a string is a container segment separator (empty or "%%")
|
||
|
inline bool is_separator(const std::string &str) {
|
||
|
return (str.empty() || (str.size() == 2 && str[0] == '%' && str[1] == '%'));
|
||
|
}
|
||
|
|
||
|
/// Verify that str consists of letters only
|
||
|
inline bool isalpha(const std::string &str) {
|
||
|
return std::all_of(str.begin(), str.end(), [](char c) { return std::isalpha(c, std::locale()); });
|
||
|
}
|
||
|
|
||
|
/// Return a lower case version of a string
|
||
|
inline std::string to_lower(std::string str) {
|
||
|
std::transform(std::begin(str), std::end(str), std::begin(str), [](const std::string::value_type &x) {
|
||
|
return std::tolower(x, std::locale());
|
||
|
});
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
/// remove underscores from a string
|
||
|
inline std::string remove_underscore(std::string str) {
|
||
|
str.erase(std::remove(std::begin(str), std::end(str), '_'), std::end(str));
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
/// @brief get valid group separators _' + local separator if different
|
||
|
/// @return a string containing the group separators
|
||
|
CLI11_INLINE std::string get_group_separators();
|
||
|
|
||
|
/// Find and replace a substring with another substring
|
||
|
CLI11_INLINE std::string find_and_replace(std::string str, std::string from, std::string to);
|
||
|
|
||
|
/// check if the flag definitions has possible false flags
|
||
|
inline bool has_default_flag_values(const std::string &flags) {
|
||
|
return (flags.find_first_of("{!") != std::string::npos);
|
||
|
}
|
||
|
|
||
|
CLI11_INLINE void remove_default_flag_values(std::string &flags);
|
||
|
|
||
|
/// Check if a string is a member of a list of strings and optionally ignore case or ignore underscores
|
||
|
CLI11_INLINE std::ptrdiff_t find_member(std::string name,
|
||
|
const std::vector<std::string> names,
|
||
|
bool ignore_case = false,
|
||
|
bool ignore_underscore = false);
|
||
|
|
||
|
/// Find a trigger string and call a modify callable function that takes the current string and starting position of the
|
||
|
/// trigger and returns the position in the string to search for the next trigger string
|
||
|
template <typename Callable> inline std::string find_and_modify(std::string str, std::string trigger, Callable modify) {
|
||
|
std::size_t start_pos = 0;
|
||
|
while((start_pos = str.find(trigger, start_pos)) != std::string::npos) {
|
||
|
start_pos = modify(str, start_pos);
|
||
|
}
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
/// close a sequence of characters indicated by a closure character. Brackets allows sub sequences
|
||
|
/// recognized bracket sequences include "'`[(<{ other closure characters are assumed to be literal strings
|
||
|
CLI11_INLINE std::size_t close_sequence(const std::string &str, std::size_t start, char closure_char);
|
||
|
|
||
|
/// Split a string '"one two" "three"' into 'one two', 'three'
|
||
|
/// Quote characters can be ` ' or " or bracket characters [{(< with matching to the matching bracket
|
||
|
CLI11_INLINE std::vector<std::string> split_up(std::string str, char delimiter = '\0');
|
||
|
|
||
|
/// get the value of an environmental variable or empty string if empty
|
||
|
CLI11_INLINE std::string get_environment_value(const std::string &env_name);
|
||
|
|
||
|
/// This function detects an equal or colon followed by an escaped quote after an argument
|
||
|
/// then modifies the string to replace the equality with a space. This is needed
|
||
|
/// to allow the split up function to work properly and is intended to be used with the find_and_modify function
|
||
|
/// the return value is the offset+1 which is required by the find_and_modify function.
|
||
|
CLI11_INLINE std::size_t escape_detect(std::string &str, std::size_t offset);
|
||
|
|
||
|
/// @brief detect if a string has escapable characters
|
||
|
/// @param str the string to do the detection on
|
||
|
/// @return true if the string has escapable characters
|
||
|
CLI11_INLINE bool has_escapable_character(const std::string &str);
|
||
|
|
||
|
/// @brief escape all escapable characters
|
||
|
/// @param str the string to escape
|
||
|
/// @return a string with the escapable characters escaped with '\'
|
||
|
CLI11_INLINE std::string add_escaped_characters(const std::string &str);
|
||
|
|
||
|
/// @brief replace the escaped characters with their equivalent
|
||
|
CLI11_INLINE std::string remove_escaped_characters(const std::string &str);
|
||
|
|
||
|
/// generate a string with all non printable characters escaped to hex codes
|
||
|
CLI11_INLINE std::string binary_escape_string(const std::string &string_to_escape, bool force = false);
|
||
|
|
||
|
CLI11_INLINE bool is_binary_escaped_string(const std::string &escaped_string);
|
||
|
|
||
|
/// extract an escaped binary_string
|
||
|
CLI11_INLINE std::string extract_binary_string(const std::string &escaped_string);
|
||
|
|
||
|
/// process a quoted string, remove the quotes and if appropriate handle escaped characters
|
||
|
CLI11_INLINE bool process_quoted_string(std::string &str,
|
||
|
char string_char = '\"',
|
||
|
char literal_char = '\'',
|
||
|
bool disable_secondary_array_processing = false);
|
||
|
|
||
|
/// This function formats the given text as a paragraph with fixed width and applies correct line wrapping
|
||
|
/// with a custom line prefix. The paragraph will get streamed to the given ostream.
|
||
|
CLI11_INLINE std::ostream &streamOutAsParagraph(std::ostream &out,
|
||
|
const std::string &text,
|
||
|
std::size_t paragraphWidth,
|
||
|
const std::string &linePrefix = "",
|
||
|
bool skipPrefixOnFirstLine = false);
|
||
|
|
||
|
} // namespace detail
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
namespace detail {
|
||
|
CLI11_INLINE std::vector<std::string> split(const std::string &s, char delim) {
|
||
|
std::vector<std::string> elems;
|
||
|
// Check to see if empty string, give consistent result
|
||
|
if(s.empty()) {
|
||
|
elems.emplace_back();
|
||
|
} else {
|
||
|
std::stringstream ss;
|
||
|
ss.str(s);
|
||
|
std::string item;
|
||
|
while(std::getline(ss, item, delim)) {
|
||
|
elems.push_back(item);
|
||
|
}
|
||
|
}
|
||
|
return elems;
|
||
|
}
|
||
|
|
||
|
CLI11_INLINE std::string <rim(std::string &str) {
|
||
|
auto it = std::find_if(str.begin(), str.end(), [](char ch) { return !std::isspace<char>(ch, std::locale()); });
|
||
|
str.erase(str.begin(), it);
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
CLI11_INLINE std::string <rim(std::string &str, const std::string &filter) {
|
||
|
auto it = std::find_if(str.begin(), str.end(), [&filter](char ch) { return filter.find(ch) == std::string::npos; });
|
||
|
str.erase(str.begin(), it);
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
CLI11_INLINE std::string &rtrim(std::string &str) {
|
||
|
auto it = std::find_if(str.rbegin(), str.rend(), [](char ch) { return !std::isspace<char>(ch, std::locale()); });
|
||
|
str.erase(it.base(), str.end());
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
CLI11_INLINE std::string &rtrim(std::string &str, const std::string &filter) {
|
||
|
auto it =
|
||
|
std::find_if(str.rbegin(), str.rend(), [&filter](char ch) { return filter.find(ch) == std::string::npos; });
|
||
|
str.erase(it.base(), str.end());
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
CLI11_INLINE std::string &remove_quotes(std::string &str) {
|
||
|
if(str.length() > 1 && (str.front() == '"' || str.front() == '\'' || str.front() == '`')) {
|
||
|
if(str.front() == str.back()) {
|
||
|
str.pop_back();
|
||
|
str.erase(str.begin(), str.begin() + 1);
|
||
|
}
|
||
|
}
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
CLI11_INLINE std::string &remove_outer(std::string &str, char key) {
|
||
|
if(str.length() > 1 && (str.front() == key)) {
|
||
|
if(str.front() == str.back()) {
|
||
|
str.pop_back();
|
||
|
str.erase(str.begin(), str.begin() + 1);
|
||
|
}
|
||
|
}
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
CLI11_INLINE std::string fix_newlines(const std::string &leader, std::string input) {
|
||
|
std::string::size_type n = 0;
|
||
|
while(n != std::string::npos && n < input.size()) {
|
||
|
n = input.find_first_of("\r\n", n);
|
||
|
if(n != std::string::npos) {
|
||
|
input = input.substr(0, n + 1) + leader + input.substr(n + 1);
|
||
|
n += leader.size();
|
||
|
}
|
||
|
}
|
||
|
return input;
|
||
|
}
|
||
|
|
||
|
CLI11_INLINE std::ostream &format_aliases(std::ostream &out, const std::vector<std::string> &aliases, std::size_t wid) {
|
||
|
if(!aliases.empty()) {
|
||
|
out << std::setw(static_cast<int>(wid)) << " aliases: ";
|
||
|
bool front = true;
|
||
|
for(const auto &alias : aliases) {
|
||
|
if(!front) {
|
||
|
out << ", ";
|
||
|
} else {
|
||
|
front = false;
|
||
|
}
|
||
|
out << detail::fix_newlines(" ", alias);
|
||
|
}
|
||
|
out << "\n";
|
||
|
}
|
||
|
return out;
|
||
|
}
|
||
|
|
||
|
CLI11_INLINE bool valid_name_string(const std::string &str) {
|
||
|
if(str.empty() || !valid_first_char(str[0])) {
|
||
|
return false;
|
||
|
}
|
||
|
auto e = str.end();
|
||
|
for(auto c = str.begin() + 1; c != e; ++c)
|
||
|
if(!valid_later_char(*c))
|
||
|
return false;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
CLI11_INLINE std::string get_group_separators() {
|
||
|
std::string separators{"_'"};
|
||
|
#if CLI11_HAS_RTTI != 0
|
||
|
char group_separator = std::use_facet<std::numpunct<char>>(std::locale()).thousands_sep();
|
||
|
separators.push_back(group_separator);
|
||
|
#endif
|
||
|
return separators;
|
||
|
}
|
||
|
|
||
|
CLI11_INLINE std::string find_and_replace(std::string str, std::string from, std::string to) {
|
||
|
|
||
|
std::size_t start_pos = 0;
|
||
|
|
||
|
while((start_pos = str.find(from, start_pos)) != std::string::npos) {
|
||
|
str.replace(start_pos, from.length(), to);
|
||
|
start_pos += to.length();
|
||
|
}
|
||
|
|
||
|
return str;
|
||
|
}
|
||
|
|
||
|
CLI11_INLINE void remove_default_flag_values(std::string &flags) {
|
||
|
auto loc = flags.find_first_of('{', 2);
|
||
|
while(loc != std::string::npos) {
|
||
|
auto finish = flags.find_first_of("},", loc + 1);
|
||
|
if((finish != std::string::npos) && (flags[finish] == '}')) {
|
||
|
flags.erase(flags.begin() + static_cast<std::ptrdiff_t>(loc),
|
||
|
flags.begin() + static_cast<std::ptrdiff_t>(finish) + 1);
|
||
|
}
|
||
|
loc = flags.find_first_of('{', loc + 1);
|
||
|
}
|
||
|
flags.erase(std::remove(flags.begin(), flags.end(), '!'), flags.end());
|
||
|
}
|
||
|
|
||
|
CLI11_INLINE std::ptrdiff_t
|
||
|
find_member(std::string name, const std::vector<std::string> names, bool ignore_case, bool ignore_underscore) {
|
||
|
auto it = std::end(names);
|
||
|
if(ignore_case) {
|
||
|
if(ignore_underscore) {
|
||
|
name = detail::to_lower(detail::remove_underscore(name));
|
||
|
it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) {
|
||
|
return detail::to_lower(detail::remove_underscore(local_name)) == name;
|
||
|
});
|
||
|
} else {
|
||
|
name = detail::to_lower(name);
|
||
|
it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) {
|
||
|
return detail::to_lower(local_name) == name;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
} else if(ignore_underscore) {
|
||
|
name = detail::remove_underscore(name);
|
||
|
it = std::find_if(std::begin(names), std::end(names), [&name](std::string local_name) {
|
||
|
return detail::remove_underscore(local_name) == name;
|
||
|
});
|
||
|
} else {
|
||
|
it = std::find(std::begin(names), std::end(names), name);
|
||
|
}
|
||
|
|
||
|
return (it != std::end(names)) ? (it - std::begin(names)) : (-1);
|
||
|
}
|
||
|
|
||
|
static const std::string escapedChars("\b\t\n\f\r\"\\");
|
||
|
static const std::string escapedCharsCode("btnfr\"\\");
|
||
|
static const std::string bracketChars{"\"'`[(<{"};
|
||
|
static const std::string matchBracketChars("\"'`])>}");
|
||
|
|
||
|
CLI11_INLINE bool has_escapable_character(const std::string &str) {
|
||
|
return (str.find_first_of(escapedChars) != std::string::npos);
|
||
|
}
|
||
|
|
||
|
CLI11_INLINE std::string add_escaped_characters(const std::string &str) {
|
||
|
std::string out;
|
||
|
out.reserve(str.size() + 4);
|
||
|
for(char s : str) {
|
||
|
auto sloc = escapedChars.find_first_of(s);
|
||
|
if(sloc != std::string::npos) {
|
||
|
out.push_back('\\');
|
||
|
out.push_back(escapedCharsCode[sloc]);
|
||
|
} else {
|
||
|
out.push_back(s);
|
||
|
}
|
||
|
}
|
||
|
return out;
|
||
|
}
|
||
|
|
||
|
CLI11_INLINE std::uint32_t hexConvert(char hc) {
|
||
|
int hcode{0};
|
||
|
if(hc >= '0' && hc <= '9') {
|
||
|
hcode = (hc - '0');
|
||
|
} else if(hc >= 'A' && hc <= 'F') {
|
||
|
hcode = (hc - 'A' + 10);
|
||
|
} else if(hc >= 'a' && hc <= 'f') {
|
||
|
hcode = (hc - 'a' + 10);
|
||
|
} else {
|
||
|
hcode = -1;
|
||
|
}
|
||
|
return static_cast<uint32_t>(hcode);
|
||
|
}
|
||
|
|
||
|
CLI11_INLINE char make_char(std::uint32_t code) { return static_cast<char>(static_cast<unsigned char>(code)); }
|
||
|
|
||
|
CLI11_INLINE void append_codepoint(std::string &str, std::uint32_t code) {
|
||
|
if(code < 0x80) { // ascii code equivalent
|
||
|
str.push_back(static_cast<char>(code));
|
||
|
} else if(code < 0x800) { // \u0080 to \u07FF
|
||
|
// 110yyyyx 10xxxxxx; 0x3f == 0b0011'1111
|
||
|
str.push_back(make_char(0xC0 | code >> 6));
|
||
|
str.push_back(make_char(0x80 | (code & 0x3F)));
|
||
|
} else if(code < 0x10000) { // U+0800...U+FFFF
|
||
|
if(0xD800 <= code && code <= 0xDFFF) {
|
||
|
throw std::invalid_argument("[0xD800, 0xDFFF] are not valid UTF-8.");
|
||
|
}
|
||
|
// 1110yyyy 10yxxxxx 10xxxxxx
|
||
|
str.push_back(make_char(0xE0 | code >> 12));
|
||
|
str.push_back(make_char(0x80 | (code >> 6 & 0x3F)));
|
||
|
str.push_back(make_char(0x80 | (code & 0x3F)));
|
||
|
} else if(code < 0x110000) { // U+010000 ... U+10FFFF
|
||
|
// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx
|
||
|
str.push_back(make_char(0xF0 | code >> 18));
|
||
|
str.push_back(make_char(0x80 | (code >> 12 & 0x3F)));
|
||
|
str.push_back(make_char(0x80 | (code >> 6 & 0x3F)));
|
||
|
str.push_back(make_char(0x80 | (code & 0x3F)));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CLI11_INLINE std::string remove_escaped_characters(const std::string &str) {
|
||
|
|
||
|
std::string out;
|
||
|
out.reserve(str.size());
|
||
|
for(auto loc = str.begin(); loc < str.end(); ++loc) {
|
||
|
if(*loc == '\\') {
|
||
|
if(str.end() - loc < 2) {
|
||
|
throw std::invalid_argument("invalid escape sequence " + str);
|
||
|
}
|
||
|
auto ecloc = escapedCharsCode.find_first_of(*(loc + 1));
|
||
|
if(ecloc != std::string::npos) {
|
||
|
out.push_back(escapedChars[ecloc]);
|
||
|
++loc;
|
||
|
} else if(*(loc + 1) == 'u') {
|
||
|
// must have 4 hex characters
|
||
|
if(str.end() - loc < 6) {
|
||
|
throw std::invalid_argument("unicode sequence must have 4 hex codes " + str);
|
||
|
}
|
||
|
std::uint32_t code{0};
|
||
|
std::uint32_t mplier{16 * 16 * 16};
|
||
|
for(int ii = 2; ii < 6; ++ii) {
|
||
|
std::uint32_t res = hexConvert(*(loc + ii));
|
||
|
if(res > 0x0F) {
|
||
|
throw std::invalid_argument("unicode sequence must have 4 hex codes " + str);
|
||
|
}
|
||
|
code += res * mplier;
|
||
|
mplier = mplier / 16;
|
||
|
}
|
||
|
append_codepoint(out, code);
|
||
|
loc += 5;
|
||
|
} else if(*(loc + 1) == 'U') {
|
||
|
// must have 8 hex characters
|
||
|
if(str.end() - loc < 10) {
|
||
|
throw std::invalid_argument("unicode sequence must have 8 hex codes " + str);
|
||
|
}
|
||
|
std::uint32_t code{0};
|
||
|
std::uint32_t mplier{16 * 16 * 16 * 16 * 16 * 16 * 16};
|
||
|
for(int ii = 2; ii < 10; ++ii) {
|
||
|
std::uint32_t res = hexConvert(*(loc + ii));
|
||
|
if(res > 0x0F) {
|
||
|
throw std::invalid_argument("unicode sequence must have 8 hex codes " + str);
|
||
|
}
|
||
|
code += res * mplier;
|
||
|
mplier = mplier / 16;
|
||
|
}
|
||
|
append_codepoint(out, code);
|
||
|
loc += 9;
|
||
|
} else if(*(loc + 1) == '0') {
|
||
|
out.push_back('\0');
|
||
|
++loc;
|
||
|
} else {
|
||
|
throw std::invalid_argument(std::string("unrecognized escape sequence \\") + *(loc + 1) + " in " + str);
|
||
|
}
|
||
|
} else {
|
||
|
out.push_back(*loc);
|
||
|
}
|
||
|
}
|
||
|
return out;
|
||
|
}
|
||
|
|
||
|
CLI11_INLINE std::size_t close_string_quote(const std::string &str, std::size_t start, char closure_char) {
|
||
|
std::size_t loc{0};
|
||
|
for(loc = start + 1; loc < str.size(); ++loc) {
|
||
|
if(str[loc] == closure_char) {
|
||
|
break;
|
||
|
}
|
||
|
if(str[loc] == '\\') {
|
||
|
// skip the next character for escaped sequences
|
||
|
++loc;
|
||
|
}
|
||
|
}
|
||
|
return loc;
|
||
|
}
|
||
|
|
||
|
CLI11_INLINE std::size_t close_literal_quote(const std::string &str, std::size_t start, char closure_char) {
|
||
|
auto loc = str.find_first_of(closure_char, start + 1);
|
||
|
return (loc != std::string::npos ? loc : str.size());
|
||
|
}
|
||
|
|
||
|
CLI11_INLINE std::size_t close_sequence(const std::string &str, std::size_t start, char closure_char) {
|
||
|
|
||
|
auto bracket_loc = matchBracketChars.find(closure_char);
|
||
|
switch(bracket_loc) {
|
||
|
case 0:
|
||
|
return close_string_quote(str, start, closure_char);
|
||
|
case 1:
|
||
|
case 2:
|
||
|
#if defined(_MSC_VER) && _MSC_VER < 1920
|
||
|
case(std::size_t)-1:
|
||
|
#else
|
||
|
case std::string::npos:
|
||
|
#endif
|
||
|
return close_literal_quote(str, start, closure_char);
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
std::string closures(1, closure_char);
|
||
|
auto loc = start + 1;
|
||
|
|
||
|
while(loc < str.size()) {
|
||
|
if(str[loc] == closures.back()) {
|
||
|
closures.pop_back();
|
||
|
if(closures.empty()) {
|
||
|
return loc;
|
||
|
}
|
||
|
}
|
||
|
bracket_loc = bracketChars.find(str[loc]);
|
||
|
if(bracket_loc != std::string::npos) {
|
||
|
switch(bracket_loc) {
|
||
|
case 0:
|
||
|
loc = close_string_quote(str, loc, str[loc]);
|
||
|
break;
|
||
|
case 1:
|
||
|
case 2:
|
||
|
loc = close_literal_quote(str, loc, str[loc]);
|
||
|
break;
|
||
|
default:
|
||
|
closures.push_back(matchBracketChars[bracket_loc]);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
++loc;
|
||
|
}
|
||
|
if(loc > str.size()) {
|
||
|
loc = str.size();
|
||
|
}
|
||
|
return loc;
|
||
|
}
|
||
|
|
||
|
CLI11_INLINE std::vector<std::string> split_up(std::string str, char delimiter) {
|
||
|
|
||
|
auto find_ws = [delimiter](char ch) {
|
||
|
return (delimiter == '\0') ? std::isspace<char>(ch, std::locale()) : (ch == delimiter);
|
||
|
};
|
||
|
trim(str);
|
||
|
|
||
|
std::vector<std::string> output;
|
||
|
while(!str.empty()) {
|
||
|
if(bracketChars.find_first_of(str[0]) != std::string::npos) {
|
||
|
auto bracketLoc = bracketChars.find_first_of(str[0]);
|
||
|
auto end = close_sequence(str, 0, matchBracketChars[bracketLoc]);
|
||
|
if(end >= str.size()) {
|
||
|
output.push_back(std::move(str));
|
||
|
str.clear();
|
||
|
} else {
|
||
|
output.push_back(str.substr(0, end + 1));
|
||
|
if(end + 2 < str.size()) {
|
||
|
str = str.substr(end + 2);
|
||
|
} else {
|
||
|
str.clear();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
auto it = std::find_if(std::begin(str), std::end(str), find_ws);
|
||
|
if(it != std::end(str)) {
|
||
|
std::string value = std::string(str.begin(), it);
|
||
|
output.push_back(value);
|
||
|
str = std::string(it + 1, str.end());
|
||
|
} else {
|
||
|
output.push_back(str);
|
||
|
str.clear();
|
||
|
}
|
||
|
}
|
||
|
trim(str);
|
||
|
}
|
||
|
return output;
|
||
|
}
|
||
|
|
||
|
CLI11_INLINE std::size_t escape_detect(std::string &str, std::size_t offset) {
|
||
|
auto next = str[offset + 1];
|
||
|
if((next == '\"') || (next == '\'') || (next == '`')) {
|
||
|
auto astart = str.find_last_of("-/ \"\'`", offset - 1);
|
||
|
if(astart != std::string::npos) {
|
||
|
if(str[astart] == ((str[offset] == '=') ? '-' : '/'))
|
||
|
str[offset] = ' '; // interpret this as a space so the split_up works properly
|
||
|
}
|
||
|
}
|
||
|
return offset + 1;
|
||
|
}
|
||
|
|
||
|
CLI11_INLINE std::string binary_escape_string(const std::string &string_to_escape, bool force) {
|
||
|
// s is our escaped output string
|
||
|
std::string escaped_string{};
|
||
|
// loop through all characters
|
||
|
for(char c : string_to_escape) {
|
||
|
// check if a given character is printable
|
||
|
// the cast is necessary to avoid undefined behaviour
|
||
|
if(isprint(static_cast<unsigned char>(c)) == 0) {
|
||
|
std::stringstream stream;
|
||
|
// if the character is not printable
|
||
|
// we'll convert it to a hex string using a stringstream
|
||
|
// note that since char is signed we have to cast it to unsigned first
|
||
|
stream << std::hex << static_cast<unsigned int>(static_cast<unsigned char>(c));
|
||
|
std::string code = stream.str();
|
||
|
escaped_string += std::string("\\x") + (code.size() < 2 ? "0" : "") + code;
|
||
|
} else if(c == 'x' || c == 'X') {
|
||
|
// need to check for inadvertent binary sequences
|
||
|
if(!escaped_string.empty() && escaped_string.back() == '\\') {
|
||
|
escaped_string += std::string("\\x") + (c == 'x' ? "78" : "58");
|
||
|
} else {
|
||
|
escaped_string.push_back(c);
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
escaped_string.push_back(c);
|
||
|
}
|
||
|
}
|
||
|
if(escaped_string != string_to_escape || force) {
|
||
|
auto sqLoc = escaped_string.find('\'');
|
||
|
while(sqLoc != std::string::npos) {
|
||
|
escaped_string[sqLoc] = '\\';
|
||
|
escaped_string.insert(sqLoc + 1, "x27");
|
||
|
sqLoc = escaped_string.find('\'');
|
||
|
}
|
||
|
escaped_string.insert(0, "'B\"(");
|
||
|
escaped_string.push_back(')');
|
||
|
escaped_string.push_back('"');
|
||
|
escaped_string.push_back('\'');
|
||
|
}
|
||
|
return escaped_string;
|
||
|
}
|
||
|
|
||
|
CLI11_INLINE bool is_binary_escaped_string(const std::string &escaped_string) {
|
||
|
size_t ssize = escaped_string.size();
|
||
|
if(escaped_string.compare(0, 3, "B\"(") == 0 && escaped_string.compare(ssize - 2, 2, ")\"") == 0) {
|
||
|
return true;
|
||
|
}
|
||
|
return (escaped_string.compare(0, 4, "'B\"(") == 0 && escaped_string.compare(ssize - 3, 3, ")\"'") == 0);
|
||
|
}
|
||
|
|
||
|
CLI11_INLINE std::string extract_binary_string(const std::string &escaped_string) {
|
||
|
std::size_t start{0};
|
||
|
std::size_t tail{0};
|
||
|
size_t ssize = escaped_string.size();
|
||
|
if(escaped_string.compare(0, 3, "B\"(") == 0 && escaped_string.compare(ssize - 2, 2, ")\"") == 0) {
|
||
|
start = 3;
|
||
|
tail = 2;
|
||
|
} else if(escaped_string.compare(0, 4, "'B\"(") == 0 && escaped_string.compare(ssize - 3, 3, ")\"'") == 0) {
|
||
|
start = 4;
|
||
|
tail = 3;
|
||
|
}
|
||
|
|
||
|
if(start == 0) {
|
||
|
return escaped_string;
|
||
|
}
|
||
|
std::string outstring;
|
||
|
|
||
|
outstring.reserve(ssize - start - tail);
|
||
|
std::size_t loc = start;
|
||
|
while(loc < ssize - tail) {
|
||
|
// ssize-2 to skip )" at the end
|
||
|
if(escaped_string[loc] == '\\' && (escaped_string[loc + 1] == 'x' || escaped_string[loc + 1] == 'X')) {
|
||
|
auto c1 = escaped_string[loc + 2];
|
||
|
auto c2 = escaped_string[loc + 3];
|
||
|
|
||
|
std::uint32_t res1 = hexConvert(c1);
|
||
|
std::uint32_t res2 = hexConvert(c2);
|
||
|
if(res1 <= 0x0F && res2 <= 0x0F) {
|
||
|
loc += 4;
|
||
|
outstring.push_back(static_cast<char>(res1 * 16 + res2));
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
outstring.push_back(escaped_string[loc]);
|
||
|
++loc;
|
||
|
}
|
||
|
return outstring;
|
||
|
}
|
||
|
|
||
|
CLI11_INLINE void remove_quotes(std::vector<std::string> &args) {
|
||
|
for(auto &arg : args) {
|
||
|
if(arg.front() == '\"' && arg.back() == '\"') {
|
||
|
remove_quotes(arg);
|
||
|
// only remove escaped for string arguments not literal strings
|
||
|
arg = remove_escaped_characters(arg);
|
||
|
} else {
|
||
|
remove_quotes(arg);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CLI11_INLINE void handle_secondary_array(std::string &str) {
|
||
|
if(str.size() >= 2 && str.front() == '[' && str.back() == ']') {
|
||
|
// handle some special array processing for arguments if it might be interpreted as a secondary array
|
||
|
std::string tstr{"[["};
|
||
|
for(std::size_t ii = 1; ii < str.size(); ++ii) {
|
||
|
tstr.push_back(str[ii]);
|
||
|
tstr.push_back(str[ii]);
|
||
|
}
|
||
|
str = std::move(tstr);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CLI11_INLINE bool
|
||
|
process_quoted_string(std::string &str, char string_char, char literal_char, bool disable_secondary_array_processing) {
|
||
|
if(str.size() <= 1) {
|
||
|
return false;
|
||
|
}
|
||
|
if(detail::is_binary_escaped_string(str)) {
|
||
|
str = detail::extract_binary_string(str);
|
||
|
if(!disable_secondary_array_processing)
|
||
|
handle_secondary_array(str);
|
||
|
return true;
|
||
|
}
|
||
|
if(str.front() == string_char && str.back() == string_char) {
|
||
|
detail::remove_outer(str, string_char);
|
||
|
if(str.find_first_of('\\') != std::string::npos) {
|
||
Moving Photo Compress Archiver to its own repo. Last commit was main