2 * inih -- simple .INI file parser
4 * Copyright (c) 2009, Brush Technology
6 * Joe Hershberger, National Instruments, joe.hershberger@ni.com
9 * The "inih" library is distributed under the following license, which is
10 * derived from and very similar to the 3-clause BSD license:
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions are met:
14 * * Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * * Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * * Neither the name of Brush Technology nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY BRUSH TECHNOLOGY ''AS IS'' AND ANY
24 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
26 * DISCLAIMED. IN NO EVENT SHALL BRUSH TECHNOLOGY BE LIABLE FOR ANY
27 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
28 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
30 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 * Go to the project home page for more info:
35 * http://code.google.com/p/inih/
40 #include <environment.h>
41 #include <linux/ctype.h>
42 #include <linux/string.h>
44 #ifdef CONFIG_INI_MAX_LINE
45 #define MAX_LINE CONFIG_INI_MAX_LINE
50 #ifdef CONFIG_INI_MAX_SECTION
51 #define MAX_SECTION CONFIG_INI_MAX_SECTION
53 #define MAX_SECTION 50
56 #ifdef CONFIG_INI_MAX_NAME
57 #define MAX_NAME CONFIG_INI_MAX_NAME
62 /* Strip whitespace chars off end of given string, in place. Return s. */
63 static char *rstrip(char *s)
65 char *p = s + strlen(s);
67 while (p > s && isspace(*--p))
72 /* Return pointer to first non-whitespace char in given string. */
73 static char *lskip(const char *s)
75 while (*s && isspace(*s))
80 /* Return pointer to first char c or ';' comment in given string, or pointer to
81 null at end of string if neither found. ';' must be prefixed by a whitespace
82 character to register as a comment. */
83 static char *find_char_or_comment(const char *s, char c)
85 int was_whitespace = 0;
87 while (*s && *s != c && !(was_whitespace && *s == ';')) {
88 was_whitespace = isspace(*s);
94 /* Version of strncpy that ensures dest (size bytes) is null-terminated. */
95 static char *strncpy0(char *dest, const char *src, size_t size)
97 strncpy(dest, src, size);
98 dest[size - 1] = '\0';
102 /* Emulate the behavior of fgets but on memory */
103 static char *memgets(char *str, int num, char **mem, size_t *memsize)
109 end = memchr(*mem, '\n', *memsize);
113 end = *mem + *memsize;
116 len = min((end - *mem) + newline, num);
117 memcpy(str, *mem, len);
121 /* prepare the mem vars for the next call */
122 *memsize -= (end - *mem) + newline;
123 *mem += (end - *mem) + newline;
128 /* Parse given INI-style file. May have [section]s, name=value pairs
129 (whitespace stripped), and comments starting with ';' (semicolon). Section
130 is "" if name=value pair parsed before any section heading. name:value
131 pairs are also supported as a concession to Python's ConfigParser.
133 For each name=value pair parsed, call handler function with given user
134 pointer as well as section, name, and value (data only valid for duration
135 of handler call). Handler should return nonzero on success, zero on error.
137 Returns 0 on success, line number of first error on parse error (doesn't
138 stop on first error).
140 static int ini_parse(char *filestart, size_t filelen,
141 int (*handler)(void *, char *, char *, char *), void *user)
143 /* Uses a fair bit of stack (use heap instead if you need to) */
145 char section[MAX_SECTION] = "";
146 char prev_name[MAX_NAME] = "";
148 char *curmem = filestart;
153 size_t memleft = filelen;
157 /* Scan through file line by line */
158 while (memgets(line, sizeof(line), &curmem, &memleft) != NULL) {
160 start = lskip(rstrip(line));
162 if (*start == ';' || *start == '#') {
164 * Per Python ConfigParser, allow '#' comments at start
168 #if CONFIG_INI_ALLOW_MULTILINE
169 else if (*prev_name && *start && start > line) {
171 * Non-blank line with leading whitespace, treat as
172 * continuation of previous name's value (as per Python
175 if (!handler(user, section, prev_name, start) && !error)
179 else if (*start == '[') {
180 /* A "[section]" line */
181 end = find_char_or_comment(start + 1, ']');
184 strncpy0(section, start + 1, sizeof(section));
187 /* No ']' found on section line */
190 } else if (*start && *start != ';') {
191 /* Not a comment, must be a name[=:]value pair */
192 end = find_char_or_comment(start, '=');
194 end = find_char_or_comment(start, ':');
195 if (*end == '=' || *end == ':') {
197 name = rstrip(start);
198 value = lskip(end + 1);
199 end = find_char_or_comment(value, '\0');
203 /* Strip double-quotes */
204 if (value[0] == '"' &&
205 value[strlen(value)-1] == '"') {
206 value[strlen(value)-1] = '\0';
211 * Valid name[=:]value pair found, call handler
213 strncpy0(prev_name, name, sizeof(prev_name));
214 if (!handler(user, section, name, value) &&
218 /* No '=' or ':' found on name[=:]value line */
226 static int ini_handler(void *user, char *section, char *name, char *value)
228 char *requested_section = (char *)user;
229 #ifdef CONFIG_INI_CASE_INSENSITIVE
232 for (i = 0; i < strlen(requested_section); i++)
233 requested_section[i] = tolower(requested_section[i]);
234 for (i = 0; i < strlen(section); i++)
235 section[i] = tolower(section[i]);
238 if (!strcmp(section, requested_section)) {
239 #ifdef CONFIG_INI_CASE_INSENSITIVE
240 for (i = 0; i < strlen(name); i++)
241 name[i] = tolower(name[i]);
242 for (i = 0; i < strlen(value); i++)
243 value[i] = tolower(value[i]);
246 printf("ini: Imported %s as %s\n", name, value);
253 static int do_ini(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
260 return CMD_RET_USAGE;
263 file_address = (char *)simple_strtoul(
264 argc < 3 ? getenv("loadaddr") : argv[2], NULL, 16);
265 file_size = (size_t)simple_strtoul(
266 argc < 4 ? getenv("filesize") : argv[3], NULL, 16);
268 return ini_parse(file_address, file_size, ini_handler, (void *)section);
273 "parse an ini file in memory and merge the specified section into the env",
274 "section [[file-address] file-size]"