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