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
11 #include <stdlib.h>
12
SkRTConfRegistry()13 SkRTConfRegistry::SkRTConfRegistry(): fConfs(100) {
14
15 FILE *fp = sk_fopen(configFileLocation(), kRead_SkFILE_Flag);
16
17 if (!fp) {
18 return;
19 }
20
21 char line[1024];
22
23 while (!sk_feof(fp)) {
24
25 if (!sk_fgets(line, sizeof(line), fp)) {
26 break;
27 }
28
29 char *commentptr = strchr(line, '#');
30 if (commentptr == line) {
31 continue;
32 }
33 if (commentptr) {
34 *commentptr = '\0';
35 }
36
37 char sep[] = " \t\r\n";
38
39 char *keyptr = strtok(line, sep);
40 if (!keyptr) {
41 continue;
42 }
43
44 char *valptr = strtok(nullptr, sep);
45 if (!valptr) {
46 continue;
47 }
48
49 SkString *key = new SkString(keyptr);
50 SkString *val = new SkString(valptr);
51
52 fConfigFileKeys.append(1, &key);
53 fConfigFileValues.append(1, &val);
54 }
55 sk_fclose(fp);
56 }
57
~SkRTConfRegistry()58 SkRTConfRegistry::~SkRTConfRegistry() {
59 ConfMap::Iter iter(fConfs);
60 SkTDArray<SkRTConfBase *> *confArray;
61
62 while (iter.next(&confArray)) {
63 delete confArray;
64 }
65
66 for (int i = 0 ; i < fConfigFileKeys.count() ; i++) {
67 delete fConfigFileKeys[i];
68 delete fConfigFileValues[i];
69 }
70 }
71
configFileLocation() const72 const char *SkRTConfRegistry::configFileLocation() const {
73 return "skia.conf"; // for now -- should probably do something fancier like home directories or whatever.
74 }
75
76 // dump all known runtime config options to the file with their default values.
77 // to trigger this, make a config file of zero size.
possiblyDumpFile() const78 void SkRTConfRegistry::possiblyDumpFile() const {
79 const char *path = configFileLocation();
80 FILE *fp = sk_fopen(path, kRead_SkFILE_Flag);
81 if (!fp) {
82 return;
83 }
84 size_t configFileSize = sk_fgetsize(fp);
85 if (configFileSize == 0) {
86 printAll(path);
87 }
88 sk_fclose(fp);
89 }
90
91 // Run through every provided configuration option and print a warning if the user hasn't
92 // declared a correponding configuration object somewhere.
validate() const93 void SkRTConfRegistry::validate() const {
94 for (int i = 0 ; i < fConfigFileKeys.count() ; i++) {
95 if (!fConfs.find(fConfigFileKeys[i]->c_str())) {
96 SkDebugf("WARNING: You have config value %s in your configuration file, but I've never heard of that.\n", fConfigFileKeys[i]->c_str());
97 }
98 }
99 }
100
printAll(const char * fname) const101 void SkRTConfRegistry::printAll(const char *fname) const {
102 SkWStream *o;
103
104 if (fname) {
105 o = new SkFILEWStream(fname);
106 } else {
107 o = new SkDebugWStream();
108 }
109
110 ConfMap::Iter iter(fConfs);
111 SkTDArray<SkRTConfBase *> *confArray;
112
113 while (iter.next(&confArray)) {
114 if (confArray->getAt(0)->isDefault()) {
115 o->writeText("# ");
116 }
117 confArray->getAt(0)->print(o);
118 o->newline();
119 }
120
121 delete o;
122 }
123
hasNonDefault() const124 bool SkRTConfRegistry::hasNonDefault() const {
125 ConfMap::Iter iter(fConfs);
126 SkTDArray<SkRTConfBase *> *confArray;
127 while (iter.next(&confArray)) {
128 if (!confArray->getAt(0)->isDefault()) {
129 return true;
130 }
131 }
132 return false;
133 }
134
printNonDefault(const char * fname) const135 void SkRTConfRegistry::printNonDefault(const char *fname) const {
136 SkWStream *o;
137
138 if (fname) {
139 o = new SkFILEWStream(fname);
140 } else {
141 o = new SkDebugWStream();
142 }
143 ConfMap::Iter iter(fConfs);
144 SkTDArray<SkRTConfBase *> *confArray;
145
146 while (iter.next(&confArray)) {
147 if (!confArray->getAt(0)->isDefault()) {
148 confArray->getAt(0)->print(o);
149 o->newline();
150 }
151 }
152
153 delete o;
154 }
155
156 // register a configuration variable after its value has been set by the parser.
157 // we maintain a vector of these things instead of just a single one because the
158 // user might set the value after initialization time and we need to have
159 // all the pointers lying around, not just one.
registerConf(SkRTConfBase * conf)160 void SkRTConfRegistry::registerConf(SkRTConfBase *conf) {
161 SkTDArray<SkRTConfBase *> *confArray;
162 if (fConfs.find(conf->getName(), &confArray)) {
163 if (!conf->equals(confArray->getAt(0))) {
164 SkDebugf("WARNING: Skia config \"%s\" was registered more than once in incompatible ways.\n", conf->getName());
165 } else {
166 confArray->append(1, &conf);
167 }
168 } else {
169 confArray = new SkTDArray<SkRTConfBase *>;
170 confArray->append(1, &conf);
171 fConfs.set(conf->getName(),confArray);
172 }
173 }
174
doParse(const char *,bool * success)175 template <typename T> T doParse(const char *, bool *success ) {
176 SkDebugf("WARNING: Invoked non-specialized doParse function...\n");
177 if (success) {
178 *success = false;
179 }
180 return (T) 0;
181 }
182
doParse(const char * s,bool * success)183 template<> bool doParse<bool>(const char *s, bool *success) {
184 if (success) {
185 *success = true;
186 }
187 if (!strcmp(s,"1") || !strcmp(s,"true")) {
188 return true;
189 }
190 if (!strcmp(s,"0") || !strcmp(s,"false")) {
191 return false;
192 }
193 if (success) {
194 *success = false;
195 }
196 return false;
197 }
198
doParse(const char * s,bool * success)199 template<> const char * doParse<const char *>(const char * s, bool *success) {
200 if (success) {
201 *success = true;
202 }
203 return s;
204 }
205
doParse(const char * s,bool * success)206 template<> int doParse<int>(const char * s, bool *success) {
207 if (success) {
208 *success = true;
209 }
210 return atoi(s);
211 }
212
doParse(const char * s,bool * success)213 template<> unsigned int doParse<unsigned int>(const char * s, bool *success) {
214 if (success) {
215 *success = true;
216 }
217 return (unsigned int) atoi(s);
218 }
219
doParse(const char * s,bool * success)220 template<> float doParse<float>(const char * s, bool *success) {
221 if (success) {
222 *success = true;
223 }
224 return (float) atof(s);
225 }
226
doParse(const char * s,bool * success)227 template<> double doParse<double>(const char * s, bool *success) {
228 if (success) {
229 *success = true;
230 }
231 return atof(s);
232 }
233
str_replace(char * s,char search,char replace)234 static inline void str_replace(char *s, char search, char replace) {
235 for (char *ptr = s ; *ptr ; ptr++) {
236 if (*ptr == search) {
237 *ptr = replace;
238 }
239 }
240 }
241
parse(const char * name,T * value)242 template<typename T> bool SkRTConfRegistry::parse(const char *name, T* value) {
243 const char *str = nullptr;
244
245 for (int i = fConfigFileKeys.count() - 1 ; i >= 0; i--) {
246 if (fConfigFileKeys[i]->equals(name)) {
247 str = fConfigFileValues[i]->c_str();
248 break;
249 }
250 }
251
252 SkString environment_variable("skia.");
253 environment_variable.append(name);
254
255 const char *environment_value = getenv(environment_variable.c_str());
256 if (environment_value) {
257 str = environment_value;
258 } else {
259 // apparently my shell doesn't let me have environment variables that
260 // have periods in them, so also let the user substitute underscores.
261 SkAutoTMalloc<char> underscore_name(SkStrDup(environment_variable.c_str()));
262 str_replace(underscore_name.get(), '.', '_');
263 environment_value = getenv(underscore_name.get());
264 if (environment_value) {
265 str = environment_value;
266 }
267 }
268
269 if (!str) {
270 return false;
271 }
272
273 bool success;
274 T new_value = doParse<T>(str, &success);
275 if (success) {
276 *value = new_value;
277 } else {
278 SkDebugf("WARNING: Couldn't parse value \'%s\' for variable \'%s\'\n",
279 str, name);
280 }
281 return success;
282 }
283
284 // need to explicitly instantiate the parsing function for every config type we might have...
285
286 template bool SkRTConfRegistry::parse(const char *name, bool *value);
287 template bool SkRTConfRegistry::parse(const char *name, int *value);
288 template bool SkRTConfRegistry::parse(const char *name, unsigned int *value);
289 template bool SkRTConfRegistry::parse(const char *name, float *value);
290 template bool SkRTConfRegistry::parse(const char *name, double *value);
291 template bool SkRTConfRegistry::parse(const char *name, const char **value);
292
set(const char * name,T value,bool warnIfNotFound)293 template <typename T> void SkRTConfRegistry::set(const char *name,
294 T value,
295 bool warnIfNotFound) {
296 SkTDArray<SkRTConfBase *> *confArray;
297 if (!fConfs.find(name, &confArray)) {
298 if (warnIfNotFound) {
299 SkDebugf("WARNING: Attempting to set configuration value \"%s\","
300 " but I've never heard of that.\n", name);
301 }
302 return;
303 }
304 SkASSERT(confArray != nullptr);
305 for (SkRTConfBase **confBase = confArray->begin(); confBase != confArray->end(); confBase++) {
306 // static_cast here is okay because there's only one kind of child class.
307 SkRTConf<T> *concrete = static_cast<SkRTConf<T> *>(*confBase);
308
309 if (concrete) {
310 concrete->set(value);
311 }
312 }
313 }
314
315 template void SkRTConfRegistry::set(const char *name, bool value, bool);
316 template void SkRTConfRegistry::set(const char *name, int value, bool);
317 template void SkRTConfRegistry::set(const char *name, unsigned int value, bool);
318 template void SkRTConfRegistry::set(const char *name, float value, bool);
319 template void SkRTConfRegistry::set(const char *name, double value, bool);
320 template void SkRTConfRegistry::set(const char *name, char * value, bool);
321
skRTConfRegistry()322 SkRTConfRegistry &skRTConfRegistry() {
323 static SkRTConfRegistry r;
324 return r;
325 }
326