00001 // -*-Mode: C++;-*- 00002 // $Header: /Volumes/cvsrep/developer/OpenADFortTk/src/CmdLineParser.h,v 1.5 2004/04/28 17:46:19 eraxxon Exp $ 00003 00004 // * BeginCopyright ********************************************************* 00005 // *********************************************************** EndCopyright * 00006 00007 //*************************************************************************** 00008 // 00009 // File: 00010 // $Source: /Volumes/cvsrep/developer/OpenADFortTk/src/CmdLineParser.h,v $ 00011 // 00012 // Nathan Tallent 00013 // 00014 // Purpose: 00015 // [The purpose of this file] 00016 // 00017 // Description: 00018 // [The set of functions, macros, etc. defined in the file] 00019 // 00020 //*************************************************************************** 00021 00022 #ifndef CmdLineParser_H 00023 #define CmdLineParser_H 00024 00025 //************************* System Include Files **************************** 00026 00027 #include <iostream> 00028 #include <map> 00029 #include <vector> 00030 #include <string> 00031 00032 //*************************** User Include Files **************************** 00033 00034 #include <inttypes.h> /* commonly available, unlike <stdint.h> */ 00035 00036 //*************************** Forward Declarations *************************** 00037 00038 //**************************************************************************** 00039 00040 //**************************************************************************** 00041 // CmdLineParser 00042 //**************************************************************************** 00043 00044 // CmdLineParser: Parses arguments on the command line, argc and argv. 00045 // Provides easy access to both optional arguments (with short or long 00046 // specifiers) and regular arguments. Provides functionality similar 00047 // to getopt() and GNU's getopt_long(), but in an easier to use -- and 00048 // in the case of getopt_long(), more portable -- package. In 00049 // addition, the package provides configurable handling of duplicate 00050 // options/arguments and routines to convert string arguments into 00051 // numerical types. 00052 // 00053 // A user creates a NULL-terminated array of option descriptors 00054 // (OptArgDesc) indicating switch names and arguments, if any. He 00055 // then instantiates a CmdLineParser and parses argc/argv. Errors are 00056 // delivered with the exception Exception. The parser object provides 00057 // access to all optional and regular arguments using the interface 00058 // routines belows. 00059 // 00060 // More details: 00061 // The command line generally has the form (but note qualifications below): 00062 // <command> [arguments] 00063 // 00064 // [arguments] ::= [optional_arg] | [regular_arg] 00065 // 00066 // [optional_arg] ::= -f [arg] | -f[arg] 00067 // | --foo [arg] | --foo=arg 00068 // 00069 // An element of argv that starts with '-' or '--' (and is not exactly 00070 // '--') signifies an option. The option switch is the text without 00071 // the initial dashes. As the above shows, we support a variety of 00072 // option styles. 00073 // 00074 // Features: 00075 // - The '--' token forces the end of optional argument scanning and 00076 // iterprets everything following as a list of regular arguments. 00077 // This is useful for non-option arguments that begin with dashes. 00078 // - Unlike getopt() we do not support the deprecated use of the single 00079 // '-'; this is an error. 00080 // - Long argument switches may be abbreviated if the abbreviation 00081 // is unique. 00082 // - Configurable handling of duplicate options and arguments. 00083 // 00084 // Limitations: 00085 // - Unlike getopt(), we do not currently support short switch grouping, 00086 // e.g. using -abc instead of -a -b -c. [FIXME: we can assume that 00087 // only options without arguments are allowed to be grouped.] 00088 // 00089 // Warnings: 00090 // - Switches that take optional arguments can be confusing. For 00091 // example, assume a command 'foo' takes a filename and on option, 00092 // --debug, which itself takes an optional debug level. The 00093 // following commands pose no difficulties: 00094 // foo myfile 00095 // foo --debug=3 myfile 00096 // foo --debug 3 myfile 00097 // foo myfile --debug 00098 // However, in the following 00099 // foo --debug myfile 00100 // 'myfile' is erroneously assumed to be the optional argument to 00101 // --debug. While the '--' token solves this, it remains awkward. 00102 // 00103 class CmdLineParser { 00104 public: 00105 00106 // --------------------------------------------------------- 00107 // Structure used to describe command line options 00108 // --------------------------------------------------------- 00109 00110 // Describes if an option switch takes an argument 00111 enum OptKind { 00112 ARG_NULL = 0, 00113 ARG_NONE, // switch does not take argument 00114 ARG_REQ, // switch must take an argument 00115 ARG_OPT // switch takes an (optional!) argument 00116 }; 00117 00118 // Describes how to handle duplicate options and option arguments 00119 enum DupOptKind { 00120 DUPOPT_NULL = 0, 00121 DUPOPT_ERR, // throw an exception for duplicate option or argument 00122 DUPOPT_CLOB, // clobber any previous argument 00123 DUPOPT_CAT // concat all available arguments using 'dupArgSep' 00124 }; 00125 00126 struct OptArgDesc { 00127 00128 bool operator==(const OptArgDesc& x) const { 00129 return (swShort == x.swShort && swLong == x.swLong 00130 && kind == x.kind && dupKind == x.dupKind 00131 && dupArgSep == x.dupArgSep); 00132 } 00133 bool operator!=(const OptArgDesc& x) const { return !(*this == x); } 00134 00135 // Data 00136 char swShort; // 0 if n/a 00137 const char* swLong; // NULL if n/a 00138 OptKind kind; 00139 DupOptKind dupKind; 00140 const char* dupArgSep; // separator for 'DUPARG_CONCAT' 00141 }; 00142 00143 // The NULL terminator (two versions). The use of the first version 00144 // is preferable, but some older compilers won't support it. 00145 static OptArgDesc OptArgDesc_NULL; 00146 # define CmdLineParser_OptArgDesc_NULL_MACRO \ 00147 { 0, NULL, CmdLineParser::ARG_NULL, CmdLineParser::DUPOPT_NULL, NULL } 00148 00149 00150 // --------------------------------------------------------- 00151 // Exception thrown when errors are encountered 00152 // --------------------------------------------------------- 00153 00154 class Exception { 00155 public: 00156 Exception(const char* m) : msg(m) { } 00157 Exception(std::string m) : msg(m) { } 00158 virtual ~Exception () { } 00159 00160 virtual const std::string& GetMessage() const { return msg; } 00161 virtual void Report(std::ostream& os) const { 00162 os << "CmdLineParser::Exception: " << msg << std::endl; 00163 } 00164 virtual void Report() const { Report(std::cerr); } 00165 00166 protected: 00167 std::string msg; 00168 }; 00169 00170 class ParseError : public Exception { 00171 public: 00172 ParseError(const char* m) : Exception(m) { } 00173 ParseError(std::string m) : Exception(m) { } 00174 virtual ~ParseError () { } 00175 }; 00176 00177 class InternalError : public Exception { 00178 public: 00179 InternalError(const char* m) : Exception(m) { } 00180 InternalError(std::string m) : Exception(m) { } 00181 virtual ~InternalError () { } 00182 private: 00183 void Ctor() { 00184 msg = "CmdLineParser internal error (Don't abuse me!): " + msg; 00185 } 00186 }; 00187 00188 // --------------------------------------------------------- 00189 00190 public: 00191 // --------------------------------------------------------- 00192 // Constructor/Destructor 00193 // --------------------------------------------------------- 00194 CmdLineParser(); 00195 CmdLineParser(const OptArgDesc* optArgDescs, 00196 int argc, const char* const argv[]); 00197 ~CmdLineParser(); 00198 00199 // ------------------------------------------------------- 00200 // Parsing 00201 // ------------------------------------------------------- 00202 00203 // Parse: Given a NULL terminated array of OptArgDesc describing 00204 // command line arguments, parses the argv/argc into switches, 00205 // optional and required arguments. 00206 void 00207 Parse(const OptArgDesc* optArgDescs, 00208 int argc, const char* const argv[]); 00209 00210 // ------------------------------------------------------- 00211 // Parsed Data: Command 00212 // ------------------------------------------------------- 00213 00214 // GetCmd: The command (will be valid even after a parse error) 00215 const std::string& GetCmd() const; 00216 00217 // ------------------------------------------------------- 00218 // Parsed Data: Optional arguments 00219 // ------------------------------------------------------- 00220 00221 // IsOpt: (IsOption) Given a short or long switch, returns whether 00222 // the switch has been seen. 00223 bool IsOpt(const char swShort) const; 00224 bool IsOpt(const char* swLong) const; 00225 bool IsOpt(const std::string& sw) const; 00226 00227 // IsOptArg: (IsOptionArgument) Given a short or long switch, 00228 // returns whether an argument is associated with it. Designed for 00229 // switches that optionally take arguments. 00230 bool IsOptArg(const char swShort) const; 00231 bool IsOptArg(const char* swLong) const; 00232 bool IsOptArg(const std::string& sw) const; 00233 00234 // GetOptArg: (GetOptionArgument) Given a short or long switch, get 00235 // the argument associated with it. Assumes user has verified that 00236 // an argument *exists*. 00237 const std::string& GetOptArg(const char swShort) const; 00238 const std::string& GetOptArg(const char* swLong) const; 00239 const std::string& GetOptArg(const std::string& sw) const; 00240 00241 // ------------------------------------------------------- 00242 // Parsed Data: Arguments 00243 // ------------------------------------------------------- 00244 unsigned int GetNumArgs() const; 00245 const std::string& GetArg(unsigned int i) const; 00246 00247 // ------------------------------------------------------- 00248 // Convert strings into other formats 00249 // ------------------------------------------------------- 00250 // The input should be non-empty 00251 static long ToLong(const std::string& str); 00252 static uint64_t ToUInt64(const std::string& str); 00253 static double ToDbl(const std::string& str); 00254 00255 // ------------------------------------------------------- 00256 // Misc 00257 // ------------------------------------------------------- 00258 void Dump(std::ostream& os = std::cerr) const; 00259 void DDump() const; 00260 00261 private: 00262 // Should not be used 00263 CmdLineParser(const CmdLineParser& p) { } 00264 CmdLineParser& operator=(const CmdLineParser& x) { return *this; } 00265 00266 typedef std::map<std::string, std::string*> SwitchToArgMap; 00267 typedef std::vector<std::string> ArgVec; 00268 00269 // Switch descriptor (Because of limited use, we allow this to be 00270 // returned as an object) 00271 class SwDesc { 00272 public: 00273 SwDesc() : isLong(false) { } 00274 SwDesc(const char* sw_, bool isLong_, const char* arg_) 00275 : sw(sw_), isLong(isLong_), arg(arg_) { } 00276 SwDesc(const std::string& sw_, bool isLong_, const std::string& arg_) 00277 : sw(sw_), isLong(isLong_), arg(arg_) { } 00278 ~SwDesc() { } 00279 // use default copy constructor if necessary 00280 00281 std::string sw; // switch text without dashes 00282 bool isLong; // long style 00283 std::string arg; // any argument 00284 }; 00285 00286 private: 00287 void Ctor(); 00288 void Reset(); 00289 void CheckForErrors(const OptArgDesc* optArgDescs); 00290 00291 const OptArgDesc* 00292 CreateSortedCopy(const OptArgDesc* optArgDescs); 00293 00294 // Parsing helpers 00295 SwDesc 00296 MakeSwitchDesc(const char* str); 00297 00298 const OptArgDesc* 00299 FindOptDesc(const OptArgDesc* optArgDescs, const SwDesc& swdesc, 00300 bool errOnMultipleMatches = true); 00301 00302 void 00303 AddOption(const OptArgDesc& odesc, const SwDesc& swdesc); 00304 00305 void 00306 AddOption(const OptArgDesc& odesc, 00307 const std::string& sw, const std::string& arg); 00308 00309 private: 00310 00311 std::string command; // comand name 00312 SwitchToArgMap switchToArgMap; // optional arguments 00313 ArgVec arguments; // regular arguments 00314 }; 00315 00316 //**************************************************************************** 00317 00318 #endif 00319
1.5.7.1