blob: ec9949bafcdc52e389e5220b775fbd7592699f06 [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
mtklein77a83962014-06-20 08:24:56 -070014DEFINE_bool(undefok, false, "Silently ignore unknown flags instead of crashing.");
commit-bot@chromium.orgffc224f2014-03-25 21:00:02 +000015
mtklein19e259b2015-05-04 10:54:48 -070016template <typename T> static void ignore_result(const T&) {}
17
scroggo@google.com58104a92013-04-24 19:25:26 +000018bool SkFlagInfo::CreateStringFlag(const char* name, const char* shortName,
19 SkCommandLineFlags::StringArray* pStrings,
20 const char* defaultValue, const char* helpString) {
21 SkFlagInfo* info = SkNEW_ARGS(SkFlagInfo, (name, shortName, kString_FlagType, helpString));
22 info->fDefaultString.set(defaultValue);
23
24 info->fStrings = pStrings;
25 SetDefaultStrings(pStrings, defaultValue);
26 return true;
27}
28
29void SkFlagInfo::SetDefaultStrings(SkCommandLineFlags::StringArray* pStrings,
30 const char* defaultValue) {
31 pStrings->reset();
scroggo@google.comd0419012013-04-24 19:37:52 +000032 if (NULL == defaultValue) {
33 return;
34 }
scroggo@google.com58104a92013-04-24 19:25:26 +000035 // If default is "", leave the array empty.
36 size_t defaultLength = strlen(defaultValue);
37 if (defaultLength > 0) {
38 const char* const defaultEnd = defaultValue + defaultLength;
39 const char* begin = defaultValue;
40 while (true) {
41 while (begin < defaultEnd && ' ' == *begin) {
42 begin++;
43 }
44 if (begin < defaultEnd) {
45 const char* end = begin + 1;
46 while (end < defaultEnd && ' ' != *end) {
47 end++;
48 }
49 size_t length = end - begin;
50 pStrings->append(begin, length);
51 begin = end + 1;
52 } else {
53 break;
54 }
55 }
56 }
57}
58
scroggo@google.com5dc4ca12013-03-21 13:10:59 +000059static bool string_is_in(const char* target, const char* set[], size_t len) {
60 for (size_t i = 0; i < len; i++) {
61 if (0 == strcmp(target, set[i])) {
62 return true;
63 }
64 }
65 return false;
66}
67
68/**
69 * Check to see whether string represents a boolean value.
70 * @param string C style string to parse.
71 * @param result Pointer to a boolean which will be set to the value in the string, if the
72 * string represents a boolean.
73 * @param boolean True if the string represents a boolean, false otherwise.
74 */
75static bool parse_bool_arg(const char* string, bool* result) {
76 static const char* trueValues[] = { "1", "TRUE", "true" };
77 if (string_is_in(string, trueValues, SK_ARRAY_COUNT(trueValues))) {
78 *result = true;
79 return true;
80 }
81 static const char* falseValues[] = { "0", "FALSE", "false" };
82 if (string_is_in(string, falseValues, SK_ARRAY_COUNT(falseValues))) {
83 *result = false;
84 return true;
85 }
86 SkDebugf("Parameter \"%s\" not supported.\n", string);
87 return false;
88}
89
90bool SkFlagInfo::match(const char* string) {
91 if (SkStrStartsWith(string, '-') && strlen(string) > 1) {
92 string++;
scroggo@google.com604e0c22013-04-09 21:25:46 +000093 const SkString* compareName;
scroggo@google.com5dc4ca12013-03-21 13:10:59 +000094 if (SkStrStartsWith(string, '-') && strlen(string) > 1) {
95 string++;
scroggo@google.com604e0c22013-04-09 21:25:46 +000096 // There were two dashes. Compare against full name.
97 compareName = &fName;
98 } else {
99 // One dash. Compare against the short name.
100 compareName = &fShortName;
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000101 }
102 if (kBool_FlagType == fFlagType) {
103 // In this case, go ahead and set the value.
scroggo@google.com604e0c22013-04-09 21:25:46 +0000104 if (compareName->equals(string)) {
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000105 *fBoolValue = true;
106 return true;
107 }
108 if (SkStrStartsWith(string, "no") && strlen(string) > 2) {
109 string += 2;
scroggo@google.com604e0c22013-04-09 21:25:46 +0000110 // Only allow "no" to be prepended to the full name.
111 if (fName.equals(string)) {
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000112 *fBoolValue = false;
113 return true;
114 }
115 return false;
116 }
117 int equalIndex = SkStrFind(string, "=");
118 if (equalIndex > 0) {
119 // The string has an equal sign. Check to see if the string matches.
120 SkString flag(string, equalIndex);
scroggo@google.com604e0c22013-04-09 21:25:46 +0000121 if (flag.equals(*compareName)) {
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000122 // Check to see if the remainder beyond the equal sign is true or false:
123 string += equalIndex + 1;
124 parse_bool_arg(string, fBoolValue);
125 return true;
scroggo@google.com604e0c22013-04-09 21:25:46 +0000126 } else {
127 return false;
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000128 }
129 }
130 }
scroggo@google.com604e0c22013-04-09 21:25:46 +0000131 return compareName->equals(string);
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000132 } else {
133 // Has no dash
134 return false;
135 }
136 return false;
137}
138
scroggo@google.comd9ba9a02013-03-21 19:43:15 +0000139SkFlagInfo* SkCommandLineFlags::gHead;
140SkString SkCommandLineFlags::gUsage;
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000141
scroggo@google.comd9ba9a02013-03-21 19:43:15 +0000142void SkCommandLineFlags::SetUsage(const char* usage) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000143 gUsage.set(usage);
144}
145
146// Maximum line length for the help message.
halcanary27523142015-04-27 06:41:35 -0700147#define LINE_LENGTH 72
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000148
scroggo@google.com8366df02013-03-20 19:50:41 +0000149static void print_help_for_flag(const SkFlagInfo* flag) {
halcanary27523142015-04-27 06:41:35 -0700150 SkDebugf(" --%s", flag->name().c_str());
scroggo@google.com8366df02013-03-20 19:50:41 +0000151 const SkString& shortName = flag->shortName();
152 if (shortName.size() > 0) {
153 SkDebugf(" or -%s", shortName.c_str());
154 }
155 SkDebugf(":\ttype: %s", flag->typeAsString().c_str());
156 if (flag->defaultValue().size() > 0) {
157 SkDebugf("\tdefault: %s", flag->defaultValue().c_str());
158 }
159 SkDebugf("\n");
160 const SkString& help = flag->help();
161 size_t length = help.size();
162 const char* currLine = help.c_str();
163 const char* stop = currLine + length;
164 while (currLine < stop) {
165 if (strlen(currLine) < LINE_LENGTH) {
166 // Only one line length's worth of text left.
halcanary27523142015-04-27 06:41:35 -0700167 SkDebugf(" %s\n", currLine);
scroggo@google.com8366df02013-03-20 19:50:41 +0000168 break;
169 }
170 int lineBreak = SkStrFind(currLine, "\n");
171 if (lineBreak < 0 || lineBreak > LINE_LENGTH) {
172 // No line break within line length. Will need to insert one.
173 // Find a space before the line break.
174 int spaceIndex = LINE_LENGTH - 1;
175 while (spaceIndex > 0 && currLine[spaceIndex] != ' ') {
176 spaceIndex--;
177 }
178 int gap;
179 if (0 == spaceIndex) {
180 // No spaces on the entire line. Go ahead and break mid word.
181 spaceIndex = LINE_LENGTH;
182 gap = 0;
183 } else {
184 // Skip the space on the next line
185 gap = 1;
186 }
halcanary27523142015-04-27 06:41:35 -0700187 SkDebugf(" %.*s\n", spaceIndex, currLine);
scroggo@google.com8366df02013-03-20 19:50:41 +0000188 currLine += spaceIndex + gap;
189 } else {
190 // the line break is within the limit. Break there.
191 lineBreak++;
halcanary27523142015-04-27 06:41:35 -0700192 SkDebugf(" %.*s", lineBreak, currLine);
scroggo@google.com8366df02013-03-20 19:50:41 +0000193 currLine += lineBreak;
194 }
195 }
196 SkDebugf("\n");
197}
198
halcanary03758b82015-01-18 10:39:25 -0800199namespace {
200struct CompareFlagsByName {
201 bool operator()(SkFlagInfo* a, SkFlagInfo* b) const {
202 return strcmp(a->name().c_str(), b->name().c_str()) < 0;
203 }
204};
205} // namespace
206
scroggo@google.comd9ba9a02013-03-21 19:43:15 +0000207void SkCommandLineFlags::Parse(int argc, char** argv) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000208 // Only allow calling this function once.
209 static bool gOnce;
210 if (gOnce) {
scroggo@google.com604e0c22013-04-09 21:25:46 +0000211 SkDebugf("Parse should only be called once at the beginning of main!\n");
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000212 SkASSERT(false);
213 return;
214 }
215 gOnce = true;
216
217 bool helpPrinted = false;
218 // Loop over argv, starting with 1, since the first is just the name of the program.
219 for (int i = 1; i < argc; i++) {
scroggo@google.com604e0c22013-04-09 21:25:46 +0000220 if (0 == strcmp("-h", argv[i]) || 0 == strcmp("--help", argv[i])) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000221 // Print help message.
scroggo@google.com8366df02013-03-20 19:50:41 +0000222 SkTDArray<const char*> helpFlags;
223 for (int j = i + 1; j < argc; j++) {
224 if (SkStrStartsWith(argv[j], '-')) {
225 break;
226 }
227 helpFlags.append(1, &argv[j]);
228 }
229 if (0 == helpFlags.count()) {
230 // Only print general help message if help for specific flags is not requested.
231 SkDebugf("%s\n%s\n", argv[0], gUsage.c_str());
232 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000233 SkDebugf("Flags:\n");
halcanary03758b82015-01-18 10:39:25 -0800234
235 if (0 == helpFlags.count()) {
scroggo@google.com8366df02013-03-20 19:50:41 +0000236 // If no flags followed --help, print them all
halcanary03758b82015-01-18 10:39:25 -0800237 SkTDArray<SkFlagInfo*> allFlags;
238 for (SkFlagInfo* flag = SkCommandLineFlags::gHead; flag;
239 flag = flag->next()) {
240 allFlags.push(flag);
241 }
242 SkTQSort(&allFlags[0], &allFlags[allFlags.count() - 1],
243 CompareFlagsByName());
244 for (int i = 0; i < allFlags.count(); ++i) {
245 print_help_for_flag(allFlags[i]);
246 }
247 } else {
248 for (SkFlagInfo* flag = SkCommandLineFlags::gHead; flag;
249 flag = flag->next()) {
scroggo@google.com8366df02013-03-20 19:50:41 +0000250 for (int k = 0; k < helpFlags.count(); k++) {
251 if (flag->name().equals(helpFlags[k]) ||
252 flag->shortName().equals(helpFlags[k])) {
halcanary03758b82015-01-18 10:39:25 -0800253 print_help_for_flag(flag);
scroggo@google.com8366df02013-03-20 19:50:41 +0000254 helpFlags.remove(k);
255 break;
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000256 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000257 }
258 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000259 }
scroggo@google.com8366df02013-03-20 19:50:41 +0000260 if (helpFlags.count() > 0) {
261 SkDebugf("Requested help for unrecognized flags:\n");
262 for (int k = 0; k < helpFlags.count(); k++) {
halcanary27523142015-04-27 06:41:35 -0700263 SkDebugf(" --%s\n", helpFlags[k]);
scroggo@google.com8366df02013-03-20 19:50:41 +0000264 }
265 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000266 helpPrinted = true;
267 }
268 if (!helpPrinted) {
269 bool flagMatched = false;
270 SkFlagInfo* flag = gHead;
271 while (flag != NULL) {
272 if (flag->match(argv[i])) {
273 flagMatched = true;
274 switch (flag->getFlagType()) {
275 case SkFlagInfo::kBool_FlagType:
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000276 // Can be handled by match, above, but can also be set by the next
277 // string.
278 if (i+1 < argc && !SkStrStartsWith(argv[i+1], '-')) {
279 i++;
280 bool value;
281 if (parse_bool_arg(argv[i], &value)) {
282 flag->setBool(value);
283 }
284 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000285 break;
286 case SkFlagInfo::kString_FlagType:
287 flag->resetStrings();
288 // Add all arguments until another flag is reached.
mtkleindfb7da32015-02-14 18:56:31 -0800289 while (i+1 < argc) {
290 char* end = NULL;
mtklein19e259b2015-05-04 10:54:48 -0700291 // Negative numbers aren't flags.
292 ignore_result(strtod(argv[i+1], &end));
mtkleindfb7da32015-02-14 18:56:31 -0800293 if (end == argv[i+1] && SkStrStartsWith(argv[i+1], '-')) {
294 break;
295 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000296 i++;
297 flag->append(argv[i]);
298 }
299 break;
300 case SkFlagInfo::kInt_FlagType:
301 i++;
302 flag->setInt(atoi(argv[i]));
303 break;
304 case SkFlagInfo::kDouble_FlagType:
305 i++;
306 flag->setDouble(atof(argv[i]));
307 break;
308 default:
mtklein@google.com330313a2013-08-22 15:37:26 +0000309 SkDEBUGFAIL("Invalid flag type");
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000310 }
311 break;
312 }
313 flag = flag->next();
314 }
315 if (!flagMatched) {
caryclarkc8fcafb2015-01-30 12:37:02 -0800316#if SK_BUILD_FOR_MAC
caryclark83ca6282015-06-10 09:31:09 -0700317 if (SkStrStartsWith(argv[i], "NSDocumentRevisions")
318 || SkStrStartsWith(argv[i], "-NSDocumentRevisions")) {
319 i++; // skip YES
caryclarkc8fcafb2015-01-30 12:37:02 -0800320 } else
321#endif
mtklein929f63f2015-04-08 08:30:38 -0700322 if (FLAGS_undefok) {
323 SkDebugf("FYI: ignoring unknown flag '%s'.\n", argv[i]);
324 } else {
325 SkDebugf("Got unknown flag '%s'. Exiting.\n", argv[i]);
commit-bot@chromium.orgffc224f2014-03-25 21:00:02 +0000326 exit(-1);
327 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000328 }
329 }
330 }
331 // Since all of the flags have been set, release the memory used by each
332 // flag. FLAGS_x can still be used after this.
333 SkFlagInfo* flag = gHead;
334 gHead = NULL;
335 while (flag != NULL) {
336 SkFlagInfo* next = flag->next();
337 SkDELETE(flag);
338 flag = next;
339 }
340 if (helpPrinted) {
341 exit(0);
342 }
343}
sglez@google.com586db932013-07-24 17:24:23 +0000344
commit-bot@chromium.orga6f37e72013-08-30 15:52:46 +0000345namespace {
346
347template <typename Strings>
348bool ShouldSkipImpl(const Strings& strings, const char* name) {
sglez@google.com586db932013-07-24 17:24:23 +0000349 int count = strings.count();
350 size_t testLen = strlen(name);
351 bool anyExclude = count == 0;
352 for (int i = 0; i < strings.count(); ++i) {
353 const char* matchName = strings[i];
354 size_t matchLen = strlen(matchName);
355 bool matchExclude, matchStart, matchEnd;
356 if ((matchExclude = matchName[0] == '~')) {
357 anyExclude = true;
358 matchName++;
359 matchLen--;
360 }
361 if ((matchStart = matchName[0] == '^')) {
362 matchName++;
363 matchLen--;
364 }
365 if ((matchEnd = matchName[matchLen - 1] == '$')) {
366 matchLen--;
367 }
368 if (matchStart ? (!matchEnd || matchLen == testLen)
369 && strncmp(name, matchName, matchLen) == 0
370 : matchEnd ? matchLen <= testLen
371 && strncmp(name + testLen - matchLen, matchName, matchLen) == 0
372 : strstr(name, matchName) != 0) {
373 return matchExclude;
374 }
375 }
376 return !anyExclude;
377}
commit-bot@chromium.orga6f37e72013-08-30 15:52:46 +0000378
379} // namespace
380
381bool SkCommandLineFlags::ShouldSkip(const SkTDArray<const char*>& strings, const char* name) {
382 return ShouldSkipImpl(strings, name);
383}
384bool SkCommandLineFlags::ShouldSkip(const StringArray& strings, const char* name) {
385 return ShouldSkipImpl(strings, name);
386}