hush: fix some TODOs. TODO in export builting: +250 bytes.
authorDenis Vlasenko <vda.linux@googlemail.com>
Fri, 10 Apr 2009 13:49:10 +0000 (13:49 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Fri, 10 Apr 2009 13:49:10 +0000 (13:49 -0000)
 simplify unexpected EOF handling.

function                                             old     new   delta
builtin_export                                       117     374    +257
o_reset_to_empty_unquoted                              -      21     +21
parse_stream                                        1926    1929      +3
syntax_error_unterm_ch                                31      32      +1
handle_dollar                                        644     641      -3
parse_stream_dquoted                                 307     298      -9
add_till_backquote                                   106      97      -9
add_till_closing_paren                               303     286     -17
o_reset                                               21       -     -21
------------------------------------------------------------------------------
(add/remove: 1/1 grow/shrink: 3/4 up/down: 282/-59)           Total: 223 bytes

shell/hush.c
shell/hush_test/hush-misc/export.right [new file with mode: 0644]
shell/hush_test/hush-misc/export.tests [new file with mode: 0755]

index 422fc63f650d720b949c1d5a827ca3f4b42f3c90..e3dfa0673bf983391825f20cbfce99f1966be18e 100644 (file)
@@ -701,12 +701,19 @@ static void syntax_error_at(unsigned lineno, const char *msg)
        die_if_script(lineno, "syntax error at '%s'", msg);
 }
 
+/* It so happens that all such cases are totally fatal
+ * even if shell is interactive: EOF while looking for closing
+ * delimiter. There is nowhere to read stuff from after that,
+ * it's EOF! The only choice is to terminate.
+ */
+static void syntax_error_unterm_ch(unsigned lineno, char ch) NORETURN;
 static void syntax_error_unterm_ch(unsigned lineno, char ch)
 {
        char msg[2];
        msg[0] = ch;
        msg[1] = '\0';
        die_if_script(lineno, "syntax error: unterminated %s", msg);
+       xfunc_die();
 }
 
 static void syntax_error_unterm_str(unsigned lineno, const char *s)
@@ -1433,7 +1440,7 @@ static void setup_string_in_str(struct in_str *i, const char *s)
  */
 #define B_CHUNK  (32 * sizeof(char*))
 
-static void o_reset(o_string *o)
+static void o_reset_to_empty_unquoted(o_string *o)
 {
        o->length = 0;
        o->o_quoted = 0;
@@ -4043,7 +4050,7 @@ static int done_word(o_string *word, struct parse_context *ctx)
                ) {
                        debug_printf_parse(": checking '%s' for reserved-ness\n", word->data);
                        if (reserved_word(word, ctx)) {
-                               o_reset(word);
+                               o_reset_to_empty_unquoted(word);
                                debug_printf_parse("done_word return %d\n",
                                                (ctx->ctx_res_w == RES_SNTX));
                                return (ctx->ctx_res_w == RES_SNTX);
@@ -4099,7 +4106,7 @@ static int done_word(o_string *word, struct parse_context *ctx)
        }
 #endif
 
-       o_reset(word);
+       o_reset_to_empty_unquoted(word);
 
        debug_printf_parse("done_word return 0\n");
        return 0;
@@ -4249,7 +4256,7 @@ static int redirect_opt_num(o_string *o)
        num = bb_strtou(o->data, NULL, 10);
        if (errno || num < 0)
                return -1;
-       o_reset(o);
+       o_reset_to_empty_unquoted(o);
        return num;
 }
 
@@ -4458,7 +4465,6 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
                bb_error_msg("aha '%s' is a function, parsing it...", dest->data);
                //command->fname = dest->data;
                command->grp_type = GRP_FUNCTION;
-//TODO: review every o_reset() location... do they handle all o_string fields correctly?
                memset(dest, 0, sizeof(*dest));
        }
 #endif
@@ -4510,40 +4516,39 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
 
 #if ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT
 /* Subroutines for copying $(...) and `...` things */
-static int add_till_backquote(o_string *dest, struct in_str *input);
+static void add_till_backquote(o_string *dest, struct in_str *input);
 /* '...' */
-static int add_till_single_quote(o_string *dest, struct in_str *input)
+static void add_till_single_quote(o_string *dest, struct in_str *input)
 {
        while (1) {
                int ch = i_getch(input);
                if (ch == EOF) {
                        syntax_error_unterm_ch('\'');
-                       return 1;
+                       /*xfunc_die(); - redundant */
                }
                if (ch == '\'')
-                       return 0;
+                       return;
                o_addchr(dest, ch);
        }
 }
 /* "...\"...`..`...." - do we need to handle "...$(..)..." too? */
-static int add_till_double_quote(o_string *dest, struct in_str *input)
+static void add_till_double_quote(o_string *dest, struct in_str *input)
 {
        while (1) {
                int ch = i_getch(input);
                if (ch == EOF) {
                        syntax_error_unterm_ch('"');
-                       return 1;
+                       /*xfunc_die(); - redundant */
                }
                if (ch == '"')
-                       return 0;
+                       return;
                if (ch == '\\') {  /* \x. Copy both chars. */
                        o_addchr(dest, ch);
                        ch = i_getch(input);
                }
                o_addchr(dest, ch);
                if (ch == '`') {
-                       if (add_till_backquote(dest, input))
-                               return 1;
+                       add_till_backquote(dest, input);
                        o_addchr(dest, ch);
                        continue;
                }
@@ -4564,22 +4569,22 @@ static int add_till_double_quote(o_string *dest, struct in_str *input)
  * Example                               Output
  * echo `echo '\'TEST\`echo ZZ\`BEST`    \TESTZZBEST
  */
-static int add_till_backquote(o_string *dest, struct in_str *input)
+static void add_till_backquote(o_string *dest, struct in_str *input)
 {
        while (1) {
                int ch = i_getch(input);
                if (ch == EOF) {
                        syntax_error_unterm_ch('`');
-                       return 1;
+                       /*xfunc_die(); - redundant */
                }
                if (ch == '`')
-                       return 0;
+                       return;
                if (ch == '\\') {
                        /* \x. Copy both chars unless it is \` */
                        int ch2 = i_getch(input);
                        if (ch2 == EOF) {
                                syntax_error_unterm_ch('`');
-                               return 1;
+                               /*xfunc_die(); - redundant */
                        }
                        if (ch2 != '`' && ch2 != '$' && ch2 != '\\')
                                o_addchr(dest, ch);
@@ -4600,14 +4605,14 @@ static int add_till_backquote(o_string *dest, struct in_str *input)
  * echo $(echo 'TEST)' BEST)            TEST) BEST
  * echo $(echo \(\(TEST\) BEST)         ((TEST) BEST
  */
-static int add_till_closing_paren(o_string *dest, struct in_str *input, bool dbl)
+static void add_till_closing_paren(o_string *dest, struct in_str *input, bool dbl)
 {
        int count = 0;
        while (1) {
                int ch = i_getch(input);
                if (ch == EOF) {
                        syntax_error_unterm_ch(')');
-                       return 1;
+                       /*xfunc_die(); - redundant */
                }
                if (ch == '(')
                        count++;
@@ -4623,14 +4628,12 @@ static int add_till_closing_paren(o_string *dest, struct in_str *input, bool dbl
                }
                o_addchr(dest, ch);
                if (ch == '\'') {
-                       if (add_till_single_quote(dest, input))
-                               return 1;
+                       add_till_single_quote(dest, input);
                        o_addchr(dest, ch);
                        continue;
                }
                if (ch == '"') {
-                       if (add_till_double_quote(dest, input))
-                               return 1;
+                       add_till_double_quote(dest, input);
                        o_addchr(dest, ch);
                        continue;
                }
@@ -4639,13 +4642,12 @@ static int add_till_closing_paren(o_string *dest, struct in_str *input, bool dbl
                        ch = i_getch(input);
                        if (ch == EOF) {
                                syntax_error_unterm_ch(')');
-                               return 1;
+                               /*xfunc_die(); - redundant */
                        }
                        o_addchr(dest, ch);
                        continue;
                }
        }
-       return 0;
 }
 #endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT */
 
@@ -4786,8 +4788,7 @@ static int handle_dollar(o_string *as_string,
 #  if !BB_MMU
                        pos = dest->length;
 #  endif
-                       if (add_till_closing_paren(dest, input, true))
-                               return 1;
+                       add_till_closing_paren(dest, input, true);
 #  if !BB_MMU
                        if (as_string) {
                                o_addstr(as_string, dest->data + pos);
@@ -4805,8 +4806,7 @@ static int handle_dollar(o_string *as_string,
 #  if !BB_MMU
                pos = dest->length;
 #  endif
-               if (add_till_closing_paren(dest, input, false))
-                       return 1;
+               add_till_closing_paren(dest, input, false);
 #  if !BB_MMU
                if (as_string) {
                        o_addstr(as_string, dest->data + pos);
@@ -4862,8 +4862,7 @@ static int parse_stream_dquoted(o_string *as_string,
        /* note: can't move it above ch == dquote_end check! */
        if (ch == EOF) {
                syntax_error_unterm_ch('"');
-               debug_printf_parse("parse_stream_dquoted return 1: unterminated \"\n");
-               return 1;
+               /*xfunc_die(); - redundant */
        }
        next = '\0';
        if (ch != '\n') {
@@ -4872,11 +4871,9 @@ static int parse_stream_dquoted(o_string *as_string,
        debug_printf_parse(": ch=%c (%d) escape=%d\n",
                                        ch, ch, dest->o_escape);
        if (ch == '\\') {
-//TODO: check interactive behavior
                if (next == EOF) {
                        syntax_error("\\<eof>");
-                       debug_printf_parse("parse_stream_dquoted return 1: \\<eof>\n");
-                       return 1;
+                       xfunc_die();
                }
                /* bash:
                 * "The backslash retains its special meaning [in "..."]
@@ -4904,8 +4901,7 @@ static int parse_stream_dquoted(o_string *as_string,
                //int pos = dest->length;
                o_addchr(dest, SPECIAL_VAR_SYMBOL);
                o_addchr(dest, 0x80 | '`');
-               if (add_till_backquote(dest, input))
-                       return 1;
+               add_till_backquote(dest, input);
                o_addchr(dest, SPECIAL_VAR_SYMBOL);
                //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos);
                goto again;
@@ -4982,16 +4978,17 @@ static struct pipe *parse_stream(char **pstring,
 
                        if (heredoc_cnt) {
                                syntax_error_unterm_str("here document");
-                               goto parse_error;
+                               xfunc_die();
                        }
                        if (done_word(&dest, &ctx)) {
-                               goto parse_error;
+                               xfunc_die();
                        }
                        o_free(&dest);
                        done_pipe(&ctx, PIPE_SEQ);
                        pi = ctx.list_head;
                        /* If we got nothing... */
-// TODO: test script consisting of just "&"
+                       /* (this makes bare "&" cmd a no-op.
+                        * bash says: "syntax error near unexpected token '&'") */
                        if (pi->num_cmds == 0
                            IF_HAS_KEYWORDS( && pi->res_word == RES_NONE)
                        ) {
@@ -5182,7 +5179,7 @@ static struct pipe *parse_stream(char **pstring,
                case '\\':
                        if (next == EOF) {
                                syntax_error("\\<eof>");
-                               goto parse_error;
+                               xfunc_die();
                        }
                        o_addchr(&dest, '\\');
                        ch = i_getch(input);
@@ -5205,7 +5202,7 @@ static struct pipe *parse_stream(char **pstring,
                                ch = i_getch(input);
                                if (ch == EOF) {
                                        syntax_error_unterm_ch('\'');
-                                       goto parse_error;
+                                       /*xfunc_die(); - redundant */
                                }
                                nommu_addchr(&ctx.as_string, ch);
                                if (ch == '\'')
@@ -5232,8 +5229,7 @@ static struct pipe *parse_stream(char **pstring,
 #if !BB_MMU
                        pos = dest.length;
 #endif
-                       if (add_till_backquote(&dest, input))
-                               goto parse_error;
+                       add_till_backquote(&dest, input);
 #if !BB_MMU
                        o_addstr(&ctx.as_string, dest.data + pos);
                        o_addchr(&ctx.as_string, '`');
@@ -5976,7 +5972,8 @@ static int builtin_cd(char **argv)
        const char *newdir = argv[1];
        if (newdir == NULL) {
                /* bash does nothing (exitcode 0) if HOME is ""; if it's unset,
-                * bash says "bash: cd: HOME not set" and does nothing (exitcode 1)
+                * bash says "bash: cd: HOME not set" and does nothing
+                * (exitcode 1)
                 */
                newdir = getenv("HOME") ? : "/";
        }
@@ -6024,14 +6021,42 @@ static int builtin_exit(char **argv)
 static int builtin_export(char **argv)
 {
        if (*++argv == NULL) {
-               // TODO:
-               // ash emits: export VAR='VAL'
-               // bash: declare -x VAR="VAL"
-               // (both also escape as needed (quotes, $, etc))
                char **e = environ;
-               if (e)
-                       while (*e)
+               if (e) {
+                       while (*e) {
+#if 0
                                puts(*e++);
+#else
+                               /* ash emits: export VAR='VAL'
+                                * bash: declare -x VAR="VAL"
+                                * we follow ash example */
+                               const char *s = *e++;
+                               const char *p = strchr(s, '=');
+
+                               if (!p) /* wtf? take next variable */
+                                       continue;
+                               /* export var= */
+                               printf("export %.*s", (int)(p - s) + 1, s);
+                               s = p + 1;
+                               while (*s) {
+                                       if (*s != '\'') {
+                                               p = strchrnul(s, '\'');
+                                               /* print 'xxxx' */
+                                               printf("'%.*s'", (int)(p - s), s);
+                                               if (*p == '\0')
+                                                       break;
+                                               s = p;
+                                       }
+                                       /* s points to '; print ''...'''" */
+                                       putchar('"');
+                                       do putchar('\''); while (*++s == '\'');
+                                       putchar('"');
+                               }
+                               putchar('\n');
+#endif
+                       }
+                       fflush(stdout);
+               }
                return EXIT_SUCCESS;
        }
 
diff --git a/shell/hush_test/hush-misc/export.right b/shell/hush_test/hush-misc/export.right
new file mode 100644 (file)
index 0000000..4df2e38
--- /dev/null
@@ -0,0 +1,6 @@
+export aaa1="'''"
+export aaa2=
+export aaa3="'''"'abc'
+export aaa4='def'"'''"
+export aaa5="'''"'abc'"'''"'def'"'''"
+Done
diff --git a/shell/hush_test/hush-misc/export.tests b/shell/hush_test/hush-misc/export.tests
new file mode 100755 (executable)
index 0000000..87a27ec
--- /dev/null
@@ -0,0 +1,7 @@
+export aaa1="'''"
+export aaa2=""
+export aaa3="'''abc"
+export aaa4="def'''"
+export aaa5="'''abc'''def'''"
+export | grep aaa.=
+echo Done