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