blob: 6f1491bf1cb00dcd8bd506c1fec4458dc781860b [file] [log] [blame]
scroggo@google.com161e1ba2013-03-04 16:41:06 +00001/*
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
scroggo@google.comd9ba9a02013-03-21 19:43:15 +00008#include "SkCommandLineFlags.h"
scroggo@google.comb7dbf632013-04-23 15:38:09 +00009#include "SkTDArray.h"
halcanary03758b82015-01-18 10:39:25 -080010#include "SkTSort.h"
scroggo@google.com161e1ba2013-03-04 16:41:06 +000011
mtklein77a83962014-06-20 08:24:56 -070012DEFINE_bool(undefok, false, "Silently ignore unknown flags instead of crashing.");
commit-bot@chromium.orgffc224f2014-03-25 21:00:02 +000013
scroggo@google.com58104a92013-04-24 19:25:26 +000014bool SkFlagInfo::CreateStringFlag(const char* name, const char* shortName,
15 SkCommandLineFlags::StringArray* pStrings,
16 const char* defaultValue, const char* helpString) {
17 SkFlagInfo* info = SkNEW_ARGS(SkFlagInfo, (name, shortName, kString_FlagType, helpString));
18 info->fDefaultString.set(defaultValue);
19
20 info->fStrings = pStrings;
21 SetDefaultStrings(pStrings, defaultValue);
22 return true;
23}
24
25void SkFlagInfo::SetDefaultStrings(SkCommandLineFlags::StringArray* pStrings,
26 const char* defaultValue) {
27 pStrings->reset();
scroggo@google.comd0419012013-04-24 19:37:52 +000028 if (NULL == defaultValue) {
29 return;
30 }
scroggo@google.com58104a92013-04-24 19:25:26 +000031 // If default is "", leave the array empty.
32 size_t defaultLength = strlen(defaultValue);
33 if (defaultLength > 0) {
34 const char* const defaultEnd = defaultValue + defaultLength;
35 const char* begin = defaultValue;
36 while (true) {
37 while (begin < defaultEnd && ' ' == *begin) {
38 begin++;
39 }
40 if (begin < defaultEnd) {
41 const char* end = begin + 1;
42 while (end < defaultEnd && ' ' != *end) {
43 end++;
44 }
45 size_t length = end - begin;
46 pStrings->append(begin, length);
47 begin = end + 1;
48 } else {
49 break;
50 }
51 }
52 }
53}
54
scroggo@google.com5dc4ca12013-03-21 13:10:59 +000055static bool string_is_in(const char* target, const char* set[], size_t len) {
56 for (size_t i = 0; i < len; i++) {
57 if (0 == strcmp(target, set[i])) {
58 return true;
59 }
60 }
61 return false;
62}
63
64/**
65 * Check to see whether string represents a boolean value.
66 * @param string C style string to parse.
67 * @param result Pointer to a boolean which will be set to the value in the string, if the
68 * string represents a boolean.
69 * @param boolean True if the string represents a boolean, false otherwise.
70 */
71static bool parse_bool_arg(const char* string, bool* result) {
72 static const char* trueValues[] = { "1", "TRUE", "true" };
73 if (string_is_in(string, trueValues, SK_ARRAY_COUNT(trueValues))) {
74 *result = true;
75 return true;
76 }
77 static const char* falseValues[] = { "0", "FALSE", "false" };
78 if (string_is_in(string, falseValues, SK_ARRAY_COUNT(falseValues))) {
79 *result = false;
80 return true;
81 }
82 SkDebugf("Parameter \"%s\" not supported.\n", string);
83 return false;
84}
85
86bool SkFlagInfo::match(const char* string) {
87 if (SkStrStartsWith(string, '-') && strlen(string) > 1) {
88 string++;
scroggo@google.com604e0c22013-04-09 21:25:46 +000089 const SkString* compareName;
scroggo@google.com5dc4ca12013-03-21 13:10:59 +000090 if (SkStrStartsWith(string, '-') && strlen(string) > 1) {
91 string++;
scroggo@google.com604e0c22013-04-09 21:25:46 +000092 // There were two dashes. Compare against full name.
93 compareName = &fName;
94 } else {
95 // One dash. Compare against the short name.
96 compareName = &fShortName;
scroggo@google.com5dc4ca12013-03-21 13:10:59 +000097 }
98 if (kBool_FlagType == fFlagType) {
99 // In this case, go ahead and set the value.
scroggo@google.com604e0c22013-04-09 21:25:46 +0000100 if (compareName->equals(string)) {
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000101 *fBoolValue = true;
102 return true;
103 }
104 if (SkStrStartsWith(string, "no") && strlen(string) > 2) {
105 string += 2;
scroggo@google.com604e0c22013-04-09 21:25:46 +0000106 // Only allow "no" to be prepended to the full name.
107 if (fName.equals(string)) {
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000108 *fBoolValue = false;
109 return true;
110 }
111 return false;
112 }
113 int equalIndex = SkStrFind(string, "=");
114 if (equalIndex > 0) {
115 // The string has an equal sign. Check to see if the string matches.
116 SkString flag(string, equalIndex);
scroggo@google.com604e0c22013-04-09 21:25:46 +0000117 if (flag.equals(*compareName)) {
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000118 // Check to see if the remainder beyond the equal sign is true or false:
119 string += equalIndex + 1;
120 parse_bool_arg(string, fBoolValue);
121 return true;
scroggo@google.com604e0c22013-04-09 21:25:46 +0000122 } else {
123 return false;
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000124 }
125 }
126 }
scroggo@google.com604e0c22013-04-09 21:25:46 +0000127 return compareName->equals(string);
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000128 } else {
129 // Has no dash
130 return false;
131 }
132 return false;
133}
134
scroggo@google.comd9ba9a02013-03-21 19:43:15 +0000135SkFlagInfo* SkCommandLineFlags::gHead;
136SkString SkCommandLineFlags::gUsage;
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000137
scroggo@google.comd9ba9a02013-03-21 19:43:15 +0000138void SkCommandLineFlags::SetUsage(const char* usage) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000139 gUsage.set(usage);
140}
141
142// Maximum line length for the help message.
143#define LINE_LENGTH 80
144
scroggo@google.com8366df02013-03-20 19:50:41 +0000145static void print_help_for_flag(const SkFlagInfo* flag) {
146 SkDebugf("\t--%s", flag->name().c_str());
147 const SkString& shortName = flag->shortName();
148 if (shortName.size() > 0) {
149 SkDebugf(" or -%s", shortName.c_str());
150 }
151 SkDebugf(":\ttype: %s", flag->typeAsString().c_str());
152 if (flag->defaultValue().size() > 0) {
153 SkDebugf("\tdefault: %s", flag->defaultValue().c_str());
154 }
155 SkDebugf("\n");
156 const SkString& help = flag->help();
157 size_t length = help.size();
158 const char* currLine = help.c_str();
159 const char* stop = currLine + length;
160 while (currLine < stop) {
161 if (strlen(currLine) < LINE_LENGTH) {
162 // Only one line length's worth of text left.
163 SkDebugf("\t\t%s\n", currLine);
164 break;
165 }
166 int lineBreak = SkStrFind(currLine, "\n");
167 if (lineBreak < 0 || lineBreak > LINE_LENGTH) {
168 // No line break within line length. Will need to insert one.
169 // Find a space before the line break.
170 int spaceIndex = LINE_LENGTH - 1;
171 while (spaceIndex > 0 && currLine[spaceIndex] != ' ') {
172 spaceIndex--;
173 }
174 int gap;
175 if (0 == spaceIndex) {
176 // No spaces on the entire line. Go ahead and break mid word.
177 spaceIndex = LINE_LENGTH;
178 gap = 0;
179 } else {
180 // Skip the space on the next line
181 gap = 1;
182 }
183 SkDebugf("\t\t%.*s\n", spaceIndex, currLine);
184 currLine += spaceIndex + gap;
185 } else {
186 // the line break is within the limit. Break there.
187 lineBreak++;
188 SkDebugf("\t\t%.*s", lineBreak, currLine);
189 currLine += lineBreak;
190 }
191 }
192 SkDebugf("\n");
193}
194
halcanary03758b82015-01-18 10:39:25 -0800195namespace {
196struct CompareFlagsByName {
197 bool operator()(SkFlagInfo* a, SkFlagInfo* b) const {
198 return strcmp(a->name().c_str(), b->name().c_str()) < 0;
199 }
200};
201} // namespace
202
scroggo@google.comd9ba9a02013-03-21 19:43:15 +0000203void SkCommandLineFlags::Parse(int argc, char** argv) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000204 // Only allow calling this function once.
205 static bool gOnce;
206 if (gOnce) {
scroggo@google.com604e0c22013-04-09 21:25:46 +0000207 SkDebugf("Parse should only be called once at the beginning of main!\n");
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000208 SkASSERT(false);
209 return;
210 }
211 gOnce = true;
212
213 bool helpPrinted = false;
214 // Loop over argv, starting with 1, since the first is just the name of the program.
215 for (int i = 1; i < argc; i++) {
scroggo@google.com604e0c22013-04-09 21:25:46 +0000216 if (0 == strcmp("-h", argv[i]) || 0 == strcmp("--help", argv[i])) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000217 // Print help message.
scroggo@google.com8366df02013-03-20 19:50:41 +0000218 SkTDArray<const char*> helpFlags;
219 for (int j = i + 1; j < argc; j++) {
220 if (SkStrStartsWith(argv[j], '-')) {
221 break;
222 }
223 helpFlags.append(1, &argv[j]);
224 }
225 if (0 == helpFlags.count()) {
226 // Only print general help message if help for specific flags is not requested.
227 SkDebugf("%s\n%s\n", argv[0], gUsage.c_str());
228 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000229 SkDebugf("Flags:\n");
halcanary03758b82015-01-18 10:39:25 -0800230
231 if (0 == helpFlags.count()) {
scroggo@google.com8366df02013-03-20 19:50:41 +0000232 // If no flags followed --help, print them all
halcanary03758b82015-01-18 10:39:25 -0800233 SkTDArray<SkFlagInfo*> allFlags;
234 for (SkFlagInfo* flag = SkCommandLineFlags::gHead; flag;
235 flag = flag->next()) {
236 allFlags.push(flag);
237 }
238 SkTQSort(&allFlags[0], &allFlags[allFlags.count() - 1],
239 CompareFlagsByName());
240 for (int i = 0; i < allFlags.count(); ++i) {
241 print_help_for_flag(allFlags[i]);
242 }
243 } else {
244 for (SkFlagInfo* flag = SkCommandLineFlags::gHead; flag;
245 flag = flag->next()) {
scroggo@google.com8366df02013-03-20 19:50:41 +0000246 for (int k = 0; k < helpFlags.count(); k++) {
247 if (flag->name().equals(helpFlags[k]) ||
248 flag->shortName().equals(helpFlags[k])) {
halcanary03758b82015-01-18 10:39:25 -0800249 print_help_for_flag(flag);
scroggo@google.com8366df02013-03-20 19:50:41 +0000250 helpFlags.remove(k);
251 break;
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000252 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000253 }
254 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000255 }
scroggo@google.com8366df02013-03-20 19:50:41 +0000256 if (helpFlags.count() > 0) {
257 SkDebugf("Requested help for unrecognized flags:\n");
258 for (int k = 0; k < helpFlags.count(); k++) {
259 SkDebugf("\t--%s\n", helpFlags[k]);
260 }
261 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000262 helpPrinted = true;
263 }
264 if (!helpPrinted) {
265 bool flagMatched = false;
266 SkFlagInfo* flag = gHead;
267 while (flag != NULL) {
268 if (flag->match(argv[i])) {
269 flagMatched = true;
270 switch (flag->getFlagType()) {
271 case SkFlagInfo::kBool_FlagType:
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000272 // Can be handled by match, above, but can also be set by the next
273 // string.
274 if (i+1 < argc && !SkStrStartsWith(argv[i+1], '-')) {
275 i++;
276 bool value;
277 if (parse_bool_arg(argv[i], &value)) {
278 flag->setBool(value);
279 }
280 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000281 break;
282 case SkFlagInfo::kString_FlagType:
283 flag->resetStrings();
284 // Add all arguments until another flag is reached.
mtkleindfb7da32015-02-14 18:56:31 -0800285 while (i+1 < argc) {
286 char* end = NULL;
287 (void)strtod(argv[i+1], &end); // Negative numbers aren't flags.
288 if (end == argv[i+1] && SkStrStartsWith(argv[i+1], '-')) {
289 break;
290 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000291 i++;
292 flag->append(argv[i]);
293 }
294 break;
295 case SkFlagInfo::kInt_FlagType:
296 i++;
297 flag->setInt(atoi(argv[i]));
298 break;
299 case SkFlagInfo::kDouble_FlagType:
300 i++;
301 flag->setDouble(atof(argv[i]));
302 break;
303 default:
mtklein@google.com330313a2013-08-22 15:37:26 +0000304 SkDEBUGFAIL("Invalid flag type");
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000305 }
306 break;
307 }
308 flag = flag->next();
309 }
310 if (!flagMatched) {
caryclarkc8fcafb2015-01-30 12:37:02 -0800311#if SK_BUILD_FOR_MAC
312 if (SkStrStartsWith(argv[i], "NSDocumentRevisions")) {
313 i++; // skip YES
314 } else
315#endif
mtklein77a83962014-06-20 08:24:56 -0700316 if (!FLAGS_undefok) {
commit-bot@chromium.orgffc224f2014-03-25 21:00:02 +0000317 SkDebugf("Got unknown flag \"%s\". Exiting.\n", argv[i]);
318 exit(-1);
319 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000320 }
321 }
322 }
323 // Since all of the flags have been set, release the memory used by each
324 // flag. FLAGS_x can still be used after this.
325 SkFlagInfo* flag = gHead;
326 gHead = NULL;
327 while (flag != NULL) {
328 SkFlagInfo* next = flag->next();
329 SkDELETE(flag);
330 flag = next;
331 }
332 if (helpPrinted) {
333 exit(0);
334 }
335}
sglez@google.com586db932013-07-24 17:24:23 +0000336
commit-bot@chromium.orga6f37e72013-08-30 15:52:46 +0000337namespace {
338
339template <typename Strings>
340bool ShouldSkipImpl(const Strings& strings, const char* name) {
sglez@google.com586db932013-07-24 17:24:23 +0000341 int count = strings.count();
342 size_t testLen = strlen(name);
343 bool anyExclude = count == 0;
344 for (int i = 0; i < strings.count(); ++i) {
345 const char* matchName = strings[i];
346 size_t matchLen = strlen(matchName);
347 bool matchExclude, matchStart, matchEnd;
348 if ((matchExclude = matchName[0] == '~')) {
349 anyExclude = true;
350 matchName++;
351 matchLen--;
352 }
353 if ((matchStart = matchName[0] == '^')) {
354 matchName++;
355 matchLen--;
356 }
357 if ((matchEnd = matchName[matchLen - 1] == '$')) {
358 matchLen--;
359 }
360 if (matchStart ? (!matchEnd || matchLen == testLen)
361 && strncmp(name, matchName, matchLen) == 0
362 : matchEnd ? matchLen <= testLen
363 && strncmp(name + testLen - matchLen, matchName, matchLen) == 0
364 : strstr(name, matchName) != 0) {
365 return matchExclude;
366 }
367 }
368 return !anyExclude;
369}
commit-bot@chromium.orga6f37e72013-08-30 15:52:46 +0000370
371} // namespace
372
373bool SkCommandLineFlags::ShouldSkip(const SkTDArray<const char*>& strings, const char* name) {
374 return ShouldSkipImpl(strings, name);
375}
376bool SkCommandLineFlags::ShouldSkip(const StringArray& strings, const char* name) {
377 return ShouldSkipImpl(strings, name);
378}