src/third-party/inih/ini.c (view raw)
1/* inih -- simple .INI file parser
2
3inih is released under the New BSD license (see LICENSE.txt). Go to the project
4home page for more info:
5
6http://code.google.com/p/inih/
7
8*/
9
10#include <stdio.h>
11#ifndef PSP2
12#include <ctype.h>
13#endif
14#include <string.h>
15
16#include "ini.h"
17
18#if !INI_USE_STACK
19#include <stdlib.h>
20#endif
21
22#define MAX_SECTION 50
23#define MAX_NAME 50
24
25/* Strip whitespace chars off end of given string, in place. Return s. */
26static char* rstrip(char* s)
27{
28 char* p = s + strlen(s);
29 while (p > s && isspace((unsigned char)(*--p)))
30 *p = '\0';
31 return s;
32}
33
34/* Return pointer to first non-whitespace char in given string. */
35static char* lskip(const char* s)
36{
37 while (*s && isspace((unsigned char)(*s)))
38 s++;
39 return (char*)s;
40}
41
42/* Return pointer to first char c or ';' comment in given string, or pointer to
43 null at end of string if neither found. ';' must be prefixed by a whitespace
44 character to register as a comment. */
45static char* find_char_or_comment(const char* s, char c)
46{
47 int was_whitespace = 0;
48 while (*s && *s != c && !(was_whitespace && *s == ';')) {
49 was_whitespace = isspace((unsigned char)(*s));
50 s++;
51 }
52 return (char*)s;
53}
54
55/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
56static char* strncpy0(char* dest, const char* src, size_t size)
57{
58 strncpy(dest, src, size);
59 dest[size - 1] = '\0';
60 return dest;
61}
62
63/* See documentation in header file. */
64int ini_parse_file(FILE* file,
65 int (*handler)(void*, const char*, const char*,
66 const char*),
67 void* user)
68{
69 /* Uses a fair bit of stack (use heap instead if you need to) */
70#if INI_USE_STACK
71 char line[INI_MAX_LINE];
72#else
73 char* line;
74#endif
75 char section[MAX_SECTION] = "";
76 char prev_name[MAX_NAME] = "";
77
78 char* start;
79 char* end;
80 char* name;
81 char* value;
82 int lineno = 0;
83 int error = 0;
84
85#if !INI_USE_STACK
86 line = (char*)malloc(INI_MAX_LINE);
87 if (!line) {
88 return -2;
89 }
90#endif
91
92 /* Scan through file line by line */
93 while (fgets(line, INI_MAX_LINE, file) != NULL) {
94 lineno++;
95
96 start = line;
97#if INI_ALLOW_BOM
98 if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
99 (unsigned char)start[1] == 0xBB &&
100 (unsigned char)start[2] == 0xBF) {
101 start += 3;
102 }
103#endif
104 start = lskip(rstrip(start));
105
106 if (*start == ';' || *start == '#') {
107 /* Per Python ConfigParser, allow '#' comments at start of line */
108 }
109#if INI_ALLOW_MULTILINE
110 else if (*prev_name && *start && start > line) {
111 /* Non-black line with leading whitespace, treat as continuation
112 of previous name's value (as per Python ConfigParser). */
113 if (!handler(user, section, prev_name, start) && !error)
114 error = lineno;
115 }
116#endif
117 else if (*start == '[') {
118 /* A "[section]" line */
119 end = find_char_or_comment(start + 1, ']');
120 if (*end == ']') {
121 *end = '\0';
122 strncpy0(section, start + 1, sizeof(section));
123 *prev_name = '\0';
124 }
125 else if (!error) {
126 /* No ']' found on section line */
127 error = lineno;
128 }
129 }
130 else if (*start && *start != ';') {
131 /* Not a comment, must be a name[=:]value pair */
132 end = find_char_or_comment(start, '=');
133 if (*end != '=') {
134 end = find_char_or_comment(start, ':');
135 }
136 if (*end == '=' || *end == ':') {
137 *end = '\0';
138 name = rstrip(start);
139 value = lskip(end + 1);
140 end = find_char_or_comment(value, '\0');
141 if (*end == ';')
142 *end = '\0';
143 rstrip(value);
144
145 /* Valid name[=:]value pair found, call handler */
146 strncpy0(prev_name, name, sizeof(prev_name));
147 if (!handler(user, section, name, value) && !error)
148 error = lineno;
149 }
150 else if (!error) {
151 /* No '=' or ':' found on name[=:]value line */
152 error = lineno;
153 }
154 }
155
156#if INI_STOP_ON_FIRST_ERROR
157 if (error)
158 break;
159#endif
160 }
161
162#if !INI_USE_STACK
163 free(line);
164#endif
165
166 return error;
167}
168
169/* See documentation in header file. */
170int ini_parse(const char* filename,
171 int (*handler)(void*, const char*, const char*, const char*),
172 void* user)
173{
174 FILE* file;
175 int error;
176
177 file = fopen(filename, "r");
178 if (!file)
179 return -1;
180 error = ini_parse_file(file, handler, user);
181 fclose(file);
182 return error;
183}