
Add a sort of batch mode to fw_setenv, allowing to set multiple variables in one shot, without updating the flash after each set as now. It is added the possibility to pass a config file with a list of pairs <variable, value> to be set, separated by a TAB character.
Signed-off-by: Stefano Babic sbabic@denx.de --- tools/env/fw_env.c | 279 ++++++++++++++++++++++++++++++++++++++++------- tools/env/fw_env.h | 1 + tools/env/fw_env_main.c | 25 ++++- 3 files changed, 263 insertions(+), 42 deletions(-)
Changes since V1:
Wolfgang Denk: - check returns of malloc call (no malloc required in this version) - added messages in case of errors - replace interface with a more similar I/O interface (fw_env_open, fw_env_write, fw_env_close) - Allow any number of leading whitespaces in the file format <white spaces>name<white space>value - Refactoring out some general code - Check for too long lines - Accept stdin as filename
diff --git a/tools/env/fw_env.c b/tools/env/fw_env.c index a46205d..6bf2761 100644 --- a/tools/env/fw_env.c +++ b/tools/env/fw_env.c @@ -45,8 +45,7 @@
#include "fw_env.h"
-#define CMD_GETENV "fw_printenv" -#define CMD_SETENV "fw_setenv" +#define WHITESPACE(c) ((c == '\t') || (c == ' '))
#define min(x, y) ({ \ typeof(x) _min1 = (x); \ @@ -225,6 +224,22 @@ static inline ulong getenvsize (void) return rc; }
+static char *fw_string_blank(char *s, int noblank) +{ + int i; + int len = strlen(s); + + for (i = 0; i < len; i++, s++) { + if ((noblank && !WHITESPACE(*s)) || + (!noblank && WHITESPACE(*s))) + break; + } + if (i == len) + return NULL; + + return s; +} + /* * Search the environment for a variable. * Return the value, if found, or NULL, if not found. @@ -327,30 +342,39 @@ int fw_printenv (int argc, char *argv[]) return rc; }
-/* - * Deletes or sets environment variables. Returns -1 and sets errno error codes: - * 0 - OK - * EINVAL - need at least 1 argument - * EROFS - certain variables ("ethaddr", "serial#") cannot be - * modified or deleted - * - */ -int fw_setenv (int argc, char *argv[]) +int fw_env_open(void) { - int i, len; - char *env, *nxt; - char *oldval = NULL; - char *name; + return env_init(); +}
- if (argc < 2) { - errno = EINVAL; - return -1; +int fw_env_close(void) +{ + /* + * Update CRC + */ + *environment.crc = crc32(0, (uint8_t *) environment.data, ENV_SIZE); + + /* write environment back to flash */ + if (flash_io(O_RDWR)) { + fprintf(stderr, + "Error: can't write fw_env to flash\n"); + return -1; }
- if (env_init ()) - return -1; + return 0; +}
- name = argv[1]; + +/* + * Set/Clear a single variable in the environment. + * This is called in sequence to update the environment + * in RAM without updating the copy in flash after each set + */ +int fw_env_write(char *name, char *value) +{ + int len; + char *env, *nxt; + char *oldval = NULL;
/* * search if variable with this name already exists @@ -358,7 +382,7 @@ int fw_setenv (int argc, char *argv[]) for (nxt = env = environment.data; *env; env = nxt + 1) { for (nxt = env; *nxt; ++nxt) { if (nxt >= &environment.data[ENV_SIZE]) { - fprintf (stderr, "## Error: " + fprintf(stderr, "## Error: " "environment not terminated\n"); errno = EINVAL; return -1; @@ -396,8 +420,8 @@ int fw_setenv (int argc, char *argv[]) }
/* Delete only ? */ - if (argc < 3) - goto WRITE_FLASH; + if (!value || !strlen(value)) + return 0;
/* * Append new definition at the end @@ -411,41 +435,216 @@ int fw_setenv (int argc, char *argv[]) */ len = strlen (name) + 2; /* add '=' for first arg, ' ' for all others */ - for (i = 2; i < argc; ++i) { - len += strlen (argv[i]) + 1; - } + len += strlen(value) + 1; + if (len > (&environment.data[ENV_SIZE] - env)) { fprintf (stderr, "Error: environment overflow, "%s" deleted\n", name); return -1; } + while ((*env = *name++) != '\0') env++; - for (i = 2; i < argc; ++i) { - char *val = argv[i]; - - *env = (i == 2) ? '=' : ' '; - while ((*++env = *val++) != '\0'); - } + *env = '='; + while ((*++env = *value++) != '\0') + ;
/* end is marked with double '\0' */ *++env = '\0';
- WRITE_FLASH: + return 0; +} + +/* + * Deletes or sets environment variables. Returns -1 and sets errno error codes: + * 0 - OK + * EINVAL - need at least 1 argument + * EROFS - certain variables ("ethaddr", "serial#") cannot be + * modified or deleted + * + */ +int fw_setenv(int argc, char *argv[]) +{ + int i, len; + char *env; + char *name; + char *value = NULL; + char *tmpval = NULL; + + if (argc < 2) { + errno = EINVAL; + return -1; + } + + if (fw_env_open()) { + fprintf(stderr, "Error: environment not initialized\n"); + return -1; + } + + name = argv[1];
/* - * Update CRC + * Overflow when: + * "name" + "=" + "val" +"\0\0" > CONFIG_ENV_SIZE - (env-environment) */ - *environment.crc = crc32 (0, (uint8_t *) environment.data, ENV_SIZE); + len = strlen(name) + 2; + /* add '=' for first arg, ' ' for all others */ + for (i = 2; i < argc; ++i) + len += strlen(argv[i]) + 1;
- /* write environment back to flash */ - if (flash_io (O_RDWR)) { - fprintf (stderr, "Error: can't write fw_env to flash\n"); + if (len > (&environment.data[ENV_SIZE] - env)) { + fprintf(stderr, + "Error: environment overflow, "%s" deleted\n", + name); return -1; }
- return 0; + /* Allocate enough place to the data string */ + for (i = 2; i < argc; ++i) { + char *val = argv[i]; + if (!value) { + value = (char *)malloc(len - strlen(name)); + if (!value) { + fprintf(stderr, + "Cannot malloc %u bytes: %s\n", + len - strlen(name), strerror(errno)); + return -1; + } + memset(value, 0, len - strlen(name)); + tmpval = value; + } + if (i != 2) + *tmpval++ = ' '; + while (*val != '\0') + *tmpval++ = *val++; + } + + fw_env_write(name, value); + + if (value) + free(value); + + return fw_env_close(); +} + +/* + * Parse a file and configure the u-boot variables. + * The script file has a very simple format, as follows: + * + * Each line has a couple with name, value: + * <white spaces>variable_name<white spaces>variable_value + * + * Both variable_name and variable_value are interpreted as strings. + * Any character after <white spaces> and before ending \r\n is interpreted + * as variable's value (no comment allowed on these lines !) + * + * Comments are allowed if the first character in the line is # + * + * Returns -1 and sets errno error codes: + * 0 - OK + * -1 - Error + */ +int fw_parse_script(char *fname) +{ + FILE *fp; + char dump[1024]; /* Maximum line length in the file */ + char *name; + char *val; + int lineno = 0; + int len; + int ret = 0; + + if (fw_env_open()) { + fprintf(stderr, "Error: environment not initialized\n"); + return -1; + } + + if (strcmp(fname, "-") == 0) + fp = stdin; + else { + fp = fopen(fname, "r"); + if (fp == NULL) { + fprintf(stderr, "I cannot open %s for reading\n", + fname); + return -1; + } + } + + while (fgets(dump, sizeof(dump), fp)) { + lineno++; + len = strlen(dump); + + /* + * Read a whole line from the file. If the line is too long + * or is not terminated, reports an error and exit. + */ + if (dump[len - 1] != '\n') { + fprintf(stderr, + "Line %d not corrected terminated or too long\n", + lineno); + ret = -1; + break; + } + + /* Drop ending line feed / carriage return */ + while (len > 0 && (dump[len - 1] == '\n' || + dump[len - 1] == '\r')) { + dump[len - 1] = '\0'; + len--; + } + + /* Skip comment or empty lines */ + if ((len == 0) || dump[0] == '#') + continue; + + /* + * Search for variable's name, + * remove leading whitespaces + */ + name = fw_string_blank(dump, 1); + if (!name) + continue; + + /* The first white space is the end of variable name */ + val = fw_string_blank(name, 0); + len = strlen(name); + if (val) { + *val++ = '\0'; + if ((val - name) < len) + val = fw_string_blank(val, 1); + else + val = NULL; + } + +#ifdef DEBUG + fprintf(stderr, "Setting %s : %s\n", + name, val ? val : " removed"); +#endif + + /* + * If there is an error setting a variable, + * try to save the environment and returns an error + */ + if (fw_env_write(name, val)) { + fprintf(stderr, + "fw_env_write returns with error : %s\n", + strerror(errno)); + fw_env_close(); + ret = -1; + break; + } + + } + + /* Close file if not stdin */ + if (strcmp(fname, "-") != 0) + fclose(fp); + + ret |= fw_env_close(); + + return ret; + }
/* diff --git a/tools/env/fw_env.h b/tools/env/fw_env.h index c04da54..f24d7a9 100644 --- a/tools/env/fw_env.h +++ b/tools/env/fw_env.h @@ -50,5 +50,6 @@ extern int fw_printenv(int argc, char *argv[]); extern char *fw_getenv (char *name); extern int fw_setenv (int argc, char *argv[]); +extern int fw_parse_script(char *fname);
extern unsigned long crc32 (unsigned long, const unsigned char *, unsigned); diff --git a/tools/env/fw_env_main.c b/tools/env/fw_env_main.c index 7f631c4..3ba9eae 100644 --- a/tools/env/fw_env_main.c +++ b/tools/env/fw_env_main.c @@ -42,16 +42,24 @@ #include <stdio.h> #include <string.h> #include <stdlib.h> +#include <getopt.h> #include "fw_env.h"
#define CMD_PRINTENV "fw_printenv" #define CMD_SETENV "fw_setenv"
+static struct option long_options[] = { + {"script", required_argument, NULL, 's'}, + {NULL, 0, NULL, 0} +}; + int main(int argc, char *argv[]) { char *p; char *cmdname = *argv; + char *script_file = NULL; + int c;
if ((p = strrchr (cmdname, '/')) != NULL) { cmdname = p + 1; @@ -65,9 +73,22 @@ main(int argc, char *argv[]) return (EXIT_SUCCESS);
} else if (strcmp(cmdname, CMD_SETENV) == 0) { + while ((c = getopt_long (argc, argv, "s:", + long_options, NULL)) != EOF) { + switch (c) { + case 's': + script_file = optarg; + break; + } + }
- if (fw_setenv (argc, argv) != 0) - return (EXIT_FAILURE); + if (!script_file) { + if (fw_setenv(argc, argv) != 0) + return EXIT_FAILURE; + } else { + if (fw_parse_script(script_file) != 0) + return EXIT_FAILURE; + }
return (EXIT_SUCCESS);