$search
00001 #include "utilmm/configfile/commandline.hh" 00002 00003 #include "utilmm/configfile/configset.hh" 00004 #include "utilmm/stringtools.hh" 00005 #include <boost/regex.hpp> 00006 00007 using namespace boost; 00008 using namespace std; 00009 00010 namespace { 00011 00012 char advance(std::string const& source, std::string const& allowed, size_t& current, std::string& text) 00013 { 00014 size_t next = source.find_first_of(allowed, current); 00015 text = string(source, current, next - current); 00016 current = next; 00017 00018 return (next == string::npos) ? 0 : source[current]; 00019 } 00020 00021 using namespace utilmm; 00022 00023 } 00024 00025 namespace utilmm 00026 { 00031 cmdline_option::cmdline_option(const std::string& description) 00032 : m_multiple(false), m_required(false), m_argument_flags(None) 00033 { 00034 static regex rx_valid_identifier("[\\w-]+"); 00035 00036 size_t current = 0; 00037 if (description[current] == '!') 00038 { 00039 ++current; 00040 m_required = true; 00041 } 00042 if (description[current] == '*') 00043 { 00044 ++current; 00045 m_multiple = true; 00046 } 00047 00048 if (!advance(description, ":", current, m_config)) 00049 throw bad_syntax(description, "expected ':'"); 00050 00051 char delim = advance(description, ",=?:", ++current, m_long); 00052 if (! regex_match(m_long, rx_valid_identifier)) 00053 throw bad_syntax(description, "invalid identifier"); 00054 if (!delim) return; 00055 00056 std::string text; 00057 00058 if (delim == ',') 00059 { 00060 delim = advance(description, "=?:", ++current, text); 00061 00062 if (text.size() != 1) 00063 throw bad_syntax(description, "short option string must be only one character"); 00064 m_short = text; 00065 if (! regex_match(m_short, rx_valid_identifier)) 00066 throw bad_syntax(description, "invalid short option character"); 00067 00068 if (! delim) return; 00069 } 00070 00071 if (delim == '=' || delim == '?') 00072 { 00073 if (delim == '?') 00074 m_argument_flags |= Optional; 00075 00076 delim = advance(description, ",:", ++current, text); 00077 00078 if (text == "int") 00079 m_argument_flags |= IntArgument; 00080 else if (text == "string") 00081 m_argument_flags |= StringArgument; 00082 else if (text == "bool") 00083 m_argument_flags |= BoolArgument; 00084 else 00085 throw bad_syntax(description, "invalid option type"); 00086 00087 if (delim == ',') 00088 { 00089 delim = advance(description, ":", ++current, text); 00090 if (isRequired() && !isArgumentOptional()) 00091 throw bad_syntax(description, "it is meaningless to have a default value for a required argument of a require option"); 00092 00093 if (!checkArgument(text)) 00094 throw bad_syntax(description, "default value for " + getLong() + " is not a valid value"); 00095 00096 m_argument_flags |= DefaultValue; 00097 m_default = text; 00098 } 00099 else if (m_argument_flags & Optional) 00100 throw bad_syntax(description, "options with optional arguments should have a default value"); 00101 00102 if (!delim) return; 00103 } 00104 00105 m_help = string(description, current + 1); 00106 } 00107 00108 cmdline_option::~cmdline_option() { } 00109 00110 bool cmdline_option::isMultiple() const { return m_multiple; } 00111 std::string cmdline_option::getConfigKey() const 00112 { 00113 if (!m_config.empty()) 00114 return m_config; 00115 return getLong(); 00116 } 00117 std::string cmdline_option::getLong() const { return m_long; } 00118 std::string cmdline_option::getShort() const { return m_short; } 00119 std::string cmdline_option::getHelp() const { return m_help; } 00120 00121 bool cmdline_option::isRequired() const { return m_required; } 00122 00123 int cmdline_option::getArgumentFlags() const { return m_argument_flags; } 00124 bool cmdline_option::hasArgument() const { return m_argument_flags; } 00125 bool cmdline_option::isArgumentOptional() const { return m_argument_flags & Optional; } 00126 00127 bool cmdline_option::hasDefaultValue() const { return m_argument_flags & DefaultValue; } 00128 std::string cmdline_option::getDefaultValue() const 00129 { 00130 if (hasDefaultValue()) 00131 return m_default; 00132 return "true"; 00133 } 00134 00138 bool cmdline_option::checkArgument(const std::string& value) const 00139 { 00140 static const regex 00141 rx_int("[0-9]+"), 00142 rx_bool("1|0|false|true"); 00143 00144 00145 if (m_argument_flags & IntArgument) 00146 return regex_match(value, rx_int); 00147 else if (m_argument_flags & BoolArgument) 00148 return regex_match(value, rx_bool); 00149 else return true; 00150 } 00151 00152 command_line::command_line(const char* options[]) 00153 { 00154 for(char const** opt = options; *opt; ++opt) 00155 m_options.push_back(cmdline_option(*opt)); 00156 } 00157 00158 command_line::command_line(const std::list<std::string>& description) 00159 { 00160 for(list<string>::const_iterator it = description.begin(); it != description.end(); ++it) 00161 m_options.push_back(cmdline_option(*it)); 00162 } 00163 00164 command_line::~command_line() { } 00165 00166 void command_line::add_argument(config_set& config, cmdline_option const& optdesc, std::string const& value) 00167 { 00168 if (! optdesc.checkArgument(value)) 00169 throw commandline_error("invalid value for --" + optdesc.getLong()); 00170 00171 if (optdesc.isMultiple()) 00172 config.insert(optdesc.getConfigKey(), value); 00173 else 00174 config.set(optdesc.getConfigKey(), value); 00175 } 00176 00177 int command_line::option_match(config_set& config, cmdline_option const& opt, int argc, char const* const* argv, int i) 00178 { 00179 if (argv[i] == "-" + opt.getShort()) 00180 { 00181 // check if it looks like we have an argument 00182 bool has_argument = (i + 1 < argc && argv[i + 1][0] != '-'); 00183 00184 if (!opt.hasArgument() || (opt.isArgumentOptional() && !has_argument)) 00185 add_argument(config, opt, opt.getDefaultValue()); 00186 else if (has_argument) 00187 { 00188 add_argument(config, opt, argv[i + 1]); 00189 return i + 2; 00190 } 00191 else 00192 throw commandline_error("missing argument to -" + opt.getShort()); 00193 } 00194 else if (argv[i] == "--" + opt.getLong()) 00195 { 00196 if (opt.hasArgument() && !opt.isArgumentOptional()) 00197 throw commandline_error("missing argument to --" + opt.getLong()); 00198 00199 add_argument(config, opt, opt.getDefaultValue()); 00200 } 00201 else if (starts_with(argv[i], "--" + opt.getLong() + "=")) 00202 { 00203 if (!opt.hasArgument()) 00204 throw commandline_error("argument provided to --" + opt.getLong()); 00205 00206 int seed_size = opt.getLong().length() + 3; 00207 add_argument(config, opt, std::string(argv[i]).substr(seed_size)); 00208 } 00209 else return i; 00210 return i + 1; 00211 } 00212 00213 void command_line::parse(int argc, char const* const argv[], config_set& config) 00214 { 00215 std::list<string> remains; 00216 00217 // Parse options from the command line 00218 for (int i = 0; i < argc; ) 00219 { 00220 if (argv[i][0] != '-') 00221 { 00222 remains.push_back(argv[i]); 00223 ++i; 00224 continue; 00225 } 00226 00227 // Get the option description object (if this is an option) 00228 Options::iterator opt; 00229 for (opt = m_options.begin(); opt != m_options.end(); ++opt) 00230 { 00231 int new_index = option_match(config, *opt, argc, argv, i); 00232 if (new_index != i) 00233 { 00234 i = new_index; 00235 break; 00236 } 00237 } 00238 if (opt == m_options.end()) 00239 throw commandline_error("unknown argument " + string(argv[i])); 00240 } 00241 00242 // Set default values for options that have one 00243 for (Options::iterator opt = m_options.begin(); opt != m_options.end(); ++opt) 00244 { 00245 if (config.exists(opt->getConfigKey())) 00246 continue; 00247 00248 if (opt->isRequired()) 00249 throw commandline_error("required option --" + opt->getLong() + " is missing"); 00250 else if (opt->hasDefaultValue()) 00251 config.set(opt->getConfigKey(), opt->getDefaultValue()); 00252 } 00253 00254 m_remaining = remains; 00255 } 00256 00257 list<string> command_line::remaining() const { return m_remaining; } 00258 void command_line::setBanner(std::string const& str) { m_banner = str; } 00259 void command_line::usage(std::ostream& out) const 00260 { 00261 if (! m_banner.empty()) 00262 out << m_banner << "\n"; 00263 00264 for(Options::const_iterator it = m_options.begin(); it != m_options.end(); ++it) 00265 { 00266 string longopt = it->getLong(), shortopt = it->getShort(); 00267 bool hasarg = it->hasArgument(); 00268 bool optarg = it->isArgumentOptional(); 00269 string helpstring = it->getHelp(); 00270 00271 out << " "; 00272 if (!shortopt.empty()) 00273 out << "-" << shortopt << "\t"; 00274 out << "--" << longopt; 00275 if (hasarg) 00276 { 00277 if (optarg) out << "[=VALUE]"; 00278 else out << "=VALUE"; 00279 } 00280 out << "\t" << helpstring << "\n"; 00281 } 00282 00283 } 00284 } 00285 00286