1 /* (c) 2002-2004 by Marcin Wiacek */
2 
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <ctype.h>
7 #include <errno.h>
8 #ifdef HAVE_WCHAR_H
9 #  include <wchar.h>
10 #endif
11 #ifdef HAVE_WCTYPE_H
12 #  include <wctype.h>
13 #endif
14 
15 #include <gammu-config.h>
16 #include <gammu-inifile.h>
17 #include "coding/coding.h"
18 
19 #include "../../libgammu/misc/string.h"
20 
21 /**
22  * Read information from file in Windows INI format style
23  */
INI_ReadFile(const char * FileName,gboolean Unicode,INI_Section ** result)24 GSM_Error INI_ReadFile(const char *FileName, gboolean Unicode, INI_Section **result)
25 {
26 	FILE		*f;
27 	gboolean		FFEEUnicode=FALSE;
28 	int		level = -1, buffer1used, buffer2used;
29 	size_t		bufferused, i, read_buffer_used=1000,read_buffer_pos=1000, num;
30 	unsigned char	ch[3], *buffer = NULL;
31 	unsigned char	*buffer2 = NULL, *buffer1 = NULL, read_buffer[1001];
32         INI_Section 	*INI_info = NULL, *INI_head = NULL, *heading;
33         INI_Entry 	*entry;
34 	GSM_Error	error = ERR_NONE;
35 
36 	*result = NULL;
37 
38 	if (FileName == NULL) {
39 		return ERR_CANTOPENFILE;
40 	}
41 
42 	f = fopen(FileName,"rb");
43 	if (f == NULL) {
44 		return ERR_CANTOPENFILE;
45 	}
46 
47 	num = 0;
48 	while(1) {
49 		/* We read one line from file */
50 		bufferused = 0;
51 		while (1) {
52 			if (read_buffer_used == read_buffer_pos) {
53 				read_buffer_used = fread(read_buffer,1,1000,f);
54 				read_buffer_pos = 0;
55 				if (read_buffer_used == 0) {
56 					error = ERR_NONE;
57 					goto done;
58 				}
59 			}
60 			if (Unicode) {
61 				if (num == 0) {
62 					if (read_buffer_used == read_buffer_pos) {
63 						continue;
64 					}
65 					ch[0] = read_buffer[read_buffer_pos++];
66 					num = 1;
67 				}
68 				if (num == 1) {
69 					if (read_buffer_used == read_buffer_pos) {
70 						continue;
71 					}
72 					ch[1] = read_buffer[read_buffer_pos++];
73 					num = 0;
74 				}
75 				if (level == -1) {
76 					if (ch[0] == 0xFF && ch[1] == 0xFE) FFEEUnicode = TRUE;
77 					level = 0;
78 					continue;
79 				}
80 				if (FFEEUnicode) {
81 					ch[2] = ch[0]; ch[0] = ch[1]; ch[1] = ch[2];
82 				}
83 			} else {
84 				if (read_buffer_used == read_buffer_pos) {
85 					continue;
86 				}
87 				ch[0] = 0;
88 				ch[1] = read_buffer[read_buffer_pos++];
89 				if (level == -1) level = 0;
90 			}
91 			if ((ch[0] == 0 && ch[1] == 13) ||
92 			    (ch[0] == 0 && ch[1] == 10)) {
93 				break;
94 			}
95 			buffer 			= (unsigned char *)realloc(buffer,bufferused+2);
96                         if (buffer == NULL) {
97                                 error = ERR_MOREMEMORY;
98 				goto done;
99                         }
100 			buffer[bufferused] 	= ch[0];
101 			buffer[bufferused+1] 	= ch[1];
102 			bufferused		= bufferused + 2;
103 		}
104 
105 		buffer1used = 0;
106 		buffer2used = 0;
107 		if (level == 1) level = 0;
108 		if (level == 3 || level == 4 || level == 5) level = 2;
109 
110 		/* We parse read line */
111 		for (i=0;i<bufferused/2;i++) {
112 			ch[0] = buffer[i*2];
113 			ch[1] = buffer[i*2+1];
114 			if (level == 0) { /* search for name of section */
115 				if (ch[0] == 0 && ch[1] == '[') level = 1;
116 				if (ch[0] == 0 && ch[1] == ';') {
117 					break;
118 				}
119 				if (ch[0] == 0 && ch[1] == '#') {
120 					break;
121 				}
122 				continue;
123 			}
124 			if (level == 1) { /* section name */
125 				if (ch[0] == 0 && ch[1] == ']') {
126 					if (buffer1used == 0) {
127 						break;
128 					}
129 					if (Unicode) {
130 						buffer1 		= (unsigned char *)realloc(buffer1,buffer1used+2);
131 						buffer1[buffer1used] 	= 0;
132 						buffer1[buffer1used+1] 	= 0;
133 						buffer1used		= buffer1used + 2;
134 					} else {
135 						buffer1 		= (unsigned char *)realloc(buffer1,buffer1used+1);
136 						buffer1[buffer1used] 	= 0x00;
137 						buffer1used		= buffer1used + 1;
138 					}
139 					heading = (INI_Section *)malloc(sizeof(*heading));
140 		                        if (heading == NULL) {
141 						error = ERR_MOREMEMORY;
142 						goto done;
143 		                        }
144 					heading->SectionName = (char *)malloc(buffer1used);
145 					memcpy(heading->SectionName,buffer1,buffer1used);
146 		                        heading->Prev = INI_info;
147 					heading->Next = NULL;
148 		                        if (INI_info != NULL) {
149 		                                INI_info->Next  = heading;
150 		                        } else {
151 		                                INI_head 	= heading;
152 		                        }
153 		                        INI_info 		= heading;
154 					INI_info->SubEntries 	= NULL;
155 					level 	 		= 2;
156 					break;
157 				}
158 				if (Unicode) {
159 					buffer1 		= (unsigned char *)realloc(buffer1,buffer1used+2);
160 					buffer1[buffer1used] 	= ch[0];
161 					buffer1[buffer1used+1] 	= ch[1];
162 					buffer1used		= buffer1used + 2;
163 				} else {
164 					buffer1 		= (unsigned char *)realloc(buffer1,buffer1used+1);
165 					buffer1[buffer1used] 	= ch[1];
166 					buffer1used		= buffer1used + 1;
167 				}
168 				continue;
169 			}
170 			if (level == 2) { /* search for key name */
171 				if (ch[0] == 0 && ch[1] == ';') {
172 					break;
173 				}
174 				if (ch[0] == 0 && ch[1] == '#') {
175 					break;
176 				}
177 				if (ch[0] == 0 && ch[1] == '[') {
178 					level = 1;
179 					continue;
180 				}
181 				if (Unicode) {
182 			                if (myiswspace(ch)) {
183 						continue;
184 					}
185 				} else {
186 			                if (isspace((int) ch[1])) {
187 						continue;
188 					}
189 				}
190 				level = 3;
191 			}
192 			if (level == 3) { /* key name */
193 				if (ch[0] == 0 && ch[1] == '=') {
194 					if (buffer1used == 0) {
195 						break;
196 					}
197 					while(1) {
198 						if (Unicode) {
199 					                if (!myiswspace(buffer1+(buffer1used-2))) {
200 								break;
201 							}
202 							buffer1used = buffer1used - 2;
203 						} else {
204 					                if (!isspace((int)buffer1[buffer1used-1])) {
205 								break;
206 							}
207 							buffer1used = buffer1used - 1;
208 						}
209 					}
210 					level = 4;
211 					continue;
212 				}
213 				if (Unicode) {
214 					buffer1 		= (unsigned char *)realloc(buffer1,buffer1used+2);
215 					buffer1[buffer1used] 	= ch[0];
216 					buffer1[buffer1used+1] 	= ch[1];
217 					buffer1used		= buffer1used + 2;
218 				} else {
219 					buffer1 		= (unsigned char *)realloc(buffer1,buffer1used+1);
220 					buffer1[buffer1used] 	= ch[1];
221 					buffer1used		= buffer1used + 1;
222 				}
223 			}
224 			if (level == 4) { /* search for key value */
225 				if (Unicode) {
226 			                if (myiswspace(ch)) {
227 						continue;
228 					}
229 				} else {
230 			                if (isspace((int) ch[1])) {
231 						continue;
232 					}
233 				}
234 				level = 5;
235 			}
236 			if (level == 5) { /* key value */
237 				if (Unicode) {
238 					buffer2 		= (unsigned char *)realloc(buffer2,buffer2used+2);
239 					buffer2[buffer2used] 	= ch[0];
240 					buffer2[buffer2used+1] 	= ch[1];
241 					buffer2used		= buffer2used + 2;
242 				} else {
243 					buffer2 		= (unsigned char *)realloc(buffer2,buffer2used+1);
244 					buffer2[buffer2used] 	= ch[1];
245 					buffer2used		= buffer2used + 1;
246 				}
247 			}
248 		}
249 		if (level == 5) {
250 			if (Unicode) {
251 				while (myiswspace(buffer2 + buffer2used - 2) && buffer2used > 0) {
252 					buffer2used -= 2;
253 				}
254 			} else {
255 				while (isspace(buffer2[buffer2used - 1]) && buffer2used > 0) {
256 					buffer2used -= 1;
257 				}
258 			}
259 			if (buffer2used == 0) {
260 				continue;
261 			}
262 
263 			entry = (INI_Entry *)malloc(sizeof(*entry));
264                         if (entry == NULL) {
265                                 error = ERR_MOREMEMORY;
266 				goto done;
267                         }
268 			if (Unicode) {
269 				buffer1 		= (unsigned char *)realloc(buffer1,buffer1used+2);
270 				buffer1[buffer1used] 	= 0;
271 				buffer1[buffer1used+1] 	= 0;
272 				buffer1used		= buffer1used + 2;
273 				buffer2 		= (unsigned char *)realloc(buffer2,buffer2used+2);
274 				buffer2[buffer2used] 	= 0;
275 				buffer2[buffer2used+1] 	= 0;
276 				buffer2used		= buffer2used + 2;
277 			} else {
278 				buffer1 		= (unsigned char *)realloc(buffer1,buffer1used+1);
279 				buffer1[buffer1used] 	= 0x00;
280 				buffer1used		= buffer1used + 1;
281 				buffer2 		= (unsigned char *)realloc(buffer2,buffer2used+1);
282 				buffer2[buffer2used] 	= 0x00;
283 				buffer2used		= buffer2used + 1;
284 			}
285 
286 			entry->EntryName = (char *)malloc(buffer1used);
287 			if (entry->EntryName == NULL) {
288                                 error = ERR_MOREMEMORY;
289 				goto done;
290                         }
291 			memcpy(entry->EntryName,buffer1,buffer1used);
292 
293 			entry->EntryValue = (char *)malloc(buffer2used);
294 			if (entry->EntryValue == NULL) {
295                                 error = ERR_MOREMEMORY;
296 				goto done;
297                         }
298 			memcpy(entry->EntryValue,buffer2,buffer2used);
299 
300 			entry->Prev = NULL;
301                         entry->Next = INI_info->SubEntries;
302                         if (INI_info->SubEntries != NULL) {
303 				INI_info->SubEntries->Prev = entry;
304 			}
305                         INI_info->SubEntries = entry;
306 		}
307 	}
308 done:
309 	free(buffer);
310 	buffer=NULL;
311 	free(buffer1);
312 	buffer1=NULL;
313 	free(buffer2);
314 	buffer2=NULL;
315 	fclose(f);
316 
317 	if (error == ERR_NONE) {
318 		*result = INI_head;
319 		if (INI_head == NULL) {
320 			error = ERR_FILENOTSUPPORTED;
321 		}
322 	}
323 	return error;
324 }
325 
326 /**
327  * Returns integer value from configuration.
328  */
INI_GetInt(INI_Section * cfg,const unsigned char * section,const unsigned char * key,int fallback)329 int INI_GetInt(INI_Section *cfg, const unsigned char *section, const unsigned char *key, int fallback)
330 {
331 	char *str;
332 
333 	str = (char *)INI_GetValue(cfg, section, key, FALSE);
334 	if (str) {
335 		return atoi(str);
336 	} else {
337 		return fallback;
338 	}
339 }
340 
341 /**
342  * Returns integer value from configuration.
343  */
INI_GetBool(INI_Section * cfg,const unsigned char * section,const unsigned char * key,gboolean fallback)344 gboolean INI_GetBool(INI_Section *cfg, const unsigned char *section, const unsigned char *key, gboolean fallback)
345 {
346 	char *str;
347 	gboolean ret;
348 
349 	str = (char *)INI_GetValue(cfg, section, key, FALSE);
350 	if (str) {
351 		ret = GSM_StringToBool(str);
352 		if (ret == -1) {
353 			return fallback;
354 		}
355 		return ret;
356 	} else {
357 		return fallback;
358 	}
359 }
360 
361 /**
362  * Search for key value in file in Windows INI format style
363  * Returns found value or NULL
364  */
INI_GetValue(INI_Section * cfg,const unsigned char * section,const unsigned char * key,const gboolean Unicode)365 unsigned char *INI_GetValue(INI_Section *cfg, const unsigned char *section, const unsigned char *key, const gboolean Unicode)
366 {
367         INI_Section 	*sec;
368         INI_Entry  	*ent;
369 
370         if (cfg == NULL || section == NULL || key == NULL) return NULL;
371 
372 	if (Unicode) {
373 	        /* Search for section */
374 		sec = cfg;
375 		while (sec != NULL) {
376 	                if (mywstrncasecmp(section, sec->SectionName, 0)) {
377 	                        /* Search for key inside section */
378 				ent = sec->SubEntries;
379 				while (ent != NULL) {
380 	                                if (mywstrncasecmp(key,ent->EntryName,0)) {
381 	                                        return ent->EntryValue;
382 	                                }
383 					ent = ent->Next;
384 	                        }
385 	                }
386 			sec = sec->Next;
387 		}
388 	} else {
389 	        /* Search for section */
390 		sec = cfg;
391 		while (sec != NULL) {
392 	                if (strcasecmp(section, sec->SectionName) == 0) {
393 	                        /* Search for key inside section */
394 				ent = sec->SubEntries;
395 				while (ent != NULL) {
396 	                                if (strcasecmp(key,ent->EntryName) == 0) {
397 	                                        return ent->EntryValue;
398 	                                }
399 					ent = ent->Next;
400 	                        }
401 	                }
402 			sec = sec->Next;
403 		}
404 	}
405         return NULL;
406 }
407 
408 /* Return last value in specified section */
INI_FindLastSectionEntry(INI_Section * file_info,const unsigned char * section,const gboolean Unicode)409 INI_Entry *INI_FindLastSectionEntry(INI_Section *file_info, const unsigned char *section, const gboolean Unicode)
410 {
411 	INI_Section 	*h;
412 	INI_Entry	*e;
413 
414 	e = NULL;
415 	/* First find our section */
416         for (h = file_info; h != NULL; h = h->Next) {
417 		if (Unicode) {
418 			if (mywstrncasecmp(section, h->SectionName, 0)) {
419 				e = h->SubEntries;
420 				break;
421 			}
422 		} else {
423 			if (strcasecmp(section, h->SectionName) == 0) {
424 				e = h->SubEntries;
425 				break;
426 			}
427 		}
428 	}
429 
430 	if (e == NULL) return NULL;
431 
432 	/* Goes into last value in section */
433 	while (e->Next != NULL) e = e->Next;
434 	return e;
435 }
436 
INI_Free_Entries(INI_Entry * entry)437 void INI_Free_Entries(INI_Entry *entry)
438 {
439 	INI_Entry *cur = entry, *next=NULL;
440 
441 	if (cur == NULL) return;
442 	while (cur != NULL) {
443 		next = cur->Next;
444 		free(cur->EntryName);
445 		cur->EntryName=NULL;
446 		free(cur->EntryValue);
447 		cur->EntryValue=NULL;
448 		free(cur);
449 		cur=NULL;
450 		cur=next;
451 	}
452 }
453 
INI_Free(INI_Section * head)454 void INI_Free(INI_Section *head)
455 {
456 	INI_Section *cur = head, *next;
457 
458 	if (cur == NULL) return;
459 	while (cur != NULL) {
460 		next = cur->Next;
461 		free(cur->SectionName);
462 		cur->SectionName=NULL;
463 		INI_Free_Entries(cur->SubEntries);
464 		free(cur);
465 		cur=NULL;
466 		cur=next;
467 	}
468 }
469 
GSM_StringToBool(const char * value)470 gboolean GSM_StringToBool(const char *value)
471 {
472 	if (strcasecmp(value, "true") == 0) return TRUE;
473 	if (strcasecmp(value, "yes") == 0) return TRUE;
474 	if (strcasecmp(value, "y") == 0) return TRUE;
475 	if (strcasecmp(value, "t") == 0) return TRUE;
476 	if (strcasecmp(value, "1") == 0) return TRUE;
477 	if (strcasecmp(value, "false") == 0) return FALSE;
478 	if (strcasecmp(value, "no") == 0) return FALSE;
479 	if (strcasecmp(value, "f") == 0) return FALSE;
480 	if (strcasecmp(value, "n") == 0) return FALSE;
481 	if (strcasecmp(value, "0") == 0) return FALSE;
482 	return -1;
483 }
484 
485 /* How should editor hadle tabs in this file? Add editor commands here.
486  * vim: noexpandtab sw=8 ts=8 sts=8:
487  */
488