/src/skia/tools/flags/CommandLineFlags.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
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 | | |
8 | | #include "include/private/base/SkTDArray.h" |
9 | | #include "src/base/SkTSort.h" |
10 | | #include "tools/flags/CommandLineFlags.h" |
11 | | |
12 | | #include <stdlib.h> |
13 | | |
14 | 0 | template <typename T> static void ignore_result(const T&) {} |
15 | | |
16 | | bool SkFlagInfo::CreateStringFlag(const char* name, |
17 | | const char* shortName, |
18 | | CommandLineFlags::StringArray* pStrings, |
19 | | const char* defaultValue, |
20 | | const char* helpString, |
21 | 148 | const char* extendedHelpString) { |
22 | 148 | SkFlagInfo* info = |
23 | 148 | new SkFlagInfo(name, shortName, kString_FlagType, helpString, extendedHelpString); |
24 | 148 | info->fDefaultString.set(defaultValue); |
25 | | |
26 | 148 | info->fStrings = pStrings; |
27 | 148 | SetDefaultStrings(pStrings, defaultValue); |
28 | 148 | return true; |
29 | 148 | } |
30 | | |
31 | | void SkFlagInfo::SetDefaultStrings(CommandLineFlags::StringArray* pStrings, |
32 | 148 | const char* defaultValue) { |
33 | 148 | pStrings->reset(); |
34 | 148 | if (nullptr == defaultValue) { |
35 | 0 | return; |
36 | 0 | } |
37 | | // If default is "", leave the array empty. |
38 | 148 | size_t defaultLength = strlen(defaultValue); |
39 | 148 | if (defaultLength > 0) { |
40 | 74 | const char* const defaultEnd = defaultValue + defaultLength; |
41 | 74 | const char* begin = defaultValue; |
42 | 148 | while (true) { |
43 | 148 | while (begin < defaultEnd && ' ' == *begin) { |
44 | 0 | begin++; |
45 | 0 | } |
46 | 148 | if (begin < defaultEnd) { |
47 | 74 | const char* end = begin + 1; |
48 | 666 | while (end < defaultEnd && ' ' != *end) { |
49 | 592 | end++; |
50 | 592 | } |
51 | 74 | size_t length = end - begin; |
52 | 74 | pStrings->append(begin, length); |
53 | 74 | begin = end + 1; |
54 | 74 | } else { |
55 | 74 | break; |
56 | 74 | } |
57 | 148 | } |
58 | 74 | } |
59 | 148 | } |
60 | | |
61 | 0 | static bool string_is_in(const char* target, const char* set[], size_t len) { |
62 | 0 | for (size_t i = 0; i < len; i++) { |
63 | 0 | if (0 == strcmp(target, set[i])) { |
64 | 0 | return true; |
65 | 0 | } |
66 | 0 | } |
67 | 0 | return false; |
68 | 0 | } |
69 | | |
70 | | /** |
71 | | * Check to see whether string represents a boolean value. |
72 | | * @param string C style string to parse. |
73 | | * @param result Pointer to a boolean which will be set to the value in the string, if the |
74 | | * string represents a boolean. |
75 | | * @param boolean True if the string represents a boolean, false otherwise. |
76 | | */ |
77 | 0 | static bool parse_bool_arg(const char* string, bool* result) { |
78 | 0 | static const char* trueValues[] = {"1", "TRUE", "true"}; |
79 | 0 | if (string_is_in(string, trueValues, std::size(trueValues))) { |
80 | 0 | *result = true; |
81 | 0 | return true; |
82 | 0 | } |
83 | 0 | static const char* falseValues[] = {"0", "FALSE", "false"}; |
84 | 0 | if (string_is_in(string, falseValues, std::size(falseValues))) { |
85 | 0 | *result = false; |
86 | 0 | return true; |
87 | 0 | } |
88 | 0 | SkDebugf("Parameter \"%s\" not supported.\n", string); |
89 | 0 | return false; |
90 | 0 | } |
91 | | |
92 | 0 | bool SkFlagInfo::match(const char* string) { |
93 | 0 | if (SkStrStartsWith(string, '-') && strlen(string) > 1) { |
94 | 0 | string++; |
95 | 0 | const SkString* compareName; |
96 | 0 | if (SkStrStartsWith(string, '-') && strlen(string) > 1) { |
97 | 0 | string++; |
98 | | // There were two dashes. Compare against full name. |
99 | 0 | compareName = &fName; |
100 | 0 | } else { |
101 | | // One dash. Compare against the short name. |
102 | 0 | compareName = &fShortName; |
103 | 0 | } |
104 | 0 | if (kBool_FlagType == fFlagType) { |
105 | | // In this case, go ahead and set the value. |
106 | 0 | if (compareName->equals(string)) { |
107 | 0 | *fBoolValue = true; |
108 | 0 | return true; |
109 | 0 | } |
110 | 0 | if (SkStrStartsWith(string, "no") && strlen(string) > 2) { |
111 | 0 | string += 2; |
112 | | // Only allow "no" to be prepended to the full name. |
113 | 0 | if (fName.equals(string)) { |
114 | 0 | *fBoolValue = false; |
115 | 0 | return true; |
116 | 0 | } |
117 | 0 | return false; |
118 | 0 | } |
119 | 0 | int equalIndex = SkStrFind(string, "="); |
120 | 0 | if (equalIndex > 0) { |
121 | | // The string has an equal sign. Check to see if the string matches. |
122 | 0 | SkString flag(string, equalIndex); |
123 | 0 | if (flag.equals(*compareName)) { |
124 | | // Check to see if the remainder beyond the equal sign is true or false: |
125 | 0 | string += equalIndex + 1; |
126 | 0 | parse_bool_arg(string, fBoolValue); |
127 | 0 | return true; |
128 | 0 | } else { |
129 | 0 | return false; |
130 | 0 | } |
131 | 0 | } |
132 | 0 | } |
133 | 0 | return compareName->equals(string); |
134 | 0 | } |
135 | | |
136 | | // Has no dash |
137 | 0 | return false; |
138 | 0 | } |
139 | | |
140 | | SkFlagInfo* CommandLineFlags::gHead; |
141 | | SkString CommandLineFlags::gUsage; |
142 | | |
143 | 0 | void CommandLineFlags::SetUsage(const char* usage) { gUsage.set(usage); } |
144 | | |
145 | 0 | void CommandLineFlags::PrintUsage() { SkDebugf("%s", gUsage.c_str()); } |
146 | | |
147 | | // Maximum line length for the help message. |
148 | 0 | #define LINE_LENGTH 72 |
149 | | |
150 | 0 | static void print_indented(const SkString& text) { |
151 | 0 | size_t length = text.size(); |
152 | 0 | const char* currLine = text.c_str(); |
153 | 0 | const char* stop = currLine + length; |
154 | 0 | while (currLine < stop) { |
155 | 0 | int lineBreak = SkStrFind(currLine, "\n"); |
156 | 0 | if (lineBreak < 0) { |
157 | 0 | lineBreak = static_cast<int>(strlen(currLine)); |
158 | 0 | } |
159 | 0 | if (lineBreak > LINE_LENGTH) { |
160 | | // No line break within line length. Will need to insert one. |
161 | | // Find a space before the line break. |
162 | 0 | int spaceIndex = LINE_LENGTH - 1; |
163 | 0 | while (spaceIndex > 0 && currLine[spaceIndex] != ' ') { |
164 | 0 | spaceIndex--; |
165 | 0 | } |
166 | 0 | int gap; |
167 | 0 | if (0 == spaceIndex) { |
168 | | // No spaces on the entire line. Go ahead and break mid word. |
169 | 0 | spaceIndex = LINE_LENGTH; |
170 | 0 | gap = 0; |
171 | 0 | } else { |
172 | | // Skip the space on the next line |
173 | 0 | gap = 1; |
174 | 0 | } |
175 | 0 | SkDebugf(" %.*s\n", spaceIndex, currLine); |
176 | 0 | currLine += spaceIndex + gap; |
177 | 0 | } else { |
178 | | // the line break is within the limit. Break there. |
179 | 0 | lineBreak++; |
180 | 0 | SkDebugf(" %.*s", lineBreak, currLine); |
181 | 0 | currLine += lineBreak; |
182 | 0 | } |
183 | 0 | } |
184 | 0 | } |
185 | | |
186 | 0 | static void print_help_for_flag(const SkFlagInfo* flag) { |
187 | 0 | SkDebugf(" --%s", flag->name().c_str()); |
188 | 0 | const SkString& shortName = flag->shortName(); |
189 | 0 | if (shortName.size() > 0) { |
190 | 0 | SkDebugf(" or -%s", shortName.c_str()); |
191 | 0 | } |
192 | 0 | SkDebugf(":\ttype: %s", flag->typeAsString().c_str()); |
193 | 0 | if (flag->defaultValue().size() > 0) { |
194 | 0 | SkDebugf("\tdefault: %s", flag->defaultValue().c_str()); |
195 | 0 | } |
196 | 0 | SkDebugf("\n"); |
197 | 0 | const SkString& help = flag->help(); |
198 | 0 | print_indented(help); |
199 | 0 | SkDebugf("\n"); |
200 | 0 | } |
201 | 0 | static void print_extended_help_for_flag(const SkFlagInfo* flag) { |
202 | 0 | print_help_for_flag(flag); |
203 | 0 | print_indented(flag->extendedHelp()); |
204 | 0 | SkDebugf("\n"); |
205 | 0 | } |
206 | | |
207 | | namespace { |
208 | | struct CompareFlagsByName { |
209 | 0 | bool operator()(SkFlagInfo* a, SkFlagInfo* b) const { |
210 | 0 | return strcmp(a->name().c_str(), b->name().c_str()) < 0; |
211 | 0 | } |
212 | | }; |
213 | | } // namespace |
214 | | |
215 | 0 | void CommandLineFlags::Parse(int argc, const char* const* argv) { |
216 | | // Only allow calling this function once. |
217 | 0 | static bool gOnce; |
218 | 0 | if (gOnce) { |
219 | 0 | SkDebugf("Parse should only be called once at the beginning of main!\n"); |
220 | 0 | SkASSERT(false); |
221 | 0 | return; |
222 | 0 | } |
223 | 0 | gOnce = true; |
224 | |
|
225 | 0 | bool helpPrinted = false; |
226 | 0 | bool flagsPrinted = false; |
227 | | // Loop over argv, starting with 1, since the first is just the name of the program. |
228 | 0 | for (int i = 1; i < argc; i++) { |
229 | 0 | if (0 == strcmp("-h", argv[i]) || 0 == strcmp("--help", argv[i])) { |
230 | | // Print help message. |
231 | 0 | SkTDArray<const char*> helpFlags; |
232 | 0 | for (int j = i + 1; j < argc; j++) { |
233 | 0 | if (SkStrStartsWith(argv[j], '-')) { |
234 | 0 | break; |
235 | 0 | } |
236 | 0 | helpFlags.append(1, &argv[j]); |
237 | 0 | } |
238 | 0 | if (0 == helpFlags.size()) { |
239 | | // Only print general help message if help for specific flags is not requested. |
240 | 0 | SkDebugf("%s\n%s\n", argv[0], gUsage.c_str()); |
241 | 0 | } |
242 | 0 | if (!flagsPrinted) { |
243 | 0 | SkDebugf("Flags:\n"); |
244 | 0 | flagsPrinted = true; |
245 | 0 | } |
246 | 0 | if (0 == helpFlags.size()) { |
247 | | // If no flags followed --help, print them all |
248 | 0 | SkTDArray<SkFlagInfo*> allFlags; |
249 | 0 | for (SkFlagInfo* flag = CommandLineFlags::gHead; flag; flag = flag->next()) { |
250 | 0 | allFlags.push_back(flag); |
251 | 0 | } |
252 | 0 | SkTQSort(allFlags.begin(), allFlags.end(), CompareFlagsByName()); |
253 | 0 | for (SkFlagInfo* flag : allFlags) { |
254 | 0 | print_help_for_flag(flag); |
255 | 0 | if (flag->extendedHelp().size() > 0) { |
256 | 0 | SkDebugf(" Use '--help %s' for more information.\n", |
257 | 0 | flag->name().c_str()); |
258 | 0 | } |
259 | 0 | } |
260 | 0 | } else { |
261 | 0 | for (SkFlagInfo* flag = CommandLineFlags::gHead; flag; flag = flag->next()) { |
262 | 0 | for (int k = 0; k < helpFlags.size(); k++) { |
263 | 0 | if (flag->name().equals(helpFlags[k]) || |
264 | 0 | flag->shortName().equals(helpFlags[k])) { |
265 | 0 | print_extended_help_for_flag(flag); |
266 | 0 | helpFlags.remove(k); |
267 | 0 | break; |
268 | 0 | } |
269 | 0 | } |
270 | 0 | } |
271 | 0 | } |
272 | 0 | if (helpFlags.size() > 0) { |
273 | 0 | SkDebugf("Requested help for unrecognized flags:\n"); |
274 | 0 | for (int k = 0; k < helpFlags.size(); k++) { |
275 | 0 | SkDebugf(" --%s\n", helpFlags[k]); |
276 | 0 | } |
277 | 0 | } |
278 | 0 | helpPrinted = true; |
279 | 0 | } |
280 | 0 | if (!helpPrinted) { |
281 | 0 | SkFlagInfo* matchedFlag = nullptr; |
282 | 0 | SkFlagInfo* flag = gHead; |
283 | 0 | int startI = i; |
284 | 0 | while (flag != nullptr) { |
285 | 0 | if (flag->match(argv[startI])) { |
286 | 0 | i = startI; |
287 | 0 | if (matchedFlag) { |
288 | | // Don't redefine the same flag with different types. |
289 | 0 | SkASSERT(matchedFlag->getFlagType() == flag->getFlagType()); |
290 | 0 | } else { |
291 | 0 | matchedFlag = flag; |
292 | 0 | } |
293 | 0 | switch (flag->getFlagType()) { |
294 | 0 | case SkFlagInfo::kBool_FlagType: |
295 | | // Can be handled by match, above, but can also be set by the next |
296 | | // string. |
297 | 0 | if (i + 1 < argc && !SkStrStartsWith(argv[i + 1], '-')) { |
298 | 0 | i++; |
299 | 0 | bool value; |
300 | 0 | if (parse_bool_arg(argv[i], &value)) { |
301 | 0 | flag->setBool(value); |
302 | 0 | } |
303 | 0 | } |
304 | 0 | break; |
305 | 0 | case SkFlagInfo::kString_FlagType: |
306 | 0 | flag->resetStrings(); |
307 | | // Add all arguments until another flag is reached. |
308 | 0 | while (i + 1 < argc) { |
309 | 0 | char* end = nullptr; |
310 | | // Negative numbers aren't flags. |
311 | 0 | ignore_result(strtod(argv[i + 1], &end)); |
312 | 0 | if (end == argv[i + 1] && SkStrStartsWith(argv[i + 1], '-')) { |
313 | 0 | break; |
314 | 0 | } |
315 | 0 | i++; |
316 | 0 | flag->append(argv[i]); |
317 | 0 | } |
318 | 0 | break; |
319 | 0 | case SkFlagInfo::kInt_FlagType: |
320 | 0 | i++; |
321 | 0 | flag->setInt(atoi(argv[i])); |
322 | 0 | break; |
323 | 0 | case SkFlagInfo::kDouble_FlagType: |
324 | 0 | i++; |
325 | 0 | flag->setDouble(atof(argv[i])); |
326 | 0 | break; |
327 | 0 | default: SkDEBUGFAIL("Invalid flag type"); |
328 | 0 | } |
329 | 0 | } |
330 | 0 | flag = flag->next(); |
331 | 0 | } |
332 | 0 | if (!matchedFlag) { |
333 | | #if defined(SK_BUILD_FOR_MAC) |
334 | | if (SkStrStartsWith(argv[i], "NSDocumentRevisions") || |
335 | | SkStrStartsWith(argv[i], "-NSDocumentRevisions")) { |
336 | | i++; // skip YES |
337 | | } else |
338 | | #endif |
339 | 0 | SkDebugf("Got unknown flag '%s'. Exiting.\n", argv[i]); |
340 | 0 | exit(-1); |
341 | 0 | } |
342 | 0 | } |
343 | 0 | } |
344 | | // Since all of the flags have been set, release the memory used by each |
345 | | // flag. FLAGS_x can still be used after this. |
346 | 0 | SkFlagInfo* flag = gHead; |
347 | 0 | gHead = nullptr; |
348 | 0 | while (flag != nullptr) { |
349 | 0 | SkFlagInfo* next = flag->next(); |
350 | 0 | delete flag; |
351 | 0 | flag = next; |
352 | 0 | } |
353 | 0 | if (helpPrinted) { |
354 | 0 | exit(0); |
355 | 0 | } |
356 | 0 | } Unexecuted instantiation: CommandLineFlags::Parse(int, char const* const*) Unexecuted instantiation: CommandLineFlags::Parse(int, char const* const*) |
357 | | |
358 | | namespace { |
359 | | |
360 | 0 | template <typename Strings> bool ShouldSkipImpl(const Strings& strings, const char* name) { |
361 | 0 | int count = strings.size(); |
362 | 0 | size_t testLen = strlen(name); |
363 | 0 | bool anyExclude = count == 0; |
364 | 0 | for (int i = 0; i < strings.size(); ++i) { |
365 | 0 | const char* matchName = strings[i]; |
366 | 0 | size_t matchLen = strlen(matchName); |
367 | 0 | bool matchExclude, matchStart, matchEnd; |
368 | 0 | if ((matchExclude = matchName[0] == '~')) { |
369 | 0 | anyExclude = true; |
370 | 0 | matchName++; |
371 | 0 | matchLen--; |
372 | 0 | } |
373 | 0 | if ((matchStart = matchName[0] == '^')) { |
374 | 0 | matchName++; |
375 | 0 | matchLen--; |
376 | 0 | } |
377 | 0 | if ((matchEnd = matchName[matchLen - 1] == '$')) { |
378 | 0 | matchLen--; |
379 | 0 | } |
380 | 0 | if (matchStart |
381 | 0 | ? (!matchEnd || matchLen == testLen) && strncmp(name, matchName, matchLen) == 0 |
382 | 0 | : matchEnd |
383 | 0 | ? matchLen <= testLen && |
384 | 0 | strncmp(name + testLen - matchLen, matchName, matchLen) == 0 |
385 | 0 | : strstr(name, matchName) != nullptr) { |
386 | 0 | return matchExclude; |
387 | 0 | } |
388 | 0 | } |
389 | 0 | return !anyExclude; |
390 | 0 | } Unexecuted instantiation: CommandLineFlags.cpp:bool (anonymous namespace)::ShouldSkipImpl<SkTDArray<char const*> >(SkTDArray<char const*> const&, char const*) Unexecuted instantiation: CommandLineFlags.cpp:bool (anonymous namespace)::ShouldSkipImpl<CommandLineFlags::StringArray>(CommandLineFlags::StringArray const&, char const*) |
391 | | |
392 | | } // namespace |
393 | | |
394 | 0 | bool CommandLineFlags::ShouldSkip(const SkTDArray<const char*>& strings, const char* name) { |
395 | 0 | return ShouldSkipImpl(strings, name); |
396 | 0 | } |
397 | 0 | bool CommandLineFlags::ShouldSkip(const StringArray& strings, const char* name) { |
398 | 0 | return ShouldSkipImpl(strings, name); |
399 | 0 | } |