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 = 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
56SkRTConfRegistry::~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
70const 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.
76void 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.
91void 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
99void SkRTConfRegistry::printAll(const char *fname) const {
100    SkWStream *o;
101
102    if (NULL != 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
122bool 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
133void SkRTConfRegistry::printNonDefault(const char *fname) const {
134    SkWStream *o;
135
136    if (NULL != 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.
158void 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
173template <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
181template<> 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
197template<> const char * doParse<const char *>(const char * s, bool *success) {
198    if (success) {
199        *success = true;
200    }
201    return s;
202}
203
204template<> int doParse<int>(const char * s, bool *success) {
205    if (success) {
206        *success = true;
207    }
208    return atoi(s);
209}
210
211template<> 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
218template<> float doParse<float>(const char * s, bool *success) {
219    if (success) {
220        *success = true;
221    }
222    return (float) atof(s);
223}
224
225template<> double doParse<double>(const char * s, bool *success) {
226    if (success) {
227        *success = true;
228    }
229    return atof(s);
230}
231
232static 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
240template<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
284template bool SkRTConfRegistry::parse(const char *name, bool *value);
285template bool SkRTConfRegistry::parse(const char *name, int *value);
286template bool SkRTConfRegistry::parse(const char *name, unsigned int *value);
287template bool SkRTConfRegistry::parse(const char *name, float *value);
288template bool SkRTConfRegistry::parse(const char *name, double *value);
289template bool SkRTConfRegistry::parse(const char *name, const char **value);
290
291template <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
313template void SkRTConfRegistry::set(const char *name, bool value, bool);
314template void SkRTConfRegistry::set(const char *name, int value, bool);
315template void SkRTConfRegistry::set(const char *name, unsigned int value, bool);
316template void SkRTConfRegistry::set(const char *name, float value, bool);
317template void SkRTConfRegistry::set(const char *name, double value, bool);
318template void SkRTConfRegistry::set(const char *name, char * value, bool);
319
320SkRTConfRegistry &skRTConfRegistry() {
321    static SkRTConfRegistry r;
322    return r;
323}
324
325
326#ifdef SK_SUPPORT_UNITTEST
327
328#ifdef SK_BUILD_FOR_WIN32
329static void sk_setenv(const char* key, const char* value) {
330    _putenv_s(key, value);
331}
332#else
333static void sk_setenv(const char* key, const char* value) {
334    setenv(key, value, 1);
335}
336#endif
337
338void SkRTConfRegistry::UnitTest() {
339    SkRTConfRegistry registryWithoutContents(true);
340
341    sk_setenv("skia_nonexistent_item", "132");
342    int result = 0;
343    registryWithoutContents.parse("nonexistent.item", &result);
344    SkASSERT(result == 132);
345}
346
347SkRTConfRegistry::SkRTConfRegistry(bool)
348    : fConfs(100) {
349}
350#endif
351