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