blob: 3c57d00f0d336c01fa16266caebe352368f44f47 [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
mtklein19e259b2015-05-04 10:54:48 -070014template <typename T> static void ignore_result(const T&) {}
15
scroggo@google.com58104a92013-04-24 19:25:26 +000016bool SkFlagInfo::CreateStringFlag(const char* name, const char* shortName,
17 SkCommandLineFlags::StringArray* pStrings,
18 const char* defaultValue, const char* helpString) {
19 SkFlagInfo* info = SkNEW_ARGS(SkFlagInfo, (name, shortName, kString_FlagType, helpString));
20 info->fDefaultString.set(defaultValue);
21
22 info->fStrings = pStrings;
23 SetDefaultStrings(pStrings, defaultValue);
24 return true;
25}
26
27void SkFlagInfo::SetDefaultStrings(SkCommandLineFlags::StringArray* pStrings,
28 const char* defaultValue) {
29 pStrings->reset();
scroggo@google.comd0419012013-04-24 19:37:52 +000030 if (NULL == defaultValue) {
31 return;
32 }
scroggo@google.com58104a92013-04-24 19:25:26 +000033 // If default is "", leave the array empty.
34 size_t defaultLength = strlen(defaultValue);
35 if (defaultLength > 0) {
36 const char* const defaultEnd = defaultValue + defaultLength;
37 const char* begin = defaultValue;
38 while (true) {
39 while (begin < defaultEnd && ' ' == *begin) {
40 begin++;
41 }
42 if (begin < defaultEnd) {
43 const char* end = begin + 1;
44 while (end < defaultEnd && ' ' != *end) {
45 end++;
46 }
47 size_t length = end - begin;
48 pStrings->append(begin, length);
49 begin = end + 1;
50 } else {
51 break;
52 }
53 }
54 }
55}
56
scroggo@google.com5dc4ca12013-03-21 13:10:59 +000057static bool string_is_in(const char* target, const char* set[], size_t len) {
58 for (size_t i = 0; i < len; i++) {
59 if (0 == strcmp(target, set[i])) {
60 return true;
61 }
62 }
63 return false;
64}
65
66/**
67 * Check to see whether string represents a boolean value.
68 * @param string C style string to parse.
69 * @param result Pointer to a boolean which will be set to the value in the string, if the
70 * string represents a boolean.
71 * @param boolean True if the string represents a boolean, false otherwise.
72 */
73static bool parse_bool_arg(const char* string, bool* result) {
74 static const char* trueValues[] = { "1", "TRUE", "true" };
75 if (string_is_in(string, trueValues, SK_ARRAY_COUNT(trueValues))) {
76 *result = true;
77 return true;
78 }
79 static const char* falseValues[] = { "0", "FALSE", "false" };
80 if (string_is_in(string, falseValues, SK_ARRAY_COUNT(falseValues))) {
81 *result = false;
82 return true;
83 }
84 SkDebugf("Parameter \"%s\" not supported.\n", string);
85 return false;
86}
87
88bool SkFlagInfo::match(const char* string) {
89 if (SkStrStartsWith(string, '-') && strlen(string) > 1) {
90 string++;
scroggo@google.com604e0c22013-04-09 21:25:46 +000091 const SkString* compareName;
scroggo@google.com5dc4ca12013-03-21 13:10:59 +000092 if (SkStrStartsWith(string, '-') && strlen(string) > 1) {
93 string++;
scroggo@google.com604e0c22013-04-09 21:25:46 +000094 // There were two dashes. Compare against full name.
95 compareName = &fName;
96 } else {
97 // One dash. Compare against the short name.
98 compareName = &fShortName;
scroggo@google.com5dc4ca12013-03-21 13:10:59 +000099 }
100 if (kBool_FlagType == fFlagType) {
101 // In this case, go ahead and set the value.
scroggo@google.com604e0c22013-04-09 21:25:46 +0000102 if (compareName->equals(string)) {
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000103 *fBoolValue = true;
104 return true;
105 }
106 if (SkStrStartsWith(string, "no") && strlen(string) > 2) {
107 string += 2;
scroggo@google.com604e0c22013-04-09 21:25:46 +0000108 // Only allow "no" to be prepended to the full name.
109 if (fName.equals(string)) {
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000110 *fBoolValue = false;
111 return true;
112 }
113 return false;
114 }
115 int equalIndex = SkStrFind(string, "=");
116 if (equalIndex > 0) {
117 // The string has an equal sign. Check to see if the string matches.
118 SkString flag(string, equalIndex);
scroggo@google.com604e0c22013-04-09 21:25:46 +0000119 if (flag.equals(*compareName)) {
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000120 // Check to see if the remainder beyond the equal sign is true or false:
121 string += equalIndex + 1;
122 parse_bool_arg(string, fBoolValue);
123 return true;
scroggo@google.com604e0c22013-04-09 21:25:46 +0000124 } else {
125 return false;
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000126 }
127 }
128 }
scroggo@google.com604e0c22013-04-09 21:25:46 +0000129 return compareName->equals(string);
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000130 } else {
131 // Has no dash
132 return false;
133 }
134 return false;
135}
136
scroggo@google.comd9ba9a02013-03-21 19:43:15 +0000137SkFlagInfo* SkCommandLineFlags::gHead;
138SkString SkCommandLineFlags::gUsage;
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000139
scroggo@google.comd9ba9a02013-03-21 19:43:15 +0000140void SkCommandLineFlags::SetUsage(const char* usage) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000141 gUsage.set(usage);
142}
143
144// Maximum line length for the help message.
halcanary27523142015-04-27 06:41:35 -0700145#define LINE_LENGTH 72
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000146
scroggo@google.com8366df02013-03-20 19:50:41 +0000147static void print_help_for_flag(const SkFlagInfo* flag) {
halcanary27523142015-04-27 06:41:35 -0700148 SkDebugf(" --%s", flag->name().c_str());
scroggo@google.com8366df02013-03-20 19:50:41 +0000149 const SkString& shortName = flag->shortName();
150 if (shortName.size() > 0) {
151 SkDebugf(" or -%s", shortName.c_str());
152 }
153 SkDebugf(":\ttype: %s", flag->typeAsString().c_str());
154 if (flag->defaultValue().size() > 0) {
155 SkDebugf("\tdefault: %s", flag->defaultValue().c_str());
156 }
157 SkDebugf("\n");
158 const SkString& help = flag->help();
159 size_t length = help.size();
160 const char* currLine = help.c_str();
161 const char* stop = currLine + length;
162 while (currLine < stop) {
163 if (strlen(currLine) < LINE_LENGTH) {
164 // Only one line length's worth of text left.
halcanary27523142015-04-27 06:41:35 -0700165 SkDebugf(" %s\n", currLine);
scroggo@google.com8366df02013-03-20 19:50:41 +0000166 break;
167 }
168 int lineBreak = SkStrFind(currLine, "\n");
169 if (lineBreak < 0 || lineBreak > LINE_LENGTH) {
170 // No line break within line length. Will need to insert one.
171 // Find a space before the line break.
172 int spaceIndex = LINE_LENGTH - 1;
173 while (spaceIndex > 0 && currLine[spaceIndex] != ' ') {
174 spaceIndex--;
175 }
176 int gap;
177 if (0 == spaceIndex) {
178 // No spaces on the entire line. Go ahead and break mid word.
179 spaceIndex = LINE_LENGTH;
180 gap = 0;
181 } else {
182 // Skip the space on the next line
183 gap = 1;
184 }
halcanary27523142015-04-27 06:41:35 -0700185 SkDebugf(" %.*s\n", spaceIndex, currLine);
scroggo@google.com8366df02013-03-20 19:50:41 +0000186 currLine += spaceIndex + gap;
187 } else {
188 // the line break is within the limit. Break there.
189 lineBreak++;
halcanary27523142015-04-27 06:41:35 -0700190 SkDebugf(" %.*s", lineBreak, currLine);
scroggo@google.com8366df02013-03-20 19:50:41 +0000191 currLine += lineBreak;
192 }
193 }
194 SkDebugf("\n");
195}
196
halcanary03758b82015-01-18 10:39:25 -0800197namespace {
198struct CompareFlagsByName {
199 bool operator()(SkFlagInfo* a, SkFlagInfo* b) const {
200 return strcmp(a->name().c_str(), b->name().c_str()) < 0;
201 }
202};
203} // namespace
204
scroggo@google.comd9ba9a02013-03-21 19:43:15 +0000205void SkCommandLineFlags::Parse(int argc, char** argv) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000206 // Only allow calling this function once.
207 static bool gOnce;
208 if (gOnce) {
scroggo@google.com604e0c22013-04-09 21:25:46 +0000209 SkDebugf("Parse should only be called once at the beginning of main!\n");
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000210 SkASSERT(false);
211 return;
212 }
213 gOnce = true;
214
215 bool helpPrinted = false;
216 // Loop over argv, starting with 1, since the first is just the name of the program.
217 for (int i = 1; i < argc; i++) {
scroggo@google.com604e0c22013-04-09 21:25:46 +0000218 if (0 == strcmp("-h", argv[i]) || 0 == strcmp("--help", argv[i])) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000219 // Print help message.
scroggo@google.com8366df02013-03-20 19:50:41 +0000220 SkTDArray<const char*> helpFlags;
221 for (int j = i + 1; j < argc; j++) {
222 if (SkStrStartsWith(argv[j], '-')) {
223 break;
224 }
225 helpFlags.append(1, &argv[j]);
226 }
227 if (0 == helpFlags.count()) {
228 // Only print general help message if help for specific flags is not requested.
229 SkDebugf("%s\n%s\n", argv[0], gUsage.c_str());
230 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000231 SkDebugf("Flags:\n");
halcanary03758b82015-01-18 10:39:25 -0800232
233 if (0 == helpFlags.count()) {
scroggo@google.com8366df02013-03-20 19:50:41 +0000234 // If no flags followed --help, print them all
halcanary03758b82015-01-18 10:39:25 -0800235 SkTDArray<SkFlagInfo*> allFlags;
236 for (SkFlagInfo* flag = SkCommandLineFlags::gHead; flag;
237 flag = flag->next()) {
238 allFlags.push(flag);
239 }
240 SkTQSort(&allFlags[0], &allFlags[allFlags.count() - 1],
241 CompareFlagsByName());
242 for (int i = 0; i < allFlags.count(); ++i) {
243 print_help_for_flag(allFlags[i]);
244 }
245 } else {
246 for (SkFlagInfo* flag = SkCommandLineFlags::gHead; flag;
247 flag = flag->next()) {
scroggo@google.com8366df02013-03-20 19:50:41 +0000248 for (int k = 0; k < helpFlags.count(); k++) {
249 if (flag->name().equals(helpFlags[k]) ||
250 flag->shortName().equals(helpFlags[k])) {
halcanary03758b82015-01-18 10:39:25 -0800251 print_help_for_flag(flag);
scroggo@google.com8366df02013-03-20 19:50:41 +0000252 helpFlags.remove(k);
253 break;
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000254 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000255 }
256 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000257 }
scroggo@google.com8366df02013-03-20 19:50:41 +0000258 if (helpFlags.count() > 0) {
259 SkDebugf("Requested help for unrecognized flags:\n");
260 for (int k = 0; k < helpFlags.count(); k++) {
halcanary27523142015-04-27 06:41:35 -0700261 SkDebugf(" --%s\n", helpFlags[k]);
scroggo@google.com8366df02013-03-20 19:50:41 +0000262 }
263 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000264 helpPrinted = true;
265 }
266 if (!helpPrinted) {
267 bool flagMatched = false;
268 SkFlagInfo* flag = gHead;
269 while (flag != NULL) {
270 if (flag->match(argv[i])) {
271 flagMatched = true;
272 switch (flag->getFlagType()) {
273 case SkFlagInfo::kBool_FlagType:
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000274 // Can be handled by match, above, but can also be set by the next
275 // string.
276 if (i+1 < argc && !SkStrStartsWith(argv[i+1], '-')) {
277 i++;
278 bool value;
279 if (parse_bool_arg(argv[i], &value)) {
280 flag->setBool(value);
281 }
282 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000283 break;
284 case SkFlagInfo::kString_FlagType:
285 flag->resetStrings();
286 // Add all arguments until another flag is reached.
mtkleindfb7da32015-02-14 18:56:31 -0800287 while (i+1 < argc) {
288 char* end = NULL;
mtklein19e259b2015-05-04 10:54:48 -0700289 // Negative numbers aren't flags.
290 ignore_result(strtod(argv[i+1], &end));
mtkleindfb7da32015-02-14 18:56:31 -0800291 if (end == argv[i+1] && SkStrStartsWith(argv[i+1], '-')) {
292 break;
293 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000294 i++;
295 flag->append(argv[i]);
296 }
297 break;
298 case SkFlagInfo::kInt_FlagType:
299 i++;
300 flag->setInt(atoi(argv[i]));
301 break;
302 case SkFlagInfo::kDouble_FlagType:
303 i++;
304 flag->setDouble(atof(argv[i]));
305 break;
306 default:
mtklein@google.com330313a2013-08-22 15:37:26 +0000307 SkDEBUGFAIL("Invalid flag type");
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000308 }
309 break;
310 }
311 flag = flag->next();
312 }
313 if (!flagMatched) {
caryclarkc8fcafb2015-01-30 12:37:02 -0800314#if SK_BUILD_FOR_MAC
caryclark83ca6282015-06-10 09:31:09 -0700315 if (SkStrStartsWith(argv[i], "NSDocumentRevisions")
316 || SkStrStartsWith(argv[i], "-NSDocumentRevisions")) {
317 i++; // skip YES
caryclarkc8fcafb2015-01-30 12:37:02 -0800318 } else
319#endif
mtklein929f63f2015-04-08 08:30:38 -0700320 if (FLAGS_undefok) {
321 SkDebugf("FYI: ignoring unknown flag '%s'.\n", argv[i]);
322 } else {
323 SkDebugf("Got unknown flag '%s'. Exiting.\n", argv[i]);
commit-bot@chromium.orgffc224f2014-03-25 21:00:02 +0000324 exit(-1);
325 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000326 }
327 }
328 }
329 // Since all of the flags have been set, release the memory used by each
330 // flag. FLAGS_x can still be used after this.
331 SkFlagInfo* flag = gHead;
332 gHead = NULL;
333 while (flag != NULL) {
334 SkFlagInfo* next = flag->next();
335 SkDELETE(flag);
336 flag = next;
337 }
338 if (helpPrinted) {
339 exit(0);
340 }
341}
sglez@google.com586db932013-07-24 17:24:23 +0000342
commit-bot@chromium.orga6f37e72013-08-30 15:52:46 +0000343namespace {
344
345template <typename Strings>
346bool ShouldSkipImpl(const Strings& strings, const char* name) {
sglez@google.com586db932013-07-24 17:24:23 +0000347 int count = strings.count();
348 size_t testLen = strlen(name);
349 bool anyExclude = count == 0;
350 for (int i = 0; i < strings.count(); ++i) {
351 const char* matchName = strings[i];
352 size_t matchLen = strlen(matchName);
353 bool matchExclude, matchStart, matchEnd;
354 if ((matchExclude = matchName[0] == '~')) {
355 anyExclude = true;
356 matchName++;
357 matchLen--;
358 }
359 if ((matchStart = matchName[0] == '^')) {
360 matchName++;
361 matchLen--;
362 }
363 if ((matchEnd = matchName[matchLen - 1] == '$')) {
364 matchLen--;
365 }
366 if (matchStart ? (!matchEnd || matchLen == testLen)
367 && strncmp(name, matchName, matchLen) == 0
368 : matchEnd ? matchLen <= testLen
369 && strncmp(name + testLen - matchLen, matchName, matchLen) == 0
370 : strstr(name, matchName) != 0) {
371 return matchExclude;
372 }
373 }
374 return !anyExclude;
375}
commit-bot@chromium.orga6f37e72013-08-30 15:52:46 +0000376
377} // namespace
378
379bool SkCommandLineFlags::ShouldSkip(const SkTDArray<const char*>& strings, const char* name) {
380 return ShouldSkipImpl(strings, name);
381}
382bool SkCommandLineFlags::ShouldSkip(const StringArray& strings, const char* name) {
383 return ShouldSkipImpl(strings, name);
384}