sandbox: Migrate CONFIG_I2C_EEPROM
[platform/kernel/u-boot.git] / cmd / ini.c
1 /*
2  * inih -- simple .INI file parser
3  *
4  * Copyright (c) 2009, Brush Technology
5  * Copyright (c) 2012:
6  *              Joe Hershberger, National Instruments, joe.hershberger@ni.com
7  * All rights reserved.
8  *
9  * SPDX-License-Identifier:     BSD-3-Clause
10  *
11  * Go to the project home page for more info:
12  * http://code.google.com/p/inih/
13  */
14
15 #include <common.h>
16 #include <command.h>
17 #include <environment.h>
18 #include <linux/ctype.h>
19 #include <linux/string.h>
20
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. */
40 static 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. */
50 static 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. */
60 static 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. */
72 static 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 */
80 static 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         }
93         len = min((end - *mem) + newline, num);
94         memcpy(str, *mem, len);
95         if (len < num)
96                 str[len] = '\0';
97
98         /* prepare the mem vars for the next call */
99         *memsize -= (end - *mem) + newline;
100         *mem += (end - *mem) + newline;
101
102         return str;
103 }
104
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.
109
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.
113
114    Returns 0 on success, line number of first error on parse error (doesn't
115    stop on first error).
116 */
117 static int ini_parse(char *filestart, size_t filelen,
118         int (*handler)(void *, char *, char *, char *), void *user)
119 {
120         /* Uses a fair bit of stack (use heap instead if you need to) */
121         char line[MAX_LINE];
122         char section[MAX_SECTION] = "";
123         char prev_name[MAX_NAME] = "";
124
125         char *curmem = filestart;
126         char *start;
127         char *end;
128         char *name;
129         char *value;
130         size_t memleft = filelen;
131         int lineno = 0;
132         int error = 0;
133
134         /* Scan through file line by line */
135         while (memgets(line, sizeof(line), &curmem, &memleft) != NULL) {
136                 lineno++;
137                 start = lskip(rstrip(line));
138
139                 if (*start == ';' || *start == '#') {
140                         /*
141                          * Per Python ConfigParser, allow '#' comments at start
142                          * of line
143                          */
144                 }
145 #if CONFIG_INI_ALLOW_MULTILINE
146                 else if (*prev_name && *start && start > line) {
147                         /*
148                          * Non-blank line with leading whitespace, treat as
149                          * continuation of previous name's value (as per Python
150                          * ConfigParser).
151                          */
152                         if (!handler(user, section, prev_name, start) && !error)
153                                 error = lineno;
154                 }
155 #endif
156                 else if (*start == '[') {
157                         /* A "[section]" line */
158                         end = find_char_or_comment(start + 1, ']');
159                         if (*end == ']') {
160                                 *end = '\0';
161                                 strncpy0(section, start + 1, sizeof(section));
162                                 *prev_name = '\0';
163                         } else if (!error) {
164                                 /* No ']' found on section line */
165                                 error = lineno;
166                         }
167                 } else if (*start && *start != ';') {
168                         /* Not a comment, must be a name[=:]value pair */
169                         end = find_char_or_comment(start, '=');
170                         if (*end != '=')
171                                 end = find_char_or_comment(start, ':');
172                         if (*end == '=' || *end == ':') {
173                                 *end = '\0';
174                                 name = rstrip(start);
175                                 value = lskip(end + 1);
176                                 end = find_char_or_comment(value, '\0');
177                                 if (*end == ';')
178                                         *end = '\0';
179                                 rstrip(value);
180                                 /* Strip double-quotes */
181                                 if (value[0] == '"' &&
182                                     value[strlen(value)-1] == '"') {
183                                         value[strlen(value)-1] = '\0';
184                                         value += 1;
185                                 }
186
187                                 /*
188                                  * Valid name[=:]value pair found, call handler
189                                  */
190                                 strncpy0(prev_name, name, sizeof(prev_name));
191                                 if (!handler(user, section, name, value) &&
192                                      !error)
193                                         error = lineno;
194                         } else if (!error)
195                                 /* No '=' or ':' found on name[=:]value line */
196                                 error = lineno;
197                 }
198         }
199
200         return error;
201 }
202
203 static int ini_handler(void *user, char *section, char *name, char *value)
204 {
205         char *requested_section = (char *)user;
206 #ifdef CONFIG_INI_CASE_INSENSITIVE
207         int i;
208
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]);
213 #endif
214
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]);
221 #endif
222                 setenv(name, value);
223                 printf("ini: Imported %s as %s\n", name, value);
224         }
225
226         /* success */
227         return 1;
228 }
229
230 static int do_ini(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
231 {
232         const char *section;
233         char *file_address;
234         size_t file_size;
235
236         if (argc == 1)
237                 return CMD_RET_USAGE;
238
239         section = argv[1];
240         file_address = (char *)simple_strtoul(
241                 argc < 3 ? getenv("loadaddr") : argv[2], NULL, 16);
242         file_size = (size_t)simple_strtoul(
243                 argc < 4 ? getenv("filesize") : argv[3], NULL, 16);
244
245         return ini_parse(file_address, file_size, ini_handler, (void *)section);
246 }
247
248 U_BOOT_CMD(
249         ini, 4, 0, do_ini,
250         "parse an ini file in memory and merge the specified section into the env",
251         "section [[file-address] file-size]"
252 );