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