hush: use ash's read builtin
authorDenys Vlasenko <vda.linux@googlemail.com>
Tue, 12 Jan 2010 22:29:57 +0000 (23:29 +0100)
committerDenys Vlasenko <vda.linux@googlemail.com>
Tue, 12 Jan 2010 22:29:57 +0000 (23:29 +0100)
function                                             old     new   delta
shell_builtin_read                                     -    1000   +1000
set_local_var_from_halves                              -      24     +24
setvar2                                                -       7      +7
...
popstring                                            140     134      -6
ash_main                                            1375    1368      -7
setvar                                               184     174     -10
arith_set_local_var                                   36       -     -36
builtin_read                                        1096     185    -911
------------------------------------------------------------------------------
(add/remove: 3/1 grow/shrink: 5/23 up/down: 1038/-1007)        Total: 31 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
18 files changed:
shell/Kbuild
shell/ash.c
shell/builtin_read.c
shell/builtin_read.h
shell/hush.c
shell/hush_test/hush-read/read_REPLY.right [new file with mode: 0644]
shell/hush_test/hush-read/read_REPLY.tests [new file with mode: 0755]
shell/hush_test/hush-read/read_ifs.right [new file with mode: 0644]
shell/hush_test/hush-read/read_ifs.tests [new file with mode: 0755]
shell/hush_test/hush-read/read_n.right [new file with mode: 0644]
shell/hush_test/hush-read/read_n.tests [new file with mode: 0755]
shell/hush_test/hush-read/read_r.right [new file with mode: 0644]
shell/hush_test/hush-read/read_r.tests [new file with mode: 0755]
shell/hush_test/hush-read/read_t.right [new file with mode: 0644]
shell/hush_test/hush-read/read_t.tests [new file with mode: 0755]
shell/match.h
shell/math.c
shell/math.h

index 155ac6f..d8306dc 100644 (file)
@@ -6,7 +6,7 @@
 
 lib-y:=
 lib-$(CONFIG_ASH)      += ash.o ash_ptr_hack.o shell_common.o builtin_read.o
-lib-$(CONFIG_HUSH)     += hush.o match.o
+lib-$(CONFIG_HUSH)     += hush.o match.o shell_common.o builtin_read.o
 lib-$(CONFIG_CTTYHACK) += cttyhack.o
 
 lib-$(CONFIG_SH_MATH_SUPPORT) += math.o
index c7deffd..31dc592 100644 (file)
@@ -1884,7 +1884,7 @@ getoptsreset(const char *value)
  * Return of a legal variable name (a letter or underscore followed by zero or
  * more letters, underscores, and digits).
  */
-static char *
+static char* FAST_FUNC
 endofname(const char *name)
 {
        char *p;
@@ -1991,7 +1991,7 @@ findvar(struct var **vpp, const char *name)
 /*
  * Find the value of a variable.  Returns NULL if not set.
  */
-static const char *
+static const char* FAST_FUNC
 lookupvar(const char *name)
 {
        struct var *v;
@@ -2114,6 +2114,12 @@ setvar(const char *name, const char *val, int flags)
        INT_ON;
 }
 
+static void FAST_FUNC
+setvar2(const char *name, const char *val)
+{
+       setvar(name, val, 0);
+}
+
 #if ENABLE_ASH_GETOPTS
 /*
  * Safe version of setvar, returns 1 on success 0 on failure.
@@ -5304,7 +5310,7 @@ ash_arith(const char *s)
        int errcode = 0;
 
        math_hooks.lookupvar = lookupvar;
-       math_hooks.setvar = setvar;
+       math_hooks.setvar    = setvar2;
        math_hooks.endofname = endofname;
 
        INT_OFF;
@@ -12526,7 +12532,7 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
                }
        }
 
-       r = builtin_read(setvar,
+       r = shell_builtin_read(setvar2,
                argptr,
                bltinlookup("IFS"), /* can be NULL */
                read_flags,
index 24cfe08..412bcf8 100644 (file)
@@ -21,7 +21,7 @@
 #include "builtin_read.h"
 
 const char* FAST_FUNC
-builtin_read(void (*setvar)(const char *name, const char *val, int flags),
+shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
        char       **argv,
        const char *ifs,
        int        read_flags,
@@ -178,7 +178,7 @@ builtin_read(void (*setvar)(const char *name, const char *val, int flags),
                        if (argv[1] != NULL && is_ifs) {
                                buffer[bufpos] = '\0';
                                bufpos = 0;
-                               setvar(*argv, buffer, 0);
+                               setvar(*argv, buffer);
                                argv++;
                                /* can we skip one non-space ifs char? (2: yes) */
                                startword = isspace(c) ? 2 : 1;
@@ -195,14 +195,14 @@ builtin_read(void (*setvar)(const char *name, const char *val, int flags),
                        continue;
                buffer[bufpos + 1] = '\0';
                /* Use the remainder as a value for the next variable */
-               setvar(*argv, buffer, 0);
+               setvar(*argv, buffer);
                /* Set the rest to "" */
                while (*++argv)
-                       setvar(*argv, "", 0);
+                       setvar(*argv, "");
        } else {
                /* Note: no $IFS removal */
                buffer[bufpos] = '\0';
-               setvar("REPLY", buffer, 0);
+               setvar("REPLY", buffer);
        }
 
  ret:
index 930d014..bc23dc5 100644 (file)
@@ -27,7 +27,7 @@ enum {
 };
 
 const char* FAST_FUNC
-builtin_read(void (*setvar)(const char *name, const char *val, int flags),
+shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val),
        char       **argv,
        const char *ifs,
        int        read_flags,
index 88a7b98..bb0ab84 100644 (file)
@@ -84,6 +84,9 @@
 #if ENABLE_HUSH_CASE
 # include <fnmatch.h>
 #endif
+
+#include "shell_common.h"
+#include "builtin_read.h"
 #include "math.h"
 #include "match.h"
 #if ENABLE_HUSH_RANDOM_SUPPORT
@@ -1307,7 +1310,7 @@ static struct variable *get_local_var(const char *name)
        return NULL;
 }
 
-static const char *get_local_var_value(const char *name)
+static const char* FAST_FUNC get_local_var_value(const char *name)
 {
        struct variable **pp = get_ptr_to_local_var(name);
        if (pp)
@@ -1510,7 +1513,7 @@ static void unset_vars(char **strings)
 #if ENABLE_SH_MATH_SUPPORT
 #define is_name(c)      ((c) == '_' || isalpha((unsigned char)(c)))
 #define is_in_name(c)   ((c) == '_' || isalnum((unsigned char)(c)))
-static char *endofname(const char *name)
+static char* FAST_FUNC endofname(const char *name)
 {
        char *p;
 
@@ -1524,11 +1527,10 @@ static char *endofname(const char *name)
        return p;
 }
 
-static void arith_set_local_var(const char *name, const char *val, int flags)
+static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val)
 {
-       /* arith code doesnt malloc space, so do it for it */
        char *var = xasprintf("%s=%s", name, val);
-       set_local_var(var, flags, /*lvl:*/ 0, /*ro:*/ 0);
+       set_local_var(var, /*flags:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
 }
 #endif
 
@@ -2538,7 +2540,7 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg, char
 
                        exp_str = expand_pseudo_dquoted(arg);
                        hooks.lookupvar = get_local_var_value;
-                       hooks.setvar = arith_set_local_var;
+                       hooks.setvar = set_local_var_from_halves;
                        hooks.endofname = endofname;
                        res = arith(exp_str ? exp_str : arg, &errcode, &hooks);
                        free(exp_str);
@@ -6157,7 +6159,7 @@ static struct pipe *parse_stream(char **pstring,
 
        G.ifs = get_local_var_value("IFS");
        if (G.ifs == NULL)
-               G.ifs = " \t\n";
+               G.ifs = defifs;
 
  reset:
 #if ENABLE_HUSH_INTERACTIVE
@@ -7727,24 +7729,37 @@ static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM)
 
 static int FAST_FUNC builtin_read(char **argv)
 {
-       char *string;
-       const char *name = "REPLY";
+       const char *r;
+       char *opt_n = NULL;
+       char *opt_p = NULL;
+       char *opt_t = NULL;
+       char *opt_u = NULL;
+       int read_flags;
 
-       if (argv[1]) {
-               name = argv[1];
-               /* bash (3.2.33(1)) bug: "read 0abcd" will execute,
-                * and _after_ that_ it will complain */
-               if (!is_well_formed_var_name(name, '\0')) {
-                       /* Mimic bash message */
-                       bb_error_msg("read: '%s': not a valid identifier", name);
-                       return 1;
-               }
-       }
+       /* "!": do not abort on errors.
+        * Option string must start with "sr" to match BUILTIN_READ_xxx
+        */
+       read_flags = getopt32(argv, "!srn:p:t:u:", &opt_n, &opt_p, &opt_t, &opt_u);
+       if (read_flags == (uint32_t)-1)
+               return EXIT_FAILURE;
+       argv += optind;
 
-//TODO: bash unbackslashes input, splits words and puts them in argv[i]
+       r = shell_builtin_read(set_local_var_from_halves,
+               argv,
+               get_local_var_value("IFS"), /* can be NULL */
+               read_flags,
+               opt_n,
+               opt_p,
+               opt_t,
+               opt_u
+       );
+
+       if ((uintptr_t)r > 1) {
+               bb_error_msg("%s", r);
+               r = (char*)(uintptr_t)1;
+       }
 
-       string = xmalloc_reads(STDIN_FILENO, xasprintf("%s=", name), NULL);
-       return set_local_var(string, /*exp:*/ 0, /*lvl:*/ 0, /*ro:*/ 0);
+       return (uintptr_t)r;
 }
 
 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#set
diff --git a/shell/hush_test/hush-read/read_REPLY.right b/shell/hush_test/hush-read/read_REPLY.right
new file mode 100644 (file)
index 0000000..59f5d54
--- /dev/null
@@ -0,0 +1,5 @@
+test 1: |  abc1  def  |
+test 2: |  \abc2  d\ef  |
+test 3: |abc3  def|
+test 4: |\abc4  d\ef|
+Done
diff --git a/shell/hush_test/hush-read/read_REPLY.tests b/shell/hush_test/hush-read/read_REPLY.tests
new file mode 100755 (executable)
index 0000000..ba20cae
--- /dev/null
@@ -0,0 +1,5 @@
+echo '  \abc1  d\ef  ' | ( read         ; echo "test 1: |$REPLY|" )
+echo '  \abc2  d\ef  ' | ( read -r      ; echo "test 2: |$REPLY|" )
+echo '  \abc3  d\ef  ' | ( read    REPLY; echo "test 3: |$REPLY|" )
+echo '  \abc4  d\ef  ' | ( read -r REPLY; echo "test 4: |$REPLY|" )
+echo Done
diff --git a/shell/hush_test/hush-read/read_ifs.right b/shell/hush_test/hush-read/read_ifs.right
new file mode 100644 (file)
index 0000000..b523344
--- /dev/null
@@ -0,0 +1,10 @@
+test 1: .a. .b. .c.
+test 2: .a. .b. .c.
+test 3: .a. .. .b,c.
+test 4: .a. .. .b,c.
+test 5: .a. .. .c.
+test 6: .a. .. .c. .d.
+test 7: .a. .. .b,c,d  ,  ,.
+test 8: .. .a. .b. .c.
+test 9: .a. .b. .c. ..
+test A: .. .a. .. .b. .c.
diff --git a/shell/hush_test/hush-read/read_ifs.tests b/shell/hush_test/hush-read/read_ifs.tests
new file mode 100755 (executable)
index 0000000..6e83112
--- /dev/null
@@ -0,0 +1,10 @@
+printf 'a\t\tb\tc\n' | ( IFS=$(printf "\t") read a b c;  echo "test 1: .$a. .$b. .$c." )
+printf 'a\t\tb\tc\n' | ( IFS=$(printf " \t") read a b c; echo "test 2: .$a. .$b. .$c." )
+printf 'a,,b,c\n'    | ( IFS="," read a b c;  echo "test 3: .$a. .$b. .$c." )
+printf 'a,,b,c\n'    | ( IFS=" ," read a b c; echo "test 4: .$a. .$b. .$c." )
+printf 'a ,, c\n'    | ( IFS=" ," read a b c; echo "test 5: .$a. .$b. .$c." )
+printf 'a ,, c d\n'  | ( IFS=" ," read a b c d;     echo "test 6: .$a. .$b. .$c. .$d." )
+printf ' a,,b,c,d  ,  ,\n' | ( IFS=" ," read a b c; echo "test 7: .$a. .$b. .$c." )
+printf '\t,\ta\t,\tb\tc'   | ( IFS=$(printf " \t,") read a b c d;   echo "test 8: .$a. .$b. .$c. .$d." )
+printf '\t\ta\t,\tb\tc'    | ( IFS=$(printf " \t,") read a b c d;   echo "test 9: .$a. .$b. .$c. .$d." )
+printf '\t,\ta\t,,\tb\tc'  | ( IFS=$(printf " \t,") read a b c d e; echo "test A: .$a. .$b. .$c. .$d. .$e." )
diff --git a/shell/hush_test/hush-read/read_n.right b/shell/hush_test/hush-read/read_n.right
new file mode 100644 (file)
index 0000000..1f81af0
--- /dev/null
@@ -0,0 +1,3 @@
+test
+tes
+tes
diff --git a/shell/hush_test/hush-read/read_n.tests b/shell/hush_test/hush-read/read_n.tests
new file mode 100755 (executable)
index 0000000..12423ba
--- /dev/null
@@ -0,0 +1,3 @@
+echo 'test' | (read reply; echo "$reply")
+echo 'test' | (read -n 3 reply; echo "$reply")
+echo 'test' | (read -n3 reply; echo "$reply")
diff --git a/shell/hush_test/hush-read/read_r.right b/shell/hush_test/hush-read/read_r.right
new file mode 100644 (file)
index 0000000..3536bf7
--- /dev/null
@@ -0,0 +1,2 @@
+testbest
+test\
diff --git a/shell/hush_test/hush-read/read_r.tests b/shell/hush_test/hush-read/read_r.tests
new file mode 100755 (executable)
index 0000000..2c4cc61
--- /dev/null
@@ -0,0 +1,2 @@
+echo -e 'test\\\nbest' | (read reply; echo "$reply")
+echo -e 'test\\\nbest' | (read -r reply; echo "$reply")
diff --git a/shell/hush_test/hush-read/read_t.right b/shell/hush_test/hush-read/read_t.right
new file mode 100644 (file)
index 0000000..04126cb
--- /dev/null
@@ -0,0 +1,4 @@
+><
+><
+>test<
+>test<
diff --git a/shell/hush_test/hush-read/read_t.tests b/shell/hush_test/hush-read/read_t.tests
new file mode 100755 (executable)
index 0000000..d65f1ae
--- /dev/null
@@ -0,0 +1,10 @@
+# bash 3.2 outputs:
+
+# ><
+{ echo -n 'te'; sleep 2; echo 'st'; }   | (read -t 1 reply; echo ">$reply<")
+# ><
+{               sleep 2; echo 'test'; } | (read -t 1 reply; echo ">$reply<")
+# >test<
+{ echo -n 'te'; sleep 1; echo 'st'; }   | (read -t 2 reply; echo ">$reply<")
+# >test<
+{               sleep 1; echo 'test'; } | (read -t 2 reply; echo ">$reply<")
index 98ff874..c022ceb 100644 (file)
@@ -5,6 +5,8 @@
 
 PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
 
+//TODO! Why ash.c still uses internal version?!
+
 typedef char *(*scan_t)(char *string, char *match, bool match_at_left);
 
 char *scanleft(char *string, char *match, bool match_at_left);
index 76159b2..91fb28f 100644 (file)
  *
  * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
  */
-#include "libbb.h"
-#include "math.h"
-
-#define a_e_h_t arith_eval_hooks_t
-#define lookupvar (math_hooks->lookupvar)
-#define setvar (math_hooks->setvar)
-#define endofname (math_hooks->endofname)
-
 /* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
 
    Permission is hereby granted, free of charge, to any person obtaining
@@ -99,7 +91,6 @@
  * whitespace chars should be considered.  Look below the "#include"s for a
  * precompiler test.
  */
-
 /*
  * Aug 26, 2001              Manuel Novoa III
  *
  * modified slightly to take account of my changes to the code.
  *
  */
-
 /*
  *  (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
  *
  * - protect $((num num)) as true zero expr (Manuel`s error)
  * - always use special isspace(), see comment from bash ;-)
  */
+#include "libbb.h"
+#include "math.h"
+
+#define a_e_h_t arith_eval_hooks_t
+#define lookupvar (math_hooks->lookupvar)
+#define setvar    (math_hooks->setvar   )
+#define endofname (math_hooks->endofname)
 
 #define arith_isspace(arithval) \
        (arithval == ' ' || arithval == '\n' || arithval == '\t')
@@ -420,7 +417,7 @@ arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr, a_e_h_t *math_hoo
                }
                /* save to shell variable */
                sprintf(buf, arith_t_fmt, rez);
-               setvar(numptr_m1->var, buf, 0);
+               setvar(numptr_m1->var, buf);
                /* after saving, make previous value for v++ or v-- */
                if (op == TOK_POST_INC)
                        rez--;
index 7b7898e..2b0b2b8 100644 (file)
@@ -87,12 +87,13 @@ typedef long arith_t;
 #define strto_arith_t strtoul
 #endif
 
-typedef const char *(*arith_var_lookup_t)(const char *name);
-typedef void (*arith_var_set_t)(const char *name, const char *val, int flags);
-typedef char *(*arith_var_endofname_t)(const char *name);
+typedef const char* FAST_FUNC (*arith_var_lookup_t)(const char *name);
+typedef void        FAST_FUNC (*arith_var_set_t)(const char *name, const char *val);
+typedef char*       FAST_FUNC (*arith_var_endofname_t)(const char *name);
+
 typedef struct arith_eval_hooks {
-       arith_var_lookup_t lookupvar;
-       arith_var_set_t setvar;
+       arith_var_lookup_t    lookupvar;
+       arith_var_set_t       setvar;
        arith_var_endofname_t endofname;
 } arith_eval_hooks_t;