2 * inih -- simple .INI file parser
4 * inih is released under the New BSD license (see LICENSE.txt). Go to the
5 * project home page for more info:
7 * http://code.google.com/p/inih/
12 #include <environment.h>
13 #include <linux/ctype.h>
14 #include <linux/string.h>
16 #ifdef CONFIG_INI_MAX_LINE
17 #define MAX_LINE CONFIG_INI_MAX_LINE
22 #ifdef CONFIG_INI_MAX_SECTION
23 #define MAX_SECTION CONFIG_INI_MAX_SECTION
25 #define MAX_SECTION 50
28 #ifdef CONFIG_INI_MAX_NAME
29 #define MAX_NAME CONFIG_INI_MAX_NAME
34 /* Strip whitespace chars off end of given string, in place. Return s. */
35 static char *rstrip(char *s)
37 char *p = s + strlen(s);
39 while (p > s && isspace(*--p))
44 /* Return pointer to first non-whitespace char in given string. */
45 static char *lskip(const char *s)
47 while (*s && isspace(*s))
52 /* Return pointer to first char c or ';' comment in given string, or pointer to
53 null at end of string if neither found. ';' must be prefixed by a whitespace
54 character to register as a comment. */
55 static char *find_char_or_comment(const char *s, char c)
57 int was_whitespace = 0;
59 while (*s && *s != c && !(was_whitespace && *s == ';')) {
60 was_whitespace = isspace(*s);
66 /* Version of strncpy that ensures dest (size bytes) is null-terminated. */
67 static char *strncpy0(char *dest, const char *src, size_t size)
69 strncpy(dest, src, size);
70 dest[size - 1] = '\0';
74 /* Emulate the behavior of fgets but on memory */
75 static char *memgets(char *str, int num, char **mem, size_t *memsize)
81 end = memchr(*mem, '\n', *memsize);
85 end = *mem + *memsize;
88 len = min((end - *mem) + newline, num);
89 memcpy(str, *mem, len);
93 /* prepare the mem vars for the next call */
94 *memsize -= (end - *mem) + newline;
95 *mem += (end - *mem) + newline;
100 /* Parse given INI-style file. May have [section]s, name=value pairs
101 (whitespace stripped), and comments starting with ';' (semicolon). Section
102 is "" if name=value pair parsed before any section heading. name:value
103 pairs are also supported as a concession to Python's ConfigParser.
105 For each name=value pair parsed, call handler function with given user
106 pointer as well as section, name, and value (data only valid for duration
107 of handler call). Handler should return nonzero on success, zero on error.
109 Returns 0 on success, line number of first error on parse error (doesn't
110 stop on first error).
112 static int ini_parse(char *filestart, size_t filelen,
113 int (*handler)(void *, char *, char *, char *), void *user)
115 /* Uses a fair bit of stack (use heap instead if you need to) */
117 char section[MAX_SECTION] = "";
118 char prev_name[MAX_NAME] = "";
120 char *curmem = filestart;
125 size_t memleft = filelen;
129 /* Scan through file line by line */
130 while (memgets(line, sizeof(line), &curmem, &memleft) != NULL) {
132 start = lskip(rstrip(line));
134 if (*start == ';' || *start == '#') {
136 * Per Python ConfigParser, allow '#' comments at start
140 #if CONFIG_INI_ALLOW_MULTILINE
141 else if (*prev_name && *start && start > line) {
143 * Non-blank line with leading whitespace, treat as
144 * continuation of previous name's value (as per Python
147 if (!handler(user, section, prev_name, start) && !error)
151 else if (*start == '[') {
152 /* A "[section]" line */
153 end = find_char_or_comment(start + 1, ']');
156 strncpy0(section, start + 1, sizeof(section));
159 /* No ']' found on section line */
162 } else if (*start && *start != ';') {
163 /* Not a comment, must be a name[=:]value pair */
164 end = find_char_or_comment(start, '=');
166 end = find_char_or_comment(start, ':');
167 if (*end == '=' || *end == ':') {
169 name = rstrip(start);
170 value = lskip(end + 1);
171 end = find_char_or_comment(value, '\0');
175 /* Strip double-quotes */
176 if (value[0] == '"' &&
177 value[strlen(value)-1] == '"') {
178 value[strlen(value)-1] = '\0';
183 * Valid name[=:]value pair found, call handler
185 strncpy0(prev_name, name, sizeof(prev_name));
186 if (!handler(user, section, name, value) &&
190 /* No '=' or ':' found on name[=:]value line */
198 static int ini_handler(void *user, char *section, char *name, char *value)
200 char *requested_section = (char *)user;
201 #ifdef CONFIG_INI_CASE_INSENSITIVE
204 for (i = 0; i < strlen(requested_section); i++)
205 requested_section[i] = tolower(requested_section[i]);
206 for (i = 0; i < strlen(section); i++)
207 section[i] = tolower(section[i]);
210 if (!strcmp(section, requested_section)) {
211 #ifdef CONFIG_INI_CASE_INSENSITIVE
212 for (i = 0; i < strlen(name); i++)
213 name[i] = tolower(name[i]);
214 for (i = 0; i < strlen(value); i++)
215 value[i] = tolower(value[i]);
218 printf("ini: Imported %s as %s\n", name, value);
225 static int do_ini(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
232 return CMD_RET_USAGE;
235 file_address = (char *)simple_strtoul(
236 argc < 3 ? getenv("loadaddr") : argv[2], NULL, 16);
237 file_size = (size_t)simple_strtoul(
238 argc < 4 ? getenv("filesize") : argv[3], NULL, 16);
240 return ini_parse(file_address, file_size, ini_handler, (void *)section);
245 "parse an ini file in memory and merge the specified section into the env",
246 "section [[file-address] file-size]"