aboutsummaryrefslogtreecommitdiffstats
path: root/meowpp/Usage.h
diff options
context:
space:
mode:
Diffstat (limited to 'meowpp/Usage.h')
-rw-r--r--meowpp/Usage.h601
1 files changed, 451 insertions, 150 deletions
diff --git a/meowpp/Usage.h b/meowpp/Usage.h
index 801b11c..4537202 100644
--- a/meowpp/Usage.h
+++ b/meowpp/Usage.h
@@ -1,160 +1,461 @@
-#ifndef Usage_H__
-#define Usage_H__
+#ifndef MEOW_USAGE_H__
+#define MEOW_USAGE_H__
+
+#include "utility.h"
#include <cstdlib>
#include <string>
#include <vector>
#include <map>
+#include <algorithm>
-namespace meow{
- class Usage{
- private:
- typedef std::string String;
- typedef std::vector<String> Strings;
- class Value{
- public:
- Value();
- Value(String const& v);
- Value(String const& v, String const& d);
- String getUsage() const;
- String getValue() const;
- bool operator==(Value const& b) const;
- private:
- String value;
- String description;
- };
- typedef std::vector<Value> Values;
- class Option{
- public:
- Option();
- Option(String const& des);
- Option(String const& des,
- String const& typ,
- String const& def,
- bool must);
- bool setValue(String const& str);
- String getValue(size_t index) const;
- size_t getValuesCount() const;
- bool addValueAccept(String const& val,
- String const& des);
- bool hasSetup() const;
- bool hasValue() const;
- bool chkSetup() const;
- String getUsage(unsigned char opt, bool detail) const;
- private:
- Strings values;
- Values values_accept;
- String value_default;
- String value_type;
- String description;
- bool has_value;
- bool has_setup;
- bool must_setup;
- };
- typedef std::map<unsigned char, Option> Options;
- typedef Options::const_iterator OptionsIterator;
- public:
- Usage();
- Usage(String const& _name);
- ////////// **# Add other options #** ///////////
- bool import(Usage const& usage);
- bool update(Usage const& usage);
- /////////// **# add a option/value #** /////////
- bool addOption(unsigned char opt, String const& des);
- bool addOption(unsigned char opt, String const& des,
- String const& val_type,
- String const& val_default,
- bool must);
- bool addOptionValueAccept(unsigned char opt,
- String const& val,
- String const& des);
- ///////// **# access informations #** //////////
- bool hasOptionSetup(unsigned char opt ) const;
- size_t getOptionValuesCount(unsigned char opt ) const;
- String getOptionValue(unsigned char opt, size_t index) const;
- size_t getProcArgsCount() const;
- String getProcArg(size_t index) const;
- Strings getProcArgs() const;
- //////// **# add a usage description #** ///////
- void addUsageBegin(String const& des);
- void addUsageEnd (String const& des);
- ///////////// **# access usages #** ////////////
- String getUsage() const;
- ////////// **# analysis argc,argv #** //////////
- bool setArguments(int argc, char** argv, String* errmsg);
- private:
- String name;
- Options options;
- Strings usage_begin;
- Strings usage_end ;
- Strings proc_arguments;
- };
- //#
- //# === meow:: *Usage* (C++ Class)
- //# ==== Description
- //# `Usage` 是用來分析argc, argv和輸出usage document的class.
- //# argc, argv的部份, 有以下規則
- //#
- //# * `-c` 其中 `c` 可以代換成正常的一個字元的字符,
- //# 這種選像要嘛就是 *有設置* , 不然就是 *沒設置*
- //# * `-c <value>` 附加一個value, 這種選項可以是選擇性 ,即要設定與否都可以,
- //# 反之則一定要設定. 另外可以給定value的預設值以及哪些value是可接受的
- //# * `<value>` 其他, 一律視為process arguments
- //#
- //# ==== Methods
- //# * `Usage(String const& _name)` +
- //# 建構子, 所有說明文字中 *<name>* 都會被代換成 `_name`
- //# * `Usage()` +
- //# 建構子, `_name` 自動取為 " *nobody* "
- //# * `import(Usage const& usage)` +
- //# 將另一個usage的設定匯入, 回傳成功與否 *(bool)*
- //# * `update(Usage const& usage)` +
- //# 將另一個usage分析argc,argv出來的資料拿來用, 回傳成功與否 *(bool)*
- //# * `addOption(unsigned char option, String const& description)` +
- //# 新增一個不接額外選項的參數, 並附上說明文字, 回傳成功與否 *(bool)*
- //# * `addOption(unsigned char option, String const& description,
- //# String const& value_type, String const& value_default, bool must)` +
- //# 新增一個有額外選項的參數, 並附上說明文字, 額外選項的型別跟預設值.
- //# 說明文字中所有的" *<types>* "將會被取代指定的型別, 其中 `must` 代表
- //# " *是否一定要設定此參數* " , 回傳表成功與否 *(bool)*
- //# * `addOptionValueAccept(unsigned char option,
- //# String const& value, String const& description)` +
- //# 針對某個option, 新增一個可接受的額外選項 (如果某個option從頭到尾都
- //# 沒有新增可接受的選項, 則視為不限制), 回傳成功與否 *(bool)*
- //# * `hasOptionSetup(unsigned char option)` +
- //# 回傳是否有此選項 *(bool)*
- //# * `getOptionValuesCount(unsigned char option)` +
- //# 回傳此參數被設置了幾次 *(size_t)* , 只對有接額外參數的有效
- //# * `getOptionValue(unsigned char option, size_t index)` +
- //# 回傳第`index`個額外選項 *(String)*
- //# * `getProcArgsCount()` +
- //# 回傳有多少個Process Arguments *(size_t)*
- //# * `getProcArg(size_t index)` +
- //# 取得第`index`個Process Argument *(String)*
- //# * `getProcArgs()` +
- //# 回傳一個陣列, 包含所有Process Arguments *(Strings)*
- //# * `addUsageBegin(String const& des)` +
- //# 新增一段usage document於每個選項逐條說明之前
- //# * `addUsageEnd (String const& des)` +
- //# 新增一段usage document於每個選項逐條說明之後
- //# * `String getUsage()` +
- //# 回傳usage document *(String)*
- //# * `setArguments(int argc, char** argv, String* errmsg)` +
- //# 輸入argv, argc, 回傳是否沒有錯誤發生 *(bool)* , 其中如果有錯誤發生,
- //# 且 `errmsg != NULL` 則會將錯誤訊息寫入之
- //#
- //#[NOTE]
- //#==================================
- //# * `String` 是 `std::string` .
- //# * `Strings` 是 `std::vector< std::string> >`.
- //# * 如果沒有寫回傳什麼, 就是回傳 `void`
- //#==================================
- //#
- //#'''
- //#
+extern "C" {
+#include <unistd.h>
}
-#include "Usage.hpp"
+namespace meow {
+/*!
+ * @brief 管理參數設置, 自訂usage document, 分析argc, argv
+ *
+ * \b Usage 是用來分析argc, argv和輸出usage document的class. \n
+ * argc, argv的部份, 有以下規則
+ * - \b -c 其中 \a c 可以代換成一個字符, 這種選像可能是 \b 有設置 或 \b 沒設置
+ * - \b -c \a value 附加一個 \a value , 這種選項可以是 \b 選擇性 或
+ * \b 必要的 , 另外可以給定value的預設值以及哪些value是可接受
+ * - \a value 其他, 一律視為 \b process \b arguments
+ *
+ * @author cathook
+ */
+class Usage {
+private:
+ typedef std::string String;
+ typedef std::vector<String> Strings;
+ //! 存 (value, description)
+ class Value {
+ private:
+ String value_;
+ String description_;
+ public:
+ Value() {
+ }
+ Value(String const& value, String const& description) {
+ value_ = value;
+ description_ = stringReplace(description, "<value>", value);
+ }
+ String usage() const {
+ return stringPrintf("%8s%s : %s\n",
+ " ", value_.c_str(), description_.c_str());
+ }
+ String value() const {
+ return value_;
+ }
+ bool operator==(Value const& b) const {
+ return (value_ == b.value_);
+ }
+ };
+ typedef std::vector<Value> Values;
+ //! 存 option, 其中可能有value可能沒有
+ class Option {
+ private:
+ Strings values_;
+ Values values_accept_;
+ String value_default_;
+ String value_type_;
+ String description_;
+ bool has_value_;
+ bool has_setup_;
+ bool must_setup_;
+ public:
+ Option() {
+ }
+ Option(String const& description) {
+ has_setup_ = false;
+ has_value_ = false;
+ description_ = description;
+ must_setup_ = false;
+ }
+ Option(String const& description,
+ String const& type,
+ String const& default_value,
+ bool must) {
+ has_setup_ = false;
+ has_value_ = true;
+ description_ = description;
+ value_type_ = type;
+ value_default_ = default_value;
+ must_setup_ = must;
+ }
+ Strings const& values() const {
+ return values_;
+ }
+ String value(size_t index) const {
+ if (!has_value_) return "";
+ if (!has_setup_ || index >= values_.size()) return value_default_;
+ return values_[index];
+ }
+ ssize_t valueAdd(String const& value) {
+ if (!has_value_) {
+ has_setup_ = true;
+ return 0;
+ }
+ if (values_accept_.size() > 0 &&
+ std::find(values_accept_.begin(), values_accept_.end(),
+ Value(value, "")) == values_accept_.end())
+ return -1;
+ values_.push_back(value);
+ has_setup_ = true;
+ return values_.size() - 1;
+ }
+ bool valueAcceptAdd(String const& value, String const& description) {
+ if (!has_value_) return false;
+ if (std::find(values_accept_.begin(), values_accept_.end(),
+ Value(value, "")) == values_accept_.end()){
+ values_accept_.push_back(Value(value, description));
+ }
+ return true;
+ }
+ bool valueAcceptChk(String const& value){
+ if (!has_value_) return false;
+ if (values_accept_.size() == 0) return true;
+ return (std::find(values_accept_.begin(), values_accept_.end(),
+ Value(value, "")) != values_accept_.end());
+ }
+ bool hasSetup() const{ return has_setup_; }
+ bool hasValue() const{ return has_value_; }
+ bool chkSetup() const{ return !(must_setup_ && !has_setup_); }
+
+ String usage(unsigned char opt, bool detail) const {
+ String ret(stringPrintf("-%c ", opt));
+ if (!detail) {
+ if (has_value_) ret += value_type_;
+ if (!must_setup_) ret = "[" + ret + "]";
+ } else {
+ if (has_value_) {
+ ret += value_type_ + " ";
+ String default_string("");
+ if (value_default_ != "")
+ default_string = "defalut='" + value_default_ + "'";
+ String optional_string("");
+ if (!must_setup_)
+ optional_string = "optional";
+ String tmp;
+ if (default_string.size() + optional_string.size() > 0) {
+ if (default_string.size() > 0 && optional_string.size() > 0) {
+ ret += "(" + optional_string + ", " + default_string + ")";
+ } else {
+ ret += "(" + optional_string + default_string + ")";
+ }
+ }
+ }
+ ret += "\n";
+ String accept_string;
+ for (size_t i = 0; i < values_accept_.size(); i++) {
+ if (i > 0)
+ accept_string += (i + 1 < values_accept_.size()
+ ? ", " : " or ");
+ accept_string += "'" + values_accept_[i].value() + "'";
+ }
+ if (accept_string.size() == 0) accept_string = "... (anything)";
+ ret += " " + stringReplace(stringReplace(description_,
+ "<type>",
+ value_type_),
+ "<values>",
+ accept_string) + "\n";
+ for (size_t i = 0; i < values_accept_.size(); i++) {
+ ret += values_accept_[i].usage();
+ }
+ ret += "\n";
+ }
+ return ret;
+ }
+ };
+ typedef std::map<unsigned char, Option> Options;
+ typedef Options::const_iterator OptionsIterator;
+ String name_;
+ Options options_;
+ Strings usage_begin_;
+ Strings usage_end_;
+ Strings proc_arguments_;
+public:
+ /*!
+ * @brief constructor
+ *
+ * 所有說明文字中 \a \<name\> 都會被代換成空字串
+ */
+ Usage() {
+ }
+
+ /*!
+ * @brief constructor
+ *
+ * 所有說明文字中 \a "<name>" 都會被代換成空字串 \b name
+ */
+ Usage(String const& name) {
+ name_ = name;
+ }
+
+
+ /*!
+ * @brief constructor
+ *
+ * 將另一個usage原封不動的複製過來
+ */
+ Usage(Usage const& usage) {
+ name_ = usage.name_;
+ options_ = usage.options_;
+ usage_begin_ = usage.usage_begin_;
+ usage_end_ = usage.usage_end_;
+ proc_arguments_ = usage.proc_arguments_;
+ }
+
+ /*!
+ * @brief 將另一個usage的設置匯入
+ *
+ * @param [in] usage 另一個usage
+ * @return \c true/false 表示 \b 是否成功
+ */
+ bool import(Usage const& usage) {
+ for (OptionsIterator
+ it = usage.options_.begin(); it != usage.options_.end(); ++it) {
+ if (options_.find(it->first) != options_.end())
+ return false;
+ }
+ for (OptionsIterator
+ it = usage.options_.begin(); it != usage.options_.end(); ++it) {
+ options_[it->first] = it->second;
+ }
+ for (size_t i = 0; i < usage.usage_begin_.size(); ++i)
+ usage_begin_.push_back(usage.usage_begin_[i]);
+ for (size_t i = 0; i < usage.usage_end_.size(); ++i)
+ usage_end_.push_back(usage.usage_end_[i]);
+ return true;
+ }
+
+ /*!
+ * @brief 將另一個usage的選項設置加進來
+ *
+ * @param [in] usage 另一個usage
+ * @return \c true/false 表 \b 是否成功
+ */
+ bool update(Usage const& usage) {
+ for (OptionsIterator
+ it = usage.options_.begin(); it != usage.options_.end(); ++it) {
+ if (options_.find(it->first) == options_.end()) continue;
+ for(size_t i = 0, I = it->second.values().size(); i < I; i++){
+ options_[it->first].valueAdd(it->second.value(i));
+ }
+ }
+ return true;
+ }
+
+ /*!
+ * @brief 新增一個沒有額外選項的選項
+ *
+ * @param [in] opt 指定字符
+ * @param [in] des 即description, 用來解釋這個選項的意義用的
+ * @return \c true/false 表 \b 是否成功
+ */
+ bool optionAdd(unsigned char opt, String const& des) {
+ if (options_.find(opt) != options_.end()) return false;
+ options_[opt] = Option(des);
+ return true;
+ }
+
+ /*!
+ * @brief 新增一個有額外選項的選項
+ *
+ * @param [in] opt 指定字符
+ * @param [in] des 即description, 用來解釋這個選項的意義用的
+ * @param [in] val_type 表示額外選項的型態, 寫在USAGE裡面給人看用的
+ * @param [in] val_default 預設值, 若為空字串則當作沒有預設值
+ * @param [in] must 表示是否一定要設定
+ * @return \c true/false 表 \b 是否成功
+ */
+ bool optionAdd(unsigned char opt, String const& des,
+ String const& val_type,
+ String const& val_default,
+ bool must) {
+ if (options_.find(opt) != options_.end()) return false;
+ options_[opt] = Option(des, val_type, val_default, must);
+ return true;
+ }
+
+ /*!
+ * @brief 針對-(opt)新增一個可接受的額外選項
+ *
+ * @param [in] opt 指定字符
+ * @param [in] val 額外選項
+ * @param [in] des 關於此額外選項的說明
+ * @return \c true/false 表 \b 是否成功
+ */
+ bool optionValueAcceptAdd(unsigned char opt,
+ String const& val,
+ String const& des) {
+ if (options_.find(opt) == options_.end()) return false;
+ return options_[opt].valueAcceptAdd(val, des);
+ }
+
+ /*!
+ * @brief 回傳是否有設定此選項
+ *
+ * @param [in] opt 指定字符
+ * @return \c true/false 表 \b 是否有設定此選項
+ */
+ bool hasOptionSetup(unsigned char opt) const {
+ return (options_.find(opt) != options_.end() &&
+ options_.find(opt)->second.hasSetup());
+ }
+
+ /*!
+ * @brief 回傳參數 \b -(opt) 被設置幾次
+ *
+ * @param [in] opt 指定字符
+ * @return 回傳次數
+ */
+ size_t optionValuesSize(unsigned char opt) const {
+ if(options_.find(opt) == options_.end()) return 0;
+ return options_.find(opt)->second.values().size();
+ }
+
+ /*!
+ * @brief 回傳參數 \b -(opt) 的第 \b index 個額外選項
+ *
+ * @param [in] opt 指定字符
+ * @param [in] index 第幾個
+ * @return 回傳參數 \b -(opt) 的第 \b index 個額外選項
+ */
+ String optionValue(unsigned char opt, size_t index) const {
+ if (options_.find(opt) == options_.end()) {
+ return String();
+ }
+ return options_.find(opt)->second.value(index);
+ }
+
+ /*!
+ * @brief 取得有幾個process arguments
+ *
+ * @return 有幾個process arguments
+ */
+ size_t procArgsSize() const {
+ return proc_arguments_.size();
+ }
+
+ /*!
+ * @brief 取得第i個process argument
+ *
+ * @param [in] index 第幾個
+ * @return 回傳第 \a index 個 \b process \b argument
+ */
+ String procArg(size_t index) const {
+ if (index >= proc_arguments_.size()) {
+ return String();
+ }
+ return proc_arguments_[index];
+ }
+
+ /*!
+ * @brief 取得process arguments array
+ *
+ * @return 一個 \c std::vector , 包含所有 \b Process \b arguments
+ */
+ Strings const& procArgs() const{
+ return proc_arguments_;
+ }
+
+ /*!
+ * @brief 新增一段usage document於每個選項逐條說明之前
+ *
+ * @param [in] des 要新增的usage document
+ */
+ void usageBeginAdd(String const& des) {
+ usage_begin_.push_back(stringReplace(des, "<name>", name_));
+ }
+
+ /*!
+ * @brief 新增一段usage document於每個選項逐條說明之後
+ *
+ * @param [in] des 要新增的usage document
+ */
+ void usageEndAdd(String const& des) {
+ usage_end_.push_back(stringReplace(des, "<name>", name_));
+ }
+
+ /*!
+ * @brief 回傳usage string
+ *
+ * @return \b usage \b string
+ */
+ String usage() const{
+ Usage::String out = stringPrintf("USAGE\n %s", name_.c_str());
+ for (OptionsIterator
+ it = options_.begin(); it != options_.end(); ++it)
+ out += " " + it->second.usage(it->first, false);
+ out += "\n\nDESCRIPTION\n";
+ for (size_t i = 0; i < usage_begin_.size(); ++i) {
+ out += " " + usage_begin_[i] + "\n\n";
+ }
+ for (OptionsIterator
+ it = options_.begin(); it != options_.end(); ++it) {
+ out += it->second.usage(it->first, true);
+ }
+ for (size_t i = 0; i < usage_end_.size(); ++i) {
+ out += " " + usage_end_[i] + "\n\n";
+ }
+ return out;
+ }
+
+ /*!
+ * @brief 給定argc, argv, 將各參數設置
+ * @param [in] argc,argv
+ * @param [out] errmsg 將錯誤訊息寫到這裡
+ * (若給定NULL pointer, 則會把錯誤訊息忽略)
+ * @return \c true/false \b 成功與否 (否的話代表有錯誤的設定值在其中)
+ */
+ bool arguments(int argc, char** argv, String* errmsg){
+ opterr = 0;
+ String s;
+ OptionsIterator it;
+ String zzz;
+ String& err = (errmsg == NULL ? zzz : *errmsg);
+ for (it = options_.begin(); it != options_.end(); ++it) {
+ s += (char)(it->first);
+ if (it->second.hasValue()) s += ":";
+ }
+ bool succ = true;
+ for (int opt; (opt = getopt(argc, argv, s.c_str())) != -1; ) {
+ if (options_.find(opt) == options_.end()) {
+ if(options_.find(optopt) == options_.end()){
+ err += stringPrintf("Unknown option '-%c'\n", optopt);
+ }else{
+ err += stringPrintf("No specify argument to '-%c'\n",
+ optopt);
+ }
+ succ = false;
+ continue;
+ }
+ if (options_[opt].valueAdd(optarg == NULL ? "" : optarg) < 0) {
+ err += stringPrintf("Option argument '%s' to '-%c' is not allowed\n"
+ , optarg, opt);
+ succ = false;
+ continue;
+ }
+ }
+ for (it = options_.begin(); it != options_.end(); it++) {
+ if (it->second.chkSetup() == false) {
+ err += stringPrintf("No specify argument to '-%c'\n",
+ it->first);
+ succ = false;
+ continue;
+ }
+ }
+ for (int i = optind; i < argc; i++) {
+ proc_arguments_.push_back(String(argv[i]));
+ }
+ return succ;
+ }
+};
+
+} // meow
-#endif // Usage_H__
+#endif // MEOW_USAGE_H__