From 004e86949de8c913ca9605bd19a6bba356c41488 Mon Sep 17 00:00:00 2001 From: Dimitre Date: Thu, 13 Apr 2023 14:53:21 -0300 Subject: [PATCH 01/25] changes --- commandLine/Project.xcconfig | 1 + commandLine/bin/data/.gitkeep | 0 .../commandLine.xcodeproj/project.pbxproj | 4 +- .../xcschemes/commandLine Debug.xcscheme | 31 +- .../xcschemes/commandLine Release.xcscheme | 10 + commandLine/src/addons/ofAddon.cpp | 18 +- commandLine/src/main.cpp | 60 +- commandLine/src/optionparser.h | 2120 +++++++++-------- commandLine/src/utils/Utils.cpp | 5 +- 9 files changed, 1167 insertions(+), 1082 deletions(-) delete mode 100644 commandLine/bin/data/.gitkeep diff --git a/commandLine/Project.xcconfig b/commandLine/Project.xcconfig index 676e6c1c..a3ed5d7e 100644 --- a/commandLine/Project.xcconfig +++ b/commandLine/Project.xcconfig @@ -1,6 +1,7 @@ //THE PATH TO THE ROOT OF OUR OF PATH RELATIVE TO THIS PROJECT. //THIS NEEDS TO BE DEFINED BEFORE CoreOF.xcconfig IS INCLUDED OF_PATH = ../../.. +//PG_OF_PATH = /Volumes/tool/ofw //THIS HAS ALL THE HEADER AND LIBS FOR OF CORE #include "../../../libs/openFrameworksCompiled/project/osx/CoreOF.xcconfig" diff --git a/commandLine/bin/data/.gitkeep b/commandLine/bin/data/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/commandLine/commandLine.xcodeproj/project.pbxproj b/commandLine/commandLine.xcodeproj/project.pbxproj index 59e911d9..00d1e06e 100644 --- a/commandLine/commandLine.xcodeproj/project.pbxproj +++ b/commandLine/commandLine.xcodeproj/project.pbxproj @@ -35,6 +35,7 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 19186B9529E8694F001D118A /* cxxopts.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = cxxopts.hpp; sourceTree = ""; }; 19A3790829E697D900D6501F /* optionparser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = optionparser.h; sourceTree = ""; }; 19A3790A29E697D900D6501F /* visualStudioProject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = visualStudioProject.cpp; sourceTree = ""; }; 19A3790B29E697D900D6501F /* baseProject.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = baseProject.h; sourceTree = ""; }; @@ -87,6 +88,7 @@ 19A3792929E697D900D6501F /* main.cpp */, 19A3792129E697D900D6501F /* ofApp.cpp */, 19A3792229E697D900D6501F /* ofApp.h */, + 19186B9529E8694F001D118A /* cxxopts.hpp */, 19A3790829E697D900D6501F /* optionparser.h */, 19A3790929E697D900D6501F /* projects */, 19A3791B29E697D900D6501F /* utils */, @@ -236,7 +238,6 @@ runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "cp \"$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/MacOS/$PRODUCT_NAME\" \"$TARGET_BUILD_DIR/projectGenerator\";\t\n"; - showEnvVarsInLog = 0; }; E42962A92163ECCD00A6A9E2 /* Run Script - Compile OF */ = { isa = PBXShellScriptBuildPhase; @@ -320,7 +321,6 @@ baseConfigurationReference = E4EB6923138AFD0F00A09F29 /* Project.xcconfig */; buildSettings = { COMBINE_HIDPI_IMAGES = YES; - COPY_PHASE_STRIP = YES; FRAMEWORK_SEARCH_PATHS = "$(inherited)"; GCC_GENERATE_DEBUGGING_SYMBOLS = YES; GCC_MODEL_TUNING = NONE; diff --git a/commandLine/commandLine.xcodeproj/xcshareddata/xcschemes/commandLine Debug.xcscheme b/commandLine/commandLine.xcodeproj/xcshareddata/xcschemes/commandLine Debug.xcscheme index 3ae4128d..80008d9c 100644 --- a/commandLine/commandLine.xcodeproj/xcshareddata/xcschemes/commandLine Debug.xcscheme +++ b/commandLine/commandLine.xcodeproj/xcshareddata/xcschemes/commandLine Debug.xcscheme @@ -23,12 +23,10 @@ - - + shouldUseLaunchSchemeArgsEnv = "YES"> + + - + - - + + + + + + - + + + + + + + libFiles; @@ -578,7 +562,7 @@ bool ofAddon::fromFS(fs::path path, const std::string & platform){ // cout << "NO " << fs::current_path() << endl; } -// cout << "libFiles " << endl; + for (auto & s : libFiles) { fs::path folder; auto srcFS = fs::path(prefixPath / fs::relative(s, containedPath)); diff --git a/commandLine/src/main.cpp b/commandLine/src/main.cpp index cc6717ca..d8ebc19f 100644 --- a/commandLine/src/main.cpp +++ b/commandLine/src/main.cpp @@ -1,6 +1,10 @@ //#include "ofMain.h" +#include "cxxopts.hpp" + #include "optionparser.h" #include "defines.h" + +cxxopts::Options options("Project Generator", "OpenFrameworks tool to generate projects"); enum optionIndex { UNKNOWN, HELP, PLUS, RECURSIVE, LISTTEMPLATES, PLATFORMS, ADDONS, OFPATH, VERBOSE, TEMPLATE, DRYRUN, SRCEXTERNAL, VERSION}; constexpr option::Descriptor usage[] = @@ -9,13 +13,13 @@ constexpr option::Descriptor usage[] = {HELP, 0,"h", "help",option::Arg::None, " --help \tPrint usage and exit." }, {RECURSIVE, 0,"r","recursive",option::Arg::None, " --recursive, -r \tupdate recursively (applies only to update)" }, {LISTTEMPLATES, 0,"l","listtemplates",option::Arg::None, " --listtemplates, -l \tlist templates available for the specified or current platform(s)" }, - {PLATFORMS, 0,"p","platforms",option::Arg::Optional, " --platforms, -p \tplatform list (such as osx, ios, winvs)" }, - {ADDONS, 0,"a","addons",option::Arg::Optional, " --addons, -a \taddon list (such as ofxOpenCv, ofxGui, ofxXmlSettings)" }, - {OFPATH, 0,"o","ofPath",option::Arg::Optional, " --ofPath, -o \tpath to openframeworks (relative or absolute). This *must* be set, or you can also alternatively use an environment variable PG_OF_PATH and if this isn't set, it will use that value instead" }, + {PLATFORMS, 0,"p","platforms",option::Arg::None, " --platforms, -p \tplatform list (such as osx, ios, winvs)" }, + {ADDONS, 0,"a","addons",option::Arg::None, " --addons, -a \taddon list (such as ofxOpenCv, ofxGui, ofxXmlSettings)" }, + {OFPATH, 0,"o","ofPath",option::Arg::None, " --ofPath, -o \tpath to openframeworks (relative or absolute). This *must* be set, or you can also alternatively use an environment variable PG_OF_PATH and if this isn't set, it will use that value instead" }, {VERBOSE, 0,"v","verbose",option::Arg::None, " --verbose, -v \trun verbose" }, - {TEMPLATE, 0,"t","template",option::Arg::Optional, " --template, -t \tproject template" }, + {TEMPLATE, 0,"t","template",option::Arg::None, " --template, -t \tproject template" }, {DRYRUN, 0,"d","dryrun",option::Arg::None, " --dryrun, -d \tdry run, don't change files" }, - {SRCEXTERNAL, 0,"s","source",option::Arg::Optional, " --source, -s \trelative or absolute path to source or include folders external to the project (such as ../../../../common_utils/" }, + {SRCEXTERNAL, 0,"s","source",option::Arg::None, " --source, -s \trelative or absolute path to source or include folders external to the project (such as ../../../../common_utils/" }, {VERSION, 0, "w", "version", option::Arg::None, " --version, -w \treturn the current version"}, {0,0,0,0,0,0} }; @@ -26,6 +30,9 @@ constexpr option::Descriptor usage[] = #include "androidStudioProject.h" #include "Utils.h" +using std::cout; +using std::endl; + namespace fs = of::filesystem; #define EXIT_OK 0 @@ -337,8 +344,6 @@ void printHelp(){ //------------------------------------------- int main(int argc, char* argv[]){ - - //------------------------------------------- pre-parse bAddonsPassedIn = false; bDryRun = false; @@ -356,24 +361,45 @@ int main(int argc, char* argv[]){ std::string projectName = ""; projectPath = ""; templateName = ""; + ofPath = ""; - +// std::exit(0); + + options + .allow_unrecognised_options() + .add_options() + ("o,ofPath", "path to openframeworks (relative or absolute). This *must* be set, or you can also alternatively use an environment variable PG_OF_PATH and if this isn't set, it will use that value instead") // a bool parameter + ("i,integer", "Int param", cxxopts::value()) + ("f,file", "File name", cxxopts::value()) + ("v,verbose", "Verbose output", cxxopts::value()->default_value("false")) + ; + +// auto result = options.parse(argc, argv); +// cout << result["ofPath"].as() << endl; + + cout << 1 << endl; // ------------------------------------------------------ parse args argc-=(argc>0); argv+=(argc>0); // skip program name argv[0] if present option::Stats stats(usage, argc, argv); std::vector options(stats.options_max); std::vector buffer(stats.buffer_max); option::Parser parse(usage, argc, argv, &options[0], &buffer[0]); + + cout << 2 << endl; + if (parse.error()) { return 1; } + cout << 3 << endl; + if (options[HELP] || argc == 0) { ofLogError() << "No arguments"; printHelp(); return EXIT_OK; } + cout << 4 << endl; // templates: if (options[LISTTEMPLATES].count() > 0){ @@ -391,7 +417,7 @@ int main(int argc, char* argv[]){ printVersion(); return EXIT_OK; } - + cout << 5 << endl; if (options[VERBOSE].count() > 0){ bVerbose = true; } @@ -433,13 +459,14 @@ int main(int argc, char* argv[]){ } + if (parse.nonOptionsCount() > 0){ projectName = parse.nonOption(0); } - // ------------------------------------------------------ post parse - + + cout << "start" << endl; nProjectsUpdated = 0; nProjectsCreated = 0; of::priv::initutils(); @@ -457,9 +484,11 @@ int main(int argc, char* argv[]){ busingEnvVar = true; ofPath = ofPathEnv; } + + currentWorkingDirectory = fs::current_path().string(); - + if (ofPath == "") { consoleSpace(); @@ -476,8 +505,12 @@ int main(int argc, char* argv[]){ // convert ofpath from relative to absolute by appending this to current path and calculating .. by canonical. // FIXME: convert ofPath and functions to fs::path + + cout << "cwd = " << fs::current_path() << endl; + if (!fs::path(ofPath).is_absolute()) { - ofPath = fs::canonical(fs::current_path() / fs::path(ofPath)).string(); + ofPath = (fs::current_path() / fs::path(ofPath)).lexically_normal().string(); +// ofPath = fs::canonical(fs::current_path() / fs::path(ofPath)).string(); } @@ -486,6 +519,7 @@ int main(int argc, char* argv[]){ return EXIT_USAGE; } + setOFRoot(ofPath); diff --git a/commandLine/src/optionparser.h b/commandLine/src/optionparser.h index 5ad67a4e..7cabb2f8 100644 --- a/commandLine/src/optionparser.h +++ b/commandLine/src/optionparser.h @@ -1,7 +1,7 @@ /* * The Lean Mean C++ Option Parser * - * Copyright (C) 2012 Matthias S. Benkmann + * Copyright (C) 2012-2017 Matthias S. Benkmann * * The "Software" in the following 2 paragraphs refers to this file containing * the code to The Lean Mean C++ Option Parser. @@ -43,11 +43,13 @@ * @brief This is the only file required to use The Lean Mean C++ Option Parser. * Just \#include it and you're set. * - * The Lean Mean C++ Option Parser handles the program's command line arguments + * The Lean Mean C++ Option Parser handles the program's command line arguments * (argc, argv). - * It supports the short and long option formats of getopt(), getopt_long() + * It supports the short and long option formats of getopt(), getopt_long() * and getopt_long_only() but has a more convenient interface. - * The following features set it apart from other option parsers: + * + * @par Feedback: + * Send questions, bug reports, feature requests etc. to: optionparser-feedback(a)lists.sourceforge.net * * @par Highlights: *
    @@ -82,18 +84,23 @@ * @endcode *
* @n - * Despite these features the code size remains tiny. + * Despite these features the code size remains tiny. * It is smaller than uClibc's GNU getopt() and just a * couple 100 bytes larger than uClibc's SUSv3 getopt(). @n * (This does not include the usage formatter, of course. But you don't have to use that.) * * @par Download: * Tarball with examples and test programs: - * optionparser-1.3.tar.gz @n + * optionparser-1.7.tar.gz @n * Just the header (this is all you really need): * optionparser.h * * @par Changelog: + * Version 1.7: Work on const-correctness. @n + * Version 1.6: Fix for MSC compiler. @n + * Version 1.5: Fixed 2 warnings about potentially uninitialized variables. @n + * Added const version of Option::next(). @n + * Version 1.4: Fixed 2 printUsage() bugs that messed up output with small COLUMNS values. @n * Version 1.3: Compatible with Microsoft Visual C++. @n * Version 1.2: Added @ref option::Option::namelen "Option::namelen" and removed the extraction * of short option characters into a special buffer. @n @@ -104,10 +111,6 @@ * options and non-options can be mixed. See * @ref option::Parser::parse() "Parser::parse()". * - * @par Feedback: - * Send questions, bug reports, feature requests etc. to: optionparser-feedback (a) lists.sourceforge.net - * @htmlonly @endhtmlonly - * * * @par Example program: * (Note: @c option::* identifiers are links that take you to their documentation.) @@ -215,20 +218,23 @@ #ifndef OPTIONPARSER_H_ #define OPTIONPARSER_H_ +#ifdef _MSC_VER +#include +#pragma intrinsic(_BitScanReverse) +#endif + /** @brief The namespace of The Lean Mean C++ Option Parser. */ namespace option { #ifdef _MSC_VER -#include -#pragma intrinsic(_BitScanReverse) struct MSC_Builtin_CLZ { static int builtin_clz(unsigned x) { - unsigned long index; - _BitScanReverse(&index, x); - return 32-index; // int is always 32bit on Windows, even for target x64 + unsigned long index; + _BitScanReverse(&index, x); + return 32-index; // int is always 32bit on Windows, even for target x64 } }; #define __builtin_clz(x) MSC_Builtin_CLZ::builtin_clz(x) @@ -526,7 +532,7 @@ class Option */ int type() const { - return desc == 0 ? 0 : desc->type; + return desc == 0 ? 0 : desc->type; } /** @@ -535,7 +541,7 @@ class Option */ int index() const { - return desc == 0 ? -1 : (int)desc->index; + return desc == 0 ? -1 : (int)desc->index; } /** @@ -550,16 +556,16 @@ class Option * * Returns 0 when called for an unused/invalid option. */ - int count() - { - int c = (desc == 0 ? 0 : 1); - Option* p = first(); - while (!p->isLast()) - { - ++c; - p = p->next_; - }; - return c; + int count() const + { + int c = (desc == 0 ? 0 : 1); + const Option* p = first(); + while (!p->isLast()) + { + ++c; + p = p->next_; + }; + return c; } /** @@ -572,7 +578,7 @@ class Option */ bool isFirst() const { - return isTagged(prev_); + return isTagged(prev_); } /** @@ -585,7 +591,7 @@ class Option */ bool isLast() const { - return isTagged(next_); + return isTagged(next_); } /** @@ -601,10 +607,18 @@ class Option */ Option* first() { - Option* p = this; - while (!p->isFirst()) - p = p->prev_; - return p; + Option* p = this; + while (!p->isFirst()) + p = p->prev_; + return p; + } + + /** + * const version of Option::first(). + */ + const Option* first() const + { + return const_cast(this)->first(); } /** @@ -625,7 +639,15 @@ class Option */ Option* last() { - return first()->prevwrap(); + return first()->prevwrap(); + } + + /** + * const version of Option::last(). + */ + const Option* last() const + { + return first()->prevwrap(); } /** @@ -638,7 +660,7 @@ class Option */ Option* prev() { - return isFirst() ? 0 : prev_; + return isFirst() ? 0 : prev_; } /** @@ -651,7 +673,15 @@ class Option */ Option* prevwrap() { - return untag(prev_); + return untag(prev_); + } + + /** + * const version of Option::prevwrap(). + */ + const Option* prevwrap() const + { + return untag(prev_); } /** @@ -664,7 +694,15 @@ class Option */ Option* next() { - return isLast() ? 0 : next_; + return isLast() ? 0 : next_; + } + + /** + * const version of Option::next(). + */ + const Option* next() const + { + return isLast() ? 0 : next_; } /** @@ -677,7 +715,7 @@ class Option */ Option* nextwrap() { - return untag(next_); + return untag(next_); } /** @@ -692,12 +730,12 @@ class Option */ void append(Option* new_last) { - Option* p = last(); - Option* f = first(); - p->next_ = new_last; - new_last->prev_ = p; - new_last->next_ = tag(f); - f->prev_ = tag(new_last); + Option* p = last(); + Option* f = first(); + p->next_ = new_last; + new_last->prev_ = p; + new_last->next_ = tag(f); + f->prev_ = tag(new_last); } /** @@ -718,7 +756,7 @@ class Option */ operator const Option*() const { - return desc ? this : 0; + return desc ? this : 0; } /** @@ -739,7 +777,7 @@ class Option */ operator Option*() { - return desc ? this : 0; + return desc ? this : 0; } /** @@ -747,10 +785,10 @@ class Option * @ref desc, @ref name, @ref arg and @ref namelen. */ Option() : - desc(0), name(0), arg(0), namelen(0) + desc(0), name(0), arg(0), namelen(0) { - prev_ = tag(this); - next_ = tag(this); + prev_ = tag(this); + next_ = tag(this); } /** @@ -763,7 +801,7 @@ class Option */ Option(const Descriptor* desc_, const char* name_, const char* arg_) { - init(desc_, name_, arg_); + init(desc_, name_, arg_); } /** @@ -773,7 +811,7 @@ class Option */ void operator=(const Option& orig) { - init(orig.desc, orig.name, orig.arg); + init(orig.desc, orig.name, orig.arg); } /** @@ -783,7 +821,7 @@ class Option */ Option(const Option& orig) { - init(orig.desc, orig.name, orig.arg); + init(orig.desc, orig.name, orig.arg); } private: @@ -797,34 +835,34 @@ class Option */ void init(const Descriptor* desc_, const char* name_, const char* arg_) { - desc = desc_; - name = name_; - arg = arg_; - prev_ = tag(this); - next_ = tag(this); - namelen = 0; - if (name == 0) - return; - namelen = 1; - if (name[0] != '-') - return; - while (name[namelen] != 0 && name[namelen] != '=') - ++namelen; + desc = desc_; + name = name_; + arg = arg_; + prev_ = tag(this); + next_ = tag(this); + namelen = 0; + if (name == 0) + return; + namelen = 1; + if (name[0] != '-') + return; + while (name[namelen] != 0 && name[namelen] != '=') + ++namelen; } static Option* tag(Option* ptr) { - return (Option*) ((unsigned long long) ptr | 1); + return (Option*) ((unsigned long long) ptr | 1); } static Option* untag(Option* ptr) { - return (Option*) ((unsigned long long) ptr & ~1ull); + return (Option*) ((unsigned long long) ptr & ~1ull); } static bool isTagged(Option* ptr) { - return ((unsigned long long) ptr & 1); + return ((unsigned long long) ptr & 1); } }; @@ -887,16 +925,16 @@ struct Arg //! @brief For options that don't take an argument: Returns ARG_NONE. static ArgStatus None(const Option&, bool) { - return ARG_NONE; + return ARG_NONE; } //! @brief Returns ARG_OK if the argument is attached and ARG_IGNORE otherwise. static ArgStatus Optional(const Option& option, bool) { - if (option.arg && option.name[option.namelen] != 0) - return ARG_OK; - else - return ARG_IGNORE; + if (option.arg && option.name[option.namelen] != 0) + return ARG_OK; + else + return ARG_IGNORE; } }; @@ -939,7 +977,7 @@ struct Stats * @brief Creates a Stats object with counts set to 1 (for the sentinel element). */ Stats() : - buffer_max(1), options_max(1) // 1 more than necessary as sentinel + buffer_max(1), options_max(1) // 1 more than necessary as sentinel { } @@ -953,34 +991,34 @@ struct Stats * See Parser::parse() for the meaning of the arguments. */ Stats(bool gnu, const Descriptor usage[], int argc, const char** argv, int min_abbr_len = 0, // - bool single_minus_longopt = false) : - buffer_max(1), options_max(1) // 1 more than necessary as sentinel + bool single_minus_longopt = false) : + buffer_max(1), options_max(1) // 1 more than necessary as sentinel { - add(gnu, usage, argc, argv, min_abbr_len, single_minus_longopt); + add(gnu, usage, argc, argv, min_abbr_len, single_minus_longopt); } //! @brief Stats(...) with non-const argv. Stats(bool gnu, const Descriptor usage[], int argc, char** argv, int min_abbr_len = 0, // - bool single_minus_longopt = false) : - buffer_max(1), options_max(1) // 1 more than necessary as sentinel + bool single_minus_longopt = false) : + buffer_max(1), options_max(1) // 1 more than necessary as sentinel { - add(gnu, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt); + add(gnu, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt); } //! @brief POSIX Stats(...) (gnu==false). Stats(const Descriptor usage[], int argc, const char** argv, int min_abbr_len = 0, // - bool single_minus_longopt = false) : - buffer_max(1), options_max(1) // 1 more than necessary as sentinel + bool single_minus_longopt = false) : + buffer_max(1), options_max(1) // 1 more than necessary as sentinel { - add(false, usage, argc, argv, min_abbr_len, single_minus_longopt); + add(false, usage, argc, argv, min_abbr_len, single_minus_longopt); } //! @brief POSIX Stats(...) (gnu==false) with non-const argv. Stats(const Descriptor usage[], int argc, char** argv, int min_abbr_len = 0, // - bool single_minus_longopt = false) : - buffer_max(1), options_max(1) // 1 more than necessary as sentinel + bool single_minus_longopt = false) : + buffer_max(1), options_max(1) // 1 more than necessary as sentinel { - add(false, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt); + add(false, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt); } /** @@ -993,27 +1031,27 @@ struct Stats * See Parser::parse() for the meaning of the arguments. */ void add(bool gnu, const Descriptor usage[], int argc, const char** argv, int min_abbr_len = 0, // - bool single_minus_longopt = false); + bool single_minus_longopt = false); //! @brief add() with non-const argv. void add(bool gnu, const Descriptor usage[], int argc, char** argv, int min_abbr_len = 0, // - bool single_minus_longopt = false) + bool single_minus_longopt = false) { - add(gnu, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt); + add(gnu, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt); } //! @brief POSIX add() (gnu==false). void add(const Descriptor usage[], int argc, const char** argv, int min_abbr_len = 0, // - bool single_minus_longopt = false) + bool single_minus_longopt = false) { - add(false, usage, argc, argv, min_abbr_len, single_minus_longopt); + add(false, usage, argc, argv, min_abbr_len, single_minus_longopt); } //! @brief POSIX add() (gnu==false) with non-const argv. void add(const Descriptor usage[], int argc, char** argv, int min_abbr_len = 0, // - bool single_minus_longopt = false) + bool single_minus_longopt = false) { - add(false, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt); + add(false, usage, argc, (const char**) argv, min_abbr_len, single_minus_longopt); } private: class CountOptionsAction; @@ -1051,7 +1089,7 @@ class Parser * @brief Creates a new Parser. */ Parser() : - op_count(0), nonop_count(0), nonop_args(0), err(false) + op_count(0), nonop_count(0), nonop_args(0), err(false) { } @@ -1060,34 +1098,34 @@ class Parser * @copydetails parse() */ Parser(bool gnu, const Descriptor usage[], int argc, const char** argv, Option options[], Option buffer[], - int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) : - op_count(0), nonop_count(0), nonop_args(0), err(false) + int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) : + op_count(0), nonop_count(0), nonop_args(0), err(false) { - parse(gnu, usage, argc, argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); + parse(gnu, usage, argc, argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); } //! @brief Parser(...) with non-const argv. Parser(bool gnu, const Descriptor usage[], int argc, char** argv, Option options[], Option buffer[], - int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) : - op_count(0), nonop_count(0), nonop_args(0), err(false) + int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) : + op_count(0), nonop_count(0), nonop_args(0), err(false) { - parse(gnu, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); + parse(gnu, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); } //! @brief POSIX Parser(...) (gnu==false). Parser(const Descriptor usage[], int argc, const char** argv, Option options[], Option buffer[], int min_abbr_len = 0, - bool single_minus_longopt = false, int bufmax = -1) : - op_count(0), nonop_count(0), nonop_args(0), err(false) + bool single_minus_longopt = false, int bufmax = -1) : + op_count(0), nonop_count(0), nonop_args(0), err(false) { - parse(false, usage, argc, argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); + parse(false, usage, argc, argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); } //! @brief POSIX Parser(...) (gnu==false) with non-const argv. Parser(const Descriptor usage[], int argc, char** argv, Option options[], Option buffer[], int min_abbr_len = 0, - bool single_minus_longopt = false, int bufmax = -1) : - op_count(0), nonop_count(0), nonop_args(0), err(false) + bool single_minus_longopt = false, int bufmax = -1) : + op_count(0), nonop_count(0), nonop_args(0), err(false) { - parse(false, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); + parse(false, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); } /** @@ -1147,27 +1185,27 @@ class Parser * @c options[buffer[i].index()]. */ void parse(bool gnu, const Descriptor usage[], int argc, const char** argv, Option options[], Option buffer[], - int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1); + int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1); //! @brief parse() with non-const argv. void parse(bool gnu, const Descriptor usage[], int argc, char** argv, Option options[], Option buffer[], - int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) + int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) { - parse(gnu, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); + parse(gnu, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); } //! @brief POSIX parse() (gnu==false). void parse(const Descriptor usage[], int argc, const char** argv, Option options[], Option buffer[], - int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) + int min_abbr_len = 0, bool single_minus_longopt = false, int bufmax = -1) { - parse(false, usage, argc, argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); + parse(false, usage, argc, argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); } //! @brief POSIX parse() (gnu==false) with non-const argv. void parse(const Descriptor usage[], int argc, char** argv, Option options[], Option buffer[], int min_abbr_len = 0, - bool single_minus_longopt = false, int bufmax = -1) + bool single_minus_longopt = false, int bufmax = -1) { - parse(false, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); + parse(false, usage, argc, (const char**) argv, options, buffer, min_abbr_len, single_minus_longopt, bufmax); } /** @@ -1181,7 +1219,7 @@ class Parser */ int optionsCount() { - return op_count; + return op_count; } /** @@ -1200,7 +1238,7 @@ class Parser */ int nonOptionsCount() { - return nonop_count; + return nonop_count; } /** @@ -1216,7 +1254,7 @@ class Parser */ const char** nonOptions() { - return nonop_args; + return nonop_args; } /** @@ -1224,7 +1262,7 @@ class Parser */ const char* nonOption(int i) { - return nonOptions()[i]; + return nonOptions()[i]; } /** @@ -1244,7 +1282,7 @@ class Parser */ bool error() { - return err; + return err; } private: @@ -1258,7 +1296,7 @@ class Parser * @retval false iff an unrecoverable error occurred. */ static bool workhorse(bool gnu, const Descriptor usage[], int numargs, const char** args, Action& action, - bool single_minus_longopt, bool print_errors, int min_abbr_len); + bool single_minus_longopt, bool print_errors, int min_abbr_len); /** * @internal @@ -1276,10 +1314,10 @@ class Parser */ static bool streq(const char* st1, const char* st2) { - while (*st1 != 0) - if (*st1++ != *st2++) - return false; - return (*st2 == 0 || *st2 == '='); + while (*st1 != 0) + if (*st1++ != *st2++) + return false; + return (*st2 == 0 || *st2 == '='); } /** @@ -1308,14 +1346,14 @@ class Parser */ static bool streqabbr(const char* st1, const char* st2, long long min) { - const char* st1start = st1; - while (*st1 != 0 && (*st1 == *st2)) - { - ++st1; - ++st2; - } + const char* st1start = st1; + while (*st1 != 0 && (*st1 == *st2)) + { + ++st1; + ++st2; + } - return (*st1 == 0 || (min > 0 && (st1 - st1start) >= min)) && (*st2 == 0 || *st2 == '='); + return (*st1 == 0 || (min > 0 && (st1 - st1start) >= min)) && (*st2 == 0 || *st2 == '='); } /** @@ -1326,9 +1364,9 @@ class Parser */ static bool instr(char ch, const char* st) { - while (*st != 0 && *st != ch) - ++st; - return *st == ch; + while (*st != 0 && *st != ch) + ++st; + return *st == ch; } /** @@ -1338,12 +1376,12 @@ class Parser */ static void shift(const char** args, int count) { - for (int i = 0; i > -count; --i) - { - const char* temp = args[i]; - args[i] = args[i - 1]; - args[i - 1] = temp; - } + for (int i = 0; i > -count; --i) + { + const char* temp = args[i]; + args[i] = args[i - 1]; + args[i - 1] = temp; + } } }; @@ -1364,7 +1402,7 @@ struct Parser::Action */ virtual bool perform(Option&) { - return true; + return true; } /** @@ -1377,9 +1415,9 @@ struct Parser::Action */ virtual bool finished(int numargs, const char** args) { - (void) numargs; - (void) args; - return true; + (void) numargs; + (void) args; + return true; } }; @@ -1397,16 +1435,16 @@ class Stats::CountOptionsAction: public Parser::Action * parsed Option. */ CountOptionsAction(unsigned* buffer_max_) : - buffer_max(buffer_max_) + buffer_max(buffer_max_) { } bool perform(Option&) { - if (*buffer_max == 0x7fffffff) - return false; // overflow protection: don't accept number of options that doesn't fit signed int - ++*buffer_max; - return true; + if (*buffer_max == 0x7fffffff) + return false; // overflow protection: don't accept number of options that doesn't fit signed int + ++*buffer_max; + return true; } }; @@ -1430,68 +1468,68 @@ class Parser::StoreOptionAction: public Parser::Action * @param bufmax_ number of slots in @c buffer_. @c -1 means "large enough". */ StoreOptionAction(Parser& parser_, Option options_[], Option buffer_[], int bufmax_) : - parser(parser_), options(options_), buffer(buffer_), bufmax(bufmax_) + parser(parser_), options(options_), buffer(buffer_), bufmax(bufmax_) { - // find first empty slot in buffer (if any) - int bufidx = 0; - while ((bufmax < 0 || bufidx < bufmax) && buffer[bufidx]) - ++bufidx; + // find first empty slot in buffer (if any) + int bufidx = 0; + while ((bufmax < 0 || bufidx < bufmax) && buffer[bufidx]) + ++bufidx; - // set parser's optionCount - parser.op_count = bufidx; + // set parser's optionCount + parser.op_count = bufidx; } bool perform(Option& option) { - if (bufmax < 0 || parser.op_count < bufmax) - { - if (parser.op_count == 0x7fffffff) - return false; // overflow protection: don't accept number of options that doesn't fit signed int - - buffer[parser.op_count] = option; - int idx = buffer[parser.op_count].desc->index; - if (options[idx]) - options[idx].append(buffer[parser.op_count]); - else - options[idx] = buffer[parser.op_count]; - ++parser.op_count; - } - return true; // NOTE: an option that is discarded because of a full buffer is not fatal + if (bufmax < 0 || parser.op_count < bufmax) + { + if (parser.op_count == 0x7fffffff) + return false; // overflow protection: don't accept number of options that doesn't fit signed int + + buffer[parser.op_count] = option; + int idx = buffer[parser.op_count].desc->index; + if (options[idx]) + options[idx].append(buffer[parser.op_count]); + else + options[idx] = buffer[parser.op_count]; + ++parser.op_count; + } + return true; // NOTE: an option that is discarded because of a full buffer is not fatal } bool finished(int numargs, const char** args) { - // only overwrite non-option argument list if there's at least 1 - // new non-option argument. Otherwise we keep the old list. This - // makes it easy to use default non-option arguments. - if (numargs > 0) - { - parser.nonop_count = numargs; - parser.nonop_args = args; - } + // only overwrite non-option argument list if there's at least 1 + // new non-option argument. Otherwise we keep the old list. This + // makes it easy to use default non-option arguments. + if (numargs > 0) + { + parser.nonop_count = numargs; + parser.nonop_args = args; + } - return true; + return true; } }; inline void Parser::parse(bool gnu, const Descriptor usage[], int argc, const char** argv, Option options[], - Option buffer[], int min_abbr_len, bool single_minus_longopt, int bufmax) + Option buffer[], int min_abbr_len, bool single_minus_longopt, int bufmax) { StoreOptionAction action(*this, options, buffer, bufmax); err = !workhorse(gnu, usage, argc, argv, action, single_minus_longopt, true, min_abbr_len); } inline void Stats::add(bool gnu, const Descriptor usage[], int argc, const char** argv, int min_abbr_len, - bool single_minus_longopt) + bool single_minus_longopt) { // determine size of options array. This is the greatest index used in the usage + 1 int i = 0; while (usage[i].shortopt != 0) { - if (usage[i].index + 1 >= options_max) - options_max = (usage[i].index + 1) + 1; // 1 more than necessary as sentinel + if (usage[i].index + 1 >= options_max) + options_max = (usage[i].index + 1) + 1; // 1 more than necessary as sentinel - ++i; + ++i; } CountOptionsAction action(&buffer_max); @@ -1499,181 +1537,181 @@ inline void Stats::add(bool gnu, const Descriptor usage[], int argc, const char* } inline bool Parser::workhorse(bool gnu, const Descriptor usage[], int numargs, const char** args, Action& action, - bool single_minus_longopt, bool print_errors, int min_abbr_len) + bool single_minus_longopt, bool print_errors, int min_abbr_len) { // protect against NULL pointer if (args == 0) - numargs = 0; + numargs = 0; int nonops = 0; while (numargs != 0 && *args != 0) { - const char* param = *args; // param can be --long-option, -srto or non-option argument - - // in POSIX mode the first non-option argument terminates the option list - // a lone minus character is a non-option argument - if (param[0] != '-' || param[1] == 0) - { - if (gnu) - { - ++nonops; - ++args; - if (numargs > 0) - --numargs; - continue; - } - else - break; - } - - // -- terminates the option list. The -- itself is skipped. - if (param[1] == '-' && param[2] == 0) - { - shift(args, nonops); - ++args; - if (numargs > 0) - --numargs; - break; - } - - bool handle_short_options; - const char* longopt_name; - if (param[1] == '-') // if --long-option - { - handle_short_options = false; - longopt_name = param + 2; - } - else - { - handle_short_options = true; - longopt_name = param + 1; //for testing a potential -long-option - } - - bool try_single_minus_longopt = single_minus_longopt; - bool have_more_args = (numargs > 1 || numargs < 0); // is referencing argv[1] valid? - - do // loop over short options in group, for long options the body is executed only once - { - int idx; - - const char* optarg; - - /******************** long option **********************/ - if (handle_short_options == false || try_single_minus_longopt) - { - idx = 0; - while (usage[idx].longopt != 0 && !streq(usage[idx].longopt, longopt_name)) - ++idx; - - if (usage[idx].longopt == 0 && min_abbr_len > 0) // if we should try to match abbreviated long options - { - int i1 = 0; - while (usage[i1].longopt != 0 && !streqabbr(usage[i1].longopt, longopt_name, min_abbr_len)) - ++i1; - if (usage[i1].longopt != 0) - { // now test if the match is unambiguous by checking for another match - int i2 = i1 + 1; - while (usage[i2].longopt != 0 && !streqabbr(usage[i2].longopt, longopt_name, min_abbr_len)) - ++i2; - - if (usage[i2].longopt == 0) // if there was no second match it's unambiguous, so accept i1 as idx - idx = i1; - } - } - - // if we found something, disable handle_short_options (only relevant if single_minus_longopt) - if (usage[idx].longopt != 0) - handle_short_options = false; - - try_single_minus_longopt = false; // prevent looking for longopt in the middle of shortopt group - - optarg = longopt_name; - while (*optarg != 0 && *optarg != '=') - ++optarg; - if (*optarg == '=') // attached argument - ++optarg; - else - // possibly detached argument - optarg = (have_more_args ? args[1] : 0); - } - - /************************ short option ***********************************/ - if (handle_short_options) - { - if (*++param == 0) // point at the 1st/next option character - break; // end of short option group - - idx = 0; - while (usage[idx].shortopt != 0 && !instr(*param, usage[idx].shortopt)) - ++idx; - - if (param[1] == 0) // if the potential argument is separate - optarg = (have_more_args ? args[1] : 0); - else - // if the potential argument is attached - optarg = param + 1; - } - - const Descriptor* descriptor = &usage[idx]; - - if (descriptor->shortopt == 0) /************** unknown option ********************/ - { - // look for dummy entry (shortopt == "" and longopt == "") to use as Descriptor for unknown options - idx = 0; - while (usage[idx].shortopt != 0 && (usage[idx].shortopt[0] != 0 || usage[idx].longopt[0] != 0)) - ++idx; - descriptor = (usage[idx].shortopt == 0 ? 0 : &usage[idx]); - } - - if (descriptor != 0) - { - Option option(descriptor, param, optarg); - switch (descriptor->check_arg(option, print_errors)) - { - case ARG_ILLEGAL: - return false; // fatal - case ARG_OK: - // skip one element of the argument vector, if it's a separated argument - if (optarg != 0 && have_more_args && optarg == args[1]) - { - shift(args, nonops); - if (numargs > 0) - --numargs; - ++args; - } - - // No further short options are possible after an argument - handle_short_options = false; - - break; - case ARG_IGNORE: - case ARG_NONE: - option.arg = 0; - break; - } - - if (!action.perform(option)) - return false; - } - - } while (handle_short_options); - - shift(args, nonops); - ++args; - if (numargs > 0) - --numargs; + const char* param = *args; // param can be --long-option, -srto or non-option argument + + // in POSIX mode the first non-option argument terminates the option list + // a lone minus character is a non-option argument + if (param[0] != '-' || param[1] == 0) + { + if (gnu) + { + ++nonops; + ++args; + if (numargs > 0) + --numargs; + continue; + } + else + break; + } + + // -- terminates the option list. The -- itself is skipped. + if (param[1] == '-' && param[2] == 0) + { + shift(args, nonops); + ++args; + if (numargs > 0) + --numargs; + break; + } + + bool handle_short_options; + const char* longopt_name; + if (param[1] == '-') // if --long-option + { + handle_short_options = false; + longopt_name = param + 2; + } + else + { + handle_short_options = true; + longopt_name = param + 1; //for testing a potential -long-option + } + + bool try_single_minus_longopt = single_minus_longopt; + bool have_more_args = (numargs > 1 || numargs < 0); // is referencing argv[1] valid? + + do // loop over short options in group, for long options the body is executed only once + { + int idx = 0; + + const char* optarg = 0; + + /******************** long option **********************/ + if (handle_short_options == false || try_single_minus_longopt) + { + idx = 0; + while (usage[idx].longopt != 0 && !streq(usage[idx].longopt, longopt_name)) + ++idx; + + if (usage[idx].longopt == 0 && min_abbr_len > 0) // if we should try to match abbreviated long options + { + int i1 = 0; + while (usage[i1].longopt != 0 && !streqabbr(usage[i1].longopt, longopt_name, min_abbr_len)) + ++i1; + if (usage[i1].longopt != 0) + { // now test if the match is unambiguous by checking for another match + int i2 = i1 + 1; + while (usage[i2].longopt != 0 && !streqabbr(usage[i2].longopt, longopt_name, min_abbr_len)) + ++i2; + + if (usage[i2].longopt == 0) // if there was no second match it's unambiguous, so accept i1 as idx + idx = i1; + } + } + + // if we found something, disable handle_short_options (only relevant if single_minus_longopt) + if (usage[idx].longopt != 0) + handle_short_options = false; + + try_single_minus_longopt = false; // prevent looking for longopt in the middle of shortopt group + + optarg = longopt_name; + while (*optarg != 0 && *optarg != '=') + ++optarg; + if (*optarg == '=') // attached argument + ++optarg; + else + // possibly detached argument + optarg = (have_more_args ? args[1] : 0); + } + + /************************ short option ***********************************/ + if (handle_short_options) + { + if (*++param == 0) // point at the 1st/next option character + break; // end of short option group + + idx = 0; + while (usage[idx].shortopt != 0 && !instr(*param, usage[idx].shortopt)) + ++idx; + + if (param[1] == 0) // if the potential argument is separate + optarg = (have_more_args ? args[1] : 0); + else + // if the potential argument is attached + optarg = param + 1; + } + + const Descriptor* descriptor = &usage[idx]; + + if (descriptor->shortopt == 0) /************** unknown option ********************/ + { + // look for dummy entry (shortopt == "" and longopt == "") to use as Descriptor for unknown options + idx = 0; + while (usage[idx].shortopt != 0 && (usage[idx].shortopt[0] != 0 || usage[idx].longopt[0] != 0)) + ++idx; + descriptor = (usage[idx].shortopt == 0 ? 0 : &usage[idx]); + } + + if (descriptor != 0) + { + Option option(descriptor, param, optarg); + switch (descriptor->check_arg(option, print_errors)) + { + case ARG_ILLEGAL: + return false; // fatal + case ARG_OK: + // skip one element of the argument vector, if it's a separated argument + if (optarg != 0 && have_more_args && optarg == args[1]) + { + shift(args, nonops); + if (numargs > 0) + --numargs; + ++args; + } + + // No further short options are possible after an argument + handle_short_options = false; + + break; + case ARG_IGNORE: + case ARG_NONE: + option.arg = 0; + break; + } + + if (!action.perform(option)) + return false; + } + + } while (handle_short_options); + + shift(args, nonops); + ++args; + if (numargs > 0) + --numargs; } // while if (numargs > 0 && *args == 0) // It's a bug in the caller if numargs is greater than the actual number - numargs = 0; // of arguments, but as a service to the user we fix this if we spot it. + numargs = 0; // of arguments, but as a service to the user we fix this if we spot it. if (numargs < 0) // if we don't know the number of remaining non-option arguments { // we need to count them - numargs = 0; - while (args[numargs] != 0) - ++numargs; + numargs = 0; + while (args[numargs] != 0) + ++numargs; } return action.finished(numargs + nonops, args - nonops); @@ -1691,12 +1729,12 @@ struct PrintUsageImplementation */ struct IStringWriter { - /** - * @brief Writes the given number of chars beginning at the given pointer somewhere. - */ - virtual void operator()(const char*, int) - { - } + /** + * @brief Writes the given number of chars beginning at the given pointer somewhere. + */ + virtual void operator()(const char*, int) + { + } }; /** @@ -1707,17 +1745,17 @@ struct PrintUsageImplementation template struct FunctionWriter: public IStringWriter { - Function* write; + Function* write; - virtual void operator()(const char* str, int size) - { - (*write)(str, size); - } + virtual void operator()(const char* str, int size) + { + (*write)(str, size); + } - FunctionWriter(Function* w) : - write(w) - { - } + FunctionWriter(Function* w) : + write(w) + { + } }; /** @@ -1728,17 +1766,17 @@ struct PrintUsageImplementation template struct OStreamWriter: public IStringWriter { - OStream& ostream; + OStream& ostream; - virtual void operator()(const char* str, int size) - { - ostream.write(str, size); - } + virtual void operator()(const char* str, int size) + { + ostream.write(str, size); + } - OStreamWriter(OStream& o) : - ostream(o) - { - } + OStreamWriter(OStream& o) : + ostream(o) + { + } }; /** @@ -1749,17 +1787,17 @@ struct PrintUsageImplementation template struct TemporaryWriter: public IStringWriter { - const Temporary& userstream; + const Temporary& userstream; - virtual void operator()(const char* str, int size) - { - userstream.write(str, size); - } + virtual void operator()(const char* str, int size) + { + userstream.write(str, size); + } - TemporaryWriter(const Temporary& u) : - userstream(u) - { - } + TemporaryWriter(const Temporary& u) : + userstream(u) + { + } }; /** @@ -1771,18 +1809,18 @@ struct PrintUsageImplementation template struct SyscallWriter: public IStringWriter { - Syscall* write; - int fd; + Syscall* write; + int fd; - virtual void operator()(const char* str, int size) - { - (*write)(fd, str, size); - } + virtual void operator()(const char* str, int size) + { + (*write)(fd, str, size); + } - SyscallWriter(Syscall* w, int f) : - write(w), fd(f) - { - } + SyscallWriter(Syscall* w, int f) : + write(w), fd(f) + { + } }; /** @@ -1792,18 +1830,18 @@ struct PrintUsageImplementation template struct StreamWriter: public IStringWriter { - Function* fwrite; - Stream* stream; + Function* fwrite; + Stream* stream; - virtual void operator()(const char* str, int size) - { - (*fwrite)(str, size, 1, stream); - } + virtual void operator()(const char* str, int size) + { + (*fwrite)(str, size, 1, stream); + } - StreamWriter(Function* w, Stream* s) : - fwrite(w), stream(s) - { - } + StreamWriter(Function* w, Stream* s) : + fwrite(w), stream(s) + { + } }; /** @@ -1812,7 +1850,7 @@ struct PrintUsageImplementation */ static void upmax(int& i1, int i2) { - i1 = (i1 >= i2 ? i1 : i2); + i1 = (i1 >= i2 ? i1 : i2); } /** @@ -1828,20 +1866,20 @@ struct PrintUsageImplementation */ static void indent(IStringWriter& write, int& x, int want_x) { - int indent = want_x - x; - if (indent < 0) - { - write("\n", 1); - indent = want_x; - } - - if (indent > 0) - { - char space = ' '; - for (int i = 0; i < indent; ++i) - write(&space, 1); - x = want_x; - } + int indent = want_x - x; + if (indent < 0) + { + write("\n", 1); + indent = want_x; + } + + if (indent > 0) + { + char space = ' '; + for (int i = 0; i < indent; ++i) + write(&space, 1); + x = want_x; + } } /** @@ -1864,13 +1902,13 @@ struct PrintUsageImplementation */ static bool isWideChar(unsigned ch) { - if (ch == 0x303F) - return false; + if (ch == 0x303F) + return false; - return ((0x1100 <= ch && ch <= 0x115F) || (0x2329 <= ch && ch <= 0x232A) || (0x2E80 <= ch && ch <= 0xA4C6) - || (0xA960 <= ch && ch <= 0xA97C) || (0xAC00 <= ch && ch <= 0xD7FB) || (0xF900 <= ch && ch <= 0xFAFF) - || (0xFE10 <= ch && ch <= 0xFE6B) || (0xFF01 <= ch && ch <= 0xFF60) || (0xFFE0 <= ch && ch <= 0xFFE6) - || (0x1B000 <= ch)); + return ((0x1100 <= ch && ch <= 0x115F) || (0x2329 <= ch && ch <= 0x232A) || (0x2E80 <= ch && ch <= 0xA4C6) + || (0xA960 <= ch && ch <= 0xA97C) || (0xAC00 <= ch && ch <= 0xD7FB) || (0xF900 <= ch && ch <= 0xFAFF) + || (0xFE10 <= ch && ch <= 0xFE6B) || (0xFF01 <= ch && ch <= 0xFF60) || (0xFFE0 <= ch && ch <= 0xFFE6) + || (0x1B000 <= ch)); } /** @@ -1911,253 +1949,253 @@ struct PrintUsageImplementation */ class LinePartIterator { - const Descriptor* tablestart; //!< The 1st descriptor of the current table. - const Descriptor* rowdesc; //!< The Descriptor that contains the current row. - const char* rowstart; //!< Ptr to 1st character of current row within rowdesc->help. - const char* ptr; //!< Ptr to current part within the current row. - int col; //!< Index of current column. - int len; //!< Length of the current part (that ptr points at) in BYTES - int screenlen; //!< Length of the current part in screen columns (taking narrow/wide chars into account). - int max_line_in_block; //!< Greatest index of a line within the block. This is the number of \\v within the cell with the most \\vs. - int line_in_block; //!< Line index within the current cell of the current part. - int target_line_in_block; //!< Line index of the parts we should return to the user on this iteration. - bool hit_target_line; //!< Flag whether we encountered a part with line index target_line_in_block in the current cell. - - /** - * @brief Determines the byte and character lengths of the part at @ref ptr and - * stores them in @ref len and @ref screenlen respectively. - */ - void update_length() - { - screenlen = 0; - for (len = 0; ptr[len] != 0 && ptr[len] != '\v' && ptr[len] != '\t' && ptr[len] != '\n'; ++len) - { - ++screenlen; - unsigned ch = (unsigned char) ptr[len]; - if (ch > 0xC1) // everything <= 0xC1 (yes, even 0xC1 itself) is not a valid UTF-8 start byte - { - // int __builtin_clz (unsigned int x) - // Returns the number of leading 0-bits in x, starting at the most significant bit - unsigned mask = (unsigned) -1 >> __builtin_clz(ch ^ 0xff); - ch = ch & mask; // mask out length bits, we don't verify their correctness - while (((unsigned char) ptr[len + 1] ^ 0x80) <= 0x3F) // while next byte is continuation byte - { - ch = (ch << 6) ^ (unsigned char) ptr[len + 1] ^ 0x80; // add continuation to char code - ++len; - } - // ch is the decoded unicode code point - if (ch >= 0x1100 && isWideChar(ch)) // the test for 0x1100 is here to avoid the function call in the Latin case - ++screenlen; - } - } - } + const Descriptor* tablestart; //!< The 1st descriptor of the current table. + const Descriptor* rowdesc; //!< The Descriptor that contains the current row. + const char* rowstart; //!< Ptr to 1st character of current row within rowdesc->help. + const char* ptr; //!< Ptr to current part within the current row. + int col; //!< Index of current column. + int len; //!< Length of the current part (that ptr points at) in BYTES + int screenlen; //!< Length of the current part in screen columns (taking narrow/wide chars into account). + int max_line_in_block; //!< Greatest index of a line within the block. This is the number of \\v within the cell with the most \\vs. + int line_in_block; //!< Line index within the current cell of the current part. + int target_line_in_block; //!< Line index of the parts we should return to the user on this iteration. + bool hit_target_line; //!< Flag whether we encountered a part with line index target_line_in_block in the current cell. + + /** + * @brief Determines the byte and character lengths of the part at @ref ptr and + * stores them in @ref len and @ref screenlen respectively. + */ + void update_length() + { + screenlen = 0; + for (len = 0; ptr[len] != 0 && ptr[len] != '\v' && ptr[len] != '\t' && ptr[len] != '\n'; ++len) + { + ++screenlen; + unsigned ch = (unsigned char) ptr[len]; + if (ch > 0xC1) // everything <= 0xC1 (yes, even 0xC1 itself) is not a valid UTF-8 start byte + { + // int __builtin_clz (unsigned int x) + // Returns the number of leading 0-bits in x, starting at the most significant bit + unsigned mask = (unsigned) -1 >> __builtin_clz(ch ^ 0xff); + ch = ch & mask; // mask out length bits, we don't verify their correctness + while (((unsigned char) ptr[len + 1] ^ 0x80) <= 0x3F) // while next byte is continuation byte + { + ch = (ch << 6) ^ (unsigned char) ptr[len + 1] ^ 0x80; // add continuation to char code + ++len; + } + // ch is the decoded unicode code point + if (ch >= 0x1100 && isWideChar(ch)) // the test for 0x1100 is here to avoid the function call in the Latin case + ++screenlen; + } + } + } public: - //! @brief Creates an iterator for @c usage. - LinePartIterator(const Descriptor usage[]) : - tablestart(usage), rowdesc(0), rowstart(0), ptr(0), col(-1), len(0), max_line_in_block(0), line_in_block(0), - target_line_in_block(0), hit_target_line(true) - { - } - - /** - * @brief Moves iteration to the next table (if any). Has to be called once on a new - * LinePartIterator to move to the 1st table. - * @retval false if moving to next table failed because no further table exists. - */ - bool nextTable() - { - // If this is NOT the first time nextTable() is called after the constructor, - // then skip to the next table break (i.e. a Descriptor with help == 0) - if (rowdesc != 0) - { - while (tablestart->help != 0 && tablestart->shortopt != 0) - ++tablestart; - } - - // Find the next table after the break (if any) - while (tablestart->help == 0 && tablestart->shortopt != 0) - ++tablestart; - - restartTable(); - return rowstart != 0; - } - - /** - * @brief Reset iteration to the beginning of the current table. - */ - void restartTable() - { - rowdesc = tablestart; - rowstart = tablestart->help; - ptr = 0; - } - - /** - * @brief Moves iteration to the next row (if any). Has to be called once after each call to - * @ref nextTable() to move to the 1st row of the table. - * @retval false if moving to next row failed because no further row exists. - */ - bool nextRow() - { - if (ptr == 0) - { - restartRow(); - return rowstart != 0; - } - - while (*ptr != 0 && *ptr != '\n') - ++ptr; - - if (*ptr == 0) - { - if ((rowdesc + 1)->help == 0) // table break - return false; - - ++rowdesc; - rowstart = rowdesc->help; - } - else // if (*ptr == '\n') - { - rowstart = ptr + 1; - } - - restartRow(); - return true; - } - - /** - * @brief Reset iteration to the beginning of the current row. - */ - void restartRow() - { - ptr = rowstart; - col = -1; - len = 0; - screenlen = 0; - max_line_in_block = 0; - line_in_block = 0; - target_line_in_block = 0; - hit_target_line = true; - } - - /** - * @brief Moves iteration to the next part (if any). Has to be called once after each call to - * @ref nextRow() to move to the 1st part of the row. - * @retval false if moving to next part failed because no further part exists. - * - * See @ref LinePartIterator for details about the iteration. - */ - bool next() - { - if (ptr == 0) - return false; - - if (col == -1) - { - col = 0; - update_length(); - return true; - } - - ptr += len; - while (true) - { - switch (*ptr) - { - case '\v': - upmax(max_line_in_block, ++line_in_block); - ++ptr; - break; - case '\t': - if (!hit_target_line) // if previous column did not have the targetline - { // then "insert" a 0-length part - update_length(); - hit_target_line = true; - return true; - } - - hit_target_line = false; - line_in_block = 0; - ++col; - ++ptr; - break; - case 0: - case '\n': - if (!hit_target_line) // if previous column did not have the targetline - { // then "insert" a 0-length part - update_length(); - hit_target_line = true; - return true; - } - - if (++target_line_in_block > max_line_in_block) - { - update_length(); - return false; - } - - hit_target_line = false; - line_in_block = 0; - col = 0; - ptr = rowstart; - continue; - default: - ++ptr; - continue; - } // switch - - if (line_in_block == target_line_in_block) - { - update_length(); - hit_target_line = true; - return true; - } - } // while - } - - /** - * @brief Returns the index (counting from 0) of the column in which - * the part pointed to by @ref data() is located. - */ - int column() - { - return col; - } - - /** - * @brief Returns the index (counting from 0) of the line within the current column - * this part belongs to. - */ - int line() - { - return target_line_in_block; // NOT line_in_block !!! It would be wrong if !hit_target_line - } - - /** - * @brief Returns the length of the part pointed to by @ref data() in raw chars (not UTF-8 characters). - */ - int length() - { - return len; - } - - /** - * @brief Returns the width in screen columns of the part pointed to by @ref data(). - * Takes multi-byte UTF-8 sequences and wide characters into account. - */ - int screenLength() - { - return screenlen; - } - - /** - * @brief Returns the current part of the iteration. - */ - const char* data() - { - return ptr; - } + //! @brief Creates an iterator for @c usage. + LinePartIterator(const Descriptor usage[]) : + tablestart(usage), rowdesc(0), rowstart(0), ptr(0), col(-1), len(0), max_line_in_block(0), line_in_block(0), + target_line_in_block(0), hit_target_line(true) + { + } + + /** + * @brief Moves iteration to the next table (if any). Has to be called once on a new + * LinePartIterator to move to the 1st table. + * @retval false if moving to next table failed because no further table exists. + */ + bool nextTable() + { + // If this is NOT the first time nextTable() is called after the constructor, + // then skip to the next table break (i.e. a Descriptor with help == 0) + if (rowdesc != 0) + { + while (tablestart->help != 0 && tablestart->shortopt != 0) + ++tablestart; + } + + // Find the next table after the break (if any) + while (tablestart->help == 0 && tablestart->shortopt != 0) + ++tablestart; + + restartTable(); + return rowstart != 0; + } + + /** + * @brief Reset iteration to the beginning of the current table. + */ + void restartTable() + { + rowdesc = tablestart; + rowstart = tablestart->help; + ptr = 0; + } + + /** + * @brief Moves iteration to the next row (if any). Has to be called once after each call to + * @ref nextTable() to move to the 1st row of the table. + * @retval false if moving to next row failed because no further row exists. + */ + bool nextRow() + { + if (ptr == 0) + { + restartRow(); + return rowstart != 0; + } + + while (*ptr != 0 && *ptr != '\n') + ++ptr; + + if (*ptr == 0) + { + if ((rowdesc + 1)->help == 0) // table break + return false; + + ++rowdesc; + rowstart = rowdesc->help; + } + else // if (*ptr == '\n') + { + rowstart = ptr + 1; + } + + restartRow(); + return true; + } + + /** + * @brief Reset iteration to the beginning of the current row. + */ + void restartRow() + { + ptr = rowstart; + col = -1; + len = 0; + screenlen = 0; + max_line_in_block = 0; + line_in_block = 0; + target_line_in_block = 0; + hit_target_line = true; + } + + /** + * @brief Moves iteration to the next part (if any). Has to be called once after each call to + * @ref nextRow() to move to the 1st part of the row. + * @retval false if moving to next part failed because no further part exists. + * + * See @ref LinePartIterator for details about the iteration. + */ + bool next() + { + if (ptr == 0) + return false; + + if (col == -1) + { + col = 0; + update_length(); + return true; + } + + ptr += len; + while (true) + { + switch (*ptr) + { + case '\v': + upmax(max_line_in_block, ++line_in_block); + ++ptr; + break; + case '\t': + if (!hit_target_line) // if previous column did not have the targetline + { // then "insert" a 0-length part + update_length(); + hit_target_line = true; + return true; + } + + hit_target_line = false; + line_in_block = 0; + ++col; + ++ptr; + break; + case 0: + case '\n': + if (!hit_target_line) // if previous column did not have the targetline + { // then "insert" a 0-length part + update_length(); + hit_target_line = true; + return true; + } + + if (++target_line_in_block > max_line_in_block) + { + update_length(); + return false; + } + + hit_target_line = false; + line_in_block = 0; + col = 0; + ptr = rowstart; + continue; + default: + ++ptr; + continue; + } // switch + + if (line_in_block == target_line_in_block) + { + update_length(); + hit_target_line = true; + return true; + } + } // while + } + + /** + * @brief Returns the index (counting from 0) of the column in which + * the part pointed to by @ref data() is located. + */ + int column() + { + return col; + } + + /** + * @brief Returns the index (counting from 0) of the line within the current column + * this part belongs to. + */ + int line() + { + return target_line_in_block; // NOT line_in_block !!! It would be wrong if !hit_target_line + } + + /** + * @brief Returns the length of the part pointed to by @ref data() in raw chars (not UTF-8 characters). + */ + int length() + { + return len; + } + + /** + * @brief Returns the width in screen columns of the part pointed to by @ref data(). + * Takes multi-byte UTF-8 sequences and wide characters into account. + */ + int screenLength() + { + return screenlen; + } + + /** + * @brief Returns the current part of the iteration. + */ + const char* data() + { + return ptr; + } }; /** @@ -2186,219 +2224,219 @@ struct PrintUsageImplementation */ class LineWrapper { - static const int bufmask = 15; //!< Must be a power of 2 minus 1. - /** - * @brief Ring buffer for length component of pair (data, length). - */ - int lenbuf[bufmask + 1]; - /** - * @brief Ring buffer for data component of pair (data, length). - */ - const char* datbuf[bufmask + 1]; - /** - * @brief The indentation of the column to which the LineBuffer outputs. LineBuffer - * assumes that the indentation has already been written when @ref process() - * is called, so this value is only used when a buffer flush requires writing - * additional lines of output. - */ - int x; - /** - * @brief The width of the column to line wrap. - */ - int width; - int head; //!< @brief index for next write - int tail; //!< @brief index for next read - 1 (i.e. increment tail BEFORE read) - - /** - * @brief Multiple methods of LineWrapper may decide to flush part of the buffer to - * free up space. The contract of process() says that only 1 line is output. So - * this variable is used to track whether something has output a line. It is - * reset at the beginning of process() and checked at the end to decide if - * output has already occurred or is still needed. - */ - bool wrote_something; - - bool buf_empty() - { - return ((tail + 1) & bufmask) == head; - } - - bool buf_full() - { - return tail == head; - } - - void buf_store(const char* data, int len) - { - lenbuf[head] = len; - datbuf[head] = data; - head = (head + 1) & bufmask; - } - - //! @brief Call BEFORE reading ...buf[tail]. - void buf_next() - { - tail = (tail + 1) & bufmask; - } - - /** - * @brief Writes (data,len) into the ring buffer. If the buffer is full, a single line - * is flushed out of the buffer into @c write. - */ - void output(IStringWriter& write, const char* data, int len) - { - if (buf_full()) - write_one_line(write); - - buf_store(data, len); - } - - /** - * @brief Writes a single line of output from the buffer to @c write. - */ - void write_one_line(IStringWriter& write) - { - if (wrote_something) // if we already wrote something, we need to start a new line - { - write("\n", 1); - int _ = 0; - indent(write, _, x); - } - - if (!buf_empty()) - { - buf_next(); - write(datbuf[tail], lenbuf[tail]); - } - - wrote_something = true; - } + static const int bufmask = 15; //!< Must be a power of 2 minus 1. + /** + * @brief Ring buffer for length component of pair (data, length). + */ + int lenbuf[bufmask + 1]; + /** + * @brief Ring buffer for data component of pair (data, length). + */ + const char* datbuf[bufmask + 1]; + /** + * @brief The indentation of the column to which the LineBuffer outputs. LineBuffer + * assumes that the indentation has already been written when @ref process() + * is called, so this value is only used when a buffer flush requires writing + * additional lines of output. + */ + int x; + /** + * @brief The width of the column to line wrap. + */ + int width; + int head; //!< @brief index for next write + int tail; //!< @brief index for next read - 1 (i.e. increment tail BEFORE read) + + /** + * @brief Multiple methods of LineWrapper may decide to flush part of the buffer to + * free up space. The contract of process() says that only 1 line is output. So + * this variable is used to track whether something has output a line. It is + * reset at the beginning of process() and checked at the end to decide if + * output has already occurred or is still needed. + */ + bool wrote_something; + + bool buf_empty() + { + return ((tail + 1) & bufmask) == head; + } + + bool buf_full() + { + return tail == head; + } + + void buf_store(const char* data, int len) + { + lenbuf[head] = len; + datbuf[head] = data; + head = (head + 1) & bufmask; + } + + //! @brief Call BEFORE reading ...buf[tail]. + void buf_next() + { + tail = (tail + 1) & bufmask; + } + + /** + * @brief Writes (data,len) into the ring buffer. If the buffer is full, a single line + * is flushed out of the buffer into @c write. + */ + void output(IStringWriter& write, const char* data, int len) + { + if (buf_full()) + write_one_line(write); + + buf_store(data, len); + } + + /** + * @brief Writes a single line of output from the buffer to @c write. + */ + void write_one_line(IStringWriter& write) + { + if (wrote_something) // if we already wrote something, we need to start a new line + { + write("\n", 1); + int _ = 0; + indent(write, _, x); + } + + if (!buf_empty()) + { + buf_next(); + write(datbuf[tail], lenbuf[tail]); + } + + wrote_something = true; + } public: - /** - * @brief Writes out all remaining data from the LineWrapper using @c write. - * Unlike @ref process() this method indents all lines including the first and - * will output a \\n at the end (but only if something has been written). - */ - void flush(IStringWriter& write) - { - if (buf_empty()) - return; - int _ = 0; - indent(write, _, x); - wrote_something = false; - while (!buf_empty()) - write_one_line(write); - write("\n", 1); - } - - /** - * @brief Process, wrap and output the next piece of data. - * - * process() will output at least one line of output. This is not necessarily - * the @c data passed in. It may be data queued from a prior call to process(). - * If the internal buffer is full, more than 1 line will be output. - * - * process() assumes that the a proper amount of indentation has already been - * output. It won't write any further indentation before the 1st line. If - * more than 1 line is written due to buffer constraints, the lines following - * the first will be indented by this method, though. - * - * No \\n is written by this method after the last line that is written. - * - * @param write where to write the data. - * @param data the new chunk of data to write. - * @param len the length of the chunk of data to write. - */ - void process(IStringWriter& write, const char* data, int len) - { - wrote_something = false; - - while (len > 0) - { - if (len <= width) // quick test that works because utf8width <= len (all wide chars have at least 2 bytes) - { - output(write, data, len); - len = 0; - } - else // if (len > width) it's possible (but not guaranteed) that utf8len > width - { - int utf8width = 0; - int maxi = 0; - while (maxi < len && utf8width < width) - { - int charbytes = 1; - unsigned ch = (unsigned char) data[maxi]; - if (ch > 0xC1) // everything <= 0xC1 (yes, even 0xC1 itself) is not a valid UTF-8 start byte - { - // int __builtin_clz (unsigned int x) - // Returns the number of leading 0-bits in x, starting at the most significant bit - unsigned mask = (unsigned) -1 >> __builtin_clz(ch ^ 0xff); - ch = ch & mask; // mask out length bits, we don't verify their correctness - while ((maxi + charbytes < len) && // - (((unsigned char) data[maxi + charbytes] ^ 0x80) <= 0x3F)) // while next byte is continuation byte - { - ch = (ch << 6) ^ (unsigned char) data[maxi + charbytes] ^ 0x80; // add continuation to char code - ++charbytes; - } - // ch is the decoded unicode code point - if (ch >= 0x1100 && isWideChar(ch)) // the test for 0x1100 is here to avoid the function call in the Latin case - { - if (utf8width + 2 > width) - break; - ++utf8width; - } - } - ++utf8width; - maxi += charbytes; - } - - // data[maxi-1] is the last byte of the UTF-8 sequence of the last character that fits - // onto the 1st line. If maxi == len, all characters fit on the line. - - if (maxi == len) - { - output(write, data, len); - len = 0; - } - else // if (maxi < len) at least 1 character (data[maxi] that is) doesn't fit on the line - { - int i; - for (i = maxi; i >= 0; --i) - if (data[i] == ' ') - break; - - if (i >= 0) - { - output(write, data, i); - data += i + 1; - len -= i + 1; - } - else // did not find a space to split at => split before data[maxi] - { // data[maxi] is always the beginning of a character, never a continuation byte - output(write, data, maxi); - data += maxi; - len -= maxi; - } - } - } - } - if (!wrote_something) // if we didn't already write something to make space in the buffer - write_one_line(write); // write at most one line of actual output - } - - /** - * @brief Constructs a LineWrapper that wraps its output to fit into - * screen columns @c x1 (incl.) to @c x2 (excl.). - * - * @c x1 gives the indentation LineWrapper uses if it needs to indent. - */ - LineWrapper(int x1, int x2) : - x(x1), width(x2 - x1), head(0), tail(bufmask) - { - if (width < 2) // because of wide characters we need at least width 2 or the code breaks - width = 2; - } + /** + * @brief Writes out all remaining data from the LineWrapper using @c write. + * Unlike @ref process() this method indents all lines including the first and + * will output a \\n at the end (but only if something has been written). + */ + void flush(IStringWriter& write) + { + if (buf_empty()) + return; + int _ = 0; + indent(write, _, x); + wrote_something = false; + while (!buf_empty()) + write_one_line(write); + write("\n", 1); + } + + /** + * @brief Process, wrap and output the next piece of data. + * + * process() will output at least one line of output. This is not necessarily + * the @c data passed in. It may be data queued from a prior call to process(). + * If the internal buffer is full, more than 1 line will be output. + * + * process() assumes that the a proper amount of indentation has already been + * output. It won't write any further indentation before the 1st line. If + * more than 1 line is written due to buffer constraints, the lines following + * the first will be indented by this method, though. + * + * No \\n is written by this method after the last line that is written. + * + * @param write where to write the data. + * @param data the new chunk of data to write. + * @param len the length of the chunk of data to write. + */ + void process(IStringWriter& write, const char* data, int len) + { + wrote_something = false; + + while (len > 0) + { + if (len <= width) // quick test that works because utf8width <= len (all wide chars have at least 2 bytes) + { + output(write, data, len); + len = 0; + } + else // if (len > width) it's possible (but not guaranteed) that utf8len > width + { + int utf8width = 0; + int maxi = 0; + while (maxi < len && utf8width < width) + { + int charbytes = 1; + unsigned ch = (unsigned char) data[maxi]; + if (ch > 0xC1) // everything <= 0xC1 (yes, even 0xC1 itself) is not a valid UTF-8 start byte + { + // int __builtin_clz (unsigned int x) + // Returns the number of leading 0-bits in x, starting at the most significant bit + unsigned mask = (unsigned) -1 >> __builtin_clz(ch ^ 0xff); + ch = ch & mask; // mask out length bits, we don't verify their correctness + while ((maxi + charbytes < len) && // + (((unsigned char) data[maxi + charbytes] ^ 0x80) <= 0x3F)) // while next byte is continuation byte + { + ch = (ch << 6) ^ (unsigned char) data[maxi + charbytes] ^ 0x80; // add continuation to char code + ++charbytes; + } + // ch is the decoded unicode code point + if (ch >= 0x1100 && isWideChar(ch)) // the test for 0x1100 is here to avoid the function call in the Latin case + { + if (utf8width + 2 > width) + break; + ++utf8width; + } + } + ++utf8width; + maxi += charbytes; + } + + // data[maxi-1] is the last byte of the UTF-8 sequence of the last character that fits + // onto the 1st line. If maxi == len, all characters fit on the line. + + if (maxi == len) + { + output(write, data, len); + len = 0; + } + else // if (maxi < len) at least 1 character (data[maxi] that is) doesn't fit on the line + { + int i; + for (i = maxi; i >= 0; --i) + if (data[i] == ' ') + break; + + if (i >= 0) + { + output(write, data, i); + data += i + 1; + len -= i + 1; + } + else // did not find a space to split at => split before data[maxi] + { // data[maxi] is always the beginning of a character, never a continuation byte + output(write, data, maxi); + data += maxi; + len -= maxi; + } + } + } + } + if (!wrote_something) // if we didn't already write something to make space in the buffer + write_one_line(write); // write at most one line of actual output + } + + /** + * @brief Constructs a LineWrapper that wraps its output to fit into + * screen columns @c x1 (incl.) to @c x2 (excl.). + * + * @c x1 gives the indentation LineWrapper uses if it needs to indent. + */ + LineWrapper(int x1, int x2) : + x(x1), width(x2 - x1), head(0), tail(bufmask) + { + if (width < 2) // because of wide characters we need at least width 2 or the code breaks + width = 2; + } }; /** @@ -2407,166 +2445,170 @@ struct PrintUsageImplementation * Because all printUsage() templates share this implementation, there is no template bloat. */ static void printUsage(IStringWriter& write, const Descriptor usage[], int width = 80, // - int last_column_min_percent = 50, int last_column_own_line_max_percent = 75) - { - if (width < 1) // protect against nonsense values - width = 80; - - if (width > 10000) // protect against overflow in the following computation - width = 10000; - - int last_column_min_width = ((width * last_column_min_percent) + 50) / 100; - int last_column_own_line_max_width = ((width * last_column_own_line_max_percent) + 50) / 100; - if (last_column_own_line_max_width == 0) - last_column_own_line_max_width = 1; - - LinePartIterator part(usage); - while (part.nextTable()) - { - - /***************** Determine column widths *******************************/ - - const int maxcolumns = 8; // 8 columns are enough for everyone - int col_width[maxcolumns]; - int lastcolumn; - int leftwidth; - int overlong_column_threshold = 10000; - do - { - lastcolumn = 0; - for (int i = 0; i < maxcolumns; ++i) - col_width[i] = 0; - - part.restartTable(); - while (part.nextRow()) - { - while (part.next()) - { - if (part.column() < maxcolumns) - { - upmax(lastcolumn, part.column()); - if (part.screenLength() < overlong_column_threshold) - // We don't let rows that don't use table separators (\t or \v) influence - // the width of column 0. This allows the user to interject section headers - // or explanatory paragraphs that do not participate in the table layout. - if (part.column() > 0 || part.line() > 0 || part.data()[part.length()] == '\t' - || part.data()[part.length()] == '\v') - upmax(col_width[part.column()], part.screenLength()); - } - } - } - - /* - * If the last column doesn't fit on the same - * line as the other columns, we can fix that by starting it on its own line. - * However we can't do this for any of the columns 0..lastcolumn-1. - * If their sum exceeds the maximum width we try to fix this by iteratively - * ignoring the widest line parts in the width determination until - * we arrive at a series of column widths that fit into one line. - * The result is a layout where everything is nicely formatted - * except for a few overlong fragments. - * */ - - leftwidth = 0; - overlong_column_threshold = 0; - for (int i = 0; i < lastcolumn; ++i) - { - leftwidth += col_width[i]; - upmax(overlong_column_threshold, col_width[i]); - } - - } while (leftwidth > width); - - /**************** Determine tab stops and last column handling **********************/ - - int tabstop[maxcolumns]; - tabstop[0] = 0; - for (int i = 1; i < maxcolumns; ++i) - tabstop[i] = tabstop[i - 1] + col_width[i - 1]; - - int rightwidth = width - tabstop[lastcolumn]; - bool print_last_column_on_own_line = false; - if (rightwidth < last_column_min_width && rightwidth < col_width[lastcolumn]) - { - print_last_column_on_own_line = true; - rightwidth = last_column_own_line_max_width; - } - - // If lastcolumn == 0 we must disable print_last_column_on_own_line because - // otherwise 2 copies of the last (and only) column would be output. - // Actually this is just defensive programming. It is currently not - // possible that lastcolumn==0 and print_last_column_on_own_line==true - // at the same time, because lastcolumn==0 => tabstop[lastcolumn] == 0 => - // rightwidth==width => rightwidth>=last_column_min_width (unless someone passes - // a bullshit value >100 for last_column_min_percent) => the above if condition - // is false => print_last_column_on_own_line==false - if (lastcolumn == 0) - print_last_column_on_own_line = false; - - LineWrapper lastColumnLineWrapper(width - rightwidth, width); - LineWrapper interjectionLineWrapper(0, width); - - part.restartTable(); - - /***************** Print out all rows of the table *************************************/ - - while (part.nextRow()) - { - int x = -1; - while (part.next()) - { - if (part.column() > lastcolumn) - continue; // drop excess columns (can happen if lastcolumn == maxcolumns-1) - - if (part.column() == 0) - { - if (x >= 0) - write("\n", 1); - x = 0; - } - - indent(write, x, tabstop[part.column()]); - - if ((part.column() < lastcolumn) - && (part.column() > 0 || part.line() > 0 || part.data()[part.length()] == '\t' - || part.data()[part.length()] == '\v')) - { - write(part.data(), part.length()); - x += part.screenLength(); - } - else // either part.column() == lastcolumn or we are in the special case of - // an interjection that doesn't contain \v or \t - { - // NOTE: This code block is not necessarily executed for - // each line, because some rows may have fewer columns. - - LineWrapper& lineWrapper = (part.column() == 0) ? interjectionLineWrapper : lastColumnLineWrapper; - - if (!print_last_column_on_own_line) - lineWrapper.process(write, part.data(), part.length()); - } - } // while - - if (print_last_column_on_own_line) - { - part.restartRow(); - while (part.next()) - { - if (part.column() == lastcolumn) - { - write("\n", 1); - int _ = 0; - indent(write, _, width - rightwidth); - lastColumnLineWrapper.process(write, part.data(), part.length()); - } - } - } - - write("\n", 1); - lastColumnLineWrapper.flush(write); - interjectionLineWrapper.flush(write); - } - } + int last_column_min_percent = 50, int last_column_own_line_max_percent = 75) + { + if (width < 1) // protect against nonsense values + width = 80; + + if (width > 10000) // protect against overflow in the following computation + width = 10000; + + int last_column_min_width = ((width * last_column_min_percent) + 50) / 100; + int last_column_own_line_max_width = ((width * last_column_own_line_max_percent) + 50) / 100; + if (last_column_own_line_max_width == 0) + last_column_own_line_max_width = 1; + + LinePartIterator part(usage); + while (part.nextTable()) + { + + /***************** Determine column widths *******************************/ + + const int maxcolumns = 8; // 8 columns are enough for everyone + int col_width[maxcolumns]; + int lastcolumn; + int leftwidth; + int overlong_column_threshold = 10000; + do + { + lastcolumn = 0; + for (int i = 0; i < maxcolumns; ++i) + col_width[i] = 0; + + part.restartTable(); + while (part.nextRow()) + { + while (part.next()) + { + if (part.column() < maxcolumns) + { + upmax(lastcolumn, part.column()); + if (part.screenLength() < overlong_column_threshold) + // We don't let rows that don't use table separators (\t or \v) influence + // the width of column 0. This allows the user to interject section headers + // or explanatory paragraphs that do not participate in the table layout. + if (part.column() > 0 || part.line() > 0 || part.data()[part.length()] == '\t' + || part.data()[part.length()] == '\v') + upmax(col_width[part.column()], part.screenLength()); + } + } + } + + /* + * If the last column doesn't fit on the same + * line as the other columns, we can fix that by starting it on its own line. + * However we can't do this for any of the columns 0..lastcolumn-1. + * If their sum exceeds the maximum width we try to fix this by iteratively + * ignoring the widest line parts in the width determination until + * we arrive at a series of column widths that fit into one line. + * The result is a layout where everything is nicely formatted + * except for a few overlong fragments. + * */ + + leftwidth = 0; + overlong_column_threshold = 0; + for (int i = 0; i < lastcolumn; ++i) + { + leftwidth += col_width[i]; + upmax(overlong_column_threshold, col_width[i]); + } + + } while (leftwidth > width); + + /**************** Determine tab stops and last column handling **********************/ + + int tabstop[maxcolumns]; + tabstop[0] = 0; + for (int i = 1; i < maxcolumns; ++i) + tabstop[i] = tabstop[i - 1] + col_width[i - 1]; + + int rightwidth = width - tabstop[lastcolumn]; + bool print_last_column_on_own_line = false; + if (rightwidth < last_column_min_width && // if we don't have the minimum requested width for the last column + ( col_width[lastcolumn] == 0 || // and all last columns are > overlong_column_threshold + rightwidth < col_width[lastcolumn] // or there is at least one last column that requires more than the space available + ) + ) + { + print_last_column_on_own_line = true; + rightwidth = last_column_own_line_max_width; + } + + // If lastcolumn == 0 we must disable print_last_column_on_own_line because + // otherwise 2 copies of the last (and only) column would be output. + // Actually this is just defensive programming. It is currently not + // possible that lastcolumn==0 and print_last_column_on_own_line==true + // at the same time, because lastcolumn==0 => tabstop[lastcolumn] == 0 => + // rightwidth==width => rightwidth>=last_column_min_width (unless someone passes + // a bullshit value >100 for last_column_min_percent) => the above if condition + // is false => print_last_column_on_own_line==false + if (lastcolumn == 0) + print_last_column_on_own_line = false; + + LineWrapper lastColumnLineWrapper(width - rightwidth, width); + LineWrapper interjectionLineWrapper(0, width); + + part.restartTable(); + + /***************** Print out all rows of the table *************************************/ + + while (part.nextRow()) + { + int x = -1; + while (part.next()) + { + if (part.column() > lastcolumn) + continue; // drop excess columns (can happen if lastcolumn == maxcolumns-1) + + if (part.column() == 0) + { + if (x >= 0) + write("\n", 1); + x = 0; + } + + indent(write, x, tabstop[part.column()]); + + if ((part.column() < lastcolumn) + && (part.column() > 0 || part.line() > 0 || part.data()[part.length()] == '\t' + || part.data()[part.length()] == '\v')) + { + write(part.data(), part.length()); + x += part.screenLength(); + } + else // either part.column() == lastcolumn or we are in the special case of + // an interjection that doesn't contain \v or \t + { + // NOTE: This code block is not necessarily executed for + // each line, because some rows may have fewer columns. + + LineWrapper& lineWrapper = (part.column() == 0) ? interjectionLineWrapper : lastColumnLineWrapper; + + if (!print_last_column_on_own_line || part.column() != lastcolumn) + lineWrapper.process(write, part.data(), part.length()); + } + } // while + + if (print_last_column_on_own_line) + { + part.restartRow(); + while (part.next()) + { + if (part.column() == lastcolumn) + { + write("\n", 1); + int _ = 0; + indent(write, _, width - rightwidth); + lastColumnLineWrapper.process(write, part.data(), part.length()); + } + } + } + + write("\n", 1); + lastColumnLineWrapper.flush(write); + interjectionLineWrapper.flush(write); + } + } } } @@ -2771,7 +2813,7 @@ struct PrintUsageImplementation */ template void printUsage(OStream& prn, const Descriptor usage[], int width = 80, int last_column_min_percent = 50, - int last_column_own_line_max_percent = 75) + int last_column_own_line_max_percent = 75) { PrintUsageImplementation::OStreamWriter write(prn); PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent); @@ -2779,7 +2821,7 @@ void printUsage(OStream& prn, const Descriptor usage[], int width = 80, int last template void printUsage(Function* prn, const Descriptor usage[], int width = 80, int last_column_min_percent = 50, - int last_column_own_line_max_percent = 75) + int last_column_own_line_max_percent = 75) { PrintUsageImplementation::FunctionWriter write(prn); PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent); @@ -2787,7 +2829,7 @@ void printUsage(Function* prn, const Descriptor usage[], int width = 80, int las template void printUsage(const Temporary& prn, const Descriptor usage[], int width = 80, int last_column_min_percent = 50, - int last_column_own_line_max_percent = 75) + int last_column_own_line_max_percent = 75) { PrintUsageImplementation::TemporaryWriter write(prn); PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent); @@ -2795,7 +2837,7 @@ void printUsage(const Temporary& prn, const Descriptor usage[], int width = 80, template void printUsage(Syscall* prn, int fd, const Descriptor usage[], int width = 80, int last_column_min_percent = 50, - int last_column_own_line_max_percent = 75) + int last_column_own_line_max_percent = 75) { PrintUsageImplementation::SyscallWriter write(prn, fd); PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent); @@ -2803,8 +2845,8 @@ void printUsage(Syscall* prn, int fd, const Descriptor usage[], int width = 80, template void printUsage(Function* prn, Stream* stream, const Descriptor usage[], int width = 80, int last_column_min_percent = - 50, - int last_column_own_line_max_percent = 75) + 50, + int last_column_own_line_max_percent = 75) { PrintUsageImplementation::StreamWriter write(prn, stream); PrintUsageImplementation::printUsage(write, usage, width, last_column_min_percent, last_column_own_line_max_percent); diff --git a/commandLine/src/utils/Utils.cpp b/commandLine/src/utils/Utils.cpp index 7f888f68..78f0c793 100644 --- a/commandLine/src/utils/Utils.cpp +++ b/commandLine/src/utils/Utils.cpp @@ -235,10 +235,12 @@ void getFoldersRecursively(const fs::path & path, std::vector < std::string > & } } +using std::cout; +using std::endl; void getFrameworksRecursively(const fs::path & path, std::vector < std::string > & frameworks, std::string platform){ - + cout << "getFrameworksRecursively" << endl; ofDirectory dir; dir.listDir(path); @@ -260,6 +262,7 @@ void getFrameworksRecursively(const fs::path & path, std::vector < std::string > } } + cout << frameworks.size() << endl; } From 3f07966658ee857f97d890ccac978d2726a9af3f Mon Sep 17 00:00:00 2001 From: Dimitre Date: Thu, 13 Apr 2023 15:05:12 -0300 Subject: [PATCH 02/25] updates --- .../xcschemes/commandLine Debug.xcscheme | 2 +- commandLine/src/main.cpp | 14 ++++++++------ 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/commandLine/commandLine.xcodeproj/xcshareddata/xcschemes/commandLine Debug.xcscheme b/commandLine/commandLine.xcodeproj/xcshareddata/xcschemes/commandLine Debug.xcscheme index 80008d9c..0857ea69 100644 --- a/commandLine/commandLine.xcodeproj/xcshareddata/xcschemes/commandLine Debug.xcscheme +++ b/commandLine/commandLine.xcodeproj/xcshareddata/xcschemes/commandLine Debug.xcscheme @@ -46,7 +46,7 @@ launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" - debugDocumentVersioning = "YES" + debugDocumentVersioning = "NO" debugServiceExtension = "internal" allowLocationSimulation = "YES"> ()) ("v,verbose", "Verbose output", cxxopts::value()->default_value("false")) ; - + std::cout << "Have " << argc << " arguments:" << std::endl; + for (int i = 0; i < argc; ++i) { + std::cout << argv[i] << std::endl; + } // auto result = options.parse(argc, argv); // cout << result["ofPath"].as() << endl; @@ -413,10 +415,10 @@ int main(int argc, char* argv[]){ if (options[DRYRUN].count() > 0){ bDryRun = true; } - if (options[VERSION].count() > 0){ - printVersion(); - return EXIT_OK; - } +// if (options[VERSION].count() > 0){ +// printVersion(); +// return EXIT_OK; +// } cout << 5 << endl; if (options[VERBOSE].count() > 0){ bVerbose = true; From a9c53c4b10301236c0157f8a80867f222d265604 Mon Sep 17 00:00:00 2001 From: Dimitre Date: Thu, 13 Apr 2023 15:29:00 -0300 Subject: [PATCH 03/25] up --- .../commandLine.xcodeproj/project.pbxproj | 2 +- commandLine/src/main.cpp | 30 ++++++++++--------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/commandLine/commandLine.xcodeproj/project.pbxproj b/commandLine/commandLine.xcodeproj/project.pbxproj index 00d1e06e..631e85ab 100644 --- a/commandLine/commandLine.xcodeproj/project.pbxproj +++ b/commandLine/commandLine.xcodeproj/project.pbxproj @@ -237,7 +237,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "cp \"$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/MacOS/$PRODUCT_NAME\" \"$TARGET_BUILD_DIR/projectGenerator\";\t\n"; + shellScript = "rm \"$TARGET_BUILD_DIR/projectGenerator\"\ncp \"$TARGET_BUILD_DIR/$PRODUCT_NAME.app/Contents/MacOS/$PRODUCT_NAME\" \"$TARGET_BUILD_DIR/projectGenerator\";\t\n"; }; E42962A92163ECCD00A6A9E2 /* Run Script - Compile OF */ = { isa = PBXShellScriptBuildPhase; diff --git a/commandLine/src/main.cpp b/commandLine/src/main.cpp index 310643bc..2f9ff192 100644 --- a/commandLine/src/main.cpp +++ b/commandLine/src/main.cpp @@ -343,7 +343,7 @@ void printHelp(){ //------------------------------------------- -int main(int argc, char* argv[]){ +int main(int argc, char** argv){ //------------------------------------------- pre-parse bAddonsPassedIn = false; bDryRun = false; @@ -363,21 +363,28 @@ int main(int argc, char* argv[]){ templateName = ""; ofPath = ""; + std::cout << "Have " << argc << " arguments:" << std::endl; + for (int i = 0; i < argc; ++i) { + std::cout << argv[i] << std::endl; + } options .allow_unrecognised_options() .add_options() + ("h,help", "Print usage") ("o,ofPath", "path to openframeworks (relative or absolute). This *must* be set, or you can also alternatively use an environment variable PG_OF_PATH and if this isn't set, it will use that value instead") // a bool parameter - ("i,integer", "Int param", cxxopts::value()) - ("f,file", "File name", cxxopts::value()) - ("v,verbose", "Verbose output", cxxopts::value()->default_value("false")) ; - std::cout << "Have " << argc << " arguments:" << std::endl; - for (int i = 0; i < argc; ++i) { - std::cout << argv[i] << std::endl; + + auto result = options.parse(argc, argv); + +// if (result.count("ofPath")) { +// std::cout << options.help() << std::endl; +// exit(0); +// } + + if (result.count("ofPath")) { + cout << result["ofPath"].as() << endl; } -// auto result = options.parse(argc, argv); -// cout << result["ofPath"].as() << endl; cout << 1 << endl; // ------------------------------------------------------ parse args @@ -387,13 +394,9 @@ int main(int argc, char* argv[]){ std::vector buffer(stats.buffer_max); option::Parser parse(usage, argc, argv, &options[0], &buffer[0]); - cout << 2 << endl; - if (parse.error()) { - return 1; } - cout << 3 << endl; if (options[HELP] || argc == 0) { ofLogError() << "No arguments"; @@ -401,7 +404,6 @@ int main(int argc, char* argv[]){ return EXIT_OK; } - cout << 4 << endl; // templates: if (options[LISTTEMPLATES].count() > 0){ From 1e15b15ac7b189eb10c288567bafdd2fe83bab00 Mon Sep 17 00:00:00 2001 From: Dimitre Date: Thu, 13 Apr 2023 15:35:12 -0300 Subject: [PATCH 04/25] u --- commandLine/src/main.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/commandLine/src/main.cpp b/commandLine/src/main.cpp index 2f9ff192..6baa390a 100644 --- a/commandLine/src/main.cpp +++ b/commandLine/src/main.cpp @@ -15,7 +15,7 @@ constexpr option::Descriptor usage[] = {LISTTEMPLATES, 0,"l","listtemplates",option::Arg::None, " --listtemplates, -l \tlist templates available for the specified or current platform(s)" }, {PLATFORMS, 0,"p","platforms",option::Arg::None, " --platforms, -p \tplatform list (such as osx, ios, winvs)" }, {ADDONS, 0,"a","addons",option::Arg::None, " --addons, -a \taddon list (such as ofxOpenCv, ofxGui, ofxXmlSettings)" }, - {OFPATH, 0,"o","ofPath",option::Arg::None, " --ofPath, -o \tpath to openframeworks (relative or absolute). This *must* be set, or you can also alternatively use an environment variable PG_OF_PATH and if this isn't set, it will use that value instead" }, + {OFPATH, 0,"o","ofPath",option::Arg::Optional, " --ofPath, -o \tpath to openframeworks (relative or absolute). This *must* be set, or you can also alternatively use an environment variable PG_OF_PATH and if this isn't set, it will use that value instead" }, {VERBOSE, 0,"v","verbose",option::Arg::None, " --verbose, -v \trun verbose" }, {TEMPLATE, 0,"t","template",option::Arg::None, " --template, -t \tproject template" }, {DRYRUN, 0,"d","dryrun",option::Arg::None, " --dryrun, -d \tdry run, don't change files" }, @@ -382,9 +382,9 @@ int main(int argc, char** argv){ // exit(0); // } - if (result.count("ofPath")) { - cout << result["ofPath"].as() << endl; - } +// if (result.count("ofPath")) { +// cout << result["ofPath"].as() << endl; +// } cout << 1 << endl; // ------------------------------------------------------ parse args From a35f44ce3c2997e844c1e72a9e4413411fad6928 Mon Sep 17 00:00:00 2001 From: Dimitre Date: Thu, 13 Apr 2023 16:44:11 -0300 Subject: [PATCH 05/25] updates --- commandLine/src/addons/ofAddon.cpp | 79 ++++++++++++++---------------- commandLine/src/main.cpp | 26 +++++----- commandLine/src/utils/Utils.cpp | 29 +++-------- 3 files changed, 58 insertions(+), 76 deletions(-) diff --git a/commandLine/src/addons/ofAddon.cpp b/commandLine/src/addons/ofAddon.cpp index c0c8a1dc..1d4f327b 100644 --- a/commandLine/src/addons/ofAddon.cpp +++ b/commandLine/src/addons/ofAddon.cpp @@ -587,61 +587,56 @@ bool ofAddon::fromFS(fs::path path, const std::string & platform){ l.path = fs::path(prefixPath / fs::relative(l.path, containedPath)).string(); } - -// for (int i = 0; i < (int)libs.size(); i++){ -// -// cout << "libs path before " << libs[i].path << endl; -// // does libs[] have any path ? let's fix if so. -// int end = libs[i].path.rfind(fs::path("/").make_preferred().string()); -// if (end > 0){ -// libs[i].path.erase (libs[i].path.begin(), libs[i].path.begin() + containedPath.string().length()); -// libs[i].path = prefixPath.string() + libs[i].path; -// } -// cout << "libs path after " << libs[i].path << endl; -// -// } - - for (int i = 0; i < (int)frameworks.size(); i++){ - cout << frameworks[i] << endl; + for (auto & f : frameworks) { + cout << f << endl; // knowing if we are system framework or not is important.... - bool bIsSystemFramework = false; - size_t foundUnixPath = frameworks[i].find('/'); - size_t foundWindowsPath = frameworks[i].find('\\'); + size_t foundUnixPath = f.find('/'); + size_t foundWindowsPath = f.find('\\'); if (foundUnixPath==std::string::npos && foundWindowsPath==std::string::npos){ bIsSystemFramework = true; // we have no "path" so we are system } if (bIsSystemFramework){ - ; // do we need to do anything here? - } else { - - - frameworks[i].erase (frameworks[i].begin(), frameworks[i].begin() + containedPath.string().length()); - - int init = 0; - int end = frameworks[i].rfind(fs::path("/").make_preferred().string()); - - string folder; - if (!isLocalAddon) { - folder = frameworks[i].substr(init, end); - } - else { - init = frameworks[i].find(name); - folder = ofFilePath::join("local_addons", frameworks[i].substr(init, end - init)); - } - - frameworks[i] = prefixPath.string() + frameworks[i]; - - cout << frameworks[i] << endl; - cout << folder << endl; +// f.erase (f.begin(), f.begin() + containedPath.string().length()); +// +// int init = 0; +// int end = f.rfind(fs::path("/").make_preferred().string()); +// +// string folder; +// if (!isLocalAddon) { +// folder = f.substr(init, end); +// } +// else { +// init = f.find(name); +// folder = ofFilePath::join("local_addons", f.substr(init, end - init)); +// } + +// f = prefixPath.string() + f; +// filesToFolders[f] = folder; +// cout << "prefixPath = " << prefixPath << endl; +// cout << "containedPath = " << containedPath << endl; +// cout << "f = " << f << endl; +// cout << "folder = " < & frameworks, std::string platform){ - cout << "getFrameworksRecursively" << endl; - ofDirectory dir; - dir.listDir(path); - - for (int i = 0; i < dir.size(); i++){ - - ofFile temp(dir.getFile(i)); - - if (temp.isDirectory()){ - //getLibsRecursively(dir.getPath(i), folderNames); - - // on osx, framework is a directory, let's not parse it.... - std::string ext = ""; - std::string first = ""; - splitFromLast(dir.getPath(i), ".", first, ext); - if (ext != "framework") - getFrameworksRecursively(dir.getPath(i), frameworks, platform); - else - frameworks.push_back(dir.getPath(i)); + for (const auto & entry : fs::directory_iterator(path)) { + auto f = entry.path(); + if (fs::is_directory(f)) { + if (f.extension() == ".framework") { + frameworks.emplace_back(f.string()); + } else { + getFrameworksRecursively(f, frameworks, platform); + } } - } - cout << frameworks.size() << endl; } From e331657eec56006c3669bb12fc6c7890b5728324 Mon Sep 17 00:00:00 2001 From: Dimitre Date: Thu, 13 Apr 2023 17:04:19 -0300 Subject: [PATCH 06/25] update --- commandLine/src/addons/ofAddon.cpp | 43 +- commandLine/src/cxxopts.hpp | 2823 ++++++++++++++++++++++++++++ commandLine/src/main.cpp | 42 +- 3 files changed, 2849 insertions(+), 59 deletions(-) create mode 100644 commandLine/src/cxxopts.hpp diff --git a/commandLine/src/addons/ofAddon.cpp b/commandLine/src/addons/ofAddon.cpp index 1d4f327b..0e4240be 100644 --- a/commandLine/src/addons/ofAddon.cpp +++ b/commandLine/src/addons/ofAddon.cpp @@ -566,13 +566,9 @@ bool ofAddon::fromFS(fs::path path, const std::string & platform){ for (auto & s : libFiles) { fs::path folder; auto srcFS = fs::path(prefixPath / fs::relative(s, containedPath)); - if (isLocalAddon) { - // FIXME: test if local addons is working ok - folder = fs::path("local_addons") / fs::path(s).parent_path(); folder = srcFS.parent_path(); } else { - folder = fs::path(s).parent_path(); folder = fs::relative(folder, containedPath); } @@ -581,7 +577,7 @@ bool ofAddon::fromFS(fs::path path, const std::string & platform){ filesToFolders[s] = folder.string(); } - + // changing libs folder from absolute to relative. for (auto & l : libs) { l.path = fs::path(prefixPath / fs::relative(l.path, containedPath)).string(); @@ -589,7 +585,7 @@ bool ofAddon::fromFS(fs::path path, const std::string & platform){ for (auto & f : frameworks) { - cout << f << endl; +// cout << f << endl; // knowing if we are system framework or not is important.... bool bIsSystemFramework = false; @@ -603,41 +599,16 @@ bool ofAddon::fromFS(fs::path path, const std::string & platform){ if (bIsSystemFramework){ ; // do we need to do anything here? } else { -// f.erase (f.begin(), f.begin() + containedPath.string().length()); -// -// int init = 0; -// int end = f.rfind(fs::path("/").make_preferred().string()); -// -// string folder; -// if (!isLocalAddon) { -// folder = f.substr(init, end); -// } -// else { -// init = f.find(name); -// folder = ofFilePath::join("local_addons", f.substr(init, end - init)); -// } - -// f = prefixPath.string() + f; -// filesToFolders[f] = folder; -// cout << "prefixPath = " << prefixPath << endl; -// cout << "containedPath = " << containedPath << endl; -// cout << "f = " << f << endl; -// cout << "folder = " < +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CXXOPTS_NO_EXCEPTIONS +#include +#endif + +#if defined(__GNUC__) && !defined(__clang__) +# if (__GNUC__ * 10 + __GNUC_MINOR__) < 49 +# define CXXOPTS_NO_REGEX true +# endif +#endif +#if defined(_MSC_VER) && !defined(__clang__) +#define CXXOPTS_LINKONCE_CONST __declspec(selectany) extern +#define CXXOPTS_LINKONCE __declspec(selectany) extern +#else +#define CXXOPTS_LINKONCE_CONST +#define CXXOPTS_LINKONCE +#endif + +#ifndef CXXOPTS_NO_REGEX +# include +#endif // CXXOPTS_NO_REGEX + +// Nonstandard before C++17, which is coincidentally what we also need for +#ifdef __has_include +# if __has_include() +# include +# ifdef __cpp_lib_optional +# define CXXOPTS_HAS_OPTIONAL +# endif +# endif +#endif + +#if __cplusplus >= 201603L +#define CXXOPTS_NODISCARD [[nodiscard]] +#else +#define CXXOPTS_NODISCARD +#endif + +#ifndef CXXOPTS_VECTOR_DELIMITER +#define CXXOPTS_VECTOR_DELIMITER ',' +#endif + +#define CXXOPTS__VERSION_MAJOR 3 +#define CXXOPTS__VERSION_MINOR 1 +#define CXXOPTS__VERSION_PATCH 1 + +#if (__GNUC__ < 10 || (__GNUC__ == 10 && __GNUC_MINOR__ < 1)) && __GNUC__ >= 6 + #define CXXOPTS_NULL_DEREF_IGNORE +#endif + +#if defined(__GNUC__) +#define DO_PRAGMA(x) _Pragma(#x) +#define CXXOPTS_DIAGNOSTIC_PUSH DO_PRAGMA(GCC diagnostic push) +#define CXXOPTS_DIAGNOSTIC_POP DO_PRAGMA(GCC diagnostic pop) +#define CXXOPTS_IGNORE_WARNING(x) DO_PRAGMA(GCC diagnostic ignored x) +#else +// define other compilers here if needed +#define CXXOPTS_DIAGNOSTIC_PUSH +#define CXXOPTS_DIAGNOSTIC_POP +#define CXXOPTS_IGNORE_WARNING(x) +#endif + +#ifdef CXXOPTS_NO_RTTI +#define CXXOPTS_RTTI_CAST static_cast +#else +#define CXXOPTS_RTTI_CAST dynamic_cast +#endif + +namespace cxxopts { +static constexpr struct { + uint8_t major, minor, patch; +} version = { + CXXOPTS__VERSION_MAJOR, + CXXOPTS__VERSION_MINOR, + CXXOPTS__VERSION_PATCH +}; +} // namespace cxxopts + +//when we ask cxxopts to use Unicode, help strings are processed using ICU, +//which results in the correct lengths being computed for strings when they +//are formatted for the help output +//it is necessary to make sure that can be found by the +//compiler, and that icu-uc is linked in to the binary. + +#ifdef CXXOPTS_USE_UNICODE +#include + +namespace cxxopts { + +using String = icu::UnicodeString; + +inline +String +toLocalString(std::string s) +{ + return icu::UnicodeString::fromUTF8(std::move(s)); +} + +// GNU GCC with -Weffc++ will issue a warning regarding the upcoming class, we want to silence it: +// warning: base class 'class std::enable_shared_from_this' has accessible non-virtual destructor +CXXOPTS_DIAGNOSTIC_PUSH +CXXOPTS_IGNORE_WARNING("-Wnon-virtual-dtor") +// This will be ignored under other compilers like LLVM clang. +class UnicodeStringIterator +{ + public: + + using iterator_category = std::forward_iterator_tag; + using value_type = int32_t; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using reference = value_type&; + + UnicodeStringIterator(const icu::UnicodeString* string, int32_t pos) + : s(string) + , i(pos) + { + } + + value_type + operator*() const + { + return s->char32At(i); + } + + bool + operator==(const UnicodeStringIterator& rhs) const + { + return s == rhs.s && i == rhs.i; + } + + bool + operator!=(const UnicodeStringIterator& rhs) const + { + return !(*this == rhs); + } + + UnicodeStringIterator& + operator++() + { + ++i; + return *this; + } + + UnicodeStringIterator + operator+(int32_t v) + { + return UnicodeStringIterator(s, i + v); + } + + private: + const icu::UnicodeString* s; + int32_t i; +}; +CXXOPTS_DIAGNOSTIC_POP + +inline +String& +stringAppend(String&s, String a) +{ + return s.append(std::move(a)); +} + +inline +String& +stringAppend(String& s, std::size_t n, UChar32 c) +{ + for (std::size_t i = 0; i != n; ++i) + { + s.append(c); + } + + return s; +} + +template +String& +stringAppend(String& s, Iterator begin, Iterator end) +{ + while (begin != end) + { + s.append(*begin); + ++begin; + } + + return s; +} + +inline +size_t +stringLength(const String& s) +{ + return static_cast(s.length()); +} + +inline +std::string +toUTF8String(const String& s) +{ + std::string result; + s.toUTF8String(result); + + return result; +} + +inline +bool +empty(const String& s) +{ + return s.isEmpty(); +} + +} // namespace cxxopts + +namespace std { + +inline +cxxopts::UnicodeStringIterator +begin(const icu::UnicodeString& s) +{ + return cxxopts::UnicodeStringIterator(&s, 0); +} + +inline +cxxopts::UnicodeStringIterator +end(const icu::UnicodeString& s) +{ + return cxxopts::UnicodeStringIterator(&s, s.length()); +} + +} // namespace std + +//ifdef CXXOPTS_USE_UNICODE +#else + +namespace cxxopts { + +using String = std::string; + +template +T +toLocalString(T&& t) +{ + return std::forward(t); +} + +inline +std::size_t +stringLength(const String& s) +{ + return s.length(); +} + +inline +String& +stringAppend(String&s, const String& a) +{ + return s.append(a); +} + +inline +String& +stringAppend(String& s, std::size_t n, char c) +{ + return s.append(n, c); +} + +template +String& +stringAppend(String& s, Iterator begin, Iterator end) +{ + return s.append(begin, end); +} + +template +std::string +toUTF8String(T&& t) +{ + return std::forward(t); +} + +inline +bool +empty(const std::string& s) +{ + return s.empty(); +} + +} // namespace cxxopts + +//ifdef CXXOPTS_USE_UNICODE +#endif + +namespace cxxopts { + +namespace { +CXXOPTS_LINKONCE_CONST std::string LQUOTE("\'"); +CXXOPTS_LINKONCE_CONST std::string RQUOTE("\'"); +} // namespace + +// GNU GCC with -Weffc++ will issue a warning regarding the upcoming class, we +// want to silence it: warning: base class 'class +// std::enable_shared_from_this' has accessible non-virtual +// destructor This will be ignored under other compilers like LLVM clang. +CXXOPTS_DIAGNOSTIC_PUSH +CXXOPTS_IGNORE_WARNING("-Wnon-virtual-dtor") + +// some older versions of GCC warn under this warning +CXXOPTS_IGNORE_WARNING("-Weffc++") +class Value : public std::enable_shared_from_this +{ + public: + + virtual ~Value() = default; + + virtual + std::shared_ptr + clone() const = 0; + + virtual void + parse(const std::string& text) const = 0; + + virtual void + parse() const = 0; + + virtual bool + has_default() const = 0; + + virtual bool + is_container() const = 0; + + virtual bool + has_implicit() const = 0; + + virtual std::string + get_default_value() const = 0; + + virtual std::string + get_implicit_value() const = 0; + + virtual std::shared_ptr + default_value(const std::string& value) = 0; + + virtual std::shared_ptr + implicit_value(const std::string& value) = 0; + + virtual std::shared_ptr + no_implicit_value() = 0; + + virtual bool + is_boolean() const = 0; +}; + +CXXOPTS_DIAGNOSTIC_POP + +namespace exceptions { + +class exception : public std::exception +{ + public: + explicit exception(std::string message) + : m_message(std::move(message)) + { + } + + CXXOPTS_NODISCARD + const char* + what() const noexcept override + { + return m_message.c_str(); + } + + private: + std::string m_message; +}; + +class specification : public exception +{ + public: + + explicit specification(const std::string& message) + : exception(message) + { + } +}; + +class parsing : public exception +{ + public: + explicit parsing(const std::string& message) + : exception(message) + { + } +}; + +class option_already_exists : public specification +{ + public: + explicit option_already_exists(const std::string& option) + : specification("Option " + LQUOTE + option + RQUOTE + " already exists") + { + } +}; + +class invalid_option_format : public specification +{ + public: + explicit invalid_option_format(const std::string& format) + : specification("Invalid option format " + LQUOTE + format + RQUOTE) + { + } +}; + +class invalid_option_syntax : public parsing { + public: + explicit invalid_option_syntax(const std::string& text) + : parsing("Argument " + LQUOTE + text + RQUOTE + + " starts with a - but has incorrect syntax") + { + } +}; + +class no_such_option : public parsing +{ + public: + explicit no_such_option(const std::string& option) + : parsing("Option " + LQUOTE + option + RQUOTE + " does not exist") + { + } +}; + +class missing_argument : public parsing +{ + public: + explicit missing_argument(const std::string& option) + : parsing( + "Option " + LQUOTE + option + RQUOTE + " is missing an argument" + ) + { + } +}; + +class option_requires_argument : public parsing +{ + public: + explicit option_requires_argument(const std::string& option) + : parsing( + "Option " + LQUOTE + option + RQUOTE + " requires an argument" + ) + { + } +}; + +class gratuitous_argument_for_option : public parsing +{ + public: + gratuitous_argument_for_option + ( + const std::string& option, + const std::string& arg + ) + : parsing( + "Option " + LQUOTE + option + RQUOTE + + " does not take an argument, but argument " + + LQUOTE + arg + RQUOTE + " given" + ) + { + } +}; + +class requested_option_not_present : public parsing +{ + public: + explicit requested_option_not_present(const std::string& option) + : parsing("Option " + LQUOTE + option + RQUOTE + " not present") + { + } +}; + +class option_has_no_value : public exception +{ + public: + explicit option_has_no_value(const std::string& option) + : exception( + !option.empty() ? + ("Option " + LQUOTE + option + RQUOTE + " has no value") : + "Option has no value") + { + } +}; + +class incorrect_argument_type : public parsing +{ + public: + explicit incorrect_argument_type + ( + const std::string& arg + ) + : parsing( + "Argument " + LQUOTE + arg + RQUOTE + " failed to parse" + ) + { + } +}; + +} // namespace exceptions + + +template +void throw_or_mimic(const std::string& text) +{ + static_assert(std::is_base_of::value, + "throw_or_mimic only works on std::exception and " + "deriving classes"); + +#ifndef CXXOPTS_NO_EXCEPTIONS + // If CXXOPTS_NO_EXCEPTIONS is not defined, just throw + throw T{text}; +#else + // Otherwise manually instantiate the exception, print what() to stderr, + // and exit + T exception{text}; + std::cerr << exception.what() << std::endl; + std::exit(EXIT_FAILURE); +#endif +} + +using OptionNames = std::vector; + +namespace values { + +namespace parser_tool { + +struct IntegerDesc +{ + std::string negative = ""; + std::string base = ""; + std::string value = ""; +}; +struct ArguDesc { + std::string arg_name = ""; + bool grouping = false; + bool set_value = false; + std::string value = ""; +}; + +#ifdef CXXOPTS_NO_REGEX +inline IntegerDesc SplitInteger(const std::string &text) +{ + if (text.empty()) + { + throw_or_mimic(text); + } + IntegerDesc desc; + const char *pdata = text.c_str(); + if (*pdata == '-') + { + pdata += 1; + desc.negative = "-"; + } + if (strncmp(pdata, "0x", 2) == 0) + { + pdata += 2; + desc.base = "0x"; + } + if (*pdata != '\0') + { + desc.value = std::string(pdata); + } + else + { + throw_or_mimic(text); + } + return desc; +} + +inline bool IsTrueText(const std::string &text) +{ + const char *pdata = text.c_str(); + if (*pdata == 't' || *pdata == 'T') + { + pdata += 1; + if (strncmp(pdata, "rue\0", 4) == 0) + { + return true; + } + } + else if (strncmp(pdata, "1\0", 2) == 0) + { + return true; + } + return false; +} + +inline bool IsFalseText(const std::string &text) +{ + const char *pdata = text.c_str(); + if (*pdata == 'f' || *pdata == 'F') + { + pdata += 1; + if (strncmp(pdata, "alse\0", 5) == 0) + { + return true; + } + } + else if (strncmp(pdata, "0\0", 2) == 0) + { + return true; + } + return false; +} + +inline OptionNames split_option_names(const std::string &text) +{ + OptionNames split_names; + + std::string::size_type token_start_pos = 0; + auto length = text.length(); + + if (length == 0) + { + throw_or_mimic(text); + } + + while (token_start_pos < length) { + const auto &npos = std::string::npos; + auto next_non_space_pos = text.find_first_not_of(' ', token_start_pos); + if (next_non_space_pos == npos) { + throw_or_mimic(text); + } + token_start_pos = next_non_space_pos; + auto next_delimiter_pos = text.find(',', token_start_pos); + if (next_delimiter_pos == token_start_pos) { + throw_or_mimic(text); + } + if (next_delimiter_pos == npos) { + next_delimiter_pos = length; + } + auto token_length = next_delimiter_pos - token_start_pos; + // validate the token itself matches the regex /([:alnum:][-_[:alnum:]]*/ + { + const char* option_name_valid_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "_-.?"; + + if (!std::isalnum(text[token_start_pos], std::locale::classic()) || + text.find_first_not_of(option_name_valid_chars, token_start_pos) < next_delimiter_pos) { + throw_or_mimic(text); + } + } + split_names.emplace_back(text.substr(token_start_pos, token_length)); + token_start_pos = next_delimiter_pos + 1; + } + return split_names; +} + +inline ArguDesc ParseArgument(const char *arg, bool &matched) +{ + ArguDesc argu_desc; + const char *pdata = arg; + matched = false; + if (strncmp(pdata, "--", 2) == 0) + { + pdata += 2; + if (isalnum(*pdata, std::locale::classic())) + { + argu_desc.arg_name.push_back(*pdata); + pdata += 1; + while (isalnum(*pdata, std::locale::classic()) || *pdata == '-' || *pdata == '_') + { + argu_desc.arg_name.push_back(*pdata); + pdata += 1; + } + if (argu_desc.arg_name.length() > 1) + { + if (*pdata == '=') + { + argu_desc.set_value = true; + pdata += 1; + if (*pdata != '\0') + { + argu_desc.value = std::string(pdata); + } + matched = true; + } + else if (*pdata == '\0') + { + matched = true; + } + } + } + } + else if (strncmp(pdata, "-", 1) == 0) + { + pdata += 1; + argu_desc.grouping = true; + while (isalnum(*pdata, std::locale::classic())) + { + argu_desc.arg_name.push_back(*pdata); + pdata += 1; + } + matched = !argu_desc.arg_name.empty() && *pdata == '\0'; + } + return argu_desc; +} + +#else // CXXOPTS_NO_REGEX + +namespace { +CXXOPTS_LINKONCE +std::basic_regex integer_pattern + ("(-)?(0x)?([0-9a-zA-Z]+)|((0x)?0)"); +CXXOPTS_LINKONCE +std::basic_regex truthy_pattern + ("(t|T)(rue)?|1"); +CXXOPTS_LINKONCE +std::basic_regex falsy_pattern + ("(f|F)(alse)?|0"); +CXXOPTS_LINKONCE +std::basic_regex option_matcher + ("--([[:alnum:]][-_[:alnum:]\\.]+)(=(.*))?|-([[:alnum:]].*)"); +CXXOPTS_LINKONCE +std::basic_regex option_specifier + ("([[:alnum:]][-_[:alnum:]\\.]*)(,[ ]*[[:alnum:]][-_[:alnum:]]*)*"); +CXXOPTS_LINKONCE +std::basic_regex option_specifier_separator(", *"); + +} // namespace + +inline IntegerDesc SplitInteger(const std::string &text) +{ + std::smatch match; + std::regex_match(text, match, integer_pattern); + + if (match.length() == 0) + { + throw_or_mimic(text); + } + + IntegerDesc desc; + desc.negative = match[1]; + desc.base = match[2]; + desc.value = match[3]; + + if (match.length(4) > 0) + { + desc.base = match[5]; + desc.value = "0"; + return desc; + } + + return desc; +} + +inline bool IsTrueText(const std::string &text) +{ + std::smatch result; + std::regex_match(text, result, truthy_pattern); + return !result.empty(); +} + +inline bool IsFalseText(const std::string &text) +{ + std::smatch result; + std::regex_match(text, result, falsy_pattern); + return !result.empty(); +} + +// Gets the option names specified via a single, comma-separated string, +// and returns the separate, space-discarded, non-empty names +// (without considering which or how many are single-character) +inline OptionNames split_option_names(const std::string &text) +{ + if (!std::regex_match(text.c_str(), option_specifier)) + { + throw_or_mimic(text); + } + + OptionNames split_names; + + constexpr int use_non_matches { -1 }; + auto token_iterator = std::sregex_token_iterator( + text.begin(), text.end(), option_specifier_separator, use_non_matches); + std::copy(token_iterator, std::sregex_token_iterator(), std::back_inserter(split_names)); + return split_names; +} + +inline ArguDesc ParseArgument(const char *arg, bool &matched) +{ + std::match_results result; + std::regex_match(arg, result, option_matcher); + matched = !result.empty(); + + ArguDesc argu_desc; + if (matched) { + argu_desc.arg_name = result[1].str(); + argu_desc.set_value = result[2].length() > 0; + argu_desc.value = result[3].str(); + if (result[4].length() > 0) + { + argu_desc.grouping = true; + argu_desc.arg_name = result[4].str(); + } + } + + return argu_desc; +} + +#endif // CXXOPTS_NO_REGEX +#undef CXXOPTS_NO_REGEX +} // namespace parser_tool + +namespace detail { + +template +struct SignedCheck; + +template +struct SignedCheck +{ + template + void + operator()(bool negative, U u, const std::string& text) + { + if (negative) + { + if (u > static_cast((std::numeric_limits::min)())) + { + throw_or_mimic(text); + } + } + else + { + if (u > static_cast((std::numeric_limits::max)())) + { + throw_or_mimic(text); + } + } + } +}; + +template +struct SignedCheck +{ + template + void + operator()(bool, U, const std::string&) const {} +}; + +template +void +check_signed_range(bool negative, U value, const std::string& text) +{ + SignedCheck::is_signed>()(negative, value, text); +} + +} // namespace detail + +template +void +checked_negate(R& r, T&& t, const std::string&, std::true_type) +{ + // if we got to here, then `t` is a positive number that fits into + // `R`. So to avoid MSVC C4146, we first cast it to `R`. + // See https://github.com/jarro2783/cxxopts/issues/62 for more details. + r = static_cast(-static_cast(t-1)-1); +} + +template +void +checked_negate(R&, T&&, const std::string& text, std::false_type) +{ + throw_or_mimic(text); +} + +template +void +integer_parser(const std::string& text, T& value) +{ + parser_tool::IntegerDesc int_desc = parser_tool::SplitInteger(text); + + using US = typename std::make_unsigned::type; + constexpr bool is_signed = std::numeric_limits::is_signed; + + const bool negative = int_desc.negative.length() > 0; + const uint8_t base = int_desc.base.length() > 0 ? 16 : 10; + const std::string & value_match = int_desc.value; + + US result = 0; + + for (char ch : value_match) + { + US digit = 0; + + if (ch >= '0' && ch <= '9') + { + digit = static_cast(ch - '0'); + } + else if (base == 16 && ch >= 'a' && ch <= 'f') + { + digit = static_cast(ch - 'a' + 10); + } + else if (base == 16 && ch >= 'A' && ch <= 'F') + { + digit = static_cast(ch - 'A' + 10); + } + else + { + throw_or_mimic(text); + } + + const US next = static_cast(result * base + digit); + if (result > next) + { + throw_or_mimic(text); + } + + result = next; + } + + detail::check_signed_range(negative, result, text); + + if (negative) + { + checked_negate(value, result, text, std::integral_constant()); + } + else + { + value = static_cast(result); + } +} + +template +void stringstream_parser(const std::string& text, T& value) +{ + std::stringstream in(text); + in >> value; + if (!in) { + throw_or_mimic(text); + } +} + +template ::value>::type* = nullptr + > +void parse_value(const std::string& text, T& value) +{ + integer_parser(text, value); +} + +inline +void +parse_value(const std::string& text, bool& value) +{ + if (parser_tool::IsTrueText(text)) + { + value = true; + return; + } + + if (parser_tool::IsFalseText(text)) + { + value = false; + return; + } + + throw_or_mimic(text); +} + +inline +void +parse_value(const std::string& text, std::string& value) +{ + value = text; +} + +// The fallback parser. It uses the stringstream parser to parse all types +// that have not been overloaded explicitly. It has to be placed in the +// source code before all other more specialized templates. +template ::value>::type* = nullptr + > +void +parse_value(const std::string& text, T& value) { + stringstream_parser(text, value); +} + +template +void +parse_value(const std::string& text, std::vector& value) +{ + if (text.empty()) { + T v; + parse_value(text, v); + value.emplace_back(std::move(v)); + return; + } + std::stringstream in(text); + std::string token; + while(!in.eof() && std::getline(in, token, CXXOPTS_VECTOR_DELIMITER)) { + T v; + parse_value(token, v); + value.emplace_back(std::move(v)); + } +} + +#ifdef CXXOPTS_HAS_OPTIONAL +template +void +parse_value(const std::string& text, std::optional& value) +{ + T result; + parse_value(text, result); + value = std::move(result); +} +#endif + +inline +void parse_value(const std::string& text, char& c) +{ + if (text.length() != 1) + { + throw_or_mimic(text); + } + + c = text[0]; +} + +template +struct type_is_container +{ + static constexpr bool value = false; +}; + +template +struct type_is_container> +{ + static constexpr bool value = true; +}; + +template +class abstract_value : public Value +{ + using Self = abstract_value; + + public: + abstract_value() + : m_result(std::make_shared()) + , m_store(m_result.get()) + { + } + + explicit abstract_value(T* t) + : m_store(t) + { + } + + ~abstract_value() override = default; + + abstract_value& operator=(const abstract_value&) = default; + + abstract_value(const abstract_value& rhs) + { + if (rhs.m_result) + { + m_result = std::make_shared(); + m_store = m_result.get(); + } + else + { + m_store = rhs.m_store; + } + + m_default = rhs.m_default; + m_implicit = rhs.m_implicit; + m_default_value = rhs.m_default_value; + m_implicit_value = rhs.m_implicit_value; + } + + void + parse(const std::string& text) const override + { + parse_value(text, *m_store); + } + + bool + is_container() const override + { + return type_is_container::value; + } + + void + parse() const override + { + parse_value(m_default_value, *m_store); + } + + bool + has_default() const override + { + return m_default; + } + + bool + has_implicit() const override + { + return m_implicit; + } + + std::shared_ptr + default_value(const std::string& value) override + { + m_default = true; + m_default_value = value; + return shared_from_this(); + } + + std::shared_ptr + implicit_value(const std::string& value) override + { + m_implicit = true; + m_implicit_value = value; + return shared_from_this(); + } + + std::shared_ptr + no_implicit_value() override + { + m_implicit = false; + return shared_from_this(); + } + + std::string + get_default_value() const override + { + return m_default_value; + } + + std::string + get_implicit_value() const override + { + return m_implicit_value; + } + + bool + is_boolean() const override + { + return std::is_same::value; + } + + const T& + get() const + { + if (m_store == nullptr) + { + return *m_result; + } + return *m_store; + } + + protected: + std::shared_ptr m_result{}; + T* m_store{}; + + bool m_default = false; + bool m_implicit = false; + + std::string m_default_value{}; + std::string m_implicit_value{}; +}; + +template +class standard_value : public abstract_value +{ + public: + using abstract_value::abstract_value; + + CXXOPTS_NODISCARD + std::shared_ptr + clone() const override + { + return std::make_shared>(*this); + } +}; + +template <> +class standard_value : public abstract_value +{ + public: + ~standard_value() override = default; + + standard_value() + { + set_default_and_implicit(); + } + + explicit standard_value(bool* b) + : abstract_value(b) + { + m_implicit = true; + m_implicit_value = "true"; + } + + std::shared_ptr + clone() const override + { + return std::make_shared>(*this); + } + + private: + + void + set_default_and_implicit() + { + m_default = true; + m_default_value = "false"; + m_implicit = true; + m_implicit_value = "true"; + } +}; + +} // namespace values + +template +std::shared_ptr +value() +{ + return std::make_shared>(); +} + +template +std::shared_ptr +value(T& t) +{ + return std::make_shared>(&t); +} + +class OptionAdder; + +CXXOPTS_NODISCARD +inline +const std::string& +first_or_empty(const OptionNames& long_names) +{ + static const std::string empty{""}; + return long_names.empty() ? empty : long_names.front(); +} + +class OptionDetails +{ + public: + OptionDetails + ( + std::string short_, + OptionNames long_, + String desc, + std::shared_ptr val + ) + : m_short(std::move(short_)) + , m_long(std::move(long_)) + , m_desc(std::move(desc)) + , m_value(std::move(val)) + , m_count(0) + { + m_hash = std::hash{}(first_long_name() + m_short); + } + + OptionDetails(const OptionDetails& rhs) + : m_desc(rhs.m_desc) + , m_value(rhs.m_value->clone()) + , m_count(rhs.m_count) + { + } + + OptionDetails(OptionDetails&& rhs) = default; + + CXXOPTS_NODISCARD + const String& + description() const + { + return m_desc; + } + + CXXOPTS_NODISCARD + const Value& + value() const { + return *m_value; + } + + CXXOPTS_NODISCARD + std::shared_ptr + make_storage() const + { + return m_value->clone(); + } + + CXXOPTS_NODISCARD + const std::string& + short_name() const + { + return m_short; + } + + CXXOPTS_NODISCARD + const std::string& + first_long_name() const + { + return first_or_empty(m_long); + } + + CXXOPTS_NODISCARD + const std::string& + essential_name() const + { + return m_long.empty() ? m_short : m_long.front(); + } + + CXXOPTS_NODISCARD + const OptionNames & + long_names() const + { + return m_long; + } + + std::size_t + hash() const + { + return m_hash; + } + + private: + std::string m_short{}; + OptionNames m_long{}; + String m_desc{}; + std::shared_ptr m_value{}; + int m_count; + + std::size_t m_hash{}; +}; + +struct HelpOptionDetails +{ + std::string s; + OptionNames l; + String desc; + bool has_default; + std::string default_value; + bool has_implicit; + std::string implicit_value; + std::string arg_help; + bool is_container; + bool is_boolean; +}; + +struct HelpGroupDetails +{ + std::string name{}; + std::string description{}; + std::vector options{}; +}; + +class OptionValue +{ + public: + void + parse + ( + const std::shared_ptr& details, + const std::string& text + ) + { + ensure_value(details); + ++m_count; + m_value->parse(text); + m_long_names = &details->long_names(); + } + + void + parse_default(const std::shared_ptr& details) + { + ensure_value(details); + m_default = true; + m_long_names = &details->long_names(); + m_value->parse(); + } + + void + parse_no_value(const std::shared_ptr& details) + { + m_long_names = &details->long_names(); + } + +#if defined(CXXOPTS_NULL_DEREF_IGNORE) +CXXOPTS_DIAGNOSTIC_PUSH +CXXOPTS_IGNORE_WARNING("-Wnull-dereference") +#endif + + CXXOPTS_NODISCARD + std::size_t + count() const noexcept + { + return m_count; + } + +#if defined(CXXOPTS_NULL_DEREF_IGNORE) +CXXOPTS_DIAGNOSTIC_POP +#endif + + // TODO: maybe default options should count towards the number of arguments + CXXOPTS_NODISCARD + bool + has_default() const noexcept + { + return m_default; + } + + template + const T& + as() const + { + if (m_value == nullptr) { + throw_or_mimic( + m_long_names == nullptr ? "" : first_or_empty(*m_long_names)); + } + + return CXXOPTS_RTTI_CAST&>(*m_value).get(); + } + + private: + void + ensure_value(const std::shared_ptr& details) + { + if (m_value == nullptr) + { + m_value = details->make_storage(); + } + } + + + const OptionNames * m_long_names = nullptr; + // Holding this pointer is safe, since OptionValue's only exist in key-value pairs, + // where the key has the string we point to. + std::shared_ptr m_value{}; + std::size_t m_count = 0; + bool m_default = false; +}; + +class KeyValue +{ + public: + KeyValue(std::string key_, std::string value_) noexcept + : m_key(std::move(key_)) + , m_value(std::move(value_)) + { + } + + CXXOPTS_NODISCARD + const std::string& + key() const + { + return m_key; + } + + CXXOPTS_NODISCARD + const std::string& + value() const + { + return m_value; + } + + template + T + as() const + { + T result; + values::parse_value(m_value, result); + return result; + } + + private: + std::string m_key; + std::string m_value; +}; + +using ParsedHashMap = std::unordered_map; +using NameHashMap = std::unordered_map; + +class ParseResult +{ + public: + class Iterator + { + public: + using iterator_category = std::forward_iterator_tag; + using value_type = KeyValue; + using difference_type = void; + using pointer = const KeyValue*; + using reference = const KeyValue&; + + Iterator() = default; + Iterator(const Iterator&) = default; + +// GCC complains about m_iter not being initialised in the member +// initializer list +CXXOPTS_DIAGNOSTIC_PUSH +CXXOPTS_IGNORE_WARNING("-Weffc++") + Iterator(const ParseResult *pr, bool end=false) + : m_pr(pr) + { + if (end) + { + m_sequential = false; + m_iter = m_pr->m_defaults.end(); + } + else + { + m_sequential = true; + m_iter = m_pr->m_sequential.begin(); + + if (m_iter == m_pr->m_sequential.end()) + { + m_sequential = false; + m_iter = m_pr->m_defaults.begin(); + } + } + } +CXXOPTS_DIAGNOSTIC_POP + + Iterator& operator++() + { + ++m_iter; + if(m_sequential && m_iter == m_pr->m_sequential.end()) + { + m_sequential = false; + m_iter = m_pr->m_defaults.begin(); + return *this; + } + return *this; + } + + Iterator operator++(int) + { + Iterator retval = *this; + ++(*this); + return retval; + } + + bool operator==(const Iterator& other) const + { + return (m_sequential == other.m_sequential) && (m_iter == other.m_iter); + } + + bool operator!=(const Iterator& other) const + { + return !(*this == other); + } + + const KeyValue& operator*() + { + return *m_iter; + } + + const KeyValue* operator->() + { + return m_iter.operator->(); + } + + private: + const ParseResult* m_pr; + std::vector::const_iterator m_iter; + bool m_sequential = true; + }; + + ParseResult() = default; + ParseResult(const ParseResult&) = default; + + ParseResult(NameHashMap&& keys, ParsedHashMap&& values, std::vector sequential, + std::vector default_opts, std::vector&& unmatched_args) + : m_keys(std::move(keys)) + , m_values(std::move(values)) + , m_sequential(std::move(sequential)) + , m_defaults(std::move(default_opts)) + , m_unmatched(std::move(unmatched_args)) + { + } + + ParseResult& operator=(ParseResult&&) = default; + ParseResult& operator=(const ParseResult&) = default; + + Iterator + begin() const + { + return Iterator(this); + } + + Iterator + end() const + { + return Iterator(this, true); + } + + std::size_t + count(const std::string& o) const + { + auto iter = m_keys.find(o); + if (iter == m_keys.end()) + { + return 0; + } + + auto viter = m_values.find(iter->second); + + if (viter == m_values.end()) + { + return 0; + } + + return viter->second.count(); + } + + const OptionValue& + operator[](const std::string& option) const + { + auto iter = m_keys.find(option); + + if (iter == m_keys.end()) + { + throw_or_mimic(option); + } + + auto viter = m_values.find(iter->second); + + if (viter == m_values.end()) + { + throw_or_mimic(option); + } + + return viter->second; + } + + const std::vector& + arguments() const + { + return m_sequential; + } + + const std::vector& + unmatched() const + { + return m_unmatched; + } + + const std::vector& + defaults() const + { + return m_defaults; + } + + const std::string + arguments_string() const + { + std::string result; + for(const auto& kv: m_sequential) + { + result += kv.key() + " = " + kv.value() + "\n"; + } + for(const auto& kv: m_defaults) + { + result += kv.key() + " = " + kv.value() + " " + "(default)" + "\n"; + } + return result; + } + + private: + NameHashMap m_keys{}; + ParsedHashMap m_values{}; + std::vector m_sequential{}; + std::vector m_defaults{}; + std::vector m_unmatched{}; +}; + +struct Option +{ + Option + ( + std::string opts, + std::string desc, + std::shared_ptr value = ::cxxopts::value(), + std::string arg_help = "" + ) + : opts_(std::move(opts)) + , desc_(std::move(desc)) + , value_(std::move(value)) + , arg_help_(std::move(arg_help)) + { + } + + std::string opts_; + std::string desc_; + std::shared_ptr value_; + std::string arg_help_; +}; + +using OptionMap = std::unordered_map>; +using PositionalList = std::vector; +using PositionalListIterator = PositionalList::const_iterator; + +class OptionParser +{ + public: + OptionParser(const OptionMap& options, const PositionalList& positional, bool allow_unrecognised) + : m_options(options) + , m_positional(positional) + , m_allow_unrecognised(allow_unrecognised) + { + } + + ParseResult + parse(int argc, const char* const* argv); + + bool + consume_positional(const std::string& a, PositionalListIterator& next); + + void + checked_parse_arg + ( + int argc, + const char* const* argv, + int& current, + const std::shared_ptr& value, + const std::string& name + ); + + void + add_to_option(OptionMap::const_iterator iter, const std::string& option, const std::string& arg); + + void + parse_option + ( + const std::shared_ptr& value, + const std::string& name, + const std::string& arg = "" + ); + + void + parse_default(const std::shared_ptr& details); + + void + parse_no_value(const std::shared_ptr& details); + + private: + + void finalise_aliases(); + + const OptionMap& m_options; + const PositionalList& m_positional; + + std::vector m_sequential{}; + std::vector m_defaults{}; + bool m_allow_unrecognised; + + ParsedHashMap m_parsed{}; + NameHashMap m_keys{}; +}; + +class Options +{ + public: + + explicit Options(std::string program_name, std::string help_string = "") + : m_program(std::move(program_name)) + , m_help_string(toLocalString(std::move(help_string))) + , m_custom_help("[OPTION...]") + , m_positional_help("positional parameters") + , m_show_positional(false) + , m_allow_unrecognised(false) + , m_width(76) + , m_tab_expansion(false) + , m_options(std::make_shared()) + { + } + + Options& + positional_help(std::string help_text) + { + m_positional_help = std::move(help_text); + return *this; + } + + Options& + custom_help(std::string help_text) + { + m_custom_help = std::move(help_text); + return *this; + } + + Options& + show_positional_help() + { + m_show_positional = true; + return *this; + } + + Options& + allow_unrecognised_options() + { + m_allow_unrecognised = true; + return *this; + } + + Options& + set_width(std::size_t width) + { + m_width = width; + return *this; + } + + Options& + set_tab_expansion(bool expansion=true) + { + m_tab_expansion = expansion; + return *this; + } + + ParseResult + parse(int argc, const char* const* argv); + + OptionAdder + add_options(std::string group = ""); + + void + add_options + ( + const std::string& group, + std::initializer_list