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