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