summaryrefslogtreecommitdiff
path: root/subprojects/argspp
diff options
context:
space:
mode:
authorAndrew Opalach <andrew@akon.city> 2023-11-10 19:24:56 -0500
committerAndrew Opalach <andrew@akon.city> 2023-11-12 20:04:01 -0500
commitacd44bb898996ff4e36b39b10e870e40bcaf2634 (patch)
treefa452ee2e583566a4fd28a4ac204cf62a9a1a0bc /subprojects/argspp
parentf1a9751af6cc6aa0bac91e7f28fc43b1a4cb64fd (diff)
downloadmauri-acd44bb898996ff4e36b39b10e870e40bcaf2634.tar.gz
mauri-acd44bb898996ff4e36b39b10e870e40bcaf2634.tar.bz2
mauri-acd44bb898996ff4e36b39b10e870e40bcaf2634.zip
One last push
Signed-off-by: Andrew Opalach <andrew@akon.city>
Diffstat (limited to 'subprojects/argspp')
-rw-r--r--subprojects/argspp/args.cpp477
-rw-r--r--subprojects/argspp/args.h119
-rw-r--r--subprojects/argspp/meson.build5
3 files changed, 601 insertions, 0 deletions
diff --git a/subprojects/argspp/args.cpp b/subprojects/argspp/args.cpp
new file mode 100644
index 0000000..21f483e
--- /dev/null
+++ b/subprojects/argspp/args.cpp
@@ -0,0 +1,477 @@
+// -----------------------------------------------------------------------------
+// Args++: an argument-parsing library in portable C++11.
+// -----------------------------------------------------------------------------
+
+#include "args.h"
+
+#include <algorithm>
+#include <cctype>
+#include <iostream>
+#include <sstream>
+#include <deque>
+#include <set>
+
+using namespace std;
+using namespace args;
+
+
+// -----------------------------------------------------------------------------
+// Flags and Options.
+// -----------------------------------------------------------------------------
+
+
+struct args::Flag {
+ int count = 0;
+};
+
+
+struct args::Option {
+ vector<string> values;
+ string fallback;
+};
+
+
+// -----------------------------------------------------------------------------
+// ArgStream.
+// -----------------------------------------------------------------------------
+
+
+struct args::ArgStream {
+ deque<string> args;
+ void append(string const& arg);
+ string next();
+ bool hasNext();
+};
+
+
+void ArgStream::append(string const& arg) {
+ args.push_back(arg);
+}
+
+
+string ArgStream::next() {
+ string arg = args.front();
+ args.pop_front();
+ return arg;
+}
+
+
+bool ArgStream::hasNext() {
+ return args.size() > 0;
+}
+
+
+// -----------------------------------------------------------------------------
+// ArgParser: setup.
+// -----------------------------------------------------------------------------
+
+
+void ArgParser::flag(string const& name) {
+ Flag* flag = new Flag();
+ stringstream stream(name);
+ string alias;
+ while (stream >> alias) {
+ flags[alias] = flag;
+ }
+}
+
+
+void ArgParser::option(string const& name, string const& fallback) {
+ Option* option = new Option();
+ option->fallback = fallback;
+ stringstream stream(name);
+ string alias;
+ while (stream >> alias) {
+ options[alias] = option;
+ }
+}
+
+
+// -----------------------------------------------------------------------------
+// ArgParser: retrieve values.
+// -----------------------------------------------------------------------------
+
+
+bool ArgParser::found(string const& name) const {
+ if (flags.count(name) > 0) {
+ return flags.at(name)->count > 0;
+ }
+ if (options.count(name) > 0) {
+ return options.at(name)->values.size() > 0;
+ }
+ return false;
+}
+
+
+int ArgParser::count(string const& name) const {
+ if (flags.count(name) > 0) {
+ return flags.at(name)->count;
+ }
+ if (options.count(name) > 0) {
+ return options.at(name)->values.size();
+ }
+ return 0;
+}
+
+
+string ArgParser::value(string const& name) const {
+ if (options.count(name) > 0) {
+ if (options.at(name)->values.size() > 0) {
+ return options.at(name)->values.back();
+ }
+ return options.at(name)->fallback;
+ }
+ return string();
+}
+
+
+vector<string> ArgParser::values(string const& name) const {
+ if (options.count(name) > 0) {
+ return options.at(name)->values;
+ }
+ return vector<string>();
+}
+
+
+// -----------------------------------------------------------------------------
+// ArgParser: commands.
+// -----------------------------------------------------------------------------
+
+
+ArgParser& ArgParser::command(
+ string const& name,
+ string const& helptext,
+ void (*callback)(string cmd_name, ArgParser& cmd_parser)) {
+
+ ArgParser *parser = new ArgParser();
+ parser->helptext = helptext;
+ parser->callback = callback;
+
+ stringstream stream(name);
+ string alias;
+
+ while (stream >> alias) {
+ commands[alias] = parser;
+ }
+
+ return *parser;
+}
+
+
+bool ArgParser::commandFound() {
+ return command_name != "";
+}
+
+
+string ArgParser::commandName() {
+ return command_name;
+}
+
+
+ArgParser& ArgParser::commandParser() {
+ return *commands[command_name];
+}
+
+
+// -----------------------------------------------------------------------------
+// ArgParser: parse arguments.
+// -----------------------------------------------------------------------------
+
+
+// Parse an option of the form --name=value or -n=value.
+void ArgParser::parseEqualsOption(string prefix, string name, string value) {
+ if (options.count(name) > 0) {
+ if (value.size() > 0) {
+ options[name]->values.push_back(value);
+ } else {
+ cerr << "Error: missing value for " << prefix << name << ".\n";
+ exit(1);
+ }
+ } else {
+ cerr << "Error: " << prefix << name << " is not a recognised option.\n";
+ exit(1);
+ }
+}
+
+
+// Parse a long-form option, i.e. an option beginning with a double dash.
+void ArgParser::parseLongOption(string arg, ArgStream& stream) {
+ size_t pos = arg.find("=");
+ if (pos != string::npos) {
+ parseEqualsOption("--", arg.substr(0, pos), arg.substr(pos + 1));
+ return;
+ }
+
+ if (flags.count(arg) > 0) {
+ flags[arg]->count++;
+ return;
+ }
+
+ if (options.count(arg) > 0) {
+ if (stream.hasNext()) {
+ options[arg]->values.push_back(stream.next());
+ return;
+ } else {
+ cerr << "Error: missing argument for --" << arg << ".\n";
+ exit(1);
+ }
+ }
+
+ if (arg == "help" && this->helptext != "") {
+ exitHelp();
+ }
+
+ if (arg == "version" && this->version != "") {
+ exitVersion();
+ }
+
+ cerr << "Error: --" << arg << " is not a recognised flag or option.\n";
+ exit(1);
+}
+
+
+// Parse a short-form option, i.e. an option beginning with a single dash.
+void ArgParser::parseShortOption(string arg, ArgStream& stream) {
+ size_t pos = arg.find("=");
+ if (pos != string::npos) {
+ parseEqualsOption("-", arg.substr(0, pos), arg.substr(pos + 1));
+ return;
+ }
+
+ for (char& c: arg) {
+ string name = string(1, c);
+
+ if (flags.count(name) > 0) {
+ flags[name]->count++;
+ continue;
+ }
+
+ if (options.count(name) > 0) {
+ if (stream.hasNext()) {
+ options[name]->values.push_back(stream.next());
+ continue;
+ } else {
+ if (arg.size() > 1) {
+ cerr << "Error: missing argument for '" << c << "' in -" << arg << ".\n";
+ } else {
+ cerr << "Error: missing argument for -" << c << ".\n";
+ }
+ exit(1);
+ }
+ }
+
+ if (c == 'h' && this->helptext != "") {
+ exitHelp();
+ }
+
+ if (c == 'v' && this->version != "") {
+ exitVersion();
+ }
+
+ if (arg.size() > 1) {
+ cerr << "Error: '" << c << "' in -" << arg << " is not a recognised flag or option.\n";
+ } else {
+ cerr << "Error: -" << c << " is not a recognised flag or option.\n";
+ }
+ exit(1);
+ }
+}
+
+
+// Parse a stream of string arguments.
+void ArgParser::parse(ArgStream& stream) {
+ bool is_first_arg = true;
+
+ while (stream.hasNext()) {
+ string arg = stream.next();
+
+ // If we enounter a '--', turn off option parsing.
+ if (arg == "--") {
+ while (stream.hasNext()) {
+ args.push_back(stream.next());
+ }
+ continue;
+ }
+
+ // Is the argument a long-form option or flag?
+ if (arg.compare(0, 2, "--") == 0) {
+ parseLongOption(arg.substr(2), stream);
+ continue;
+ }
+
+ // Is the argument a short-form option or flag? If the argument
+ // consists of a single dash or a dash followed by a digit, we treat
+ // it as a positional argument.
+ if (arg[0] == '-') {
+ if (arg.size() == 1 || isdigit(arg[1])) {
+ args.push_back(arg);
+ } else {
+ parseShortOption(arg.substr(1), stream);
+ }
+ continue;
+ }
+
+ // Is the argument a registered command?
+ if (is_first_arg && commands.count(arg) > 0) {
+ ArgParser* command_parser = commands[arg];
+ command_name = arg;
+ command_parser->parse(stream);
+ if (command_parser->callback != nullptr) {
+ command_parser->callback(arg, *command_parser);
+ }
+ continue;
+ }
+
+ // Is the argument the automatic 'help' command?
+ if (is_first_arg && arg == "help" && commands.size() > 0) {
+ if (stream.hasNext()) {
+ string name = stream.next();
+ if (commands.find(name) == commands.end()) {
+ cerr << "Error: '" << name << "' is not a recognised command.\n";
+ exit(1);
+ } else {
+ commands[name]->exitHelp();
+ }
+ } else {
+ cerr << "Error: the help command requires an argument.\n";
+ exit(1);
+ }
+ }
+
+ // Otherwise add the argument to our list of positional arguments.
+ args.push_back(arg);
+ is_first_arg = false;
+ }
+}
+
+
+// Parse an array of string arguments. We assume that [argc] and [argv] are the
+// original parameters passed to main() and skip the first element. In some
+// situations [argv] can be empty, i.e. [argc == 0]. This can lead to security
+// vulnerabilities if not handled explicitly.
+void ArgParser::parse(int argc, char **argv) {
+ if (argc > 1) {
+ ArgStream stream;
+ for (int i = 1; i < argc; i++) {
+ stream.append(argv[i]);
+ }
+ parse(stream);
+ }
+}
+
+
+// Parse a vector of string arguments.
+void ArgParser::parse(vector<string> args) {
+ ArgStream stream;
+ for (string& arg: args) {
+ stream.append(arg);
+ }
+ parse(stream);
+}
+
+
+// -----------------------------------------------------------------------------
+// ArgParser: utilities.
+// -----------------------------------------------------------------------------
+
+
+// Override the << stream insertion operator to support vectors. This will
+// allow us to cout our lists of option values in the print() method.
+template<typename T>
+static ostream& operator<<(ostream& stream, const vector<T>& vec) {
+ stream << "[";
+ for(size_t i = 0; i < vec.size(); ++i) {
+ if (i) cout << ", ";
+ stream << vec[i];
+ }
+ stream << "]";
+ return stream;
+}
+
+
+// Dump the parser's state to stdout.
+void ArgParser::print() {
+ cout << "Options:\n";
+ if (options.size() > 0) {
+ for (auto element: options) {
+ cout << " " << element.first << ": ";
+ Option *option = element.second;
+ cout << "(" << option->fallback << ") ";
+ cout << option->values;
+ cout << "\n";
+ }
+ } else {
+ cout << " [none]\n";
+ }
+
+ cout << "\nFlags:\n";
+ if (flags.size() > 0) {
+ for (auto element: flags) {
+ cout << " " << element.first << ": " << element.second->count << "\n";
+ }
+ } else {
+ cout << " [none]\n";
+ }
+
+ cout << "\nArguments:\n";
+ if (args.size() > 0) {
+ for (auto arg: args) {
+ cout << " " << arg << "\n";
+ }
+ } else {
+ cout << " [none]\n";
+ }
+
+ cout << "\nCommand:\n";
+ if (commandFound()) {
+ cout << " " << command_name << "\n";
+ } else {
+ cout << " [none]\n";
+ }
+}
+
+
+// Print the parser's help text and exit.
+void ArgParser::exitHelp() {
+ cout << helptext << endl;
+ exit(0);
+}
+
+
+// Print the parser's version string and exit.
+void ArgParser::exitVersion() {
+ cout << version << endl;
+ exit(0);
+}
+
+
+// -----------------------------------------------------------------------------
+// ArgParser: cleanup.
+// -----------------------------------------------------------------------------
+
+
+ArgParser::~ArgParser() {
+ set<Option*> unique_options;
+ for (auto element: options) {
+ unique_options.insert(element.second);
+ }
+ for (auto pointer: unique_options) {
+ delete pointer;
+ }
+
+ set<Flag*> unique_flags;
+ for (auto element: flags) {
+ unique_flags.insert(element.second);
+ }
+ for (auto pointer: unique_flags) {
+ delete pointer;
+ }
+
+ set<ArgParser*> unique_cmd_parsers;
+ for (auto element: commands) {
+ unique_cmd_parsers.insert(element.second);
+ }
+ for (auto pointer: unique_cmd_parsers) {
+ delete pointer;
+ }
+}
diff --git a/subprojects/argspp/args.h b/subprojects/argspp/args.h
new file mode 100644
index 0000000..a120f4e
--- /dev/null
+++ b/subprojects/argspp/args.h
@@ -0,0 +1,119 @@
+// -----------------------------------------------------------------------------
+// Args++: an argument-parsing library in portable C++11.
+//
+// Author: Darren Mulholland <dmulholl@tcd.ie>
+// License: Public Domain
+// Version: 2.1.0
+// -----------------------------------------------------------------------------
+
+#ifndef args_h
+#define args_h
+
+#include <map>
+#include <string>
+#include <vector>
+#include <sstream>
+
+namespace args {
+
+ struct ArgStream;
+ struct Option;
+ struct Flag;
+
+ class ArgParser {
+ public:
+ ArgParser(
+ std::string const& helptext = "",
+ std::string const& version = ""
+ ) : helptext(helptext), version(version) {}
+
+ ~ArgParser();
+
+ // Stores positional arguments.
+ std::vector<std::string> args;
+
+ // Application/command help text and version strings.
+ std::string helptext;
+ std::string version;
+
+ // Callback function for command parsers.
+ void (*callback)(std::string cmd_name, ArgParser& cmd_parser);
+
+ // Register flags and options.
+ void flag(std::string const& name);
+ void option(std::string const& name, std::string const& fallback = "");
+
+ // Parse the application's command line arguments.
+ void parse(int argc, char **argv);
+ void parse(std::vector<std::string> args);
+
+ // Retrieve flag and option values.
+ bool found(std::string const& name) const;
+ int count(std::string const& name) const;
+ std::string value(std::string const& name) const;
+ std::vector<std::string> values(std::string const& name) const;
+
+ template<typename T>
+ T value(std::string const& name) const
+ {
+ std::string str = value(name);
+ T ret = scan<T>(str);
+ return ret;
+ }
+
+ template<typename T>
+ std::vector<T> values(std::string const& name) const
+ {
+ std::vector<std::string> vec = values(name);
+ std::vector<T> ret;
+ ret.reserve(vec.size());
+ for (size_t i = 0; i < vec.size(); ++i) {
+ T value = vec[i];
+ ret.push_back(value);
+ }
+ return ret;
+ }
+
+ // Register a command. Returns the command's ArgParser instance.
+ ArgParser& command(
+ std::string const& name,
+ std::string const& helptext = "",
+ void (*callback)(std::string cmd_name, ArgParser& cmd_parser) = nullptr
+ );
+
+ // Utilities for handling commands manually.
+ bool commandFound();
+ std::string commandName();
+ ArgParser& commandParser();
+
+ // Print a parser instance to stdout.
+ void print();
+
+ private:
+ std::map<std::string, Option*> options;
+ std::map<std::string, Flag*> flags;
+ std::map<std::string, ArgParser*> commands;
+ std::string command_name;
+
+ void parse(ArgStream& args);
+ void registerOption(std::string const& name, Option* option);
+ void parseLongOption(std::string arg, ArgStream& stream);
+ void parseShortOption(std::string arg, ArgStream& stream);
+ void parseEqualsOption(std::string prefix, std::string name, std::string value);
+ void exitHelp();
+ void exitVersion();
+
+ template<typename T>
+ T scan(const std::string &input) const
+ {
+ std::stringstream stream(input);
+ T ret;
+ if ((stream >> ret).fail()) {
+ return T{0};
+ }
+ return ret;
+ }
+ };
+}
+
+#endif
diff --git a/subprojects/argspp/meson.build b/subprojects/argspp/meson.build
new file mode 100644
index 0000000..50aec98
--- /dev/null
+++ b/subprojects/argspp/meson.build
@@ -0,0 +1,5 @@
+project('argspp', 'cpp', version: '2.1.0')
+
+argspp_inc = include_directories('.')
+argspp_lib = static_library('argspp', 'args.cpp', include_directories: argspp_inc)
+argspp = declare_dependency(link_with: argspp_lib, include_directories: argspp_inc)