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