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