1 /* 2 * PROJECT: ReactOS Applications Manager 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * PURPOSE: Config parser 5 * COPYRIGHT: Copyright 2009 Dmitry Chapyshev (dmitry@reactos.org) 6 * Copyright 2015 Ismael Ferreras Morezuelas (swyterzone+ros@gmail.com) 7 * Copyright 2017 Alexander Shaposhnikov (sanchaez@reactos.org) 8 * Copyright 2021 Mark Jansen <mark.jansen@reactos.org> 9 */ 10 11 #include "rapps.h" 12 #include <debug.h> 13 14 struct CLocaleSections 15 { 16 CStringW Locale; 17 CStringW LocaleNeutral; 18 CStringW Section; 19 }; 20 21 struct CSectionNames 22 { 23 CLocaleSections ArchSpecific; 24 CLocaleSections ArchNeutral; 25 }; 26 static CSectionNames g_Names; 27 28 HRESULT 29 ReadIniValue(LPCWSTR File, LPCWSTR Section, LPCWSTR Name, CStringW &Output) 30 { 31 for (DWORD len = 256, ret;; len *= 2) 32 { 33 ret = GetPrivateProfileString(Section, Name, L"\n", Output.GetBuffer(len), len, File); 34 if (ret + 1 != len) 35 { 36 Output.ReleaseBuffer(ret); 37 return ret && Output[0] != L'\n' ? ret : HRESULT_FROM_WIN32(ERROR_NOT_FOUND); 38 } 39 } 40 } 41 42 CConfigParser::CConfigParser(const CStringW &FilePath) : szConfigPath(FilePath) 43 { 44 CacheINI(); 45 } 46 47 void 48 CConfigParser::ReadSection(CStringW &Buffer, const CStringW &Section, BOOL isArch) 49 { 50 DWORD len = 512; 51 DWORD result; 52 53 do 54 { 55 len *= 2; 56 57 result = GetPrivateProfileSectionW(Section, Buffer.GetBuffer(len), len, szConfigPath); 58 Buffer.ReleaseBuffer(result); 59 } while (result == len - 2); 60 61 len = 0; 62 while (len < result) 63 { 64 // Explicitly use the null terminator! 65 CString tmp = Buffer.GetBuffer() + len; 66 if (tmp.GetLength() > 0) 67 { 68 len += tmp.GetLength() + 1; 69 70 int idx = tmp.Find('='); 71 if (idx >= 0) 72 { 73 CString key = tmp.Left(idx); 74 75 #ifndef _M_IX86 76 // On non-x86 architecture we need the architecture specific URL 77 if (!isArch && key == "URLDownload") 78 { 79 continue; 80 } 81 #endif 82 83 // Is this key already present from a more specific translation? 84 if (m_Keys.FindKey(key) >= 0) 85 { 86 continue; 87 } 88 89 CString value = tmp.Mid(idx + 1); 90 m_Keys.Add(key, value); 91 } 92 else 93 { 94 DPRINT1("ERROR: invalid key/value pair: '%S'\n", tmp.GetString()); 95 } 96 } 97 else 98 { 99 break; 100 } 101 } 102 } 103 104 VOID 105 CConfigParser::CacheINI() 106 { 107 // Cache section names 108 if (g_Names.ArchSpecific.Locale.IsEmpty()) 109 { 110 CString szLocaleID; 111 const INT cchLocaleSize = 5; 112 113 GetLocaleInfoW(GetUserDefaultLCID(), LOCALE_ILANGUAGE, szLocaleID.GetBuffer(cchLocaleSize), cchLocaleSize); 114 szLocaleID.ReleaseBuffer(); 115 CString INISectionLocale = L"Section." + szLocaleID; 116 117 g_Names.ArchSpecific.Locale = INISectionLocale + L"." CurrentArchitecture; 118 g_Names.ArchNeutral.Locale = INISectionLocale; 119 120 // turn "Section.0c0a" into "Section.0a", keeping just the neutral lang part 121 if (szLocaleID.GetLength() >= 2) 122 { 123 g_Names.ArchSpecific.LocaleNeutral = L"Section." + szLocaleID.Right(2) + L"." CurrentArchitecture; 124 g_Names.ArchNeutral.LocaleNeutral = L"Section." + szLocaleID.Right(2); 125 } 126 127 g_Names.ArchSpecific.Section = L"Section." CurrentArchitecture; 128 g_Names.ArchNeutral.Section = L"Section"; 129 } 130 131 // Use a shared buffer so that we don't have to re-allocate it every time 132 CStringW Buffer; 133 134 ReadSection(Buffer, g_Names.ArchSpecific.Locale, TRUE); 135 if (!g_Names.ArchSpecific.LocaleNeutral.IsEmpty()) 136 { 137 ReadSection(Buffer, g_Names.ArchSpecific.LocaleNeutral, TRUE); 138 } 139 ReadSection(Buffer, g_Names.ArchSpecific.Section, TRUE); 140 141 ReadSection(Buffer, g_Names.ArchNeutral.Locale, FALSE); 142 if (!g_Names.ArchNeutral.LocaleNeutral.IsEmpty()) 143 { 144 ReadSection(Buffer, g_Names.ArchNeutral.LocaleNeutral, FALSE); 145 } 146 ReadSection(Buffer, g_Names.ArchNeutral.Section, FALSE); 147 } 148 149 BOOL 150 CConfigParser::GetString(const CStringW &KeyName, CStringW &ResultString) 151 { 152 int nIndex = m_Keys.FindKey(KeyName); 153 if (nIndex >= 0) 154 { 155 ResultString = m_Keys.GetValueAt(nIndex); 156 return TRUE; 157 } 158 159 ResultString.Empty(); 160 return FALSE; 161 } 162 163 BOOL 164 CConfigParser::GetInt(const CStringW &KeyName, INT &iResult) 165 { 166 CStringW Buffer; 167 168 iResult = 0; 169 170 // grab the text version of our entry 171 if (!GetString(KeyName, Buffer)) 172 return FALSE; 173 174 if (Buffer.IsEmpty()) 175 return FALSE; 176 177 // convert it to an actual integer 178 iResult = StrToIntW(Buffer.GetString()); 179 180 // we only care about values > 0 181 return (iResult > 0); 182 } 183 184 UINT 185 CConfigParser::GetSectionString(LPCWSTR Section, LPCWSTR Name, CStringW &Result) 186 { 187 HRESULT hr; // Return value; length of ini string or 0 on failure. 188 CStringW SecBuf; 189 WCHAR FullLoc[5], *NeutralLoc = FullLoc + 2; 190 GetLocaleInfoW(GetUserDefaultLCID(), LOCALE_ILANGUAGE, FullLoc, _countof(FullLoc)); 191 192 SecBuf.Format(L"%s.%s.%s", Section, FullLoc, CurrentArchitecture); 193 if ((hr = ReadIniValue(szConfigPath, SecBuf, Name, Result)) > 0) 194 return hr; 195 196 if (*NeutralLoc) 197 { 198 SecBuf.Format(L"%s.%s.%s", Section, NeutralLoc, CurrentArchitecture); 199 if ((hr = ReadIniValue(szConfigPath, SecBuf, Name, Result)) > 0) 200 return hr; 201 } 202 203 SecBuf.Format(L"%s.%s", Section, CurrentArchitecture); 204 if ((hr = ReadIniValue(szConfigPath, SecBuf, Name, Result)) > 0) 205 return hr; 206 207 SecBuf.Format(L"%s.%s", Section, FullLoc); 208 if ((hr = ReadIniValue(szConfigPath, SecBuf, Name, Result)) > 0) 209 return hr; 210 211 if (*NeutralLoc) 212 { 213 SecBuf.Format(L"%s.%s", Section, NeutralLoc); 214 if ((hr = ReadIniValue(szConfigPath, SecBuf, Name, Result)) > 0) 215 return hr; 216 } 217 218 if ((hr = ReadIniValue(szConfigPath, Section, Name, Result)) > 0) 219 return hr; 220 return 0; 221 } 222