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