blob: 6fcc318d25312ad6942f675f45fd7899b0cc2997 [file] [log] [blame]
humper@google.com7af56be2013-01-14 18:49:19 +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
8#include "SkRTConf.h"
9#include "SkOSFile.h"
10
11SkRTConfRegistry::SkRTConfRegistry(): fConfs(100) {
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +000012
humper@google.com7af56be2013-01-14 18:49:19 +000013 SkFILE *fp = sk_fopen(configFileLocation(), kRead_SkFILE_Flag);
14
15 if (!fp) {
16 return;
17 }
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +000018
humper@google.com18a48c32013-01-14 19:42:08 +000019 char line[1024];
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +000020
humper@google.com7af56be2013-01-14 18:49:19 +000021 while (!sk_feof(fp)) {
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +000022
humper@google.com18a48c32013-01-14 19:42:08 +000023 if (!sk_fgets(line, sizeof(line), fp)) {
24 break;
25 }
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +000026
humper@google.com7af56be2013-01-14 18:49:19 +000027 char *commentptr = strchr(line, '#');
28 if (commentptr == line) {
29 continue;
30 }
31 if (NULL != commentptr) {
humper@google.com18a48c32013-01-14 19:42:08 +000032 *commentptr = '\0';
humper@google.com7af56be2013-01-14 18:49:19 +000033 }
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +000034
humper@google.com18a48c32013-01-14 19:42:08 +000035 char sep[] = " \t\r\n";
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +000036
humper@google.com7af56be2013-01-14 18:49:19 +000037 char *keyptr = strtok(line, sep);
38 if (!keyptr) {
39 continue;
40 }
41
42 char *valptr = strtok(NULL, sep);
43 if (!valptr) {
44 continue;
45 }
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +000046
humper@google.com7af56be2013-01-14 18:49:19 +000047 SkString* key = new SkString(keyptr);
48 SkString* val = new SkString(valptr);
49
50 fConfigFileKeys.append(1, &key);
51 fConfigFileValues.append(1, &val);
52 }
53 sk_fclose(fp);
54}
55
56const char *SkRTConfRegistry::configFileLocation() const {
57 return "skia.conf"; // for now -- should probably do something fancier like home directories or whatever.
58}
59
60// dump all known runtime config options to the file with their default values.
61// to trigger this, make a config file of zero size.
62void SkRTConfRegistry::possiblyDumpFile() const {
63 const char *path = configFileLocation();
64 SkFILE *fp = sk_fopen(path, kRead_SkFILE_Flag);
65 if (!fp) {
66 return;
67 }
68 size_t configFileSize = sk_fgetsize(fp);
69 if (configFileSize == 0) {
70 printAll(path);
71 }
72 sk_fclose(fp);
73}
74
75// Run through every provided configuration option and print a warning if the user hasn't
76// declared a correponding configuration object somewhere.
77void SkRTConfRegistry::validate() const {
78 for (int i = 0 ; i < fConfigFileKeys.count() ; i++) {
humper@google.com2b71c432013-05-20 17:14:40 +000079 if (!fConfs.find(fConfigFileKeys[i]->c_str())) {
humper@google.com7af56be2013-01-14 18:49:19 +000080 SkDebugf("WARNING: You have config value %s in your configuration file, but I've never heard of that.\n", fConfigFileKeys[i]->c_str());
81 }
82 }
83}
84
85void SkRTConfRegistry::printAll(const char *fname) const {
86 SkWStream *o;
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +000087
humper@google.com7af56be2013-01-14 18:49:19 +000088 if (NULL != fname) {
89 o = new SkFILEWStream(fname);
90 } else {
91 o = new SkDebugWStream();
92 }
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +000093
humper@google.com7af56be2013-01-14 18:49:19 +000094 ConfMap::Iter iter(fConfs);
95 SkTDArray<SkRTConfBase *> *confArray;
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +000096
humper@google.com7af56be2013-01-14 18:49:19 +000097 while (iter.next(&confArray)) {
98 if (confArray->getAt(0)->isDefault()) {
99 o->writeText("# ");
100 }
101 confArray->getAt(0)->print(o);
102 o->newline();
103 }
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +0000104
humper@google.com7af56be2013-01-14 18:49:19 +0000105 delete o;
106}
107
108void SkRTConfRegistry::printNonDefault(const char *fname) const {
109 SkWStream *o;
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +0000110
humper@google.com7af56be2013-01-14 18:49:19 +0000111 if (NULL != fname) {
112 o = new SkFILEWStream(fname);
113 } else {
114 o = new SkDebugWStream();
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +0000115 }
humper@google.com7af56be2013-01-14 18:49:19 +0000116 ConfMap::Iter iter(fConfs);
117 SkTDArray<SkRTConfBase *> *confArray;
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +0000118
humper@google.com7af56be2013-01-14 18:49:19 +0000119 while (iter.next(&confArray)) {
120 if (!confArray->getAt(0)->isDefault()) {
121 confArray->getAt(0)->print(o);
122 o->newline();
123 }
124 }
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +0000125
humper@google.com7af56be2013-01-14 18:49:19 +0000126 delete o;
127}
128
129// register a configuration variable after its value has been set by the parser.
130// we maintain a vector of these things instead of just a single one because the
131// user might set the value after initialization time and we need to have
132// all the pointers lying around, not just one.
133void SkRTConfRegistry::registerConf(SkRTConfBase *conf) {
134 SkTDArray<SkRTConfBase *> *confArray;
135 if (fConfs.find(conf->getName(), &confArray)) {
136 if (!conf->equals(confArray->getAt(0))) {
137 SkDebugf("WARNING: Skia config \"%s\" was registered more than once in incompatible ways.\n", conf->getName());
138 } else {
139 confArray->append(1, &conf);
140 }
141 } else {
142 confArray = new SkTDArray<SkRTConfBase *>;
143 confArray->append(1, &conf);
144 fConfs.set(conf->getName(),confArray);
145 }
146}
147
sugoi@google.com8563d302013-03-04 21:03:17 +0000148template <typename T> T doParse(const char *, bool *success ) {
humper@google.com7af56be2013-01-14 18:49:19 +0000149 SkDebugf("WARNING: Invoked non-specialized doParse function...\n");
150 if (success) {
151 *success = false;
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +0000152 }
humper@google.com7af56be2013-01-14 18:49:19 +0000153 return (T) 0;
154}
155
156template<> bool doParse<bool>(const char *s, bool *success) {
157 if (success) {
158 *success = true;
159 }
160 if (!strcmp(s,"1") || !strcmp(s,"true")) {
161 return true;
162 }
163 if (!strcmp(s,"0") || !strcmp(s,"false")) {
164 return false;
165 }
166 if (success) {
167 *success = false;
168 }
169 return false;
170}
171
172template<> const char * doParse<const char *>(const char * s, bool *success) {
173 if (success) {
174 *success = true;
175 }
176 return s;
177}
178
179template<> int doParse<int>(const char * s, bool *success) {
sugoi@google.com8563d302013-03-04 21:03:17 +0000180 if (success) {
181 *success = true;
182 }
humper@google.com7af56be2013-01-14 18:49:19 +0000183 return atoi(s);
184}
185
186template<> unsigned int doParse<unsigned int>(const char * s, bool *success) {
sugoi@google.com8563d302013-03-04 21:03:17 +0000187 if (success) {
188 *success = true;
189 }
humper@google.com7af56be2013-01-14 18:49:19 +0000190 return (unsigned int) atoi(s);
191}
192
193template<> float doParse<float>(const char * s, bool *success) {
sugoi@google.com8563d302013-03-04 21:03:17 +0000194 if (success) {
195 *success = true;
196 }
humper@google.com7af56be2013-01-14 18:49:19 +0000197 return (float) atof(s);
198}
199
200template<> double doParse<double>(const char * s, bool *success) {
sugoi@google.com8563d302013-03-04 21:03:17 +0000201 if (success) {
202 *success = true;
203 }
humper@google.com7af56be2013-01-14 18:49:19 +0000204 return atof(s);
205}
206
207static inline void str_replace(char *s, char search, char replace) {
208 for (char *ptr = s ; *ptr ; ptr++) {
209 if (*ptr == search) {
210 *ptr = replace;
211 }
212 }
213}
214
215template<typename T> bool SkRTConfRegistry::parse(const char *name, T* value) {
216 SkString *str = NULL;
commit-bot@chromium.orgc1bf2de2013-08-14 18:14:37 +0000217 SkString tmp;
humper@google.com7af56be2013-01-14 18:49:19 +0000218
humper@google.com91673aa2013-07-24 21:44:40 +0000219 for (int i = fConfigFileKeys.count() - 1 ; i >= 0; i--) {
humper@google.com7af56be2013-01-14 18:49:19 +0000220 if (fConfigFileKeys[i]->equals(name)) {
221 str = fConfigFileValues[i];
humper@google.com91673aa2013-07-24 21:44:40 +0000222 break;
humper@google.com7af56be2013-01-14 18:49:19 +0000223 }
224 }
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +0000225
humper@google.com7af56be2013-01-14 18:49:19 +0000226 SkString environment_variable("skia.");
227 environment_variable.append(name);
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +0000228
humper@google.com7af56be2013-01-14 18:49:19 +0000229 const char *environment_value = getenv(environment_variable.c_str());
230 if (environment_value) {
commit-bot@chromium.orgc1bf2de2013-08-14 18:14:37 +0000231 if (NULL == str) {
232 str = &tmp;
233 }
humper@google.com7af56be2013-01-14 18:49:19 +0000234 str->set(environment_value);
235 } else {
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +0000236 // apparently my shell doesn't let me have environment variables that
humper@google.com7af56be2013-01-14 18:49:19 +0000237 // have periods in them, so also let the user substitute underscores.
238 SkString underscore_environment_variable("skia_");
239 char *underscore_name = SkStrDup(name);
240 str_replace(underscore_name,'.','_');
241 underscore_environment_variable.append(underscore_name);
242 sk_free(underscore_name);
243 environment_value = getenv(underscore_environment_variable.c_str());
244 if (environment_value) {
commit-bot@chromium.orgc1bf2de2013-08-14 18:14:37 +0000245 if (NULL == str) {
246 str = &tmp;
247 }
humper@google.com7af56be2013-01-14 18:49:19 +0000248 str->set(environment_value);
249 }
250 }
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +0000251
humper@google.com7af56be2013-01-14 18:49:19 +0000252 if (!str) {
253 return false;
254 }
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +0000255
humper@google.com7af56be2013-01-14 18:49:19 +0000256 bool success;
257 T new_value = doParse<T>(str->c_str(),&success);
258 if (success) {
259 *value = new_value;
260 } else {
261 SkDebugf("WARNING: Couldn't parse value \'%s\' for variable \'%s\'\n", str->c_str(), name);
262 }
263 return success;
264}
265
266// need to explicitly instantiate the parsing function for every config type we might have...
267
268template bool SkRTConfRegistry::parse(const char *name, bool *value);
269template bool SkRTConfRegistry::parse(const char *name, int *value);
270template bool SkRTConfRegistry::parse(const char *name, unsigned int *value);
271template bool SkRTConfRegistry::parse(const char *name, float *value);
272template bool SkRTConfRegistry::parse(const char *name, double *value);
273template bool SkRTConfRegistry::parse(const char *name, const char **value);
274
275template <typename T> void SkRTConfRegistry::set(const char *name, T value) {
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +0000276
humper@google.com7af56be2013-01-14 18:49:19 +0000277 SkTDArray<SkRTConfBase *> *confArray;
278 if (!fConfs.find(name, &confArray)) {
279 SkDebugf("WARNING: Attempting to set configuration value \"%s\", but I've never heard of that.\n", name);
280 return;
281 }
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +0000282
humper@google.com7af56be2013-01-14 18:49:19 +0000283 for (SkRTConfBase **confBase = confArray->begin(); confBase != confArray->end(); confBase++) {
humper@google.com6d29eda2013-01-14 19:20:28 +0000284 // static_cast here is okay because there's only one kind of child class.
humper@google.com810ae482013-01-14 20:11:00 +0000285 SkRTConf<T> *concrete = static_cast<SkRTConf<T> *>(*confBase);
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +0000286
humper@google.com7af56be2013-01-14 18:49:19 +0000287 if (concrete) {
288 concrete->set(value);
289 }
290 }
291}
292
293template void SkRTConfRegistry::set(const char *name, bool value);
294template void SkRTConfRegistry::set(const char *name, int value);
295template void SkRTConfRegistry::set(const char *name, unsigned int value);
296template void SkRTConfRegistry::set(const char *name, float value);
297template void SkRTConfRegistry::set(const char *name, double value);
298template void SkRTConfRegistry::set(const char *name, char * value);
299
humper@google.com7af56be2013-01-14 18:49:19 +0000300SkRTConfRegistry &skRTConfRegistry() {
301 static SkRTConfRegistry r;
302 return r;
303}
commit-bot@chromium.orgc1bf2de2013-08-14 18:14:37 +0000304
305
306#ifdef SK_SUPPORT_UNITTEST
307
308#ifdef SK_BUILD_FOR_WIN32
309static void sk_setenv(const char* key, const char* value) {
310 _putenv_s(key, value);
311}
312#else
313static void sk_setenv(const char* key, const char* value) {
314 setenv(key, value, 1);
315}
316#endif
317
318void SkRTConfRegistry::UnitTest() {
319 SkRTConfRegistry registryWithoutContents(true);
320
321 sk_setenv("skia_nonexistent_item", "132");
322 int result = 0;
323 registryWithoutContents.parse("nonexistent.item", &result);
324 SkASSERT(result == 132);
325}
326
327SkRTConfRegistry::SkRTConfRegistry(bool)
328 : fConfs(100) {
329}
330#endif