uboot/common/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 * The "inih" library is distributed under the following license, which is
  10 * derived from and very similar to the 3-clause BSD license:
  11 *
  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.
  22 *
  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.
  33 *
  34 * Go to the project home page for more info:
  35 * http://code.google.com/p/inih/
  36 */
  37
  38#include <common.h>
  39#include <command.h>
  40#include <environment.h>
  41#include <linux/ctype.h>
  42#include <linux/string.h>
  43
  44#ifdef CONFIG_INI_MAX_LINE
  45#define MAX_LINE CONFIG_INI_MAX_LINE
  46#else
  47#define MAX_LINE 200
  48#endif
  49
  50#ifdef CONFIG_INI_MAX_SECTION
  51#define MAX_SECTION CONFIG_INI_MAX_SECTION
  52#else
  53#define MAX_SECTION 50
  54#endif
  55
  56#ifdef CONFIG_INI_MAX_NAME
  57#define MAX_NAME CONFIG_INI_MAX_NAME
  58#else
  59#define MAX_NAME 50
  60#endif
  61
  62/* Strip whitespace chars off end of given string, in place. Return s. */
  63static char *rstrip(char *s)
  64{
  65        char *p = s + strlen(s);
  66
  67        while (p > s && isspace(*--p))
  68                *p = '\0';
  69        return s;
  70}
  71
  72/* Return pointer to first non-whitespace char in given string. */
  73static char *lskip(const char *s)
  74{
  75        while (*s && isspace(*s))
  76                s++;
  77        return (char *)s;
  78}
  79
  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. */
  83static char *find_char_or_comment(const char *s, char c)
  84{
  85        int was_whitespace = 0;
  86
  87        while (*s && *s != c && !(was_whitespace && *s == ';')) {
  88                was_whitespace = isspace(*s);
  89                s++;
  90        }
  91        return (char *)s;
  92}
  93
  94/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
  95static char *strncpy0(char *dest, const char *src, size_t size)
  96{
  97        strncpy(dest, src, size);
  98        dest[size - 1] = '\0';
  99        return dest;
 100}
 101
 102/* Emulate the behavior of fgets but on memory */
 103static char *memgets(char *str, int num, char **mem, size_t *memsize)
 104{
 105        char *end;
 106        int len;
 107        int newline = 1;
 108
 109        end = memchr(*mem, '\n', *memsize);
 110        if (end == NULL) {
 111                if (*memsize == 0)
 112                        return NULL;
 113                end = *mem + *memsize;
 114                newline = 0;
 115        }
 116        len = min((end - *mem) + newline, num);
 117        memcpy(str, *mem, len);
 118        if (len < num)
 119                str[len] = '\0';
 120
 121        /* prepare the mem vars for the next call */
 122        *memsize -= (end - *mem) + newline;
 123        *mem += (end - *mem) + newline;
 124
 125        return str;
 126}
 127
 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.
 132
 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.
 136
 137   Returns 0 on success, line number of first error on parse error (doesn't
 138   stop on first error).
 139*/
 140static int ini_parse(char *filestart, size_t filelen,
 141        int (*handler)(void *, char *, char *, char *), void *user)
 142{
 143        /* Uses a fair bit of stack (use heap instead if you need to) */
 144        char line[MAX_LINE];
 145        char section[MAX_SECTION] = "";
 146        char prev_name[MAX_NAME] = "";
 147
 148        char *curmem = filestart;
 149        char *start;
 150        char *end;
 151        char *name;
 152        char *value;
 153        size_t memleft = filelen;
 154        int lineno = 0;
 155        int error = 0;
 156
 157        /* Scan through file line by line */
 158        while (memgets(line, sizeof(line), &curmem, &memleft) != NULL) {
 159                lineno++;
 160                start = lskip(rstrip(line));
 161
 162                if (*start == ';' || *start == '#') {
 163                        /*
 164                         * Per Python ConfigParser, allow '#' comments at start
 165                         * of line
 166                         */
 167                }
 168#if CONFIG_INI_ALLOW_MULTILINE
 169                else if (*prev_name && *start && start > line) {
 170                        /*
 171                         * Non-blank line with leading whitespace, treat as
 172                         * continuation of previous name's value (as per Python
 173                         * ConfigParser).
 174                         */
 175                        if (!handler(user, section, prev_name, start) && !error)
 176                                error = lineno;
 177                }
 178#endif
 179                else if (*start == '[') {
 180                        /* A "[section]" line */
 181                        end = find_char_or_comment(start + 1, ']');
 182                        if (*end == ']') {
 183                                *end = '\0';
 184                                strncpy0(section, start + 1, sizeof(section));
 185                                *prev_name = '\0';
 186                        } else if (!error) {
 187                                /* No ']' found on section line */
 188                                error = lineno;
 189                        }
 190                } else if (*start && *start != ';') {
 191                        /* Not a comment, must be a name[=:]value pair */
 192                        end = find_char_or_comment(start, '=');
 193                        if (*end != '=')
 194                                end = find_char_or_comment(start, ':');
 195                        if (*end == '=' || *end == ':') {
 196                                *end = '\0';
 197                                name = rstrip(start);
 198                                value = lskip(end + 1);
 199                                end = find_char_or_comment(value, '\0');
 200                                if (*end == ';')
 201                                        *end = '\0';
 202                                rstrip(value);
 203                                /* Strip double-quotes */
 204                                if (value[0] == '"' &&
 205                                    value[strlen(value)-1] == '"') {
 206                                        value[strlen(value)-1] = '\0';
 207                                        value += 1;
 208                                }
 209
 210                                /*
 211                                 * Valid name[=:]value pair found, call handler
 212                                 */
 213                                strncpy0(prev_name, name, sizeof(prev_name));
 214                                if (!handler(user, section, name, value) &&
 215                                     !error)
 216                                        error = lineno;
 217                        } else if (!error)
 218                                /* No '=' or ':' found on name[=:]value line */
 219                                error = lineno;
 220                }
 221        }
 222
 223        return error;
 224}
 225
 226static int ini_handler(void *user, char *section, char *name, char *value)
 227{
 228        char *requested_section = (char *)user;
 229#ifdef CONFIG_INI_CASE_INSENSITIVE
 230        int i;
 231
 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]);
 236#endif
 237
 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]);
 244#endif
 245                setenv(name, value);
 246                printf("ini: Imported %s as %s\n", name, value);
 247        }
 248
 249        /* success */
 250        return 1;
 251}
 252
 253static int do_ini(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
 254{
 255        const char *section;
 256        char *file_address;
 257        size_t file_size;
 258
 259        if (argc == 1)
 260                return CMD_RET_USAGE;
 261
 262        section = argv[1];
 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);
 267
 268        return ini_parse(file_address, file_size, ini_handler, (void *)section);
 269}
 270
 271U_BOOT_CMD(
 272        ini, 4, 0, do_ini,
 273        "parse an ini file in memory and merge the specified section into the env",
 274        "section [[file-address] file-size]"
 275);
 276