implement `unset` semantics as required by POSIX
authorMike Frysinger <vapier@gentoo.org>
Mon, 30 Mar 2009 06:50:54 +0000 (06:50 -0000)
committerMike Frysinger <vapier@gentoo.org>
Mon, 30 Mar 2009 06:50:54 +0000 (06:50 -0000)
shell/hush.c
shell/hush_test/hush-vars/unset.right [new file with mode: 0644]
shell/hush_test/hush-vars/unset.tests [new file with mode: 0755]

index a7aa32a..cd6e12b 100644 (file)
@@ -1000,21 +1000,21 @@ static int set_local_var(char *str, int flg_export)
        return 0;
 }
 
-static void unset_local_var(const char *name)
+static int unset_local_var(const char *name)
 {
        struct variable *cur;
        struct variable *prev = prev; /* for gcc */
        int name_len;
 
        if (!name)
-               return;
+               return EXIT_SUCCESS;
        name_len = strlen(name);
        cur = G.top_var;
        while (cur) {
                if (strncmp(cur->varstr, name, name_len) == 0 && cur->varstr[name_len] == '=') {
                        if (cur->flg_read_only) {
                                bb_error_msg("%s: readonly variable", name);
-                               return;
+                               return EXIT_FAILURE;
                        }
                        /* prev is ok to use here because 1st variable, HUSH_VERSION,
                         * is ro, and we cannot reach this code on the 1st pass */
@@ -1024,11 +1024,12 @@ static void unset_local_var(const char *name)
                        if (!cur->max_len)
                                free(cur->varstr);
                        free(cur);
-                       return;
+                       return EXIT_SUCCESS;
                }
                prev = cur;
                cur = cur->next;
        }
+       return EXIT_SUCCESS;
 }
 
 
@@ -5025,11 +5026,40 @@ static int builtin_umask(char **argv)
        return EXIT_SUCCESS;
 }
 
+/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#unset */
 static int builtin_unset(char **argv)
 {
-       /* bash always returns true */
-       unset_local_var(argv[1]);
-       return EXIT_SUCCESS;
+       size_t i;
+       int ret;
+       bool var = true;
+
+       if (!argv[1])
+               return EXIT_SUCCESS;
+
+       i = 0;
+       if (argv[1][0] == '-') {
+               switch (argv[1][1]) {
+               case 'v': break;
+               case 'f': if (ENABLE_HUSH_FUNCTIONS) { var = false; break; }
+               default:
+                       bb_error_msg("unset: %s: invalid option", argv[1]);
+                       return EXIT_FAILURE;
+               }
+               ++i;
+       }
+
+       ret = EXIT_SUCCESS;
+       while (argv[++i]) {
+               if (var) {
+                       if (unset_local_var(argv[i]))
+                               ret = EXIT_FAILURE;
+               }
+#if ENABLE_HUSH_FUNCTIONS
+               else
+                       unset_local_func(argv[i]);
+#endif
+       }
+       return ret;
 }
 
 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */
diff --git a/shell/hush_test/hush-vars/unset.right b/shell/hush_test/hush-vars/unset.right
new file mode 100644 (file)
index 0000000..8dea7c4
--- /dev/null
@@ -0,0 +1,19 @@
+hush: unset: -: invalid option
+1
+hush: unset: -m: invalid option
+1
+0
+___
+0 f g
+0 g
+0
+___
+0 f g
+0
+0 f g
+0
+___
+hush: HUSH_VERSION: readonly variable
+1 f g
+hush: HUSH_VERSION: readonly variable
+1
diff --git a/shell/hush_test/hush-vars/unset.tests b/shell/hush_test/hush-vars/unset.tests
new file mode 100755 (executable)
index 0000000..f59ce59
--- /dev/null
@@ -0,0 +1,36 @@
+# check invalid options are rejected
+unset -
+echo $?
+unset -m a b c
+echo $?
+
+# check funky usage
+unset
+echo $?
+
+# check normal usage
+echo ___
+f=f g=g
+echo $? $f $g
+unset f
+echo $? $f $g
+unset g
+echo $? $f $g
+
+echo ___
+f=f g=g
+echo $? $f $g
+unset f g
+echo $? $f $g
+f=f g=g
+echo $? $f $g
+unset -v f g
+echo $? $f $g
+
+# check read only vars
+echo ___
+f=f g=g
+unset HUSH_VERSION
+echo $? $f $g
+unset f HUSH_VERSION g
+echo $? $f $g