blob: de36d27012daf0a8a7a1d7554e63c523b704f8c5 [file] [log] [blame]
Tom Rini83d290c2018-05-06 17:58:06 -04001// SPDX-License-Identifier: BSD-3-Clause
Joe Hershbergerc167cc02012-10-03 11:15:51 +00002/*
3 * inih -- simple .INI file parser
4 *
Joe Hershberger961c4372012-10-04 09:54:07 +00005 * Copyright (c) 2009, Brush Technology
6 * Copyright (c) 2012:
7 * Joe Hershberger, National Instruments, joe.hershberger@ni.com
8 * All rights reserved.
Joe Hershbergerc167cc02012-10-03 11:15:51 +00009 *
Joe Hershberger961c4372012-10-04 09:54:07 +000010 * Go to the project home page for more info:
Joe Hershbergerc167cc02012-10-03 11:15:51 +000011 * http://code.google.com/p/inih/
12 */
13
14#include <common.h>
15#include <command.h>
Simon Glass9fb625c2019-08-01 09:46:51 -060016#include <env.h>
Joe Hershbergerc167cc02012-10-03 11:15:51 +000017#include <linux/ctype.h>
18#include <linux/string.h>
huijie.huange2a6e982024-07-02 14:25:53 +080019#include <amlogic/ini.h>
Joe Hershbergerc167cc02012-10-03 11:15:51 +000020
21#ifdef CONFIG_INI_MAX_LINE
22#define MAX_LINE CONFIG_INI_MAX_LINE
23#else
24#define MAX_LINE 200
25#endif
26
27#ifdef CONFIG_INI_MAX_SECTION
28#define MAX_SECTION CONFIG_INI_MAX_SECTION
29#else
30#define MAX_SECTION 50
31#endif
32
33#ifdef CONFIG_INI_MAX_NAME
34#define MAX_NAME CONFIG_INI_MAX_NAME
35#else
36#define MAX_NAME 50
37#endif
38
39/* Strip whitespace chars off end of given string, in place. Return s. */
40static char *rstrip(char *s)
41{
42 char *p = s + strlen(s);
43
44 while (p > s && isspace(*--p))
45 *p = '\0';
46 return s;
47}
48
49/* Return pointer to first non-whitespace char in given string. */
50static char *lskip(const char *s)
51{
52 while (*s && isspace(*s))
53 s++;
54 return (char *)s;
55}
56
57/* Return pointer to first char c or ';' comment in given string, or pointer to
58 null at end of string if neither found. ';' must be prefixed by a whitespace
59 character to register as a comment. */
60static char *find_char_or_comment(const char *s, char c)
61{
62 int was_whitespace = 0;
63
64 while (*s && *s != c && !(was_whitespace && *s == ';')) {
65 was_whitespace = isspace(*s);
66 s++;
67 }
68 return (char *)s;
69}
70
71/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
72static char *strncpy0(char *dest, const char *src, size_t size)
73{
74 strncpy(dest, src, size);
75 dest[size - 1] = '\0';
76 return dest;
77}
78
79/* Emulate the behavior of fgets but on memory */
80static char *memgets(char *str, int num, char **mem, size_t *memsize)
81{
82 char *end;
83 int len;
84 int newline = 1;
85
86 end = memchr(*mem, '\n', *memsize);
87 if (end == NULL) {
88 if (*memsize == 0)
89 return NULL;
90 end = *mem + *memsize;
91 newline = 0;
92 }
Bo Lv72d0e902023-01-02 14:27:34 +000093#ifdef CONFIG_AMLOGIC_MODIFY
94 len = min((int)(end - *mem) + newline, num);
95#else
Joe Hershbergerc167cc02012-10-03 11:15:51 +000096 len = min((end - *mem) + newline, num);
Bo Lv72d0e902023-01-02 14:27:34 +000097#endif
Joe Hershbergerc167cc02012-10-03 11:15:51 +000098 memcpy(str, *mem, len);
99 if (len < num)
100 str[len] = '\0';
101
102 /* prepare the mem vars for the next call */
103 *memsize -= (end - *mem) + newline;
104 *mem += (end - *mem) + newline;
105
106 return str;
107}
108
109/* Parse given INI-style file. May have [section]s, name=value pairs
110 (whitespace stripped), and comments starting with ';' (semicolon). Section
111 is "" if name=value pair parsed before any section heading. name:value
112 pairs are also supported as a concession to Python's ConfigParser.
113
114 For each name=value pair parsed, call handler function with given user
115 pointer as well as section, name, and value (data only valid for duration
116 of handler call). Handler should return nonzero on success, zero on error.
117
118 Returns 0 on success, line number of first error on parse error (doesn't
119 stop on first error).
120*/
121static int ini_parse(char *filestart, size_t filelen,
122 int (*handler)(void *, char *, char *, char *), void *user)
123{
124 /* Uses a fair bit of stack (use heap instead if you need to) */
125 char line[MAX_LINE];
126 char section[MAX_SECTION] = "";
127 char prev_name[MAX_NAME] = "";
128
129 char *curmem = filestart;
130 char *start;
131 char *end;
132 char *name;
133 char *value;
134 size_t memleft = filelen;
135 int lineno = 0;
136 int error = 0;
137
138 /* Scan through file line by line */
139 while (memgets(line, sizeof(line), &curmem, &memleft) != NULL) {
140 lineno++;
141 start = lskip(rstrip(line));
142
143 if (*start == ';' || *start == '#') {
144 /*
145 * Per Python ConfigParser, allow '#' comments at start
146 * of line
147 */
148 }
149#if CONFIG_INI_ALLOW_MULTILINE
150 else if (*prev_name && *start && start > line) {
151 /*
152 * Non-blank line with leading whitespace, treat as
153 * continuation of previous name's value (as per Python
154 * ConfigParser).
155 */
156 if (!handler(user, section, prev_name, start) && !error)
157 error = lineno;
158 }
159#endif
160 else if (*start == '[') {
161 /* A "[section]" line */
162 end = find_char_or_comment(start + 1, ']');
163 if (*end == ']') {
164 *end = '\0';
165 strncpy0(section, start + 1, sizeof(section));
166 *prev_name = '\0';
167 } else if (!error) {
168 /* No ']' found on section line */
169 error = lineno;
170 }
171 } else if (*start && *start != ';') {
172 /* Not a comment, must be a name[=:]value pair */
173 end = find_char_or_comment(start, '=');
174 if (*end != '=')
175 end = find_char_or_comment(start, ':');
176 if (*end == '=' || *end == ':') {
177 *end = '\0';
178 name = rstrip(start);
179 value = lskip(end + 1);
180 end = find_char_or_comment(value, '\0');
181 if (*end == ';')
182 *end = '\0';
183 rstrip(value);
184 /* Strip double-quotes */
185 if (value[0] == '"' &&
186 value[strlen(value)-1] == '"') {
187 value[strlen(value)-1] = '\0';
188 value += 1;
189 }
190
191 /*
192 * Valid name[=:]value pair found, call handler
193 */
194 strncpy0(prev_name, name, sizeof(prev_name));
195 if (!handler(user, section, name, value) &&
196 !error)
197 error = lineno;
198 } else if (!error)
199 /* No '=' or ':' found on name[=:]value line */
200 error = lineno;
201 }
202 }
203
204 return error;
205}
206
207static int ini_handler(void *user, char *section, char *name, char *value)
208{
209 char *requested_section = (char *)user;
210#ifdef CONFIG_INI_CASE_INSENSITIVE
211 int i;
212
213 for (i = 0; i < strlen(requested_section); i++)
214 requested_section[i] = tolower(requested_section[i]);
215 for (i = 0; i < strlen(section); i++)
216 section[i] = tolower(section[i]);
217#endif
218
219 if (!strcmp(section, requested_section)) {
220#ifdef CONFIG_INI_CASE_INSENSITIVE
221 for (i = 0; i < strlen(name); i++)
222 name[i] = tolower(name[i]);
223 for (i = 0; i < strlen(value); i++)
224 value[i] = tolower(value[i]);
225#endif
Simon Glass382bee52017-08-03 12:22:09 -0600226 env_set(name, value);
Joe Hershbergerc167cc02012-10-03 11:15:51 +0000227 printf("ini: Imported %s as %s\n", name, value);
228 }
229
230 /* success */
231 return 1;
232}
233
Simon Glass09140112020-05-10 11:40:03 -0600234static int do_ini(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
Joe Hershbergerc167cc02012-10-03 11:15:51 +0000235{
236 const char *section;
237 char *file_address;
238 size_t file_size;
239
240 if (argc == 1)
241 return CMD_RET_USAGE;
242
243 section = argv[1];
Simon Glass7e5f4602021-07-24 09:03:29 -0600244 file_address = (char *)hextoul(argc < 3 ? env_get("loadaddr") : argv[2],
245 NULL);
246 file_size = (size_t)hextoul(argc < 4 ? env_get("filesize") : argv[3],
247 NULL);
Joe Hershbergerc167cc02012-10-03 11:15:51 +0000248
249 return ini_parse(file_address, file_size, ini_handler, (void *)section);
250}
251
252U_BOOT_CMD(
253 ini, 4, 0, do_ini,
254 "parse an ini file in memory and merge the specified section into the env",
255 "section [[file-address] file-size]"
256);
huijie.huange2a6e982024-07-02 14:25:53 +0800257
Dongjin Kim3b98faf2025-02-24 18:06:09 +0900258#ifndef CONFIG_S7D_ODROIDC5
huijie.huange2a6e982024-07-02 14:25:53 +0800259static int do_ini_model_list(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
260{
261 int ret;
262
Evoke Zhang3b89f5e2024-10-25 18:45:35 +0800263 if (argc == 1) {
264 ret = handle_model_list();
265 return ret;
266 }
267 if (strcmp(argv[1], "lcd") == 0 || strcmp(argv[1], "panel") == 0) {
268 handle_model_list_panel_key();
269 return 0;
270 }
huijie.huange2a6e982024-07-02 14:25:53 +0800271
Evoke Zhang3b89f5e2024-10-25 18:45:35 +0800272 return CMD_RET_USAGE;
huijie.huange2a6e982024-07-02 14:25:53 +0800273}
274
275U_BOOT_CMD(
276 model_list, 4, 0, do_ini_model_list,
277 "list ini model name",
278 " "
279);
280
281static int do_ini_model(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
282{
283 int ret;
284
285 if (argc > 1)
286 return CMD_RET_USAGE;
287
288 ret = handle_model_sum();
289 return ret;
290}
291
292U_BOOT_CMD(
293 ini_model, 4, 0, do_ini_model,
294 "parse ini file by env model_name",
295 " "
296);
Dongjin Kim3b98faf2025-02-24 18:06:09 +0900297#endif