diff options
Diffstat (limited to 'meowpp/Usage.h')
-rw-r--r-- | meowpp/Usage.h | 601 |
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__ |