SkCommandLineFlags.cpp revision 58104a9c2517b25355d6838844ace2ea876f3a1c
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#include "SkCommandLineFlags.h" 9#include "SkTDArray.h" 10 11bool SkFlagInfo::CreateStringFlag(const char* name, const char* shortName, 12 SkCommandLineFlags::StringArray* pStrings, 13 const char* defaultValue, const char* helpString) { 14 SkFlagInfo* info = SkNEW_ARGS(SkFlagInfo, (name, shortName, kString_FlagType, helpString)); 15 info->fDefaultString.set(defaultValue); 16 17 info->fStrings = pStrings; 18 SetDefaultStrings(pStrings, defaultValue); 19 return true; 20} 21 22void SkFlagInfo::SetDefaultStrings(SkCommandLineFlags::StringArray* pStrings, 23 const char* defaultValue) { 24 pStrings->reset(); 25 // If default is "", leave the array empty. 26 size_t defaultLength = strlen(defaultValue); 27 if (defaultLength > 0) { 28 const char* const defaultEnd = defaultValue + defaultLength; 29 const char* begin = defaultValue; 30 while (true) { 31 while (begin < defaultEnd && ' ' == *begin) { 32 begin++; 33 } 34 if (begin < defaultEnd) { 35 const char* end = begin + 1; 36 while (end < defaultEnd && ' ' != *end) { 37 end++; 38 } 39 size_t length = end - begin; 40 pStrings->append(begin, length); 41 begin = end + 1; 42 } else { 43 break; 44 } 45 } 46 } 47} 48 49static bool string_is_in(const char* target, const char* set[], size_t len) { 50 for (size_t i = 0; i < len; i++) { 51 if (0 == strcmp(target, set[i])) { 52 return true; 53 } 54 } 55 return false; 56} 57 58/** 59 * Check to see whether string represents a boolean value. 60 * @param string C style string to parse. 61 * @param result Pointer to a boolean which will be set to the value in the string, if the 62 * string represents a boolean. 63 * @param boolean True if the string represents a boolean, false otherwise. 64 */ 65static bool parse_bool_arg(const char* string, bool* result) { 66 static const char* trueValues[] = { "1", "TRUE", "true" }; 67 if (string_is_in(string, trueValues, SK_ARRAY_COUNT(trueValues))) { 68 *result = true; 69 return true; 70 } 71 static const char* falseValues[] = { "0", "FALSE", "false" }; 72 if (string_is_in(string, falseValues, SK_ARRAY_COUNT(falseValues))) { 73 *result = false; 74 return true; 75 } 76 SkDebugf("Parameter \"%s\" not supported.\n", string); 77 return false; 78} 79 80bool SkFlagInfo::match(const char* string) { 81 if (SkStrStartsWith(string, '-') && strlen(string) > 1) { 82 string++; 83 const SkString* compareName; 84 if (SkStrStartsWith(string, '-') && strlen(string) > 1) { 85 string++; 86 // There were two dashes. Compare against full name. 87 compareName = &fName; 88 } else { 89 // One dash. Compare against the short name. 90 compareName = &fShortName; 91 } 92 if (kBool_FlagType == fFlagType) { 93 // In this case, go ahead and set the value. 94 if (compareName->equals(string)) { 95 *fBoolValue = true; 96 return true; 97 } 98 if (SkStrStartsWith(string, "no") && strlen(string) > 2) { 99 string += 2; 100 // Only allow "no" to be prepended to the full name. 101 if (fName.equals(string)) { 102 *fBoolValue = false; 103 return true; 104 } 105 return false; 106 } 107 int equalIndex = SkStrFind(string, "="); 108 if (equalIndex > 0) { 109 // The string has an equal sign. Check to see if the string matches. 110 SkString flag(string, equalIndex); 111 if (flag.equals(*compareName)) { 112 // Check to see if the remainder beyond the equal sign is true or false: 113 string += equalIndex + 1; 114 parse_bool_arg(string, fBoolValue); 115 return true; 116 } else { 117 return false; 118 } 119 } 120 } 121 return compareName->equals(string); 122 } else { 123 // Has no dash 124 return false; 125 } 126 return false; 127} 128 129SkFlagInfo* SkCommandLineFlags::gHead; 130SkString SkCommandLineFlags::gUsage; 131 132void SkCommandLineFlags::SetUsage(const char* usage) { 133 gUsage.set(usage); 134} 135 136// Maximum line length for the help message. 137#define LINE_LENGTH 80 138 139static void print_help_for_flag(const SkFlagInfo* flag) { 140 SkDebugf("\t--%s", flag->name().c_str()); 141 const SkString& shortName = flag->shortName(); 142 if (shortName.size() > 0) { 143 SkDebugf(" or -%s", shortName.c_str()); 144 } 145 SkDebugf(":\ttype: %s", flag->typeAsString().c_str()); 146 if (flag->defaultValue().size() > 0) { 147 SkDebugf("\tdefault: %s", flag->defaultValue().c_str()); 148 } 149 SkDebugf("\n"); 150 const SkString& help = flag->help(); 151 size_t length = help.size(); 152 const char* currLine = help.c_str(); 153 const char* stop = currLine + length; 154 while (currLine < stop) { 155 if (strlen(currLine) < LINE_LENGTH) { 156 // Only one line length's worth of text left. 157 SkDebugf("\t\t%s\n", currLine); 158 break; 159 } 160 int lineBreak = SkStrFind(currLine, "\n"); 161 if (lineBreak < 0 || lineBreak > LINE_LENGTH) { 162 // No line break within line length. Will need to insert one. 163 // Find a space before the line break. 164 int spaceIndex = LINE_LENGTH - 1; 165 while (spaceIndex > 0 && currLine[spaceIndex] != ' ') { 166 spaceIndex--; 167 } 168 int gap; 169 if (0 == spaceIndex) { 170 // No spaces on the entire line. Go ahead and break mid word. 171 spaceIndex = LINE_LENGTH; 172 gap = 0; 173 } else { 174 // Skip the space on the next line 175 gap = 1; 176 } 177 SkDebugf("\t\t%.*s\n", spaceIndex, currLine); 178 currLine += spaceIndex + gap; 179 } else { 180 // the line break is within the limit. Break there. 181 lineBreak++; 182 SkDebugf("\t\t%.*s", lineBreak, currLine); 183 currLine += lineBreak; 184 } 185 } 186 SkDebugf("\n"); 187} 188 189void SkCommandLineFlags::Parse(int argc, char** argv) { 190 // Only allow calling this function once. 191 static bool gOnce; 192 if (gOnce) { 193 SkDebugf("Parse should only be called once at the beginning of main!\n"); 194 SkASSERT(false); 195 return; 196 } 197 gOnce = true; 198 199 bool helpPrinted = false; 200 // Loop over argv, starting with 1, since the first is just the name of the program. 201 for (int i = 1; i < argc; i++) { 202 if (0 == strcmp("-h", argv[i]) || 0 == strcmp("--help", argv[i])) { 203 // Print help message. 204 SkTDArray<const char*> helpFlags; 205 for (int j = i + 1; j < argc; j++) { 206 if (SkStrStartsWith(argv[j], '-')) { 207 break; 208 } 209 helpFlags.append(1, &argv[j]); 210 } 211 if (0 == helpFlags.count()) { 212 // Only print general help message if help for specific flags is not requested. 213 SkDebugf("%s\n%s\n", argv[0], gUsage.c_str()); 214 } 215 SkDebugf("Flags:\n"); 216 SkFlagInfo* flag = SkCommandLineFlags::gHead; 217 while (flag != NULL) { 218 // If no flags followed --help, print them all 219 bool printFlag = 0 == helpFlags.count(); 220 if (!printFlag) { 221 for (int k = 0; k < helpFlags.count(); k++) { 222 if (flag->name().equals(helpFlags[k]) || 223 flag->shortName().equals(helpFlags[k])) { 224 printFlag = true; 225 helpFlags.remove(k); 226 break; 227 } 228 } 229 } 230 if (printFlag) { 231 print_help_for_flag(flag); 232 } 233 flag = flag->next(); 234 } 235 if (helpFlags.count() > 0) { 236 SkDebugf("Requested help for unrecognized flags:\n"); 237 for (int k = 0; k < helpFlags.count(); k++) { 238 SkDebugf("\t--%s\n", helpFlags[k]); 239 } 240 } 241 helpPrinted = true; 242 } 243 if (!helpPrinted) { 244 bool flagMatched = false; 245 SkFlagInfo* flag = gHead; 246 while (flag != NULL) { 247 if (flag->match(argv[i])) { 248 flagMatched = true; 249 switch (flag->getFlagType()) { 250 case SkFlagInfo::kBool_FlagType: 251 // Can be handled by match, above, but can also be set by the next 252 // string. 253 if (i+1 < argc && !SkStrStartsWith(argv[i+1], '-')) { 254 i++; 255 bool value; 256 if (parse_bool_arg(argv[i], &value)) { 257 flag->setBool(value); 258 } 259 } 260 break; 261 case SkFlagInfo::kString_FlagType: 262 flag->resetStrings(); 263 // Add all arguments until another flag is reached. 264 while (i+1 < argc && !SkStrStartsWith(argv[i+1], '-')) { 265 i++; 266 flag->append(argv[i]); 267 } 268 break; 269 case SkFlagInfo::kInt_FlagType: 270 i++; 271 flag->setInt(atoi(argv[i])); 272 break; 273 case SkFlagInfo::kDouble_FlagType: 274 i++; 275 flag->setDouble(atof(argv[i])); 276 break; 277 default: 278 SkASSERT(!"Invalid flag type"); 279 } 280 break; 281 } 282 flag = flag->next(); 283 } 284 if (!flagMatched) { 285 SkDebugf("Got unknown flag \"%s\". Exiting.\n", argv[i]); 286 exit(-1); 287 } 288 } 289 } 290 // Since all of the flags have been set, release the memory used by each 291 // flag. FLAGS_x can still be used after this. 292 SkFlagInfo* flag = gHead; 293 gHead = NULL; 294 while (flag != NULL) { 295 SkFlagInfo* next = flag->next(); 296 SkDELETE(flag); 297 flag = next; 298 } 299 if (helpPrinted) { 300 exit(0); 301 } 302} 303