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
11SkRTConfRegistry::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
56const 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.
62void 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.
77void 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
85void 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
108void 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.
133void 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
148template <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
156template<> 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
172template<> const char * doParse<const char *>(const char * s, bool *success) {
173    if (success) {
174        *success = true;
175    }
176    return s;
177}
178
179template<> int doParse<int>(const char * s, bool *success) {
180    if (success) {
181        *success = true;
182    }
183    return atoi(s);
184}
185
186template<> 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
193template<> float doParse<float>(const char * s, bool *success) {
194    if (success) {
195        *success = true;
196    }
197    return (float) atof(s);
198}
199
200template<> double doParse<double>(const char * s, bool *success) {
201    if (success) {
202        *success = true;
203    }
204    return atof(s);
205}
206
207static 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
215template<typename T> bool SkRTConfRegistry::parse(const char *name, T* value) {
216    SkString *str = NULL;
217
218    for (int i = fConfigFileKeys.count() - 1 ; i >= 0; i--) {
219        if (fConfigFileKeys[i]->equals(name)) {
220            str = fConfigFileValues[i];
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->set(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        SkString underscore_environment_variable("skia_");
235        char *underscore_name = SkStrDup(name);
236        str_replace(underscore_name,'.','_');
237        underscore_environment_variable.append(underscore_name);
238        sk_free(underscore_name);
239        environment_value = getenv(underscore_environment_variable.c_str());
240        if (environment_value) {
241            str->set(environment_value);
242        }
243    }
244
245    if (!str) {
246        return false;
247    }
248
249    bool success;
250    T new_value = doParse<T>(str->c_str(),&success);
251    if (success) {
252        *value = new_value;
253    } else {
254        SkDebugf("WARNING: Couldn't parse value \'%s\' for variable \'%s\'\n", str->c_str(), name);
255    }
256    return success;
257}
258
259// need to explicitly instantiate the parsing function for every config type we might have...
260
261template bool SkRTConfRegistry::parse(const char *name, bool *value);
262template bool SkRTConfRegistry::parse(const char *name, int *value);
263template bool SkRTConfRegistry::parse(const char *name, unsigned int *value);
264template bool SkRTConfRegistry::parse(const char *name, float *value);
265template bool SkRTConfRegistry::parse(const char *name, double *value);
266template bool SkRTConfRegistry::parse(const char *name, const char **value);
267
268template <typename T> void SkRTConfRegistry::set(const char *name, T value) {
269
270    SkTDArray<SkRTConfBase *> *confArray;
271    if (!fConfs.find(name, &confArray)) {
272        SkDebugf("WARNING: Attempting to set configuration value \"%s\", but I've never heard of that.\n", name);
273        return;
274    }
275
276    for (SkRTConfBase **confBase = confArray->begin(); confBase != confArray->end(); confBase++) {
277        // static_cast here is okay because there's only one kind of child class.
278        SkRTConf<T> *concrete = static_cast<SkRTConf<T> *>(*confBase);
279
280        if (concrete) {
281            concrete->set(value);
282        }
283    }
284}
285
286template void SkRTConfRegistry::set(const char *name, bool value);
287template void SkRTConfRegistry::set(const char *name, int value);
288template void SkRTConfRegistry::set(const char *name, unsigned int value);
289template void SkRTConfRegistry::set(const char *name, float value);
290template void SkRTConfRegistry::set(const char *name, double value);
291template void SkRTConfRegistry::set(const char *name, char * value);
292
293SkRTConfRegistry &skRTConfRegistry() {
294    static SkRTConfRegistry r;
295    return r;
296}
297