Commit c167cc02033e45913ce6a3ba4575e7890ce55f07

Authored by Joe Hershberger
Committed by Tom Rini
1 parent 36180d96cc

Add a new "ini" command

This allows you to read ini-formatted data from anywhere and then
import one of the sections into the environment

This is based on rev 16 at http://code.google.com/p/inih/

Signed-off-by: Joe Hershberger <joe.hershberger@ni.com>

Showing 3 changed files with 249 additions and 0 deletions Side-by-side Diff

... ... @@ -816,6 +816,7 @@
816 816 CONFIG_CMD_IMLS List all found images
817 817 CONFIG_CMD_IMMAP * IMMR dump support
818 818 CONFIG_CMD_IMPORTENV * import an environment
  819 + CONFIG_CMD_INI * import data from an ini file into the env
819 820 CONFIG_CMD_IRQ * irqinfo
820 821 CONFIG_CMD_ITEST Integer/string test of 2 values
821 822 CONFIG_CMD_JFFS2 * JFFS2 Support
... ... @@ -107,6 +107,7 @@
107 107 COBJS-$(CONFIG_CMD_I2C) += cmd_i2c.o
108 108 COBJS-$(CONFIG_CMD_IDE) += cmd_ide.o
109 109 COBJS-$(CONFIG_CMD_IMMAP) += cmd_immap.o
  110 +COBJS-$(CONFIG_CMD_INI) += cmd_ini.o
110 111 COBJS-$(CONFIG_CMD_IRQ) += cmd_irq.o
111 112 COBJS-$(CONFIG_CMD_ITEST) += cmd_itest.o
112 113 COBJS-$(CONFIG_CMD_JFFS2) += cmd_jffs2.o
  1 +/*
  2 + * inih -- simple .INI file parser
  3 + *
  4 + * inih is released under the New BSD license (see LICENSE.txt). Go to the
  5 + * project home page for more info:
  6 + *
  7 + * http://code.google.com/p/inih/
  8 + */
  9 +
  10 +#include <common.h>
  11 +#include <command.h>
  12 +#include <environment.h>
  13 +#include <linux/ctype.h>
  14 +#include <linux/string.h>
  15 +
  16 +#ifdef CONFIG_INI_MAX_LINE
  17 +#define MAX_LINE CONFIG_INI_MAX_LINE
  18 +#else
  19 +#define MAX_LINE 200
  20 +#endif
  21 +
  22 +#ifdef CONFIG_INI_MAX_SECTION
  23 +#define MAX_SECTION CONFIG_INI_MAX_SECTION
  24 +#else
  25 +#define MAX_SECTION 50
  26 +#endif
  27 +
  28 +#ifdef CONFIG_INI_MAX_NAME
  29 +#define MAX_NAME CONFIG_INI_MAX_NAME
  30 +#else
  31 +#define MAX_NAME 50
  32 +#endif
  33 +
  34 +/* Strip whitespace chars off end of given string, in place. Return s. */
  35 +static char *rstrip(char *s)
  36 +{
  37 + char *p = s + strlen(s);
  38 +
  39 + while (p > s && isspace(*--p))
  40 + *p = '\0';
  41 + return s;
  42 +}
  43 +
  44 +/* Return pointer to first non-whitespace char in given string. */
  45 +static char *lskip(const char *s)
  46 +{
  47 + while (*s && isspace(*s))
  48 + s++;
  49 + return (char *)s;
  50 +}
  51 +
  52 +/* Return pointer to first char c or ';' comment in given string, or pointer to
  53 + null at end of string if neither found. ';' must be prefixed by a whitespace
  54 + character to register as a comment. */
  55 +static char *find_char_or_comment(const char *s, char c)
  56 +{
  57 + int was_whitespace = 0;
  58 +
  59 + while (*s && *s != c && !(was_whitespace && *s == ';')) {
  60 + was_whitespace = isspace(*s);
  61 + s++;
  62 + }
  63 + return (char *)s;
  64 +}
  65 +
  66 +/* Version of strncpy that ensures dest (size bytes) is null-terminated. */
  67 +static char *strncpy0(char *dest, const char *src, size_t size)
  68 +{
  69 + strncpy(dest, src, size);
  70 + dest[size - 1] = '\0';
  71 + return dest;
  72 +}
  73 +
  74 +/* Emulate the behavior of fgets but on memory */
  75 +static char *memgets(char *str, int num, char **mem, size_t *memsize)
  76 +{
  77 + char *end;
  78 + int len;
  79 + int newline = 1;
  80 +
  81 + end = memchr(*mem, '\n', *memsize);
  82 + if (end == NULL) {
  83 + if (*memsize == 0)
  84 + return NULL;
  85 + end = *mem + *memsize;
  86 + newline = 0;
  87 + }
  88 + len = min((end - *mem) + newline, num);
  89 + memcpy(str, *mem, len);
  90 + if (len < num)
  91 + str[len] = '\0';
  92 +
  93 + /* prepare the mem vars for the next call */
  94 + *memsize -= (end - *mem) + newline;
  95 + *mem += (end - *mem) + newline;
  96 +
  97 + return str;
  98 +}
  99 +
  100 +/* Parse given INI-style file. May have [section]s, name=value pairs
  101 + (whitespace stripped), and comments starting with ';' (semicolon). Section
  102 + is "" if name=value pair parsed before any section heading. name:value
  103 + pairs are also supported as a concession to Python's ConfigParser.
  104 +
  105 + For each name=value pair parsed, call handler function with given user
  106 + pointer as well as section, name, and value (data only valid for duration
  107 + of handler call). Handler should return nonzero on success, zero on error.
  108 +
  109 + Returns 0 on success, line number of first error on parse error (doesn't
  110 + stop on first error).
  111 +*/
  112 +static int ini_parse(char *filestart, size_t filelen,
  113 + int (*handler)(void *, char *, char *, char *), void *user)
  114 +{
  115 + /* Uses a fair bit of stack (use heap instead if you need to) */
  116 + char line[MAX_LINE];
  117 + char section[MAX_SECTION] = "";
  118 + char prev_name[MAX_NAME] = "";
  119 +
  120 + char *curmem = filestart;
  121 + char *start;
  122 + char *end;
  123 + char *name;
  124 + char *value;
  125 + size_t memleft = filelen;
  126 + int lineno = 0;
  127 + int error = 0;
  128 +
  129 + /* Scan through file line by line */
  130 + while (memgets(line, sizeof(line), &curmem, &memleft) != NULL) {
  131 + lineno++;
  132 + start = lskip(rstrip(line));
  133 +
  134 + if (*start == ';' || *start == '#') {
  135 + /*
  136 + * Per Python ConfigParser, allow '#' comments at start
  137 + * of line
  138 + */
  139 + }
  140 +#if CONFIG_INI_ALLOW_MULTILINE
  141 + else if (*prev_name && *start && start > line) {
  142 + /*
  143 + * Non-blank line with leading whitespace, treat as
  144 + * continuation of previous name's value (as per Python
  145 + * ConfigParser).
  146 + */
  147 + if (!handler(user, section, prev_name, start) && !error)
  148 + error = lineno;
  149 + }
  150 +#endif
  151 + else if (*start == '[') {
  152 + /* A "[section]" line */
  153 + end = find_char_or_comment(start + 1, ']');
  154 + if (*end == ']') {
  155 + *end = '\0';
  156 + strncpy0(section, start + 1, sizeof(section));
  157 + *prev_name = '\0';
  158 + } else if (!error) {
  159 + /* No ']' found on section line */
  160 + error = lineno;
  161 + }
  162 + } else if (*start && *start != ';') {
  163 + /* Not a comment, must be a name[=:]value pair */
  164 + end = find_char_or_comment(start, '=');
  165 + if (*end != '=')
  166 + end = find_char_or_comment(start, ':');
  167 + if (*end == '=' || *end == ':') {
  168 + *end = '\0';
  169 + name = rstrip(start);
  170 + value = lskip(end + 1);
  171 + end = find_char_or_comment(value, '\0');
  172 + if (*end == ';')
  173 + *end = '\0';
  174 + rstrip(value);
  175 + /* Strip double-quotes */
  176 + if (value[0] == '"' &&
  177 + value[strlen(value)-1] == '"') {
  178 + value[strlen(value)-1] = '\0';
  179 + value += 1;
  180 + }
  181 +
  182 + /*
  183 + * Valid name[=:]value pair found, call handler
  184 + */
  185 + strncpy0(prev_name, name, sizeof(prev_name));
  186 + if (!handler(user, section, name, value) &&
  187 + !error)
  188 + error = lineno;
  189 + } else if (!error)
  190 + /* No '=' or ':' found on name[=:]value line */
  191 + error = lineno;
  192 + }
  193 + }
  194 +
  195 + return error;
  196 +}
  197 +
  198 +static int ini_handler(void *user, char *section, char *name, char *value)
  199 +{
  200 + char *requested_section = (char *)user;
  201 +#ifdef CONFIG_INI_CASE_INSENSITIVE
  202 + int i;
  203 +
  204 + for (i = 0; i < strlen(requested_section); i++)
  205 + requested_section[i] = tolower(requested_section[i]);
  206 + for (i = 0; i < strlen(section); i++)
  207 + section[i] = tolower(section[i]);
  208 +#endif
  209 +
  210 + if (!strcmp(section, requested_section)) {
  211 +#ifdef CONFIG_INI_CASE_INSENSITIVE
  212 + for (i = 0; i < strlen(name); i++)
  213 + name[i] = tolower(name[i]);
  214 + for (i = 0; i < strlen(value); i++)
  215 + value[i] = tolower(value[i]);
  216 +#endif
  217 + setenv(name, value);
  218 + printf("ini: Imported %s as %s\n", name, value);
  219 + }
  220 +
  221 + /* success */
  222 + return 1;
  223 +}
  224 +
  225 +static int do_ini(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
  226 +{
  227 + const char *section;
  228 + char *file_address;
  229 + size_t file_size;
  230 +
  231 + if (argc == 1)
  232 + return CMD_RET_USAGE;
  233 +
  234 + section = argv[1];
  235 + file_address = (char *)simple_strtoul(
  236 + argc < 3 ? getenv("loadaddr") : argv[2], NULL, 16);
  237 + file_size = (size_t)simple_strtoul(
  238 + argc < 4 ? getenv("filesize") : argv[3], NULL, 16);
  239 +
  240 + return ini_parse(file_address, file_size, ini_handler, (void *)section);
  241 +}
  242 +
  243 +U_BOOT_CMD(
  244 + ini, 4, 0, do_ini,
  245 + "parse an ini file in memory and merge the specified section into the env",
  246 + "section [[file-address] file-size]"
  247 +);