blob: b088b4e4d38ec6675bd4c64c3b779b0cc5313975 [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
scroggo@google.com58104a92013-04-24 19:25:26 +000011bool SkFlagInfo::CreateStringFlag(const char* name, const char* shortName,
12 SkCommandLineFlags::StringArray* pStrings,
13 const char* defaultValue, const char* helpString) {
14 SkFlagInfo* info = SkNEW_ARGS(SkFlagInfo, (name, shortName, kString_FlagType, helpString));
15 info->fDefaultString.set(defaultValue);
16
17 info->fStrings = pStrings;
18 SetDefaultStrings(pStrings, defaultValue);
19 return true;
20}
21
22void SkFlagInfo::SetDefaultStrings(SkCommandLineFlags::StringArray* pStrings,
23 const char* defaultValue) {
24 pStrings->reset();
scroggo@google.comd0419012013-04-24 19:37:52 +000025 if (NULL == defaultValue) {
26 return;
27 }
scroggo@google.com58104a92013-04-24 19:25:26 +000028 // If default is "", leave the array empty.
29 size_t defaultLength = strlen(defaultValue);
30 if (defaultLength > 0) {
31 const char* const defaultEnd = defaultValue + defaultLength;
32 const char* begin = defaultValue;
33 while (true) {
34 while (begin < defaultEnd && ' ' == *begin) {
35 begin++;
36 }
37 if (begin < defaultEnd) {
38 const char* end = begin + 1;
39 while (end < defaultEnd && ' ' != *end) {
40 end++;
41 }
42 size_t length = end - begin;
43 pStrings->append(begin, length);
44 begin = end + 1;
45 } else {
46 break;
47 }
48 }
49 }
50}
51
scroggo@google.com5dc4ca12013-03-21 13:10:59 +000052static bool string_is_in(const char* target, const char* set[], size_t len) {
53 for (size_t i = 0; i < len; i++) {
54 if (0 == strcmp(target, set[i])) {
55 return true;
56 }
57 }
58 return false;
59}
60
61/**
62 * Check to see whether string represents a boolean value.
63 * @param string C style string to parse.
64 * @param result Pointer to a boolean which will be set to the value in the string, if the
65 * string represents a boolean.
66 * @param boolean True if the string represents a boolean, false otherwise.
67 */
68static bool parse_bool_arg(const char* string, bool* result) {
69 static const char* trueValues[] = { "1", "TRUE", "true" };
70 if (string_is_in(string, trueValues, SK_ARRAY_COUNT(trueValues))) {
71 *result = true;
72 return true;
73 }
74 static const char* falseValues[] = { "0", "FALSE", "false" };
75 if (string_is_in(string, falseValues, SK_ARRAY_COUNT(falseValues))) {
76 *result = false;
77 return true;
78 }
79 SkDebugf("Parameter \"%s\" not supported.\n", string);
80 return false;
81}
82
83bool SkFlagInfo::match(const char* string) {
84 if (SkStrStartsWith(string, '-') && strlen(string) > 1) {
85 string++;
scroggo@google.com604e0c22013-04-09 21:25:46 +000086 const SkString* compareName;
scroggo@google.com5dc4ca12013-03-21 13:10:59 +000087 if (SkStrStartsWith(string, '-') && strlen(string) > 1) {
88 string++;
scroggo@google.com604e0c22013-04-09 21:25:46 +000089 // There were two dashes. Compare against full name.
90 compareName = &fName;
91 } else {
92 // One dash. Compare against the short name.
93 compareName = &fShortName;
scroggo@google.com5dc4ca12013-03-21 13:10:59 +000094 }
95 if (kBool_FlagType == fFlagType) {
96 // In this case, go ahead and set the value.
scroggo@google.com604e0c22013-04-09 21:25:46 +000097 if (compareName->equals(string)) {
scroggo@google.com5dc4ca12013-03-21 13:10:59 +000098 *fBoolValue = true;
99 return true;
100 }
101 if (SkStrStartsWith(string, "no") && strlen(string) > 2) {
102 string += 2;
scroggo@google.com604e0c22013-04-09 21:25:46 +0000103 // Only allow "no" to be prepended to the full name.
104 if (fName.equals(string)) {
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000105 *fBoolValue = false;
106 return true;
107 }
108 return false;
109 }
110 int equalIndex = SkStrFind(string, "=");
111 if (equalIndex > 0) {
112 // The string has an equal sign. Check to see if the string matches.
113 SkString flag(string, equalIndex);
scroggo@google.com604e0c22013-04-09 21:25:46 +0000114 if (flag.equals(*compareName)) {
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000115 // Check to see if the remainder beyond the equal sign is true or false:
116 string += equalIndex + 1;
117 parse_bool_arg(string, fBoolValue);
118 return true;
scroggo@google.com604e0c22013-04-09 21:25:46 +0000119 } else {
120 return false;
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000121 }
122 }
123 }
scroggo@google.com604e0c22013-04-09 21:25:46 +0000124 return compareName->equals(string);
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000125 } else {
126 // Has no dash
127 return false;
128 }
129 return false;
130}
131
scroggo@google.comd9ba9a02013-03-21 19:43:15 +0000132SkFlagInfo* SkCommandLineFlags::gHead;
133SkString SkCommandLineFlags::gUsage;
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000134
scroggo@google.comd9ba9a02013-03-21 19:43:15 +0000135void SkCommandLineFlags::SetUsage(const char* usage) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000136 gUsage.set(usage);
137}
138
139// Maximum line length for the help message.
140#define LINE_LENGTH 80
141
scroggo@google.com8366df02013-03-20 19:50:41 +0000142static void print_help_for_flag(const SkFlagInfo* flag) {
143 SkDebugf("\t--%s", flag->name().c_str());
144 const SkString& shortName = flag->shortName();
145 if (shortName.size() > 0) {
146 SkDebugf(" or -%s", shortName.c_str());
147 }
148 SkDebugf(":\ttype: %s", flag->typeAsString().c_str());
149 if (flag->defaultValue().size() > 0) {
150 SkDebugf("\tdefault: %s", flag->defaultValue().c_str());
151 }
152 SkDebugf("\n");
153 const SkString& help = flag->help();
154 size_t length = help.size();
155 const char* currLine = help.c_str();
156 const char* stop = currLine + length;
157 while (currLine < stop) {
158 if (strlen(currLine) < LINE_LENGTH) {
159 // Only one line length's worth of text left.
160 SkDebugf("\t\t%s\n", currLine);
161 break;
162 }
163 int lineBreak = SkStrFind(currLine, "\n");
164 if (lineBreak < 0 || lineBreak > LINE_LENGTH) {
165 // No line break within line length. Will need to insert one.
166 // Find a space before the line break.
167 int spaceIndex = LINE_LENGTH - 1;
168 while (spaceIndex > 0 && currLine[spaceIndex] != ' ') {
169 spaceIndex--;
170 }
171 int gap;
172 if (0 == spaceIndex) {
173 // No spaces on the entire line. Go ahead and break mid word.
174 spaceIndex = LINE_LENGTH;
175 gap = 0;
176 } else {
177 // Skip the space on the next line
178 gap = 1;
179 }
180 SkDebugf("\t\t%.*s\n", spaceIndex, currLine);
181 currLine += spaceIndex + gap;
182 } else {
183 // the line break is within the limit. Break there.
184 lineBreak++;
185 SkDebugf("\t\t%.*s", lineBreak, currLine);
186 currLine += lineBreak;
187 }
188 }
189 SkDebugf("\n");
190}
191
scroggo@google.comd9ba9a02013-03-21 19:43:15 +0000192void SkCommandLineFlags::Parse(int argc, char** argv) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000193 // Only allow calling this function once.
194 static bool gOnce;
195 if (gOnce) {
scroggo@google.com604e0c22013-04-09 21:25:46 +0000196 SkDebugf("Parse should only be called once at the beginning of main!\n");
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000197 SkASSERT(false);
198 return;
199 }
200 gOnce = true;
201
202 bool helpPrinted = false;
203 // Loop over argv, starting with 1, since the first is just the name of the program.
204 for (int i = 1; i < argc; i++) {
scroggo@google.com604e0c22013-04-09 21:25:46 +0000205 if (0 == strcmp("-h", argv[i]) || 0 == strcmp("--help", argv[i])) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000206 // Print help message.
scroggo@google.com8366df02013-03-20 19:50:41 +0000207 SkTDArray<const char*> helpFlags;
208 for (int j = i + 1; j < argc; j++) {
209 if (SkStrStartsWith(argv[j], '-')) {
210 break;
211 }
212 helpFlags.append(1, &argv[j]);
213 }
214 if (0 == helpFlags.count()) {
215 // Only print general help message if help for specific flags is not requested.
216 SkDebugf("%s\n%s\n", argv[0], gUsage.c_str());
217 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000218 SkDebugf("Flags:\n");
scroggo@google.comd9ba9a02013-03-21 19:43:15 +0000219 SkFlagInfo* flag = SkCommandLineFlags::gHead;
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000220 while (flag != NULL) {
scroggo@google.com8366df02013-03-20 19:50:41 +0000221 // If no flags followed --help, print them all
222 bool printFlag = 0 == helpFlags.count();
223 if (!printFlag) {
224 for (int k = 0; k < helpFlags.count(); k++) {
225 if (flag->name().equals(helpFlags[k]) ||
226 flag->shortName().equals(helpFlags[k])) {
227 printFlag = true;
228 helpFlags.remove(k);
229 break;
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000230 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000231 }
232 }
scroggo@google.com8366df02013-03-20 19:50:41 +0000233 if (printFlag) {
234 print_help_for_flag(flag);
235 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000236 flag = flag->next();
237 }
scroggo@google.com8366df02013-03-20 19:50:41 +0000238 if (helpFlags.count() > 0) {
239 SkDebugf("Requested help for unrecognized flags:\n");
240 for (int k = 0; k < helpFlags.count(); k++) {
241 SkDebugf("\t--%s\n", helpFlags[k]);
242 }
243 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000244 helpPrinted = true;
245 }
246 if (!helpPrinted) {
247 bool flagMatched = false;
248 SkFlagInfo* flag = gHead;
249 while (flag != NULL) {
250 if (flag->match(argv[i])) {
251 flagMatched = true;
252 switch (flag->getFlagType()) {
253 case SkFlagInfo::kBool_FlagType:
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000254 // Can be handled by match, above, but can also be set by the next
255 // string.
256 if (i+1 < argc && !SkStrStartsWith(argv[i+1], '-')) {
257 i++;
258 bool value;
259 if (parse_bool_arg(argv[i], &value)) {
260 flag->setBool(value);
261 }
262 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000263 break;
264 case SkFlagInfo::kString_FlagType:
265 flag->resetStrings();
266 // Add all arguments until another flag is reached.
267 while (i+1 < argc && !SkStrStartsWith(argv[i+1], '-')) {
268 i++;
269 flag->append(argv[i]);
270 }
271 break;
272 case SkFlagInfo::kInt_FlagType:
273 i++;
274 flag->setInt(atoi(argv[i]));
275 break;
276 case SkFlagInfo::kDouble_FlagType:
277 i++;
278 flag->setDouble(atof(argv[i]));
279 break;
280 default:
281 SkASSERT(!"Invalid flag type");
282 }
283 break;
284 }
285 flag = flag->next();
286 }
287 if (!flagMatched) {
scroggo@google.com0f2cd172013-03-20 20:04:27 +0000288 SkDebugf("Got unknown flag \"%s\". Exiting.\n", argv[i]);
289 exit(-1);
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000290 }
291 }
292 }
293 // Since all of the flags have been set, release the memory used by each
294 // flag. FLAGS_x can still be used after this.
295 SkFlagInfo* flag = gHead;
296 gHead = NULL;
297 while (flag != NULL) {
298 SkFlagInfo* next = flag->next();
299 SkDELETE(flag);
300 flag = next;
301 }
302 if (helpPrinted) {
303 exit(0);
304 }
305}
sglez@google.com586db932013-07-24 17:24:23 +0000306
307bool SkCommandLineFlags::ShouldSkip(const SkTDArray<const char*>& strings, const char* name) {
308 int count = strings.count();
309 size_t testLen = strlen(name);
310 bool anyExclude = count == 0;
311 for (int i = 0; i < strings.count(); ++i) {
312 const char* matchName = strings[i];
313 size_t matchLen = strlen(matchName);
314 bool matchExclude, matchStart, matchEnd;
315 if ((matchExclude = matchName[0] == '~')) {
316 anyExclude = true;
317 matchName++;
318 matchLen--;
319 }
320 if ((matchStart = matchName[0] == '^')) {
321 matchName++;
322 matchLen--;
323 }
324 if ((matchEnd = matchName[matchLen - 1] == '$')) {
325 matchLen--;
326 }
327 if (matchStart ? (!matchEnd || matchLen == testLen)
328 && strncmp(name, matchName, matchLen) == 0
329 : matchEnd ? matchLen <= testLen
330 && strncmp(name + testLen - matchLen, matchName, matchLen) == 0
331 : strstr(name, matchName) != 0) {
332 return matchExclude;
333 }
334 }
335 return !anyExclude;
336}