12e192b24SSimon Glass /*
22e192b24SSimon Glass * inih -- simple .INI file parser
32e192b24SSimon Glass *
42e192b24SSimon Glass * Copyright (c) 2009, Brush Technology
52e192b24SSimon Glass * Copyright (c) 2012:
62e192b24SSimon Glass * Joe Hershberger, National Instruments, joe.hershberger@ni.com
72e192b24SSimon Glass * All rights reserved.
82e192b24SSimon Glass *
92e192b24SSimon Glass * SPDX-License-Identifier: BSD-3-Clause
102e192b24SSimon Glass *
112e192b24SSimon Glass * Go to the project home page for more info:
122e192b24SSimon Glass * http://code.google.com/p/inih/
132e192b24SSimon Glass */
142e192b24SSimon Glass
152e192b24SSimon Glass #include <common.h>
162e192b24SSimon Glass #include <command.h>
172e192b24SSimon Glass #include <environment.h>
182e192b24SSimon Glass #include <linux/ctype.h>
192e192b24SSimon Glass #include <linux/string.h>
202e192b24SSimon Glass
212e192b24SSimon Glass #ifdef CONFIG_INI_MAX_LINE
222e192b24SSimon Glass #define MAX_LINE CONFIG_INI_MAX_LINE
232e192b24SSimon Glass #else
242e192b24SSimon Glass #define MAX_LINE 200
252e192b24SSimon Glass #endif
262e192b24SSimon Glass
272e192b24SSimon Glass #ifdef CONFIG_INI_MAX_SECTION
282e192b24SSimon Glass #define MAX_SECTION CONFIG_INI_MAX_SECTION
292e192b24SSimon Glass #else
302e192b24SSimon Glass #define MAX_SECTION 50
312e192b24SSimon Glass #endif
322e192b24SSimon Glass
332e192b24SSimon Glass #ifdef CONFIG_INI_MAX_NAME
342e192b24SSimon Glass #define MAX_NAME CONFIG_INI_MAX_NAME
352e192b24SSimon Glass #else
362e192b24SSimon Glass #define MAX_NAME 50
372e192b24SSimon Glass #endif
382e192b24SSimon Glass
392e192b24SSimon Glass /* Strip whitespace chars off end of given string, in place. Return s. */
rstrip(char * s)402e192b24SSimon Glass static char *rstrip(char *s)
412e192b24SSimon Glass {
422e192b24SSimon Glass char *p = s + strlen(s);
432e192b24SSimon Glass
442e192b24SSimon Glass while (p > s && isspace(*--p))
452e192b24SSimon Glass *p = '\0';
462e192b24SSimon Glass return s;
472e192b24SSimon Glass }
482e192b24SSimon Glass
492e192b24SSimon Glass /* Return pointer to first non-whitespace char in given string. */
lskip(const char * s)502e192b24SSimon Glass static char *lskip(const char *s)
512e192b24SSimon Glass {
522e192b24SSimon Glass while (*s && isspace(*s))
532e192b24SSimon Glass s++;
542e192b24SSimon Glass return (char *)s;
552e192b24SSimon Glass }
562e192b24SSimon Glass
572e192b24SSimon Glass /* Return pointer to first char c or ';' comment in given string, or pointer to
582e192b24SSimon Glass null at end of string if neither found. ';' must be prefixed by a whitespace
592e192b24SSimon Glass character to register as a comment. */
find_char_or_comment(const char * s,char c)602e192b24SSimon Glass static char *find_char_or_comment(const char *s, char c)
612e192b24SSimon Glass {
622e192b24SSimon Glass int was_whitespace = 0;
632e192b24SSimon Glass
642e192b24SSimon Glass while (*s && *s != c && !(was_whitespace && *s == ';')) {
652e192b24SSimon Glass was_whitespace = isspace(*s);
662e192b24SSimon Glass s++;
672e192b24SSimon Glass }
682e192b24SSimon Glass return (char *)s;
692e192b24SSimon Glass }
702e192b24SSimon Glass
712e192b24SSimon Glass /* Version of strncpy that ensures dest (size bytes) is null-terminated. */
strncpy0(char * dest,const char * src,size_t size)722e192b24SSimon Glass static char *strncpy0(char *dest, const char *src, size_t size)
732e192b24SSimon Glass {
742e192b24SSimon Glass strncpy(dest, src, size);
752e192b24SSimon Glass dest[size - 1] = '\0';
762e192b24SSimon Glass return dest;
772e192b24SSimon Glass }
782e192b24SSimon Glass
792e192b24SSimon Glass /* Emulate the behavior of fgets but on memory */
memgets(char * str,int num,char ** mem,size_t * memsize)802e192b24SSimon Glass static char *memgets(char *str, int num, char **mem, size_t *memsize)
812e192b24SSimon Glass {
822e192b24SSimon Glass char *end;
832e192b24SSimon Glass int len;
842e192b24SSimon Glass int newline = 1;
852e192b24SSimon Glass
862e192b24SSimon Glass end = memchr(*mem, '\n', *memsize);
872e192b24SSimon Glass if (end == NULL) {
882e192b24SSimon Glass if (*memsize == 0)
892e192b24SSimon Glass return NULL;
902e192b24SSimon Glass end = *mem + *memsize;
912e192b24SSimon Glass newline = 0;
922e192b24SSimon Glass }
932e192b24SSimon Glass len = min((end - *mem) + newline, num);
942e192b24SSimon Glass memcpy(str, *mem, len);
952e192b24SSimon Glass if (len < num)
962e192b24SSimon Glass str[len] = '\0';
972e192b24SSimon Glass
982e192b24SSimon Glass /* prepare the mem vars for the next call */
992e192b24SSimon Glass *memsize -= (end - *mem) + newline;
1002e192b24SSimon Glass *mem += (end - *mem) + newline;
1012e192b24SSimon Glass
1022e192b24SSimon Glass return str;
1032e192b24SSimon Glass }
1042e192b24SSimon Glass
1052e192b24SSimon Glass /* Parse given INI-style file. May have [section]s, name=value pairs
1062e192b24SSimon Glass (whitespace stripped), and comments starting with ';' (semicolon). Section
1072e192b24SSimon Glass is "" if name=value pair parsed before any section heading. name:value
1082e192b24SSimon Glass pairs are also supported as a concession to Python's ConfigParser.
1092e192b24SSimon Glass
1102e192b24SSimon Glass For each name=value pair parsed, call handler function with given user
1112e192b24SSimon Glass pointer as well as section, name, and value (data only valid for duration
1122e192b24SSimon Glass of handler call). Handler should return nonzero on success, zero on error.
1132e192b24SSimon Glass
1142e192b24SSimon Glass Returns 0 on success, line number of first error on parse error (doesn't
1152e192b24SSimon Glass stop on first error).
1162e192b24SSimon Glass */
ini_parse(char * filestart,size_t filelen,int (* handler)(void *,char *,char *,char *),void * user)1172e192b24SSimon Glass static int ini_parse(char *filestart, size_t filelen,
1182e192b24SSimon Glass int (*handler)(void *, char *, char *, char *), void *user)
1192e192b24SSimon Glass {
1202e192b24SSimon Glass /* Uses a fair bit of stack (use heap instead if you need to) */
1212e192b24SSimon Glass char line[MAX_LINE];
1222e192b24SSimon Glass char section[MAX_SECTION] = "";
1232e192b24SSimon Glass char prev_name[MAX_NAME] = "";
1242e192b24SSimon Glass
1252e192b24SSimon Glass char *curmem = filestart;
1262e192b24SSimon Glass char *start;
1272e192b24SSimon Glass char *end;
1282e192b24SSimon Glass char *name;
1292e192b24SSimon Glass char *value;
1302e192b24SSimon Glass size_t memleft = filelen;
1312e192b24SSimon Glass int lineno = 0;
1322e192b24SSimon Glass int error = 0;
1332e192b24SSimon Glass
1342e192b24SSimon Glass /* Scan through file line by line */
1352e192b24SSimon Glass while (memgets(line, sizeof(line), &curmem, &memleft) != NULL) {
1362e192b24SSimon Glass lineno++;
1372e192b24SSimon Glass start = lskip(rstrip(line));
1382e192b24SSimon Glass
1392e192b24SSimon Glass if (*start == ';' || *start == '#') {
1402e192b24SSimon Glass /*
1412e192b24SSimon Glass * Per Python ConfigParser, allow '#' comments at start
1422e192b24SSimon Glass * of line
1432e192b24SSimon Glass */
1442e192b24SSimon Glass }
1452e192b24SSimon Glass #if CONFIG_INI_ALLOW_MULTILINE
1462e192b24SSimon Glass else if (*prev_name && *start && start > line) {
1472e192b24SSimon Glass /*
1482e192b24SSimon Glass * Non-blank line with leading whitespace, treat as
1492e192b24SSimon Glass * continuation of previous name's value (as per Python
1502e192b24SSimon Glass * ConfigParser).
1512e192b24SSimon Glass */
1522e192b24SSimon Glass if (!handler(user, section, prev_name, start) && !error)
1532e192b24SSimon Glass error = lineno;
1542e192b24SSimon Glass }
1552e192b24SSimon Glass #endif
1562e192b24SSimon Glass else if (*start == '[') {
1572e192b24SSimon Glass /* A "[section]" line */
1582e192b24SSimon Glass end = find_char_or_comment(start + 1, ']');
1592e192b24SSimon Glass if (*end == ']') {
1602e192b24SSimon Glass *end = '\0';
1612e192b24SSimon Glass strncpy0(section, start + 1, sizeof(section));
1622e192b24SSimon Glass *prev_name = '\0';
1632e192b24SSimon Glass } else if (!error) {
1642e192b24SSimon Glass /* No ']' found on section line */
1652e192b24SSimon Glass error = lineno;
1662e192b24SSimon Glass }
1672e192b24SSimon Glass } else if (*start && *start != ';') {
1682e192b24SSimon Glass /* Not a comment, must be a name[=:]value pair */
1692e192b24SSimon Glass end = find_char_or_comment(start, '=');
1702e192b24SSimon Glass if (*end != '=')
1712e192b24SSimon Glass end = find_char_or_comment(start, ':');
1722e192b24SSimon Glass if (*end == '=' || *end == ':') {
1732e192b24SSimon Glass *end = '\0';
1742e192b24SSimon Glass name = rstrip(start);
1752e192b24SSimon Glass value = lskip(end + 1);
1762e192b24SSimon Glass end = find_char_or_comment(value, '\0');
1772e192b24SSimon Glass if (*end == ';')
1782e192b24SSimon Glass *end = '\0';
1792e192b24SSimon Glass rstrip(value);
1802e192b24SSimon Glass /* Strip double-quotes */
1812e192b24SSimon Glass if (value[0] == '"' &&
1822e192b24SSimon Glass value[strlen(value)-1] == '"') {
1832e192b24SSimon Glass value[strlen(value)-1] = '\0';
1842e192b24SSimon Glass value += 1;
1852e192b24SSimon Glass }
1862e192b24SSimon Glass
1872e192b24SSimon Glass /*
1882e192b24SSimon Glass * Valid name[=:]value pair found, call handler
1892e192b24SSimon Glass */
1902e192b24SSimon Glass strncpy0(prev_name, name, sizeof(prev_name));
1912e192b24SSimon Glass if (!handler(user, section, name, value) &&
1922e192b24SSimon Glass !error)
1932e192b24SSimon Glass error = lineno;
1942e192b24SSimon Glass } else if (!error)
1952e192b24SSimon Glass /* No '=' or ':' found on name[=:]value line */
1962e192b24SSimon Glass error = lineno;
1972e192b24SSimon Glass }
1982e192b24SSimon Glass }
1992e192b24SSimon Glass
2002e192b24SSimon Glass return error;
2012e192b24SSimon Glass }
2022e192b24SSimon Glass
ini_handler(void * user,char * section,char * name,char * value)2032e192b24SSimon Glass static int ini_handler(void *user, char *section, char *name, char *value)
2042e192b24SSimon Glass {
2052e192b24SSimon Glass char *requested_section = (char *)user;
2062e192b24SSimon Glass #ifdef CONFIG_INI_CASE_INSENSITIVE
2072e192b24SSimon Glass int i;
2082e192b24SSimon Glass
2092e192b24SSimon Glass for (i = 0; i < strlen(requested_section); i++)
2102e192b24SSimon Glass requested_section[i] = tolower(requested_section[i]);
2112e192b24SSimon Glass for (i = 0; i < strlen(section); i++)
2122e192b24SSimon Glass section[i] = tolower(section[i]);
2132e192b24SSimon Glass #endif
2142e192b24SSimon Glass
2152e192b24SSimon Glass if (!strcmp(section, requested_section)) {
2162e192b24SSimon Glass #ifdef CONFIG_INI_CASE_INSENSITIVE
2172e192b24SSimon Glass for (i = 0; i < strlen(name); i++)
2182e192b24SSimon Glass name[i] = tolower(name[i]);
2192e192b24SSimon Glass for (i = 0; i < strlen(value); i++)
2202e192b24SSimon Glass value[i] = tolower(value[i]);
2212e192b24SSimon Glass #endif
222382bee57SSimon Glass env_set(name, value);
2232e192b24SSimon Glass printf("ini: Imported %s as %s\n", name, value);
2242e192b24SSimon Glass }
2252e192b24SSimon Glass
2262e192b24SSimon Glass /* success */
2272e192b24SSimon Glass return 1;
2282e192b24SSimon Glass }
2292e192b24SSimon Glass
do_ini(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])2302e192b24SSimon Glass static int do_ini(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
2312e192b24SSimon Glass {
2322e192b24SSimon Glass const char *section;
2332e192b24SSimon Glass char *file_address;
2342e192b24SSimon Glass size_t file_size;
2352e192b24SSimon Glass
2362e192b24SSimon Glass if (argc == 1)
2372e192b24SSimon Glass return CMD_RET_USAGE;
2382e192b24SSimon Glass
2392e192b24SSimon Glass section = argv[1];
2402e192b24SSimon Glass file_address = (char *)simple_strtoul(
241*00caae6dSSimon Glass argc < 3 ? env_get("loadaddr") : argv[2], NULL, 16);
2422e192b24SSimon Glass file_size = (size_t)simple_strtoul(
243*00caae6dSSimon Glass argc < 4 ? env_get("filesize") : argv[3], NULL, 16);
2442e192b24SSimon Glass
2452e192b24SSimon Glass return ini_parse(file_address, file_size, ini_handler, (void *)section);
2462e192b24SSimon Glass }
2472e192b24SSimon Glass
2482e192b24SSimon Glass U_BOOT_CMD(
2492e192b24SSimon Glass ini, 4, 0, do_ini,
2502e192b24SSimon Glass "parse an ini file in memory and merge the specified section into the env",
2512e192b24SSimon Glass "section [[file-address] file-size]"
2522e192b24SSimon Glass );
253