diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2007-11-22 08:16:57 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2007-11-22 08:16:57 +0000 |
commit | 7d75a96b15327050a294b1f54981781ce6e551e2 (patch) | |
tree | df8a1fcb74b6bd7869fe60af461c11f0684df043 /coreutils/echo.c | |
parent | 705eaf8b403555741cf6313a76da8597ae54d324 (diff) | |
download | busybox-w32-7d75a96b15327050a294b1f54981781ce6e551e2.tar.gz busybox-w32-7d75a96b15327050a294b1f54981781ce6e551e2.tar.bz2 busybox-w32-7d75a96b15327050a294b1f54981781ce6e551e2.zip |
ash: fix bug where redirection of closed fd was leaving it open afterwards.
redirect 983 1024 +41
bb_echo 276 301 +25
popredir 118 132 +14
evalcommand 1163 1176 +13
bbunpack 358 366 +8
echocmd 13 5 -8
echo_main 13 5 -8
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 5/2 up/down: 101/-16) Total: 85 bytes
text data bss dec hex filename
774999 962 9236 785197 bfb2d busybox_old
775084 962 9236 785282 bfb82 busybox_unstripped
Diffstat (limited to 'coreutils/echo.c')
-rw-r--r-- | coreutils/echo.c | 141 |
1 files changed, 139 insertions, 2 deletions
diff --git a/coreutils/echo.c b/coreutils/echo.c index 860853f02..4c7a91743 100644 --- a/coreutils/echo.c +++ b/coreutils/echo.c | |||
@@ -25,7 +25,9 @@ | |||
25 | 25 | ||
26 | #include "libbb.h" | 26 | #include "libbb.h" |
27 | 27 | ||
28 | int bb_echo(char **argv) | 28 | /* argc is unused, but removing it precludes compiler from |
29 | * using call -> jump optimization */ | ||
30 | int bb_echo(int argc, char **argv) | ||
29 | { | 31 | { |
30 | const char *arg; | 32 | const char *arg; |
31 | #if !ENABLE_FEATURE_FANCY_ECHO | 33 | #if !ENABLE_FEATURE_FANCY_ECHO |
@@ -33,6 +35,18 @@ int bb_echo(char **argv) | |||
33 | eflag = '\\', | 35 | eflag = '\\', |
34 | nflag = 1, /* 1 -- print '\n' */ | 36 | nflag = 1, /* 1 -- print '\n' */ |
35 | }; | 37 | }; |
38 | |||
39 | /* We must check that stdout is not closed. | ||
40 | * The reason for this is highly non-obvious. | ||
41 | * bb_echo is used from shell. Shell must correctly handle "echo foo" | ||
42 | * if stdout is closed. With stdio, output gets shoveled into | ||
43 | * stdout buffer, and even fflush cannot clear it out. It seems that | ||
44 | * even if libc receives EBADF on write attempts, it feels determined | ||
45 | * to output data no matter what. So it will try later, | ||
46 | * and possibly will clobber future output. Not good. */ | ||
47 | if (dup2(1, 1) != 1) | ||
48 | return -1; | ||
49 | |||
36 | arg = *++argv; | 50 | arg = *++argv; |
37 | if (!arg) | 51 | if (!arg) |
38 | goto newline_ret; | 52 | goto newline_ret; |
@@ -41,6 +55,10 @@ int bb_echo(char **argv) | |||
41 | char nflag = 1; | 55 | char nflag = 1; |
42 | char eflag = 0; | 56 | char eflag = 0; |
43 | 57 | ||
58 | /* We must check that stdout is not closed. */ | ||
59 | if (dup2(1, 1) != 1) | ||
60 | return -1; | ||
61 | |||
44 | while (1) { | 62 | while (1) { |
45 | arg = *++argv; | 63 | arg = *++argv; |
46 | if (!arg) | 64 | if (!arg) |
@@ -122,7 +140,7 @@ int bb_echo(char **argv) | |||
122 | int echo_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 140 | int echo_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
123 | int echo_main(int argc, char **argv) | 141 | int echo_main(int argc, char **argv) |
124 | { | 142 | { |
125 | return bb_echo(argv); | 143 | return bb_echo(argc, argv); |
126 | } | 144 | } |
127 | 145 | ||
128 | /*- | 146 | /*- |
@@ -163,3 +181,122 @@ int echo_main(int argc, char **argv) | |||
163 | * | 181 | * |
164 | * @(#)echo.c 8.1 (Berkeley) 5/31/93 | 182 | * @(#)echo.c 8.1 (Berkeley) 5/31/93 |
165 | */ | 183 | */ |
184 | |||
185 | #ifdef VERSION_WITH_WRITEV | ||
186 | /* We can't use stdio. | ||
187 | * The reason for this is highly non-obvious. | ||
188 | * bb_echo is used from shell. Shell must correctly handle "echo foo" | ||
189 | * if stdout is closed. With stdio, output gets shoveled into | ||
190 | * stdout buffer, and even fflush cannot clear it out. It seems that | ||
191 | * even if libc receives EBADF on write attempts, it feels determined | ||
192 | * to output data no matter what. So it will try later, | ||
193 | * and possibly will clobber future output. Not good. | ||
194 | * | ||
195 | * Using writev instead, with 'direct' conversion of argv vector. | ||
196 | */ | ||
197 | |||
198 | int bb_echo(int argc, char **argv) | ||
199 | { | ||
200 | struct iovec io[argc]; | ||
201 | struct iovec *cur_io = io; | ||
202 | char *arg; | ||
203 | char *p; | ||
204 | #if !ENABLE_FEATURE_FANCY_ECHO | ||
205 | enum { | ||
206 | eflag = '\\', | ||
207 | nflag = 1, /* 1 -- print '\n' */ | ||
208 | }; | ||
209 | arg = *++argv; | ||
210 | if (!arg) | ||
211 | goto newline_ret; | ||
212 | #else | ||
213 | char nflag = 1; | ||
214 | char eflag = 0; | ||
215 | |||
216 | while (1) { | ||
217 | arg = *++argv; | ||
218 | if (!arg) | ||
219 | goto newline_ret; | ||
220 | if (*arg != '-') | ||
221 | break; | ||
222 | |||
223 | /* If it appears that we are handling options, then make sure | ||
224 | * that all of the options specified are actually valid. | ||
225 | * Otherwise, the string should just be echoed. | ||
226 | */ | ||
227 | p = arg + 1; | ||
228 | if (!*p) /* A single '-', so echo it. */ | ||
229 | goto just_echo; | ||
230 | |||
231 | do { | ||
232 | if (!strrchr("neE", *p)) | ||
233 | goto just_echo; | ||
234 | } while (*++p); | ||
235 | |||
236 | /* All of the options in this arg are valid, so handle them. */ | ||
237 | p = arg + 1; | ||
238 | do { | ||
239 | if (*p == 'n') | ||
240 | nflag = 0; | ||
241 | if (*p == 'e') | ||
242 | eflag = '\\'; | ||
243 | } while (*++p); | ||
244 | } | ||
245 | just_echo: | ||
246 | #endif | ||
247 | |||
248 | while (1) { | ||
249 | /* arg is already == *argv and isn't NULL */ | ||
250 | int c; | ||
251 | |||
252 | cur_io->iov_base = p = arg; | ||
253 | |||
254 | if (!eflag) { | ||
255 | /* optimization for very common case */ | ||
256 | p += strlen(arg); | ||
257 | } else while ((c = *arg++)) { | ||
258 | if (c == eflag) { /* Check for escape seq. */ | ||
259 | if (*arg == 'c') { | ||
260 | /* '\c' means cancel newline and | ||
261 | * ignore all subsequent chars. */ | ||
262 | cur_io->iov_len = p - (char*)cur_io->iov_base; | ||
263 | cur_io++; | ||
264 | goto ret; | ||
265 | } | ||
266 | #if !ENABLE_FEATURE_FANCY_ECHO | ||
267 | /* SUSv3 specifies that octal escapes must begin with '0'. */ | ||
268 | if ( (((unsigned char)*arg) - '1') >= 7) | ||
269 | #endif | ||
270 | { | ||
271 | /* Since SUSv3 mandates a first digit of 0, 4-digit octals | ||
272 | * of the form \0### are accepted. */ | ||
273 | if (*arg == '0' && ((unsigned char)(arg[1]) - '0') < 8) { | ||
274 | arg++; | ||
275 | } | ||
276 | /* bb_process_escape_sequence can handle nul correctly */ | ||
277 | c = bb_process_escape_sequence( (void*) &arg); | ||
278 | } | ||
279 | } | ||
280 | *p++ = c; | ||
281 | } | ||
282 | |||
283 | arg = *++argv; | ||
284 | if (arg) | ||
285 | *p++ = ' '; | ||
286 | cur_io->iov_len = p - (char*)cur_io->iov_base; | ||
287 | cur_io++; | ||
288 | if (!arg) | ||
289 | break; | ||
290 | } | ||
291 | |||
292 | newline_ret: | ||
293 | if (nflag) { | ||
294 | cur_io->iov_base = (char*)"\n"; | ||
295 | cur_io->iov_len = 1; | ||
296 | cur_io++; | ||
297 | } | ||
298 | ret: | ||
299 | /* TODO: implement and use full_writev? */ | ||
300 | return writev(1, io, (cur_io - io)) >= 0; | ||
301 | } | ||
302 | #endif | ||