diff options
Diffstat (limited to 'coreutils/echo.c')
-rw-r--r-- | coreutils/echo.c | 87 |
1 files changed, 48 insertions, 39 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 @@ /* NB: can be used by shell even if not enabled as applet */ +/* + * NB2: we don't use stdio, we need better error handing. + * Examples include writing into non-opened stdout and error on write. + * + * With stdio, output gets shoveled into stdout buffer, and even + * fflush cannot clear it out. It seems that even if libc receives + * EBADF on write attempts, it feels determined to output data no matter what. + * If echo is called by shell, it will try writing again later, and possibly + * will clobber future output. Not good. + * + * Solaris has fpurge which discards buffered input. glibc has __fpurge. + * But this function is not standard. + */ + int echo_main(int argc UNUSED_PARAM, char **argv) { + char **pp; const char *arg; + char *out; + char *buffer; + unsigned buflen; + int r; #if !ENABLE_FEATURE_FANCY_ECHO enum { eflag = '\\', nflag = 1, /* 1 -- print '\n' */ }; - /* We must check that stdout is not closed. - * The reason for this is highly non-obvious. - * echo_main is used from shell. Shell must correctly handle "echo foo" - * if stdout is closed. With stdio, output gets shoveled into - * stdout buffer, and even fflush cannot clear it out. It seems that - * even if libc receives EBADF on write attempts, it feels determined - * to output data no matter what. So it will try later, - * and possibly will clobber future output. Not good. */ -// TODO: check fcntl() & O_ACCMODE == O_WRONLY or O_RDWR? - if (fcntl(1, F_GETFL) == -1) - return 1; /* match coreutils 6.10 (sans error msg to stderr) */ - //if (dup2(1, 1) != 1) - old way - // return 1; - - arg = *++argv; - if (!arg) - goto newline_ret; + argv++; #else const char *p; char nflag = 1; char eflag = 0; - /* We must check that stdout is not closed. */ - if (fcntl(1, F_GETFL) == -1) - return 1; - - while (1) { - arg = *++argv; - if (!arg) - goto newline_ret; - if (*arg != '-') + while ((arg = *++argv) != NULL) { + if (!arg || arg[0] != '-') break; /* If it appears that we are handling options, then make sure @@ -77,7 +73,7 @@ int echo_main(int argc UNUSED_PARAM, char **argv) */ p = arg + 1; if (!*p) /* A single '-', so echo it. */ - goto just_echo; + break; do { if (!strrchr("neE", *p)) @@ -95,19 +91,27 @@ int echo_main(int argc UNUSED_PARAM, char **argv) } just_echo: #endif - while (1) { - /* arg is already == *argv and isn't NULL */ + + buflen = 1; + pp = argv; + while ((arg = *pp) != NULL) { + buflen += strlen(arg) + 1; + pp++; + } + out = buffer = xmalloc(buflen); + + while ((arg = *argv) != NULL) { int c; if (!eflag) { /* optimization for very common case */ - fputs(arg, stdout); + out = stpcpy(out, arg); } else while ((c = *arg++)) { if (c == eflag) { /* Check for escape seq. */ if (*arg == 'c') { /* '\c' means cancel newline and * ignore all subsequent chars. */ - goto ret; + goto do_write; } #if !ENABLE_FEATURE_FANCY_ECHO /* SUSv3 specifies that octal escapes must begin with '0'. */ @@ -127,21 +131,26 @@ int echo_main(int argc UNUSED_PARAM, char **argv) c = bb_process_escape_sequence(&arg); } } - bb_putchar(c); + *out++ = c; } - arg = *++argv; - if (!arg) + if (!*++argv) break; - bb_putchar(' '); + *out++ = ' '; } - newline_ret: if (nflag) { - bb_putchar('\n'); + *out++ = '\n'; } - ret: - return fflush_all(); + + do_write: + r = full_write(STDOUT_FILENO, buffer, out - buffer); + free(buffer); + if (r < 0) { + bb_perror_msg(bb_msg_write_error); + return 1; + } + return 0; } /*- |