xref: /rk3399_rockchip-uboot/cmd/ini.c (revision 2e192b245ed36a63bab0ef576999a95e23f60ecd)
1*2e192b24SSimon Glass /*
2*2e192b24SSimon Glass  * inih -- simple .INI file parser
3*2e192b24SSimon Glass  *
4*2e192b24SSimon Glass  * Copyright (c) 2009, Brush Technology
5*2e192b24SSimon Glass  * Copyright (c) 2012:
6*2e192b24SSimon Glass  *              Joe Hershberger, National Instruments, joe.hershberger@ni.com
7*2e192b24SSimon Glass  * All rights reserved.
8*2e192b24SSimon Glass  *
9*2e192b24SSimon Glass  * SPDX-License-Identifier:	BSD-3-Clause
10*2e192b24SSimon Glass  *
11*2e192b24SSimon Glass  * Go to the project home page for more info:
12*2e192b24SSimon Glass  * http://code.google.com/p/inih/
13*2e192b24SSimon Glass  */
14*2e192b24SSimon Glass 
15*2e192b24SSimon Glass #include <common.h>
16*2e192b24SSimon Glass #include <command.h>
17*2e192b24SSimon Glass #include <environment.h>
18*2e192b24SSimon Glass #include <linux/ctype.h>
19*2e192b24SSimon Glass #include <linux/string.h>
20*2e192b24SSimon Glass 
21*2e192b24SSimon Glass #ifdef CONFIG_INI_MAX_LINE
22*2e192b24SSimon Glass #define MAX_LINE CONFIG_INI_MAX_LINE
23*2e192b24SSimon Glass #else
24*2e192b24SSimon Glass #define MAX_LINE 200
25*2e192b24SSimon Glass #endif
26*2e192b24SSimon Glass 
27*2e192b24SSimon Glass #ifdef CONFIG_INI_MAX_SECTION
28*2e192b24SSimon Glass #define MAX_SECTION CONFIG_INI_MAX_SECTION
29*2e192b24SSimon Glass #else
30*2e192b24SSimon Glass #define MAX_SECTION 50
31*2e192b24SSimon Glass #endif
32*2e192b24SSimon Glass 
33*2e192b24SSimon Glass #ifdef CONFIG_INI_MAX_NAME
34*2e192b24SSimon Glass #define MAX_NAME CONFIG_INI_MAX_NAME
35*2e192b24SSimon Glass #else
36*2e192b24SSimon Glass #define MAX_NAME 50
37*2e192b24SSimon Glass #endif
38*2e192b24SSimon Glass 
39*2e192b24SSimon Glass /* Strip whitespace chars off end of given string, in place. Return s. */
40*2e192b24SSimon Glass static char *rstrip(char *s)
41*2e192b24SSimon Glass {
42*2e192b24SSimon Glass 	char *p = s + strlen(s);
43*2e192b24SSimon Glass 
44*2e192b24SSimon Glass 	while (p > s && isspace(*--p))
45*2e192b24SSimon Glass 		*p = '\0';
46*2e192b24SSimon Glass 	return s;
47*2e192b24SSimon Glass }
48*2e192b24SSimon Glass 
49*2e192b24SSimon Glass /* Return pointer to first non-whitespace char in given string. */
50*2e192b24SSimon Glass static char *lskip(const char *s)
51*2e192b24SSimon Glass {
52*2e192b24SSimon Glass 	while (*s && isspace(*s))
53*2e192b24SSimon Glass 		s++;
54*2e192b24SSimon Glass 	return (char *)s;
55*2e192b24SSimon Glass }
56*2e192b24SSimon Glass 
57*2e192b24SSimon Glass /* Return pointer to first char c or ';' comment in given string, or pointer to
58*2e192b24SSimon Glass    null at end of string if neither found. ';' must be prefixed by a whitespace
59*2e192b24SSimon Glass    character to register as a comment. */
60*2e192b24SSimon Glass static char *find_char_or_comment(const char *s, char c)
61*2e192b24SSimon Glass {
62*2e192b24SSimon Glass 	int was_whitespace = 0;
63*2e192b24SSimon Glass 
64*2e192b24SSimon Glass 	while (*s && *s != c && !(was_whitespace && *s == ';')) {
65*2e192b24SSimon Glass 		was_whitespace = isspace(*s);
66*2e192b24SSimon Glass 		s++;
67*2e192b24SSimon Glass 	}
68*2e192b24SSimon Glass 	return (char *)s;
69*2e192b24SSimon Glass }
70*2e192b24SSimon Glass 
71*2e192b24SSimon Glass /* Version of strncpy that ensures dest (size bytes) is null-terminated. */
72*2e192b24SSimon Glass static char *strncpy0(char *dest, const char *src, size_t size)
73*2e192b24SSimon Glass {
74*2e192b24SSimon Glass 	strncpy(dest, src, size);
75*2e192b24SSimon Glass 	dest[size - 1] = '\0';
76*2e192b24SSimon Glass 	return dest;
77*2e192b24SSimon Glass }
78*2e192b24SSimon Glass 
79*2e192b24SSimon Glass /* Emulate the behavior of fgets but on memory */
80*2e192b24SSimon Glass static char *memgets(char *str, int num, char **mem, size_t *memsize)
81*2e192b24SSimon Glass {
82*2e192b24SSimon Glass 	char *end;
83*2e192b24SSimon Glass 	int len;
84*2e192b24SSimon Glass 	int newline = 1;
85*2e192b24SSimon Glass 
86*2e192b24SSimon Glass 	end = memchr(*mem, '\n', *memsize);
87*2e192b24SSimon Glass 	if (end == NULL) {
88*2e192b24SSimon Glass 		if (*memsize == 0)
89*2e192b24SSimon Glass 			return NULL;
90*2e192b24SSimon Glass 		end = *mem + *memsize;
91*2e192b24SSimon Glass 		newline = 0;
92*2e192b24SSimon Glass 	}
93*2e192b24SSimon Glass 	len = min((end - *mem) + newline, num);
94*2e192b24SSimon Glass 	memcpy(str, *mem, len);
95*2e192b24SSimon Glass 	if (len < num)
96*2e192b24SSimon Glass 		str[len] = '\0';
97*2e192b24SSimon Glass 
98*2e192b24SSimon Glass 	/* prepare the mem vars for the next call */
99*2e192b24SSimon Glass 	*memsize -= (end - *mem) + newline;
100*2e192b24SSimon Glass 	*mem += (end - *mem) + newline;
101*2e192b24SSimon Glass 
102*2e192b24SSimon Glass 	return str;
103*2e192b24SSimon Glass }
104*2e192b24SSimon Glass 
105*2e192b24SSimon Glass /* Parse given INI-style file. May have [section]s, name=value pairs
106*2e192b24SSimon Glass    (whitespace stripped), and comments starting with ';' (semicolon). Section
107*2e192b24SSimon Glass    is "" if name=value pair parsed before any section heading. name:value
108*2e192b24SSimon Glass    pairs are also supported as a concession to Python's ConfigParser.
109*2e192b24SSimon Glass 
110*2e192b24SSimon Glass    For each name=value pair parsed, call handler function with given user
111*2e192b24SSimon Glass    pointer as well as section, name, and value (data only valid for duration
112*2e192b24SSimon Glass    of handler call). Handler should return nonzero on success, zero on error.
113*2e192b24SSimon Glass 
114*2e192b24SSimon Glass    Returns 0 on success, line number of first error on parse error (doesn't
115*2e192b24SSimon Glass    stop on first error).
116*2e192b24SSimon Glass */
117*2e192b24SSimon Glass static int ini_parse(char *filestart, size_t filelen,
118*2e192b24SSimon Glass 	int (*handler)(void *, char *, char *, char *),	void *user)
119*2e192b24SSimon Glass {
120*2e192b24SSimon Glass 	/* Uses a fair bit of stack (use heap instead if you need to) */
121*2e192b24SSimon Glass 	char line[MAX_LINE];
122*2e192b24SSimon Glass 	char section[MAX_SECTION] = "";
123*2e192b24SSimon Glass 	char prev_name[MAX_NAME] = "";
124*2e192b24SSimon Glass 
125*2e192b24SSimon Glass 	char *curmem = filestart;
126*2e192b24SSimon Glass 	char *start;
127*2e192b24SSimon Glass 	char *end;
128*2e192b24SSimon Glass 	char *name;
129*2e192b24SSimon Glass 	char *value;
130*2e192b24SSimon Glass 	size_t memleft = filelen;
131*2e192b24SSimon Glass 	int lineno = 0;
132*2e192b24SSimon Glass 	int error = 0;
133*2e192b24SSimon Glass 
134*2e192b24SSimon Glass 	/* Scan through file line by line */
135*2e192b24SSimon Glass 	while (memgets(line, sizeof(line), &curmem, &memleft) != NULL) {
136*2e192b24SSimon Glass 		lineno++;
137*2e192b24SSimon Glass 		start = lskip(rstrip(line));
138*2e192b24SSimon Glass 
139*2e192b24SSimon Glass 		if (*start == ';' || *start == '#') {
140*2e192b24SSimon Glass 			/*
141*2e192b24SSimon Glass 			 * Per Python ConfigParser, allow '#' comments at start
142*2e192b24SSimon Glass 			 * of line
143*2e192b24SSimon Glass 			 */
144*2e192b24SSimon Glass 		}
145*2e192b24SSimon Glass #if CONFIG_INI_ALLOW_MULTILINE
146*2e192b24SSimon Glass 		else if (*prev_name && *start && start > line) {
147*2e192b24SSimon Glass 			/*
148*2e192b24SSimon Glass 			 * Non-blank line with leading whitespace, treat as
149*2e192b24SSimon Glass 			 * continuation of previous name's value (as per Python
150*2e192b24SSimon Glass 			 * ConfigParser).
151*2e192b24SSimon Glass 			 */
152*2e192b24SSimon Glass 			if (!handler(user, section, prev_name, start) && !error)
153*2e192b24SSimon Glass 				error = lineno;
154*2e192b24SSimon Glass 		}
155*2e192b24SSimon Glass #endif
156*2e192b24SSimon Glass 		else if (*start == '[') {
157*2e192b24SSimon Glass 			/* A "[section]" line */
158*2e192b24SSimon Glass 			end = find_char_or_comment(start + 1, ']');
159*2e192b24SSimon Glass 			if (*end == ']') {
160*2e192b24SSimon Glass 				*end = '\0';
161*2e192b24SSimon Glass 				strncpy0(section, start + 1, sizeof(section));
162*2e192b24SSimon Glass 				*prev_name = '\0';
163*2e192b24SSimon Glass 			} else if (!error) {
164*2e192b24SSimon Glass 				/* No ']' found on section line */
165*2e192b24SSimon Glass 				error = lineno;
166*2e192b24SSimon Glass 			}
167*2e192b24SSimon Glass 		} else if (*start && *start != ';') {
168*2e192b24SSimon Glass 			/* Not a comment, must be a name[=:]value pair */
169*2e192b24SSimon Glass 			end = find_char_or_comment(start, '=');
170*2e192b24SSimon Glass 			if (*end != '=')
171*2e192b24SSimon Glass 				end = find_char_or_comment(start, ':');
172*2e192b24SSimon Glass 			if (*end == '=' || *end == ':') {
173*2e192b24SSimon Glass 				*end = '\0';
174*2e192b24SSimon Glass 				name = rstrip(start);
175*2e192b24SSimon Glass 				value = lskip(end + 1);
176*2e192b24SSimon Glass 				end = find_char_or_comment(value, '\0');
177*2e192b24SSimon Glass 				if (*end == ';')
178*2e192b24SSimon Glass 					*end = '\0';
179*2e192b24SSimon Glass 				rstrip(value);
180*2e192b24SSimon Glass 				/* Strip double-quotes */
181*2e192b24SSimon Glass 				if (value[0] == '"' &&
182*2e192b24SSimon Glass 				    value[strlen(value)-1] == '"') {
183*2e192b24SSimon Glass 					value[strlen(value)-1] = '\0';
184*2e192b24SSimon Glass 					value += 1;
185*2e192b24SSimon Glass 				}
186*2e192b24SSimon Glass 
187*2e192b24SSimon Glass 				/*
188*2e192b24SSimon Glass 				 * Valid name[=:]value pair found, call handler
189*2e192b24SSimon Glass 				 */
190*2e192b24SSimon Glass 				strncpy0(prev_name, name, sizeof(prev_name));
191*2e192b24SSimon Glass 				if (!handler(user, section, name, value) &&
192*2e192b24SSimon Glass 				     !error)
193*2e192b24SSimon Glass 					error = lineno;
194*2e192b24SSimon Glass 			} else if (!error)
195*2e192b24SSimon Glass 				/* No '=' or ':' found on name[=:]value line */
196*2e192b24SSimon Glass 				error = lineno;
197*2e192b24SSimon Glass 		}
198*2e192b24SSimon Glass 	}
199*2e192b24SSimon Glass 
200*2e192b24SSimon Glass 	return error;
201*2e192b24SSimon Glass }
202*2e192b24SSimon Glass 
203*2e192b24SSimon Glass static int ini_handler(void *user, char *section, char *name, char *value)
204*2e192b24SSimon Glass {
205*2e192b24SSimon Glass 	char *requested_section = (char *)user;
206*2e192b24SSimon Glass #ifdef CONFIG_INI_CASE_INSENSITIVE
207*2e192b24SSimon Glass 	int i;
208*2e192b24SSimon Glass 
209*2e192b24SSimon Glass 	for (i = 0; i < strlen(requested_section); i++)
210*2e192b24SSimon Glass 		requested_section[i] = tolower(requested_section[i]);
211*2e192b24SSimon Glass 	for (i = 0; i < strlen(section); i++)
212*2e192b24SSimon Glass 		section[i] = tolower(section[i]);
213*2e192b24SSimon Glass #endif
214*2e192b24SSimon Glass 
215*2e192b24SSimon Glass 	if (!strcmp(section, requested_section)) {
216*2e192b24SSimon Glass #ifdef CONFIG_INI_CASE_INSENSITIVE
217*2e192b24SSimon Glass 		for (i = 0; i < strlen(name); i++)
218*2e192b24SSimon Glass 			name[i] = tolower(name[i]);
219*2e192b24SSimon Glass 		for (i = 0; i < strlen(value); i++)
220*2e192b24SSimon Glass 			value[i] = tolower(value[i]);
221*2e192b24SSimon Glass #endif
222*2e192b24SSimon Glass 		setenv(name, value);
223*2e192b24SSimon Glass 		printf("ini: Imported %s as %s\n", name, value);
224*2e192b24SSimon Glass 	}
225*2e192b24SSimon Glass 
226*2e192b24SSimon Glass 	/* success */
227*2e192b24SSimon Glass 	return 1;
228*2e192b24SSimon Glass }
229*2e192b24SSimon Glass 
230*2e192b24SSimon Glass static int do_ini(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
231*2e192b24SSimon Glass {
232*2e192b24SSimon Glass 	const char *section;
233*2e192b24SSimon Glass 	char *file_address;
234*2e192b24SSimon Glass 	size_t file_size;
235*2e192b24SSimon Glass 
236*2e192b24SSimon Glass 	if (argc == 1)
237*2e192b24SSimon Glass 		return CMD_RET_USAGE;
238*2e192b24SSimon Glass 
239*2e192b24SSimon Glass 	section = argv[1];
240*2e192b24SSimon Glass 	file_address = (char *)simple_strtoul(
241*2e192b24SSimon Glass 		argc < 3 ? getenv("loadaddr") : argv[2], NULL, 16);
242*2e192b24SSimon Glass 	file_size = (size_t)simple_strtoul(
243*2e192b24SSimon Glass 		argc < 4 ? getenv("filesize") : argv[3], NULL, 16);
244*2e192b24SSimon Glass 
245*2e192b24SSimon Glass 	return ini_parse(file_address, file_size, ini_handler, (void *)section);
246*2e192b24SSimon Glass }
247*2e192b24SSimon Glass 
248*2e192b24SSimon Glass U_BOOT_CMD(
249*2e192b24SSimon Glass 	ini, 4, 0, do_ini,
250*2e192b24SSimon Glass 	"parse an ini file in memory and merge the specified section into the env",
251*2e192b24SSimon Glass 	"section [[file-address] file-size]"
252*2e192b24SSimon Glass );
253