blob: 20b8b43b891af2d43554abfb42080c8684508816 [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 }
bsalomon49f085d2014-09-05 13:34:00 -070031 if (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
commit-bot@chromium.orgbf6a6d42014-01-31 17:32:03 +000047 SkString* key = SkNEW_ARGS(SkString,(keyptr));
48 SkString* val = SkNEW_ARGS(SkString,(valptr));
humper@google.com7af56be2013-01-14 18:49:19 +000049
50 fConfigFileKeys.append(1, &key);
51 fConfigFileValues.append(1, &val);
52 }
53 sk_fclose(fp);
54}
55
commit-bot@chromium.orgbf6a6d42014-01-31 17:32:03 +000056SkRTConfRegistry::~SkRTConfRegistry() {
57 ConfMap::Iter iter(fConfs);
58 SkTDArray<SkRTConfBase *> *confArray;
59
60 while (iter.next(&confArray)) {
61 delete confArray;
62 }
63
64 for (int i = 0 ; i < fConfigFileKeys.count() ; i++) {
65 SkDELETE(fConfigFileKeys[i]);
66 SkDELETE(fConfigFileValues[i]);
67 }
68}
69
humper@google.com7af56be2013-01-14 18:49:19 +000070const char *SkRTConfRegistry::configFileLocation() const {
71 return "skia.conf"; // for now -- should probably do something fancier like home directories or whatever.
72}
73
74// dump all known runtime config options to the file with their default values.
75// to trigger this, make a config file of zero size.
76void SkRTConfRegistry::possiblyDumpFile() const {
77 const char *path = configFileLocation();
78 SkFILE *fp = sk_fopen(path, kRead_SkFILE_Flag);
79 if (!fp) {
80 return;
81 }
82 size_t configFileSize = sk_fgetsize(fp);
83 if (configFileSize == 0) {
84 printAll(path);
85 }
86 sk_fclose(fp);
87}
88
89// Run through every provided configuration option and print a warning if the user hasn't
90// declared a correponding configuration object somewhere.
91void SkRTConfRegistry::validate() const {
92 for (int i = 0 ; i < fConfigFileKeys.count() ; i++) {
humper@google.com2b71c432013-05-20 17:14:40 +000093 if (!fConfs.find(fConfigFileKeys[i]->c_str())) {
humper@google.com7af56be2013-01-14 18:49:19 +000094 SkDebugf("WARNING: You have config value %s in your configuration file, but I've never heard of that.\n", fConfigFileKeys[i]->c_str());
95 }
96 }
97}
98
99void SkRTConfRegistry::printAll(const char *fname) const {
100 SkWStream *o;
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +0000101
bsalomon49f085d2014-09-05 13:34:00 -0700102 if (fname) {
humper@google.com7af56be2013-01-14 18:49:19 +0000103 o = new SkFILEWStream(fname);
104 } else {
105 o = new SkDebugWStream();
106 }
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +0000107
humper@google.com7af56be2013-01-14 18:49:19 +0000108 ConfMap::Iter iter(fConfs);
109 SkTDArray<SkRTConfBase *> *confArray;
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +0000110
humper@google.com7af56be2013-01-14 18:49:19 +0000111 while (iter.next(&confArray)) {
112 if (confArray->getAt(0)->isDefault()) {
113 o->writeText("# ");
114 }
115 confArray->getAt(0)->print(o);
116 o->newline();
117 }
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +0000118
humper@google.com7af56be2013-01-14 18:49:19 +0000119 delete o;
120}
121
halcanary@google.com2d1adf22014-01-10 15:00:45 +0000122bool SkRTConfRegistry::hasNonDefault() const {
123 ConfMap::Iter iter(fConfs);
124 SkTDArray<SkRTConfBase *> *confArray;
125 while (iter.next(&confArray)) {
126 if (!confArray->getAt(0)->isDefault()) {
127 return true;
128 }
129 }
130 return false;
131}
132
humper@google.com7af56be2013-01-14 18:49:19 +0000133void SkRTConfRegistry::printNonDefault(const char *fname) const {
134 SkWStream *o;
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +0000135
bsalomon49f085d2014-09-05 13:34:00 -0700136 if (fname) {
humper@google.com7af56be2013-01-14 18:49:19 +0000137 o = new SkFILEWStream(fname);
138 } else {
139 o = new SkDebugWStream();
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +0000140 }
humper@google.com7af56be2013-01-14 18:49:19 +0000141 ConfMap::Iter iter(fConfs);
142 SkTDArray<SkRTConfBase *> *confArray;
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +0000143
humper@google.com7af56be2013-01-14 18:49:19 +0000144 while (iter.next(&confArray)) {
145 if (!confArray->getAt(0)->isDefault()) {
146 confArray->getAt(0)->print(o);
147 o->newline();
148 }
149 }
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +0000150
humper@google.com7af56be2013-01-14 18:49:19 +0000151 delete o;
152}
153
154// register a configuration variable after its value has been set by the parser.
155// we maintain a vector of these things instead of just a single one because the
156// user might set the value after initialization time and we need to have
157// all the pointers lying around, not just one.
158void SkRTConfRegistry::registerConf(SkRTConfBase *conf) {
159 SkTDArray<SkRTConfBase *> *confArray;
160 if (fConfs.find(conf->getName(), &confArray)) {
161 if (!conf->equals(confArray->getAt(0))) {
162 SkDebugf("WARNING: Skia config \"%s\" was registered more than once in incompatible ways.\n", conf->getName());
163 } else {
164 confArray->append(1, &conf);
165 }
166 } else {
167 confArray = new SkTDArray<SkRTConfBase *>;
168 confArray->append(1, &conf);
169 fConfs.set(conf->getName(),confArray);
170 }
171}
172
sugoi@google.com8563d302013-03-04 21:03:17 +0000173template <typename T> T doParse(const char *, bool *success ) {
humper@google.com7af56be2013-01-14 18:49:19 +0000174 SkDebugf("WARNING: Invoked non-specialized doParse function...\n");
175 if (success) {
176 *success = false;
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +0000177 }
humper@google.com7af56be2013-01-14 18:49:19 +0000178 return (T) 0;
179}
180
181template<> bool doParse<bool>(const char *s, bool *success) {
182 if (success) {
183 *success = true;
184 }
185 if (!strcmp(s,"1") || !strcmp(s,"true")) {
186 return true;
187 }
188 if (!strcmp(s,"0") || !strcmp(s,"false")) {
189 return false;
190 }
191 if (success) {
192 *success = false;
193 }
194 return false;
195}
196
197template<> const char * doParse<const char *>(const char * s, bool *success) {
198 if (success) {
199 *success = true;
200 }
201 return s;
202}
203
204template<> int doParse<int>(const char * s, bool *success) {
sugoi@google.com8563d302013-03-04 21:03:17 +0000205 if (success) {
206 *success = true;
207 }
humper@google.com7af56be2013-01-14 18:49:19 +0000208 return atoi(s);
209}
210
211template<> unsigned int doParse<unsigned int>(const char * s, bool *success) {
sugoi@google.com8563d302013-03-04 21:03:17 +0000212 if (success) {
213 *success = true;
214 }
humper@google.com7af56be2013-01-14 18:49:19 +0000215 return (unsigned int) atoi(s);
216}
217
218template<> float doParse<float>(const char * s, bool *success) {
sugoi@google.com8563d302013-03-04 21:03:17 +0000219 if (success) {
220 *success = true;
221 }
humper@google.com7af56be2013-01-14 18:49:19 +0000222 return (float) atof(s);
223}
224
225template<> double doParse<double>(const char * s, bool *success) {
sugoi@google.com8563d302013-03-04 21:03:17 +0000226 if (success) {
227 *success = true;
228 }
humper@google.com7af56be2013-01-14 18:49:19 +0000229 return atof(s);
230}
231
232static inline void str_replace(char *s, char search, char replace) {
233 for (char *ptr = s ; *ptr ; ptr++) {
234 if (*ptr == search) {
235 *ptr = replace;
236 }
237 }
238}
239
240template<typename T> bool SkRTConfRegistry::parse(const char *name, T* value) {
halcanary@google.com7a2269e2013-11-06 14:18:46 +0000241 const char *str = NULL;
humper@google.com7af56be2013-01-14 18:49:19 +0000242
humper@google.com91673aa2013-07-24 21:44:40 +0000243 for (int i = fConfigFileKeys.count() - 1 ; i >= 0; i--) {
humper@google.com7af56be2013-01-14 18:49:19 +0000244 if (fConfigFileKeys[i]->equals(name)) {
halcanary@google.com7a2269e2013-11-06 14:18:46 +0000245 str = fConfigFileValues[i]->c_str();
humper@google.com91673aa2013-07-24 21:44:40 +0000246 break;
humper@google.com7af56be2013-01-14 18:49:19 +0000247 }
248 }
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +0000249
humper@google.com7af56be2013-01-14 18:49:19 +0000250 SkString environment_variable("skia.");
251 environment_variable.append(name);
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +0000252
humper@google.com7af56be2013-01-14 18:49:19 +0000253 const char *environment_value = getenv(environment_variable.c_str());
254 if (environment_value) {
halcanary@google.com7a2269e2013-11-06 14:18:46 +0000255 str = environment_value;
humper@google.com7af56be2013-01-14 18:49:19 +0000256 } else {
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +0000257 // apparently my shell doesn't let me have environment variables that
humper@google.com7af56be2013-01-14 18:49:19 +0000258 // have periods in them, so also let the user substitute underscores.
halcanary@google.com7a2269e2013-11-06 14:18:46 +0000259 SkAutoTMalloc<char> underscore_name(SkStrDup(environment_variable.c_str()));
260 str_replace(underscore_name.get(), '.', '_');
261 environment_value = getenv(underscore_name.get());
humper@google.com7af56be2013-01-14 18:49:19 +0000262 if (environment_value) {
halcanary@google.com7a2269e2013-11-06 14:18:46 +0000263 str = environment_value;
humper@google.com7af56be2013-01-14 18:49:19 +0000264 }
265 }
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +0000266
humper@google.com7af56be2013-01-14 18:49:19 +0000267 if (!str) {
268 return false;
269 }
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +0000270
humper@google.com7af56be2013-01-14 18:49:19 +0000271 bool success;
halcanary@google.com7a2269e2013-11-06 14:18:46 +0000272 T new_value = doParse<T>(str, &success);
humper@google.com7af56be2013-01-14 18:49:19 +0000273 if (success) {
274 *value = new_value;
275 } else {
halcanary@google.com7a2269e2013-11-06 14:18:46 +0000276 SkDebugf("WARNING: Couldn't parse value \'%s\' for variable \'%s\'\n",
277 str, name);
humper@google.com7af56be2013-01-14 18:49:19 +0000278 }
279 return success;
280}
281
282// need to explicitly instantiate the parsing function for every config type we might have...
283
284template bool SkRTConfRegistry::parse(const char *name, bool *value);
285template bool SkRTConfRegistry::parse(const char *name, int *value);
286template bool SkRTConfRegistry::parse(const char *name, unsigned int *value);
287template bool SkRTConfRegistry::parse(const char *name, float *value);
288template bool SkRTConfRegistry::parse(const char *name, double *value);
289template bool SkRTConfRegistry::parse(const char *name, const char **value);
290
halcanary@google.com1f0121a2013-11-06 15:07:44 +0000291template <typename T> void SkRTConfRegistry::set(const char *name,
292 T value,
293 bool warnIfNotFound) {
humper@google.com7af56be2013-01-14 18:49:19 +0000294 SkTDArray<SkRTConfBase *> *confArray;
295 if (!fConfs.find(name, &confArray)) {
halcanary@google.com1f0121a2013-11-06 15:07:44 +0000296 if (warnIfNotFound) {
297 SkDebugf("WARNING: Attempting to set configuration value \"%s\","
298 " but I've never heard of that.\n", name);
299 }
humper@google.com7af56be2013-01-14 18:49:19 +0000300 return;
301 }
halcanary@google.com1f0121a2013-11-06 15:07:44 +0000302 SkASSERT(confArray != NULL);
humper@google.com7af56be2013-01-14 18:49:19 +0000303 for (SkRTConfBase **confBase = confArray->begin(); confBase != confArray->end(); confBase++) {
humper@google.com6d29eda2013-01-14 19:20:28 +0000304 // static_cast here is okay because there's only one kind of child class.
humper@google.com810ae482013-01-14 20:11:00 +0000305 SkRTConf<T> *concrete = static_cast<SkRTConf<T> *>(*confBase);
skia.committer@gmail.com7fc0e0a2013-01-15 02:01:40 +0000306
humper@google.com7af56be2013-01-14 18:49:19 +0000307 if (concrete) {
308 concrete->set(value);
309 }
310 }
311}
312
halcanary@google.com1f0121a2013-11-06 15:07:44 +0000313template void SkRTConfRegistry::set(const char *name, bool value, bool);
314template void SkRTConfRegistry::set(const char *name, int value, bool);
315template void SkRTConfRegistry::set(const char *name, unsigned int value, bool);
316template void SkRTConfRegistry::set(const char *name, float value, bool);
317template void SkRTConfRegistry::set(const char *name, double value, bool);
318template void SkRTConfRegistry::set(const char *name, char * value, bool);
humper@google.com7af56be2013-01-14 18:49:19 +0000319
humper@google.com7af56be2013-01-14 18:49:19 +0000320SkRTConfRegistry &skRTConfRegistry() {
321 static SkRTConfRegistry r;
322 return r;
323}