1 /* 2 * Copyright 2013 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #ifndef SK_COMMAND_LINE_FLAGS_H 9 #define SK_COMMAND_LINE_FLAGS_H 10 11 #include "../private/SkTArray.h" 12 #include "../private/SkTDArray.h" 13 #include "SkString.h" 14 15 /** 16 * Including this file (and compiling SkCommandLineFlags.cpp) provides command line 17 * parsing. In order to use it, use the following macros in global 18 * namespace: 19 * 20 * DEFINE_bool(name, defaultValue, helpString); 21 * DEFINE_string(name, defaultValue, helpString); 22 * DEFINE_int32(name, defaultValue, helpString); 23 * DEFINE_double(name, defaultValue, helpString); 24 * 25 * Then, in main, call SkCommandLineFlags::SetUsage() to describe usage and call 26 * SkCommandLineFlags::Parse() to parse the flags. Henceforth, each flag can 27 * be referenced using 28 * 29 * FLAGS_name 30 * 31 * For example, the line 32 * 33 * DEFINE_bool(boolean, false, "The variable boolean does such and such"); 34 * 35 * will create the following variable: 36 * 37 * bool FLAGS_boolean; 38 * 39 * which will initially be set to false, and can be set to true by using the 40 * flag "--boolean" on the commandline. "--noboolean" will set FLAGS_boolean 41 * to false. FLAGS_boolean can also be set using "--boolean=true" or 42 * "--boolean true" (where "true" can be replaced by "false", "TRUE", "FALSE", 43 * "1" or "0"). 44 * 45 * The helpString will be printed if the help flag (-h or -help) is used. 46 * 47 * Similarly, the line 48 * 49 * DEFINE_int32(integer, .., ..); 50 * 51 * will create 52 * 53 * int32_t FLAGS_integer; 54 * 55 * and 56 * 57 * DEFINE_double(real, .., ..); 58 * 59 * will create 60 * 61 * double FLAGS_real; 62 * 63 * and 64 * 65 * DEFINE_uint32(unsigned, ...); 66 * 67 * will create 68 * 69 * uint32_t FLAGS_unsigned; 70 * 71 * These flags can be set by specifying, for example, "--integer 7" and 72 * "--real 3.14" on the command line. Unsigned integers are parsed from the 73 * command line using strtoul() so will detect the base (0 for octal, and 74 * 0x or 0X for hex, otherwise assumes decimal). 75 * 76 * Unlike the others, the line 77 * 78 * DEFINE_string(args, .., ..); 79 * 80 * creates an array: 81 * 82 * SkCommandLineFlags::StringArray FLAGS_args; 83 * 84 * If the default value is the empty string, FLAGS_args will default to a size 85 * of zero. Otherwise it will default to a size of 1 with the default string 86 * as its value. All strings that follow the flag on the command line (until 87 * a string that begins with '-') will be entries in the array. 88 * 89 * DEFINE_extended_string(args, .., .., extendedHelpString); 90 * 91 * creates a similar string array flag as DEFINE_string. The flag will have extended help text 92 * (extendedHelpString) that can the user can see with '--help <args>' flag. 93 * 94 * Any flag can be referenced from another file after using the following: 95 * 96 * DECLARE_x(name); 97 * 98 * (where 'x' is the type specified in the DEFINE). 99 * 100 * Inspired by gflags (https://code.google.com/p/gflags/). Is not quite as 101 * robust as gflags, but suits our purposes. For example, allows creating 102 * a flag -h or -help which will never be used, since SkCommandLineFlags handles it. 103 * SkCommandLineFlags will also allow creating --flag and --noflag. Uses the same input 104 * format as gflags and creates similarly named variables (i.e. FLAGS_name). 105 * Strings are handled differently (resulting variable will be an array of 106 * strings) so that a flag can be followed by multiple parameters. 107 */ 108 109 class SkFlagInfo; 110 111 class SkCommandLineFlags { 112 113 public: 114 /** 115 * Call to set the help message to be displayed. Should be called before 116 * Parse. 117 */ 118 static void SetUsage(const char* usage); 119 120 /** 121 * Call this to display the help message. Should be called after SetUsage. 122 */ 123 static void PrintUsage(); 124 125 /** 126 * Call at the beginning of main to parse flags created by DEFINE_x, above. 127 * Must only be called once. 128 */ 129 static void Parse(int argc, const char* const * argv); 130 131 /** 132 * Custom class for holding the arguments for a string flag. 133 * Publicly only has accessors so the strings cannot be modified. 134 */ 135 class StringArray { 136 public: StringArray()137 StringArray() { } StringArray(const SkTArray<SkString> & strings)138 explicit StringArray(const SkTArray<SkString>& strings) 139 : fStrings(strings) { 140 } 141 const char* operator[](int i) const { 142 SkASSERT(i >= 0 && i < fStrings.count()); 143 return fStrings[i].c_str(); 144 } 145 count()146 int count() const { 147 return fStrings.count(); 148 } 149 isEmpty()150 bool isEmpty() const { return this->count() == 0; } 151 152 /** 153 * Returns true iff string is equal to one of the strings in this array. 154 */ contains(const char * string)155 bool contains(const char* string) const { 156 for (int i = 0; i < fStrings.count(); i++) { 157 if (fStrings[i].equals(string)) { 158 return true; 159 } 160 } 161 return false; 162 } 163 set(int i,const char * str)164 void set(int i, const char* str) { 165 if (i >= fStrings.count()) { 166 this->append(str); 167 return; 168 } 169 fStrings[i].set(str); 170 } 171 begin()172 const SkString* begin() const { return fStrings.begin(); } end()173 const SkString* end() const { return fStrings.end(); } 174 175 private: reset()176 void reset() { fStrings.reset(); } 177 append(const char * string)178 void append(const char* string) { 179 fStrings.push_back().set(string); 180 } 181 append(const char * string,size_t length)182 void append(const char* string, size_t length) { 183 fStrings.push_back().set(string, length); 184 } 185 186 SkTArray<SkString> fStrings; 187 188 friend class SkFlagInfo; 189 }; 190 191 /* Takes a list of the form [~][^]match[$] 192 ~ causes a matching test to always be skipped 193 ^ requires the start of the test to match 194 $ requires the end of the test to match 195 ^ and $ requires an exact match 196 If a test does not match any list entry, it is skipped unless some list entry starts with ~ 197 */ 198 static bool ShouldSkip(const SkTDArray<const char*>& strings, const char* name); 199 static bool ShouldSkip(const StringArray& strings, const char* name); 200 201 private: 202 static SkFlagInfo* gHead; 203 static SkString gUsage; 204 205 // For access to gHead. 206 friend class SkFlagInfo; 207 }; 208 209 #define TO_STRING2(s) #s 210 #define TO_STRING(s) TO_STRING2(s) 211 212 #define DEFINE_bool(name, defaultValue, helpString) \ 213 bool FLAGS_##name; \ 214 SK_UNUSED static bool unused_##name = SkFlagInfo::CreateBoolFlag(TO_STRING(name), \ 215 nullptr, \ 216 &FLAGS_##name, \ 217 defaultValue, \ 218 helpString) 219 220 // bool 2 allows specifying a short name. No check is done to ensure that shortName 221 // is actually shorter than name. 222 #define DEFINE_bool2(name, shortName, defaultValue, helpString) \ 223 bool FLAGS_##name; \ 224 SK_UNUSED static bool unused_##name = SkFlagInfo::CreateBoolFlag(TO_STRING(name), \ 225 TO_STRING(shortName),\ 226 &FLAGS_##name, \ 227 defaultValue, \ 228 helpString) 229 230 #define DECLARE_bool(name) extern bool FLAGS_##name; 231 232 #define DEFINE_string(name, defaultValue, helpString) \ 233 SkCommandLineFlags::StringArray FLAGS_##name; \ 234 SK_UNUSED static bool unused_##name = SkFlagInfo::CreateStringFlag(TO_STRING(name), \ 235 nullptr, \ 236 &FLAGS_##name, \ 237 defaultValue, \ 238 helpString, nullptr) 239 #define DEFINE_extended_string(name, defaultValue, helpString, extendedHelpString) \ 240 SkCommandLineFlags::StringArray FLAGS_##name; \ 241 SK_UNUSED static bool unused_##name = SkFlagInfo::CreateStringFlag(TO_STRING(name), \ 242 nullptr, \ 243 &FLAGS_##name, \ 244 defaultValue, \ 245 helpString, \ 246 extendedHelpString) 247 248 // string2 allows specifying a short name. There is an assert that shortName 249 // is only 1 character. 250 #define DEFINE_string2(name, shortName, defaultValue, helpString) \ 251 SkCommandLineFlags::StringArray FLAGS_##name; \ 252 SK_UNUSED static bool unused_##name = SkFlagInfo::CreateStringFlag(TO_STRING(name), \ 253 TO_STRING(shortName), \ 254 &FLAGS_##name, \ 255 defaultValue, \ 256 helpString, nullptr) 257 258 #define DECLARE_string(name) extern SkCommandLineFlags::StringArray FLAGS_##name; 259 260 261 262 263 #define DEFINE_int32(name, defaultValue, helpString) \ 264 int32_t FLAGS_##name; \ 265 SK_UNUSED static bool unused_##name = SkFlagInfo::CreateIntFlag(TO_STRING(name), \ 266 &FLAGS_##name, \ 267 defaultValue, \ 268 helpString) 269 270 #define DEFINE_int32_2(name, shortName, defaultValue, helpString) \ 271 int32_t FLAGS_##name; \ 272 SK_UNUSED static bool unused_##name = SkFlagInfo::CreateIntFlag(TO_STRING(name), \ 273 TO_STRING(shortName), \ 274 &FLAGS_##name, \ 275 defaultValue, \ 276 helpString) 277 278 #define DECLARE_int32(name) extern int32_t FLAGS_##name; 279 280 281 #define DEFINE_uint32(name, defaultValue, helpString) \ 282 uint32_t FLAGS_##name; \ 283 SK_UNUSED static bool unused_##name = SkFlagInfo::CreateUintFlag(TO_STRING(name), \ 284 &FLAGS_##name, \ 285 defaultValue, \ 286 helpString) 287 288 #define DEFINE_uint32_2(name, shortName, defaultValue, helpString) \ 289 uint32_t FLAGS_##name; \ 290 SK_UNUSED static bool unused_##name = SkFlagInfo::CreateUintFlag(TO_STRING(name), \ 291 TO_STRING(shortName),\ 292 &FLAGS_##name, \ 293 defaultValue, \ 294 helpString) 295 296 #define DECLARE_uint32(name) extern uint32_t FLAGS_##name; 297 298 299 #define DEFINE_double(name, defaultValue, helpString) \ 300 double FLAGS_##name; \ 301 SK_UNUSED static bool unused_##name = SkFlagInfo::CreateDoubleFlag(TO_STRING(name), \ 302 &FLAGS_##name, \ 303 defaultValue, \ 304 helpString) 305 306 #define DECLARE_double(name) extern double FLAGS_##name; 307 308 class SkFlagInfo { 309 310 public: 311 enum FlagTypes { 312 kBool_FlagType, 313 kString_FlagType, 314 kInt_FlagType, 315 kUint_FlagType, 316 kDouble_FlagType, 317 }; 318 319 /** 320 * Each Create<Type>Flag function creates an SkFlagInfo of the specified type. The SkFlagInfo 321 * object is appended to a list, which is deleted when SkCommandLineFlags::Parse is called. 322 * Therefore, each call should be made before the call to ::Parse. They are not intended 323 * to be called directly. Instead, use the macros described above. 324 * @param name Long version (at least 2 characters) of the name of the flag. This name can 325 * be referenced on the command line as "--name" to set the value of this flag. 326 * @param shortName Short version (one character) of the name of the flag. This name can 327 * be referenced on the command line as "-shortName" to set the value of this flag. 328 * @param p<Type> Pointer to a global variable which holds the value set by SkCommandLineFlags. 329 * @param defaultValue The default value of this flag. The variable pointed to by p<Type> will 330 * be set to this value initially. This is also displayed as part of the help output. 331 * @param helpString Explanation of what this flag changes in the program. 332 */ CreateBoolFlag(const char * name,const char * shortName,bool * pBool,bool defaultValue,const char * helpString)333 static bool CreateBoolFlag(const char* name, const char* shortName, bool* pBool, 334 bool defaultValue, const char* helpString) { 335 SkFlagInfo* info = new SkFlagInfo(name, shortName, kBool_FlagType, helpString, nullptr); 336 info->fBoolValue = pBool; 337 *info->fBoolValue = info->fDefaultBool = defaultValue; 338 return true; 339 } 340 341 /** 342 * See comments for CreateBoolFlag. 343 * @param pStrings Unlike the others, this is a pointer to an array of values. 344 * @param defaultValue Thise default will be parsed so that strings separated by spaces 345 * will be added to pStrings. 346 */ 347 static bool CreateStringFlag(const char* name, const char* shortName, 348 SkCommandLineFlags::StringArray* pStrings, 349 const char* defaultValue, const char* helpString, 350 const char* extendedHelpString); 351 352 /** 353 * See comments for CreateBoolFlag. 354 */ CreateIntFlag(const char * name,int32_t * pInt,int32_t defaultValue,const char * helpString)355 static bool CreateIntFlag(const char* name, int32_t* pInt, 356 int32_t defaultValue, const char* helpString) { 357 SkFlagInfo* info = new SkFlagInfo(name, nullptr, kInt_FlagType, helpString, nullptr); 358 info->fIntValue = pInt; 359 *info->fIntValue = info->fDefaultInt = defaultValue; 360 return true; 361 } 362 CreateIntFlag(const char * name,const char * shortName,int32_t * pInt,int32_t defaultValue,const char * helpString)363 static bool CreateIntFlag(const char* name, const char* shortName, int32_t* pInt, 364 int32_t defaultValue, const char* helpString) { 365 SkFlagInfo* info = new SkFlagInfo(name, shortName, kInt_FlagType, helpString, nullptr); 366 info->fIntValue = pInt; 367 *info->fIntValue = info->fDefaultInt = defaultValue; 368 return true; 369 } 370 371 /** 372 * See comments for CreateBoolFlag. 373 */ CreateUintFlag(const char * name,uint32_t * pUint,uint32_t defaultValue,const char * helpString)374 static bool CreateUintFlag(const char* name, uint32_t* pUint, 375 uint32_t defaultValue, const char* helpString) { 376 SkFlagInfo* info = new SkFlagInfo(name, nullptr, kUint_FlagType, helpString, nullptr); 377 info->fUintValue = pUint; 378 *info->fUintValue = info->fDefaultUint = defaultValue; 379 return true; 380 } 381 CreateUintFlag(const char * name,const char * shortName,uint32_t * pUint,uint32_t defaultValue,const char * helpString)382 static bool CreateUintFlag(const char* name, const char* shortName, uint32_t* pUint, 383 uint32_t defaultValue, const char* helpString) { 384 SkFlagInfo* info = new SkFlagInfo(name, shortName, kUint_FlagType, helpString, nullptr); 385 info->fUintValue = pUint; 386 *info->fUintValue = info->fDefaultUint = defaultValue; 387 return true; 388 } 389 390 /** 391 * See comments for CreateBoolFlag. 392 */ CreateDoubleFlag(const char * name,double * pDouble,double defaultValue,const char * helpString)393 static bool CreateDoubleFlag(const char* name, double* pDouble, 394 double defaultValue, const char* helpString) { 395 SkFlagInfo* info = new SkFlagInfo(name, nullptr, kDouble_FlagType, helpString, nullptr); 396 info->fDoubleValue = pDouble; 397 *info->fDoubleValue = info->fDefaultDouble = defaultValue; 398 return true; 399 } 400 401 /** 402 * Returns true if the string matches this flag. 403 * For a boolean flag, also sets the value, since a boolean flag can be set in a number of ways 404 * without looking at the following string: 405 * --name 406 * --noname 407 * --name=true 408 * --name=false 409 * --name=1 410 * --name=0 411 * --name=TRUE 412 * --name=FALSE 413 */ 414 bool match(const char* string); 415 getFlagType()416 FlagTypes getFlagType() const { return fFlagType; } 417 resetStrings()418 void resetStrings() { 419 if (kString_FlagType == fFlagType) { 420 fStrings->reset(); 421 } else { 422 SkDEBUGFAIL("Can only call resetStrings on kString_FlagType"); 423 } 424 } 425 append(const char * string)426 void append(const char* string) { 427 if (kString_FlagType == fFlagType) { 428 fStrings->append(string); 429 } else { 430 SkDEBUGFAIL("Can only append to kString_FlagType"); 431 } 432 } 433 setInt(int32_t value)434 void setInt(int32_t value) { 435 if (kInt_FlagType == fFlagType) { 436 *fIntValue = value; 437 } else { 438 SkDEBUGFAIL("Can only call setInt on kInt_FlagType"); 439 } 440 } 441 setUint(uint32_t value)442 void setUint(uint32_t value) { 443 if (kUint_FlagType == fFlagType) { 444 *fUintValue = value; 445 } else { 446 SkDEBUGFAIL("Can only call setUint on kUint_FlagType"); 447 } 448 } 449 setDouble(double value)450 void setDouble(double value) { 451 if (kDouble_FlagType == fFlagType) { 452 *fDoubleValue = value; 453 } else { 454 SkDEBUGFAIL("Can only call setDouble on kDouble_FlagType"); 455 } 456 } 457 setBool(bool value)458 void setBool(bool value) { 459 if (kBool_FlagType == fFlagType) { 460 *fBoolValue = value; 461 } else { 462 SkDEBUGFAIL("Can only call setBool on kBool_FlagType"); 463 } 464 } 465 next()466 SkFlagInfo* next() { return fNext; } 467 name()468 const SkString& name() const { return fName; } 469 shortName()470 const SkString& shortName() const { return fShortName; } 471 help()472 const SkString& help() const { return fHelpString; } extendedHelp()473 const SkString& extendedHelp() const { return fExtendedHelpString; } 474 defaultValue()475 SkString defaultValue() const { 476 SkString result; 477 switch (fFlagType) { 478 case SkFlagInfo::kBool_FlagType: 479 result.printf("%s", fDefaultBool ? "true" : "false"); 480 break; 481 case SkFlagInfo::kString_FlagType: 482 return fDefaultString; 483 case SkFlagInfo::kInt_FlagType: 484 result.printf("%i", fDefaultInt); 485 break; 486 case SkFlagInfo::kUint_FlagType: 487 result.printf("0x%08x", fDefaultUint); 488 break; 489 case SkFlagInfo::kDouble_FlagType: 490 result.printf("%2.2f", fDefaultDouble); 491 break; 492 default: 493 SkDEBUGFAIL("Invalid flag type"); 494 } 495 return result; 496 } 497 typeAsString()498 SkString typeAsString() const { 499 switch (fFlagType) { 500 case SkFlagInfo::kBool_FlagType: 501 return SkString("bool"); 502 case SkFlagInfo::kString_FlagType: 503 return SkString("string"); 504 case SkFlagInfo::kInt_FlagType: 505 return SkString("int"); 506 case SkFlagInfo::kUint_FlagType: 507 return SkString("uint"); 508 case SkFlagInfo::kDouble_FlagType: 509 return SkString("double"); 510 default: 511 SkDEBUGFAIL("Invalid flag type"); 512 return SkString(); 513 } 514 } 515 516 private: SkFlagInfo(const char * name,const char * shortName,FlagTypes type,const char * helpString,const char * extendedHelpString)517 SkFlagInfo(const char* name, const char* shortName, FlagTypes type, const char* helpString, 518 const char* extendedHelpString) 519 : fName(name) 520 , fShortName(shortName) 521 , fFlagType(type) 522 , fHelpString(helpString) 523 , fExtendedHelpString(extendedHelpString) 524 , fBoolValue(nullptr) 525 , fDefaultBool(false) 526 , fIntValue(nullptr) 527 , fDefaultInt(0) 528 , fUintValue(nullptr) 529 , fDefaultUint(0) 530 , fDoubleValue(nullptr) 531 , fDefaultDouble(0) 532 , fStrings(nullptr) { 533 fNext = SkCommandLineFlags::gHead; 534 SkCommandLineFlags::gHead = this; 535 SkASSERT(name && strlen(name) > 1); 536 SkASSERT(nullptr == shortName || 1 == strlen(shortName)); 537 } 538 539 /** 540 * Set a StringArray to hold the values stored in defaultStrings. 541 * @param array The StringArray to modify. 542 * @param defaultStrings Space separated list of strings that should be inserted into array 543 * individually. 544 */ 545 static void SetDefaultStrings(SkCommandLineFlags::StringArray* array, 546 const char* defaultStrings); 547 548 // Name of the flag, without initial dashes 549 SkString fName; 550 SkString fShortName; 551 FlagTypes fFlagType; 552 SkString fHelpString; 553 SkString fExtendedHelpString; 554 bool* fBoolValue; 555 bool fDefaultBool; 556 int32_t* fIntValue; 557 int32_t fDefaultInt; 558 uint32_t* fUintValue; 559 uint32_t fDefaultUint; 560 double* fDoubleValue; 561 double fDefaultDouble; 562 SkCommandLineFlags::StringArray* fStrings; 563 // Both for the help string and in case fStrings is empty. 564 SkString fDefaultString; 565 566 // In order to keep a linked list. 567 SkFlagInfo* fNext; 568 }; 569 #endif // SK_COMMAND_LINE_FLAGS_H 570