2 * inih -- simple .INI file parser
4 * Copyright (c) 2009, Brush Technology
6 * Joe Hershberger, National Instruments, joe.hershberger@ni.com
9 * SPDX-License-Identifier: BSD-3-Clause
11 * Go to the project home page for more info:
12 * http://code.google.com/p/inih/
17 #include <environment.h>
18 #include <linux/ctype.h>
19 #include <linux/string.h>
21 #ifdef CONFIG_INI_MAX_LINE
22 #define MAX_LINE CONFIG_INI_MAX_LINE
27 #ifdef CONFIG_INI_MAX_SECTION
28 #define MAX_SECTION CONFIG_INI_MAX_SECTION
30 #define MAX_SECTION 50
33 #ifdef CONFIG_INI_MAX_NAME
34 #define MAX_NAME CONFIG_INI_MAX_NAME
39 /* Strip whitespace chars off end of given string, in place. Return s. */
40 static char *rstrip(char *s)
42 char *p = s + strlen(s);
44 while (p > s && isspace(*--p))
49 /* Return pointer to first non-whitespace char in given string. */
50 static char *lskip(const char *s)
52 while (*s && isspace(*s))
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. */
60 static char *find_char_or_comment(const char *s, char c)
62 int was_whitespace = 0;
64 while (*s && *s != c && !(was_whitespace && *s == ';')) {
65 was_whitespace = isspace(*s);
71 /* Version of strncpy that ensures dest (size bytes) is null-terminated. */
72 static char *strncpy0(char *dest, const char *src, size_t size)
74 strncpy(dest, src, size);
75 dest[size - 1] = '\0';
79 /* Emulate the behavior of fgets but on memory */
80 static char *memgets(char *str, int num, char **mem, size_t *memsize)
86 end = memchr(*mem, '\n', *memsize);
90 end = *mem + *memsize;
93 len = min((end - *mem) + newline, num);
94 memcpy(str, *mem, len);
98 /* prepare the mem vars for the next call */
99 *memsize -= (end - *mem) + newline;
100 *mem += (end - *mem) + newline;
105 /* Parse given INI-style file. May have [section]s, name=value pairs
106 (whitespace stripped), and comments starting with ';' (semicolon). Section
107 is "" if name=value pair parsed before any section heading. name:value
108 pairs are also supported as a concession to Python's ConfigParser.
110 For each name=value pair parsed, call handler function with given user
111 pointer as well as section, name, and value (data only valid for duration
112 of handler call). Handler should return nonzero on success, zero on error.
114 Returns 0 on success, line number of first error on parse error (doesn't
115 stop on first error).
117 static int ini_parse(char *filestart, size_t filelen,
118 int (*handler)(void *, char *, char *, char *), void *user)
120 /* Uses a fair bit of stack (use heap instead if you need to) */
122 char section[MAX_SECTION] = "";
123 char prev_name[MAX_NAME] = "";
125 char *curmem = filestart;
130 size_t memleft = filelen;
134 /* Scan through file line by line */
135 while (memgets(line, sizeof(line), &curmem, &memleft) != NULL) {
137 start = lskip(rstrip(line));
139 if (*start == ';' || *start == '#') {
141 * Per Python ConfigParser, allow '#' comments at start
145 #if CONFIG_INI_ALLOW_MULTILINE
146 else if (*prev_name && *start && start > line) {
148 * Non-blank line with leading whitespace, treat as
149 * continuation of previous name's value (as per Python
152 if (!handler(user, section, prev_name, start) && !error)
156 else if (*start == '[') {
157 /* A "[section]" line */
158 end = find_char_or_comment(start + 1, ']');
161 strncpy0(section, start + 1, sizeof(section));
164 /* No ']' found on section line */
167 } else if (*start && *start != ';') {
168 /* Not a comment, must be a name[=:]value pair */
169 end = find_char_or_comment(start, '=');
171 end = find_char_or_comment(start, ':');
172 if (*end == '=' || *end == ':') {
174 name = rstrip(start);
175 value = lskip(end + 1);
176 end = find_char_or_comment(value, '\0');
180 /* Strip double-quotes */
181 if (value[0] == '"' &&
182 value[strlen(value)-1] == '"') {
183 value[strlen(value)-1] = '\0';
188 * Valid name[=:]value pair found, call handler
190 strncpy0(prev_name, name, sizeof(prev_name));
191 if (!handler(user, section, name, value) &&
195 /* No '=' or ':' found on name[=:]value line */
203 static int ini_handler(void *user, char *section, char *name, char *value)
205 char *requested_section = (char *)user;
206 #ifdef CONFIG_INI_CASE_INSENSITIVE
209 for (i = 0; i < strlen(requested_section); i++)
210 requested_section[i] = tolower(requested_section[i]);
211 for (i = 0; i < strlen(section); i++)
212 section[i] = tolower(section[i]);
215 if (!strcmp(section, requested_section)) {
216 #ifdef CONFIG_INI_CASE_INSENSITIVE
217 for (i = 0; i < strlen(name); i++)
218 name[i] = tolower(name[i]);
219 for (i = 0; i < strlen(value); i++)
220 value[i] = tolower(value[i]);
222 env_set(name, value);
223 printf("ini: Imported %s as %s\n", name, value);
230 static int do_ini(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
237 return CMD_RET_USAGE;
240 file_address = (char *)simple_strtoul(
241 argc < 3 ? env_get("loadaddr") : argv[2], NULL, 16);
242 file_size = (size_t)simple_strtoul(
243 argc < 4 ? env_get("filesize") : argv[3], NULL, 16);
245 return ini_parse(file_address, file_size, ini_handler, (void *)section);
250 "parse an ini file in memory and merge the specified section into the env",
251 "section [[file-address] file-size]"