-
-
Notifications
You must be signed in to change notification settings - Fork 606
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
options: implement light and simple alternative to boost program options
This patch provides simple and light alternative to boost program options library. It adds handful of utility functions that are aimed to help parse command line options and intended to be used by kernel loader and the utility apps like cpiod, httpserver and cloud init. For more details on what particular functions do look at the comments in options.hh and options.cc. Signed-off-by: Waldemar Kozaczuk <[email protected]> Message-Id: <[email protected]>
- Loading branch information
Showing
5 changed files
with
498 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
/* | ||
* Copyright (C) 2019 Waldemar Kozaczuk | ||
* | ||
* This work is open source software, licensed under the terms of the | ||
* BSD license as described in the LICENSE file in the top-level directory. | ||
*/ | ||
|
||
#include <iostream> | ||
#include <functional> | ||
#include <cassert> | ||
#include <osv/options.hh> | ||
|
||
namespace options { | ||
using namespace std; | ||
// | ||
// Expects argv to contain individual arguments that are in one of the three forms: | ||
// 1) '--key' or | ||
// 2) '--key=value' or | ||
// 3) 'value' | ||
// | ||
// If 'allow_separate_values' is false, then only first 2 forms are valid and | ||
// the '--key' arguments are identified as 'flag' options (like '--enabled') | ||
// and '--key=value' arguments are treated as key, value pairs | ||
// | ||
// If 'allow_separate_values' is true, then all 3 forms are valid and | ||
// the '--key' arguments NOT followed by 'value' are identified as 'flag' options (like '--enabled') | ||
// and '--key=value' arguments are treated as key, value pairs | ||
// and '--key value' arguments are treated as key, value pairs as well | ||
map<string,vector<string>> parse_options_values(int argc, char** argv, | ||
function<void (const string&)> error_handler, | ||
bool allow_separate_values) | ||
{ | ||
map<string,vector<string>> options_values; | ||
|
||
string key(""); | ||
for (int i = 0; i < argc; i++) { | ||
string arg(argv[i]); | ||
// | ||
// Verify if is a 'long' option (starts with '--') | ||
if (arg.find("--") != 0) { | ||
// Not an option | ||
if (allow_separate_values && !key.empty()) { | ||
// Treat this arg as a value of the option specified by last key (for example: '--count 5') | ||
options_values[key].push_back(arg); | ||
key = ""; | ||
continue; | ||
} | ||
else { | ||
// Separate option values (like '--count 5') are not allowed | ||
error_handler(string("not an option: ") + arg); | ||
return options_values; | ||
} | ||
} | ||
// | ||
// Parse out value if --key=value | ||
size_t pos = arg.find('='); | ||
if (string::npos == pos) { | ||
// Treat as a 'flag' option like for example '--enableXyz' | ||
key = arg.substr(2); | ||
// Verify if a non-flag option (non-empty vector) DOES NOT exists already | ||
auto it = options_values.find(key); | ||
if (it != options_values.end() && !it->second.empty()) { | ||
error_handler(string("duplicate option: ") + arg); | ||
return options_values; | ||
} | ||
// Flag: add empty vector to the map | ||
options_values[key] = vector<string>(); | ||
} | ||
else { | ||
// Treat as an option value like for example '--count=5' (single value) | ||
// or multi-value like '--env=A --env=B=1' | ||
key = arg.substr(2, pos - 2); | ||
// Verify if a flag option (empty vector) DOES NOT exists already | ||
auto it = options_values.find(key); | ||
if (it != options_values.end() && it->second.empty()) { | ||
error_handler(string("duplicate option: ") + arg); | ||
return options_values; | ||
} | ||
|
||
auto value = arg.substr(pos + 1); | ||
if (value.empty()) { | ||
error_handler(string("the required argument for option '--") + key + "' is missing"); | ||
return options_values; | ||
} | ||
// (Key,Value) or (Key,[Val1,Val2,..]) - add value to the vector | ||
options_values[key].push_back(value); | ||
key = ""; | ||
} | ||
} | ||
|
||
return options_values; | ||
} | ||
|
||
bool extract_option_flag(map<string,vector<string>> &options_values, const string &name, function<void (const string&)> error_handler) | ||
{ | ||
auto it = options_values.find(name); | ||
if (it != options_values.end()) { | ||
if (!it->second.empty()) { | ||
error_handler(string("option '--") + name + "' does not take any arguments"); | ||
return false; | ||
} | ||
|
||
options_values.erase(it); | ||
return true; | ||
} else { | ||
return false; | ||
} | ||
} | ||
|
||
bool option_value_exists(const map<string,vector<string>> &options_values, const string &name) | ||
{ | ||
auto it = options_values.find(name); | ||
return it != options_values.end() && !it->second.empty(); | ||
} | ||
|
||
string extract_option_value(map<string,vector<string>> &options_values, const string &name) | ||
{ | ||
return extract_option_values(options_values, name)[0]; | ||
} | ||
|
||
static void handle_invalid_argument(const string &name, const string &value, function<void (const string&)> error_handler) | ||
{ | ||
error_handler(string("the argument ('") + value + "') for option '--" + name + "' is invalid"); | ||
} | ||
|
||
int extract_option_int_value(map<string,vector<string>> &options_values, const string &name, function<void (const string&)> error_handler) | ||
{ | ||
auto value_str = extract_option_values(options_values, name)[0]; | ||
size_t pos; | ||
int value = 0; | ||
|
||
try { | ||
value = std::stoi(value_str, &pos); | ||
if (pos < value_str.length()) { | ||
handle_invalid_argument(name, value_str, error_handler); | ||
} | ||
} | ||
catch (const invalid_argument& ia) { | ||
handle_invalid_argument(name, value_str, error_handler); | ||
} | ||
return value; | ||
} | ||
|
||
float extract_option_float_value(map<string,vector<string>> &options_values, const string &name, function<void (const string&)> error_handler) | ||
{ | ||
auto value_str = extract_option_values(options_values, name)[0]; | ||
size_t pos; | ||
float value = 0.0f; | ||
|
||
try { | ||
value = std::stof(value_str, &pos); | ||
if (pos < value_str.length()) { | ||
handle_invalid_argument(name, value_str, error_handler); | ||
} | ||
} | ||
catch (const invalid_argument& ia) { | ||
handle_invalid_argument(name, value_str, error_handler); | ||
} | ||
return value; | ||
} | ||
|
||
vector<string> extract_option_values(map<string,vector<string>> &options_values, const string &name) | ||
{ | ||
auto it = options_values.find(name); | ||
assert(it != options_values.end() && !it->second.empty()); | ||
auto values = it->second; | ||
options_values.erase(it); | ||
return values; | ||
} | ||
|
||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* | ||
* Copyright (C) 2019 Waldemar Kozaczuk | ||
* | ||
* This work is open source software, licensed under the terms of the | ||
* BSD license as described in the LICENSE file in the top-level directory. | ||
*/ | ||
|
||
#ifndef OSV_OPTIONS_HH | ||
#define OSV_OPTIONS_HH | ||
|
||
#include <string> | ||
#include <vector> | ||
#include <map> | ||
|
||
// | ||
// The methods in 'options' namespace provide basic functionality intended to help | ||
// parse options by the kernel loader and utility apps like cpiod, httpserver and cloud-init. | ||
namespace options { | ||
|
||
using namespace std; | ||
|
||
// Iterates over supplied array of arguments and collects them into a map | ||
// where key is an option name and the value is a vector of 0 or more values | ||
// It recognizes only so called 'long' options that start with '--' (double dash) prefix | ||
map<string,vector<string>> parse_options_values(int argc, char** argv, | ||
function<void (const string&)> error_handler, bool allow_separate_values = true); | ||
|
||
// Checks if options_values map contains a 'flag' option of '--<flag>' format | ||
// and returns true if the option found and removes that option from the map | ||
bool extract_option_flag(map<string,vector<string>> &options_values, const string &name, | ||
function<void (const string&)> error_handler); | ||
|
||
// Returns true if options_values contains single-value option (--<key>=<value>) or | ||
// multi-value one (--<key>=<val1>, --<key>=<val2>) | ||
bool option_value_exists(const map<string,vector<string>> &options_values, const string &name); | ||
|
||
// Returns the value of a single-value option (--<name>=<key>) and removes it from the options_values | ||
string extract_option_value(map<string,vector<string>> &options_values, const string &name); | ||
|
||
// Returns the value of a single-value option (--<name>=<key>), tries to convert to an integer | ||
// and removes it from the options_values | ||
int extract_option_int_value(map<string,vector<string>> &options_values, const string &name, | ||
function<void (const string&)> error_handler); | ||
|
||
// Returns the value of a single-value option (--<name>=<key>), tries to convert to a float | ||
// and removes it from the options_values | ||
float extract_option_float_value(map<string,vector<string>> &options_values, const string &name, | ||
function<void (const string&)> error_handler); | ||
|
||
// Returns the values of a multi-value option (--<key>=<val1>, --<key>=<val2>) and removes | ||
// them from the options_values | ||
vector<string> extract_option_values(map<string,vector<string>> &options_values, const string &name); | ||
|
||
}; | ||
|
||
#endif //OSV_OPTIONS_HH |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.