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
ReadIniValue(LPCWSTR File,LPCWSTR Section,LPCWSTR Name,CStringW & Output)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
CConfigParser(const CStringW & FilePath)42 CConfigParser::CConfigParser(const CStringW &FilePath) : szConfigPath(FilePath)
43 {
44 CacheINI();
45 }
46
47 void
ReadSection(CStringW & Buffer,const CStringW & Section,BOOL isArch)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
CacheINI()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
GetString(const CStringW & KeyName,CStringW & ResultString)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
GetInt(const CStringW & KeyName,INT & iResult)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
GetSectionString(LPCWSTR Section,LPCWSTR Name,CStringW & Result)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