1 
2 /*
3 *  keyfile - an easy way to read and write keyfiles
4 *  Copyright (c) 2004-2006 by Mattias Hultgren <mattias_hultgren@tele2.se>
5 *
6 *  See keyfile.h
7 */
8 
9 /*
10 News
11 ----
12 
13 v1  2006-07-27
14 --
15 
16 	Extracted this file from vartypes.h
17 	Renamed keyfile_io -> keyfile
18 	Replaced all throw_... calls with THROW_ERROR
19 	Replaced all std::string usage with utf8_string
20 
21 */
22 
23 
24 #include <stdio.h>
25 #include <string.h>
26 #include "utf8_string.h"
27 #include "vartypes.h"
28 #include "keyfile.h"
29 
30 keyfile translation;
31 
32 
33 
keysDB(keysDB & src)34 keyfile::keysDB::keysDB(keysDB &src)
35 {
36 	*this = src;
37 }
operator =(keysDB & src)38 void keyfile::keysDB::operator=(keysDB &src)
39 {
40 	name = src.name;
41 	value = src.value;
42 }
43 
44 
keyfile()45 keyfile::keyfile()
46 {
47 	keys = 0;
48 	nrofkeys = 0;
49 	replace_escape_sequences = false;
50 }
keyfile(const char * filename)51 keyfile::keyfile(const char *filename) throw(error_obj)
52 {
53 	keys = 0;
54 	nrofkeys = 0;
55 	replace_escape_sequences = false;
56 
57 	read( filename );
58 }
keyfile(const keyfile & src)59 keyfile::keyfile(const keyfile &src) throw(error_obj)
60 {
61 	keys = 0;
62 	nrofkeys = 0;
63 	replace_escape_sequences = false;
64 
65 	*this = src;
66 }
67 
~keyfile()68 keyfile::~keyfile()
69 {
70 	reset();
71 }
72 
operator =(const keyfile & src)73 void keyfile::operator=(const keyfile &src)
74 {
75 	reset();
76 	for( uint32 i=0; i<src.nrofkeys; i++ )
77 		set_key( src.keys[i].name, src.keys[i].value );
78 }
79 
reset(void)80 void keyfile::reset(void)
81 {
82 	delete [] keys;
83 	keys = 0;
84 	nrofkeys = 0;
85 }
86 
read_intern(const char * intern_file)87 void keyfile::read_intern(const char *intern_file) throw(error_obj)
88 {
89 	FILE *fp;
90 
91 	fp = tmpfile();
92 	if(fp == 0)
93 	{
94 		char tmp_err[ERROR_OBJ_MSG_LEN];
95 		snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Couldn't open/write to file (%s)"), "<tmp_file>" );
96 		THROW_ERROR( ErrorType_File_IO, tmp_err );
97 	}
98 
99 	if(fwrite( intern_file, 1, strlen(intern_file), fp ) != strlen(intern_file))
100 	{
101 		fclose(fp);
102 		char tmp_err[ERROR_OBJ_MSG_LEN];
103 		snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Couldn't open/write to file (%s)"), "<tmp_file>" );
104 		THROW_ERROR( ErrorType_File_IO, tmp_err );
105 	}
106 
107 	rewind( fp );
108 
109 	try
110 	{
111 		read( fp, "<tmp_file>" );
112 	}
113 	catch(...)
114 	{
115 		fclose( fp );
116 		throw;
117 	}
118 	if(fclose(fp) != 0)
119 	{
120 		char tmp_err[ERROR_OBJ_MSG_LEN];
121 		snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Couldn't close file (%s)"), "<tmp_file>" );
122 		THROW_ERROR( ErrorType_File_IO, tmp_err );
123 	}
124 }
125 
read(const char * filename)126 void keyfile::read(const char *filename) throw(error_obj)
127 {
128 	FILE *fp;
129 
130 	fp = fopen(filename,"r");
131 	if(fp == 0)
132 	{
133 		char tmp_err[ERROR_OBJ_MSG_LEN];
134 		snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Couldn't open/read from file (%s)"), filename );
135 		THROW_ERROR( ErrorType_File_IO, tmp_err );
136 	}
137 
138 	try
139 	{
140 		read( fp, filename );
141 	}
142 	catch(...)
143 	{
144 		fclose( fp );
145 		throw;
146 	}
147 	if(fclose(fp) != 0)
148 	{
149 		char tmp_err[ERROR_OBJ_MSG_LEN];
150 		snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Couldn't close file (%s)"), filename );
151 		THROW_ERROR( ErrorType_File_IO, tmp_err );
152 	}
153 }
154 
read(FILE * open_file,const char * filename)155 void keyfile::read(FILE *open_file, const char *filename) throw(error_obj)
156 {
157 	char str[5010];
158 	int i,keyname,value,equal,i2;
159 
160 	for(;;)
161 	{
162 		if(fgets(str,5005,open_file) == 0)
163 		{
164 			if(feof(open_file)) // this is not an error, the hole file is read
165 				break;
166 
167 			char tmp_err[ERROR_OBJ_MSG_LEN];
168 			snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Couldn't open/read from file (%s)"), filename );
169 			THROW_ERROR( ErrorType_File_IO, tmp_err );
170 		}
171 		str[5007] = ' ';
172 		str[5008] = '\"';
173 		str[5009] = '\0'; // this is to prevent that the loops overruns the buffer limit
174 
175 		for(i=0; str[i] == ' ' || str[i] == '\t' ;i++); // skipping all spaces and tabs at the beginning of the row
176 
177 		if(str[i] == '#') // this line is a comment
178 			continue;
179 
180 		if( str[i] == '\n'  ||  str[i] == '\r'  ||  str[i] == '\0' ) // empty line
181 			continue;
182 
183 		keyname = i;
184 
185 		if( str[i] == '\"' )
186 		{
187 			keyname++;
188 			for(i++;;i++)
189 			{
190 				if( str[i] == '\"' )
191 				{
192 					// let's check if this " is escaped out
193 					for( i2=0; str[i-i2-1] == '\\'; i2++ ) ;
194 
195 					if( i2 > 0  &&  (i2%2) == 1 ) // check if the \ is escaped out
196 						continue;
197 
198 					str[i] = 0;
199 					break;
200 				}
201 			}
202 		}
203 		else
204 		{
205 			for(;;i++)
206 			{
207 				if( !( ((str[i] >= 'a') & (str[i] <= 'z')) | ((str[i] >= 'A') & (str[i] <= 'Z')) | (str[i] == '_') |
208 				       ((str[i] >= '0') & (str[i] <= '9')) )  )
209 				{
210 					if(str[i] != ' ' || str[i] == '\t')
211 					{
212 						char tmp_err[ERROR_OBJ_MSG_LEN];
213 						snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Invalid format in file %s"), filename );
214 						THROW_ERROR( ErrorType_File_IO, tmp_err );
215 					}
216 					str[i] = 0;
217 					break;
218 				}
219 			}
220 		}
221 
222 		for( equal=0,i++; ; i++ ) // getting start of value
223 		{
224 			if( str[i] == ' ' || str[i] == '\t' )
225 				{  }
226 			else if( str[i] == '=' )
227 			{
228 				if( equal == 1 ) // more than one '=' sign
229 				{
230 					char tmp_err[ERROR_OBJ_MSG_LEN];
231 					snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Invalid format in file %s"), filename );
232 					THROW_ERROR( ErrorType_File_IO, tmp_err );
233 				}
234 				equal = 1;
235 			}
236 			else if(str[i] == '\"')
237 			{
238 				if(equal == 0)
239 				{
240 					char tmp_err[ERROR_OBJ_MSG_LEN];
241 					snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Invalid format in file %s"), filename );
242 					THROW_ERROR( ErrorType_File_IO, tmp_err );
243 				}
244 				i++;
245 				break;
246 			}
247 			else
248 			{
249 				char tmp_err[ERROR_OBJ_MSG_LEN];
250 				snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Invalid format in file %s"), filename );
251 				THROW_ERROR( ErrorType_File_IO, tmp_err );
252 			}
253 		}
254 		value = i;
255 
256 		for(;;i++) // getting the end of value
257 		{
258 			if(str[i] == '\"')
259 			{
260 				// let's check if this " is escaped out
261 				for( i2=0; str[i-i2-1] == '\\'; i2++ ) ;
262 
263 				if( i2 > 0  &&  (i2%2) == 1 ) // check if the \ is escaped out
264 					continue;
265 
266 				break;
267 			}
268 			else if(str[i] == 0)
269 			{
270 				char tmp_err[ERROR_OBJ_MSG_LEN];
271 				snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Invalid format in file %s"), filename );
272 				THROW_ERROR( ErrorType_File_IO, tmp_err );
273 			}
274 
275 		}
276 		str[i] = 0;
277 
278 		set_key( &str[keyname], &str[value] );
279 	}
280 }
281 
keyfile_replace_escape_sequences(utf8_string & str)282 void keyfile_replace_escape_sequences( utf8_string &str ) throw(error_obj)
283 {
284 	for( uint32 ind=0; ind < str.get_length(); ind++ )
285 	{
286 		if( str.test_character( ind, "\\" ) )
287 		{
288 			if( str.test_character( ind+1, "\\" ) )
289 				str.replace( ind, 2, "\\" );
290 			else if( str.test_character( ind+1, "n" ) )
291 				str.replace( ind, 2, "\n" );
292 			else if( str.test_character( ind+1, "r" ) )
293 				str.replace( ind, 2, "\r" );
294 			else if( str.test_character( ind+1, "t" ) )
295 				str.replace( ind, 2, "\t" );
296 			else if( str.test_character( ind+1, "\"" ) )
297 				str.replace( ind, 2, "\"" );
298 			else if( str.test_character( ind+1, "\'" ) )
299 				str.replace( ind, 2, "\'" );
300 			else
301 				THROW_ERROR( ErrorType_General, _("Unknown escape sequence") );
302 		}
303 	}
304 }
305 
set_key(const utf8_string & keyname,const utf8_string & value)306 void keyfile::set_key(const utf8_string &keyname,const utf8_string &value) throw(error_obj)
307 {
308 	utf8_string tmp_keyname( keyname );
309 	keysDB *newkeys = 0;
310 
311 	if( replace_escape_sequences )
312 		keyfile_replace_escape_sequences( tmp_keyname );
313 
314 
315 	for( uint32 i=0; i<nrofkeys; i++ )
316 	{
317 		if( keys[i].name == tmp_keyname ) // key already exists, replace it
318 		{
319 			try
320 			{
321 				keys[i].value = value;
322 
323 				if( replace_escape_sequences )
324 					keyfile_replace_escape_sequences( keys[i].value );
325 
326 				return;
327 			}
328 			catch(error_obj) { throw; }
329 			catch(...) { THROW_ERROR( ErrorType_Memory, _("Couldn't get memory.") ); }
330 		}
331 	}
332 
333 	try
334 	{
335 		newkeys = new keysDB [ nrofkeys + 1 ];
336 
337 		newkeys[nrofkeys].name = tmp_keyname;
338 		newkeys[nrofkeys].value = value;
339 	}
340 	catch(...)
341 	{
342 		if(newkeys != 0)
343 			delete [] newkeys;
344 
345 		THROW_ERROR( ErrorType_Memory, _("Couldn't get memory.") );
346 	}
347 
348 	if( replace_escape_sequences )
349 		keyfile_replace_escape_sequences( newkeys[nrofkeys].value );
350 
351 	for( uint32 i=0; i<nrofkeys; i++ )
352 		newkeys[i] = keys[i];
353 
354 	delete [] keys;
355 	keys = newkeys;
356 	nrofkeys++;
357 }
358 
remove_key(const utf8_string & keyname)359 void keyfile::remove_key( const utf8_string &keyname ) throw(error_obj)
360 {
361 	keysDB *newkeys = 0;
362 
363 	for( uint32 i=0; i<nrofkeys; i++ )
364 	{
365 		if( keys[i].name == keyname ) // key found
366 		{
367 			if( nrofkeys == 1 )
368 			{
369 				reset();
370 				return;
371 			}
372 			else
373 			{
374 				try
375 				{
376 					newkeys = new keysDB [ nrofkeys - 1 ];
377 				}
378 				catch(error_obj) { throw; }
379 				catch(...) { THROW_ERROR( ErrorType_Memory, _("Couldn't get memory.") ); }
380 
381 				uint32 step = 0;
382 				for( uint32 o=0; o<nrofkeys; o++ )
383 				{
384 					if(o == i)
385 						step = 1;
386 					else
387 						newkeys[o - step] = keys[o];
388 				}
389 				delete [] keys;
390 				keys = newkeys;
391 				nrofkeys--;
392 			}
393 		}
394 	}
395 }
396 
get_key(const utf8_string & keyname,const utf8_string * fail_pointer)397 const utf8_string * keyfile::get_key( const utf8_string &keyname, const utf8_string *fail_pointer )
398 {
399 	for( uint32 i=0; i<nrofkeys; i++ )
400 	{
401 		if(keys[i].name == keyname)
402 			return &keys[i].value;
403 	}
404 	return fail_pointer;
405 }
get_key_c_str(const utf8_string & keyname,const char * fail_pointer)406 const char * keyfile::get_key_c_str( const utf8_string &keyname, const char *fail_pointer )
407 {
408 	for( uint32 i=0; i<nrofkeys; i++ )
409 	{
410 		if( keys[i].name == keyname )
411 			return keys[i].value.c_str();
412 	}
413 	return fail_pointer;
414 }
415 
get_text(const char * keyname)416 const char* keyfile::get_text( const char *keyname )
417 {
418 	for( uint32 i=0; i<nrofkeys; i++ )
419 	{
420 		if( keys[i].name == keyname )
421 			return keys[i].value.c_str();
422 	}
423 	return keyname;
424 }
425 
keyfile_expand_to_escape_sequences(const utf8_string & src,utf8_string & dest)426 void keyfile_expand_to_escape_sequences( const utf8_string &src, utf8_string &dest )
427        throw(error_obj)
428 {
429 	dest = src;
430 
431 	try
432 	{
433 		for( uint32 i=0; i<dest.get_length(); i++ )
434 		{
435 			if( dest.test_character( i, "\\" ) )
436 			{
437 				dest.replace( i, 1, "\\\\" );
438 				i++;
439 			}
440 			else if( dest.test_character( i, "\n" ) )
441 			{
442 				dest.replace( i, 1, "\\n" );
443 				i++;
444 			}
445 			else if( dest.test_character( i, "\r" ) )
446 			{
447 				dest.replace( i, 1, "\\r" );
448 				i++;
449 			}
450 			else if( dest.test_character( i, "\t" ) )
451 			{
452 				dest.replace( i, 1, "\\t" );
453 				i++;
454 			}
455 			else if( dest.test_character( i, "\"" ) )
456 			{
457 				dest.replace( i, 1, "\\\"" );
458 				i++;
459 			}
460 		}
461 	}
462 	catch(...) {  THROW_ERROR( ErrorType_Memory, _("Couldn't get memory.") );  }
463 }
464 
write(const char * filename)465 void keyfile::write(const char *filename) throw(error_obj)
466 {
467 	FILE *fp;
468 
469 	fp = fopen( filename, "wb" );
470 
471 	if( fp == NULL )
472 	{
473 		char tmp_err[ERROR_OBJ_MSG_LEN];
474 		snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Couldn't open/write to file (%s)"), filename );
475 		THROW_ERROR( ErrorType_File_IO, tmp_err );
476 	}
477 
478 	if( replace_escape_sequences )
479 	{
480 		utf8_string name, value;
481 
482 		for( uint32 i=0; i<nrofkeys ;i++ )
483 		{
484 			keyfile_expand_to_escape_sequences( keys[i].name, name );
485 			keyfile_expand_to_escape_sequences( keys[i].value, value );
486 			fprintf( fp, "%s = \"%s\"\n", name.c_str(), value.c_str() );
487 		}
488 	}
489 	else
490 	{
491 		for( uint32 i=0; i<nrofkeys ;i++ )
492 			fprintf( fp, "%s = \"%s\"\n", keys[i].name.c_str(), keys[i].value.c_str() );
493 	}
494 
495 	if( fclose( fp ) )
496 	{
497 		char tmp_err[ERROR_OBJ_MSG_LEN];
498 		snprintf( tmp_err, ERROR_OBJ_MSG_LEN, _("Couldn't close file (%s)"), filename );
499 		THROW_ERROR( ErrorType_File_IO, tmp_err );
500 	}
501 }
502