From b15f8552d629021c3cadef3e7afcae2fa98dad1b Mon Sep 17 00:00:00 2001 From: Wohlstand Date: Sun, 30 Jul 2017 05:06:18 +0300 Subject: Improve gen_adldata program - Now it caches all generated data, so, we won't have to re-calculate same - File is writing by gen_adldata nor by stdout forward - Instead of hardcoded list of banks, I made the INI file which declares list of banks to generate - Add simple validators to tell which bank is absense and can't be loaded - Split code of gen_adldata.cc into multiple files of different role --- src/gen_adldata/ini/ini_processing.cpp | 1385 ++++++++++++++++++++++++++++++++ 1 file changed, 1385 insertions(+) create mode 100644 src/gen_adldata/ini/ini_processing.cpp (limited to 'src/gen_adldata/ini/ini_processing.cpp') diff --git a/src/gen_adldata/ini/ini_processing.cpp b/src/gen_adldata/ini/ini_processing.cpp new file mode 100644 index 0000000..2f15821 --- /dev/null +++ b/src/gen_adldata/ini/ini_processing.cpp @@ -0,0 +1,1385 @@ +/* +INI Processor - a small library which allows you parsing INI-files + +Copyright (c) 2017 Vitaliy Novichkov + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +*/ + +//#define USE_FILE_MAPPER + +/* Stop parsing on first error (default is to keep parsing). */ +//#define INI_STOP_ON_FIRST_ERROR + +#include "ini_processing.h" +#include +#include +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#endif + +#ifdef USE_FILE_MAPPER +/*****Replace this with right path to file mapper class*****/ +#include "../fileMapper/file_mapper.h" +#endif + +static const unsigned char utfbom[3] = {0xEF, 0xBB, 0xBF}; + +enum { Space = 0x01, Special = 0x02, INIParamEq = 0x04 }; + +static const unsigned char charTraits[256] = +{ + // Space: '\t', '\n', '\r', ' ' + // Special: '\n', '\r', '"', ';', '=', '\\' + // INIParamEq: ':', '=' + + 0, 0, 0, 0, 0, 0, 0, 0, 0, Space, Space | Special, 0, 0, Space | Special, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Space, 0, Special, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, INIParamEq, + Special, 0, Special | INIParamEq, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Special, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +#if 0//for speed comparison who faster - macro or inline function. Seems speeds are same +#define IS_SPACE(c) (charTraits[static_cast(c)] & Space) +#define IS_SPECIAL(c) (charTraits[static_cast(c)] & Special) +#define IS_INIEQUAL(c) (charTraits[static_cast(c)] & INIParamEq) +#else +inline unsigned char IS_SPACE(char &c) +{ + return (charTraits[static_cast(c)] & Space); +} +inline unsigned char IS_SPECIAL(char &c) +{ + return (charTraits[static_cast(c)] & Special); +} +inline unsigned char IS_INIEQUAL(char &c) +{ + return (charTraits[static_cast(c)] & INIParamEq); +} +#endif + +/* Strip whitespace chars off end of given string, in place. Return s. */ +inline char *rstrip(char *s) +{ + char *p = s + strlen(s); + + while(p > s && IS_SPACE(*--p)) + *p = '\0'; + + return s; +} + +/* Return pointer to first non-whitespace char in given string. */ +inline char *lskip(char *s) +{ + while(*s && IS_SPACE(*s)) + s++; + + return reinterpret_cast(s); +} + +inline char *lrtrim(char *s) +{ + while(*s && IS_SPACE(*s)) + s++; + + char *p = s + strlen(s); + + while(p > s && IS_SPACE(*--p)) + *p = '\0'; + + return s; +} + +/* Return pointer to first char c or ';' comment in given string, or pointer to + null at end of string if neither found. ';' must be prefixed by a whitespace + character to register as a comment. */ +inline char *find_char_or_comment(char *s, char c) +{ + unsigned char was_whitespace = 0; + + while(*s && *s != c && !(was_whitespace && *s == ';')) + { + was_whitespace = IS_SPACE(*s); + s++; + } + + return s; +} + +inline char *find_inieq_or_comment(char *s) +{ + unsigned char was_whitespace = 0; + + while(*s && (!IS_INIEQUAL(*s)) && !(was_whitespace && *s == ';')) + { + was_whitespace = IS_SPACE(*s); + s++; + } + + return s; +} + +inline char *removeQuotes(char *begin, char *end) +{ + if((*begin == '\0') || (begin == end)) + return begin; + + if((*begin == '"') && (begin + 1 != end)) + begin++; + else + return begin; + + if(*(end - 1) == '"') + *(end - 1) = '\0'; + + return begin; +} + +inline char *unescapeString(char* str) +{ + char *src, *dst; + src = str; + dst = str; + while(*src) + { + if(*src == '\\') + { + src++; + switch(*src) + { + case 'n': *dst = '\n'; break; + case 'r': *dst = '\r'; break; + case 't': *dst = '\t'; break; + default: *dst = *src; break; + } + } + else + if(src != dst) + { + *dst = *src; + } + src++; dst++; + } + *dst = '\0'; + return str; +} + +//Remove comment line from a tail of value +inline void skipcomment(char *value) +{ + unsigned char quoteDepth = 0; + + while(*value) + { + if(quoteDepth > 0) + { + if(*value == '\\') + { + value++; + continue; + } + + if(*value == '"') + --quoteDepth; + } + else if(*value == '"') + ++quoteDepth; + + if((quoteDepth == 0) && (*value == ';')) + { + *value = '\0'; + break; + } + + value++; + } +} + +inline bool memfgets(char *&line, char *data, char *&pos, char *end) +{ + line = pos; + + while(pos != end) + { + if(*pos == '\n') + { + if((pos > data) && (*(pos - 1) == '\r')) + *((pos++) - 1) = '\0';//Support CRLF too + else + *(pos++) = '\0'; + + break; + } + + ++pos; + } + + return (pos != line); + //EOF is a moment when position wasn't changed. + //If do check "pos != end", will be an inability to read last line. + //this logic allows detect true EOF when line is really eof +} + +/* See documentation in header file. */ +bool IniProcessing::parseHelper(char *data, size_t size) +{ + char *section = nullptr; + #if defined(INI_ALLOW_MULTILINE) + char *prev_name = nullptr; + #endif + char *start; + char *end; + char *name; + char *value; + int lineno = 0; + int error = 0; + char *line; + char *pos_end = data + size; + char *pos_cur = data; + params::IniKeys *recentKeys = nullptr; + + /* Scan through file line by line */ + //while (fgets(line, INI_MAX_LINE, file) != NULL) + while(memfgets(line, data, pos_cur, pos_end)) + { + lineno++; + start = line; + + if((lineno == 1) && (size >= 3) && (memcmp(start, utfbom, 3) == 0)) + start += 3; + + start = lrtrim(start); + + if(!*start)//if empty line - skip it away! + continue; + + switch(*start) + { + case ';': + case '#': + //if (*start == ';' || *start == '#') { + // /* Per Python ConfigParser, allow '#' comments at start of line */ + //} + continue; + + case '[': + { + /* A "[section]" line */ + end = find_char_or_comment(start + 1, ']'); + + if(*end == ']') + { + *end = '\0'; + section = start + 1; + //#if defined(INI_ALLOW_MULTILINE) + // prev_name = nullptr; + //#endif + recentKeys = &m_params.iniData[section]; + } + else if(!error) + { + /* No ']' found on section line */ + m_params.errorCode = ERR_SECTION_SYNTAX; + error = lineno; + } + } + break; + + default: + { + /* Not a comment, must be a name[=:]value pair */ + end = find_inieq_or_comment(start); + + if(IS_INIEQUAL(*end)) + { + *end = '\0'; + name = rstrip(start); + value = lskip(end + 1); + end = find_char_or_comment(value, '\0'); + + #ifndef CASE_SENSITIVE_KEYS + for(char *iter = name; *iter != '\0'; ++iter) + *iter = (char)tolower(*iter); + #endif + + if(*end == ';') + *end = '\0'; + + rstrip(value); + { + char *v = value; + skipcomment(v); + v = rstrip(v); + + if(!recentKeys) + recentKeys = &m_params.iniData["General"]; + + #ifdef INIDEBUG + printf("-> [%s]; %s = %s\n", section, name, v); + #endif + (*recentKeys)[name] = unescapeString( removeQuotes(v, v + strlen(v)) ); + } + } + else if(!error) + { + /* No '=' or ':' found on name[=:]value line */ + m_params.errorCode = ERR_KEY_SYNTAX; + error = lineno; + } + + break; + } + }//switch(*start) + + #if defined(INI_STOP_ON_FIRST_ERROR) + + if(error) + break; + + #endif + } + + m_params.lineWithError = error; + return (error == 0); +} + +/* See documentation in header file. */ +bool IniProcessing::parseFile(const char *filename) +{ + bool valid = true; + char *tmp = nullptr; + #ifdef USE_FILE_MAPPER + //By mystical reasons, reading whole file form fread() is faster than mapper :-P + PGE_FileMapper file(filename); + + if(!file.data) + { + m_params.errorCode = ERR_NOFILE; + return -1; + } + + tmp = reinterpret_cast(malloc(static_cast(file.size + 1))); + + if(!tmp) + { + m_params.errorCode = ERR_NO_MEMORY; + return false; + } + + memcpy(tmp, file.data, static_cast(file.size)); + *(tmp + file.size) = '\0';//null terminate last line + valid = ini_parse_file(tmp, static_cast(file.size)); + #else + #ifdef _WIN32 + //Convert UTF8 file path into UTF16 to support non-ASCII paths on Windows + std::wstring dest; + dest.resize(std::strlen(filename)); + int newSize = MultiByteToWideChar(CP_UTF8, + 0, + filename, + dest.size(), + (wchar_t *)dest.c_str(), + dest.size()); + dest.resize(newSize); + FILE *cFile = _wfopen(dest.c_str(), L"rb"); + #else + FILE *cFile = fopen(filename, "rb"); + #endif + + if(!cFile) + { + m_params.errorCode = ERR_NOFILE; + return false; + } + + fseek(cFile, 0, SEEK_END); + ssize_t size = static_cast(ftell(cFile)); + if(size < 0) + { + m_params.errorCode = ERR_KEY_SYNTAX; + fclose(cFile); + return false; + } + fseek(cFile, 0, SEEK_SET); + tmp = reinterpret_cast(malloc(static_cast(size + 1))); + if(!tmp) + { + fclose(cFile); + m_params.errorCode = ERR_NO_MEMORY; + return false; + } + + if(fread(tmp, 1, static_cast(size), cFile) != static_cast(size)) + valid = false; + + fclose(cFile); + if(valid) + { + *(tmp + size) = '\0';//null terminate last line + try + { + valid = parseHelper(tmp, static_cast(size)); + } + catch(...) + { + valid = false; + m_params.errorCode = ERR_SECTION_SYNTAX; + } + } + #endif + + free(tmp); + return valid; +} + +bool IniProcessing::parseMemory(char *mem, size_t size) +{ + bool valid = true; + char *tmp = nullptr; + tmp = reinterpret_cast(malloc(size + 1)); + + if(!tmp) + { + m_params.errorCode = ERR_NO_MEMORY; + return false; + } + + memcpy(tmp, mem, static_cast(size)); + *(tmp + size) = '\0';//null terminate last line + valid = parseHelper(tmp, size); + free(tmp); + return valid; +} + + +IniProcessing::IniProcessing() : + m_params{"", false, -1, ERR_OK, false, params::IniSections(), nullptr, ""} +{} + +IniProcessing::IniProcessing(const char *iniFileName, int) : + m_params{iniFileName, false, -1, ERR_OK, false, params::IniSections(), nullptr, ""} +{ + open(iniFileName); +} + +IniProcessing::IniProcessing(const std::string &iniFileName, int) : + m_params{iniFileName, false, -1, ERR_OK, false, params::IniSections(), nullptr, ""} +{ + open(iniFileName); +} + +#ifdef INI_PROCESSING_ALLOW_QT_TYPES +IniProcessing::IniProcessing(const QString &iniFileName, int) : + m_params{iniFileName.toStdString(), false, -1, ERR_OK, false, params::IniSections(), nullptr, ""} +{ + open(m_params.filePath); +} +#endif + +IniProcessing::IniProcessing(char *memory, size_t size): + m_params{"", false, -1, ERR_OK, false, params::IniSections(), nullptr, ""} +{ + openMem(memory, size); +} + +IniProcessing::IniProcessing(const IniProcessing &ip) : + m_params(ip.m_params) +{} + +bool IniProcessing::open(const std::string &iniFileName) +{ + std::setlocale(LC_NUMERIC, "C"); + + if(!iniFileName.empty()) + { + close(); + m_params.errorCode = ERR_OK; + m_params.filePath = iniFileName; + bool res = parseFile(m_params.filePath.c_str()); + #ifdef INIDEBUG + + if(res) + printf("\n==========WOOHOO!!!==============\n\n"); + else + printf("\n==========OOOUCH!!!==============\n\n"); + + #endif + m_params.opened = res; + return res; + } + + m_params.errorCode = ERR_NOFILE; + return false; +} + +bool IniProcessing::openMem(char *memory, size_t size) +{ + std::setlocale(LC_NUMERIC, "C"); + + if((memory != nullptr) && (size > 0)) + { + close(); + m_params.errorCode = ERR_OK; + m_params.filePath.clear(); + bool res = parseMemory(memory, size); + m_params.opened = res; + return res; + } + + m_params.errorCode = ERR_NOFILE; + return false; +} + +void IniProcessing::close() +{ + m_params.errorCode = ERR_OK; + m_params.iniData.clear(); + m_params.opened = false; + m_params.lineWithError = -1; +} + +IniProcessing::ErrCode IniProcessing::lastError() +{ + return m_params.errorCode; +} + +int IniProcessing::lineWithError() +{ + return m_params.lineWithError; +} + +bool IniProcessing::isOpened() +{ + return m_params.opened; +} + +bool IniProcessing::beginGroup(const std::string &groupName) +{ + //Keep the group name. If not exist, will be created on value write + m_params.currentGroupName = groupName; + + params::IniSections::iterator e = m_params.iniData.find(groupName); + + if(e == m_params.iniData.end()) + return false; + + params::IniKeys &k = e->second; + m_params.currentGroup = &k; + return true; +} + +bool IniProcessing::contains(const std::string &groupName) +{ + if(!m_params.opened) + return false; + + params::IniSections::iterator e = m_params.iniData.find(groupName); + return (e != m_params.iniData.end()); +} + +std::string IniProcessing::fileName() +{ + return m_params.filePath; +} + +std::string IniProcessing::group() +{ + return m_params.currentGroupName; +} + +std::vector IniProcessing::childGroups() +{ + std::vector groups; + groups.reserve(m_params.iniData.size()); + for(params::IniSections::iterator e = m_params.iniData.begin(); + e != m_params.iniData.end(); + e++) + { + groups.push_back(e->first); + } + return groups; +} + +bool IniProcessing::hasKey(const std::string &keyName) +{ + if(!m_params.opened) + return false; + + if(!m_params.currentGroup) + return false; + + params::IniKeys::iterator e = m_params.currentGroup->find(keyName); + return (e != m_params.currentGroup->end()); +} + +std::vector IniProcessing::allKeys() +{ + std::vector keys; + if(!m_params.opened) + return keys; + if(!m_params.currentGroup) + return keys; + + keys.reserve(m_params.currentGroup->size()); + + for(params::IniKeys::iterator it = m_params.currentGroup->begin(); + it != m_params.currentGroup->end(); + it++) + { + keys.push_back( it->first ); + } + + return keys; +} + +void IniProcessing::endGroup() +{ + m_params.currentGroup = nullptr; + m_params.currentGroupName.clear(); +} + +void IniProcessing::read(const char *key, bool &dest, bool defVal) +{ + bool ok = false; + params::IniKeys::iterator e = readHelper(key, ok); + + if(!ok) + { + dest = defVal; + return; + } + + std::string &k = e->second; + size_t i = 0; + size_t ss = std::min(static_cast(4ul), k.size()); + char buff[4] = {0, 0, 0, 0}; + const char *pbufi = k.c_str(); + char *pbuff = buff; + + for(; i < ss; i++) + (*pbuff++) = static_cast(std::tolower(*pbufi++)); + + if(ss < 4) + { + if(ss == 0) + { + dest = false; + return; + } + + if(ss == 1) + { + dest = (buff[0] == '1'); + return; + } + + bool isNum = true; + isNum &= std::isdigit(buff[i]) || (buff[i] == '-') || (buff[i] == '+'); + + for(size_t i = 1; i < ss; i++) + isNum &= std::isdigit(buff[i]); + + if(isNum) + { + long num = std::strtol(buff, 0, 0); + dest = num != 0l; + return; + } + else + { + dest = (std::memcmp(buff, "yes", 3) == 0) || + (std::memcmp(buff, "on", 2) == 0); + return; + } + } + + if(std::memcmp(buff, "true", 4) == 0) + { + dest = true; + return; + } + + try + { + long num = std::strtol(buff, 0, 0); + dest = num != 0l; + return; + } + catch(...) + { + dest = false; + return; + } +} + +void IniProcessing::read(const char *key, unsigned char &dest, unsigned char defVal) +{ + bool ok = false; + params::IniKeys::iterator e = readHelper(key, ok); + + if(!ok) + { + dest = defVal; + return; + } + + std::string &k = e->second; + + if(k.size() >= 1) + dest = static_cast(k[0]); + else + dest = defVal; +} + +void IniProcessing::read(const char *key, char &dest, char defVal) +{ + bool ok = false; + params::IniKeys::iterator e = readHelper(key, ok); + + if(!ok) + { + dest = defVal; + return; + } + + std::string &k = e->second; + + if(k.size() >= 1) + dest = k[0]; + else + dest = defVal; +} + +void IniProcessing::read(const char *key, unsigned short &dest, unsigned short defVal) +{ + bool ok = false; + params::IniKeys::iterator e = readHelper(key, ok); + + if(!ok) + { + dest = defVal; + return; + } + + dest = static_cast(std::strtoul(e->second.c_str(), nullptr, 0)); +} + +void IniProcessing::read(const char *key, short &dest, short defVal) +{ + bool ok = false; + params::IniKeys::iterator e = readHelper(key, ok); + + if(!ok) + { + dest = defVal; + return; + } + + dest = static_cast(std::strtol(e->second.c_str(), nullptr, 0)); +} + +void IniProcessing::read(const char *key, unsigned int &dest, unsigned int defVal) +{ + bool ok = false; + params::IniKeys::iterator e = readHelper(key, ok); + + if(!ok) + { + dest = defVal; + return; + } + + dest = static_cast(std::strtoul(e->second.c_str(), nullptr, 0)); +} + +void IniProcessing::read(const char *key, int &dest, int defVal) +{ + bool ok = false; + params::IniKeys::iterator e = readHelper(key, ok); + + if(!ok) + { + dest = defVal; + return; + } + + dest = static_cast(std::strtol(e->second.c_str(), nullptr, 0)); +} + +void IniProcessing::read(const char *key, unsigned long &dest, unsigned long defVal) +{ + bool ok = false; + params::IniKeys::iterator e = readHelper(key, ok); + + if(!ok) + { + dest = defVal; + return; + } + + dest = std::strtoul(e->second.c_str(), nullptr, 0); +} + +void IniProcessing::read(const char *key, long &dest, long defVal) +{ + bool ok = false; + params::IniKeys::iterator e = readHelper(key, ok); + + if(!ok) + { + dest = defVal; + return; + } + + dest = std::strtol(e->second.c_str(), nullptr, 0); +} + +void IniProcessing::read(const char *key, unsigned long long &dest, unsigned long long defVal) +{ + bool ok = false; + params::IniKeys::iterator e = readHelper(key, ok); + + if(!ok) + { + dest = defVal; + return; + } + + dest = std::strtoull(e->second.c_str(), nullptr, 0); +} + +void IniProcessing::read(const char *key, long long &dest, long long defVal) +{ + bool ok = false; + params::IniKeys::iterator e = readHelper(key, ok); + + if(!ok) + { + dest = defVal; + return; + } + + dest = std::strtoll(e->second.c_str(), nullptr, 0); +} + +void IniProcessing::read(const char *key, float &dest, float defVal) +{ + bool ok = false; + params::IniKeys::iterator e = readHelper(key, ok); + if(!ok) + { + dest = defVal; + return; + } + dest = std::strtof(e->second.c_str(), nullptr); +} + +void IniProcessing::read(const char *key, double &dest, double defVal) +{ + bool ok = false; + params::IniKeys::iterator e = readHelper(key, ok); + + if(!ok) + { + dest = defVal; + return; + } + dest = std::strtod(e->second.c_str(), nullptr); +} + +void IniProcessing::read(const char *key, long double &dest, long double defVal) +{ + bool ok = false; + params::IniKeys::iterator e = readHelper(key, ok); + if(!ok) + { + dest = defVal; + return; + } + dest = std::strtold(e->second.c_str(), nullptr); +} + +void IniProcessing::read(const char *key, std::string &dest, const std::string &defVal) +{ + bool ok = false; + params::IniKeys::iterator e = readHelper(key, ok); + + if(!ok) + { + dest = defVal; + return; + } + + dest = e->second; +} + +#ifdef INI_PROCESSING_ALLOW_QT_TYPES +void IniProcessing::read(const char *key, QString &dest, const QString &defVal) +{ + bool ok = false; + params::IniKeys::iterator e = readHelper(key, ok); + + if(!ok) + { + dest = defVal; + return; + } + + dest = QString::fromStdString(e->second); +} +#endif + +template +inline void StrToNumVectorHelper(const std::string &source, TList &dest, const typename TList::value_type &def) +{ + typedef typename TList::value_type T; + dest.clear(); + + if(!source.empty()) + { + std::stringstream ss(source); + std::string item; + while(std::getline(ss, item, ',')) + { + std::remove(item.begin(), item.end(), ' '); + try + { + if(std::is_same::value || + std::is_same::value || + std::is_same::value) + dest.push_back(static_cast(std::strtol(item.c_str(), NULL, 0))); + else if(std::is_same::value || + std::is_same::value || + std::is_same::value) + dest.push_back(static_cast(std::strtoul(item.c_str(), NULL, 0))); + else if(std::is_same::value) + dest.push_back(std::strtof(item.c_str(), NULL)); + else + dest.push_back(std::strtod(item.c_str(), NULL)); + } + catch(...) + { + dest.pop_back(); + } + } + + if(dest.empty()) + dest.push_back(def); + } + else + dest.push_back(def); +} + +template +void readNumArrHelper(IniProcessing *self, const char *key, TList &dest, const TList &defVal) +{ + bool ok = false; + IniProcessing::params::IniKeys::iterator e = self->readHelper(key, ok); + + if(!ok) + { + dest = defVal; + return; + } + + StrToNumVectorHelper(e->second, dest, static_cast(0)); +} + +void IniProcessing::read(const char *key, std::vector &dest, const std::vector &defVal) +{ + readNumArrHelper, unsigned short>(this, key, dest, defVal); +} +void IniProcessing::read(const char *key, std::vector &dest, const std::vector &defVal) +{ + readNumArrHelper, short>(this, key, dest, defVal); +} +void IniProcessing::read(const char *key, std::vector &dest, const std::vector &defVal) +{ + readNumArrHelper, unsigned int>(this, key, dest, defVal); +} +void IniProcessing::read(const char *key, std::vector &dest, const std::vector &defVal) +{ + readNumArrHelper, int>(this, key, dest, defVal); +} +void IniProcessing::read(const char *key, std::vector &dest, const std::vector &defVal) +{ + readNumArrHelper, unsigned long>(this, key, dest, defVal); +} +void IniProcessing::read(const char *key, std::vector &dest, const std::vector &defVal) +{ + readNumArrHelper, long>(this, key, dest, defVal); +} +void IniProcessing::read(const char *key, std::vector &dest, const std::vector &defVal) +{ + readNumArrHelper, unsigned long long>(this, key, dest, defVal); +} +void IniProcessing::read(const char *key, std::vector &dest, const std::vector &defVal) +{ + readNumArrHelper, long long>(this, key, dest, defVal); +} +void IniProcessing::read(const char *key, std::vector &dest, const std::vector &defVal) +{ + readNumArrHelper, float>(this, key, dest, defVal); +} +void IniProcessing::read(const char *key, std::vector &dest, const std::vector &defVal) +{ + readNumArrHelper, double>(this, key, dest, defVal); +} + +void IniProcessing::read(const char *key, std::vector &dest, const std::vector &defVal) +{ + readNumArrHelper, long double>(this, key, dest, defVal); +} + +#ifdef INI_PROCESSING_ALLOW_QT_TYPES +void IniProcessing::read(const char *key, QList &dest, const QList &defVal) +{ + readNumArrHelper, short>(this, key, dest, defVal); +} + +void IniProcessing::read(const char *key, QList &dest, const QList &defVal) +{ + readNumArrHelper, unsigned short>(this, key, dest, defVal); +} + +void IniProcessing::read(const char *key, QList &dest, const QList &defVal) +{ + readNumArrHelper, int>(this, key, dest, defVal); +} +void IniProcessing::read(const char *key, QList &dest, const QList &defVal) +{ + readNumArrHelper, unsigned int>(this, key, dest, defVal); +} +void IniProcessing::read(const char *key, QList &dest, const QList &defVal) +{ + readNumArrHelper, long>(this, key, dest, defVal); +} +void IniProcessing::read(const char *key, QList &dest, const QList &defVal) +{ + readNumArrHelper, unsigned long>(this, key, dest, defVal); +} +void IniProcessing::read(const char *key, QList &dest, const QList &defVal) +{ + readNumArrHelper, long long>(this, key, dest, defVal); +} +void IniProcessing::read(const char *key, QList &dest, const QList &defVal) +{ + readNumArrHelper, unsigned long long>(this, key, dest, defVal); +} +void IniProcessing::read(const char *key, QList &dest, const QList &defVal) +{ + readNumArrHelper, float>(this, key, dest, defVal); +} +void IniProcessing::read(const char *key, QList &dest, const QList &defVal) +{ + readNumArrHelper, double>(this, key, dest, defVal); +} + +void IniProcessing::read(const char *key, QList &dest, const QList &defVal) +{ + readNumArrHelper, long double>(this, key, dest, defVal); +} + +void IniProcessing::read(const char *key, QVector &dest, const QVector &defVal) +{ + readNumArrHelper, short>(this, key, dest, defVal); +} +void IniProcessing::read(const char *key, QVector &dest, const QVector &defVal) +{ + readNumArrHelper, unsigned short>(this, key, dest, defVal); +} +void IniProcessing::read(const char *key, QVector &dest, const QVector &defVal) +{ + readNumArrHelper, int>(this, key, dest, defVal); +} +void IniProcessing::read(const char *key, QVector &dest, const QVector &defVal) +{ + readNumArrHelper, unsigned int>(this, key, dest, defVal); +} +void IniProcessing::read(const char *key, QVector &dest, const QVector &defVal) +{ + readNumArrHelper, long>(this, key, dest, defVal); +} +void IniProcessing::read(const char *key, QVector &dest, const QVector &defVal) +{ + readNumArrHelper, unsigned long>(this, key, dest, defVal); +} +void IniProcessing::read(const char *key, QVector &dest, const QVector &defVal) +{ + readNumArrHelper, long long>(this, key, dest, defVal); +} +void IniProcessing::read(const char *key, QVector &dest, const QVector &defVal) +{ + readNumArrHelper, unsigned long long>(this, key, dest, defVal); +} +void IniProcessing::read(const char *key, QVector &dest, const QVector &defVal) +{ + readNumArrHelper, float>(this, key, dest, defVal); +} +void IniProcessing::read(const char *key, QVector &dest, const QVector &defVal) +{ + readNumArrHelper, double>(this, key, dest, defVal); +} + +void IniProcessing::read(const char *key, QVector &dest, const QVector &defVal) +{ + readNumArrHelper, long double>(this, key, dest, defVal); +} +#endif + +IniProcessingVariant IniProcessing::value(const char *key, const IniProcessingVariant &defVal) +{ + bool ok = false; + params::IniKeys::iterator e = readHelper(key, ok); + + if(!ok) + return defVal; + + std::string &k = e->second; + return IniProcessingVariant(&k); +} + +void IniProcessing::writeIniParam(const char *key, const std::string &value) +{ + if(m_params.currentGroupName.empty()) + return; + + bool ok = false; + params::IniKeys::iterator e = readHelper(key, ok); + if(ok) + { + e->second = value; + } + else + { + if(!m_params.currentGroup) + { + m_params.iniData.insert({m_params.currentGroupName, params::IniKeys()}); + m_params.currentGroup = &m_params.iniData[m_params.currentGroupName]; + } + m_params.currentGroup->insert({std::string(key), value}); + //Mark as opened + m_params.opened = true; + } +} + +void IniProcessing::setValue(const char *key, unsigned short value) +{ + writeIniParam(key, std::to_string(value)); +} + +void IniProcessing::setValue(const char *key, short value) +{ + writeIniParam(key, std::to_string(value)); +} + +void IniProcessing::setValue(const char *key, unsigned int value) +{ + writeIniParam(key, std::to_string(value)); +} + +void IniProcessing::setValue(const char *key, int value) +{ + writeIniParam(key, std::to_string(value)); +} + +void IniProcessing::setValue(const char *key, unsigned long value) +{ + writeIniParam(key, std::to_string(value)); +} + +void IniProcessing::setValue(const char *key, long value) +{ + writeIniParam(key, std::to_string(value)); +} + +void IniProcessing::setValue(const char *key, unsigned long long value) +{ + writeIniParam(key, std::to_string(value)); +} + +void IniProcessing::setValue(const char *key, long long value) +{ + writeIniParam(key, std::to_string(value)); +} + +void IniProcessing::setValue(const char *key, float value) +{ + writeIniParam(key, IniProcessing::to_string_with_precision(value)); +} + +void IniProcessing::setValue(const char *key, double value) +{ + writeIniParam(key, IniProcessing::to_string_with_precision(value)); +} + +void IniProcessing::setValue(const char *key, long double value) +{ + writeIniParam(key, IniProcessing::to_string_with_precision(value)); +} + +void IniProcessing::setValue(const char *key, const char *value) +{ + writeIniParam(key, value); +} + +void IniProcessing::setValue(const char *key, const std::string &value) +{ + writeIniParam(key, value); +} + +#ifdef INI_PROCESSING_ALLOW_QT_TYPES +void IniProcessing::setValue(const char *key, const QString &value) +{ + writeIniParam(key, value.toStdString()); +} +#endif + +static inline bool isFloatValue(const std::string &str) +{ + enum State + { + ST_SIGN = 0, + ST_DOT, + ST_EXPONENT, + ST_EXPONENT_SIGN, + ST_TAIL + } st = ST_SIGN; + + for(const char &c : str) + { + if(!isdigit(c)) + { + switch(st) + { + case ST_SIGN: + if(c != '-') + return false; + st = ST_DOT; + continue; + case ST_DOT: + if(c != '.') + return false; + else + if((c == 'E') || (c == 'e')) + st = ST_EXPONENT_SIGN; + else + st = ST_EXPONENT; + continue; + case ST_EXPONENT: + if((c != 'E') && (c != 'e')) + return false; + st = ST_EXPONENT_SIGN; + continue; + case ST_EXPONENT_SIGN: + if(c != '-') + return false; + st = ST_TAIL; + continue; + case ST_TAIL: + return false; + } + return false; + } + else + { + if(st == ST_SIGN) + st = ST_DOT; + } + } + return true; +} + +bool IniProcessing::writeIniFile() +{ + #ifdef _WIN32 + //Convert UTF8 file path into UTF16 to support non-ASCII paths on Windows + std::wstring dest; + dest.resize(m_params.filePath.size()); + int newSize = MultiByteToWideChar(CP_UTF8, + 0, + m_params.filePath.c_str(), + dest.size(), + (wchar_t *)dest.c_str(), + dest.size()); + dest.resize(newSize); + FILE *cFile = _wfopen(dest.c_str(), L"wb"); + #else + FILE *cFile = fopen(m_params.filePath.c_str(), "wb"); + #endif + if(!cFile) + return false; + + for(params::IniSections::iterator group = m_params.iniData.begin(); + group != m_params.iniData.end(); + group++) + { + fprintf(cFile, "[%s]\n", group->first.c_str()); + for(params::IniKeys::iterator key = group->second.begin(); + key != group->second.end(); + key++) + { + if(isFloatValue(key->second)) + { + //Store as-is without quoting + fprintf(cFile, "%s = %s\n", key->first.c_str(), key->second.c_str()); + } + else + { + //Set escape quotes and put the string with a quotes + std::string &s = key->second; + std::string escaped; + escaped.reserve(s.length() * 2); + for(char &c : s) + { + switch(c) + { + case '\n': escaped += "\\n"; break; + case '\r': escaped += "\\r"; break; + case '\t': escaped += "\\t"; break; + default: + if((c == '\\') || (c == '"')) + escaped.push_back('\\'); + escaped.push_back(c); + } + } + fprintf(cFile, "%s = \"%s\"\n", key->first.c_str(), escaped.c_str()); + } + } + fprintf(cFile, "\n"); + fflush(cFile); + } + fclose(cFile); + return true; +} + -- cgit v1.2.3