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*)&sectionCount, 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*)&sectionCount, 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*)&sectionCount, 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