1 /*
2  * libini - Library to read INI configuration files
3  *
4  * Copyright (C) 2014 Paul Cercueil <paul@crapouillou.net>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  */
16 
17 #include <errno.h>
18 #include <stdbool.h>
19 #include <stdint.h>
20 #include <stdio.h>
21 
22 #include "ini.h"
23 
24 struct INI {
25 	const char *buf, *end, *curr;
26 	bool free_buf_on_exit;
27 };
28 
_ini_open_mem(const char * buf,size_t len,bool free_buf_on_exit)29 static struct INI *_ini_open_mem(const char *buf,
30 			size_t len, bool free_buf_on_exit)
31 {
32 	struct INI *ini = malloc(sizeof(*ini));
33 	if (!ini) {
34 		errno = ENOMEM;
35 		return NULL;
36 	}
37 
38 	ini->buf = ini->curr = buf;
39 	ini->end = buf + len;
40 	ini->free_buf_on_exit = free_buf_on_exit;
41 	return ini;
42 }
43 
ini_open_mem(const char * buf,size_t len)44 struct INI *ini_open_mem(const char *buf, size_t len)
45 {
46 	return _ini_open_mem(buf, len, false);
47 }
48 
ini_open(const char * file)49 struct INI *ini_open(const char *file)
50 {
51 	FILE *f;
52 	char *buf, *ptr;
53 	size_t len, left;
54 	struct INI *ini = NULL;
55 	int ret = 0;
56 
57 	f = fopen(file, "r");
58 	if (!f) {
59 		ret = -errno;
60 		goto err_set_errno;
61 	}
62 
63 	fseek(f, 0, SEEK_END);
64 	ret = ftell(f);
65 
66 	if (ret <= 0) {
67 		ret = -EINVAL;
68 		goto error_fclose;
69 	}
70 
71 	len = (size_t) ret;
72 	buf = malloc(len);
73 	if (!buf) {
74 		ret = -ENOMEM;
75 		goto error_fclose;
76 	}
77 
78 	rewind(f);
79 
80 	for (left = len, ptr = buf; left; ) {
81 		size_t tmp = fread(ptr, 1, left, f);
82 		if (tmp == 0) {
83 			if (feof(f))
84 				break;
85 
86 			ret = -ferror(f);
87 			free(buf);
88 			goto error_fclose;
89 		}
90 
91 		left -= tmp;
92 		ptr += tmp;
93 	}
94 
95 	ini = _ini_open_mem(buf, len - left, true);
96 	if (!ini)
97 		ret = -errno;
98 
99 error_fclose:
100 	fclose(f);
101 err_set_errno:
102 	errno = -ret;
103 	return ini;
104 }
105 
ini_close(struct INI * ini)106 void ini_close(struct INI *ini)
107 {
108 	if (ini->free_buf_on_exit)
109 		free((char *) ini->buf);
110 	free(ini);
111 }
112 
skip_comments(struct INI * ini)113 static bool skip_comments(struct INI *ini)
114 {
115 	const char *curr = ini->curr;
116 	const char *end = ini->end;
117 
118 	while (curr != end) {
119 		if (*curr == '\r' || *curr == '\n')
120 			curr++;
121 		else if (*curr == '#')
122 			do { curr++; } while (curr != end && *curr != '\n');
123 		else
124 			break;
125 	}
126 
127 	ini->curr = curr;
128 	return curr == end;
129 }
130 
skip_line(struct INI * ini)131 static bool skip_line(struct INI *ini)
132 {
133 	const char *curr = ini->curr;
134 	const char *end = ini->end;
135 
136 	for (; curr != end && *curr != '\n'; curr++);
137 	if (curr == end) {
138 		ini->curr = end;
139 		return true;
140 	} else {
141 		ini->curr = curr + 1;
142 		return false;
143 	}
144 }
145 
ini_next_section(struct INI * ini,const char ** name,size_t * name_len)146 int ini_next_section(struct INI *ini, const char **name, size_t *name_len)
147 {
148 	const char *_name;
149 	if (ini->curr == ini->end)
150 		return 0; /* EOF: no more sections */
151 
152 	if (ini->curr == ini->buf) {
153 		if (skip_comments(ini) || *ini->curr != '[')
154 			return -EIO;
155 	} else while (*ini->curr != '[' && !skip_line(ini));
156 
157 	if (ini->curr == ini->end)
158 		return 0; /* EOF: no more sections */
159 
160 	_name = ++ini->curr;
161 	do {
162 		ini->curr++;
163 		if (ini->curr == ini->end || *ini->curr == '\n')
164 			return -EIO;
165 	} while (*ini->curr != ']');
166 
167 
168 	if (name && name_len) {
169 		*name = _name;
170 		*name_len = ini->curr - _name;
171 	}
172 
173 	ini->curr++;
174 	return 1;
175 }
176 
ini_read_pair(struct INI * ini,const char ** key,size_t * key_len,const char ** value,size_t * value_len)177 int ini_read_pair(struct INI *ini,
178 			const char **key, size_t *key_len,
179 			const char **value, size_t *value_len)
180 {
181 	size_t _key_len = 0;
182 	const char *_key, *_value, *curr, *end = ini->end;
183 
184 	if (skip_comments(ini))
185 		return 0;
186 	curr = _key = ini->curr;
187 
188 	if (*curr == '[')
189 		return 0;
190 
191 	while (true) {
192 		curr++;
193 
194 		if (curr == end || *curr == '\n') {
195 			return -EIO;
196 
197 		} else if (*curr == '=') {
198 			const char *tmp = curr;
199 			_key_len = curr - _key;
200 			for (tmp = curr - 1; tmp > ini->curr &&
201 					(*tmp == ' ' || *tmp == '\t'); tmp--)
202 				_key_len--;
203 			curr++;
204 			break;
205 		}
206 	}
207 
208 	/* Skip whitespaces. */
209 	while (curr != end && (*curr == ' ' || *curr == '\t')) curr++;
210 	if (curr == end)
211 		return -EIO;
212 
213 	_value = curr;
214 
215 	while (curr != end && *curr != '\n') curr++;
216 	if (curr == end)
217 		return -EIO;
218 
219 	*value = _value;
220 	*value_len = curr - _value - (*(curr - 1) == '\r');
221 	*key = _key;
222 	*key_len = _key_len;
223 
224 	ini->curr = ++curr;
225 	return 1;
226 }
227 
ini_set_read_pointer(struct INI * ini,const char * pointer)228 void ini_set_read_pointer(struct INI *ini, const char *pointer)
229 {
230 	if ((uintptr_t) pointer < (uintptr_t) ini->buf)
231 		ini->curr = ini->buf;
232 	else if ((uintptr_t) pointer > (uintptr_t) ini->end)
233 		ini->curr = ini->end;
234 	else
235 		ini->curr = pointer;
236 }
237 
ini_get_line_number(struct INI * ini,const char * pointer)238 int ini_get_line_number(struct INI *ini, const char *pointer)
239 {
240 	int line = 1;
241 	const char *it;
242 
243 	if ((uintptr_t) pointer < (uintptr_t) ini->buf)
244 		return -EINVAL;
245 	if ((uintptr_t) pointer > (uintptr_t) ini->end)
246 		return -EINVAL;
247 
248 	for (it = ini->buf; (uintptr_t) it < (uintptr_t) pointer; it++)
249 		line += (*it == '\n');
250 
251 	return line;
252 }
253