OSDN Git Service

IniOptionsMgr.*: Reduce the number of reads from the INI file (2)
[winmerge-jp/winmerge-jp.git] / Src / Common / IniOptionsMgr.cpp
1 /**
2  * @file IniOptionsMgr.cpp
3  *
4  * @brief Implementation of Ini file Options management class.
5  *
6  */
7
8 #include "pch.h"
9 #include "IniOptionsMgr.h"
10 #include "OptionsMgr.h"
11 #include <Windows.h>
12 #include <codecvt>
13 #include <filesystem>
14 #include <string>
15 #include <fstream>
16
17 using std::filesystem::current_path;
18
19 LPCWSTR CIniOptionsMgr::lpFilePath = NULL;
20
21 LPCWSTR lpAppName = TEXT("WinMerge");
22 LPCWSTR lpFileName = TEXT("\\winmerge.ini");
23
24 CIniOptionsMgr::CIniOptionsMgr()
25         : m_serializing(true)
26 {
27         InitializeCriticalSection(&m_cs);
28 }
29
30 CIniOptionsMgr::~CIniOptionsMgr()
31 {
32         DeleteCriticalSection(&m_cs);
33         delete[] CIniOptionsMgr::lpFilePath;
34 }
35
36 /**
37  * @brief Checks wheter INI file exists.
38  * @return TRUE if INI file exist,
39  *   FALSE otherwise.
40  */
41 bool CIniOptionsMgr::CheckIfIniFileExist()
42 {
43         std::ifstream f(GetFilePath());
44         return f.good();
45 }
46
47 /**
48  * @brief Get path to INI file.
49  * @return path to INI file
50  */
51 LPCWSTR CIniOptionsMgr::GetFilePath()
52 {
53         if (CIniOptionsMgr::lpFilePath == NULL)
54         {
55                 // create path
56                 std::filesystem::path p = current_path();
57                 p += lpFileName;
58
59                 // change type
60                 std::wstring str = p.wstring();
61                 size_t length = str.length() + 1;
62                 wchar_t* strCp = new wchar_t[length];
63                 wcscpy_s(strCp, length, str.c_str());
64
65                 // set path
66                 CIniOptionsMgr::lpFilePath = strCp;
67         }
68
69         return CIniOptionsMgr::lpFilePath;
70 }
71
72 int CIniOptionsMgr::InitOption(const String& name, const varprop::VariantValue& defaultValue)
73 {
74         // Check type & bail if null
75         int valType = defaultValue.GetType();
76         if (valType == varprop::VT_NULL)
77                 return COption::OPT_ERR;
78
79         // If we're not loading & saving options, bail
80         if (!m_serializing)
81                 return AddOption(name, defaultValue);
82
83         EnterCriticalSection(&m_cs);
84
85         // check if value exist
86         String textValue = ReadValueFromFile(name);
87         bool found = textValue.size() != 0;
88
89         // Actually save value into our in-memory options table
90         int retVal = AddOption(name, defaultValue);
91
92         // Update registry if successfully saved to in-memory table
93         if (retVal == COption::OPT_OK)
94         {
95                 if (found)
96                 {
97                         varprop::VariantValue value(defaultValue);
98                         retVal = ParseValue(name, textValue, value);
99                         if (retVal == COption::OPT_OK)
100                         {
101                                 retVal = Set(name, value);
102                         }
103                 }
104         }
105
106         LeaveCriticalSection(&m_cs);
107         return retVal;
108 }
109
110 int CIniOptionsMgr::InitOption(const String& name, const String& defaultValue)
111 {
112         varprop::VariantValue defValue;
113         defValue.SetString(defaultValue);
114         return InitOption(name, defValue);
115 }
116
117 int CIniOptionsMgr::InitOption(const String& name, const TCHAR* defaultValue)
118 {
119         return InitOption(name, String(defaultValue));
120 }
121
122 int CIniOptionsMgr::InitOption(const String& name, int defaultValue, bool serializable)
123 {
124         varprop::VariantValue defValue;
125         int retVal = COption::OPT_OK;
126
127         defValue.SetInt(defaultValue);
128         if (serializable)
129                 retVal = InitOption(name, defValue);
130         else
131                 AddOption(name, defValue);
132         return retVal;
133 }
134
135 int CIniOptionsMgr::InitOption(const String& name, bool defaultValue)
136 {
137         varprop::VariantValue defValue;
138         defValue.SetBool(defaultValue);
139         return InitOption(name, defValue);
140 }
141
142 int CIniOptionsMgr::SaveOption(const String& name)
143 {
144         if (!m_serializing) return COption::OPT_OK;
145
146         varprop::VariantValue value;
147         int retVal = COption::OPT_OK;
148
149         value = Get(name);
150         int valType = value.GetType();
151         if (valType == varprop::VT_NULL)
152                 retVal = COption::OPT_NOTFOUND;
153
154         if (retVal == COption::OPT_OK)
155         {
156                 if (valType == varprop::VT_STRING)
157                 {
158                         String strVal = value.GetString();
159                         LPCWSTR text = strVal.c_str();
160                         WritePrivateProfileString(lpAppName, name.c_str(), text, GetFilePath());
161                 }
162                 else if (valType == varprop::VT_INT)
163                 {
164                         DWORD dwordVal = value.GetInt();
165                         String strVal = strutils::to_str(dwordVal);
166                         LPCWSTR text = strVal.c_str();
167                         WritePrivateProfileString(lpAppName, name.c_str(), text, GetFilePath());
168                 }
169                 else if (valType == varprop::VT_BOOL)
170                 {
171                         DWORD dwordVal = value.GetBool() ? 1 : 0;
172                         String strVal = strutils::to_str(dwordVal);
173                         LPCWSTR text = strVal.c_str();
174                         WritePrivateProfileString(lpAppName, name.c_str(), text, GetFilePath());
175                 }
176                 else
177                 {
178                         retVal = COption::OPT_UNKNOWN_TYPE;
179                 }
180         }
181         return retVal;
182 }
183
184 /**
185  * @brief Set new value for option and save option to file
186  */
187 int CIniOptionsMgr::SaveOption(const String& name, const varprop::VariantValue& value)
188 {
189         int retVal = Set(name, value);
190         if (retVal == COption::OPT_OK)
191                 retVal = SaveOption(name);
192         return retVal;
193 }
194
195 /**
196  * @brief Set new string value for option and save option to file
197  */
198 int CIniOptionsMgr::SaveOption(const String& name, const String& value)
199 {
200         varprop::VariantValue val;
201         val.SetString(value);
202         int retVal = Set(name, val);
203         if (retVal == COption::OPT_OK)
204                 retVal = SaveOption(name);
205         return retVal;
206 }
207
208 /**
209  * @brief Set new string value for option and save option to file
210  */
211 int CIniOptionsMgr::SaveOption(const String& name, const TCHAR* value)
212 {
213         return SaveOption(name, String(value));
214 }
215
216 int CIniOptionsMgr::SaveOption(const String& name, int value)
217 {
218         varprop::VariantValue val;
219         val.SetInt(value);
220         int retVal = Set(name, val);
221         if (retVal == COption::OPT_OK)
222                 retVal = SaveOption(name);
223         return retVal;
224 }
225
226 int CIniOptionsMgr::SaveOption(const String& name, bool value)
227 {
228         varprop::VariantValue val;
229         val.SetBool(value);
230         int retVal = Set(name, val);
231         if (retVal == COption::OPT_OK)
232                 retVal = SaveOption(name);
233         return retVal;
234 }
235
236 int CIniOptionsMgr::RemoveOption(const String& name)
237 {
238         int retVal = COption::OPT_OK;
239
240         String strPath;
241         String strValueName;
242
243         SplitName(name, strPath, strValueName);
244
245         if (!strValueName.empty())
246         {
247                 retVal = COptionsMgr::RemoveOption(name);
248         }
249         else
250         {
251                 for (auto it = m_optionsMap.begin(); it != m_optionsMap.end(); )
252                 {
253                         if (it->first.find(strPath) == 0)
254                                 it = m_optionsMap.erase(it);
255                         else
256                                 ++it;
257                 }
258                 retVal = COption::OPT_OK;
259         }
260
261         EnterCriticalSection(&m_cs);
262
263         WritePrivateProfileString(lpAppName, name.c_str(), NULL, GetFilePath());
264
265         LeaveCriticalSection(&m_cs);
266
267         return retVal;
268 }
269
270 String CIniOptionsMgr::ReadValueFromFile(const String& name)
271 {
272         if (m_iniFileKeyValues.empty())
273         {
274                 std::vector<TCHAR> str(32768);
275                 if (GetPrivateProfileSection(lpAppName, str.data(), static_cast<DWORD>(str.size()), GetFilePath()) > 0)
276                 {
277                         TCHAR* p = str.data();
278                         while (*p)
279                         {
280                                 TCHAR* v = _tcschr(p, '=');
281                                 if (!v)
282                                         break;
283                                 ++v;
284                                 size_t vlen = _tcslen(v);
285                                 m_iniFileKeyValues.insert_or_assign(String{ p, v - 1 }, String{v, v + vlen});
286                                 p = v + vlen + 1;
287                         }
288                 }
289         }
290         return m_iniFileKeyValues[name];
291 }
292
293 int CIniOptionsMgr::ParseValue(const String& strName, String& textValue, varprop::VariantValue& value)
294 {
295         int valType = value.GetType();
296         int retVal = COption::OPT_OK;
297
298         if (valType == varprop::VT_STRING)
299         {
300                 value.SetString(textValue);
301                 retVal = Set(strName, value);
302         }
303         else if (valType == varprop::VT_INT)
304         {
305                 value.SetInt(std::stoul(textValue));
306                 retVal = Set(strName, value);
307         }
308         else if (valType == varprop::VT_BOOL)
309         {
310                 value.SetBool(textValue[0] == '1' ? true : false);
311                 retVal = Set(strName, value);
312         }
313         else
314                 retVal = COption::OPT_WRONG_TYPE;
315
316         return retVal;
317 }
318
319 /**
320  * @brief Split option name to path (in registry) and
321  * valuename (in registry).
322  *
323  * Option names are given as "full path", e.g. "Settings/AutomaticRescan".
324  * This function splits that to path "Settings/" and valuename
325  * "AutomaticRescan".
326  * @param [in] strName Option name
327  * @param [out] srPath Path (key) in registry
328  * @param [out] strValue Value in registry
329  */
330 void CIniOptionsMgr::SplitName(const String& strName, String& strPath,
331         String& strValue) const
332 {
333         size_t pos = strName.rfind('/');
334         if (pos != String::npos)
335         {
336                 size_t len = strName.length();
337                 strValue = strName.substr(pos + 1, len - pos - 1); //Right(len - pos - 1);
338                 strPath = strName.substr(0, pos);  //Left(pos);
339         }
340         else
341         {
342                 strValue = strName;
343                 strPath.erase();
344         }
345 }