diff modules/options/options.cpp @ 486:7ee8da32da98

Unify all in modules/
author David Demelier <markand@malikania.fr>
date Fri, 13 Nov 2015 09:26:46 +0100
parents
children d8ed4da7688c
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/modules/options/options.cpp	Fri Nov 13 09:26:46 2015 +0100
@@ -0,0 +1,189 @@
+/*
+ * options.cpp -- parse Unix command line options
+ *
+ * Copyright (c) 2015 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 <cassert>
+
+#include "options.h"
+
+namespace option {
+
+namespace {
+
+using Iterator = std::vector<std::string>::iterator;
+using Args = std::vector<std::string>;
+
+inline bool isOption(const std::string &arg) noexcept
+{
+	return arg.size() >= 2 && arg[0] == '-';
+}
+
+inline bool isLongOption(const std::string &arg) noexcept
+{
+	assert(isOption(arg));
+
+	return arg.size() >= 3 && arg[1] == '-';
+}
+
+inline bool isShortSimple(const std::string &arg) noexcept
+{
+	assert(isOption(arg));
+	assert(!isLongOption(arg));
+
+	return arg.size() == 2;
+}
+
+void parseLongOption(Result &result, Args &args, Iterator &it, Iterator &end, const Options &definition)
+{
+	auto arg = *it++;
+	auto opt = definition.find(arg);
+
+	if (opt == definition.end()) {
+		throw InvalidOption{arg};
+	}
+
+	/* Need argument? */
+	if (opt->second) {
+		if (it == end || isOption(*it)) {
+			throw MissingValue{arg};
+		}
+
+		result.insert(std::make_pair(arg, *it++));
+		it = args.erase(args.begin(), it);
+		end = args.end();
+	} else {
+		result.insert(std::make_pair(arg, ""));
+		it = args.erase(args.begin());
+		end = args.end();
+	}
+}
+
+void parseShortOption(Result &result, Args &args, Iterator &it, Iterator &end, const Options &definition)
+{
+	if (isShortSimple(*it)) {
+		/*
+		 * Here two cases:
+		 *
+		 * -v (no option)
+		 * -c value
+		 */
+		auto arg = *it++;
+		auto opt = definition.find(arg);
+
+		if (opt == definition.end()) {
+			throw InvalidOption{arg};
+		}
+
+		/* Need argument? */
+		if (opt->second) {
+			if (it == end || isOption(*it)) {
+				throw MissingValue{arg};
+			}
+
+			result.insert(std::make_pair(arg, *it++));
+			it = args.erase(args.begin(), it);
+			end = args.end();
+		} else {
+			result.insert(std::make_pair(arg, ""));
+			it = args.erase(args.begin());
+			end = args.end();
+		}
+	} else {
+		/*
+		 * Here multiple scenarios:
+		 *
+		 * 1. -abc (-a -b -c if all are simple boolean arguments)
+		 * 2. -vc foo.conf (-v -c foo.conf if -c is argument dependant)
+		 * 3. -vcfoo.conf (-v -c foo.conf also)
+		 */
+		auto value = it->substr(1);
+		auto len = value.length();
+		int toremove = 1;
+
+		for (decltype(len) i = 0; i < len; ++i) {
+			auto arg = std::string{'-'} + value[i];
+			auto opt = definition.find(arg);
+
+			if (opt == definition.end()) {
+				throw InvalidOption{arg};
+			}
+
+			if (opt->second) {
+				if (i == (len - 1)) {
+					/* End of string, get the next argument (see 2.) */
+					if (++it == end || isOption(*it)) {
+						throw MissingValue{arg};
+					}
+
+					result.insert(std::make_pair(arg, *it));
+					toremove += 1;
+				} else {
+					result.insert(std::make_pair(arg, value.substr(i + 1)));
+					i = len;
+				}
+			} else {
+				result.insert(std::make_pair(arg, ""));
+			}
+		}
+
+		it = args.erase(args.begin(), args.begin() + toremove);
+		end = args.end();
+	}
+}
+
+} // !namespace
+
+Result read(std::vector<std::string> &args, const Options &definition)
+{
+	Result result;
+
+	auto it = args.begin();
+	auto end = args.end();
+
+	while (it != end) {
+		if (!isOption(*it)) {
+			break;
+		}
+
+		if (isLongOption(*it)) {
+			parseLongOption(result, args, it, end, definition);
+		} else {
+			parseShortOption(result, args, it, end, definition);
+		}
+	}
+
+	return result;
+}
+
+Result read(int &argc, char **&argv, const Options &definition)
+{
+	std::vector<std::string> args;
+
+	for (int i = 0; i < argc; ++i) {
+		args.push_back(argv[i]);
+	}
+
+	auto before = args.size();
+	auto result = read(args, definition);
+
+	argc -= before - args.size();
+	argv += before - args.size();
+
+	return result;
+}
+
+} // !option