1 /*
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
SkRTConfRegistry()11 SkRTConfRegistry::SkRTConfRegistry(): fConfs(100) {
12
13 SkFILE *fp = sk_fopen(configFileLocation(), kRead_SkFILE_Flag);
14
15 if (!fp) {
16 return;
17 }
18
19 char line[1024];
20
21 while (!sk_feof(fp)) {
22
23 if (!sk_fgets(line, sizeof(line), fp)) {
24 break;
25 }
26
27 char *commentptr = strchr(line, '#');
28 if (commentptr == line) {
29 continue;
30 }
31 if (NULL != commentptr) {
32 *commentptr = '\0';
33 }
34
35 char sep[] = " \t\r\n";
36
37 char *keyptr = strtok(line, sep);
38 if (!keyptr) {
39 continue;
40 }
41
42 char *valptr = strtok(NULL, sep);
43 if (!valptr) {
44 continue;
45 }
46
47 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
configFileLocation() const56 const 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.
possiblyDumpFile() const62 void 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.
validate() const77 void SkRTConfRegistry::validate() const {
78 for (int i = 0 ; i < fConfigFileKeys.count() ; i++) {
79 if (!fConfs.find(fConfigFileKeys[i]->c_str())) {
80 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
printAll(const char * fname) const85 void SkRTConfRegistry::printAll(const char *fname) const {
86 SkWStream *o;
87
88 if (NULL != fname) {
89 o = new SkFILEWStream(fname);
90 } else {
91 o = new SkDebugWStream();
92 }
93
94 ConfMap::Iter iter(fConfs);
95 SkTDArray<SkRTConfBase *> *confArray;
96
97 while (iter.next(&confArray)) {
98 if (confArray->getAt(0)->isDefault()) {
99 o->writeText("# ");
100 }
101 confArray->getAt(0)->print(o);
102 o->newline();
103 }
104
105 delete o;
106 }
107
printNonDefault(const char * fname) const108 void SkRTConfRegistry::printNonDefault(const char *fname) const {
109 SkWStream *o;
110
111 if (NULL != fname) {
112 o = new SkFILEWStream(fname);
113 } else {
114 o = new SkDebugWStream();
115 }
116 ConfMap::Iter iter(fConfs);
117 SkTDArray<SkRTConfBase *> *confArray;
118
119 while (iter.next(&confArray)) {
120 if (!confArray->getAt(0)->isDefault()) {
121 confArray->getAt(0)->print(o);
122 o->newline();
123 }
124 }
125
126 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.
registerConf(SkRTConfBase * conf)133 void 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
doParse(const char *,bool * success)148 template <typename T> T doParse(const char *, bool *success ) {
149 SkDebugf("WARNING: Invoked non-specialized doParse function...\n");
150 if (success) {
151 *success = false;
152 }
153 return (T) 0;
154 }
155
doParse(const char * s,bool * success)156 template<> 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
doParse(const char * s,bool * success)172 template<> const char * doParse<const char *>(const char * s, bool *success) {
173 if (success) {
174 *success = true;
175 }
176 return s;
177 }
178
doParse(const char * s,bool * success)179 template<> int doParse<int>(const char * s, bool *success) {
180 if (success) {
181 *success = true;
182 }
183 return atoi(s);
184 }
185
doParse(const char * s,bool * success)186 template<> unsigned int doParse<unsigned int>(const char * s, bool *success) {
187 if (success) {
188 *success = true;
189 }
190 return (unsigned int) atoi(s);
191 }
192
doParse(const char * s,bool * success)193 template<> float doParse<float>(const char * s, bool *success) {
194 if (success) {
195 *success = true;
196 }
197 return (float) atof(s);
198 }
199
doParse(const char * s,bool * success)200 template<> double doParse<double>(const char * s, bool *success) {
201 if (success) {
202 *success = true;
203 }
204 return atof(s);
205 }
206
str_replace(char * s,char search,char replace)207 static 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
parse(const char * name,T * value)215 template<typename T> bool SkRTConfRegistry::parse(const char *name, T* value) {
216 const char *str = NULL;
217
218 for (int i = fConfigFileKeys.count() - 1 ; i >= 0; i--) {
219 if (fConfigFileKeys[i]->equals(name)) {
220 str = fConfigFileValues[i]->c_str();
221 break;
222 }
223 }
224
225 SkString environment_variable("skia.");
226 environment_variable.append(name);
227
228 const char *environment_value = getenv(environment_variable.c_str());
229 if (environment_value) {
230 str = environment_value;
231 } else {
232 // apparently my shell doesn't let me have environment variables that
233 // have periods in them, so also let the user substitute underscores.
234 SkAutoTMalloc<char> underscore_name(SkStrDup(environment_variable.c_str()));
235 str_replace(underscore_name.get(), '.', '_');
236 environment_value = getenv(underscore_name.get());
237 if (environment_value) {
238 str = environment_value;
239 }
240 }
241
242 if (!str) {
243 return false;
244 }
245
246 bool success;
247 T new_value = doParse<T>(str, &success);
248 if (success) {
249 *value = new_value;
250 } else {
251 SkDebugf("WARNING: Couldn't parse value \'%s\' for variable \'%s\'\n",
252 str, name);
253 }
254 return success;
255 }
256
257 // need to explicitly instantiate the parsing function for every config type we might have...
258
259 template bool SkRTConfRegistry::parse(const char *name, bool *value);
260 template bool SkRTConfRegistry::parse(const char *name, int *value);
261 template bool SkRTConfRegistry::parse(const char *name, unsigned int *value);
262 template bool SkRTConfRegistry::parse(const char *name, float *value);
263 template bool SkRTConfRegistry::parse(const char *name, double *value);
264 template bool SkRTConfRegistry::parse(const char *name, const char **value);
265
set(const char * name,T value,bool warnIfNotFound)266 template <typename T> void SkRTConfRegistry::set(const char *name,
267 T value,
268 bool warnIfNotFound) {
269 SkTDArray<SkRTConfBase *> *confArray;
270 if (!fConfs.find(name, &confArray)) {
271 if (warnIfNotFound) {
272 SkDebugf("WARNING: Attempting to set configuration value \"%s\","
273 " but I've never heard of that.\n", name);
274 }
275 return;
276 }
277 SkASSERT(confArray != NULL);
278 for (SkRTConfBase **confBase = confArray->begin(); confBase != confArray->end(); confBase++) {
279 // static_cast here is okay because there's only one kind of child class.
280 SkRTConf<T> *concrete = static_cast<SkRTConf<T> *>(*confBase);
281
282 if (concrete) {
283 concrete->set(value);
284 }
285 }
286 }
287
288 template void SkRTConfRegistry::set(const char *name, bool value, bool);
289 template void SkRTConfRegistry::set(const char *name, int value, bool);
290 template void SkRTConfRegistry::set(const char *name, unsigned int value, bool);
291 template void SkRTConfRegistry::set(const char *name, float value, bool);
292 template void SkRTConfRegistry::set(const char *name, double value, bool);
293 template void SkRTConfRegistry::set(const char *name, char * value, bool);
294
skRTConfRegistry()295 SkRTConfRegistry &skRTConfRegistry() {
296 static SkRTConfRegistry r;
297 return r;
298 }
299
300
301 #ifdef SK_SUPPORT_UNITTEST
302
303 #ifdef SK_BUILD_FOR_WIN32
sk_setenv(const char * key,const char * value)304 static void sk_setenv(const char* key, const char* value) {
305 _putenv_s(key, value);
306 }
307 #else
sk_setenv(const char * key,const char * value)308 static void sk_setenv(const char* key, const char* value) {
309 setenv(key, value, 1);
310 }
311 #endif
312
UnitTest()313 void SkRTConfRegistry::UnitTest() {
314 SkRTConfRegistry registryWithoutContents(true);
315
316 sk_setenv("skia_nonexistent_item", "132");
317 int result = 0;
318 registryWithoutContents.parse("nonexistent.item", &result);
319 SkASSERT(result == 132);
320 }
321
SkRTConfRegistry(bool)322 SkRTConfRegistry::SkRTConfRegistry(bool)
323 : fConfs(100) {
324 }
325 #endif
326