|  | // Copyright 2016 The SwiftShader Authors. All Rights Reserved. | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //    http://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  |  | 
|  | #include "Configurator.hpp" | 
|  | #include "Debug.hpp" | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <fstream> | 
|  | #include <istream> | 
|  |  | 
|  | namespace { | 
|  | inline std::string trimSpaces(const std::string &str) | 
|  | { | 
|  | std::string trimmed = str; | 
|  | trimmed.erase(trimmed.begin(), std::find_if(trimmed.begin(), trimmed.end(), [](unsigned char c) { | 
|  | return !std::isspace(c); | 
|  | })); | 
|  | trimmed.erase(std::find_if(trimmed.rbegin(), trimmed.rend(), [](unsigned char c) { | 
|  | return !std::isspace(c); | 
|  | }).base(), | 
|  | trimmed.end()); | 
|  | return trimmed; | 
|  | } | 
|  | }  // namespace | 
|  |  | 
|  | namespace sw { | 
|  |  | 
|  | Configurator::Configurator(const std::string &filePath) | 
|  | { | 
|  | std::fstream file(filePath, std::ios::in); | 
|  | if(file.fail()) | 
|  | { | 
|  | return; | 
|  | } | 
|  | readConfiguration(file); | 
|  | file.close(); | 
|  | } | 
|  |  | 
|  | Configurator::Configurator(std::istream &str) | 
|  | { | 
|  | readConfiguration(str); | 
|  | } | 
|  |  | 
|  | bool Configurator::readConfiguration(std::istream &str) | 
|  | { | 
|  | std::string line; | 
|  | std::string sectionName; | 
|  |  | 
|  | int lineNumber = 0; | 
|  | while(getline(str, line)) | 
|  | { | 
|  | ++lineNumber; | 
|  | if(line.length()) | 
|  | { | 
|  | if(line[line.length() - 1] == '\r') | 
|  | { | 
|  | line = line.substr(0, line.length() - 1); | 
|  | } | 
|  |  | 
|  | if(!isprint(line[0])) | 
|  | { | 
|  | sw::warn("Cannot parse line %d of configuration, skipping line\n", lineNumber); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | std::string::size_type pLeft = line.find_first_of(";#[="); | 
|  |  | 
|  | if(pLeft != std::string::npos) | 
|  | { | 
|  | switch(line[pLeft]) | 
|  | { | 
|  | case '[': | 
|  | { | 
|  | std::string::size_type pRight = line.find_last_of("]"); | 
|  |  | 
|  | if(pRight != std::string::npos && pRight > pLeft) | 
|  | { | 
|  | sectionName = trimSpaces(line.substr(pLeft + 1, pRight - pLeft - 1)); | 
|  | if(!sectionName.length()) | 
|  | { | 
|  | sw::warn("Found empty section name at line %d of configuration\n", lineNumber); | 
|  | } | 
|  | } | 
|  | } | 
|  | break; | 
|  | case '=': | 
|  | { | 
|  | std::string key = trimSpaces(line.substr(0, pLeft)); | 
|  | std::string value = trimSpaces(line.substr(pLeft + 1)); | 
|  | if(!key.length() || !value.length()) | 
|  | { | 
|  | sw::warn("Cannot parse key-value pair at line %d of configuration (key or value is empty), skipping key-value pair\n", lineNumber); | 
|  | } | 
|  | else | 
|  | { | 
|  | sections[sectionName].keyValuePairs[key] = value; | 
|  | } | 
|  | } | 
|  | break; | 
|  | case ';': | 
|  | case '#': | 
|  | // Ignore comments | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return sections.size() > 0; | 
|  | } | 
|  |  | 
|  | void Configurator::writeFile(const std::string &filePath, const std::string &title) | 
|  | { | 
|  | std::fstream file(filePath, std::ios::out); | 
|  | if(file.fail()) | 
|  | { | 
|  | return; | 
|  | } | 
|  |  | 
|  | file << "; " << title << std::endl | 
|  | << std::endl; | 
|  |  | 
|  | for(const auto &[sectionName, section] : sections) | 
|  | { | 
|  | file << "[" << sectionName << "]" << std::endl; | 
|  | for(const auto &[key, value] : section.keyValuePairs) | 
|  | { | 
|  | file << key << "=" << value << std::endl; | 
|  | } | 
|  | file << std::endl; | 
|  | } | 
|  |  | 
|  | file.close(); | 
|  | } | 
|  |  | 
|  | std::optional<std::string> Configurator::getValueIfExists(const std::string §ionName, const std::string &keyName) const | 
|  | { | 
|  | const auto section = sections.find(sectionName); | 
|  | if(section == sections.end()) | 
|  | { | 
|  | return std::nullopt; | 
|  | } | 
|  |  | 
|  | const auto keyValue = section->second.keyValuePairs.find(keyName); | 
|  | if(keyValue == section->second.keyValuePairs.end()) | 
|  | { | 
|  | return std::nullopt; | 
|  | } | 
|  |  | 
|  | return keyValue->second; | 
|  | } | 
|  |  | 
|  | std::string Configurator::getValue(const std::string §ionName, const std::string &keyName, const std::string &defaultValue) const | 
|  | { | 
|  | const auto value = getValueIfExists(sectionName, keyName); | 
|  | if(value) | 
|  | { | 
|  | return *value; | 
|  | } | 
|  | return defaultValue; | 
|  | } | 
|  |  | 
|  | void Configurator::addValue(const std::string §ionName, const std::string &keyName, const std::string &value) | 
|  | { | 
|  | sections[sectionName].keyValuePairs[keyName] = value; | 
|  | } | 
|  |  | 
|  | bool Configurator::getBoolean(const std::string §ionName, const std::string &keyName, bool defaultValue) const | 
|  | { | 
|  | auto strValue = getValueIfExists(sectionName, keyName); | 
|  | if(!strValue) | 
|  | { | 
|  | return defaultValue; | 
|  | } | 
|  |  | 
|  | std::stringstream ss{ *strValue }; | 
|  |  | 
|  | bool val; | 
|  | ss >> val; | 
|  | if(ss.fail()) | 
|  | { | 
|  | // Accept "true" and "false" as well. | 
|  | ss.clear(); | 
|  | ss >> std::boolalpha >> val; | 
|  | } | 
|  | return val; | 
|  | } | 
|  |  | 
|  | double Configurator::getFloat(const std::string §ionName, const std::string &keyName, double defaultValue) const | 
|  | { | 
|  | auto strValue = getValueIfExists(sectionName, keyName); | 
|  | if(!strValue) | 
|  | { | 
|  | return defaultValue; | 
|  | } | 
|  |  | 
|  | std::stringstream ss{ *strValue }; | 
|  |  | 
|  | double val = 0; | 
|  | ss >> val; | 
|  | return val; | 
|  | } | 
|  | }  // namespace sw |