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