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