[U-Boot-Users] [patch #10/11] HUSH parser fixes.

Hello
The following patch fixes various shortcomings in the hush parser.
1. A new test command was added. This is a simplified version of the one in the sh shell.
2. A new exit command was added which terminates the current executing script.
3. Handing of $? (exit code of last executed command), was wrong. Instead of being evaluated in the proper place it was evaluated once at the start of the evaluation.
For example the following script worked like this.
test 1 -eq 2 # set $? to 1 echo $? # outputs 1 test 1 -eq 1 # set $? to 0 echo $? # still outputs 1
Now it is fixed.
4. The simplified parser uses '(' and ')' for enclosing variables. This caused problems when migrating to hush because hash uses '{' and '}'. Modified to accept parentheses too.
Regards
Pantelis
README | 5 + common/command.c | 153 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ common/hush.c | 93 ++++++++++++++++++++++++++++++--- 3 files changed, 242 insertions(+), 9 deletions(-)
diff -ruNb u-boot-original/README u-boot-parser/README --- u-boot-original/README 2004-03-30 14:37:28.395972064 +0300 +++ u-boot-parser/README 2004-03-30 15:54:35.451552696 +0300 @@ -1270,6 +1270,11 @@ of the backslashes before semicolons and special symbols.
+ Note 2: + In order to allow compatibility between the original + parser we also allow to enclose variables with '(' + and ')'. Example $ipaddr = ${ipaddr} = $(ipaddr). + - Default Environment: CONFIG_EXTRA_ENV_SETTINGS
diff -ruNb u-boot-original/common/command.c u-boot-parser/common/command.c --- u-boot-original/common/command.c 2004-03-30 14:37:28.665931024 +0300 +++ u-boot-parser/common/command.c 2004-03-30 15:37:59.834909432 +0300 @@ -74,6 +74,159 @@ " - echo args to console; \c suppresses newline\n" );
+#ifdef CFG_HUSH_PARSER + +int +do_test (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + char **ap; + int left, adv, expr, last_expr, neg, last_cmp; + + /* args? */ + if (argc < 3) + return 1; + +#if 0 + { + printf("test:"); + left = 1; + while (argv[left]) + printf(" %s", argv[left++]); + } +#endif + + last_expr = 0; + left = argc - 1; ap = argv + 1; + if (left > 0 && strcmp(ap[0], "!") == 0) { + neg = 1; + ap++; + left--; + } else + neg = 0; + + expr = -1; + last_cmp = -1; + last_expr = -1; + while (left > 0) { + + if (strcmp(ap[0], "-o") == 0 || strcmp(ap[0], "-a") == 0) + adv = 1; + else if (strcmp(ap[0], "-z") == 0 || strcmp(ap[0], "-n") == 0) + adv = 2; + else + adv = 3; + + if (left < adv) { + expr = 1; + break; + } + + if (adv == 1) { + if (strcmp(ap[0], "-o") == 0) { + last_expr = expr; + last_cmp = 0; + } else if (strcmp(ap[0], "-a") == 0) { + last_expr = expr; + last_cmp = 1; + } else { + expr = 1; + break; + } + } + + if (adv == 2) { + if (strcmp(ap[0], "-z") == 0) + expr = strlen(ap[1]) == 0 ? 0 : 1; + else if (strcmp(ap[0], "-n") == 0) + expr = strlen(ap[1]) == 0 ? 1 : 0; + else { + expr = 1; + break; + } + + if (last_cmp == 0) + expr = last_expr || expr; + else if (last_cmp == 1) + expr = last_expr && expr; + last_cmp = -1; + } + + if (adv == 3) { + if (strcmp(ap[1], "=") == 0) + expr = strcmp(ap[0], ap[2]) == 0; + else if (strcmp(ap[1], "!=") == 0) + expr = strcmp(ap[0], ap[2]) != 0; + else if (strcmp(ap[1], ">") == 0) + expr = strcmp(ap[0], ap[2]) > 0; + else if (strcmp(ap[1], "<") == 0) + expr = strcmp(ap[0], ap[2]) < 0; + else if (strcmp(ap[1], "-eq") == 0) + expr = simple_strtol(ap[0], NULL, 10) == simple_strtol(ap[2], NULL, 10); + else if (strcmp(ap[1], "-ne") == 0) + expr = simple_strtol(ap[0], NULL, 10) != simple_strtol(ap[2], NULL, 10); + else if (strcmp(ap[1], "-lt") == 0) + expr = simple_strtol(ap[0], NULL, 10) < simple_strtol(ap[2], NULL, 10); + else if (strcmp(ap[1], "-le") == 0) + expr = simple_strtol(ap[0], NULL, 10) <= simple_strtol(ap[2], NULL, 10); + else if (strcmp(ap[1], "-gt") == 0) + expr = simple_strtol(ap[0], NULL, 10) > simple_strtol(ap[2], NULL, 10); + else if (strcmp(ap[1], "-ge") == 0) + expr = simple_strtol(ap[0], NULL, 10) >= simple_strtol(ap[2], NULL, 10); + else { + expr = 1; + break; + } + + if (last_cmp == 0) + expr = last_expr || expr; + else if (last_cmp == 1) + expr = last_expr && expr; + last_cmp = -1; + } + + ap += adv; left -= adv; + } + + if (neg) + expr = !expr; + + expr = !expr; + +#if 0 + printf(": returns %d\n", expr); +#endif + + return expr; +} + +U_BOOT_CMD( + test, CFG_MAXARGS, 1, do_test, + "test - minimal test like /bin/sh\n", + "[args..]\n" + " - test functionality\n" +); + +int +do_exit (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[]) +{ + int r; + + r = 0; + if (argc > 1) + r = simple_strtoul(argv[1], NULL, 10); + + return -r - 2; +} + +U_BOOT_CMD( + exit, 2, 1, do_exit, + "exit - exit script\n", + " - exit functionality\n" +); + + +#endif + /* * Use puts() instead of printf() to avoid printf buffer overflow * for long help messages diff -ruNb u-boot-original/common/hush.c u-boot-parser/common/hush.c --- u-boot-original/common/hush.c 2004-03-30 14:37:28.669930416 +0300 +++ u-boot-parser/common/hush.c 2004-03-30 15:37:59.850907000 +0300 @@ -290,6 +290,7 @@ unsigned int global_argc; #endif unsigned int last_return_code; +int nesting_level; #ifndef __U_BOOT__ extern char **environ; /* This is in <unistd.h>, but protected with __USE_GNU */ #endif @@ -416,7 +417,9 @@ static int b_addchr(o_string *o, int ch); static void b_reset(o_string *o); static int b_addqchr(o_string *o, int ch, int quote); +#ifndef __U_BOOT__ static int b_adduint(o_string *o, unsigned int i); +#endif /* in_str manipulations: */ static int static_get(struct in_str *i); static int static_peek(struct in_str *i); @@ -936,6 +939,7 @@ return p + 1; }
+#ifndef __U_BOOT__ static int b_adduint(o_string *o, unsigned int i) { int r; @@ -944,6 +948,7 @@ do r=b_addchr(o, *p++); while (r==0 && *p); return r; } +#endif
static int static_get(struct in_str *i) { @@ -1921,6 +1926,10 @@ } last_return_code=rcode; #else + if (rcode < -1) { + last_return_code = -rcode - 2; + return -2; /* exit */ + } last_return_code=(rcode == 0) ? 0 : 1; #endif #ifndef __U_BOOT__ @@ -2145,6 +2154,10 @@ } #endif
+#ifdef __U_BOOT__ +static char *get_dollar_var(char ch); +#endif + /* This is used to get/check local shell variables */ static char *get_local_var(const char *s) { @@ -2152,6 +2165,12 @@
if (!s) return NULL; + +#ifdef __U_BOOT__ + if (*s == '$') + return get_dollar_var(s[1]); +#endif + for (cur = top_vars; cur; cur=cur->next) if(strcmp(cur->name, s)==0) return cur->value; @@ -2168,12 +2187,19 @@ int result=0; struct variables *cur;
+#ifdef __U_BOOT__ + /* might be possible! */ + if (!isalpha(*s)) + return -1; +#endif + name=strdup(s);
#ifdef __U_BOOT__ if (getenv(name) != NULL) { printf ("ERROR: " "There is a global environment variable with the same name.\n"); + free(name); return -1; } #endif @@ -2278,7 +2304,10 @@
static int is_assignment(const char *s) { - if (s==NULL || !isalpha(*s)) return 0; + if (s == NULL) + return 0; + + if (!isalpha(*s)) return 0; ++s; while(isalnum(*s) || *s=='_') ++s; return *s=='='; @@ -2749,15 +2778,35 @@ * see the bash man page under "Parameter Expansion" */ static char *lookup_param(char *src) { - char *p=NULL; - if (src) { + char *p; + + if (!src) + return NULL; + p = getenv(src); if (!p) p = get_local_var(src); - } + return p; }
+#ifdef __U_BOOT__ +static char *get_dollar_var(char ch) +{ + static char buf[40]; + + buf[0] = '\0'; + switch (ch) { + case '?': + sprintf(buf, "%u", (unsigned int)last_return_code); + break; + default: + return NULL; + } + return buf; +} +#endif + /* return code: 0 for OK, 1 for syntax error */ static int handle_dollar(o_string *dest, struct p_context *ctx, struct in_str *input) { @@ -2769,6 +2818,7 @@ #ifndef __U_BOOT__ char sep[]=" "; #endif + char match; int ch = input->peek(input); /* first character after the $ */ debug_printf("handle_dollar: ch=%c\n",ch); if (isalpha(ch)) { @@ -2799,7 +2849,15 @@ break; #endif case '?': +#ifndef __U_BOOT__ b_adduint(dest,last_return_code); +#else + ctx->child->sp++; + b_addchr(dest, SPECIAL_VAR_SYMBOL); + b_addchr(dest, '$'); + b_addchr(dest, '?'); + b_addchr(dest, SPECIAL_VAR_SYMBOL); +#endif advance = 1; break; #ifndef __U_BOOT__ @@ -2809,14 +2867,18 @@ break; #endif case '{': +#ifdef __U_BOOT__ + case '(': +#endif + match = ch == '{' ? '}' : ')'; b_addchr(dest, SPECIAL_VAR_SYMBOL); ctx->child->sp++; b_getch(input); /* XXX maybe someone will try to escape the '}' */ - while(ch=b_getch(input),ch!=EOF && ch!='}') { + while(ch=b_getch(input),ch!=EOF && ch!=match) { b_addchr(dest,ch); } - if (ch != '}') { + if (ch != match) { syntax(); return 1; } @@ -2885,8 +2947,10 @@ if (input->__promptme == 0) return 1; #endif next = (ch == '\n') ? 0 : b_peek(input); - debug_printf("parse_stream: ch=%c (%d) m=%d quote=%d\n", - ch,ch,m,dest->quote); + + debug_printf("parse_stream: ch=%c (%d) m=%d quote=%d - %c\n", + ch >= ' ' ? ch : '.',ch,m,dest->quote,ctx->stack == NULL ? '*' : '.'); + if (m==0 || ((m==1 || m==2) && dest->quote)) { b_addqchr(dest, ch, dest->quote); } else { @@ -3107,7 +3171,18 @@ #ifndef __U_BOOT__ run_list(ctx.list_head); #else - if (((code = run_list(ctx.list_head)) == -1)) + code = run_list(ctx.list_head); + if (code == -2) { /* exit */ + b_free(&temp); + code = 0; + /* XXX hackish way to not allow exit from main loop */ + if (inp->peek == file_peek) { + printf("exit not allowed from main input shell.\n"); + continue; + } + break; + } + if (code == -1) flag_repeat = 0; #endif } else {

In message 406988DC.7040408@intracom.gr you wrote:
The following patch fixes various shortcomings in the hush parser.
A new test command was added. This is a simplified version of the one in the sh shell.
A new exit command was added which terminates the current executing script.
Handing of $? (exit code of last executed command), was wrong. Instead of being evaluated in the proper place it was evaluated once at the start of the evaluation.
For example the following script worked like this.
test 1 -eq 2 # set $? to 1 echo $? # outputs 1 test 1 -eq 1 # set $? to 0 echo $? # still outputs 1
Now it is fixed.
Can you please re-submit a patch whioch included these 3 items only?
- The simplified parser uses '(' and ')' for enclosing variables. This caused problems when migrating to hush because hash uses '{' and '}'. Modified to accept parentheses too.
Sorry, but I reject this patch. I want hush to stay as bourne shell compatible as possible. I'd much rather fix the simple command line parser to use ``${...}'' instead.
Best regards,
Wolfgang Denk

Wolfgang Denk wrote:
In message 406988DC.7040408@intracom.gr you wrote:
The following patch fixes various shortcomings in the hush parser.
- A new test command was added. This is a simplified
version of the one in the sh shell.
- A new exit command was added which terminates the
current executing script.
- Handing of $? (exit code of last executed command), was
wrong. Instead of being evaluated in the proper place it was evaluated once at the start of the evaluation.
For example the following script worked like this.
test 1 -eq 2 # set $? to 1 echo $? # outputs 1 test 1 -eq 1 # set $? to 0 echo $? # still outputs 1
Now it is fixed.
Can you please re-submit a patch whioch included these 3 items only?
Sure, coming right up.
- The simplified parser uses '(' and ')' for enclosing variables.
This caused problems when migrating to hush because hash uses '{' and '}'. Modified to accept parentheses too.
Sorry, but I reject this patch. I want hush to stay as bourne shell compatible as possible. I'd much rather fix the simple command line parser to use ``${...}'' instead.
Could I at least make it a compile option? We have a number of boards in the field that would like to upgrade to hush without changing the configuration.
Best regards,
Wolfgang Denk
Regards
Pantelis

In message 407F8A48.7010100@intracom.gr you wrote:
Sorry, but I reject this patch. I want hush to stay as bourne shell compatible as possible. I'd much rather fix the simple command line parser to use ``${...}'' instead.
Could I at least make it a compile option? We have a number of boards in the field that would like to upgrade to hush without changing the configuration.
Please see my previous message about this. If you upgrade to a new version of U-Boot anyway, you can for example use a standalone tool that is run once to convert the environment.
Best regards,
Wolfgang Denk
participants (2)
-
Pantelis Antoniou
-
Wolfgang Denk