#include "Core.h" namespace Upp { static void LoadIniStream(Stream &sin, VectorMap& ret, const char *sfile); static void LoadIniFile(const char *filename, VectorMap& ret) { FileIn in(filename); if(in) LoadIniStream(in, ret, filename); } static void LoadIniStream(Stream& in, VectorMap& key, const char *sfile) { bool env = false; while(!in.IsEof()) { String line = in.GetLine(); CParser p(line); if(p.IsId()) { String k = p.ReadId(); if(p.Char('=')) { String h = TrimBoth((String)p.GetSpacePtr()); if(env) { String hh; const char *s = ~h; while(*s) { if(*s == '$') { s++; if(*s == '$') { hh.Cat('$'); s++; } else { String id; if (*s == '{') { while(*++s != '}') id.Cat(*s); s++; } else { while(iscid(*s)) id.Cat(*s++); } hh.Cat(GetEnv(id)); } } else hh.Cat(*s++); } key.Add(k, hh); } else key.Add(k, h); } } else if(p.Char('@')) { if(p.Id("include")) { String fn = p.GetPtr(); if(!IsFullPath(fn) && sfile) fn = AppendFileName(GetFileFolder(GetFullPath(sfile)), fn); LoadIniFile(fn, key); } else if(p.Id("end")) return; else if(p.Id("replace-env")) env = true; else if(p.Id("ignore-env")) env = false; } } } VectorMap LoadIniStream(Stream &sin) { VectorMap ret; LoadIniStream(sin, ret, NULL); return ret; } VectorMap LoadIniFile(const char *filename) { VectorMap ret; LoadIniFile(filename, ret); return ret; } static StaticMutex sMtx; static char sIniFile[512]; int ini_version__ = 1; void ReloadIniFile() { Mutex::Lock __(sMtx); ini_version__++; } void SetIniFile(const char *name) { Mutex::Lock __(sMtx); strncpy(sIniFile, name, 511); ReloadIniFile(); } void IniSet__(int& version) { BarrierWrite(version, ini_version__); } #ifdef flagSO bool IniChanged__(int version) { return version != ReadWithBarrier(ini_version__); } #endif String GetIniFile() { return *sIniFile ? sIniFile : ~ConfigFile("q.ini"); } static VectorMap& sIniKeys() { static VectorMap key; static int version; if(version != ini_version__) { version = ini_version__; key = LoadIniFile(GetIniFile()); #ifdef PLATFORM_WIN32 if(key.GetCount() == 0) key = LoadIniFile(~GetExeDirFile("q.ini")); if(key.GetCount() == 0) key = LoadIniFile("c:\\q.ini"); #endif #ifdef PLATFORM_POSIX if(key.GetCount() == 0) key = LoadIniFile(GetHomeDirFile("q.ini")); #endif } return key; } VectorMap GetIniKeys() { Mutex::Lock __(sMtx); return clone(sIniKeys()); } String GetIniKey(const char *id, const String& def) { ASSERT_(IsMainRunning(), "GetIniKey is allowed only after APP_MAIN has started"); Mutex::Lock __(sMtx); return sIniKeys().Get(id, def); } String GetIniKey(const char *id) { return GetIniKey(id, String()); } static StaticMutex strMtx; IniString::operator String() { String x; { Mutex::Lock __(strMtx); String& s = (*ref_fn)(); if(IniChanged__(version)) { s = TrimBoth(GetIniKey(id)); if(IsNull(s)) s = (*def)(); } x = s; IniSet__(version); } return x; } String IniString::operator=(const String& s) { Mutex::Lock __(strMtx); (*ref_fn)() = s; IniSet__(version); return s; } String IniString::ToString() const { return (String)const_cast(*this); } int64 ReadIniInt(const char *id) { String s = GetIniKey(id); CParser p(s); int64 num; int sgn = 1; if(p.Char('-')) sgn = -1; else p.Char('+'); if(p.Char2('0', 'x') || p.Char2('0', 'X')) num = p.ReadNumber64(16); else if(p.IsNumber()) num = p.ReadNumber(); else return Null; num = sgn * num; if(p.Char('K')) num <<= 10; else if(p.Char('M')) num <<= 20; else if(p.Char('G')) num <<= 30; else if(p.Char('T')) num <<= 40; return num; } int IniInt::Load() { Mutex::Lock __(sMtx); if(IniChanged__(version)) { int64 v = ReadIniInt(id); value = IsNull(v) ? (*def)() : (int)v; IniSet__(version); } return value; } int IniInt::operator=(int b) { Mutex::Lock __(sMtx); BarrierWrite(version, -1); value = b; IniSet__(version); return b; } String IniInt::ToString() const { return AsString((int)const_cast(*this)); } static StaticMutex si64Mtx; IniInt64::operator int64() { Mutex::Lock __(si64Mtx); if(IniChanged__(version)) { value = ReadIniInt(id); if(IsNull(value)) value = (*def)(); IniSet__(version); } return value; } int64 IniInt64::operator=(int64 b) { Mutex::Lock __(si64Mtx); value = b; return b; } String IniInt64::ToString() const { return AsString((int64)const_cast(*this)); } double IniDouble::Load() { Mutex::Lock __(sMtx); if(IniChanged__(version)) { value = ScanDouble(TrimBoth(ToLower(GetIniKey(id)))); if(IsNull(value)) value = (*def)(); IniSet__(version); } return value; } double IniDouble::operator=(double b) { Mutex::Lock __(sMtx); BarrierWrite(version, -1); value = b; IniSet__(version); return b; } String IniDouble::ToString() const { return AsString((double)const_cast(*this)); } bool IniBool::Load() { Mutex::Lock __(sMtx); if(IniChanged__(version)) { String h = TrimBoth(ToLower(GetIniKey(id))); if(h.GetCount()) value = h == "1" || h == "yes" || h == "true" || h == "y"; else value = (*def)(); IniSet__(version); } return value; } bool IniBool::operator=(bool b) { Mutex::Lock __(sMtx); BarrierWrite(version, -1); value = b; IniSet__(version); return b; } String IniBool::ToString() const { return AsString((bool)const_cast(*this)); } Array& sIniInfo() { static Array s; return s; } void AddIniInfo(const char *id, String (*current)(), String (*def)(), const char *info) { IniInfo& f = sIniInfo().Add(); f.id = id; f.current = current; f.def = def; f.info = info; } const Array& GetIniInfo() { return sIniInfo(); } String GetIniInfoFormatted() { String r; for(int i = 0; i < sIniInfo().GetCount(); i++) { IniInfo& f = sIniInfo()[i]; r << f.id << " = " << (*f.current)() << " [default: " << (*f.def)() << "]\r\n" << " " << f.info << "\r\n"; } return r; } String DefaultIniFileContent() { String r; for(int i = 0; i < sIniInfo().GetCount(); i++) { IniInfo& f = sIniInfo()[i]; r << "#" << f.info << "\r\n" << f.id << '=' << (*f.def)() << "\r\n\r\n"; } return r; } String CurrentIniFileContent(bool comment_defaults) { String r; for(int i = 0; i < sIniInfo().GetCount(); i++) { IniInfo& f = sIniInfo()[i]; r << "#" << f.info << "\r\n"; if (comment_defaults && (*f.current)() == (*f.def)()) r << '#'; r << f.id << '=' << (*f.current)() << "\r\n\r\n"; } return r; } void TextSettings::Load(const char *filename) { FileIn in(filename); int themei = 0; settings.Add(""); while(!in.IsEof()) { String ln = in.GetLine(); const char *s = ln; if(*s == '[') { s++; String theme; while(*s && *s != ']') theme.Cat(*s++); themei = settings.FindAdd(theme); } else { if(themei >= 0) { String key; while(*s && *s != '=') { key.Cat(*s++); } if(*s == '=') s++; String value; while(*s) { value.Cat(*s++); } if(!IsEmpty(key)) settings[themei].GetAdd(TrimBoth(key)) = TrimBoth(value); } } } } String TextSettings::Get(const char *group, const char *key) const { int itemi = settings.Find(group); return itemi < 0 ? Null : settings.Get(group).Get(key, Null); } String TextSettings::Get(int groupIndex, const char *key) const { return groupIndex >= 0 && groupIndex < settings.GetCount() ? settings[groupIndex].Get(key, Null) : Null; } String TextSettings::Get(int groupIndex, int keyIndex) const { if (groupIndex >= 0 && groupIndex < settings.GetCount()) return keyIndex >= 0 && keyIndex < settings[groupIndex].GetCount() ? settings[groupIndex][keyIndex] : Null; else return Null; } };