blob: 20c322974d57e3dabdb3944bf2032707ae110894 [file] [log] [blame]
Lei Qian7bf98232018-09-20 17:56:38 +08001/*
2 * Copyright (C) 2017 Amlogic Corporation.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define _CRT_SECURE_NO_WARNINGS
18//#pragma warning (disable: 4127)
19
20#include <stdio.h>
21#include <ctype.h>
22#include <string.h>
23
24#include "ini.h"
25
26#if !INI_USE_STACK
27#include <stdlib.h>
28#endif
29
30#define MAX_SECTION 50
31#define MAX_NAME 50
32
33/* Strip whitespace chars off end of given string, in place. Return s. */
34static char* rstrip(char* s) {
35 char* p = s + strlen(s);
36 while (p > s && isspace((unsigned char) (*--p)))
37 *p = '\0';
38 return s;
39}
40
41/* Return pointer to first non-whitespace char in given string. */
42static char* lskip(const char* s) {
43 while (*s && isspace((unsigned char) (*s)))
44 s++;
45 return (char*) s;
46}
47
48/* Return pointer to first char c or ';' comment in given string, or pointer to
49 null at end of string if neither found. ';' must be prefixed by a whitespace
50 character to register as a comment. */
51static char* find_char_or_comment(const char* s, char c) {
52 int was_whitespace = 0;
53 while (*s && *s != c && !(was_whitespace && *s == ';')) {
54 was_whitespace = isspace((unsigned char) (*s));
55 s++;
56 }
57 return (char*) s;
58}
59
60/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
61static char* strncpy0(char* dest, const char* src, size_t size) {
62 strncpy(dest, src, size);
63 dest[size - 1] = '\0';
64 return dest;
65}
66
67/* See documentation in header file. */
68int ini_parse_file(FILE* file,
69 int (*handler)(void*, const char*, const char*, const char*),
70 void* user) {
71 /* Uses a fair bit of stack (use heap instead if you need to) */
72#if INI_USE_STACK
73 char line[INI_MAX_LINE];
74#else
75 char* line;
76#endif
77 char section[MAX_SECTION] = "";
78 char prev_name[MAX_NAME] = "";
79
80 char* start;
81 char* end;
82 char* name;
83 char* value;
84 int lineno = 0;
85 int error = 0;
86
87#if !INI_USE_STACK
88 line = (char*) malloc(INI_MAX_LINE);
89 if (!line) {
90 return -2;
91 }
92#endif
93
94 /* Scan through file line by line */
95 while (fgets(line, INI_MAX_LINE, file) != NULL) {
96 lineno++;
97
98 start = line;
99#if INI_ALLOW_BOM
100 if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
101 (unsigned char)start[1] == 0xBB &&
102 (unsigned char)start[2] == 0xBF) {
103 start += 3;
104 }
105#endif
106 start = lskip(rstrip(start));
107
108 if (*start == ';' || *start == '#') {
109 /* Per Python ConfigParser, allow '#' comments at start of line */
110 }
111 else if (*start == '[') {
112 /* A "[section]" line */
113 end = find_char_or_comment(start + 1, ']');
114 if (*end == ']') {
115 *end = '\0';
116 strncpy0(section, start + 1, sizeof(section));
117 *prev_name = '\0';
118 } else if (!error) {
119 /* No ']' found on section line */
120 error = lineno;
121 }
122 }
123#if INI_ALLOW_MULTILINE
124 else if (*prev_name && *start && (start > line || strstr(start, "=") == NULL)) {
125 /* Non-black line with leading whitespace, treat as continuation
126 of previous name's value (as per Python ConfigParser). */
127 if (!handler(user, section, prev_name, start) && !error)
128 error = lineno;
129 }
130#endif
131 else if (*start && *start != ';') {
132 /* Not a comment, must be a name[=:]value pair */
133 end = find_char_or_comment(start, '=');
134 if (*end != '=') {
135 end = find_char_or_comment(start, ':');
136 }
137 if (*end == '=' || *end == ':') {
138 *end = '\0';
139 name = rstrip(start);
140 value = lskip(end + 1);
141 end = find_char_or_comment(value, '\0');
142 if (*end == ';')
143 *end = '\0';
144 rstrip(value);
145
146 /* Valid name[=:]value pair found, call handler */
147 strncpy0(prev_name, name, sizeof(prev_name));
148 if (!handler(user, section, name, value) && !error)
149 error = lineno;
150 } else if (!error) {
151 /* No '=' or ':' found on name[=:]value line */
152 error = lineno;
153 }
154 }
155 }
156
157#if !INI_USE_STACK
158 free(line);
159#endif
160
161 return error;
162}
163
164int ini_parse_mem(const char* buf,
165 int (*handler)(void* user, const char* section, const char* name,
166 const char* value), void* user) {
167 char* bufptr = (char*) buf;
168
169 /* Uses a fair bit of stack (use heap instead if you need to) */
170#if INI_USE_STACK
171 char line[INI_MAX_LINE];
172#else
173 char* line;
174#endif
175 char section[MAX_SECTION] = "";
176 char prev_name[MAX_NAME] = "";
177
178 char* start;
179 char* end;
180 char* name;
181 char* value;
182 int lineno = 0;
183 int error = 0;
184
185#if !INI_USE_STACK
186 line = (char*) malloc(INI_MAX_LINE);
187 if (!line) {
188 return -2;
189 }
190#endif
191
192 while (1) {
193 int ncount = 0;
194 while (*bufptr != '\0') {
195 if (*bufptr == '\r' || *bufptr == '\n')
196 break;
197
198 line[ncount] = *bufptr++;
199 ncount++;
200 }
201 while (*bufptr == '\r' || *bufptr == '\n')
202 bufptr++;
203 line[ncount] = 0;
204
205 if (ncount == 0)
206 break;
207
208 /* Scan through file line by line */
209 //while (fgets(line, INI_MAX_LINE, file) != NULL) {
210 lineno++;
211
212 start = line;
213#if INI_ALLOW_BOM
214 if (lineno == 1 && (unsigned char)start[0] == 0xEF &&
215 (unsigned char)start[1] == 0xBB &&
216 (unsigned char)start[2] == 0xBF) {
217 start += 3;
218 }
219#endif
220 start = lskip(rstrip(start));
221
222 if (*start == ';' || *start == '#') {
223 /* Per Python ConfigParser, allow '#' comments at start of line */
224 }
225#if INI_ALLOW_MULTILINE
226 else if (*prev_name && *start && start > line) {
227 /* Non-black line with leading whitespace, treat as continuation
228 of previous name's value (as per Python ConfigParser). */
229 if (!handler(user, section, prev_name, start) && !error)
230 error = lineno;
231 }
232#endif
233 else if (*start == '[') {
234 /* A "[section]" line */
235 end = find_char_or_comment(start + 1, ']');
236 if (*end == ']') {
237 *end = '\0';
238 strncpy0(section, start + 1, sizeof(section));
239 *prev_name = '\0';
240 } else if (!error) {
241 /* No ']' found on section line */
242 error = lineno;
243 }
244 } else if (*start && *start != ';') {
245 /* Not a comment, must be a name[=:]value pair */
246 end = find_char_or_comment(start, '=');
247 if (*end != '=') {
248 end = find_char_or_comment(start, ':');
249 }
250 if (*end == '=' || *end == ':') {
251 *end = '\0';
252 name = rstrip(start);
253 value = lskip(end + 1);
254 end = find_char_or_comment(value, '\0');
255 if (*end == ';')
256 *end = '\0';
257 rstrip(value);
258
259 /* Valid name[=:]value pair found, call handler */
260 strncpy0(prev_name, name, sizeof(prev_name));
261 if (!handler(user, section, name, value) && !error)
262 error = lineno;
263 } else if (!error) {
264 /* No '=' or ':' found on name[=:]value line */
265 error = lineno;
266 }
267 }
268 }
269
270#if !INI_USE_STACK
271 free(line);
272#endif
273
274 return error;
275}
276
277/* See documentation in header file. */
278int ini_parse(const char* filename,
279 int (*handler)(void*, const char*, const char*, const char*),
280 void* user) {
281 FILE* file;
282 int error;
283
284 file = fopen(filename, "r");
285 if (!file)
286 return -1;
287 error = ini_parse_file(file, handler, user);
288 fclose(file);
289 return error;
290}