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
* 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;
/*
* 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;
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.
int errcode = 0;
math_hooks.lookupvar = lookupvar;
- math_hooks.setvar = setvar;
+ math_hooks.setvar = setvar2;
math_hooks.endofname = endofname;
INT_OFF;
}
}
- r = builtin_read(setvar,
+ r = shell_builtin_read(setvar2,
argptr,
bltinlookup("IFS"), /* can be NULL */
read_flags,
#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,
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;
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:
};
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,
#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
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)
#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;
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
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);
G.ifs = get_local_var_value("IFS");
if (G.ifs == NULL)
- G.ifs = " \t\n";
+ G.ifs = defifs;
reset:
#if ENABLE_HUSH_INTERACTIVE
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
--- /dev/null
+test 1: | abc1 def |
+test 2: | \abc2 d\ef |
+test 3: |abc3 def|
+test 4: |\abc4 d\ef|
+Done
--- /dev/null
+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
--- /dev/null
+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.
--- /dev/null
+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." )
--- /dev/null
+test
+tes
+tes
--- /dev/null
+echo 'test' | (read reply; echo "$reply")
+echo 'test' | (read -n 3 reply; echo "$reply")
+echo 'test' | (read -n3 reply; echo "$reply")
--- /dev/null
+testbest
+test\
--- /dev/null
+echo -e 'test\\\nbest' | (read reply; echo "$reply")
+echo -e 'test\\\nbest' | (read -r reply; echo "$reply")
--- /dev/null
+><
+><
+>test<
+>test<
--- /dev/null
+# 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<")
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);
*
* 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
* 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')
}
/* 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--;
#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;