123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275 |
- /*
- * inih -- simple .INI file parser
- *
- * Copyright (c) 2009, Brush Technology
- * Copyright (c) 2012:
- * Joe Hershberger, National Instruments, joe.hershberger@ni.com
- * All rights reserved.
- *
- * The "inih" library is distributed under the following license, which is
- * derived from and very similar to the 3-clause BSD license:
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * * Neither the name of Brush Technology nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY BRUSH TECHNOLOGY ''AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL BRUSH TECHNOLOGY BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * Go to the project home page for more info:
- * http://code.google.com/p/inih/
- */
- #include <common.h>
- #include <command.h>
- #include <environment.h>
- #include <linux/ctype.h>
- #include <linux/string.h>
- #ifdef CONFIG_INI_MAX_LINE
- #define MAX_LINE CONFIG_INI_MAX_LINE
- #else
- #define MAX_LINE 200
- #endif
- #ifdef CONFIG_INI_MAX_SECTION
- #define MAX_SECTION CONFIG_INI_MAX_SECTION
- #else
- #define MAX_SECTION 50
- #endif
- #ifdef CONFIG_INI_MAX_NAME
- #define MAX_NAME CONFIG_INI_MAX_NAME
- #else
- #define MAX_NAME 50
- #endif
- /* Strip whitespace chars off end of given string, in place. Return s. */
- static char *rstrip(char *s)
- {
- char *p = s + strlen(s);
- while (p > s && isspace(*--p))
- *p = '\0';
- return s;
- }
- /* Return pointer to first non-whitespace char in given string. */
- static char *lskip(const char *s)
- {
- while (*s && isspace(*s))
- s++;
- return (char *)s;
- }
- /* Return pointer to first char c or ';' comment in given string, or pointer to
- null at end of string if neither found. ';' must be prefixed by a whitespace
- character to register as a comment. */
- static char *find_char_or_comment(const char *s, char c)
- {
- int was_whitespace = 0;
- while (*s && *s != c && !(was_whitespace && *s == ';')) {
- was_whitespace = isspace(*s);
- s++;
- }
- return (char *)s;
- }
- /* Version of strncpy that ensures dest (size bytes) is null-terminated. */
- static char *strncpy0(char *dest, const char *src, size_t size)
- {
- strncpy(dest, src, size);
- dest[size - 1] = '\0';
- return dest;
- }
- /* Emulate the behavior of fgets but on memory */
- static char *memgets(char *str, int num, char **mem, size_t *memsize)
- {
- char *end;
- int len;
- int newline = 1;
- end = memchr(*mem, '\n', *memsize);
- if (end == NULL) {
- if (*memsize == 0)
- return NULL;
- end = *mem + *memsize;
- newline = 0;
- }
- len = min((end - *mem) + newline, num);
- memcpy(str, *mem, len);
- if (len < num)
- str[len] = '\0';
- /* prepare the mem vars for the next call */
- *memsize -= (end - *mem) + newline;
- *mem += (end - *mem) + newline;
- return str;
- }
- /* Parse given INI-style file. May have [section]s, name=value pairs
- (whitespace stripped), and comments starting with ';' (semicolon). Section
- is "" if name=value pair parsed before any section heading. name:value
- pairs are also supported as a concession to Python's ConfigParser.
- For each name=value pair parsed, call handler function with given user
- pointer as well as section, name, and value (data only valid for duration
- of handler call). Handler should return nonzero on success, zero on error.
- Returns 0 on success, line number of first error on parse error (doesn't
- stop on first error).
- */
- static int ini_parse(char *filestart, size_t filelen,
- int (*handler)(void *, char *, char *, char *), void *user)
- {
- /* Uses a fair bit of stack (use heap instead if you need to) */
- char line[MAX_LINE];
- char section[MAX_SECTION] = "";
- char prev_name[MAX_NAME] = "";
- char *curmem = filestart;
- char *start;
- char *end;
- char *name;
- char *value;
- size_t memleft = filelen;
- int lineno = 0;
- int error = 0;
- /* Scan through file line by line */
- while (memgets(line, sizeof(line), &curmem, &memleft) != NULL) {
- lineno++;
- start = lskip(rstrip(line));
- if (*start == ';' || *start == '#') {
- /*
- * Per Python ConfigParser, allow '#' comments at start
- * of line
- */
- }
- #if CONFIG_INI_ALLOW_MULTILINE
- else if (*prev_name && *start && start > line) {
- /*
- * Non-blank line with leading whitespace, treat as
- * continuation of previous name's value (as per Python
- * ConfigParser).
- */
- if (!handler(user, section, prev_name, start) && !error)
- error = lineno;
- }
- #endif
- else if (*start == '[') {
- /* A "[section]" line */
- end = find_char_or_comment(start + 1, ']');
- if (*end == ']') {
- *end = '\0';
- strncpy0(section, start + 1, sizeof(section));
- *prev_name = '\0';
- } else if (!error) {
- /* No ']' found on section line */
- error = lineno;
- }
- } else if (*start && *start != ';') {
- /* Not a comment, must be a name[=:]value pair */
- end = find_char_or_comment(start, '=');
- if (*end != '=')
- end = find_char_or_comment(start, ':');
- if (*end == '=' || *end == ':') {
- *end = '\0';
- name = rstrip(start);
- value = lskip(end + 1);
- end = find_char_or_comment(value, '\0');
- if (*end == ';')
- *end = '\0';
- rstrip(value);
- /* Strip double-quotes */
- if (value[0] == '"' &&
- value[strlen(value)-1] == '"') {
- value[strlen(value)-1] = '\0';
- value += 1;
- }
- /*
- * Valid name[=:]value pair found, call handler
- */
- strncpy0(prev_name, name, sizeof(prev_name));
- if (!handler(user, section, name, value) &&
- !error)
- error = lineno;
- } else if (!error)
- /* No '=' or ':' found on name[=:]value line */
- error = lineno;
- }
- }
- return error;
- }
- static int ini_handler(void *user, char *section, char *name, char *value)
- {
- char *requested_section = (char *)user;
- #ifdef CONFIG_INI_CASE_INSENSITIVE
- int i;
- for (i = 0; i < strlen(requested_section); i++)
- requested_section[i] = tolower(requested_section[i]);
- for (i = 0; i < strlen(section); i++)
- section[i] = tolower(section[i]);
- #endif
- if (!strcmp(section, requested_section)) {
- #ifdef CONFIG_INI_CASE_INSENSITIVE
- for (i = 0; i < strlen(name); i++)
- name[i] = tolower(name[i]);
- for (i = 0; i < strlen(value); i++)
- value[i] = tolower(value[i]);
- #endif
- setenv(name, value);
- printf("ini: Imported %s as %s\n", name, value);
- }
- /* success */
- return 1;
- }
- static int do_ini(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
- {
- const char *section;
- char *file_address;
- size_t file_size;
- if (argc == 1)
- return CMD_RET_USAGE;
- section = argv[1];
- file_address = (char *)simple_strtoul(
- argc < 3 ? getenv("loadaddr") : argv[2], NULL, 16);
- file_size = (size_t)simple_strtoul(
- argc < 4 ? getenv("filesize") : argv[3], NULL, 16);
- return ini_parse(file_address, file_size, ini_handler, (void *)section);
- }
- U_BOOT_CMD(
- ini, 4, 0, do_ini,
- "parse an ini file in memory and merge the specified section into the env",
- "section [[file-address] file-size]"
- );
|