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