diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2011-02-07 02:03:51 +0100 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2011-02-07 02:03:51 +0100 |
commit | 8ee2adab21328761b80e0cbc513eda7eaa880b24 (patch) | |
tree | c771696ab3b1be735e831b1721ab751c874e1cd3 | |
parent | b8ab4b038803df195eee9844c3597dd640c00393 (diff) | |
download | busybox-w32-8ee2adab21328761b80e0cbc513eda7eaa880b24.tar.gz busybox-w32-8ee2adab21328761b80e0cbc513eda7eaa880b24.tar.bz2 busybox-w32-8ee2adab21328761b80e0cbc513eda7eaa880b24.zip |
echo: do not retry on write errors
function old new delta
echo_main 297 336 +39
stpcpy - 22 +22
run_pipe 1561 1566 +5
pseudo_exec_argv 187 192 +5
hush_exit 75 80 +5
------------------------------------------------------------------------------
(add/remove: 3/0 grow/shrink: 4/0 up/down: 98/0) Total: 76 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r-- | coreutils/echo.c | 87 | ||||
-rw-r--r-- | shell/ash_test/ash-misc/echo_write_error.right | 2 | ||||
-rw-r--r-- | shell/ash_test/ash-misc/echo_write_error.tests | 7 | ||||
-rw-r--r-- | shell/ash_test/ash-redir/redir.right | 1 | ||||
-rw-r--r-- | shell/hush.c | 8 | ||||
-rw-r--r-- | shell/hush_test/hush-misc/echo_write_error.right | 2 | ||||
-rwxr-xr-x | shell/hush_test/hush-misc/echo_write_error.tests | 7 |
7 files changed, 74 insertions, 40 deletions
diff --git a/coreutils/echo.c b/coreutils/echo.c index 3821e594e..5fa3d10c5 100644 --- a/coreutils/echo.c +++ b/coreutils/echo.c | |||
@@ -29,46 +29,42 @@ | |||
29 | 29 | ||
30 | /* NB: can be used by shell even if not enabled as applet */ | 30 | /* NB: can be used by shell even if not enabled as applet */ |
31 | 31 | ||
32 | /* | ||
33 | * NB2: we don't use stdio, we need better error handing. | ||
34 | * Examples include writing into non-opened stdout and error on write. | ||
35 | * | ||
36 | * With stdio, output gets shoveled into stdout buffer, and even | ||
37 | * fflush cannot clear it out. It seems that even if libc receives | ||
38 | * EBADF on write attempts, it feels determined to output data no matter what. | ||
39 | * If echo is called by shell, it will try writing again later, and possibly | ||
40 | * will clobber future output. Not good. | ||
41 | * | ||
42 | * Solaris has fpurge which discards buffered input. glibc has __fpurge. | ||
43 | * But this function is not standard. | ||
44 | */ | ||
45 | |||
32 | int echo_main(int argc UNUSED_PARAM, char **argv) | 46 | int echo_main(int argc UNUSED_PARAM, char **argv) |
33 | { | 47 | { |
48 | char **pp; | ||
34 | const char *arg; | 49 | const char *arg; |
50 | char *out; | ||
51 | char *buffer; | ||
52 | unsigned buflen; | ||
53 | int r; | ||
35 | #if !ENABLE_FEATURE_FANCY_ECHO | 54 | #if !ENABLE_FEATURE_FANCY_ECHO |
36 | enum { | 55 | enum { |
37 | eflag = '\\', | 56 | eflag = '\\', |
38 | nflag = 1, /* 1 -- print '\n' */ | 57 | nflag = 1, /* 1 -- print '\n' */ |
39 | }; | 58 | }; |
40 | 59 | ||
41 | /* We must check that stdout is not closed. | 60 | argv++; |
42 | * The reason for this is highly non-obvious. | ||
43 | * echo_main is used from shell. Shell must correctly handle "echo foo" | ||
44 | * if stdout is closed. With stdio, output gets shoveled into | ||
45 | * stdout buffer, and even fflush cannot clear it out. It seems that | ||
46 | * even if libc receives EBADF on write attempts, it feels determined | ||
47 | * to output data no matter what. So it will try later, | ||
48 | * and possibly will clobber future output. Not good. */ | ||
49 | // TODO: check fcntl() & O_ACCMODE == O_WRONLY or O_RDWR? | ||
50 | if (fcntl(1, F_GETFL) == -1) | ||
51 | return 1; /* match coreutils 6.10 (sans error msg to stderr) */ | ||
52 | //if (dup2(1, 1) != 1) - old way | ||
53 | // return 1; | ||
54 | |||
55 | arg = *++argv; | ||
56 | if (!arg) | ||
57 | goto newline_ret; | ||
58 | #else | 61 | #else |
59 | const char *p; | 62 | const char *p; |
60 | char nflag = 1; | 63 | char nflag = 1; |
61 | char eflag = 0; | 64 | char eflag = 0; |
62 | 65 | ||
63 | /* We must check that stdout is not closed. */ | 66 | while ((arg = *++argv) != NULL) { |
64 | if (fcntl(1, F_GETFL) == -1) | 67 | if (!arg || arg[0] != '-') |
65 | return 1; | ||
66 | |||
67 | while (1) { | ||
68 | arg = *++argv; | ||
69 | if (!arg) | ||
70 | goto newline_ret; | ||
71 | if (*arg != '-') | ||
72 | break; | 68 | break; |
73 | 69 | ||
74 | /* If it appears that we are handling options, then make sure | 70 | /* If it appears that we are handling options, then make sure |
@@ -77,7 +73,7 @@ int echo_main(int argc UNUSED_PARAM, char **argv) | |||
77 | */ | 73 | */ |
78 | p = arg + 1; | 74 | p = arg + 1; |
79 | if (!*p) /* A single '-', so echo it. */ | 75 | if (!*p) /* A single '-', so echo it. */ |
80 | goto just_echo; | 76 | break; |
81 | 77 | ||
82 | do { | 78 | do { |
83 | if (!strrchr("neE", *p)) | 79 | if (!strrchr("neE", *p)) |
@@ -95,19 +91,27 @@ int echo_main(int argc UNUSED_PARAM, char **argv) | |||
95 | } | 91 | } |
96 | just_echo: | 92 | just_echo: |
97 | #endif | 93 | #endif |
98 | while (1) { | 94 | |
99 | /* arg is already == *argv and isn't NULL */ | 95 | buflen = 1; |
96 | pp = argv; | ||
97 | while ((arg = *pp) != NULL) { | ||
98 | buflen += strlen(arg) + 1; | ||
99 | pp++; | ||
100 | } | ||
101 | out = buffer = xmalloc(buflen); | ||
102 | |||
103 | while ((arg = *argv) != NULL) { | ||
100 | int c; | 104 | int c; |
101 | 105 | ||
102 | if (!eflag) { | 106 | if (!eflag) { |
103 | /* optimization for very common case */ | 107 | /* optimization for very common case */ |
104 | fputs(arg, stdout); | 108 | out = stpcpy(out, arg); |
105 | } else while ((c = *arg++)) { | 109 | } else while ((c = *arg++)) { |
106 | if (c == eflag) { /* Check for escape seq. */ | 110 | if (c == eflag) { /* Check for escape seq. */ |
107 | if (*arg == 'c') { | 111 | if (*arg == 'c') { |
108 | /* '\c' means cancel newline and | 112 | /* '\c' means cancel newline and |
109 | * ignore all subsequent chars. */ | 113 | * ignore all subsequent chars. */ |
110 | goto ret; | 114 | goto do_write; |
111 | } | 115 | } |
112 | #if !ENABLE_FEATURE_FANCY_ECHO | 116 | #if !ENABLE_FEATURE_FANCY_ECHO |
113 | /* SUSv3 specifies that octal escapes must begin with '0'. */ | 117 | /* SUSv3 specifies that octal escapes must begin with '0'. */ |
@@ -127,21 +131,26 @@ int echo_main(int argc UNUSED_PARAM, char **argv) | |||
127 | c = bb_process_escape_sequence(&arg); | 131 | c = bb_process_escape_sequence(&arg); |
128 | } | 132 | } |
129 | } | 133 | } |
130 | bb_putchar(c); | 134 | *out++ = c; |
131 | } | 135 | } |
132 | 136 | ||
133 | arg = *++argv; | 137 | if (!*++argv) |
134 | if (!arg) | ||
135 | break; | 138 | break; |
136 | bb_putchar(' '); | 139 | *out++ = ' '; |
137 | } | 140 | } |
138 | 141 | ||
139 | newline_ret: | ||
140 | if (nflag) { | 142 | if (nflag) { |
141 | bb_putchar('\n'); | 143 | *out++ = '\n'; |
142 | } | 144 | } |
143 | ret: | 145 | |
144 | return fflush_all(); | 146 | do_write: |
147 | r = full_write(STDOUT_FILENO, buffer, out - buffer); | ||
148 | free(buffer); | ||
149 | if (r < 0) { | ||
150 | bb_perror_msg(bb_msg_write_error); | ||
151 | return 1; | ||
152 | } | ||
153 | return 0; | ||
145 | } | 154 | } |
146 | 155 | ||
147 | /*- | 156 | /*- |
diff --git a/shell/ash_test/ash-misc/echo_write_error.right b/shell/ash_test/ash-misc/echo_write_error.right new file mode 100644 index 000000000..3e91a13d3 --- /dev/null +++ b/shell/ash_test/ash-misc/echo_write_error.right | |||
@@ -0,0 +1,2 @@ | |||
1 | ash: write error: Broken pipe | ||
2 | Ok: 1 | ||
diff --git a/shell/ash_test/ash-misc/echo_write_error.tests b/shell/ash_test/ash-misc/echo_write_error.tests new file mode 100644 index 000000000..0a40c9ff7 --- /dev/null +++ b/shell/ash_test/ash-misc/echo_write_error.tests | |||
@@ -0,0 +1,7 @@ | |||
1 | trap "" PIPE | ||
2 | |||
3 | { | ||
4 | sleep 1 | ||
5 | echo Cant write this - get EPIPE | ||
6 | echo Ok: $? >&2 | ||
7 | } | { true; } | ||
diff --git a/shell/ash_test/ash-redir/redir.right b/shell/ash_test/ash-redir/redir.right index 2a02d41ce..c1a6e729a 100644 --- a/shell/ash_test/ash-redir/redir.right +++ b/shell/ash_test/ash-redir/redir.right | |||
@@ -1 +1,2 @@ | |||
1 | ash: write error: Bad file descriptor | ||
1 | TEST | 2 | TEST |
diff --git a/shell/hush.c b/shell/hush.c index 10788b8e7..e857e7464 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -1418,6 +1418,7 @@ static void sigexit(int sig) | |||
1418 | static void hush_exit(int exitcode) NORETURN; | 1418 | static void hush_exit(int exitcode) NORETURN; |
1419 | static void hush_exit(int exitcode) | 1419 | static void hush_exit(int exitcode) |
1420 | { | 1420 | { |
1421 | fflush_all(); | ||
1421 | if (G.exiting <= 0 && G.traps && G.traps[0] && G.traps[0][0]) { | 1422 | if (G.exiting <= 0 && G.traps && G.traps[0] && G.traps[0][0]) { |
1422 | /* Prevent recursion: | 1423 | /* Prevent recursion: |
1423 | * trap "echo Hi; exit" EXIT; exit | 1424 | * trap "echo Hi; exit" EXIT; exit |
@@ -6105,10 +6106,13 @@ static void exec_builtin(char ***to_free, | |||
6105 | char **argv) | 6106 | char **argv) |
6106 | { | 6107 | { |
6107 | #if BB_MMU | 6108 | #if BB_MMU |
6108 | int rcode = x->b_function(argv); | 6109 | int rcode; |
6110 | fflush_all(); | ||
6111 | rcode = x->b_function(argv); | ||
6109 | fflush_all(); | 6112 | fflush_all(); |
6110 | _exit(rcode); | 6113 | _exit(rcode); |
6111 | #else | 6114 | #else |
6115 | fflush_all(); | ||
6112 | /* On NOMMU, we must never block! | 6116 | /* On NOMMU, we must never block! |
6113 | * Example: { sleep 99 | read line; } & echo Ok | 6117 | * Example: { sleep 99 | read line; } & echo Ok |
6114 | */ | 6118 | */ |
@@ -6832,6 +6836,7 @@ static NOINLINE int run_pipe(struct pipe *pi) | |||
6832 | if (!funcp) { | 6836 | if (!funcp) { |
6833 | debug_printf_exec(": builtin '%s' '%s'...\n", | 6837 | debug_printf_exec(": builtin '%s' '%s'...\n", |
6834 | x->b_cmd, argv_expanded[1]); | 6838 | x->b_cmd, argv_expanded[1]); |
6839 | fflush_all(); | ||
6835 | rcode = x->b_function(argv_expanded) & 0xff; | 6840 | rcode = x->b_function(argv_expanded) & 0xff; |
6836 | fflush_all(); | 6841 | fflush_all(); |
6837 | } | 6842 | } |
@@ -7641,6 +7646,7 @@ int hush_main(int argc, char **argv) | |||
7641 | G.global_argc -= builtin_argc; /* skip [BARGV...] "" */ | 7646 | G.global_argc -= builtin_argc; /* skip [BARGV...] "" */ |
7642 | G.global_argv += builtin_argc; | 7647 | G.global_argv += builtin_argc; |
7643 | G.global_argv[-1] = NULL; /* replace "" */ | 7648 | G.global_argv[-1] = NULL; /* replace "" */ |
7649 | fflush_all(); | ||
7644 | G.last_exitcode = x->b_function(argv + optind - 1); | 7650 | G.last_exitcode = x->b_function(argv + optind - 1); |
7645 | } | 7651 | } |
7646 | goto final_return; | 7652 | goto final_return; |
diff --git a/shell/hush_test/hush-misc/echo_write_error.right b/shell/hush_test/hush-misc/echo_write_error.right new file mode 100644 index 000000000..ddcad4363 --- /dev/null +++ b/shell/hush_test/hush-misc/echo_write_error.right | |||
@@ -0,0 +1,2 @@ | |||
1 | hush: write error: Broken pipe | ||
2 | Ok: 1 | ||
diff --git a/shell/hush_test/hush-misc/echo_write_error.tests b/shell/hush_test/hush-misc/echo_write_error.tests new file mode 100755 index 000000000..0a40c9ff7 --- /dev/null +++ b/shell/hush_test/hush-misc/echo_write_error.tests | |||
@@ -0,0 +1,7 @@ | |||
1 | trap "" PIPE | ||
2 | |||
3 | { | ||
4 | sleep 1 | ||
5 | echo Cant write this - get EPIPE | ||
6 | echo Ok: $? >&2 | ||
7 | } | { true; } | ||