blob: cddf9fb0b35cc5907f55740391f32bf774cab561 [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,
kkinnunen3e980c32015-12-23 01:33:00 -080025 const char* defaultValue, const char* helpString,
26 const char* extendedHelpString) {
27 SkFlagInfo* info = new SkFlagInfo(name, shortName, kString_FlagType, helpString,
28 extendedHelpString);
scroggo@google.com58104a92013-04-24 19:25:26 +000029 info->fDefaultString.set(defaultValue);
30
31 info->fStrings = pStrings;
32 SetDefaultStrings(pStrings, defaultValue);
33 return true;
34}
35
36void SkFlagInfo::SetDefaultStrings(SkCommandLineFlags::StringArray* pStrings,
37 const char* defaultValue) {
38 pStrings->reset();
halcanary96fcdcc2015-08-27 07:41:13 -070039 if (nullptr == defaultValue) {
scroggo@google.comd0419012013-04-24 19:37:52 +000040 return;
41 }
scroggo@google.com58104a92013-04-24 19:25:26 +000042 // If default is "", leave the array empty.
43 size_t defaultLength = strlen(defaultValue);
44 if (defaultLength > 0) {
45 const char* const defaultEnd = defaultValue + defaultLength;
46 const char* begin = defaultValue;
47 while (true) {
48 while (begin < defaultEnd && ' ' == *begin) {
49 begin++;
50 }
51 if (begin < defaultEnd) {
52 const char* end = begin + 1;
53 while (end < defaultEnd && ' ' != *end) {
54 end++;
55 }
56 size_t length = end - begin;
57 pStrings->append(begin, length);
58 begin = end + 1;
59 } else {
60 break;
61 }
62 }
63 }
64}
65
scroggo@google.com5dc4ca12013-03-21 13:10:59 +000066static bool string_is_in(const char* target, const char* set[], size_t len) {
67 for (size_t i = 0; i < len; i++) {
68 if (0 == strcmp(target, set[i])) {
69 return true;
70 }
71 }
72 return false;
73}
74
75/**
76 * Check to see whether string represents a boolean value.
77 * @param string C style string to parse.
78 * @param result Pointer to a boolean which will be set to the value in the string, if the
79 * string represents a boolean.
80 * @param boolean True if the string represents a boolean, false otherwise.
81 */
82static bool parse_bool_arg(const char* string, bool* result) {
83 static const char* trueValues[] = { "1", "TRUE", "true" };
84 if (string_is_in(string, trueValues, SK_ARRAY_COUNT(trueValues))) {
85 *result = true;
86 return true;
87 }
88 static const char* falseValues[] = { "0", "FALSE", "false" };
89 if (string_is_in(string, falseValues, SK_ARRAY_COUNT(falseValues))) {
90 *result = false;
91 return true;
92 }
93 SkDebugf("Parameter \"%s\" not supported.\n", string);
94 return false;
95}
96
97bool SkFlagInfo::match(const char* string) {
98 if (SkStrStartsWith(string, '-') && strlen(string) > 1) {
99 string++;
scroggo@google.com604e0c22013-04-09 21:25:46 +0000100 const SkString* compareName;
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000101 if (SkStrStartsWith(string, '-') && strlen(string) > 1) {
102 string++;
scroggo@google.com604e0c22013-04-09 21:25:46 +0000103 // There were two dashes. Compare against full name.
104 compareName = &fName;
105 } else {
106 // One dash. Compare against the short name.
107 compareName = &fShortName;
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000108 }
109 if (kBool_FlagType == fFlagType) {
110 // In this case, go ahead and set the value.
scroggo@google.com604e0c22013-04-09 21:25:46 +0000111 if (compareName->equals(string)) {
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000112 *fBoolValue = true;
113 return true;
114 }
115 if (SkStrStartsWith(string, "no") && strlen(string) > 2) {
116 string += 2;
scroggo@google.com604e0c22013-04-09 21:25:46 +0000117 // Only allow "no" to be prepended to the full name.
118 if (fName.equals(string)) {
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000119 *fBoolValue = false;
120 return true;
121 }
122 return false;
123 }
124 int equalIndex = SkStrFind(string, "=");
125 if (equalIndex > 0) {
126 // The string has an equal sign. Check to see if the string matches.
127 SkString flag(string, equalIndex);
scroggo@google.com604e0c22013-04-09 21:25:46 +0000128 if (flag.equals(*compareName)) {
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000129 // Check to see if the remainder beyond the equal sign is true or false:
130 string += equalIndex + 1;
131 parse_bool_arg(string, fBoolValue);
132 return true;
scroggo@google.com604e0c22013-04-09 21:25:46 +0000133 } else {
134 return false;
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000135 }
136 }
137 }
scroggo@google.com604e0c22013-04-09 21:25:46 +0000138 return compareName->equals(string);
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000139 } else {
140 // Has no dash
141 return false;
142 }
143 return false;
144}
145
scroggo@google.comd9ba9a02013-03-21 19:43:15 +0000146SkFlagInfo* SkCommandLineFlags::gHead;
147SkString SkCommandLineFlags::gUsage;
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000148
scroggo@google.comd9ba9a02013-03-21 19:43:15 +0000149void SkCommandLineFlags::SetUsage(const char* usage) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000150 gUsage.set(usage);
151}
152
153// Maximum line length for the help message.
halcanary27523142015-04-27 06:41:35 -0700154#define LINE_LENGTH 72
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000155
kkinnunen3e980c32015-12-23 01:33:00 -0800156static void print_indented(const SkString& text) {
157 size_t length = text.size();
158 const char* currLine = text.c_str();
scroggo@google.com8366df02013-03-20 19:50:41 +0000159 const char* stop = currLine + length;
160 while (currLine < stop) {
rmistry0f515bd2015-12-22 10:22:26 -0800161 int lineBreak = SkStrFind(currLine, "\n");
kkinnunen3e980c32015-12-23 01:33:00 -0800162 if (lineBreak < 0) {
163 lineBreak = static_cast<int>(strlen(currLine));
164 }
165 if (lineBreak > LINE_LENGTH) {
scroggo@google.com8366df02013-03-20 19:50:41 +0000166 // No line break within line length. Will need to insert one.
167 // Find a space before the line break.
168 int spaceIndex = LINE_LENGTH - 1;
169 while (spaceIndex > 0 && currLine[spaceIndex] != ' ') {
170 spaceIndex--;
171 }
172 int gap;
173 if (0 == spaceIndex) {
174 // No spaces on the entire line. Go ahead and break mid word.
175 spaceIndex = LINE_LENGTH;
176 gap = 0;
177 } else {
178 // Skip the space on the next line
179 gap = 1;
180 }
halcanary27523142015-04-27 06:41:35 -0700181 SkDebugf(" %.*s\n", spaceIndex, currLine);
scroggo@google.com8366df02013-03-20 19:50:41 +0000182 currLine += spaceIndex + gap;
183 } else {
184 // the line break is within the limit. Break there.
185 lineBreak++;
halcanary27523142015-04-27 06:41:35 -0700186 SkDebugf(" %.*s", lineBreak, currLine);
scroggo@google.com8366df02013-03-20 19:50:41 +0000187 currLine += lineBreak;
188 }
189 }
kkinnunen3e980c32015-12-23 01:33:00 -0800190}
191
192static void print_help_for_flag(const SkFlagInfo* flag) {
193 SkDebugf(" --%s", flag->name().c_str());
194 const SkString& shortName = flag->shortName();
195 if (shortName.size() > 0) {
196 SkDebugf(" or -%s", shortName.c_str());
197 }
198 SkDebugf(":\ttype: %s", flag->typeAsString().c_str());
199 if (flag->defaultValue().size() > 0) {
200 SkDebugf("\tdefault: %s", flag->defaultValue().c_str());
201 }
202 SkDebugf("\n");
203 const SkString& help = flag->help();
204 print_indented(help);
205 SkDebugf("\n");
206}
207static void print_extended_help_for_flag(const SkFlagInfo* flag) {
208 print_help_for_flag(flag);
209 print_indented(flag->extendedHelp());
scroggo@google.com8366df02013-03-20 19:50:41 +0000210 SkDebugf("\n");
211}
212
halcanary03758b82015-01-18 10:39:25 -0800213namespace {
214struct CompareFlagsByName {
215 bool operator()(SkFlagInfo* a, SkFlagInfo* b) const {
216 return strcmp(a->name().c_str(), b->name().c_str()) < 0;
217 }
218};
219} // namespace
220
scroggo@google.comd9ba9a02013-03-21 19:43:15 +0000221void SkCommandLineFlags::Parse(int argc, char** argv) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000222 // Only allow calling this function once.
223 static bool gOnce;
224 if (gOnce) {
scroggo@google.com604e0c22013-04-09 21:25:46 +0000225 SkDebugf("Parse should only be called once at the beginning of main!\n");
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000226 SkASSERT(false);
227 return;
228 }
229 gOnce = true;
230
231 bool helpPrinted = false;
232 // Loop over argv, starting with 1, since the first is just the name of the program.
233 for (int i = 1; i < argc; i++) {
scroggo@google.com604e0c22013-04-09 21:25:46 +0000234 if (0 == strcmp("-h", argv[i]) || 0 == strcmp("--help", argv[i])) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000235 // Print help message.
scroggo@google.com8366df02013-03-20 19:50:41 +0000236 SkTDArray<const char*> helpFlags;
237 for (int j = i + 1; j < argc; j++) {
238 if (SkStrStartsWith(argv[j], '-')) {
239 break;
240 }
241 helpFlags.append(1, &argv[j]);
242 }
243 if (0 == helpFlags.count()) {
244 // Only print general help message if help for specific flags is not requested.
245 SkDebugf("%s\n%s\n", argv[0], gUsage.c_str());
246 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000247 SkDebugf("Flags:\n");
halcanary03758b82015-01-18 10:39:25 -0800248
249 if (0 == helpFlags.count()) {
scroggo@google.com8366df02013-03-20 19:50:41 +0000250 // If no flags followed --help, print them all
halcanary03758b82015-01-18 10:39:25 -0800251 SkTDArray<SkFlagInfo*> allFlags;
252 for (SkFlagInfo* flag = SkCommandLineFlags::gHead; flag;
253 flag = flag->next()) {
254 allFlags.push(flag);
255 }
256 SkTQSort(&allFlags[0], &allFlags[allFlags.count() - 1],
257 CompareFlagsByName());
258 for (int i = 0; i < allFlags.count(); ++i) {
259 print_help_for_flag(allFlags[i]);
kkinnunen3e980c32015-12-23 01:33:00 -0800260 if (allFlags[i]->extendedHelp().size() > 0) {
261 SkDebugf(" Use '--help %s' for more information.\n",
262 allFlags[i]->name().c_str());
263 }
halcanary03758b82015-01-18 10:39:25 -0800264 }
265 } else {
266 for (SkFlagInfo* flag = SkCommandLineFlags::gHead; flag;
267 flag = flag->next()) {
scroggo@google.com8366df02013-03-20 19:50:41 +0000268 for (int k = 0; k < helpFlags.count(); k++) {
269 if (flag->name().equals(helpFlags[k]) ||
270 flag->shortName().equals(helpFlags[k])) {
kkinnunen3e980c32015-12-23 01:33:00 -0800271 print_extended_help_for_flag(flag);
scroggo@google.com8366df02013-03-20 19:50:41 +0000272 helpFlags.remove(k);
273 break;
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000274 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000275 }
276 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000277 }
scroggo@google.com8366df02013-03-20 19:50:41 +0000278 if (helpFlags.count() > 0) {
279 SkDebugf("Requested help for unrecognized flags:\n");
280 for (int k = 0; k < helpFlags.count(); k++) {
halcanary27523142015-04-27 06:41:35 -0700281 SkDebugf(" --%s\n", helpFlags[k]);
scroggo@google.com8366df02013-03-20 19:50:41 +0000282 }
283 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000284 helpPrinted = true;
285 }
286 if (!helpPrinted) {
287 bool flagMatched = false;
288 SkFlagInfo* flag = gHead;
halcanary96fcdcc2015-08-27 07:41:13 -0700289 while (flag != nullptr) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000290 if (flag->match(argv[i])) {
291 flagMatched = true;
292 switch (flag->getFlagType()) {
293 case SkFlagInfo::kBool_FlagType:
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000294 // Can be handled by match, above, but can also be set by the next
295 // string.
296 if (i+1 < argc && !SkStrStartsWith(argv[i+1], '-')) {
297 i++;
298 bool value;
299 if (parse_bool_arg(argv[i], &value)) {
300 flag->setBool(value);
301 }
302 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000303 break;
304 case SkFlagInfo::kString_FlagType:
305 flag->resetStrings();
306 // Add all arguments until another flag is reached.
mtkleindfb7da32015-02-14 18:56:31 -0800307 while (i+1 < argc) {
halcanary96fcdcc2015-08-27 07:41:13 -0700308 char* end = nullptr;
mtklein19e259b2015-05-04 10:54:48 -0700309 // Negative numbers aren't flags.
310 ignore_result(strtod(argv[i+1], &end));
mtkleindfb7da32015-02-14 18:56:31 -0800311 if (end == argv[i+1] && SkStrStartsWith(argv[i+1], '-')) {
312 break;
313 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000314 i++;
315 flag->append(argv[i]);
316 }
317 break;
318 case SkFlagInfo::kInt_FlagType:
319 i++;
320 flag->setInt(atoi(argv[i]));
321 break;
322 case SkFlagInfo::kDouble_FlagType:
323 i++;
324 flag->setDouble(atof(argv[i]));
325 break;
326 default:
mtklein@google.com330313a2013-08-22 15:37:26 +0000327 SkDEBUGFAIL("Invalid flag type");
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000328 }
329 break;
330 }
331 flag = flag->next();
332 }
333 if (!flagMatched) {
caryclarkc8fcafb2015-01-30 12:37:02 -0800334#if SK_BUILD_FOR_MAC
caryclark83ca6282015-06-10 09:31:09 -0700335 if (SkStrStartsWith(argv[i], "NSDocumentRevisions")
336 || SkStrStartsWith(argv[i], "-NSDocumentRevisions")) {
337 i++; // skip YES
caryclarkc8fcafb2015-01-30 12:37:02 -0800338 } else
339#endif
mtklein929f63f2015-04-08 08:30:38 -0700340 if (FLAGS_undefok) {
341 SkDebugf("FYI: ignoring unknown flag '%s'.\n", argv[i]);
342 } else {
343 SkDebugf("Got unknown flag '%s'. Exiting.\n", argv[i]);
commit-bot@chromium.orgffc224f2014-03-25 21:00:02 +0000344 exit(-1);
345 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000346 }
347 }
348 }
349 // Since all of the flags have been set, release the memory used by each
350 // flag. FLAGS_x can still be used after this.
351 SkFlagInfo* flag = gHead;
halcanary96fcdcc2015-08-27 07:41:13 -0700352 gHead = nullptr;
353 while (flag != nullptr) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000354 SkFlagInfo* next = flag->next();
halcanary385fe4d2015-08-26 13:07:48 -0700355 delete flag;
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000356 flag = next;
357 }
358 if (helpPrinted) {
359 exit(0);
360 }
361}
sglez@google.com586db932013-07-24 17:24:23 +0000362
commit-bot@chromium.orga6f37e72013-08-30 15:52:46 +0000363namespace {
364
365template <typename Strings>
366bool ShouldSkipImpl(const Strings& strings, const char* name) {
sglez@google.com586db932013-07-24 17:24:23 +0000367 int count = strings.count();
368 size_t testLen = strlen(name);
369 bool anyExclude = count == 0;
370 for (int i = 0; i < strings.count(); ++i) {
371 const char* matchName = strings[i];
372 size_t matchLen = strlen(matchName);
373 bool matchExclude, matchStart, matchEnd;
374 if ((matchExclude = matchName[0] == '~')) {
375 anyExclude = true;
376 matchName++;
377 matchLen--;
378 }
379 if ((matchStart = matchName[0] == '^')) {
380 matchName++;
381 matchLen--;
382 }
383 if ((matchEnd = matchName[matchLen - 1] == '$')) {
384 matchLen--;
385 }
386 if (matchStart ? (!matchEnd || matchLen == testLen)
387 && strncmp(name, matchName, matchLen) == 0
388 : matchEnd ? matchLen <= testLen
389 && strncmp(name + testLen - matchLen, matchName, matchLen) == 0
390 : strstr(name, matchName) != 0) {
391 return matchExclude;
392 }
393 }
394 return !anyExclude;
395}
commit-bot@chromium.orga6f37e72013-08-30 15:52:46 +0000396
397} // namespace
398
399bool SkCommandLineFlags::ShouldSkip(const SkTDArray<const char*>& strings, const char* name) {
400 return ShouldSkipImpl(strings, name);
401}
402bool SkCommandLineFlags::ShouldSkip(const StringArray& strings, const char* name) {
403 return ShouldSkipImpl(strings, name);
404}