]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
rewrote arglist to argv conversion and added $@, $* support
authorBVK Chaitanya <bvk.groups@gmail.com>
Wed, 12 May 2010 04:49:12 +0000 (10:19 +0530)
committerBVK Chaitanya <bvk.groups@gmail.com>
Wed, 12 May 2010 04:49:12 +0000 (10:19 +0530)
conf/common.rmk
include/grub/script_sh.h
script/argv.c [new file with mode: 0644]
script/execute.c
script/yylex.l
tests/grub_script_echo1.in
tests/grub_script_functions.in
tests/grub_script_vars1.in

index 4b39e9b71a1247e49a0598a8f287c7bb979e9282..54146904bc9121b4f8d3f9eadac4955e2e90c0a9 100644 (file)
@@ -660,7 +660,7 @@ normal_mod_CFLAGS = $(COMMON_CFLAGS)
 normal_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
 # For sh.mod.
-sh_mod_SOURCES = script/main.c script/script.c script/execute.c \
+sh_mod_SOURCES = script/main.c script/script.c script/argv.c script/execute.c \
        script/function.c script/lexer.c grub_script.tab.c grub_script.yy.c
 sh_mod_CFLAGS = $(COMMON_CFLAGS) $(POSIX_CFLAGS) -Wno-error
 sh_mod_LDFLAGS = $(COMMON_LDFLAGS)
index e1edbec156c7f7e57135d5eac1baab425a3e0424..5455fc763436c7243d8a57b8c4e341c59ec26b46 100644 (file)
@@ -63,6 +63,13 @@ struct grub_script_arg
   struct grub_script_arg *next;
 };
 
+/* An argument vector.  */
+struct grub_script_argv
+{
+  int argc;
+  char **args;
+};
+
 /* A complete argument.  It consists of a list of one or more `struct
    grub_script_arg's.  */
 struct grub_script_arglist
@@ -215,6 +222,11 @@ struct grub_parser_param
   struct grub_lexer_param *lexerstate;
 };
 
+void grub_script_argv_free    (struct grub_script_argv *argv);
+int grub_script_argv_next     (struct grub_script_argv *argv);
+int grub_script_argv_append   (struct grub_script_argv *argv, const char *s);
+int grub_script_argv_split_append (struct grub_script_argv *argv, char *s);
+
 struct grub_script_arglist *
 grub_script_create_arglist (struct grub_parser_param *state);
 
diff --git a/script/argv.c b/script/argv.c
new file mode 100644 (file)
index 0000000..1ac81f4
--- /dev/null
@@ -0,0 +1,128 @@
+/* argv.c - methods for constructing argument vector */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2010  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/mm.h>
+#include <grub/script_sh.h>
+
+#define ARG_ALLOCATION_UNIT  (32 * sizeof (char))
+#define ARGV_ALLOCATION_UNIT (8 * sizeof (void*))
+
+void
+grub_script_argv_free (struct grub_script_argv *argv)
+{
+  int i;
+
+  if (argv->args)
+    {
+      for (i = 0; i < argv->argc; i++)
+       grub_free (argv->args[i]);
+
+      grub_free (argv->args);
+    }
+
+  argv->argc = 0;
+  argv->args = 0;
+}
+
+/* Prepare for next argc.  */
+int
+grub_script_argv_next (struct grub_script_argv *argv)
+{
+  char **p = argv->args;
+
+  if (argv->argc == 0)
+    {
+      p = grub_malloc (ALIGN_UP (2 * sizeof (char *), ARG_ALLOCATION_UNIT));
+      if (! p)
+       return 1;
+
+      argv->argc = 1;
+      argv->args = p;
+      argv->args[0] = 0;
+      argv->args[1] = 0;
+      return 0;
+    }
+
+  if (! argv->args[argv->argc - 1])
+    return 0;
+
+  p = grub_realloc (p, ALIGN_UP ((argv->argc + 1) * sizeof (char *),
+                                ARG_ALLOCATION_UNIT));
+  if (! p)
+    return 1;
+
+  argv->argc++;
+  argv->args = p;
+  argv->args[argv->argc] = 0;
+  return 0;
+}
+
+/* Append `s' to the last argument.  */
+int
+grub_script_argv_append (struct grub_script_argv *argv, const char *s)
+{
+  int a, b;
+  char *p = argv->args[argv->argc - 1];
+
+  if (! s)
+    return 0;
+
+  a = p ? grub_strlen (p) : 0;
+  b = grub_strlen (s);
+
+  p = grub_realloc (p, ALIGN_UP ((a + b + 1) * sizeof (char),
+                                ARG_ALLOCATION_UNIT));
+  if (! p)
+    return 1;
+
+  grub_strcpy (p + a, s);
+  argv->args[argv->argc - 1] = p;
+  return 0;
+}
+
+/* Split `s' and append words as multiple arguments.  */
+int
+grub_script_argv_split_append (struct grub_script_argv *argv, char *s)
+{
+  char ch;
+  char *p;
+  int errors = 0;
+
+  if (! s)
+    return 0;
+
+  while (! errors && *s)
+    {
+      p = s;
+      while (*s && ! grub_isspace (*s))
+       s++;
+
+      ch = *s;
+      *s = '\0';
+      errors += grub_script_argv_append (argv, p);
+      *s = ch;
+
+      while (*s && grub_isspace (*s))
+       s++;
+
+      if (*s)
+       errors += grub_script_argv_next (argv);
+    }
+  return errors;
+}
index 573dab4cb2990740d876e12af4a9b25eeff515d6..905f457d379c0cb71b968b62754b5cfed2331a6b 100644 (file)
 /* Scope for grub script functions.  */
 struct grub_script_scope
 {
-  char **args;
-  unsigned int argc;
+  struct grub_script_argv argv;
 };
 static struct grub_script_scope *scope = 0;
 
-static char *
-grub_script_env_get (const char *name)
+static int
+grub_env_special (const char *name)
 {
-  char *p = 0;
-  unsigned long num = 0;
+  if (grub_isdigit (name[0]) ||
+      grub_strcmp (name, "#") == 0 ||
+      grub_strcmp (name, "*") == 0 ||
+      grub_strcmp (name, "@") == 0)
+    return 1;
+  return 0;
+}
 
-  if (! scope)
-    return grub_env_get (name);
+static char **
+grub_script_env_get (const char *name, grub_script_arg_type_t type)
+{
+  int errors = 0;
+  struct grub_script_argv result = { 0, 0 };
 
-  if (grub_isdigit (name[0]))
+  errors += grub_script_argv_next (&result);
+  if (! grub_env_special (name))
     {
-      num = grub_strtoul (name, &p, 10);
-      if (p && *p == '\0')
-       {
-         if (num == 0)
-           return 0; /* XXX no file name, for now.  */
-
-         return (num > scope->argc ? 0 : scope->args[num - 1]);
-       }
-      else
+      char *v = grub_env_get (name);
+      if (v && v[0])
        {
-         grub_error (GRUB_ERR_BAD_ARGUMENT, "bad variabe name substitution");
-         return 0;
+         if (type == GRUB_SCRIPT_ARG_TYPE_VAR)
+           errors += grub_script_argv_split_append (&result, v);
+         else
+           errors += grub_script_argv_append (&result, v);
        }
     }
+  else if (! scope)
+    errors += grub_script_argv_append (&result, 0);
+
   else if (grub_strcmp (name, "#") == 0)
     {
-      static char buf[32]; /* Rewritten everytime.  */
-      grub_snprintf (buf, sizeof (buf), "%u", scope->argc);
-      return buf;
+      char buffer[ERRNO_DIGITS_MAX + 1];
+      grub_snprintf (buffer, sizeof (buffer), "%u", scope->argv.argc);
+      errors += grub_script_argv_append (&result, buffer);
+    }
+  else if (grub_strcmp (name, "*") == 0)
+    {
+      int i;
+
+      for (i = 0; ! errors && i < scope->argv.argc; i++)
+       if (type == GRUB_SCRIPT_ARG_TYPE_VAR)
+         {
+           if (i != 0)
+             errors += grub_script_argv_next (&result);
+           errors += grub_script_argv_split_append (&result,
+                                                    scope->argv.args[i]);
+         }
+       else
+         {
+           if (i != 0)
+             errors += grub_script_argv_append (&result, " ");
+           errors += grub_script_argv_append (&result,
+                                              scope->argv.args[i]);
+         }
+    }
+  else if (grub_strcmp (name, "@") == 0)
+    {
+      int i;
+
+      for (i = 0; ! errors && i < scope->argv.argc; i++)
+       {
+         if (i != 0)
+           errors += grub_script_argv_next (&result);
+
+         if (type == GRUB_SCRIPT_ARG_TYPE_VAR)
+           errors += grub_script_argv_split_append (&result,
+                                                    scope->argv.args[i]);
+         else
+           errors += grub_script_argv_append (&result,
+                                              scope->argv.args[i]);
+       }
     }
   else
-    return grub_env_get (name);
+    {
+      unsigned long num = grub_strtoul (name, 0, 10);
+      if (num == 0)
+       ; /* XXX no file name, for now.  */
+
+      else if (num <= scope->argv.argc)
+       {
+         if (type == GRUB_SCRIPT_ARG_TYPE_VAR)
+           errors += grub_script_argv_split_append (&result,
+                                                    scope->argv.args[num - 1]);
+         else
+           errors += grub_script_argv_append (&result,
+                                              scope->argv.args[num - 1]);
+       }
+    }
+  return result.args;
 }
 
 static grub_err_t
 grub_script_env_set (const char *name, const char *val)
 {
-  if (grub_isdigit (name[0]) || grub_strcmp (name, "#") == 0)
+  if (grub_env_special (name))
     return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad variable name");
 
   return grub_env_set (name, val);
 }
 
-static grub_err_t
-grub_script_execute_cmd (struct grub_script_cmd *cmd)
-{
-  int ret;
-  char errnobuf[ERRNO_DIGITS_MAX + 1];
-
-  if (cmd == 0)
-    return 0;
-
-  ret = cmd->exec (cmd);
-
-  grub_snprintf (errnobuf, sizeof (errnobuf), "%d", ret);
-  grub_env_set ("?", errnobuf);
-  return ret;
-}
-
-#define ARG_ALLOCATION_UNIT  (32 * sizeof (char))
-#define ARGV_ALLOCATION_UNIT (8 * sizeof (void*))
-
 /* Expand arguments in ARGLIST into multiple arguments.  */
-char **
-grub_script_execute_arglist_to_argv (struct grub_script_arglist *arglist, int *count)
+static int
+grub_script_arglist_to_argv (struct grub_script_arglist *arglist,
+                            struct grub_script_argv *argv)
 {
   int i;
-  int oom;
-  int argc;
-  int empty;
-  char *ptr;
-  char **argv;
-  char *value;
-  struct grub_script_arg *arg;
-
-  auto void push (char *str);
-  void push (char *str)
-  {
-    char **p;
-
-    if (oom)
-      return;
-
-    p = grub_realloc (argv, ALIGN_UP (sizeof(char*) * (argc + 1), ARGV_ALLOCATION_UNIT));
-    if (!p)
-      oom = 1;
-    else
-      {
-       p[argc++] = str;
-       argv = p;
-      }
-  }
-
-  auto char* append (const char *str, grub_size_t nchar);
-  char* append (const char *str, grub_size_t nchar)
-  {
-    int len;
-    int old;
-    char *p;
-
-    if (oom || !str)
-      return 0;
-
-    len = nchar ?: grub_strlen (str);
-    old = argv[argc - 1] ? grub_strlen (argv[argc - 1]) : 0;
-    p = grub_realloc (argv[argc - 1], ALIGN_UP(old + len + 1, ARG_ALLOCATION_UNIT));
-
-    if (p)
-      {
-       grub_strncpy (p + old, str, len);
-       p[old + len] = '\0';
-      }
-    else
-      {
-       oom = 1;
-       grub_free (argv[argc - 1]);
-      }
-    argv[argc - 1] = p;
-    return argv[argc - 1];
-  }
-
-  /* Move *STR to the begining of next word, but return current word.  */
-  auto char* move_to_next (char **str);
-  char* move_to_next (char **str)
-  {
-    char *end;
-    char *start;
-
-    if (oom || !str || !*str)
-      return 0;
-
-    start = *str;
-    while (*start && grub_isspace (*start)) start++;
-    if (*start == '\0')
-      return 0;
-
-    end = start + 1;
-    while (*end && !grub_isspace (*end)) end++;
-
-    *str = end;
-    return start;
-  }
-
-  oom = 0;
-  argv = 0;
-  argc = 0;
-  push (0);
-  for (; arglist; arglist = arglist->next)
+  int error = 0;
+  char **values = 0;
+  struct grub_script_arg *arg = 0;
+  struct grub_script_argv result = { 0, 0 };
+
+  for (; error == 0 && arglist && arglist->arg; arglist = arglist->next)
     {
-      empty = 1;
+      error += grub_script_argv_next (&result);
+
       arg = arglist->arg;
       while (arg)
        {
+         if (error)
+           break;
+
          switch (arg->type)
            {
            case GRUB_SCRIPT_ARG_TYPE_VAR:
-             value = grub_script_env_get (arg->str);
-             while (value && *value && (ptr = move_to_next(&value)))
+           case GRUB_SCRIPT_ARG_TYPE_DQVAR:
+             values = grub_script_env_get (arg->str, arg->type);
+             for (i = 0; values && values[i]; i++)
                {
-                 empty = 0;
-                 append (ptr, value - ptr);
-                 if (*value) push(0);
+                 if (i != 0)
+                   error += grub_script_argv_next (&result);
+                 error += grub_script_argv_append (&result, values[i]);
                }
+             grub_free (values);
              break;
 
            case GRUB_SCRIPT_ARG_TYPE_TEXT:
-             if (grub_strlen (arg->str) > 0)
-               {
-                 empty = 0;
-                 append (arg->str, 0);
-               }
+             if (grub_strlen (arg->str))
+               error += grub_script_argv_append (&result, arg->str);
              break;
 
            case GRUB_SCRIPT_ARG_TYPE_DQSTR:
            case GRUB_SCRIPT_ARG_TYPE_SQSTR:
-             empty = 0;
-             append (arg->str, 0);
-             break;
-
-           case GRUB_SCRIPT_ARG_TYPE_DQVAR:
-             empty = 0;
-             append (grub_script_env_get (arg->str), 0);
+             error += grub_script_argv_append (&result, arg->str);
              break;
            }
          arg = arg->next;
        }
-      if (!empty)
-       push (0);
     }
 
-  if (oom)
-    {
-      for (i = 0; i < argc; i++)
-       grub_free (argv[i]);
-      grub_free (argv);
-      argv = 0;
-    }
+  if (error)
+    return 1;
+
+  if (! result.args[result.argc - 1])
+    result.argc--;
 
-  if (argv)
-    *count = argc - 1;
+  *argv = result;
+  return 0;
+}
+
+static grub_err_t
+grub_script_execute_cmd (struct grub_script_cmd *cmd)
+{
+  int ret;
+  char errnobuf[ERRNO_DIGITS_MAX + 1];
+
+  if (cmd == 0)
+    return 0;
+
+  ret = cmd->exec (cmd);
 
-  return argv;
+  grub_snprintf (errnobuf, sizeof (errnobuf), "%d", ret);
+  grub_env_set ("?", errnobuf);
+  return ret;
 }
 
 /* Execute a function call.  */
@@ -251,8 +223,8 @@ grub_script_function_call (grub_script_function_t func, int argc, char **args)
   struct grub_script_scope *old_scope;
   struct grub_script_scope new_scope;
 
-  new_scope.argc = argc;
-  new_scope.args = args;
+  new_scope.argv.argc = argc;
+  new_scope.argv.args = args;
 
   old_scope = scope;
   scope = &new_scope;
@@ -268,21 +240,18 @@ grub_err_t
 grub_script_execute_cmdline (struct grub_script_cmd *cmd)
 {
   struct grub_script_cmdline *cmdline = (struct grub_script_cmdline *) cmd;
-  char **args = 0;
-  int i = 0;
   grub_command_t grubcmd;
   grub_err_t ret = 0;
-  int argcount = 0;
   grub_script_function_t func = 0;
   char errnobuf[18];
   char *cmdname;
+  struct grub_script_argv argv = { 0, 0 };
 
   /* Lookup the command.  */
-  args = grub_script_execute_arglist_to_argv (cmdline->arglist, &argcount);
-  if (!args)
+  if (grub_script_arglist_to_argv (cmdline->arglist, &argv))
     return grub_errno;
 
-  cmdname = args[0];
+  cmdname = argv.args[0];
   grubcmd = grub_command_find (cmdname);
   if (! grubcmd)
     {
@@ -319,14 +288,12 @@ grub_script_execute_cmdline (struct grub_script_cmd *cmd)
 
   /* Execute the GRUB command or function.  */
   if (grubcmd)
-    ret = (grubcmd->func) (grubcmd, argcount - 1, args + 1);
+    ret = (grubcmd->func) (grubcmd, argv.argc - 1, argv.args + 1);
   else
-    ret = grub_script_function_call (func, argcount - 1, args + 1);
+    ret = grub_script_function_call (func, argv.argc - 1, argv.args + 1);
 
   /* Free arguments.  */
-  for (i = 0; i < argcount; i++)
-    grub_free (args[i]);
-  grub_free (args);
+  grub_script_argv_free (&argv);
 
   if (grub_errno == GRUB_ERR_TEST_FAILURE)
     grub_errno = GRUB_ERR_NONE;
@@ -363,7 +330,7 @@ grub_script_execute_cmdif (struct grub_script_cmd *cmd)
   /* Check if the commands results in a true or a false.  The value is
      read from the env variable `?'.  */
   grub_script_execute_cmd (cmdif->exec_to_evaluate);
-  result = grub_script_env_get ("?");
+  result = grub_env_get ("?");
 
   grub_errno = GRUB_ERR_NONE;
 
@@ -381,23 +348,20 @@ grub_script_execute_cmdfor (struct grub_script_cmd *cmd)
 {
   int i;
   int result;
-  char **args;
-  int argcount;
+  struct grub_script_argv argv;
   struct grub_script_cmdfor *cmdfor = (struct grub_script_cmdfor *) cmd;
 
-  args = grub_script_execute_arglist_to_argv (cmdfor->words, &argcount);
-  if (!args)
+  if (grub_script_arglist_to_argv (cmdfor->words, &argv))
     return grub_errno;
 
   result = 0;
-  for (i = 0; i < argcount; i++)
+  for (i = 0; i < argv.argc; i++)
     {
-      grub_script_env_set (cmdfor->name->str, args[i]);
+      grub_script_env_set (cmdfor->name->str, argv.args[i]);
       result = grub_script_execute_cmd (cmdfor->list);
-      grub_free (args[i]);
     }
 
-  grub_free (args);
+  grub_script_argv_free (&argv);
   return result;
 }
 
@@ -426,26 +390,20 @@ grub_err_t
 grub_script_execute_menuentry (struct grub_script_cmd *cmd)
 {
   struct grub_script_cmd_menuentry *cmd_menuentry;
-  char **args = 0;
-  int argcount = 0;
-  int i = 0;
+  struct grub_script_argv argv = {0, 0};
 
   cmd_menuentry = (struct grub_script_cmd_menuentry *) cmd;
 
   if (cmd_menuentry->arglist)
     {
-      args = grub_script_execute_arglist_to_argv (cmd_menuentry->arglist, &argcount);
-      if (!args)
+      if (grub_script_arglist_to_argv (cmd_menuentry->arglist, &argv))
        return grub_errno;
     }
 
-  grub_normal_add_menu_entry (argcount, (const char **) args,
+  grub_normal_add_menu_entry (argv.argc, (const char **) argv.args,
                              cmd_menuentry->sourcecode);
 
-  /* Free arguments.  */
-  for (i = 0; i < argcount; i++)
-    grub_free (args[i]);
-  grub_free (args);
+  grub_script_argv_free (&argv);
 
   return grub_errno;
 }
index f563ac30d02d80517858e314810c8469c34efd51..bfc53a6ff67ec0be9060aafcb2f1d7414114f281 100644 (file)
@@ -119,7 +119,7 @@ DIGITS          [[:digit:]]+
 NAME            [[:alpha:]_][[:alnum:][:digit:]_]*
 
 ESC             \\.
-SPECIAL        \?|\#
+SPECIAL         \?|\#|\*|\@
 VARIABLE        ${NAME}|$\{{NAME}\}|${DIGITS}|$\{{DIGITS}\}|${SPECIAL}|$\{{SPECIAL}\}
 DQSTR           \"([^\\\"]|{ESC})*\"
 SQSTR           \'[^\']*\'
index 048907a76ca4c5bd76e768ad18a7dbc6980b8298..554dd68ed6202ec34ace89ab726b0cd577e850db 100644 (file)
 # You should have received a copy of the GNU General Public License
 # along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
 
+# simple arguments
+echo one two three
+echo "one two three"
+echo 'one two three'
+
+# empty arguments
+echo a "" b
+echo a '' b
+
+echo a $foo b
+echo a ${foo} b
+
+echo a "$foo" b
+echo a "${foo}" b
+
+# multi-part arguments
+echo one"two"three
+echo one${two}three
+echo one"two"$three
+
+echo one'two'three
+echo one${two}three
+echo one'two'$three
+
+echo one'two'three"four"five${six}seven$eight
+
+
 foo=bar
 echo $foo ${foo}
 echo "$foo" "${foo}"
index 41af87474a82249585440cf42f086f1a27fc2466..234a1be134677cfd5ea2f581c7e3c526c858dcb5 100644 (file)
@@ -18,7 +18,7 @@
 
 echo parameter count
 function fcount {
-  echo "$#"
+  echo fcount "$#"
 }
 
 fcount
@@ -27,7 +27,7 @@ fcount a b
 
 echo parameter count, with nesting
 function ffcount {
-  echo "$#"
+  echo ffcount "$#"
   fcount
   fcount a
   fcount a b
@@ -39,9 +39,9 @@ ffcount 1 2
 
 echo parameters
 function fparam {
-  echo 1 $1
-  echo 2 $2
-  echo 3 $3
+  echo fparam 1 $1
+  echo fparam 2 $2
+  echo fparam 3 $3
 }
 
 fparam
@@ -50,9 +50,9 @@ fparam a b
 
 echo parameters, with nesting
 function ffparam {
-  echo 1 $1
-  echo 2 $2
-  echo 3 $3
+  echo ffparam 1 $1
+  echo ffparam 2 $2
+  echo ffparam 3 $3
   fparam
   fparam a
   fparam a b
@@ -61,3 +61,57 @@ function ffparam {
 ffparam
 ffparam 1
 ffparam 1 2
+
+echo parameter expansion with specials
+function fstar {
+  for f in $*
+  do
+    echo fstar $f
+  done
+}
+
+fstar
+fstar a
+fstar a "1 2"
+fstar a "1 2" b
+
+function fdqstar {
+  for f in "$*"
+  do
+    echo fdqstar $f
+  done
+}
+
+fdqstar
+fdqstar a
+fdqstar a "1 2"
+fdqstar a "1 2" b
+
+function fat {
+  for f in $@
+  do
+    echo fat $f
+  done
+}
+
+fat
+fat a
+fat a "1 2"
+fat a "1 2" b
+fat a "1 2" b "c d"
+fat a "1 2" b "c d" e
+
+function fdqat {
+  for f in "$@"
+  do
+    echo fdqat $f
+  done
+}
+
+# fdqat # this case needs special handling, lets ignore till we really need it.
+fdqat a
+fdqat a "1 2"
+fdqat a "1 2" b
+fdqat a "1 2" b "c d"
+fdqat a "1 2" b "c d" e
+
index 9ff897627e2cc5ab5547fc7bfc3ed50a75ee652b..77b3cf2989c1830d53096af21689e03bb86f5783 100644 (file)
@@ -28,7 +28,7 @@ foo=foo
 echo "" $foo
 
 echo $bar $foo
+
 bar=""
 echo $bar $foo