1 /*
2 Feather INI Parser - 1.41
3 You are free to use this however you wish.
4
5 If you find a bug, please attept to debug the cause.
6 Post your environment details and the cause or fix in the issues section of GitHub.
7
8 Written by Turbine.
9
10 Website:
11 https://github.com/Turbine1991/feather-ini-parser
12 http://code.google.com/p/feather-ini-parser/downloads
13
14 Help:
15 Bundled example & readme.
16 http://code.google.com/p/feather-ini-parser/wiki/Tutorials
17 */
18
19 #pragma once
20
21 #include <string>
22 #include <fstream>
23 #include <sstream>
24 #include <stdint.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <ostream>
28
29 #define FINI_SAFE
30 #define FINI_BUFFER_SIZE 256
31
32 #if __cplusplus >= 201103L
33 #include <unordered_map>
34 #define FINI_CPP11
35 #define ALLOCATE_SECTIONS 100
36 #define ALLOCATE_KEYS 5
37 #else
38 #include <map>
39 #endif
40
41 #ifdef FINI_WIDE_SUPPORT
42 #include <wchar.h>
43
44 typedef std::wstringstream fini_sstream_t;
45 typedef std::wstring fini_string_t;
46 typedef wchar_t fini_char_t;
47 typedef std::wifstream fini_ifstream_t;
48 typedef std::wofstream fini_ofstream_t;
49
50 #define fini_strlen(a) wcslen(a)
51 #define fini_strncpy(a, b) wcscpy(a, b)
52 #define fini_strncpy(a, b, c) wcsncpy(a, b, c)
53 #define fini_strtok(a, b) wcstok(a, b)
54
55 #define _T(x) L ##x
56 #else
57 #include <cstring>
58
59 typedef std::stringstream fini_sstream_t;
60 typedef std::string fini_string_t;
61 typedef char fini_char_t;
62 typedef std::ifstream fini_ifstream_t;
63 typedef std::ofstream fini_ofstream_t;
64
65 #define fini_strlen(a) strlen(a)
66 #define fini_strcpy(a, b) strcpy(a, b)
67 #define fini_strncpy(a, b, c) strncpy(a, b, c)
68 #define fini_strtok(a, b) strtok(a, b)
69
70 #define _T(x) x
71 #endif
72
73 #define CHAR_SIZE sizeof(fini_char_t)
74
75 ///Simple converter using templates and streams to effectively required for the flexibility of handling native types
76 class Converters
77 {
78 public:
79 template <typename T, typename U>
80 static T Convert(U value);
81 template <typename T>
82 static size_t GetDataSize(T& value);
83 static size_t GetDataSize(fini_string_t value);
84 };
85
86 ///
87 template <typename T = fini_string_t, typename U = fini_string_t, typename V = fini_string_t>
88 class INI
89 {
90 public:
91 typedef T section_t;
92 typedef U key_t;
93 typedef V value_t;
94 typedef INI<section_t, key_t, value_t> ini_t;
95
96 ///Type definition declarations
97 #ifdef FINI_CPP11
98 typedef typename std::unordered_map<key_t, value_t> keys_t;
99 typedef typename std::unordered_map<section_t, keys_t*> sections_t;
100 #else
101 typedef typename std::map<key_t, value_t> keys_t;
102 typedef typename std::map<section_t, keys_t*> sections_t;
103 #endif
104
105 typedef typename keys_t::iterator keysit_t;
106 typedef typename sections_t::iterator sectionsit_t;
107
108 typedef typename std::pair<key_t, value_t> keyspair_t;
109 typedef typename std::pair<section_t, keys_t*> sectionspair_t;
110
111 typedef char data_t;
112
113 enum source_e { SOURCE_FILE, SOURCE_MEMORY };
114
115 ///Data members
116 std::string filename;
117 data_t* data;
118 size_t dataSize;
119 keys_t* current;
120 sections_t sections;
121 source_e source;
122
123 ///Constuctor/Destructor
124 //Specify the filename to associate and whether to parse immediately
INI(const std::string filename,bool doParse)125 INI(const std::string filename, bool doParse) : filename(filename)
126 {
127 init(SOURCE_FILE, doParse);
128 }
129
130 //Used for loading INI from memory
INI(void * data,size_t dataSize,bool doParse)131 INI(void* data, size_t dataSize, bool doParse) : data((data_t*)data), dataSize(dataSize)
132 {
133 init(SOURCE_MEMORY, doParse);
134 }
135
~INI()136 ~INI()
137 {
138 clear();
139 }
140
141 ///Access Content
142 //Provide bracket access to section contents
operator [](section_t section)143 keys_t& operator[](section_t section)
144 {
145 #ifdef FINI_SAFE
146 if (!sections[section])
147 sections[section] = new keys_t;
148 #endif
149
150 return *sections[section];
151 }
152
153 //Create a new section and select it
create(const section_t section)154 bool create(const section_t section)
155 {
156 if (select(section))
157 return false;
158
159 current = new keys_t;
160 sections[section] = current;
161
162 reserveKeys(current);
163
164 return true;
165 }
166
167 //Select a section for performing operations
select(const section_t section)168 bool select(const section_t section)
169 {
170 sectionsit_t sectionsit = sections.find(section);
171 if (sectionsit == sections.end())
172 return false;
173
174 current = sectionsit->second;
175
176 return true;
177 }
178
179 ///Debug
operator <<(std::ostream & os,const ini_t & ini)180 friend std::ostream& operator<<(std::ostream& os, const ini_t& ini)
181 {
182 #ifdef FINI_CPP11
183 for (auto i = ini.sections.begin(); i != ini.sections.end(); i++) //typename ini_t::sectionsit_t
184 {
185 //Section name as ini_t::section_t
186 os << '[' << i->first << ']' << std::endl;
187
188 if (i->second->size() == 0) //No keys/values in section, skip to next
189 continue;
190
191 for (typename ini_t::keysit_t j = i->second->begin(); j != i->second->end(); j++)
192 {
193 //Name as ini_t::key_t & Value as ini_t::key_t
194 os << " " << j->first << "=" << j->second << std::endl;
195 }
196 }
197 #else
198 std::cout << "Error: FINI requires CPP11 when outputting to stream." << std::endl;
199 #endif
200
201 return os;
202 }
203
204 ///Set
205 //Assign a value for key under the selected section
set(const key_t key,const value_t value)206 bool set(const key_t key, const value_t value)
207 {
208 if (current == NULL)
209 return false;
210
211 (*current)[key] = value;
212
213 return true;
214 }
215
216 template <typename W, typename X>
set(const W key,const X value)217 bool set(const W key, const X value)
218 {
219 return set(Converters::Convert<key_t>(key), Converters::Convert<value_t>(value));
220 }
221
222 ///Get
get(const key_t key,value_t def=value_t ())223 value_t get(const key_t key, value_t def = value_t())
224 {
225 keysit_t it = current->find(key);
226 if (current == NULL || it == current->end())
227 return def;
228
229 return it->second;
230 }
231
get(const section_t section,const key_t key,value_t def)232 value_t get(const section_t section, const key_t key, value_t def)
233 {
234 if (!select(section))
235 return def;
236
237 return get(key, def);
238 }
239
240 template <typename W, typename X>
get(const W key,const X def=value_t ())241 X get(const W key, const X def = value_t())
242 {
243 return Converters::Convert<X>(get(Converters::Convert<key_t>(key), Converters::Convert<value_t>(def)));
244 }
245
246 template <typename W>
get(const W key,const fini_char_t * def=_T (""))247 fini_string_t get(const W key, const fini_char_t* def = _T("")) //Handle C string default value without casting
248 {
249 return Converters::Convert<fini_string_t>(get(Converters::Convert<key_t>(key), Converters::Convert<value_t>(def)));
250 }
251
252 template <typename W, typename X, typename Y>
get(const W section,const X key,const Y def)253 Y get(const W section, const X key, const Y def)
254 {
255 return Converters::Convert<Y>(get(Converters::Convert<section_t>(section), Converters::Convert<key_t>(key), Converters::Convert<value_t>(def)));
256 }
257
258 template <typename W, typename X>
get(const W section,const X key,const fini_char_t * def)259 fini_string_t get(const W section, const X key, const fini_char_t* def) //Handle C string default value without casting
260 {
261 return Converters::Convert<fini_string_t>(get(Converters::Convert<section_t>(section), Converters::Convert<key_t>(key), Converters::Convert<value_t>(def)));
262 }
263
264 ///Functions
parse(std::istream & file)265 void parse(std::istream& file)
266 {
267 fini_string_t line;
268 bool first = true;
269 fini_sstream_t out;
270 while (std::getline(file, line))
271 {
272 if (first)
273 {
274 first = false;
275 if (line[0] == 0xEF) //Allows handling of UTF-16/32 documents
276 {
277 line.erase(0, 3);
278 return;
279 }
280 }
281 if (!line.empty())
282 {
283 auto len = line.length();
284 if (len > 0 && !((len >= 2 && (line[0] == '/' && line[1] == '/')) || (len >= 1 && line[0] == '#'))) //Ignore comment and empty lines
285 {
286 if (line[0] == ';')
287 continue;
288
289 // section
290 if (line[0] == '[')
291 {
292 // without section brackets
293 auto ssection = line.substr(1, line.find(']') - 1);
294 out << ssection;
295
296 // "convert" to section_t
297 section_t section;
298 out >> section;
299
300 current = new keys_t;
301 sections[section] = current;
302 }
303 // key
304 else
305 {
306 key_t key;
307 value_t value;
308 fini_string_t skey = line.substr(0, line.find('='));
309 fini_string_t svalue = line.substr(line.find('=') + 1, line.size());
310 if (!(skey.empty() || svalue.empty())) {
311 skey = skey.substr(skey.find_first_not_of(' '), skey.length());
312
313 // "convert" value
314 out << skey;
315 out >> key;
316 out.clear();
317 out.str(fini_string_t());
318
319 // "convert" value
320 out << svalue;
321 out >> value;
322
323 if (value != value_t())
324 (*current)[key] = value;
325 }
326 }
327 out.clear();
328 out.str(fini_string_t()); //Clear existing stream;
329 }
330 }
331 }
332 }
333
334 //Parse an INI's contents into memory from the filename given during construction
parse()335 bool parse()
336 {
337 switch (source)
338 {
339 case SOURCE_FILE: {
340 fini_ifstream_t file(filename.c_str());
341
342 if (!file.is_open())
343 return false;
344
345 parse(file);
346
347 file.close();
348 }
349 break;
350
351 case SOURCE_MEMORY: {
352 std::stringstream sstream;
353 sstream.rdbuf()->pubsetbuf(data, dataSize);
354
355 parse(sstream);
356 }
357 break;
358 }
359
360 return true;
361 }
362
parseBinary()363 bool parseBinary()
364 {
365 fini_ifstream_t file(filename.c_str(), std::ios::binary);
366 if (!file.is_open())
367 return false;
368
369 size_t sectionCount;
370 size_t keyCount;
371 key_t key;
372 value_t value;
373 section_t section;
374
375 //file.read((fini_char_t*)§ionCount, sizeof(sectionCount));
376 file >> sectionCount;
377
378 for (size_t i = 0; i < sectionCount; i++)
379 {
380 if (i > 0)
381 file.seekg(1 + file.tellg());
382
383 file.read((fini_char_t*)&keyCount, sizeof(keyCount));
384 file >> section;
385
386 create(section);
387
388 for (size_t j = 0; j < keyCount; j++)
389 {
390 file >> key;
391 file >> value;
392 set(key, value);
393 }
394 }
395
396 file.close();
397
398 return true;
399 }
400
401 //Clear the contents from memory
clear()402 void clear()
403 {
404 clean();
405 sections.clear();
406 }
407
408 ///Output
409 //Save from memory into file
save(const std::string filename="")410 bool save(const std::string filename = "")
411 {
412 if (!hasFileAssociation(filename))
413 return false;
414
415 fini_ofstream_t file(((filename == "") ? this->filename : filename).c_str(), std::ios::trunc);
416 if (!file.is_open())
417 return false;
418
419 //Loop through sections
420 for (typename INI::sectionsit_t i = sections.begin(); i != sections.end(); i++)
421 {
422 if (i->second->size() == 0) //No keys/values in section, skip to next
423 continue;
424
425 //Write section
426 const fini_string_t temp = makeSection(i->first);
427 const fini_char_t* line = temp.c_str();
428 file.write(line, fini_strlen(line));
429
430 for (typename INI::keysit_t j = i->second->begin(); j != i->second->end(); j++)
431 {
432 //Write key and value
433 const fini_string_t temp = makeKeyValue(j->first, j->second);
434 const fini_char_t* line = temp.c_str();
435 file.write(line, fini_strlen(line));
436 }
437 }
438
439 file.close();
440
441 return true;
442 }
443
444 //Saves it without any conventional INI formatting characters, however it only uses string streams
saveBinary(const std::string filename="")445 bool saveBinary(const std::string filename = "")
446 {
447 if (!hasFileAssociation(filename))
448 return false;
449
450 fini_ofstream_t file(((filename == "") ? this->filename : filename).c_str(), std::ios::trunc | std::ios::binary);
451 if (!file.is_open())
452 return false;
453
454 size_t sectionCount = sections.size();
455 size_t keyCount;
456
457 file.write((fini_char_t*)§ionCount, sizeof(sectionCount));
458
459 //Loop through sections
460 for (typename INI::sectionsit_t i = sections.begin(); i != sections.end(); i++)
461 {
462 keyCount = i->second->size();
463 file.write((fini_char_t*)&keyCount, sizeof(keyCount));
464
465 file << i->first << std::endl;
466
467 for (typename INI::keysit_t j = i->second->begin(); j != i->second->end(); j++)
468 {
469 file << j->first << std::endl;
470 file << j->second << std::endl;
471 }
472 }
473
474 file.close();
475
476 return true;
477 }
478
479 //Saves it as a true binary file, intended to replace the existing one. Don't bother using it with all strings.
saveBinaryExperimental(std::string filename="")480 bool saveBinaryExperimental(std::string filename = "")
481 {
482 if (!hasFileAssociation(filename))
483 return false;
484
485 fini_ofstream_t file(((filename == "") ? this->filename : filename).c_str(), std::ios::trunc | std::ios::binary);
486 if (!file.is_open())
487 return false;
488
489 size_t sectionCount = sections.size();
490 size_t keyCount;
491
492 file.write((fini_char_t*)§ionCount, sizeof(sectionCount));
493
494 //Loop through sections
495 for (typename INI::sectionsit_t i = sections.begin(); i != sections.end(); i++)
496 {
497 keyCount = i->second->size();
498 file.write((fini_char_t*)&keyCount, sizeof(keyCount));
499
500 file.write((fini_char_t*)&i->first, Converters::GetDataSize(i->first));
501
502 for (typename INI::keysit_t j = i->second->begin(); j != i->second->end(); j++)
503 {
504 file.write((fini_char_t*)&j->first, Converters::GetDataSize(j->first));
505 file.write((fini_char_t*)&j->second, Converters::GetDataSize(j->second));
506 }
507 }
508
509 file.close();
510
511 return true;
512 }
513
514 //Alows another INI's contents to be insert into another, with the ability to retain the original values
merge(ini_t & other,bool retainValues=true)515 void merge(ini_t& other, bool retainValues = true)
516 {
517 for (typename INI::sectionsit_t i = other.sections.begin(); i != other.sections.end(); i++)
518 {
519 if (!select(i->first)) //Create and insert all key values into a missing section
520 {
521 keys_t* keys = new keys_t(*i->second);
522 sections.insert(std::make_pair(i->first, keys));
523 }
524 else
525 {
526 for (typename INI::keysit_t j = i->second->begin(); j != i->second->end(); j++)
527 {
528 keysit_t it = current->find(j->first);
529 if (it == current->end())
530 current->insert(std::make_pair(j->first, j->second));
531 else if (!retainValues)
532 it->second = j->second;
533 }
534 }
535 }
536 }
537
538 private:
539 ///Functions
540 //Init the INI in with values set by constructor
init(source_e source,bool doParse)541 void init(source_e source, bool doParse)
542 {
543 this->source = source;
544
545 reserveSections();
546 if (doParse)
547 parse();
548 }
549
550 //Clean the contents for descruction
clean()551 void clean()
552 {
553 for (sectionsit_t i = sections.begin(); i != sections.end(); i++)
554 delete i->second;
555
556 current = NULL;
557 }
558
559 //Make any alterations to the raw line
nake(const fini_char_t *)560 void nake(const fini_char_t*) //Strip the line of any non-interpretable characters
561 {
562
563 }
564
reserveSections()565 void reserveSections()
566 {
567 #ifdef FINI_CPP11
568 sections.reserve(ALLOCATE_SECTIONS);
569 #endif
570 }
571
reserveKeys(keys_t * current)572 void reserveKeys(keys_t* current)
573 {
574 #ifdef FINI_CPP11
575 current->reserve(ALLOCATE_KEYS);
576 #endif
577 }
578
hasFileAssociation(std::string filename)579 bool hasFileAssociation(std::string filename)
580 {
581 if (source == SOURCE_MEMORY && filename == "") //No association to a file
582 return false;
583
584 return true;
585 }
586
587 ///Output
588 //Creates a section as a string
makeSection(const section_t & section)589 fini_string_t makeSection(const section_t& section)
590 {
591 fini_sstream_t line;
592 line << '[' << section << ']' << std::endl;
593
594 return line.str();
595 }
596
597 //Creates a key and a value as a string
makeKeyValue(const key_t & key,const value_t & value)598 fini_string_t makeKeyValue(const key_t& key, const value_t& value)
599 {
600 fini_sstream_t line;
601 line << key << '=' << value << std::endl;
602
603 return line.str();
604 }
605 };
606
607 ///Definitions
608 template <typename T, typename U>
Convert(U value)609 inline T Converters::Convert(U value)
610 {
611 fini_sstream_t sout;
612 T result;
613
614 sout << value;
615 sout >> result;
616
617 sout.str(fini_string_t());
618
619 return result;
620 }
621
622 template <>
Convert(fini_string_t value)623 inline fini_string_t Converters::Convert<fini_string_t, fini_string_t>(fini_string_t value)
624 {
625 return value;
626 }
627
628 template <>
Convert(const fini_char_t * value)629 inline fini_string_t Converters::Convert<fini_string_t>(const fini_char_t* value)
630 {
631 return value;
632 }
633
634 template <typename T>
GetDataSize(T & value)635 inline size_t Converters::GetDataSize(T& value)
636 {
637 return sizeof(value);
638 }
639
GetDataSize(fini_string_t value)640 inline size_t Converters::GetDataSize(fini_string_t value)
641 {
642 return value.size() + 1;
643 }
644