blob: 7420d2643e9391ca904220319d448b88bf6306dc [file] [log] [blame]
scroggo@google.com161e1ba2013-03-04 16:41:06 +00001/*
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
scroggo@google.comd9ba9a02013-03-21 19:43:15 +00008#include "SkCommandLineFlags.h"
scroggo@google.comb7dbf632013-04-23 15:38:09 +00009#include "SkTDArray.h"
scroggo@google.com161e1ba2013-03-04 16:41:06 +000010
scroggo@google.com58104a92013-04-24 19:25:26 +000011bool 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
22void SkFlagInfo::SetDefaultStrings(SkCommandLineFlags::StringArray* pStrings,
23 const char* defaultValue) {
24 pStrings->reset();
25 // If default is "", leave the array empty.
26 size_t defaultLength = strlen(defaultValue);
27 if (defaultLength > 0) {
28 const char* const defaultEnd = defaultValue + defaultLength;
29 const char* begin = defaultValue;
30 while (true) {
31 while (begin < defaultEnd && ' ' == *begin) {
32 begin++;
33 }
34 if (begin < defaultEnd) {
35 const char* end = begin + 1;
36 while (end < defaultEnd && ' ' != *end) {
37 end++;
38 }
39 size_t length = end - begin;
40 pStrings->append(begin, length);
41 begin = end + 1;
42 } else {
43 break;
44 }
45 }
46 }
47}
48
scroggo@google.com5dc4ca12013-03-21 13:10:59 +000049static bool string_is_in(const char* target, const char* set[], size_t len) {
50 for (size_t i = 0; i < len; i++) {
51 if (0 == strcmp(target, set[i])) {
52 return true;
53 }
54 }
55 return false;
56}
57
58/**
59 * Check to see whether string represents a boolean value.
60 * @param string C style string to parse.
61 * @param result Pointer to a boolean which will be set to the value in the string, if the
62 * string represents a boolean.
63 * @param boolean True if the string represents a boolean, false otherwise.
64 */
65static bool parse_bool_arg(const char* string, bool* result) {
66 static const char* trueValues[] = { "1", "TRUE", "true" };
67 if (string_is_in(string, trueValues, SK_ARRAY_COUNT(trueValues))) {
68 *result = true;
69 return true;
70 }
71 static const char* falseValues[] = { "0", "FALSE", "false" };
72 if (string_is_in(string, falseValues, SK_ARRAY_COUNT(falseValues))) {
73 *result = false;
74 return true;
75 }
76 SkDebugf("Parameter \"%s\" not supported.\n", string);
77 return false;
78}
79
80bool SkFlagInfo::match(const char* string) {
81 if (SkStrStartsWith(string, '-') && strlen(string) > 1) {
82 string++;
scroggo@google.com604e0c22013-04-09 21:25:46 +000083 const SkString* compareName;
scroggo@google.com5dc4ca12013-03-21 13:10:59 +000084 if (SkStrStartsWith(string, '-') && strlen(string) > 1) {
85 string++;
scroggo@google.com604e0c22013-04-09 21:25:46 +000086 // There were two dashes. Compare against full name.
87 compareName = &fName;
88 } else {
89 // One dash. Compare against the short name.
90 compareName = &fShortName;
scroggo@google.com5dc4ca12013-03-21 13:10:59 +000091 }
92 if (kBool_FlagType == fFlagType) {
93 // In this case, go ahead and set the value.
scroggo@google.com604e0c22013-04-09 21:25:46 +000094 if (compareName->equals(string)) {
scroggo@google.com5dc4ca12013-03-21 13:10:59 +000095 *fBoolValue = true;
96 return true;
97 }
98 if (SkStrStartsWith(string, "no") && strlen(string) > 2) {
99 string += 2;
scroggo@google.com604e0c22013-04-09 21:25:46 +0000100 // Only allow "no" to be prepended to the full name.
101 if (fName.equals(string)) {
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000102 *fBoolValue = false;
103 return true;
104 }
105 return false;
106 }
107 int equalIndex = SkStrFind(string, "=");
108 if (equalIndex > 0) {
109 // The string has an equal sign. Check to see if the string matches.
110 SkString flag(string, equalIndex);
scroggo@google.com604e0c22013-04-09 21:25:46 +0000111 if (flag.equals(*compareName)) {
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000112 // Check to see if the remainder beyond the equal sign is true or false:
113 string += equalIndex + 1;
114 parse_bool_arg(string, fBoolValue);
115 return true;
scroggo@google.com604e0c22013-04-09 21:25:46 +0000116 } else {
117 return false;
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000118 }
119 }
120 }
scroggo@google.com604e0c22013-04-09 21:25:46 +0000121 return compareName->equals(string);
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000122 } else {
123 // Has no dash
124 return false;
125 }
126 return false;
127}
128
scroggo@google.comd9ba9a02013-03-21 19:43:15 +0000129SkFlagInfo* SkCommandLineFlags::gHead;
130SkString SkCommandLineFlags::gUsage;
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000131
scroggo@google.comd9ba9a02013-03-21 19:43:15 +0000132void SkCommandLineFlags::SetUsage(const char* usage) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000133 gUsage.set(usage);
134}
135
136// Maximum line length for the help message.
137#define LINE_LENGTH 80
138
scroggo@google.com8366df02013-03-20 19:50:41 +0000139static void print_help_for_flag(const SkFlagInfo* flag) {
140 SkDebugf("\t--%s", flag->name().c_str());
141 const SkString& shortName = flag->shortName();
142 if (shortName.size() > 0) {
143 SkDebugf(" or -%s", shortName.c_str());
144 }
145 SkDebugf(":\ttype: %s", flag->typeAsString().c_str());
146 if (flag->defaultValue().size() > 0) {
147 SkDebugf("\tdefault: %s", flag->defaultValue().c_str());
148 }
149 SkDebugf("\n");
150 const SkString& help = flag->help();
151 size_t length = help.size();
152 const char* currLine = help.c_str();
153 const char* stop = currLine + length;
154 while (currLine < stop) {
155 if (strlen(currLine) < LINE_LENGTH) {
156 // Only one line length's worth of text left.
157 SkDebugf("\t\t%s\n", currLine);
158 break;
159 }
160 int lineBreak = SkStrFind(currLine, "\n");
161 if (lineBreak < 0 || lineBreak > LINE_LENGTH) {
162 // No line break within line length. Will need to insert one.
163 // Find a space before the line break.
164 int spaceIndex = LINE_LENGTH - 1;
165 while (spaceIndex > 0 && currLine[spaceIndex] != ' ') {
166 spaceIndex--;
167 }
168 int gap;
169 if (0 == spaceIndex) {
170 // No spaces on the entire line. Go ahead and break mid word.
171 spaceIndex = LINE_LENGTH;
172 gap = 0;
173 } else {
174 // Skip the space on the next line
175 gap = 1;
176 }
177 SkDebugf("\t\t%.*s\n", spaceIndex, currLine);
178 currLine += spaceIndex + gap;
179 } else {
180 // the line break is within the limit. Break there.
181 lineBreak++;
182 SkDebugf("\t\t%.*s", lineBreak, currLine);
183 currLine += lineBreak;
184 }
185 }
186 SkDebugf("\n");
187}
188
scroggo@google.comd9ba9a02013-03-21 19:43:15 +0000189void SkCommandLineFlags::Parse(int argc, char** argv) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000190 // Only allow calling this function once.
191 static bool gOnce;
192 if (gOnce) {
scroggo@google.com604e0c22013-04-09 21:25:46 +0000193 SkDebugf("Parse should only be called once at the beginning of main!\n");
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000194 SkASSERT(false);
195 return;
196 }
197 gOnce = true;
198
199 bool helpPrinted = false;
200 // Loop over argv, starting with 1, since the first is just the name of the program.
201 for (int i = 1; i < argc; i++) {
scroggo@google.com604e0c22013-04-09 21:25:46 +0000202 if (0 == strcmp("-h", argv[i]) || 0 == strcmp("--help", argv[i])) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000203 // Print help message.
scroggo@google.com8366df02013-03-20 19:50:41 +0000204 SkTDArray<const char*> helpFlags;
205 for (int j = i + 1; j < argc; j++) {
206 if (SkStrStartsWith(argv[j], '-')) {
207 break;
208 }
209 helpFlags.append(1, &argv[j]);
210 }
211 if (0 == helpFlags.count()) {
212 // Only print general help message if help for specific flags is not requested.
213 SkDebugf("%s\n%s\n", argv[0], gUsage.c_str());
214 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000215 SkDebugf("Flags:\n");
scroggo@google.comd9ba9a02013-03-21 19:43:15 +0000216 SkFlagInfo* flag = SkCommandLineFlags::gHead;
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000217 while (flag != NULL) {
scroggo@google.com8366df02013-03-20 19:50:41 +0000218 // If no flags followed --help, print them all
219 bool printFlag = 0 == helpFlags.count();
220 if (!printFlag) {
221 for (int k = 0; k < helpFlags.count(); k++) {
222 if (flag->name().equals(helpFlags[k]) ||
223 flag->shortName().equals(helpFlags[k])) {
224 printFlag = true;
225 helpFlags.remove(k);
226 break;
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000227 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000228 }
229 }
scroggo@google.com8366df02013-03-20 19:50:41 +0000230 if (printFlag) {
231 print_help_for_flag(flag);
232 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000233 flag = flag->next();
234 }
scroggo@google.com8366df02013-03-20 19:50:41 +0000235 if (helpFlags.count() > 0) {
236 SkDebugf("Requested help for unrecognized flags:\n");
237 for (int k = 0; k < helpFlags.count(); k++) {
238 SkDebugf("\t--%s\n", helpFlags[k]);
239 }
240 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000241 helpPrinted = true;
242 }
243 if (!helpPrinted) {
244 bool flagMatched = false;
245 SkFlagInfo* flag = gHead;
246 while (flag != NULL) {
247 if (flag->match(argv[i])) {
248 flagMatched = true;
249 switch (flag->getFlagType()) {
250 case SkFlagInfo::kBool_FlagType:
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000251 // Can be handled by match, above, but can also be set by the next
252 // string.
253 if (i+1 < argc && !SkStrStartsWith(argv[i+1], '-')) {
254 i++;
255 bool value;
256 if (parse_bool_arg(argv[i], &value)) {
257 flag->setBool(value);
258 }
259 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000260 break;
261 case SkFlagInfo::kString_FlagType:
262 flag->resetStrings();
263 // Add all arguments until another flag is reached.
264 while (i+1 < argc && !SkStrStartsWith(argv[i+1], '-')) {
265 i++;
266 flag->append(argv[i]);
267 }
268 break;
269 case SkFlagInfo::kInt_FlagType:
270 i++;
271 flag->setInt(atoi(argv[i]));
272 break;
273 case SkFlagInfo::kDouble_FlagType:
274 i++;
275 flag->setDouble(atof(argv[i]));
276 break;
277 default:
278 SkASSERT(!"Invalid flag type");
279 }
280 break;
281 }
282 flag = flag->next();
283 }
284 if (!flagMatched) {
scroggo@google.com0f2cd172013-03-20 20:04:27 +0000285 SkDebugf("Got unknown flag \"%s\". Exiting.\n", argv[i]);
286 exit(-1);
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000287 }
288 }
289 }
290 // Since all of the flags have been set, release the memory used by each
291 // flag. FLAGS_x can still be used after this.
292 SkFlagInfo* flag = gHead;
293 gHead = NULL;
294 while (flag != NULL) {
295 SkFlagInfo* next = flag->next();
296 SkDELETE(flag);
297 flag = next;
298 }
299 if (helpPrinted) {
300 exit(0);
301 }
302}