1 /*
2 Copyright (c) 2003, 2004, 2005, 2016 Olivier Sessink
3 All rights reserved.
4 
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
8   * Redistributions of source code must retain the above copyright
9     notice, this list of conditions and the following disclaimer.
10   * Redistributions in binary form must reproduce the above
11     copyright notice, this list of conditions and the following
12     disclaimer in the documentation and/or other materials provided
13     with the distribution.
14   * The names of its contributors may not be used to endorse or
15     promote products derived from this software without specific
16     prior written permission.
17 
18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
23 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
28 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 POSSIBILITY OF SUCH DAMAGE.
30 */
31 #include "config.h"
32 
33 #include <ctype.h> /* isspace() */
34 #include <stdio.h> /* fseek() */
35 #include <stdlib.h> /* malloc() */
36 #include <string.h> /* memset() */
37 #include <fcntl.h> /* fcntl() */
38 /*#define DEBUG*/
39 
40 #ifdef DEBUG
41 #include <syslog.h>
42 #endif
43 
44 #include "jk_lib.h"
45 #include "iniparser.h"
46 
new_iniparser(char * filename)47 Tiniparser *new_iniparser(char *filename) {
48 	FILE *tmp;
49 	tmp = fopen(filename, "r");
50 	if (tmp) {
51 		Tiniparser *ip = malloc(sizeof(Tiniparser));
52 		ip->filename = strdup(filename);
53 		ip->fd = tmp;
54 		/* set close-on-exec so this file descriptor will not be passed
55 		to the a process after an exec() call */
56 		fcntl(fileno(ip->fd), F_SETFD, FD_CLOEXEC);
57 		DEBUG_MSG("new_iniparser, ip=%p for filename %s\n",ip,filename);
58 		return ip;
59 	}
60 	return NULL;
61 }
62 
iniparser_close(Tiniparser * ip)63 void iniparser_close(Tiniparser *ip) {
64 	DEBUG_MSG("close fd\n");
65 	fclose(ip->fd);
66 	DEBUG_MSG("free filename=%p\n",ip->filename);
67 	free(ip->filename);
68 	DEBUG_MSG("free ip=%p\n",ip->filename);
69 	free(ip);
70 	DEBUG_MSG("done\n");
71 }
72 
iniparser_next_section(Tiniparser * ip,char * buf,int buflen)73 char *iniparser_next_section(Tiniparser *ip, char *buf, int buflen) {
74 	int sectionNameChar=0, sectionStart=0;
75 	unsigned short int inComment = 0;
76 	char prevch='\0', ch;
77 	DEBUG_MSG("iniparser_next_section, looking for next section..\n");
78 	while (!feof(ip->fd)){
79 		ch=fgetc(ip->fd);
80 		if (ch == '#' && (prevch == '\n' || prevch=='\0')) {
81 			DEBUG_MSG("Comment start (%c)\n",ch);
82 			inComment = 1;
83 		} else if (ch == '\n' && inComment == 1) {
84 			DEBUG_MSG("Comment stop (%c)\n",ch);
85 			inComment = 0;
86 		} else if (inComment == 1) {
87 			/* do nothing if in comment */
88 			/*DEBUG_MSG("do nothing, we're in a comment (%c)\n",ch);*/
89 		} else if (!sectionStart && ch=='[') {
90 			DEBUG_MSG("Section begins (%c)\n",ch);
91 			sectionStart=1;
92 		} else if (sectionStart && ch != ']') {
93 			buf[sectionNameChar] = ch;
94 			sectionNameChar++;
95 			DEBUG_MSG("added '%c' to sectionname\n",ch);
96 		} else if (sectionStart && sectionNameChar != 0 && ch==']') {
97 			buf[sectionNameChar] = '\0';
98 			DEBUG_MSG("iniparser_next_section, found '%c', sectionStart=%d, found [%s]\n", ch,sectionStart,buf);
99 			return buf;
100 		}
101 		prevch = ch;
102 	}
103 	return NULL;
104 }
105 /* test if section 'section' is available, and leaves the filepointer at the end of the section name */
iniparser_has_section(Tiniparser * ip,const char * section)106 unsigned short int iniparser_has_section(Tiniparser *ip, const char *section) {
107 	char buffer[256], *found;
108 	fseek(ip->fd,0,SEEK_SET);
109 	DEBUG_MSG("iniparser_has_section, looking for %s from position %d\n",section,0);
110 	while ((found = iniparser_next_section(ip, buffer, 256))) {
111 		DEBUG_MSG("comparing %s and %s\n",section,found);
112 		if (strcmp(found, section)==0) {
113 			DEBUG_MSG("iniparser_has_section, return 1\n");
114 			return 1;
115 		}
116 	}
117 	return 0;
118 }
119 
iniparser_get_string_at_position(Tiniparser * ip,const char * section,const char * key,long position,char * buffer,int bufferlen)120 int iniparser_get_string_at_position(Tiniparser*ip, const char *section, const char *key, long position, char *buffer, int bufferlen) {
121 	char ch='\0', prevch='\0';
122 	unsigned int sectionNameChar=0, keyNameChar=0, bufferChar=0;
123 	unsigned short int inSection=0, sectionStart=0, foundKey=0, inComment=0, inWrongKey=0;
124 	DEBUG_MSG("iniparser_get_string_at_position, looking for key %s in section %s, starting at pos %ld\n",key,section,position);
125 	if (fseek(ip->fd,position,SEEK_SET) != 0) {
126 		DEBUG_MSG("there was an error seeking to %ld, current position=%ld, reset to zero\n",position,ftell(ip->fd));
127 		fseek(ip->fd, 0, SEEK_SET);
128 	}
129 	DEBUG_MSG("current position of the stream is %ld\n",ftell(ip->fd));
130 	while (!feof(ip->fd)){
131 		prevch = ch;
132 		ch=fgetc(ip->fd);
133 
134 		if (inComment == 1) {
135 			if (ch == '\n') {
136 				DEBUG_MSG("end of comment found\n");
137 				inComment = 0;
138 			}
139 			continue;
140 		} else if  (ch == '#' && (prevch == '\n' || prevch == '\0')) {
141 			DEBUG_MSG("inComment!\n");
142 			inComment = 1;
143 			continue;
144 		}
145 
146 		if (!inSection) {
147 			if (!sectionStart && ch=='['){
148 				if (inSection){
149 					/* found nothing */
150 					break;
151 				}
152 				DEBUG_MSG("Section begins. Looking for [%s]\n", section);
153 				sectionStart=1;
154 			} else if (sectionStart && ch==section[sectionNameChar]){
155 				DEBUG_MSG("Matched section name character: %c\n", ch);
156 				sectionNameChar++;
157 			} else if (sectionStart && sectionNameChar != 0 && ch==']'){
158 				DEBUG_MSG("Found section name end, inSection=%d, found [%s]\n",inSection,section);
159 				sectionStart=0;
160 				inSection=1;
161 				sectionNameChar=0;
162 				DEBUG_MSG("The correct section %s is now found, now we continue with the key %s\n", section, key);
163 			} else if (sectionStart){
164 				DEBUG_MSG("Oops, wrong section, %c is not in position %d of %s\n", ch,sectionNameChar,section);
165 				sectionStart=0;
166 				sectionNameChar=0;
167 			}
168 		} else if (inWrongKey/* && inSection is implied */) {
169 			if (ch == '\n') {
170 				DEBUG_MSG("inWrongKey, found end of line!\n");
171 				inWrongKey = 0;
172 				foundKey=0;
173 				keyNameChar=0;
174 			} else {
175 				/*DEBUG_MSG("inWrongKey, found %c, pass till end of line\n",ch);*/
176 			}
177 		} else if (!foundKey /* && inSection is implied */) {
178 			if (ch==key[keyNameChar]){
179 				DEBUG_MSG("Found a valid letter of the key: %c on position %d of %s, continue to test if next character is also valid\n", ch,keyNameChar,key);
180 				keyNameChar++;
181 			} else if (isspace(ch)) {
182 				/* *before* the key, and *after* the key, before the '=' there can be spaces */
183 				DEBUG_MSG("found a space, we ignore spaces when we are looking for the key\n");
184 			} else if (keyNameChar != 0 && ch == '='){
185 				DEBUG_MSG("Character %c, found the key %s, set foundKey to 1\n", ch,key);
186 				foundKey=1;
187 			} else if (ch=='\n'){
188 				DEBUG_MSG("End of line, start looking again for %s\n", key);
189 				inWrongKey=0;
190 				keyNameChar=0;
191 			} else if (ch=='[') {
192 				DEBUG_MSG("Found the start of a new section, abort, the key does not exist\n");
193 				buffer[0]='\0';
194 				return -1;
195 			} else {
196 				DEBUG_MSG("if all else fails: %c must be a character that is not on position %d of key %s, set inWrongKey\n",ch,keyNameChar,key);
197 				inWrongKey=1;
198 			}
199 		} else if (foundKey /* && inSection is implied */) {
200 			if (bufferChar < bufferlen){
201 				if (ch != '\n') {
202 					DEBUG_MSG("Insection, found the key, getting the content for the key: %c\n", ch);
203 					buffer[bufferChar++]=ch;
204 				} else {
205 					DEBUG_MSG("found a newline: the end of the content of the key! ");
206 					buffer[bufferChar]='\0';
207 					DEBUG_MSG("return '%s'\n",buffer);
208 					return bufferChar;
209 				}
210 			} else {
211 				DEBUG_MSG("Hit the buffer max, EOM, done w/ key %s=\n", key);
212 				break;
213 			}
214 		} else {
215 			DEBUG_MSG("unhandled character %c ?\n",ch);
216 		}
217 		prevch = ch;
218 	}
219 	buffer[bufferChar]='\0';
220 	DEBUG_MSG("iniparser_get_string_at_position, end-of-file, bufferChar=%d\n",bufferChar);
221 	return bufferChar;
222 }
223 
iniparser_get_int_at_position(Tiniparser * ip,const char * section,const char * key,long position,int defaultval)224 int iniparser_get_int_at_position(Tiniparser *ip, const char *section, const char *key, long position, int defaultval) {
225 	char data[25];
226 	int ret=defaultval;
227 	memset(data, 0, 25);
228 	if (iniparser_get_string_at_position(ip, section, key, position, data, 25)==-1){
229 		return defaultval;
230 	}
231 	strip_string(data);
232 	sscanf(data, "%u", &ret);
233 	return ret;
234 }
235 
iniparser_get_octalint_at_position(Tiniparser * ip,const char * section,const char * key,long position,int defaultval)236 int iniparser_get_octalint_at_position(Tiniparser *ip, const char *section, const char *key, long position, int defaultval) {
237 	char data[25];
238 	int ret=defaultval;
239 	memset(data, 0, 25);
240 	if (iniparser_get_string_at_position(ip, section, key, position, data, 25)==-1){
241 		return defaultval;
242 	}
243 	strip_string(data);
244 	sscanf(data, "%o", &ret);
245 	return ret;
246 }
247 
iniparser_get_float_at_position(Tiniparser * ip,const char * section,const char * key,long position,float defaultval)248 float iniparser_get_float_at_position(Tiniparser *ip, const char *section, const char *key, long position, float defaultval) {
249 	float ret = defaultval;
250 	char data[25];
251 	memset(data, 0, 25);
252 	if (iniparser_get_string_at_position(ip, section, key, position, data, 25)==-1){
253 		DEBUG_MSG("iniparser_get_float_at_position, no string found\n");
254 		return 0.0;
255 	}
256 	strip_string(data);
257 	sscanf(data, "%f", &ret);
258 	return ret;
259 }
260 /*
261 int iniparser_value_len(Tiniparser *ip, const char *section, const char *key){
262 	char ch;
263 	unsigned int sectionNameChar=0, keyNameChar=0;
264 	unsigned int valueLength=0;
265 	unsigned short int inSection=0, sectionStart=0, foundKey=0;
266 	while (!feof(ip->fd)){
267 		ch=fgetc(ip->fd);
268 		if (!sectionStart && ch=='['){
269 			if (inSection){
270 				break;
271 			}
272 			sectionStart=1;
273 		} else if (sectionStart && ch==section[sectionNameChar]){
274 			sectionNameChar++;
275 		} else if (sectionStart && sectionNameChar != 0 && ch==']'){
276 			sectionStart=0;
277 			inSection=1;
278 			sectionNameChar=0;
279 		} else if (sectionStart){
280 			sectionStart=0;
281 			sectionNameChar=0;
282 		}
283 
284 		if (inSection && !foundKey && ch==key[keyNameChar]){
285 			keyNameChar++;
286 		} else if (inSection && !foundKey && keyNameChar != 0 && ch == '='){
287 			foundKey=1;
288 		} else if (inSection && keyNameChar != 0 && !foundKey){
289 			foundKey=0;
290 			keyNameChar=0;
291 		} else if (inSection && foundKey && (ch==13 || ch==10 || ch==';')){
292 			foundKey=0;
293 			break;
294 		} else if (inSection && foundKey){
295 			valueLength++;
296 		}
297 	}
298 	return valueLength;
299 }
300 */
301