diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-03-25 00:07:12 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-03-25 00:07:12 +0000 |
commit | 59f351ccda4c66785b864086bf23868768ca6f61 (patch) | |
tree | f57eab03adfd44de984d03190a0e19dd5ca66f05 /shell | |
parent | f39653e3060427b4a48f70e486d7c71729bffd96 (diff) | |
download | busybox-w32-59f351ccda4c66785b864086bf23868768ca6f61.tar.gz busybox-w32-59f351ccda4c66785b864086bf23868768ca6f61.tar.bz2 busybox-w32-59f351ccda4c66785b864086bf23868768ca6f61.zip |
ash: add read -u; fix read -t and read -n; add testsuite entries.
Diffstat (limited to 'shell')
-rw-r--r-- | shell/ash.c | 122 | ||||
-rw-r--r-- | shell/ash_test/ash-read/read_n.right | 3 | ||||
-rwxr-xr-x | shell/ash_test/ash-read/read_n.tests | 3 | ||||
-rw-r--r-- | shell/ash_test/ash-read/read_r.right | 2 | ||||
-rwxr-xr-x | shell/ash_test/ash-read/read_r.tests | 2 | ||||
-rw-r--r-- | shell/ash_test/ash-read/read_t.right | 4 | ||||
-rwxr-xr-x | shell/ash_test/ash-read/read_t.tests | 10 |
7 files changed, 91 insertions, 55 deletions
diff --git a/shell/ash.c b/shell/ash.c index d9ce202fd..2b6133d20 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -11672,10 +11672,18 @@ typedef enum __rlimit_resource rlim_t; | |||
11672 | #endif | 11672 | #endif |
11673 | 11673 | ||
11674 | /* | 11674 | /* |
11675 | * The read builtin. The -e option causes backslashes to escape the | 11675 | * The read builtin. Options: |
11676 | * following character. | 11676 | * -r Do not interpret '\' specially |
11677 | * | 11677 | * -s Turn off echo (tty only) |
11678 | * -n NCHARS Read NCHARS max | ||
11679 | * -p PROMPT Display PROMPT on stderr (if input is from tty) | ||
11680 | * -t SECONDS Timeout after SECONDS (tty or pipe only) | ||
11681 | * -u FD Read from given FD instead of fd 0 | ||
11678 | * This uses unbuffered input, which may be avoidable in some cases. | 11682 | * This uses unbuffered input, which may be avoidable in some cases. |
11683 | * TODO: bash also has: | ||
11684 | * -a ARRAY Read into array[0],[1],etc | ||
11685 | * -d DELIM End on DELIM char, not newline | ||
11686 | * -e Use line editing (tty only) | ||
11679 | */ | 11687 | */ |
11680 | static int | 11688 | static int |
11681 | readcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) | 11689 | readcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) |
@@ -11690,31 +11698,23 @@ readcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) | |||
11690 | int startword; | 11698 | int startword; |
11691 | int status; | 11699 | int status; |
11692 | int i; | 11700 | int i; |
11701 | int fd = 0; | ||
11693 | #if ENABLE_ASH_READ_NCHARS | 11702 | #if ENABLE_ASH_READ_NCHARS |
11694 | int n_flag = 0; | 11703 | int nchars = 0; /* if != 0, -n is in effect */ |
11695 | int nchars = 0; | ||
11696 | int silent = 0; | 11704 | int silent = 0; |
11697 | struct termios tty, old_tty; | 11705 | struct termios tty, old_tty; |
11698 | #endif | 11706 | #endif |
11699 | #if ENABLE_ASH_READ_TIMEOUT | 11707 | #if ENABLE_ASH_READ_TIMEOUT |
11700 | fd_set set; | 11708 | unsigned end_ms = 0; |
11701 | struct timeval ts; | 11709 | unsigned timeout = 0; |
11702 | |||
11703 | ts.tv_sec = ts.tv_usec = 0; | ||
11704 | #endif | 11710 | #endif |
11705 | 11711 | ||
11706 | rflag = 0; | 11712 | rflag = 0; |
11707 | prompt = NULL; | 11713 | prompt = NULL; |
11708 | #if ENABLE_ASH_READ_NCHARS && ENABLE_ASH_READ_TIMEOUT | 11714 | while ((i = nextopt("p:u:r" |
11709 | while ((i = nextopt("p:rt:n:s")) != '\0') | 11715 | USE_ASH_READ_TIMEOUT("t:") |
11710 | #elif ENABLE_ASH_READ_NCHARS | 11716 | USE_ASH_READ_NCHARS("n:s") |
11711 | while ((i = nextopt("p:rn:s")) != '\0') | 11717 | )) != '\0') { |
11712 | #elif ENABLE_ASH_READ_TIMEOUT | ||
11713 | while ((i = nextopt("p:rt:")) != '\0') | ||
11714 | #else | ||
11715 | while ((i = nextopt("p:r")) != '\0') | ||
11716 | #endif | ||
11717 | { | ||
11718 | switch (i) { | 11718 | switch (i) { |
11719 | case 'p': | 11719 | case 'p': |
11720 | prompt = optionarg; | 11720 | prompt = optionarg; |
@@ -11724,7 +11724,7 @@ readcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) | |||
11724 | nchars = bb_strtou(optionarg, NULL, 10); | 11724 | nchars = bb_strtou(optionarg, NULL, 10); |
11725 | if (nchars < 0 || errno) | 11725 | if (nchars < 0 || errno) |
11726 | ash_msg_and_raise_error("invalid count"); | 11726 | ash_msg_and_raise_error("invalid count"); |
11727 | n_flag = nchars; /* just a flag "nchars is nonzero" */ | 11727 | /* nchars == 0: off (bash 3.2 does this too) */ |
11728 | break; | 11728 | break; |
11729 | case 's': | 11729 | case 's': |
11730 | silent = 1; | 11730 | silent = 1; |
@@ -11732,6 +11732,11 @@ readcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) | |||
11732 | #endif | 11732 | #endif |
11733 | #if ENABLE_ASH_READ_TIMEOUT | 11733 | #if ENABLE_ASH_READ_TIMEOUT |
11734 | case 't': | 11734 | case 't': |
11735 | timeout = bb_strtou(optionarg, NULL, 10); | ||
11736 | if (errno || timeout > UINT_MAX / 2048) | ||
11737 | ash_msg_and_raise_error("invalid timeout"); | ||
11738 | timeout *= 1000; | ||
11739 | #if 0 /* even bash have no -t N.NNN support */ | ||
11735 | ts.tv_sec = bb_strtou(optionarg, &p, 10); | 11740 | ts.tv_sec = bb_strtou(optionarg, &p, 10); |
11736 | ts.tv_usec = 0; | 11741 | ts.tv_usec = 0; |
11737 | /* EINVAL means number is ok, but not terminated by NUL */ | 11742 | /* EINVAL means number is ok, but not terminated by NUL */ |
@@ -11755,16 +11760,22 @@ readcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) | |||
11755 | if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */ | 11760 | if (!(ts.tv_sec | ts.tv_usec)) { /* both are 0? */ |
11756 | ash_msg_and_raise_error("invalid timeout"); | 11761 | ash_msg_and_raise_error("invalid timeout"); |
11757 | } | 11762 | } |
11763 | #endif /* if 0 */ | ||
11758 | break; | 11764 | break; |
11759 | #endif | 11765 | #endif |
11760 | case 'r': | 11766 | case 'r': |
11761 | rflag = 1; | 11767 | rflag = 1; |
11762 | break; | 11768 | break; |
11769 | case 'u': | ||
11770 | fd = bb_strtou(optionarg, NULL, 10); | ||
11771 | if (fd < 0 || errno) | ||
11772 | ash_msg_and_raise_error("invalid file descriptor"); | ||
11773 | break; | ||
11763 | default: | 11774 | default: |
11764 | break; | 11775 | break; |
11765 | } | 11776 | } |
11766 | } | 11777 | } |
11767 | if (prompt && isatty(0)) { | 11778 | if (prompt && isatty(fd)) { |
11768 | out2str(prompt); | 11779 | out2str(prompt); |
11769 | } | 11780 | } |
11770 | ap = argptr; | 11781 | ap = argptr; |
@@ -11774,46 +11785,48 @@ readcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) | |||
11774 | if (ifs == NULL) | 11785 | if (ifs == NULL) |
11775 | ifs = defifs; | 11786 | ifs = defifs; |
11776 | #if ENABLE_ASH_READ_NCHARS | 11787 | #if ENABLE_ASH_READ_NCHARS |
11777 | if (n_flag || silent) { | 11788 | tcgetattr(fd, &tty); |
11778 | if (tcgetattr(0, &tty) != 0) { | 11789 | old_tty = tty; |
11779 | /* Not a tty */ | 11790 | if (nchars || silent) { |
11780 | n_flag = 0; | 11791 | if (nchars) { |
11781 | silent = 0; | 11792 | tty.c_lflag &= ~ICANON; |
11782 | } else { | 11793 | tty.c_cc[VMIN] = nchars < 256 ? nchars : 255; |
11783 | old_tty = tty; | ||
11784 | if (n_flag) { | ||
11785 | tty.c_lflag &= ~ICANON; | ||
11786 | tty.c_cc[VMIN] = nchars < 256 ? nchars : 255; | ||
11787 | } | ||
11788 | if (silent) { | ||
11789 | tty.c_lflag &= ~(ECHO | ECHOK | ECHONL); | ||
11790 | } | ||
11791 | tcsetattr(0, TCSANOW, &tty); | ||
11792 | } | 11794 | } |
11793 | } | 11795 | if (silent) { |
11794 | #endif | 11796 | tty.c_lflag &= ~(ECHO | ECHOK | ECHONL); |
11795 | #if ENABLE_ASH_READ_TIMEOUT | ||
11796 | if (ts.tv_sec || ts.tv_usec) { | ||
11797 | FD_ZERO(&set); | ||
11798 | FD_SET(0, &set); | ||
11799 | |||
11800 | /* poll-based wait produces bigger code, using select */ | ||
11801 | i = select(1, &set, NULL, NULL, &ts); | ||
11802 | if (!i) { /* timed out! */ | ||
11803 | #if ENABLE_ASH_READ_NCHARS | ||
11804 | if (n_flag) | ||
11805 | tcsetattr(0, TCSANOW, &old_tty); | ||
11806 | #endif | ||
11807 | return 1; | ||
11808 | } | 11797 | } |
11798 | /* if tcgetattr failed, tcsetattr will fail too. | ||
11799 | * Ignoring, it's harmless. */ | ||
11800 | tcsetattr(fd, TCSANOW, &tty); | ||
11809 | } | 11801 | } |
11810 | #endif | 11802 | #endif |
11803 | |||
11811 | status = 0; | 11804 | status = 0; |
11812 | startword = 1; | 11805 | startword = 1; |
11813 | backslash = 0; | 11806 | backslash = 0; |
11807 | #if ENABLE_ASH_READ_TIMEOUT | ||
11808 | if (timeout) /* NB: ensuring end_ms is nonzero */ | ||
11809 | end_ms = ((unsigned)(monotonic_us() / 1000) + timeout) | 1; | ||
11810 | #endif | ||
11814 | STARTSTACKSTR(p); | 11811 | STARTSTACKSTR(p); |
11815 | do { | 11812 | do { |
11816 | if (nonblock_safe_read(0, &c, 1) != 1) { | 11813 | #if ENABLE_ASH_READ_TIMEOUT |
11814 | if (end_ms) { | ||
11815 | struct pollfd pfd[1]; | ||
11816 | pfd[0].fd = fd; | ||
11817 | pfd[0].events = POLLIN; | ||
11818 | timeout = end_ms - (unsigned)(monotonic_us() / 1000); | ||
11819 | if ((int)timeout <= 0 /* already late? */ | ||
11820 | || safe_poll(pfd, 1, timeout) != 1 /* no? wait... */ | ||
11821 | ) { /* timed out! */ | ||
11822 | #if ENABLE_ASH_READ_NCHARS | ||
11823 | tcsetattr(fd, TCSANOW, &old_tty); | ||
11824 | #endif | ||
11825 | return 1; | ||
11826 | } | ||
11827 | } | ||
11828 | #endif | ||
11829 | if (nonblock_safe_read(fd, &c, 1) != 1) { | ||
11817 | status = 1; | 11830 | status = 1; |
11818 | break; | 11831 | break; |
11819 | } | 11832 | } |
@@ -11848,14 +11861,13 @@ readcmd(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) | |||
11848 | } | 11861 | } |
11849 | /* end of do {} while: */ | 11862 | /* end of do {} while: */ |
11850 | #if ENABLE_ASH_READ_NCHARS | 11863 | #if ENABLE_ASH_READ_NCHARS |
11851 | while (!n_flag || --nchars); | 11864 | while (--nchars); |
11852 | #else | 11865 | #else |
11853 | while (1); | 11866 | while (1); |
11854 | #endif | 11867 | #endif |
11855 | 11868 | ||
11856 | #if ENABLE_ASH_READ_NCHARS | 11869 | #if ENABLE_ASH_READ_NCHARS |
11857 | if (n_flag || silent) | 11870 | tcsetattr(fd, TCSANOW, &old_tty); |
11858 | tcsetattr(0, TCSANOW, &old_tty); | ||
11859 | #endif | 11871 | #endif |
11860 | 11872 | ||
11861 | STACKSTRNUL(p); | 11873 | STACKSTRNUL(p); |
diff --git a/shell/ash_test/ash-read/read_n.right b/shell/ash_test/ash-read/read_n.right new file mode 100644 index 000000000..1f81af0bc --- /dev/null +++ b/shell/ash_test/ash-read/read_n.right | |||
@@ -0,0 +1,3 @@ | |||
1 | test | ||
2 | tes | ||
3 | tes | ||
diff --git a/shell/ash_test/ash-read/read_n.tests b/shell/ash_test/ash-read/read_n.tests new file mode 100755 index 000000000..12423ba6e --- /dev/null +++ b/shell/ash_test/ash-read/read_n.tests | |||
@@ -0,0 +1,3 @@ | |||
1 | echo 'test' | (read reply; echo "$reply") | ||
2 | echo 'test' | (read -n 3 reply; echo "$reply") | ||
3 | echo 'test' | (read -n3 reply; echo "$reply") | ||
diff --git a/shell/ash_test/ash-read/read_r.right b/shell/ash_test/ash-read/read_r.right new file mode 100644 index 000000000..3536bf757 --- /dev/null +++ b/shell/ash_test/ash-read/read_r.right | |||
@@ -0,0 +1,2 @@ | |||
1 | testbest | ||
2 | test\ | ||
diff --git a/shell/ash_test/ash-read/read_r.tests b/shell/ash_test/ash-read/read_r.tests new file mode 100755 index 000000000..2c4cc6106 --- /dev/null +++ b/shell/ash_test/ash-read/read_r.tests | |||
@@ -0,0 +1,2 @@ | |||
1 | echo -e 'test\\\nbest' | (read reply; echo "$reply") | ||
2 | echo -e 'test\\\nbest' | (read -r reply; echo "$reply") | ||
diff --git a/shell/ash_test/ash-read/read_t.right b/shell/ash_test/ash-read/read_t.right new file mode 100644 index 000000000..04126cbe6 --- /dev/null +++ b/shell/ash_test/ash-read/read_t.right | |||
@@ -0,0 +1,4 @@ | |||
1 | >< | ||
2 | >< | ||
3 | >test< | ||
4 | >test< | ||
diff --git a/shell/ash_test/ash-read/read_t.tests b/shell/ash_test/ash-read/read_t.tests new file mode 100755 index 000000000..d65f1aeaa --- /dev/null +++ b/shell/ash_test/ash-read/read_t.tests | |||
@@ -0,0 +1,10 @@ | |||
1 | # bash 3.2 outputs: | ||
2 | |||
3 | # >< | ||
4 | { echo -n 'te'; sleep 2; echo 'st'; } | (read -t 1 reply; echo ">$reply<") | ||
5 | # >< | ||
6 | { sleep 2; echo 'test'; } | (read -t 1 reply; echo ">$reply<") | ||
7 | # >test< | ||
8 | { echo -n 'te'; sleep 1; echo 'st'; } | (read -t 2 reply; echo ">$reply<") | ||
9 | # >test< | ||
10 | { sleep 1; echo 'test'; } | (read -t 2 reply; echo ">$reply<") | ||