X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=coreutils%2Fecho.c;h=72e18c161209419894528f91e30a53885a16edb8;hb=26d11b8133b3ba71e6e94ff525e45984ddeaef72;hp=decca095f25fc1dfa4f92a299e8a183cb731ba20;hpb=6a0ad2506116f4ddc3f9f617a90ba04a57eeef88;p=oweals%2Fbusybox.git diff --git a/coreutils/echo.c b/coreutils/echo.c index decca095f..72e18c161 100644 --- a/coreutils/echo.c +++ b/coreutils/echo.c @@ -5,7 +5,7 @@ * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * - * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + * Licensed under GPLv2 or later, see file LICENSE in this source tree. * * Original copyright notice is retained at the end of this file. */ @@ -29,122 +29,135 @@ /* 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; #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 ((arg = *++argv) != NULL) { + char n, e; - while (1) { - arg = *++argv; - if (!arg) - goto newline_ret; - if (*arg != '-') - break; + if (arg[0] != '-') + break; /* not an option arg, echo it */ /* If it appears that we are handling options, then make sure * that all of the options specified are actually valid. * Otherwise, the string should just be echoed. */ - p = arg + 1; - if (!*p) /* A single '-', so echo it. */ - goto just_echo; - + arg++; + n = nflag; + e = eflag; do { - if (!strrchr("neE", *p)) + if (*arg == 'n') + n = 0; + else if (*arg == 'e') + e = '\\'; + else if (*arg != 'E') { + /* "-ccc" arg with one of c's invalid, echo it */ + /* arg consisting from just "-" also handled here */ goto just_echo; - } while (*++p); - - /* All of the options in this arg are valid, so handle them. */ - p = arg + 1; - do { - if (*p == 'n') - nflag = 0; - if (*p == 'e') - eflag = '\\'; - } while (*++p); + } + } while (*++arg); + nflag = n; + eflag = e; } just_echo: #endif - while (1) { - /* arg is already == *argv and isn't NULL */ + + buflen = 0; + pp = argv; + while ((arg = *pp) != NULL) { + buflen += strlen(arg) + 1; + pp++; + } + out = buffer = xmalloc(buflen + 1); /* +1 is needed for "no args" case */ + + while ((arg = *argv) != NULL) { int c; if (!eflag) { /* optimization for very common case */ - fputs(arg, stdout); - } else while ((c = *arg++)) { - if (c == eflag) { /* Check for escape seq. */ + out = stpcpy(out, arg); + } else + while ((c = *arg++) != '\0') { + if (c == eflag) { + /* This is an "\x" sequence */ + if (*arg == 'c') { - /* '\c' means cancel newline and + /* "\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'. */ - if ( ((int)(unsigned char)(*arg) - '0') >= 8) /* '8' or bigger */ -#endif - { - /* Since SUSv3 mandates a first digit of 0, 4-digit octals - * of the form \0### are accepted. */ - if (*arg == '0') { - /* NB: don't turn "...\0" into "...\" */ - if (arg[1] && ((unsigned char)(arg[1]) - '0') < 8) { - arg++; - } + /* Since SUSv3 mandates a first digit of 0, 4-digit octals + * of the form \0### are accepted. */ + if (*arg == '0') { + if ((unsigned char)(arg[1] - '0') < 8) { + /* 2nd char is 0..7: skip leading '0' */ + arg++; } - /* bb_process_escape_sequence handles NUL correctly - * ("...\" case). */ - c = bb_process_escape_sequence(&arg); + } + /* bb_process_escape_sequence handles NUL correctly + * ("...\" case). */ + { + /* optimization: don't force arg to be on-stack, + * use another variable for that. ~30 bytes win */ + const char *z = arg; + c = bb_process_escape_sequence(&z); + arg = z; } } - 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(stdout); + + do_write: + /* Careful to error out on partial writes too (think ENOSPC!) */ + errno = 0; + /*r =*/ full_write(STDOUT_FILENO, buffer, out - buffer); + free(buffer); + if (/*WRONG:r < 0*/ errno) { + bb_perror_msg(bb_msg_write_error); + return 1; + } + return 0; } -/*- +/* * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * @@ -230,7 +243,7 @@ int echo_main(int argc, char **argv) goto just_echo; do { - if (!strrchr("neE", *p)) + if (!strchr("neE", *p)) goto just_echo; } while (*++p); @@ -256,27 +269,23 @@ int echo_main(int argc, char **argv) /* optimization for very common case */ p += strlen(arg); } else while ((c = *arg++)) { - if (c == eflag) { /* Check for escape seq. */ + if (c == eflag) { + /* This is an "\x" sequence */ + if (*arg == 'c') { - /* '\c' means cancel newline and + /* "\c" means cancel newline and * ignore all subsequent chars. */ cur_io->iov_len = p - (char*)cur_io->iov_base; cur_io++; goto ret; } -#if !ENABLE_FEATURE_FANCY_ECHO - /* SUSv3 specifies that octal escapes must begin with '0'. */ - if ( (((unsigned char)*arg) - '1') >= 7) -#endif - { - /* Since SUSv3 mandates a first digit of 0, 4-digit octals - * of the form \0### are accepted. */ - if (*arg == '0' && ((unsigned char)(arg[1]) - '0') < 8) { - arg++; - } - /* bb_process_escape_sequence can handle nul correctly */ - c = bb_process_escape_sequence( (void*) &arg); + /* Since SUSv3 mandates a first digit of 0, 4-digit octals + * of the form \0### are accepted. */ + if (*arg == '0' && (unsigned char)(arg[1] - '0') < 8) { + arg++; } + /* bb_process_escape_sequence can handle nul correctly */ + c = bb_process_escape_sequence( (void*) &arg); } *p++ = c; }