/* * Copyright 2013 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "SkRTConf.h" #include "SkOSFile.h" SkRTConfRegistry::SkRTConfRegistry(): fConfs(100) { SkFILE *fp = sk_fopen(configFileLocation(), kRead_SkFILE_Flag); if (!fp) { return; } char line[1024]; while (!sk_feof(fp)) { if (!sk_fgets(line, sizeof(line), fp)) { break; } char *commentptr = strchr(line, '#'); if (commentptr == line) { continue; } if (NULL != commentptr) { *commentptr = '\0'; } char sep[] = " \t\r\n"; char *keyptr = strtok(line, sep); if (!keyptr) { continue; } char *valptr = strtok(NULL, sep); if (!valptr) { continue; } SkString* key = new SkString(keyptr); SkString* val = new SkString(valptr); fConfigFileKeys.append(1, &key); fConfigFileValues.append(1, &val); } sk_fclose(fp); } const char *SkRTConfRegistry::configFileLocation() const { return "skia.conf"; // for now -- should probably do something fancier like home directories or whatever. } // dump all known runtime config options to the file with their default values. // to trigger this, make a config file of zero size. void SkRTConfRegistry::possiblyDumpFile() const { const char *path = configFileLocation(); SkFILE *fp = sk_fopen(path, kRead_SkFILE_Flag); if (!fp) { return; } size_t configFileSize = sk_fgetsize(fp); if (configFileSize == 0) { printAll(path); } sk_fclose(fp); } // Run through every provided configuration option and print a warning if the user hasn't // declared a correponding configuration object somewhere. void SkRTConfRegistry::validate() const { for (int i = 0 ; i < fConfigFileKeys.count() ; i++) { if (!fConfs.find(fConfigFileKeys[i]->c_str())) { SkDebugf("WARNING: You have config value %s in your configuration file, but I've never heard of that.\n", fConfigFileKeys[i]->c_str()); } } } void SkRTConfRegistry::printAll(const char *fname) const { SkWStream *o; if (NULL != fname) { o = new SkFILEWStream(fname); } else { o = new SkDebugWStream(); } ConfMap::Iter iter(fConfs); SkTDArray *confArray; while (iter.next(&confArray)) { if (confArray->getAt(0)->isDefault()) { o->writeText("# "); } confArray->getAt(0)->print(o); o->newline(); } delete o; } bool SkRTConfRegistry::hasNonDefault() const { ConfMap::Iter iter(fConfs); SkTDArray *confArray; while (iter.next(&confArray)) { if (!confArray->getAt(0)->isDefault()) { return true; } } return false; } void SkRTConfRegistry::printNonDefault(const char *fname) const { SkWStream *o; if (NULL != fname) { o = new SkFILEWStream(fname); } else { o = new SkDebugWStream(); } ConfMap::Iter iter(fConfs); SkTDArray *confArray; while (iter.next(&confArray)) { if (!confArray->getAt(0)->isDefault()) { confArray->getAt(0)->print(o); o->newline(); } } delete o; } // register a configuration variable after its value has been set by the parser. // we maintain a vector of these things instead of just a single one because the // user might set the value after initialization time and we need to have // all the pointers lying around, not just one. void SkRTConfRegistry::registerConf(SkRTConfBase *conf) { SkTDArray *confArray; if (fConfs.find(conf->getName(), &confArray)) { if (!conf->equals(confArray->getAt(0))) { SkDebugf("WARNING: Skia config \"%s\" was registered more than once in incompatible ways.\n", conf->getName()); } else { confArray->append(1, &conf); } } else { confArray = new SkTDArray; confArray->append(1, &conf); fConfs.set(conf->getName(),confArray); } } template T doParse(const char *, bool *success ) { SkDebugf("WARNING: Invoked non-specialized doParse function...\n"); if (success) { *success = false; } return (T) 0; } template<> bool doParse(const char *s, bool *success) { if (success) { *success = true; } if (!strcmp(s,"1") || !strcmp(s,"true")) { return true; } if (!strcmp(s,"0") || !strcmp(s,"false")) { return false; } if (success) { *success = false; } return false; } template<> const char * doParse(const char * s, bool *success) { if (success) { *success = true; } return s; } template<> int doParse(const char * s, bool *success) { if (success) { *success = true; } return atoi(s); } template<> unsigned int doParse(const char * s, bool *success) { if (success) { *success = true; } return (unsigned int) atoi(s); } template<> float doParse(const char * s, bool *success) { if (success) { *success = true; } return (float) atof(s); } template<> double doParse(const char * s, bool *success) { if (success) { *success = true; } return atof(s); } static inline void str_replace(char *s, char search, char replace) { for (char *ptr = s ; *ptr ; ptr++) { if (*ptr == search) { *ptr = replace; } } } template bool SkRTConfRegistry::parse(const char *name, T* value) { const char *str = NULL; for (int i = fConfigFileKeys.count() - 1 ; i >= 0; i--) { if (fConfigFileKeys[i]->equals(name)) { str = fConfigFileValues[i]->c_str(); break; } } SkString environment_variable("skia."); environment_variable.append(name); const char *environment_value = getenv(environment_variable.c_str()); if (environment_value) { str = environment_value; } else { // apparently my shell doesn't let me have environment variables that // have periods in them, so also let the user substitute underscores. SkAutoTMalloc underscore_name(SkStrDup(environment_variable.c_str())); str_replace(underscore_name.get(), '.', '_'); environment_value = getenv(underscore_name.get()); if (environment_value) { str = environment_value; } } if (!str) { return false; } bool success; T new_value = doParse(str, &success); if (success) { *value = new_value; } else { SkDebugf("WARNING: Couldn't parse value \'%s\' for variable \'%s\'\n", str, name); } return success; } // need to explicitly instantiate the parsing function for every config type we might have... template bool SkRTConfRegistry::parse(const char *name, bool *value); template bool SkRTConfRegistry::parse(const char *name, int *value); template bool SkRTConfRegistry::parse(const char *name, unsigned int *value); template bool SkRTConfRegistry::parse(const char *name, float *value); template bool SkRTConfRegistry::parse(const char *name, double *value); template bool SkRTConfRegistry::parse(const char *name, const char **value); template void SkRTConfRegistry::set(const char *name, T value, bool warnIfNotFound) { SkTDArray *confArray; if (!fConfs.find(name, &confArray)) { if (warnIfNotFound) { SkDebugf("WARNING: Attempting to set configuration value \"%s\"," " but I've never heard of that.\n", name); } return; } SkASSERT(confArray != NULL); for (SkRTConfBase **confBase = confArray->begin(); confBase != confArray->end(); confBase++) { // static_cast here is okay because there's only one kind of child class. SkRTConf *concrete = static_cast *>(*confBase); if (concrete) { concrete->set(value); } } } template void SkRTConfRegistry::set(const char *name, bool value, bool); template void SkRTConfRegistry::set(const char *name, int value, bool); template void SkRTConfRegistry::set(const char *name, unsigned int value, bool); template void SkRTConfRegistry::set(const char *name, float value, bool); template void SkRTConfRegistry::set(const char *name, double value, bool); template void SkRTConfRegistry::set(const char *name, char * value, bool); SkRTConfRegistry &skRTConfRegistry() { static SkRTConfRegistry r; return r; } #ifdef SK_SUPPORT_UNITTEST #ifdef SK_BUILD_FOR_WIN32 static void sk_setenv(const char* key, const char* value) { _putenv_s(key, value); } #else static void sk_setenv(const char* key, const char* value) { setenv(key, value, 1); } #endif void SkRTConfRegistry::UnitTest() { SkRTConfRegistry registryWithoutContents(true); sk_setenv("skia_nonexistent_item", "132"); int result = 0; registryWithoutContents.parse("nonexistent.item", &result); SkASSERT(result == 132); } SkRTConfRegistry::SkRTConfRegistry(bool) : fConfs(100) { } #endif