00001 #ifndef TAGCOLL_COMMANDLINE_H
00002 #define TAGCOLL_COMMANDLINE_H
00003
00004 #include <tagcoll/Exception.h>
00005 #include <string>
00006 #include <vector>
00007 #include <list>
00008 #include <map>
00009 #include <ostream>
00010
00011 namespace Tagcoll {
00012 namespace commandline {
00013
00014 typedef std::list<const char*> arglist;
00015 typedef arglist::iterator iter;
00016
00017 class BadOption : public ConsistencyCheckException
00018 {
00019 public:
00020 BadOption(const std::string& context) throw ()
00021 : ConsistencyCheckException(context) {}
00022 virtual ~BadOption() throw () {}
00023
00024 virtual const char* type() const throw () { return "BadOption"; }
00025 };
00026
00028 class Parser
00029 {
00030 std::string m_name;
00031
00032 public:
00033 Parser(const std::string& name) : m_name(name) {}
00034 virtual ~Parser() {}
00035
00036 const std::string& name() const { return m_name; }
00037
00045 virtual iter parseList(arglist& list) { return parse(list, list.begin()); }
00046
00056 virtual iter parse(arglist& list, iter begin) = 0;
00057 };
00058
00060 class Option
00061 {
00062 std::string m_name;
00063 mutable std::string m_fullUsage;
00064
00065 public:
00066 Option(const std::string& name) : m_name(name) {}
00067 Option(const std::string& name, char shortName, const std::string& longName) : m_name(name)
00068 {
00069 if (shortName != 0)
00070 shortNames.push_back(shortName);
00071 if (!longName.empty())
00072 longNames.push_back(longName);
00073 }
00074 virtual ~Option() {}
00075
00076 const std::string& name() const { return m_name; }
00077
00078 void addAlias(char c) { shortNames.push_back(c); }
00079 void addAlias(const std::string& str) { longNames.push_back(str); }
00080
00081 virtual bool boolValue() const = 0;
00082 virtual std::string stringValue() const = 0;
00083 virtual int intValue() const;
00084
00092 virtual bool parse(const char* str = 0) = 0;
00093
00095 const std::string& fullUsage() const;
00096 std::string fullUsageForMan() const;
00097
00098 std::vector<char> shortNames;
00099 std::vector<std::string> longNames;
00100
00101 std::string usage;
00102 std::string description;
00103 };
00104
00106 class BoolOption : public Option
00107 {
00108 bool m_value;
00109 public:
00110 BoolOption(const std::string& name)
00111 : Option(name), m_value(false) {}
00112 BoolOption(const std::string& name, char shortName, const std::string& longName)
00113 : Option(name, shortName, longName), m_value(false) {}
00114
00115 bool boolValue() const { return m_value; }
00116 std::string stringValue() const { return m_value ? "true" : "false"; }
00117
00118 bool parse(const char* str) { m_value = true; return false; }
00119 };
00120
00121
00122 class StringOption : public Option
00123 {
00124 std::string m_value;
00125 public:
00126 StringOption(const std::string& name)
00127 : Option(name)
00128 {
00129 usage = "<val>";
00130 }
00131 StringOption(const std::string& name, char shortName, const std::string& longName)
00132 : Option(name, shortName, longName)
00133 {
00134 usage = "<val>";
00135 }
00136
00137 bool boolValue() const { return !m_value.empty(); }
00138 std::string stringValue() const { return m_value; }
00139
00140 bool parse(const char* str);
00141 };
00142
00143
00144 class IntOption : public Option
00145 {
00146 bool m_has_value;
00147 int m_value;
00148
00149 public:
00150 IntOption(const std::string& name)
00151 : Option(name), m_has_value(false), m_value(0)
00152 {
00153 usage = "<num>";
00154 }
00155 IntOption(const std::string& name, char shortName, const std::string& longName)
00156 : Option(name, shortName, longName), m_has_value(false), m_value(0)
00157 {
00158 usage = "<num>";
00159 }
00160
00161 bool boolValue() const { return m_has_value; }
00162 int intValue() const { return m_value; }
00163 std::string stringValue() const;
00164
00165 bool parse(const char* str);
00166 };
00167
00168 class ExistingFileOption : public Option
00169 {
00170 std::string m_value;
00171 public:
00172 ExistingFileOption(const std::string& name)
00173 : Option(name)
00174 {
00175 usage = "<file>";
00176 }
00177 ExistingFileOption(const std::string& name, char shortName, const std::string& longName)
00178 : Option(name, shortName, longName)
00179 {
00180 usage = "<file>";
00181 }
00182
00183 bool boolValue() const { return !m_value.empty(); }
00184 std::string stringValue() const { return m_value; }
00185
00186 bool parse(const char* str);
00187 };
00188
00189 class OptionGroup
00190 {
00191
00192 public:
00193 void add(Option* o) { options.push_back(o); }
00194
00195 std::vector<Option*> options;
00196
00197 std::string description;
00198 };
00199
00201 class OptionParser : public Parser
00202 {
00203 std::map<char, Option*> m_short;
00204 std::map<std::string, Option*> m_long;
00205 std::vector<OptionGroup*> m_groups;
00206 std::vector<Option*> m_options;
00207
00209 iter parseConsecutiveSwitches(arglist& list, iter begin);
00210
00211 void addWithoutAna(Option* o);
00212
00213 public:
00214 OptionParser(const std::string& name)
00215 : Parser(name), primaryAlias(name) {}
00216
00217 void add(Option* o);
00218 void add(OptionGroup* group);
00219
00220 const std::vector<OptionGroup*>& groups() const { return m_groups; }
00221 const std::vector<Option*>& options() const { return m_options; }
00222
00227 virtual iter parse(arglist& list, iter begin);
00228
00229 std::string primaryAlias;
00230 std::vector<std::string> aliases;
00231 std::string usage;
00232 std::string description;
00233 std::string longDescription;
00234 std::string examples;
00235 };
00236
00237 class CommandParser : public Parser
00238 {
00239 OptionParser* m_last_command;
00240 std::map<std::string, OptionParser*> m_aliases;
00241
00242 void add(const std::string& alias, OptionParser* o);
00243
00244 public:
00245 CommandParser(const std::string& name)
00246 : Parser(name), m_last_command(0) {}
00247
00248 OptionParser* lastCommand() const { return m_last_command; }
00249 OptionParser* command(const std::string& name) const;
00250
00251 void add(OptionParser& o);
00252
00261 virtual iter parse(arglist& list, iter begin);
00262
00263 std::map<std::string, OptionParser*> getCommandInfo() const;
00264
00265 std::string usage;
00266 std::string description;
00267 std::string longDescription;
00268 };
00269
00276 template<class Base>
00277 class MainParser : public Base
00278 {
00279 arglist args;
00280
00281 public:
00282 MainParser(const std::string& name) : Base(name) {}
00283
00284 arglist parse(int argc, const char* argv[])
00285 {
00286 for (int i = 1; i < argc; i++)
00287 args.push_back(argv[i]);
00288 this->parseList(args);
00289 return args;
00290 }
00291
00292 bool hasNext() const { return !args.empty(); }
00293
00294 std::string next()
00295 {
00296 if (args.empty())
00297 return std::string();
00298 std::string res(*args.begin());
00299 args.erase(args.begin());
00300 return res;
00301 }
00302 };
00303
00304 class DocMaker
00305 {
00306 protected:
00307 std::string m_app;
00308 std::string m_ver;
00309
00310 public:
00311 DocMaker(const std::string& app, const std::string& ver)
00312 : m_app(app), m_ver(ver) {}
00313 };
00314
00315 class Help : public DocMaker
00316 {
00317 public:
00318 Help(const std::string& app, const std::string& ver)
00319 : DocMaker(app, ver) {}
00320
00321 void outputVersion(std::ostream& out);
00322 void outputHelp(std::ostream& out, const CommandParser& cp);
00323 void outputHelp(std::ostream& out, const OptionParser& cp);
00324 };
00325
00326 class Manpage : public DocMaker
00327 {
00328 public:
00329 enum where { BEFORE, BEGINNING, END };
00330
00331 private:
00332 struct Hook
00333 {
00334 std::string section;
00335 where placement;
00336 std::string text;
00337
00338 Hook(const std::string& section, where placement, const std::string& text)
00339 : section(section), placement(placement), text(text) {}
00340 };
00341
00342 std::vector<Hook> hooks;
00343 std::string lastSection;
00344
00345 void outputParagraph(std::ostream& out, const std::string& str);
00346 void outputOption(std::ostream& out, const Option* o);
00347 void runHooks(std::ostream& out, const std::string& section, where where);
00348 void startSection(std::ostream& out, const std::string& name);
00349 void endSection(std::ostream& out);
00350
00351
00352 public:
00353 Manpage(const std::string& app, const std::string& ver)
00354 : DocMaker(app, ver) {}
00355
00356 void addHook(const std::string& section, where placement, const std::string& text)
00357 {
00358 hooks.push_back(Hook(section, placement, text));
00359 }
00360 void readHooks(const std::string& file);
00361
00362 void output(std::ostream& out, const CommandParser& cp, int section);
00363 void output(std::ostream& out, const OptionParser& cp, int section);
00364 };
00365
00366 }
00367 }
00368
00369
00370 #endif