diff --git a/tools/env/fw_env.c b/tools/env/fw_env.c index a46205d868..04f3bf0a43 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); \ @@ -210,7 +209,6 @@ static char default_environment[] = { static int flash_io (int mode); static char *envmatch (char * s1, char * s2); -static int env_init (void); static int parse_config (void); #if defined(CONFIG_FILE) @@ -225,6 +223,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. @@ -233,7 +247,7 @@ char *fw_getenv (char *name) { char *env, *nxt; - if (env_init ()) + if (fw_env_open()) return NULL; for (env = environment.data; *env; env = nxt + 1) { @@ -264,7 +278,7 @@ int fw_printenv (int argc, char *argv[]) int i, n_flag; int rc = 0; - if (env_init ()) + if (fw_env_open()) return -1; if (argc == 1) { /* Print all env variables */ @@ -327,30 +341,34 @@ 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_close(void) { - int i, len; - char *env, *nxt; - char *oldval = NULL; - char *name; + /* + * Update CRC + */ + *environment.crc = crc32(0, (uint8_t *) environment.data, ENV_SIZE); - if (argc < 2) { - errno = EINVAL; - return -1; + /* 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 +376,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 +414,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 +429,202 @@ 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; +} - /* - * Update CRC - */ - *environment.crc = crc32 (0, (uint8_t *) environment.data, ENV_SIZE); +/* + * 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 *name; + char *value = NULL; + char *tmpval = NULL; - /* write environment back to flash */ - if (flash_io (O_RDWR)) { - fprintf (stderr, "Error: can't write fw_env to flash\n"); + if (argc < 2) { + errno = EINVAL; return -1; } - return 0; + if (fw_env_open()) { + fprintf(stderr, "Error: environment not initialized\n"); + return -1; + } + + name = argv[1]; + + len = strlen(name) + 2; + for (i = 2; i < argc; ++i) + len += strlen(argv[i]) + 1; + + /* 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: + * variable_namevariable_value + * + * Both variable_name and variable_value are interpreted as strings. + * Any character after 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)); + ret = -1; + break; + } + + } + + /* Close file if not stdin */ + if (strcmp(fname, "-") != 0) + fclose(fp); + + ret |= fw_env_close(); + + return ret; + } /* @@ -880,7 +1059,7 @@ static char *envmatch (char * s1, char * s2) /* * Prevent confusion if running from erased flash memory */ -static int env_init (void) +int fw_env_open(void) { int crc0, crc0_ok; char flag0; diff --git a/tools/env/fw_env.h b/tools/env/fw_env.h index c04da541fb..8130fa150c 100644 --- a/tools/env/fw_env.h +++ b/tools/env/fw_env.h @@ -50,5 +50,9 @@ 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 int fw_env_open(void); +extern int fw_env_write(char *name, char *value); +extern int fw_env_close(void); 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 7f631c4494..82116b4b29 100644 --- a/tools/env/fw_env_main.c +++ b/tools/env/fw_env_main.c @@ -42,34 +42,87 @@ #include #include #include +#include #include "fw_env.h" #define CMD_PRINTENV "fw_printenv" #define CMD_SETENV "fw_setenv" +static struct option long_options[] = { + {"script", required_argument, NULL, 's'}, + {"help", no_argument, NULL, 'h'}, + {NULL, 0, NULL, 0} +}; + +void usage(void) +{ + + fprintf(stderr, "fw_printenv/fw_setenv, " + "a command line interface to U-Boot environment\n\n" + "usage:\tfw_printenv\n" + "\tfw_setenv [variable name] [variable value]\n" + "\tfw_setenv -s [ file ]\n" + "\tfw_setenv -s - < [ file ]\n\n" + "The file passed as argument contains only pairs " + "name / value\n" + "Example:\n" + "# Any line starting with # is treated as comment\n" + "\n" + "\t netdev eth0\n" + "\t kernel_addr 400000\n" + "\t var1\n" + "\t var2 The quick brown fox jumps over the " + "lazy dog\n" + "\n" + "A variable without value will be dropped. It is possible\n" + "to put any number of spaces between the fields, but any\n" + "space inside the value is treated as part of the value " + "itself.\n\n" + ); +} + 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; } + while ((c = getopt_long (argc, argv, "s:h", + long_options, NULL)) != EOF) { + switch (c) { + case 's': + script_file = optarg; + break; + case 'h': + usage(); + return EXIT_SUCCESS; + } + } + + if (strcmp(cmdname, CMD_PRINTENV) == 0) { if (fw_printenv (argc, argv) != 0) - return (EXIT_FAILURE); + return EXIT_FAILURE; - return (EXIT_SUCCESS); + return EXIT_SUCCESS; } else if (strcmp(cmdname, CMD_SETENV) == 0) { + if (!script_file) { + if (fw_setenv(argc, argv) != 0) + return EXIT_FAILURE; + } else { + if (fw_parse_script(script_file) != 0) + return EXIT_FAILURE; + } - if (fw_setenv (argc, argv) != 0) - return (EXIT_FAILURE); - - return (EXIT_SUCCESS); + return EXIT_SUCCESS; } @@ -77,5 +130,5 @@ main(int argc, char *argv[]) "Identity crisis - may be called as `" CMD_PRINTENV "' or as `" CMD_SETENV "' but not as `%s'\n", cmdname); - return (EXIT_FAILURE); + return EXIT_FAILURE; }