blob: 9b2c9616cbf4ebc0a70c4bfc33cc98910053bc4d [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
bungeman60e0fee2015-08-26 05:15:46 -070012#include <stdlib.h>
13
iroth8b99ef42015-11-02 11:11:21 -080014#if defined(GOOGLE3) && (defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_IOS))
15 // I don't know why, but this is defined by //base only for non-Linux.
benjaminwagner86ea33e2015-10-26 10:46:25 -070016 DECLARE_bool(undefok)
17#else
18 DEFINE_bool(undefok, false, "Silently ignore unknown flags instead of crashing.");
19#endif
commit-bot@chromium.orgffc224f2014-03-25 21:00:02 +000020
mtklein19e259b2015-05-04 10:54:48 -070021template <typename T> static void ignore_result(const T&) {}
22
scroggo@google.com58104a92013-04-24 19:25:26 +000023bool SkFlagInfo::CreateStringFlag(const char* name, const char* shortName,
24 SkCommandLineFlags::StringArray* pStrings,
joshualitt4dabf832015-12-18 06:02:18 -080025 const char* defaultValue, const char* helpString) {
26 SkFlagInfo* info = new SkFlagInfo(name, shortName, kString_FlagType, helpString);
scroggo@google.com58104a92013-04-24 19:25:26 +000027 info->fDefaultString.set(defaultValue);
28
29 info->fStrings = pStrings;
30 SetDefaultStrings(pStrings, defaultValue);
31 return true;
32}
33
34void SkFlagInfo::SetDefaultStrings(SkCommandLineFlags::StringArray* pStrings,
35 const char* defaultValue) {
36 pStrings->reset();
halcanary96fcdcc2015-08-27 07:41:13 -070037 if (nullptr == defaultValue) {
scroggo@google.comd0419012013-04-24 19:37:52 +000038 return;
39 }
scroggo@google.com58104a92013-04-24 19:25:26 +000040 // If default is "", leave the array empty.
41 size_t defaultLength = strlen(defaultValue);
42 if (defaultLength > 0) {
43 const char* const defaultEnd = defaultValue + defaultLength;
44 const char* begin = defaultValue;
45 while (true) {
46 while (begin < defaultEnd && ' ' == *begin) {
47 begin++;
48 }
49 if (begin < defaultEnd) {
50 const char* end = begin + 1;
51 while (end < defaultEnd && ' ' != *end) {
52 end++;
53 }
54 size_t length = end - begin;
55 pStrings->append(begin, length);
56 begin = end + 1;
57 } else {
58 break;
59 }
60 }
61 }
62}
63
scroggo@google.com5dc4ca12013-03-21 13:10:59 +000064static bool string_is_in(const char* target, const char* set[], size_t len) {
65 for (size_t i = 0; i < len; i++) {
66 if (0 == strcmp(target, set[i])) {
67 return true;
68 }
69 }
70 return false;
71}
72
73/**
74 * Check to see whether string represents a boolean value.
75 * @param string C style string to parse.
76 * @param result Pointer to a boolean which will be set to the value in the string, if the
77 * string represents a boolean.
78 * @param boolean True if the string represents a boolean, false otherwise.
79 */
80static bool parse_bool_arg(const char* string, bool* result) {
81 static const char* trueValues[] = { "1", "TRUE", "true" };
82 if (string_is_in(string, trueValues, SK_ARRAY_COUNT(trueValues))) {
83 *result = true;
84 return true;
85 }
86 static const char* falseValues[] = { "0", "FALSE", "false" };
87 if (string_is_in(string, falseValues, SK_ARRAY_COUNT(falseValues))) {
88 *result = false;
89 return true;
90 }
91 SkDebugf("Parameter \"%s\" not supported.\n", string);
92 return false;
93}
94
95bool SkFlagInfo::match(const char* string) {
96 if (SkStrStartsWith(string, '-') && strlen(string) > 1) {
97 string++;
scroggo@google.com604e0c22013-04-09 21:25:46 +000098 const SkString* compareName;
scroggo@google.com5dc4ca12013-03-21 13:10:59 +000099 if (SkStrStartsWith(string, '-') && strlen(string) > 1) {
100 string++;
scroggo@google.com604e0c22013-04-09 21:25:46 +0000101 // There were two dashes. Compare against full name.
102 compareName = &fName;
103 } else {
104 // One dash. Compare against the short name.
105 compareName = &fShortName;
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000106 }
107 if (kBool_FlagType == fFlagType) {
108 // In this case, go ahead and set the value.
scroggo@google.com604e0c22013-04-09 21:25:46 +0000109 if (compareName->equals(string)) {
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000110 *fBoolValue = true;
111 return true;
112 }
113 if (SkStrStartsWith(string, "no") && strlen(string) > 2) {
114 string += 2;
scroggo@google.com604e0c22013-04-09 21:25:46 +0000115 // Only allow "no" to be prepended to the full name.
116 if (fName.equals(string)) {
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000117 *fBoolValue = false;
118 return true;
119 }
120 return false;
121 }
122 int equalIndex = SkStrFind(string, "=");
123 if (equalIndex > 0) {
124 // The string has an equal sign. Check to see if the string matches.
125 SkString flag(string, equalIndex);
scroggo@google.com604e0c22013-04-09 21:25:46 +0000126 if (flag.equals(*compareName)) {
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000127 // Check to see if the remainder beyond the equal sign is true or false:
128 string += equalIndex + 1;
129 parse_bool_arg(string, fBoolValue);
130 return true;
scroggo@google.com604e0c22013-04-09 21:25:46 +0000131 } else {
132 return false;
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000133 }
134 }
135 }
scroggo@google.com604e0c22013-04-09 21:25:46 +0000136 return compareName->equals(string);
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000137 } else {
138 // Has no dash
139 return false;
140 }
141 return false;
142}
143
scroggo@google.comd9ba9a02013-03-21 19:43:15 +0000144SkFlagInfo* SkCommandLineFlags::gHead;
145SkString SkCommandLineFlags::gUsage;
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000146
scroggo@google.comd9ba9a02013-03-21 19:43:15 +0000147void SkCommandLineFlags::SetUsage(const char* usage) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000148 gUsage.set(usage);
149}
150
151// Maximum line length for the help message.
halcanary27523142015-04-27 06:41:35 -0700152#define LINE_LENGTH 72
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000153
joshualitt4dabf832015-12-18 06:02:18 -0800154static void print_help_for_flag(const SkFlagInfo* flag) {
155 SkDebugf(" --%s", flag->name().c_str());
156 const SkString& shortName = flag->shortName();
157 if (shortName.size() > 0) {
158 SkDebugf(" or -%s", shortName.c_str());
159 }
160 SkDebugf(":\ttype: %s", flag->typeAsString().c_str());
161 if (flag->defaultValue().size() > 0) {
162 SkDebugf("\tdefault: %s", flag->defaultValue().c_str());
163 }
164 SkDebugf("\n");
165 const SkString& help = flag->help();
166 size_t length = help.size();
167 const char* currLine = help.c_str();
scroggo@google.com8366df02013-03-20 19:50:41 +0000168 const char* stop = currLine + length;
169 while (currLine < stop) {
joshualitt4dabf832015-12-18 06:02:18 -0800170 if (strlen(currLine) < LINE_LENGTH) {
171 // Only one line length's worth of text left.
172 SkDebugf(" %s\n", currLine);
173 break;
kkinnunenc8b43362015-12-18 03:27:32 -0800174 }
joshualitt4dabf832015-12-18 06:02:18 -0800175 int lineBreak = SkStrFind(currLine, "\n");
176 if (lineBreak < 0 || lineBreak > LINE_LENGTH) {
scroggo@google.com8366df02013-03-20 19:50:41 +0000177 // No line break within line length. Will need to insert one.
178 // Find a space before the line break.
179 int spaceIndex = LINE_LENGTH - 1;
180 while (spaceIndex > 0 && currLine[spaceIndex] != ' ') {
181 spaceIndex--;
182 }
183 int gap;
184 if (0 == spaceIndex) {
185 // No spaces on the entire line. Go ahead and break mid word.
186 spaceIndex = LINE_LENGTH;
187 gap = 0;
188 } else {
189 // Skip the space on the next line
190 gap = 1;
191 }
halcanary27523142015-04-27 06:41:35 -0700192 SkDebugf(" %.*s\n", spaceIndex, currLine);
scroggo@google.com8366df02013-03-20 19:50:41 +0000193 currLine += spaceIndex + gap;
194 } else {
195 // the line break is within the limit. Break there.
196 lineBreak++;
halcanary27523142015-04-27 06:41:35 -0700197 SkDebugf(" %.*s", lineBreak, currLine);
scroggo@google.com8366df02013-03-20 19:50:41 +0000198 currLine += lineBreak;
199 }
200 }
201 SkDebugf("\n");
202}
203
halcanary03758b82015-01-18 10:39:25 -0800204namespace {
205struct CompareFlagsByName {
206 bool operator()(SkFlagInfo* a, SkFlagInfo* b) const {
207 return strcmp(a->name().c_str(), b->name().c_str()) < 0;
208 }
209};
210} // namespace
211
scroggo@google.comd9ba9a02013-03-21 19:43:15 +0000212void SkCommandLineFlags::Parse(int argc, char** argv) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000213 // Only allow calling this function once.
214 static bool gOnce;
215 if (gOnce) {
scroggo@google.com604e0c22013-04-09 21:25:46 +0000216 SkDebugf("Parse should only be called once at the beginning of main!\n");
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000217 SkASSERT(false);
218 return;
219 }
220 gOnce = true;
221
222 bool helpPrinted = false;
223 // Loop over argv, starting with 1, since the first is just the name of the program.
224 for (int i = 1; i < argc; i++) {
scroggo@google.com604e0c22013-04-09 21:25:46 +0000225 if (0 == strcmp("-h", argv[i]) || 0 == strcmp("--help", argv[i])) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000226 // Print help message.
scroggo@google.com8366df02013-03-20 19:50:41 +0000227 SkTDArray<const char*> helpFlags;
228 for (int j = i + 1; j < argc; j++) {
229 if (SkStrStartsWith(argv[j], '-')) {
230 break;
231 }
232 helpFlags.append(1, &argv[j]);
233 }
234 if (0 == helpFlags.count()) {
235 // Only print general help message if help for specific flags is not requested.
236 SkDebugf("%s\n%s\n", argv[0], gUsage.c_str());
237 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000238 SkDebugf("Flags:\n");
halcanary03758b82015-01-18 10:39:25 -0800239
240 if (0 == helpFlags.count()) {
scroggo@google.com8366df02013-03-20 19:50:41 +0000241 // If no flags followed --help, print them all
halcanary03758b82015-01-18 10:39:25 -0800242 SkTDArray<SkFlagInfo*> allFlags;
243 for (SkFlagInfo* flag = SkCommandLineFlags::gHead; flag;
244 flag = flag->next()) {
245 allFlags.push(flag);
246 }
247 SkTQSort(&allFlags[0], &allFlags[allFlags.count() - 1],
248 CompareFlagsByName());
249 for (int i = 0; i < allFlags.count(); ++i) {
250 print_help_for_flag(allFlags[i]);
251 }
252 } else {
253 for (SkFlagInfo* flag = SkCommandLineFlags::gHead; flag;
254 flag = flag->next()) {
scroggo@google.com8366df02013-03-20 19:50:41 +0000255 for (int k = 0; k < helpFlags.count(); k++) {
256 if (flag->name().equals(helpFlags[k]) ||
257 flag->shortName().equals(helpFlags[k])) {
joshualitt4dabf832015-12-18 06:02:18 -0800258 print_help_for_flag(flag);
scroggo@google.com8366df02013-03-20 19:50:41 +0000259 helpFlags.remove(k);
260 break;
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000261 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000262 }
263 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000264 }
scroggo@google.com8366df02013-03-20 19:50:41 +0000265 if (helpFlags.count() > 0) {
266 SkDebugf("Requested help for unrecognized flags:\n");
267 for (int k = 0; k < helpFlags.count(); k++) {
halcanary27523142015-04-27 06:41:35 -0700268 SkDebugf(" --%s\n", helpFlags[k]);
scroggo@google.com8366df02013-03-20 19:50:41 +0000269 }
270 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000271 helpPrinted = true;
272 }
273 if (!helpPrinted) {
274 bool flagMatched = false;
275 SkFlagInfo* flag = gHead;
halcanary96fcdcc2015-08-27 07:41:13 -0700276 while (flag != nullptr) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000277 if (flag->match(argv[i])) {
278 flagMatched = true;
279 switch (flag->getFlagType()) {
280 case SkFlagInfo::kBool_FlagType:
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000281 // Can be handled by match, above, but can also be set by the next
282 // string.
283 if (i+1 < argc && !SkStrStartsWith(argv[i+1], '-')) {
284 i++;
285 bool value;
286 if (parse_bool_arg(argv[i], &value)) {
287 flag->setBool(value);
288 }
289 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000290 break;
291 case SkFlagInfo::kString_FlagType:
292 flag->resetStrings();
293 // Add all arguments until another flag is reached.
mtkleindfb7da32015-02-14 18:56:31 -0800294 while (i+1 < argc) {
halcanary96fcdcc2015-08-27 07:41:13 -0700295 char* end = nullptr;
mtklein19e259b2015-05-04 10:54:48 -0700296 // Negative numbers aren't flags.
297 ignore_result(strtod(argv[i+1], &end));
mtkleindfb7da32015-02-14 18:56:31 -0800298 if (end == argv[i+1] && SkStrStartsWith(argv[i+1], '-')) {
299 break;
300 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000301 i++;
302 flag->append(argv[i]);
303 }
304 break;
305 case SkFlagInfo::kInt_FlagType:
306 i++;
307 flag->setInt(atoi(argv[i]));
308 break;
309 case SkFlagInfo::kDouble_FlagType:
310 i++;
311 flag->setDouble(atof(argv[i]));
312 break;
313 default:
mtklein@google.com330313a2013-08-22 15:37:26 +0000314 SkDEBUGFAIL("Invalid flag type");
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000315 }
316 break;
317 }
318 flag = flag->next();
319 }
320 if (!flagMatched) {
caryclarkc8fcafb2015-01-30 12:37:02 -0800321#if SK_BUILD_FOR_MAC
caryclark83ca6282015-06-10 09:31:09 -0700322 if (SkStrStartsWith(argv[i], "NSDocumentRevisions")
323 || SkStrStartsWith(argv[i], "-NSDocumentRevisions")) {
324 i++; // skip YES
caryclarkc8fcafb2015-01-30 12:37:02 -0800325 } else
326#endif
mtklein929f63f2015-04-08 08:30:38 -0700327 if (FLAGS_undefok) {
328 SkDebugf("FYI: ignoring unknown flag '%s'.\n", argv[i]);
329 } else {
330 SkDebugf("Got unknown flag '%s'. Exiting.\n", argv[i]);
commit-bot@chromium.orgffc224f2014-03-25 21:00:02 +0000331 exit(-1);
332 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000333 }
334 }
335 }
336 // Since all of the flags have been set, release the memory used by each
337 // flag. FLAGS_x can still be used after this.
338 SkFlagInfo* flag = gHead;
halcanary96fcdcc2015-08-27 07:41:13 -0700339 gHead = nullptr;
340 while (flag != nullptr) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000341 SkFlagInfo* next = flag->next();
halcanary385fe4d2015-08-26 13:07:48 -0700342 delete flag;
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000343 flag = next;
344 }
345 if (helpPrinted) {
346 exit(0);
347 }
348}
sglez@google.com586db932013-07-24 17:24:23 +0000349
commit-bot@chromium.orga6f37e72013-08-30 15:52:46 +0000350namespace {
351
352template <typename Strings>
353bool ShouldSkipImpl(const Strings& strings, const char* name) {
sglez@google.com586db932013-07-24 17:24:23 +0000354 int count = strings.count();
355 size_t testLen = strlen(name);
356 bool anyExclude = count == 0;
357 for (int i = 0; i < strings.count(); ++i) {
358 const char* matchName = strings[i];
359 size_t matchLen = strlen(matchName);
360 bool matchExclude, matchStart, matchEnd;
361 if ((matchExclude = matchName[0] == '~')) {
362 anyExclude = true;
363 matchName++;
364 matchLen--;
365 }
366 if ((matchStart = matchName[0] == '^')) {
367 matchName++;
368 matchLen--;
369 }
370 if ((matchEnd = matchName[matchLen - 1] == '$')) {
371 matchLen--;
372 }
373 if (matchStart ? (!matchEnd || matchLen == testLen)
374 && strncmp(name, matchName, matchLen) == 0
375 : matchEnd ? matchLen <= testLen
376 && strncmp(name + testLen - matchLen, matchName, matchLen) == 0
377 : strstr(name, matchName) != 0) {
378 return matchExclude;
379 }
380 }
381 return !anyExclude;
382}
commit-bot@chromium.orga6f37e72013-08-30 15:52:46 +0000383
384} // namespace
385
386bool SkCommandLineFlags::ShouldSkip(const SkTDArray<const char*>& strings, const char* name) {
387 return ShouldSkipImpl(strings, name);
388}
389bool SkCommandLineFlags::ShouldSkip(const StringArray& strings, const char* name) {
390 return ShouldSkipImpl(strings, name);
391}