uboot/cmd/ini.c
<<
>>
Prefs
   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. */
  40static 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. */
  50static 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. */
  60static 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. */
  72static 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 */
  80static 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*/
 117static 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
 203static 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
 230static 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
 248U_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);
 253