diff C++/modules/OptionParser/OptionParser.cpp @ 334:0b576ee64d45

* Create brand new hierarchy * Rename DynLib to Dynlib * Remove some warnings
author David Demelier <markand@malikania.fr>
date Sun, 08 Mar 2015 14:26:33 +0100
parents C++/OptionParser.cpp@99e83685d4da
children 3a1380b4428c
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/C++/modules/OptionParser/OptionParser.cpp	Sun Mar 08 14:26:33 2015 +0100
@@ -0,0 +1,211 @@
+/*
+ * OptionParser.cpp -- command line option parser
+ *
+ * Copyright (c) 2013, 2014 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <algorithm>
+
+#include "OptionParser.h"
+
+bool OptionParser::isShort(const std::string &arg) const
+{
+	return arg.size() >= 2 && arg[0] == '-' && arg[1] != '-';
+}
+
+bool OptionParser::isLong(const std::string &arg) const
+{
+	return arg.size() >= 3 && arg[0] == '-' && arg[1] == '-' && arg[2] != '-';
+}
+
+bool OptionParser::isOption(const std::string &arg) const
+{
+	return isShort(arg) || isLong(arg);
+}
+
+std::string OptionParser::key(const std::string &arg) const
+{
+	if (isShort(arg))
+		return arg.substr(1, 1);
+
+	return arg.substr(2);
+}
+
+bool OptionParser::isShortCompacted(const std::string &arg) const
+{
+	return arg.size() >= 3;
+}
+
+bool OptionParser::isDefined(const std::string &arg) const
+{
+	auto n = key(arg);
+	auto it = std::find_if(m_options.begin(), m_options.end(), [&] (const Option &o) -> bool {
+		return o.key() == n || o.full() == n;
+	});
+
+	return it != m_options.end();
+}
+
+const Option &OptionParser::get(const std::string &arg) const
+{
+	std::string n = key(arg);
+
+	return *std::find_if(m_options.begin(), m_options.end(), [&] (const Option &o) -> bool {
+		return o.key() == n || o.full() == n;
+	});
+}
+
+bool OptionParser::isToggle(const std::string &arg) const
+{
+	return (get(arg).flags() & Option::NoArg);
+}
+
+void OptionParser::readShort(OptionPack &pack, Args::const_iterator &it, Args::const_iterator end) const
+{
+	/*
+	 * There are many options when passing a short option:
+	 *
+	 * 1. -cmyconfig	Takes on argument but parsed as unique,
+	 * 2. -c myconfig	Takes on argument but parsed as two strings
+	 * 3. -abc		If a is not a toggle option, its argument is `bc'
+	 * 4. -abc		If a is a toggle option and b, c are toggle, they are added
+	 */
+
+	std::string v = it->substr(2);
+	std::string k = key(*it);
+	const Option &option = get(std::string("-") + k);
+
+	if (isToggle(*it)) {
+		// 3. and optionally 4.
+		pack.push_back(OptionValue(option, ""));
+		pack.m_argsParsed += 1;
+
+		if (isShortCompacted(*it)) {
+			for (char c : v) {
+				if (!isDefined("-" + std::string(1, c))) {
+					pack.m_error = "-" + std::string(1, c) + " is not a valid option";
+					break;
+				}
+
+				const Option &sub = get("-" + std::string(1, c));
+
+				pack.push_back(OptionValue(sub, ""));
+			}
+		}
+
+		++ it;
+	} else {
+		// 1.
+		if (isShortCompacted(*it++)) {
+			pack.push_back(OptionValue(option, v));
+			pack.m_argsParsed += 1;
+		} else {
+			// 2.
+			if (it == end) {
+				pack.m_error = option.key() + " requires an option";
+			} else {
+				pack.push_back(OptionValue(option, *it++));
+				pack.m_argsParsed += 2;
+			}
+		}
+	}
+}
+
+void OptionParser::readFull(OptionPack &pack, Args::const_iterator &it, Args::const_iterator end) const
+{
+	/*
+	 * Long options can't be compacted, there are only two possibilities:
+	 *
+	 * 1. --fullscreen	No argument
+	 * 2. --config foo	One argument
+	 */
+	const Option &option = get(*it);
+
+	if (!isToggle(*it)) {
+		// 2.
+		if (++it == end) {
+			pack.m_error = "--" + option.full() + " requires an option";
+		} else {
+			pack.push_back(OptionValue(option, *it++));
+			pack.m_argsParsed += 2;
+		}
+	} else {
+		pack.push_back(OptionValue(option, ""));
+		pack.m_argsParsed ++;
+
+		++ it;
+	}
+}
+
+OptionParser::OptionParser(std::initializer_list<Option> options)
+	: m_options(options.begin(), options.end())
+{
+}
+
+OptionParser::OptionParser(std::vector<Option> options)
+	: m_options(std::move(options))
+{
+}
+
+OptionPack OptionParser::parse(Args::const_iterator it, Args::const_iterator end, int flags) const
+{
+	OptionPack pack;
+
+	while (it != end) {
+		if (!isOption(*it)) {
+			if (flags & Unstrict) {
+				pack.m_argsParsed ++;
+				it ++;
+				continue;
+			} else {
+				pack.m_error = *it + " is not an option";
+				return pack;
+			}
+		}
+
+		if (!isDefined(*it)) {
+			pack.m_error = "Invalid option";
+			return pack;
+		}
+
+		if (isShort(*it)) {
+			readShort(pack, it, end);
+		} else {
+			readFull(pack, it, end);
+		}
+
+		// Read failure
+		if (pack.m_error != "No error") {
+			return pack;
+		}
+	}
+
+	return pack;
+}
+
+OptionPack OptionParser::parse(int argc, char **argv, int flags) const
+{
+	std::vector<std::string> args;
+
+	for (int i = 0; i < argc; ++i)
+		args.push_back(argv[i]);
+
+	return parse(args, flags);
+}
+
+OptionPack OptionParser::parse(const std::vector<std::string> &args, int flags) const
+{
+	return parse(args.begin(), args.end(), flags);
+}