1 |
/* |
2 |
* $Id: IniFile.h,v 1.4 2007-08-18 08:52:18 maya Exp $ |
3 |
*/ |
4 |
|
5 |
#ifndef _YCL_INIFILE_H_ |
6 |
#define _YCL_INIFILE_H_ |
7 |
|
8 |
#if _MSC_VER >= 1000 |
9 |
#pragma once |
10 |
#endif // _MSC_VER >= 1000 |
11 |
|
12 |
#include <YCL/common.h> |
13 |
|
14 |
#include <YCL/String.h> |
15 |
#include <YCL/StringBuffer.h> |
16 |
#include <YCL/StringUtil.h> |
17 |
#include <YCL/Integer.h> |
18 |
#include <YCL/Enumeration.h> |
19 |
#include <YCL/Vector.h> |
20 |
#include <YCL/Hashtable.h> |
21 |
|
22 |
namespace yebisuya { |
23 |
|
24 |
class IniFile { |
25 |
private: |
26 |
static String YES() { |
27 |
static const char YES[] = "yes"; |
28 |
static String string = YES; |
29 |
return string; |
30 |
} |
31 |
static String NO() { |
32 |
static const char NO[] = "no"; |
33 |
static String string = NO; |
34 |
return string; |
35 |
} |
36 |
static const char* INVALID() { |
37 |
static const char string[] = "\n:"; |
38 |
return string; |
39 |
} |
40 |
|
41 |
class EnumKeyNames : public Enumeration<String> { |
42 |
private: |
43 |
Vector<String> list; |
44 |
mutable int index; |
45 |
public: |
46 |
EnumKeyNames(const char* filename, const char* section):index(0) { |
47 |
Hashtable<String, int> table; |
48 |
int section_len = strlen(section); |
49 |
char* buffer; |
50 |
size_t size = 256; |
51 |
while ((buffer = getSectionNames(filename, size)) == NULL) |
52 |
size += 256; |
53 |
const char* name = buffer; |
54 |
while (*name != '\0') { |
55 |
if (strncmp(name, section, section_len) == 0) { |
56 |
if (name[section_len] == '\\') { |
57 |
int i; |
58 |
name += section_len + 1; |
59 |
for (i = 0; name[i] != '\0'; i++) { |
60 |
if (name[i] == '\\') |
61 |
break; |
62 |
if (String::isLeadByte(name[i])) |
63 |
i++; |
64 |
} |
65 |
table.put(String(name, i), 1); |
66 |
} |
67 |
} |
68 |
while (*name++ != '\0') |
69 |
; |
70 |
} |
71 |
delete[] buffer; |
72 |
Pointer<Enumeration<String> > keys = table.keys(); |
73 |
while (keys->hasMoreElements()) { |
74 |
list.add(keys->nextElement()); |
75 |
} |
76 |
} |
77 |
virtual bool hasMoreElements()const { |
78 |
return index < list.size(); |
79 |
} |
80 |
virtual String nextElement()const { |
81 |
return list.get(index++); |
82 |
} |
83 |
}; |
84 |
friend class EnumKeyNames; |
85 |
class EnumValueNames : public Enumeration<String> { |
86 |
private: |
87 |
char* buffer; |
88 |
mutable char* p; |
89 |
public: |
90 |
EnumValueNames(const char* filename, const char* section) { |
91 |
size_t size = 256; |
92 |
while ((buffer = getValueNames(filename, section, size)) == NULL) |
93 |
size += 256; |
94 |
p = buffer; |
95 |
} |
96 |
~EnumValueNames() { |
97 |
delete[] buffer; |
98 |
} |
99 |
virtual bool hasMoreElements()const { |
100 |
return *p != '\0'; |
101 |
} |
102 |
virtual String nextElement()const { |
103 |
String next = p; |
104 |
while (*p++ != '\0') |
105 |
; |
106 |
return next; |
107 |
} |
108 |
}; |
109 |
friend class EnumValueNames; |
110 |
class Value { |
111 |
friend class IniFile; |
112 |
private: |
113 |
const IniFile*const ini; |
114 |
String name; |
115 |
Value(const IniFile* ini, const String& name):ini(ini), name(name) { |
116 |
} |
117 |
Value(); // never implemented |
118 |
Value(const Value&); // never implemented |
119 |
void operator=(const Value&); // never implemented |
120 |
public: |
121 |
operator String()const { |
122 |
return ini->getString(name); |
123 |
} |
124 |
void operator=(const String& value) { |
125 |
((IniFile*) ini)->setString(name, value); |
126 |
} |
127 |
operator long()const { |
128 |
return ini->getInteger(name); |
129 |
} |
130 |
void operator=(long value) { |
131 |
((IniFile*) ini)->setInteger(name, value); |
132 |
} |
133 |
operator bool()const { |
134 |
return ini->getBoolean(name); |
135 |
} |
136 |
void operator=(bool value) { |
137 |
((IniFile*) ini)->setBoolean(name, value); |
138 |
} |
139 |
bool remove() { |
140 |
return ((IniFile*) ini)->deleteValue(name); |
141 |
} |
142 |
}; |
143 |
static char* getSectionNames(const char* filename, size_t size) { |
144 |
char* buffer = new char[size]; |
145 |
if (::GetPrivateProfileSectionNames(buffer, size, filename) < size - 2) |
146 |
return buffer; |
147 |
delete[] buffer; |
148 |
return NULL; |
149 |
} |
150 |
static char* getValueNames(const char* filename, const char* section, size_t size) { |
151 |
static const char null = '\0'; |
152 |
char* buffer = new char[size]; |
153 |
if (::GetPrivateProfileString(section, NULL, &null, buffer, size, filename) < size - 2) |
154 |
return buffer; |
155 |
delete[] buffer; |
156 |
return NULL; |
157 |
} |
158 |
static String getString(const char* filename, const char* section, const char* name, bool& exists, size_t size) { |
159 |
char* buffer = (char*) alloca(size); |
160 |
size_t len = ::GetPrivateProfileString(section, name, INVALID(), buffer, size, filename); |
161 |
if (len < size - 2) { |
162 |
// ���s����������������ini�t�@�C�������������������������������s���������������� |
163 |
if (buffer[0] == '\n') { |
164 |
exists = false; |
165 |
return NULL; |
166 |
} |
167 |
// �G�X�P�[�v�������W�J |
168 |
return StringUtil::unescape(buffer); |
169 |
} |
170 |
exists = true; |
171 |
return NULL; |
172 |
} |
173 |
static String generateSectionName(const char* parentSection, const char* subkeyName) { |
174 |
StringBuffer buffer = parentSection; |
175 |
buffer.append('\\'); |
176 |
buffer.append(subkeyName); |
177 |
return buffer.toString(); |
178 |
} |
179 |
static bool deleteAllSubsection(const char* filename, const char* section, const char* subkey) { |
180 |
String keyname = generateSectionName(section, subkey); |
181 |
int keyname_len = keyname.length(); |
182 |
|
183 |
char* buffer; |
184 |
size_t size = 256; |
185 |
while ((buffer = getSectionNames(filename, size)) == NULL) |
186 |
size += 256; |
187 |
const char* name = buffer; |
188 |
bool succeeded = true; |
189 |
while (*name != '\0') { |
190 |
if (strncmp(name, keyname, keyname_len) == 0) { |
191 |
if (name[keyname_len] == '\0' || name[keyname_len] == '\\') { |
192 |
if (!::WritePrivateProfileString(name, NULL, NULL, filename)) { |
193 |
succeeded = false; |
194 |
break; |
195 |
} |
196 |
} |
197 |
} |
198 |
while (*name++ != '\0') |
199 |
; |
200 |
} |
201 |
delete[] buffer; |
202 |
return succeeded; |
203 |
} |
204 |
|
205 |
String filename; |
206 |
String section; |
207 |
public: |
208 |
IniFile() { |
209 |
} |
210 |
IniFile(String filename, String section):filename(filename), section(section) { |
211 |
} |
212 |
IniFile(const IniFile& parent, const char* subkeyName):filename(parent.filename), section(generateSectionName(parent.section, subkeyName)) { |
213 |
} |
214 |
|
215 |
bool isOpened()const { |
216 |
return filename != NULL && section != NULL; |
217 |
} |
218 |
long getInteger(const char* name, long defaultValue = 0)const { |
219 |
return filename != NULL && section != NULL ? ::GetPrivateProfileInt(section, name, defaultValue, filename) : defaultValue; |
220 |
} |
221 |
String getString(const char* name)const { |
222 |
return getString(name, NULL); |
223 |
} |
224 |
String getString(const char* name, String defaultValue)const { |
225 |
if (filename != NULL && section != NULL && name != NULL) { |
226 |
size_t size = 256; |
227 |
String string; |
228 |
bool exists; |
229 |
while ((string = getString(filename, section, name, exists, size)) == NULL && exists) |
230 |
size += 256; |
231 |
if (string != NULL) |
232 |
return string; |
233 |
} |
234 |
return defaultValue; |
235 |
} |
236 |
bool getBoolean(const char* name, bool defaultValue = false)const { |
237 |
String string = getString(name); |
238 |
if (string != NULL) { |
239 |
if (string == YES()) |
240 |
return true; |
241 |
if (string == NO()) |
242 |
return false; |
243 |
} |
244 |
return defaultValue; |
245 |
} |
246 |
|
247 |
bool setInteger(const char* name, long value) { |
248 |
return setString(name, Integer::toString(value)); |
249 |
} |
250 |
bool setString(const char* name, String value) { |
251 |
if (filename != NULL && section != NULL && name != NULL) { |
252 |
// NULL�����������G�X�P�[�v��������""������ |
253 |
if (value != NULL) { |
254 |
StringBuffer buffer; |
255 |
buffer.append('"'); |
256 |
if (!StringUtil::escape(buffer, value)) |
257 |
buffer.append(value); |
258 |
buffer.append('"'); |
259 |
value = buffer.toString(); |
260 |
} |
261 |
return ::WritePrivateProfileString(section, name, value, filename) != FALSE; |
262 |
} |
263 |
return false; |
264 |
} |
265 |
bool setBoolean(const char* name, bool value) { |
266 |
return setString(name, value ? YES() : NO()); |
267 |
} |
268 |
|
269 |
bool existsValue(const char* name) { |
270 |
char buffer[3]; |
271 |
::GetPrivateProfileString(section, name, INVALID(), buffer, countof(buffer), filename); |
272 |
return buffer[0] != '\n'; |
273 |
} |
274 |
bool deleteKey(const char* name) { |
275 |
return filename != NULL && section != NULL && name != NULL && deleteAllSubsection(filename, section, name); |
276 |
} |
277 |
bool deleteValue(const char* name) { |
278 |
return filename != NULL && section != NULL && name != NULL |
279 |
&& ::WritePrivateProfileString(section, name, NULL, filename) != FALSE; |
280 |
} |
281 |
|
282 |
bool open(String filename, String section) { |
283 |
close(); |
284 |
this->filename = filename; |
285 |
this->section = section; |
286 |
return isOpened(); |
287 |
} |
288 |
bool open(const IniFile& parent, const char* subkeyName) { |
289 |
close(); |
290 |
filename = parent.filename; |
291 |
section = generateSectionName(parent.section, subkeyName); |
292 |
return isOpened(); |
293 |
} |
294 |
void close() { |
295 |
filename = NULL; |
296 |
section = NULL; |
297 |
} |
298 |
|
299 |
Pointer<Enumeration<String> > keyNames()const { |
300 |
return filename != NULL && section != NULL ? new EnumKeyNames(filename, section) : NULL; |
301 |
} |
302 |
Pointer<Enumeration<String> > valueNames()const { |
303 |
return filename != NULL && section != NULL ? new EnumValueNames(filename, section) : NULL; |
304 |
} |
305 |
|
306 |
Value operator[](String name) { |
307 |
return Value(this, name); |
308 |
} |
309 |
const Value operator[](String name)const { |
310 |
return Value(this, name); |
311 |
} |
312 |
}; |
313 |
|
314 |
} |
315 |
|
316 |
#endif//_YCL_INIFILE_H_ |