blob: d634edb31100dafa02404bd24502b9ee8f5d1cb7 [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.com5dc4ca12013-03-21 13:10:59 +000011static bool string_is_in(const char* target, const char* set[], size_t len) {
12 for (size_t i = 0; i < len; i++) {
13 if (0 == strcmp(target, set[i])) {
14 return true;
15 }
16 }
17 return false;
18}
19
20/**
21 * Check to see whether string represents a boolean value.
22 * @param string C style string to parse.
23 * @param result Pointer to a boolean which will be set to the value in the string, if the
24 * string represents a boolean.
25 * @param boolean True if the string represents a boolean, false otherwise.
26 */
27static bool parse_bool_arg(const char* string, bool* result) {
28 static const char* trueValues[] = { "1", "TRUE", "true" };
29 if (string_is_in(string, trueValues, SK_ARRAY_COUNT(trueValues))) {
30 *result = true;
31 return true;
32 }
33 static const char* falseValues[] = { "0", "FALSE", "false" };
34 if (string_is_in(string, falseValues, SK_ARRAY_COUNT(falseValues))) {
35 *result = false;
36 return true;
37 }
38 SkDebugf("Parameter \"%s\" not supported.\n", string);
39 return false;
40}
41
42bool SkFlagInfo::match(const char* string) {
43 if (SkStrStartsWith(string, '-') && strlen(string) > 1) {
44 string++;
scroggo@google.com604e0c22013-04-09 21:25:46 +000045 const SkString* compareName;
scroggo@google.com5dc4ca12013-03-21 13:10:59 +000046 if (SkStrStartsWith(string, '-') && strlen(string) > 1) {
47 string++;
scroggo@google.com604e0c22013-04-09 21:25:46 +000048 // There were two dashes. Compare against full name.
49 compareName = &fName;
50 } else {
51 // One dash. Compare against the short name.
52 compareName = &fShortName;
scroggo@google.com5dc4ca12013-03-21 13:10:59 +000053 }
54 if (kBool_FlagType == fFlagType) {
55 // In this case, go ahead and set the value.
scroggo@google.com604e0c22013-04-09 21:25:46 +000056 if (compareName->equals(string)) {
scroggo@google.com5dc4ca12013-03-21 13:10:59 +000057 *fBoolValue = true;
58 return true;
59 }
60 if (SkStrStartsWith(string, "no") && strlen(string) > 2) {
61 string += 2;
scroggo@google.com604e0c22013-04-09 21:25:46 +000062 // Only allow "no" to be prepended to the full name.
63 if (fName.equals(string)) {
scroggo@google.com5dc4ca12013-03-21 13:10:59 +000064 *fBoolValue = false;
65 return true;
66 }
67 return false;
68 }
69 int equalIndex = SkStrFind(string, "=");
70 if (equalIndex > 0) {
71 // The string has an equal sign. Check to see if the string matches.
72 SkString flag(string, equalIndex);
scroggo@google.com604e0c22013-04-09 21:25:46 +000073 if (flag.equals(*compareName)) {
scroggo@google.com5dc4ca12013-03-21 13:10:59 +000074 // Check to see if the remainder beyond the equal sign is true or false:
75 string += equalIndex + 1;
76 parse_bool_arg(string, fBoolValue);
77 return true;
scroggo@google.com604e0c22013-04-09 21:25:46 +000078 } else {
79 return false;
scroggo@google.com5dc4ca12013-03-21 13:10:59 +000080 }
81 }
82 }
scroggo@google.com604e0c22013-04-09 21:25:46 +000083 return compareName->equals(string);
scroggo@google.com5dc4ca12013-03-21 13:10:59 +000084 } else {
85 // Has no dash
86 return false;
87 }
88 return false;
89}
90
scroggo@google.comd9ba9a02013-03-21 19:43:15 +000091SkFlagInfo* SkCommandLineFlags::gHead;
92SkString SkCommandLineFlags::gUsage;
scroggo@google.com161e1ba2013-03-04 16:41:06 +000093
scroggo@google.comd9ba9a02013-03-21 19:43:15 +000094void SkCommandLineFlags::SetUsage(const char* usage) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +000095 gUsage.set(usage);
96}
97
98// Maximum line length for the help message.
99#define LINE_LENGTH 80
100
scroggo@google.com8366df02013-03-20 19:50:41 +0000101static void print_help_for_flag(const SkFlagInfo* flag) {
102 SkDebugf("\t--%s", flag->name().c_str());
103 const SkString& shortName = flag->shortName();
104 if (shortName.size() > 0) {
105 SkDebugf(" or -%s", shortName.c_str());
106 }
107 SkDebugf(":\ttype: %s", flag->typeAsString().c_str());
108 if (flag->defaultValue().size() > 0) {
109 SkDebugf("\tdefault: %s", flag->defaultValue().c_str());
110 }
111 SkDebugf("\n");
112 const SkString& help = flag->help();
113 size_t length = help.size();
114 const char* currLine = help.c_str();
115 const char* stop = currLine + length;
116 while (currLine < stop) {
117 if (strlen(currLine) < LINE_LENGTH) {
118 // Only one line length's worth of text left.
119 SkDebugf("\t\t%s\n", currLine);
120 break;
121 }
122 int lineBreak = SkStrFind(currLine, "\n");
123 if (lineBreak < 0 || lineBreak > LINE_LENGTH) {
124 // No line break within line length. Will need to insert one.
125 // Find a space before the line break.
126 int spaceIndex = LINE_LENGTH - 1;
127 while (spaceIndex > 0 && currLine[spaceIndex] != ' ') {
128 spaceIndex--;
129 }
130 int gap;
131 if (0 == spaceIndex) {
132 // No spaces on the entire line. Go ahead and break mid word.
133 spaceIndex = LINE_LENGTH;
134 gap = 0;
135 } else {
136 // Skip the space on the next line
137 gap = 1;
138 }
139 SkDebugf("\t\t%.*s\n", spaceIndex, currLine);
140 currLine += spaceIndex + gap;
141 } else {
142 // the line break is within the limit. Break there.
143 lineBreak++;
144 SkDebugf("\t\t%.*s", lineBreak, currLine);
145 currLine += lineBreak;
146 }
147 }
148 SkDebugf("\n");
149}
150
scroggo@google.comd9ba9a02013-03-21 19:43:15 +0000151void SkCommandLineFlags::Parse(int argc, char** argv) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000152 // Only allow calling this function once.
153 static bool gOnce;
154 if (gOnce) {
scroggo@google.com604e0c22013-04-09 21:25:46 +0000155 SkDebugf("Parse should only be called once at the beginning of main!\n");
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000156 SkASSERT(false);
157 return;
158 }
159 gOnce = true;
160
161 bool helpPrinted = false;
162 // Loop over argv, starting with 1, since the first is just the name of the program.
163 for (int i = 1; i < argc; i++) {
scroggo@google.com604e0c22013-04-09 21:25:46 +0000164 if (0 == strcmp("-h", argv[i]) || 0 == strcmp("--help", argv[i])) {
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000165 // Print help message.
scroggo@google.com8366df02013-03-20 19:50:41 +0000166 SkTDArray<const char*> helpFlags;
167 for (int j = i + 1; j < argc; j++) {
168 if (SkStrStartsWith(argv[j], '-')) {
169 break;
170 }
171 helpFlags.append(1, &argv[j]);
172 }
173 if (0 == helpFlags.count()) {
174 // Only print general help message if help for specific flags is not requested.
175 SkDebugf("%s\n%s\n", argv[0], gUsage.c_str());
176 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000177 SkDebugf("Flags:\n");
scroggo@google.comd9ba9a02013-03-21 19:43:15 +0000178 SkFlagInfo* flag = SkCommandLineFlags::gHead;
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000179 while (flag != NULL) {
scroggo@google.com8366df02013-03-20 19:50:41 +0000180 // If no flags followed --help, print them all
181 bool printFlag = 0 == helpFlags.count();
182 if (!printFlag) {
183 for (int k = 0; k < helpFlags.count(); k++) {
184 if (flag->name().equals(helpFlags[k]) ||
185 flag->shortName().equals(helpFlags[k])) {
186 printFlag = true;
187 helpFlags.remove(k);
188 break;
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000189 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000190 }
191 }
scroggo@google.com8366df02013-03-20 19:50:41 +0000192 if (printFlag) {
193 print_help_for_flag(flag);
194 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000195 flag = flag->next();
196 }
scroggo@google.com8366df02013-03-20 19:50:41 +0000197 if (helpFlags.count() > 0) {
198 SkDebugf("Requested help for unrecognized flags:\n");
199 for (int k = 0; k < helpFlags.count(); k++) {
200 SkDebugf("\t--%s\n", helpFlags[k]);
201 }
202 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000203 helpPrinted = true;
204 }
205 if (!helpPrinted) {
206 bool flagMatched = false;
207 SkFlagInfo* flag = gHead;
208 while (flag != NULL) {
209 if (flag->match(argv[i])) {
210 flagMatched = true;
211 switch (flag->getFlagType()) {
212 case SkFlagInfo::kBool_FlagType:
scroggo@google.com5dc4ca12013-03-21 13:10:59 +0000213 // Can be handled by match, above, but can also be set by the next
214 // string.
215 if (i+1 < argc && !SkStrStartsWith(argv[i+1], '-')) {
216 i++;
217 bool value;
218 if (parse_bool_arg(argv[i], &value)) {
219 flag->setBool(value);
220 }
221 }
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000222 break;
223 case SkFlagInfo::kString_FlagType:
224 flag->resetStrings();
225 // Add all arguments until another flag is reached.
226 while (i+1 < argc && !SkStrStartsWith(argv[i+1], '-')) {
227 i++;
228 flag->append(argv[i]);
229 }
230 break;
231 case SkFlagInfo::kInt_FlagType:
232 i++;
233 flag->setInt(atoi(argv[i]));
234 break;
235 case SkFlagInfo::kDouble_FlagType:
236 i++;
237 flag->setDouble(atof(argv[i]));
238 break;
239 default:
240 SkASSERT(!"Invalid flag type");
241 }
242 break;
243 }
244 flag = flag->next();
245 }
246 if (!flagMatched) {
scroggo@google.com0f2cd172013-03-20 20:04:27 +0000247 SkDebugf("Got unknown flag \"%s\". Exiting.\n", argv[i]);
248 exit(-1);
scroggo@google.com161e1ba2013-03-04 16:41:06 +0000249 }
250 }
251 }
252 // Since all of the flags have been set, release the memory used by each
253 // flag. FLAGS_x can still be used after this.
254 SkFlagInfo* flag = gHead;
255 gHead = NULL;
256 while (flag != NULL) {
257 SkFlagInfo* next = flag->next();
258 SkDELETE(flag);
259 flag = next;
260 }
261 if (helpPrinted) {
262 exit(0);
263 }
264}