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