blob: c25fec5343cb04f2b3744c248b163a314090b80e [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"
scroggo@google.com161e1ba2013-03-04 16:41:06 +000010
mtklein77a83962014-06-20 08:24:56 -070011DEFINE_bool(undefok, false, "Silently ignore unknown flags instead of crashing.");
commit-bot@chromium.orgffc224f2014-03-25 21:00:02 +000012
scroggo@google.com58104a92013-04-24 19:25:26 +000013bool SkFlagInfo::CreateStringFlag(const char* name, const char* shortName,
14 SkCommandLineFlags::StringArray* pStrings,
15 const char* defaultValue, const char* helpString) {
16 SkFlagInfo* info = SkNEW_ARGS(SkFlagInfo, (name, shortName, kString_FlagType, helpString));
17 info->fDefaultString.set(defaultValue);
18
19 info->fStrings = pStrings;
20 SetDefaultStrings(pStrings, defaultValue);
21 return true;
22}
23
24void SkFlagInfo::SetDefaultStrings(SkCommandLineFlags::StringArray* pStrings,
25 const char* defaultValue) {
26 pStrings->reset();
scroggo@google.comd0419012013-04-24 19:37:52 +000027 if (NULL == defaultValue) {
28 return;
29 }
scroggo@google.com58104a92013-04-24 19:25:26 +000030 // If default is "", leave the array empty.
31 size_t defaultLength = strlen(defaultValue);
32 if (defaultLength > 0) {
33 const char* const defaultEnd = defaultValue + defaultLength;
34 const char* begin = defaultValue;
35 while (true) {
36 while (begin < defaultEnd && ' ' == *begin) {
37 begin++;
38 }
39 if (begin < defaultEnd) {
40 const char* end = begin + 1;
41 while (end < defaultEnd && ' ' != *end) {
42 end++;
43 }
44 size_t length = end - begin;
45 pStrings->append(begin, length);
46 begin = end + 1;
47 } else {
48 break;
49 }
50 }
51 }
52}
53
scroggo@google.com5dc4ca12013-03-21 13:10:59 +000054static bool string_is_in(const char* target, const char* set[], size_t len) {
55 for (size_t i = 0; i < len; i++) {
56 if (0 == strcmp(target, set[i])) {
57 return true;
58 }
59 }
60 return false;
61}
62
63/**
64 * Check to see whether string represents a boolean value.
65 * @param string C style string to parse.
66 * @param result Pointer to a boolean which will be set to the value in the string, if the
67 * string represents a boolean.
68 * @param boolean True if the string represents a boolean, false otherwise.
69 */
70static bool parse_bool_arg(const char* string, bool* result) {
71 static const char* trueValues[] = { "1", "TRUE", "true" };
72 if (string_is_in(string, trueValues, SK_ARRAY_COUNT(trueValues))) {
73 *result = true;
74 return true;
75 }
76 static const char* falseValues[] = { "0", "FALSE", "false" };
77 if (string_is_in(string, falseValues, SK_ARRAY_COUNT(falseValues))) {
78 *result = false;
79 return true;
80 }
81 SkDebugf("Parameter \"%s\" not supported.\n", string);
82 return false;
83}
84
85bool SkFlagInfo::match(const char* string) {
86 if (SkStrStartsWith(string, '-') && strlen(string) > 1) {
87 string++;
scroggo@google.com604e0c22013-04-09 21:25:46 +000088 const SkString* compareName;
scroggo@google.com5dc4ca12013-03-21 13:10:59 +000089 if (SkStrStartsWith(string, '-') && strlen(string) > 1) {
90 string++;
scroggo@google.com604e0c22013-04-09 21:25:46 +000091 // There were two dashes. Compare against full name.
92 compareName = &fName;
93 } else {
94 // One dash. Compare against the short name.
95 compareName = &fShortName;
scroggo@google.com5dc4ca12013-03-21 13:10:59 +000096 }
97 if (kBool_FlagType == fFlagType) {
98 // In this case, go ahead and set the value.
scroggo@google.com604e0c22013-04-09 21:25:46 +000099 if (compareName->equals(string)) {
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000100 *fBoolValue = true;
101 return true;
102 }
103 if (SkStrStartsWith(string, "no") && strlen(string) > 2) {
104 string += 2;
scroggo@google.com604e0c22013-04-09 21:25:46 +0000105 // Only allow "no" to be prepended to the full name.
106 if (fName.equals(string)) {
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000107 *fBoolValue = false;
108 return true;
109 }
110 return false;
111 }
112 int equalIndex = SkStrFind(string, "=");
113 if (equalIndex > 0) {
114 // The string has an equal sign. Check to see if the string matches.
115 SkString flag(string, equalIndex);
scroggo@google.com604e0c22013-04-09 21:25:46 +0000116 if (flag.equals(*compareName)) {
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000117 // Check to see if the remainder beyond the equal sign is true or false:
118 string += equalIndex + 1;
119 parse_bool_arg(string, fBoolValue);
120 return true;
scroggo@google.com604e0c22013-04-09 21:25:46 +0000121 } else {
122 return false;
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000123 }
124 }
125 }
scroggo@google.com604e0c22013-04-09 21:25:46 +0000126 return compareName->equals(string);
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000127 } else {
128 // Has no dash
129 return false;
130 }
131 return false;
132}
133
scroggo@google.comd9ba9a02013-03-21 19:43:15 +0000134SkFlagInfo* SkCommandLineFlags::gHead;
135SkString SkCommandLineFlags::gUsage;
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000136
scroggo@google.comd9ba9a02013-03-21 19:43:15 +0000137void SkCommandLineFlags::SetUsage(const char* usage) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000138 gUsage.set(usage);
139}
140
141// Maximum line length for the help message.
142#define LINE_LENGTH 80
143
scroggo@google.com8366df02013-03-20 19:50:41 +0000144static void print_help_for_flag(const SkFlagInfo* flag) {
145 SkDebugf("\t--%s", flag->name().c_str());
146 const SkString& shortName = flag->shortName();
147 if (shortName.size() > 0) {
148 SkDebugf(" or -%s", shortName.c_str());
149 }
150 SkDebugf(":\ttype: %s", flag->typeAsString().c_str());
151 if (flag->defaultValue().size() > 0) {
152 SkDebugf("\tdefault: %s", flag->defaultValue().c_str());
153 }
154 SkDebugf("\n");
155 const SkString& help = flag->help();
156 size_t length = help.size();
157 const char* currLine = help.c_str();
158 const char* stop = currLine + length;
159 while (currLine < stop) {
160 if (strlen(currLine) < LINE_LENGTH) {
161 // Only one line length's worth of text left.
162 SkDebugf("\t\t%s\n", currLine);
163 break;
164 }
165 int lineBreak = SkStrFind(currLine, "\n");
166 if (lineBreak < 0 || lineBreak > LINE_LENGTH) {
167 // No line break within line length. Will need to insert one.
168 // Find a space before the line break.
169 int spaceIndex = LINE_LENGTH - 1;
170 while (spaceIndex > 0 && currLine[spaceIndex] != ' ') {
171 spaceIndex--;
172 }
173 int gap;
174 if (0 == spaceIndex) {
175 // No spaces on the entire line. Go ahead and break mid word.
176 spaceIndex = LINE_LENGTH;
177 gap = 0;
178 } else {
179 // Skip the space on the next line
180 gap = 1;
181 }
182 SkDebugf("\t\t%.*s\n", spaceIndex, currLine);
183 currLine += spaceIndex + gap;
184 } else {
185 // the line break is within the limit. Break there.
186 lineBreak++;
187 SkDebugf("\t\t%.*s", lineBreak, currLine);
188 currLine += lineBreak;
189 }
190 }
191 SkDebugf("\n");
192}
193
scroggo@google.comd9ba9a02013-03-21 19:43:15 +0000194void SkCommandLineFlags::Parse(int argc, char** argv) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000195 // Only allow calling this function once.
196 static bool gOnce;
197 if (gOnce) {
scroggo@google.com604e0c22013-04-09 21:25:46 +0000198 SkDebugf("Parse should only be called once at the beginning of main!\n");
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000199 SkASSERT(false);
200 return;
201 }
202 gOnce = true;
203
204 bool helpPrinted = false;
205 // Loop over argv, starting with 1, since the first is just the name of the program.
206 for (int i = 1; i < argc; i++) {
scroggo@google.com604e0c22013-04-09 21:25:46 +0000207 if (0 == strcmp("-h", argv[i]) || 0 == strcmp("--help", argv[i])) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000208 // Print help message.
scroggo@google.com8366df02013-03-20 19:50:41 +0000209 SkTDArray<const char*> helpFlags;
210 for (int j = i + 1; j < argc; j++) {
211 if (SkStrStartsWith(argv[j], '-')) {
212 break;
213 }
214 helpFlags.append(1, &argv[j]);
215 }
216 if (0 == helpFlags.count()) {
217 // Only print general help message if help for specific flags is not requested.
218 SkDebugf("%s\n%s\n", argv[0], gUsage.c_str());
219 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000220 SkDebugf("Flags:\n");
scroggo@google.comd9ba9a02013-03-21 19:43:15 +0000221 SkFlagInfo* flag = SkCommandLineFlags::gHead;
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000222 while (flag != NULL) {
scroggo@google.com8366df02013-03-20 19:50:41 +0000223 // If no flags followed --help, print them all
224 bool printFlag = 0 == helpFlags.count();
225 if (!printFlag) {
226 for (int k = 0; k < helpFlags.count(); k++) {
227 if (flag->name().equals(helpFlags[k]) ||
228 flag->shortName().equals(helpFlags[k])) {
229 printFlag = true;
230 helpFlags.remove(k);
231 break;
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000232 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000233 }
234 }
scroggo@google.com8366df02013-03-20 19:50:41 +0000235 if (printFlag) {
236 print_help_for_flag(flag);
237 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000238 flag = flag->next();
239 }
scroggo@google.com8366df02013-03-20 19:50:41 +0000240 if (helpFlags.count() > 0) {
241 SkDebugf("Requested help for unrecognized flags:\n");
242 for (int k = 0; k < helpFlags.count(); k++) {
243 SkDebugf("\t--%s\n", helpFlags[k]);
244 }
245 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000246 helpPrinted = true;
247 }
248 if (!helpPrinted) {
249 bool flagMatched = false;
250 SkFlagInfo* flag = gHead;
251 while (flag != NULL) {
252 if (flag->match(argv[i])) {
253 flagMatched = true;
254 switch (flag->getFlagType()) {
255 case SkFlagInfo::kBool_FlagType:
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000256 // Can be handled by match, above, but can also be set by the next
257 // string.
258 if (i+1 < argc && !SkStrStartsWith(argv[i+1], '-')) {
259 i++;
260 bool value;
261 if (parse_bool_arg(argv[i], &value)) {
262 flag->setBool(value);
263 }
264 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000265 break;
266 case SkFlagInfo::kString_FlagType:
267 flag->resetStrings();
268 // Add all arguments until another flag is reached.
269 while (i+1 < argc && !SkStrStartsWith(argv[i+1], '-')) {
270 i++;
271 flag->append(argv[i]);
272 }
273 break;
274 case SkFlagInfo::kInt_FlagType:
275 i++;
276 flag->setInt(atoi(argv[i]));
277 break;
278 case SkFlagInfo::kDouble_FlagType:
279 i++;
280 flag->setDouble(atof(argv[i]));
281 break;
282 default:
mtklein@google.com330313a2013-08-22 15:37:26 +0000283 SkDEBUGFAIL("Invalid flag type");
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000284 }
285 break;
286 }
287 flag = flag->next();
288 }
289 if (!flagMatched) {
mtklein77a83962014-06-20 08:24:56 -0700290 if (!FLAGS_undefok) {
commit-bot@chromium.orgffc224f2014-03-25 21:00:02 +0000291 SkDebugf("Got unknown flag \"%s\". Exiting.\n", argv[i]);
292 exit(-1);
293 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000294 }
295 }
296 }
297 // Since all of the flags have been set, release the memory used by each
298 // flag. FLAGS_x can still be used after this.
299 SkFlagInfo* flag = gHead;
300 gHead = NULL;
301 while (flag != NULL) {
302 SkFlagInfo* next = flag->next();
303 SkDELETE(flag);
304 flag = next;
305 }
306 if (helpPrinted) {
307 exit(0);
308 }
309}
sglez@google.com586db932013-07-24 17:24:23 +0000310
commit-bot@chromium.orga6f37e72013-08-30 15:52:46 +0000311namespace {
312
313template <typename Strings>
314bool ShouldSkipImpl(const Strings& strings, const char* name) {
sglez@google.com586db932013-07-24 17:24:23 +0000315 int count = strings.count();
316 size_t testLen = strlen(name);
317 bool anyExclude = count == 0;
318 for (int i = 0; i < strings.count(); ++i) {
319 const char* matchName = strings[i];
320 size_t matchLen = strlen(matchName);
321 bool matchExclude, matchStart, matchEnd;
322 if ((matchExclude = matchName[0] == '~')) {
323 anyExclude = true;
324 matchName++;
325 matchLen--;
326 }
327 if ((matchStart = matchName[0] == '^')) {
328 matchName++;
329 matchLen--;
330 }
331 if ((matchEnd = matchName[matchLen - 1] == '$')) {
332 matchLen--;
333 }
334 if (matchStart ? (!matchEnd || matchLen == testLen)
335 && strncmp(name, matchName, matchLen) == 0
336 : matchEnd ? matchLen <= testLen
337 && strncmp(name + testLen - matchLen, matchName, matchLen) == 0
338 : strstr(name, matchName) != 0) {
339 return matchExclude;
340 }
341 }
342 return !anyExclude;
343}
commit-bot@chromium.orga6f37e72013-08-30 15:52:46 +0000344
345} // namespace
346
347bool SkCommandLineFlags::ShouldSkip(const SkTDArray<const char*>& strings, const char* name) {
348 return ShouldSkipImpl(strings, name);
349}
350bool SkCommandLineFlags::ShouldSkip(const StringArray& strings, const char* name) {
351 return ShouldSkipImpl(strings, name);
352}