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 (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 = SkNEW_ARGS(SkString,(keyptr));
48         SkString* val = SkNEW_ARGS(SkString,(valptr));
49 
50         fConfigFileKeys.append(1, &key);
51         fConfigFileValues.append(1, &val);
52     }
53     sk_fclose(fp);
54 }
55 
~SkRTConfRegistry()56 SkRTConfRegistry::~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 
configFileLocation() const70 const 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.
possiblyDumpFile() const76 void 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.
validate() const91 void SkRTConfRegistry::validate() const {
92     for (int i = 0 ; i < fConfigFileKeys.count() ; i++) {
93         if (!fConfs.find(fConfigFileKeys[i]->c_str())) {
94             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 
printAll(const char * fname) const99 void SkRTConfRegistry::printAll(const char *fname) const {
100     SkWStream *o;
101 
102     if (fname) {
103         o = new SkFILEWStream(fname);
104     } else {
105         o = new SkDebugWStream();
106     }
107 
108     ConfMap::Iter iter(fConfs);
109     SkTDArray<SkRTConfBase *> *confArray;
110 
111     while (iter.next(&confArray)) {
112         if (confArray->getAt(0)->isDefault()) {
113             o->writeText("# ");
114         }
115         confArray->getAt(0)->print(o);
116         o->newline();
117     }
118 
119     delete o;
120 }
121 
hasNonDefault() const122 bool 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 
printNonDefault(const char * fname) const133 void SkRTConfRegistry::printNonDefault(const char *fname) const {
134     SkWStream *o;
135 
136     if (fname) {
137         o = new SkFILEWStream(fname);
138     } else {
139         o = new SkDebugWStream();
140     }
141     ConfMap::Iter iter(fConfs);
142     SkTDArray<SkRTConfBase *> *confArray;
143 
144     while (iter.next(&confArray)) {
145         if (!confArray->getAt(0)->isDefault()) {
146             confArray->getAt(0)->print(o);
147             o->newline();
148         }
149     }
150 
151     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.
registerConf(SkRTConfBase * conf)158 void 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 
doParse(const char *,bool * success)173 template <typename T> T doParse(const char *, bool *success ) {
174     SkDebugf("WARNING: Invoked non-specialized doParse function...\n");
175     if (success) {
176         *success = false;
177     }
178     return (T) 0;
179 }
180 
doParse(const char * s,bool * success)181 template<> 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 
doParse(const char * s,bool * success)197 template<> const char * doParse<const char *>(const char * s, bool *success) {
198     if (success) {
199         *success = true;
200     }
201     return s;
202 }
203 
doParse(const char * s,bool * success)204 template<> int doParse<int>(const char * s, bool *success) {
205     if (success) {
206         *success = true;
207     }
208     return atoi(s);
209 }
210 
doParse(const char * s,bool * success)211 template<> unsigned int doParse<unsigned int>(const char * s, bool *success) {
212     if (success) {
213         *success = true;
214     }
215     return (unsigned int) atoi(s);
216 }
217 
doParse(const char * s,bool * success)218 template<> float doParse<float>(const char * s, bool *success) {
219     if (success) {
220         *success = true;
221     }
222     return (float) atof(s);
223 }
224 
doParse(const char * s,bool * success)225 template<> double doParse<double>(const char * s, bool *success) {
226     if (success) {
227         *success = true;
228     }
229     return atof(s);
230 }
231 
str_replace(char * s,char search,char replace)232 static 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 
parse(const char * name,T * value)240 template<typename T> bool SkRTConfRegistry::parse(const char *name, T* value) {
241     const char *str = NULL;
242 
243     for (int i = fConfigFileKeys.count() - 1 ; i >= 0; i--) {
244         if (fConfigFileKeys[i]->equals(name)) {
245             str = fConfigFileValues[i]->c_str();
246             break;
247         }
248     }
249 
250     SkString environment_variable("skia.");
251     environment_variable.append(name);
252 
253     const char *environment_value = getenv(environment_variable.c_str());
254     if (environment_value) {
255         str = environment_value;
256     } else {
257         // apparently my shell doesn't let me have environment variables that
258         // have periods in them, so also let the user substitute underscores.
259         SkAutoTMalloc<char> underscore_name(SkStrDup(environment_variable.c_str()));
260         str_replace(underscore_name.get(), '.', '_');
261         environment_value = getenv(underscore_name.get());
262         if (environment_value) {
263             str = environment_value;
264         }
265     }
266 
267     if (!str) {
268         return false;
269     }
270 
271     bool success;
272     T new_value = doParse<T>(str, &success);
273     if (success) {
274         *value = new_value;
275     } else {
276         SkDebugf("WARNING: Couldn't parse value \'%s\' for variable \'%s\'\n",
277                  str, name);
278     }
279     return success;
280 }
281 
282 // need to explicitly instantiate the parsing function for every config type we might have...
283 
284 template bool SkRTConfRegistry::parse(const char *name, bool *value);
285 template bool SkRTConfRegistry::parse(const char *name, int *value);
286 template bool SkRTConfRegistry::parse(const char *name, unsigned int *value);
287 template bool SkRTConfRegistry::parse(const char *name, float *value);
288 template bool SkRTConfRegistry::parse(const char *name, double *value);
289 template bool SkRTConfRegistry::parse(const char *name, const char **value);
290 
set(const char * name,T value,bool warnIfNotFound)291 template <typename T> void SkRTConfRegistry::set(const char *name,
292                                                  T value,
293                                                  bool warnIfNotFound) {
294     SkTDArray<SkRTConfBase *> *confArray;
295     if (!fConfs.find(name, &confArray)) {
296         if (warnIfNotFound) {
297             SkDebugf("WARNING: Attempting to set configuration value \"%s\","
298                      " but I've never heard of that.\n", name);
299         }
300         return;
301     }
302     SkASSERT(confArray != NULL);
303     for (SkRTConfBase **confBase = confArray->begin(); confBase != confArray->end(); confBase++) {
304         // static_cast here is okay because there's only one kind of child class.
305         SkRTConf<T> *concrete = static_cast<SkRTConf<T> *>(*confBase);
306 
307         if (concrete) {
308             concrete->set(value);
309         }
310     }
311 }
312 
313 template void SkRTConfRegistry::set(const char *name, bool value, bool);
314 template void SkRTConfRegistry::set(const char *name, int value, bool);
315 template void SkRTConfRegistry::set(const char *name, unsigned int value, bool);
316 template void SkRTConfRegistry::set(const char *name, float value, bool);
317 template void SkRTConfRegistry::set(const char *name, double value, bool);
318 template void SkRTConfRegistry::set(const char *name, char * value, bool);
319 
skRTConfRegistry()320 SkRTConfRegistry &skRTConfRegistry() {
321     static SkRTConfRegistry r;
322     return r;
323 }
324