Line data Source code
1 : /* inih -- simple .INI file parser
2 :
3 : inih is released under the New BSD license (see LICENSE.txt). Go to the project
4 : home page for more info:
5 :
6 : http://code.google.com/p/inih/
7 :
8 : */
9 :
10 : #include "inih.h"
11 : #include <ctype.h>
12 : #include <kdbassert.h>
13 : #include <kdblogger.h>
14 : #include <regex.h>
15 : #include <stdio.h>
16 : #include <stdlib.h>
17 : #include <string.h>
18 :
19 : #if !INI_USE_STACK
20 : #include <stdlib.h>
21 : #endif
22 :
23 : #define MAX_SECTION 65535
24 : #define MAX_NAME 65535
25 :
26 : /* Strip whitespace chars off end of given string, in place. Return s. */
27 256948 : static char * rstrip (char * s)
28 : {
29 256948 : char * p = s + strlen (s);
30 548308 : while (p > s && isspace ((unsigned char) (*--p)))
31 : {
32 34412 : *p = '\0';
33 : }
34 256948 : return s;
35 : }
36 :
37 : /* Return pointer to first non-whitespace char in given string. */
38 293074 : static char * lskip (const char * s)
39 : {
40 691122 : while (*s && isspace ((unsigned char) (*s)))
41 : {
42 104974 : s++;
43 : }
44 293074 : return (char *) s;
45 : }
46 :
47 : /* Return pointer to first char c or ';' comment in given string, or pointer to
48 : null at end of string if neither found. ';' must be prefixed by a whitespace
49 : character to register as a comment. */
50 128473 : static char * find_char_or_comment (const char * s, char c)
51 : {
52 128473 : int was_whitespace = 0;
53 1216465 : while (*s && *s != c && !(was_whitespace && *s == ';'))
54 : {
55 959519 : was_whitespace = isspace ((unsigned char) (*s));
56 959519 : s++;
57 : }
58 128473 : return (char *) s;
59 : }
60 :
61 : /* Version of strncpy that ensures dest (size bytes) is null-terminated. */
62 : static char * strncpy0 (char * dest, const char * src, size_t size)
63 : {
64 : #if defined(__GNUC__) && __GNUC__ >= 8 && !defined(__clang__)
65 : #pragma GCC diagnostic push
66 : #pragma GCC diagnostic ignored "-Wstringop-truncation"
67 : #endif
68 150974 : strncpy (dest, src, size);
69 : #if defined(__GNUC__) && __GNUC__ >= 8 && !defined(__clang__)
70 : #pragma GCC diagnostic pop
71 : #endif
72 150974 : dest[size - 1] = '\0';
73 : return dest;
74 : }
75 :
76 164598 : static int isContinuation (const char * line, const struct IniConfig * config)
77 : {
78 164598 : if (!(*line)) return 0;
79 164598 : return !strncmp (line, config->continuationString, strlen (config->continuationString));
80 : }
81 :
82 : static int isSection (const char * line)
83 : {
84 153771 : if (!(*line)) return 0;
85 153771 : if (*line == '[') return 1;
86 : return 0;
87 : }
88 :
89 : static int isComment (const char * line)
90 : {
91 131275 : if (!(*line)) return 0;
92 131275 : if (*line == '#' || *line == ';') return 1;
93 : return 0;
94 : }
95 :
96 : /* See documentation in header file. */
97 16999 : int ini_parse_file (FILE * file, const struct IniConfig * config, void * user)
98 : {
99 : /* Uses a fair bit of stack (use heap instead if you need to) */
100 : char * line;
101 :
102 16999 : char section[MAX_SECTION] = "";
103 16999 : char prev_name[MAX_NAME] = "";
104 :
105 : char * start;
106 : char * end;
107 : char * name;
108 : char * value;
109 16999 : char delim = config->delim;
110 16999 : int lineno = 0;
111 16999 : int error = 0;
112 :
113 16999 : line = (char *) malloc (INI_MAX_LINE);
114 :
115 : ELEKTRA_LOG_DEBUG ("Allocated memory for line");
116 :
117 16999 : if (!line)
118 : {
119 : return -2;
120 : }
121 :
122 : /* Scan through file line by line */
123 181607 : while (fgets (line, INI_MAX_LINE, file) != NULL)
124 : {
125 164610 : lineno++;
126 : ELEKTRA_LOG_DEBUG ("Read line %d with content “%s”", lineno, line);
127 :
128 164610 : start = line;
129 : #if INI_ALLOW_BOM
130 164610 : if (lineno == 1 && (unsigned char) start[0] == 0xEF && (unsigned char) start[1] == 0xBB && (unsigned char) start[2] == 0xBF)
131 : {
132 0 : start += 3;
133 0 : config->bomHandler (user, 1);
134 : }
135 : else
136 : {
137 164610 : config->bomHandler (user, 0);
138 : }
139 : #endif
140 164610 : if (*start == '\n')
141 : {
142 12 : if (!config->commentHandler (user, "") && !error) error = lineno;
143 12 : continue;
144 : }
145 164598 : start = lskip (line);
146 164598 : if (*start == '\0')
147 : {
148 0 : if (!config->commentHandler (user, "") && !error) error = lineno;
149 0 : continue;
150 : }
151 164598 : if (isContinuation (line, config) && config->supportMultiline && *prev_name)
152 : {
153 10827 : start = line + strlen (config->continuationString);
154 10827 : if (*start == '"') ++start;
155 10827 : end = line + (strlen (line) - 1);
156 32481 : while ((*end != '"') && (!isprint (*end)) && (end > start))
157 : {
158 10827 : if (*end == '\n') *end = '\0';
159 10827 : --end;
160 : }
161 10827 : if (*end == '"') *end = '\0';
162 :
163 10827 : if (!config->keyHandler (user, section, prev_name, start, 1) && !error) error = lineno;
164 : }
165 307542 : else if (isSection (line))
166 : {
167 : ELEKTRA_LOG_DEBUG ("Line contains a section");
168 22496 : end = line + (strlen (line) - 1);
169 67488 : while (end > start)
170 : {
171 44992 : if (*end == ']') break;
172 22496 : --end;
173 : }
174 22496 : ++start;
175 22496 : if (*end == ']')
176 : {
177 22496 : *end = '\0';
178 22496 : strncpy0 (section, start, sizeof (section));
179 22496 : *prev_name = '\0';
180 : ELEKTRA_LOG_DEBUG ("Found section “%s”", section);
181 :
182 22496 : size_t numberBackslashes = 0;
183 44993 : for (char * endSection = section + strlen (section) - 1; endSection >= section && *endSection == '\\';
184 1 : endSection--)
185 : {
186 1 : numberBackslashes++;
187 : }
188 22496 : if (numberBackslashes % 2 != 0)
189 : {
190 : ELEKTRA_LOG_WARNING ("Found uneven number of backlashes at end of section");
191 : error = lineno;
192 : break;
193 : }
194 :
195 22495 : if (!config->sectionHandler (user, section) && !error) error = lineno;
196 : }
197 : else
198 : {
199 0 : end = line + (strlen (line) - 1);
200 0 : if (*end == '\n')
201 : {
202 : strncpy0 (section, start, sizeof (section));
203 0 : while (fgets (line, INI_MAX_LINE, file))
204 : {
205 0 : end = line + (strlen (line) - 1);
206 0 : while ((end > line) && *end != ']')
207 0 : --end;
208 0 : if (*end == ']')
209 : {
210 0 : *end = '\0';
211 0 : strncpy0 (section + strlen (section), line, sizeof (section) - strlen (section));
212 0 : *prev_name = '\0';
213 0 : if (!config->sectionHandler (user, section) && !error) error = lineno;
214 : break;
215 : }
216 : else
217 : {
218 0 : strncpy0 (section + strlen (section), line, sizeof (section) - strlen (section));
219 : }
220 : }
221 : }
222 : else
223 : {
224 : error = lineno;
225 : }
226 : }
227 : }
228 262550 : else if (isComment (line))
229 : {
230 2797 : start = line;
231 2797 : end = line + (strlen (line) - 1);
232 2797 : if (*end == '\n') *end = '\0';
233 2797 : if (!config->commentHandler (user, start) && !error) error = lineno;
234 : }
235 : else
236 : {
237 : ELEKTRA_LOG_DEBUG ("Line contains a key");
238 :
239 : char * ptr = start;
240 : unsigned int assign = 0;
241 : ELEKTRA_LOG_DEBUG ("Search for delimiter “%c”", delim);
242 6275606 : while (*ptr)
243 : {
244 6147128 : if (*ptr == delim)
245 : {
246 128486 : ++assign;
247 : }
248 6147128 : ++ptr;
249 : }
250 :
251 128478 : if (assign == 1)
252 : {
253 : ELEKTRA_LOG_DEBUG ("Found exactly one delimiter");
254 128464 : name = start;
255 128464 : end = strchr (start, delim);
256 128464 : if (*name == '"')
257 : {
258 : ELEKTRA_LOG_DEBUG ("Name starts with double quote character");
259 1821 : ++name;
260 1821 : if (*(end - 2) == '"')
261 : {
262 0 : *(end - 2) = '\0';
263 : }
264 1821 : else if (*(end - 1) == '"')
265 : {
266 1820 : *(end - 1) = '\0';
267 : }
268 : else
269 : {
270 : ELEKTRA_LOG_DEBUG ("Did not find closing double quote characters in current line");
271 : strncpy0 (prev_name, name, sizeof (prev_name));
272 2 : while (fgets (line, INI_MAX_LINE, file))
273 : {
274 : ELEKTRA_LOG_DEBUG ("Read continuation line with content “%s”", line);
275 1 : end = line + (strlen (line) - 1);
276 5 : while (end > line && *end != '"')
277 3 : --end;
278 1 : if (*end == '"')
279 : {
280 : ELEKTRA_LOG_DEBUG ("Found closing double quote character");
281 0 : *(end++) = '\0';
282 0 : strncpy0 (prev_name + strlen (prev_name), line,
283 0 : sizeof (prev_name) - strlen (prev_name));
284 : break;
285 : }
286 : else
287 : {
288 : ELEKTRA_LOG_DEBUG ("Found name continuation");
289 1 : strncpy (prev_name + strlen (prev_name), line,
290 : sizeof (prev_name) - strlen (prev_name));
291 : }
292 : ELEKTRA_LOG_DEBUG ("New extended name is “%s”", prev_name);
293 : }
294 : name = prev_name;
295 : ELEKTRA_LOG_DEBUG ("Name of key is “%s”", name);
296 : }
297 : }
298 128464 : if (*end != delim)
299 : {
300 : ELEKTRA_LOG_DEBUG ("Search for delimiter in “%s”", end);
301 1 : ptr = lskip (end + 1);
302 1 : end = strchr (ptr, delim);
303 1 : if (end && *end == delim)
304 : {
305 0 : *end = '\0';
306 : ELEKTRA_LOG_DEBUG ("Found delimiter – New name is “%s”", end);
307 : }
308 : else
309 : {
310 : ELEKTRA_LOG_WARNING ("Unable to find delimiter");
311 : error = lineno;
312 : break;
313 : }
314 : }
315 : else
316 : {
317 128463 : *end = '\0';
318 : }
319 128463 : if (name != prev_name && end > line)
320 : {
321 128463 : rstrip (end - 1);
322 : }
323 128463 : value = lskip (end + 1);
324 128463 : end = find_char_or_comment (value, '\0');
325 128463 : if (*end == ';') *end = '\0';
326 128463 : rstrip (value);
327 128463 : if (*value == '"')
328 : {
329 313 : *(value++) = '\0';
330 1252 : while ((*end != '"') && !isprint (*end) && end > value)
331 626 : --end;
332 313 : if (*end == '"') *end = '\0';
333 : }
334 128463 : if (prev_name != name) strncpy0 (prev_name, name, sizeof (prev_name));
335 128463 : if (!config->keyHandler (user, section, name, value, 0) && !error) error = lineno;
336 : }
337 14 : else if (assign == 0)
338 : {
339 : ELEKTRA_LOG_DEBUG ("Found no delimiter");
340 4 : if (*start == '"')
341 : {
342 : ELEKTRA_LOG_DEBUG ("Found initial double quote character");
343 0 : ++start;
344 0 : end = line + (strlen (line) - 1);
345 0 : while (end > start && *end != '"')
346 0 : --end;
347 0 : if (*end == '"' && end != start)
348 : {
349 0 : *end = '\0';
350 0 : if (!config->keyHandler (user, section, start, NULL, 0) && !error) error = lineno;
351 : }
352 : else
353 : {
354 : ELEKTRA_LOG_DEBUG ("Did not find closing double quote character");
355 : strncpy0 (prev_name, start, sizeof (prev_name));
356 0 : while (fgets (line, INI_MAX_LINE, file))
357 : {
358 0 : end = line + (strlen (line) - 1);
359 : ELEKTRA_LOG_DEBUG ("Read continuation line with content “%s”", line);
360 0 : while (end > line && *end != '"')
361 0 : --end;
362 0 : if (*end == '"')
363 : {
364 : ELEKTRA_LOG_DEBUG ("Found closing double quote character");
365 0 : *end = '\0';
366 0 : strncpy0 (prev_name + strlen (prev_name), line,
367 0 : sizeof (prev_name) - strlen (prev_name));
368 : break;
369 : }
370 : else
371 : {
372 : ELEKTRA_LOG_DEBUG ("Found name continuation");
373 0 : strncpy (prev_name + strlen (prev_name), line,
374 : sizeof (prev_name) - strlen (prev_name));
375 : }
376 : ELEKTRA_LOG_DEBUG ("New extended name is “%s”", prev_name);
377 : }
378 0 : name = prev_name;
379 0 : ptr = end + 1;
380 0 : end = strchr (ptr, '=');
381 0 : if (!end) end = strchr (ptr, ':');
382 0 : if (!end)
383 : {
384 0 : if (!config->keyHandler (user, section, name, NULL, 0) && !error) error = lineno;
385 : }
386 : else
387 : {
388 0 : *end = '\0';
389 0 : value = lskip (end + 1);
390 0 : if (*value == '"') end = find_char_or_comment (value, '\0');
391 0 : if (*end == ';') *end = '\0';
392 0 : rstrip (value);
393 0 : if (*value == '"' || *(value + 1) == '"')
394 : {
395 0 : if (*value == '"')
396 0 : *(value++) = '\0';
397 0 : else if (*(value + 1) == '"')
398 : {
399 0 : *(value + 1) = '\0';
400 0 : value += 2;
401 : }
402 0 : while ((*end != '"') && !isprint (*end) && end > value)
403 0 : --end;
404 0 : if (*end == '"') *end = '\0';
405 : }
406 : if (prev_name != name) strncpy0 (prev_name, name, sizeof (prev_name));
407 0 : if (!config->keyHandler (user, section, name, value, 0) && !error) error = lineno;
408 : }
409 : }
410 : }
411 : else
412 : {
413 4 : name = rstrip (start);
414 4 : strncpy0 (prev_name, name, sizeof (prev_name));
415 4 : if (!config->keyHandler (user, section, name, NULL, 0) && !error) error = lineno;
416 : }
417 : }
418 : else
419 : {
420 : ELEKTRA_LOG_DEBUG ("Found multiple delimiters");
421 :
422 10 : end = start + 1;
423 10 : if (*start == '"')
424 : {
425 : /* Quoted Name:
426 : - The name has to end with a double quote
427 : - We do not allow any double quotes inside the name
428 : */
429 : name = start + 1;
430 8 : while (*end && *end != '"')
431 : {
432 6 : end++;
433 : }
434 2 : if (!*end)
435 : {
436 : error = lineno;
437 : break;
438 : }
439 2 : *end = '\0';
440 :
441 2 : value = end + 1;
442 2 : value = lskip (value);
443 2 : if (!*value || *value != delim)
444 : {
445 : error = lineno;
446 : break;
447 : }
448 2 : value++;
449 : }
450 : else
451 : {
452 : /* Unquoted Name:
453 : - The name can not contain a delimiter unless it is the very first character
454 : - Trailing whitespace is removed from the name
455 : */
456 : name = start;
457 48 : while (*end && *end != delim)
458 : {
459 40 : end++;
460 : }
461 8 : if (!end)
462 : {
463 : error = lineno;
464 : break;
465 : }
466 8 : *end = '\0';
467 8 : start = rstrip (start);
468 8 : value = end + 1;
469 : }
470 10 : value = lskip (value);
471 :
472 10 : end = find_char_or_comment (value, '\0');
473 10 : if (*end == ';') *end = '\0';
474 10 : rstrip (value);
475 10 : if (*value == '"' || *(value + 1) == '"')
476 : {
477 10 : if (*value == '"')
478 10 : *(value++) = '\0';
479 0 : else if (*(value + 1) == '"')
480 : {
481 0 : *(value + 1) = '\0';
482 0 : value += 2;
483 : }
484 30 : while ((*end != '"') && !isprint (*end) && end > value)
485 20 : --end;
486 10 : if (*end == '"') *end = '\0';
487 : }
488 10 : strncpy0 (prev_name, name, sizeof (prev_name));
489 :
490 10 : if (!config->keyHandler (user, section, name, value, 0) && !error) error = lineno;
491 : }
492 : }
493 :
494 : #if INI_STOP_ON_FIRST_ERROR
495 : if (error) break;
496 : #endif
497 : }
498 :
499 16999 : free (line);
500 16999 : return error;
501 : }
502 :
503 : /* See documentation in header file. */
504 0 : int ini_parse (const char * filename, const struct IniConfig * config, void * user)
505 : {
506 : FILE * file;
507 : int error;
508 :
509 0 : file = fopen (filename, "r");
510 0 : if (!file) return -1;
511 0 : error = ini_parse_file (file, config, user);
512 0 : fclose (file);
513 0 : return error;
514 : }
|