From 2d27e4ccbbc29d6ec1b907849984cec30f24e734 Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Sat, 25 Nov 2006 23:50:28 +0000 Subject: [PATCH] tee: fix bug: argv[-1] is a no-no! bb_full_fd_action: optimize die_if_ferror: ": filename" isn't a good err msg, add "..I/O error" --- coreutils/tee.c | 76 ++++++++++++++++++++++++------------------------- include/libbb.h | 2 +- libbb/copyfd.c | 47 +++++++++++++++++++----------- libbb/xfuncs.c | 4 +-- 4 files changed, 71 insertions(+), 58 deletions(-) diff --git a/coreutils/tee.c b/coreutils/tee.c index 06c94aba6..640a231ef 100644 --- a/coreutils/tee.c +++ b/coreutils/tee.c @@ -17,67 +17,66 @@ int tee_main(int argc, char **argv) { const char *mode = "w\0a"; FILE **files; - FILE **p; - char **filenames; + FILE **fp; + char **names; + char **np; int flags; int retval = EXIT_SUCCESS; -#ifdef CONFIG_FEATURE_TEE_USE_BLOCK_IO +#if ENABLE_FEATURE_TEE_USE_BLOCK_IO ssize_t c; # define buf bb_common_bufsiz1 #else int c; #endif - flags = getopt32(argc, argv, "ia"); /* 'a' must be 2nd */ + argc -= optind; + argv += optind; mode += (flags & 2); /* Since 'a' is the 2nd option... */ if (flags & 1) { - signal(SIGINT, SIG_IGN); /* TODO - switch to sigaction.*/ + signal(SIGINT, SIG_IGN); /* TODO - switch to sigaction. */ } - /* gnu tee ignores SIGPIPE in case one of the output files is a pipe * that doesn't consume all its input. Good idea... */ - signal(SIGPIPE, SIG_IGN); /* TODO - switch to sigaction.*/ + signal(SIGPIPE, SIG_IGN); /* TODO - switch to sigaction. */ /* Allocate an array of FILE *'s, with one extra for a sentinal. */ - p = files = (FILE **)xmalloc(sizeof(FILE *) * (argc - optind + 2)); - *p = stdout; - argv += optind - 1; - filenames = argv - 1; - *filenames = (char *) bb_msg_standard_input; /* for later */ - goto GOT_NEW_FILE; + fp = files = xzalloc(sizeof(FILE *) * (argc + 2)); + np = names = argv - 1; + files[0] = stdout; + goto GOT_NEW_FILE; do { - if ((*p = fopen_or_warn(*argv, mode)) == NULL) { + *fp = fopen_or_warn(*argv, mode); + if (*fp == NULL) { retval = EXIT_FAILURE; continue; } - filenames[(int)(p - files)] = *argv; - GOT_NEW_FILE: - setbuf(*p, NULL); /* tee must not buffer output. */ - ++p; - } while (*++argv); - - *p = NULL; /* Store the sentinal value. */ - -#ifdef CONFIG_FEATURE_TEE_USE_BLOCK_IO + *np = *argv++; + GOT_NEW_FILE: + setbuf(*fp++, NULL); /* tee must not buffer output. */ + np++; + } while (*argv); + /* names[0] will be filled later */ + +#if ENABLE_FEATURE_TEE_USE_BLOCK_IO while ((c = safe_read(STDIN_FILENO, buf, BUFSIZ)) > 0) { - for (p=files ; *p ; p++) { - fwrite(buf, 1, c, *p); - } + fp = files; + do + fwrite(buf, 1, c, *fp++); + while (*fp); } - - if (c < 0) { /* Make sure read errors are signaled. */ + if (c < 0) { /* Make sure read errors are signaled. */ retval = EXIT_FAILURE; } - #else setvbuf(stdout, NULL, _IONBF, 0); while ((c = getchar()) != EOF) { - for (p=files ; *p ; p++) { - putc(c, *p); - } + fp = files; + do + putc(c, *fp++); + while (*fp); } #endif @@ -87,14 +86,15 @@ int tee_main(int argc, char **argv) * setting the first entry to stdin and checking stdout error * status with fflush_stdout_and_exit()... although fflush()ing * is unnecessary here. */ - - p = files; - *p = stdin; - do { /* Now check for (input and) output errors. */ + np = names; + fp = files; + names[0] = (char *) bb_msg_standard_input; + files[0] = stdin; + do { /* Now check for input and output errors. */ /* Checking ferror should be sufficient, but we may want to fclose. * If we do, remember not to close stdin! */ - die_if_ferror(*p, filenames[(int)(p - files)]); - } while (*++p); + die_if_ferror(*fp++, *np++); + } while (*fp); fflush_stdout_and_exit(retval); } diff --git a/include/libbb.h b/include/libbb.h index 5cba27932..61e379423 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -271,7 +271,7 @@ extern void *xmalloc_open_read_close(const char *filename, size_t *sizep); extern ssize_t safe_write(int fd, const void *buf, size_t count); extern ssize_t full_write(int fd, const void *buf, size_t count); -extern void xwrite(int fd, void *buf, size_t count); +extern void xwrite(int fd, const void *buf, size_t count); /* Reads and prints to stdout till eof, then closes FILE. Exits on error: */ extern void xprint_and_close_file(FILE *file); diff --git a/libbb/copyfd.c b/libbb/copyfd.c index 601c51ce4..c6b886647 100644 --- a/libbb/copyfd.c +++ b/libbb/copyfd.c @@ -25,37 +25,50 @@ static off_t bb_full_fd_action(int src_fd, int dst_fd, off_t size) { int status = -1; off_t total = 0; - RESERVE_CONFIG_BUFFER(buffer,BUFSIZ); + RESERVE_CONFIG_BUFFER(buffer, BUFSIZ); if (src_fd < 0) goto out; - while (!size || total < size) { - ssize_t wr, rd; - rd = safe_read(src_fd, buffer, - (!size || size - total > BUFSIZ) ? BUFSIZ : size - total); + if (!size) { + size = BUFSIZ; + status = 1; /* copy until eof */ + } + + while (1) { + ssize_t rd; - if (rd > 0) { - /* A -1 dst_fd means we need to fake it... */ - wr = (dst_fd < 0) ? rd : full_write(dst_fd, buffer, rd); + rd = safe_read(src_fd, buffer, size > BUFSIZ ? BUFSIZ : size); + + if (!rd) { /* eof - all done. */ + status = 0; + break; + } + if (rd < 0) { + bb_perror_msg(bb_msg_read_error); + break; + } + /* dst_fd == -1 is a fake, else... */ + if (dst_fd >= 0) { + ssize_t wr = full_write(dst_fd, buffer, rd); if (wr < rd) { bb_perror_msg(bb_msg_write_error); break; } - total += wr; - if (total == size) status = 0; - } else if (rd < 0) { - bb_perror_msg(bb_msg_read_error); - break; - } else { /* eof - all done. */ - status = 0; - break; + } + total += rd; + if (status < 0) { + size -= rd; + if (!size) { + status = 0; + break; + } } } out: RELEASE_CONFIG_BUFFER(buffer); - return status ? status : total; + return status ? -1 : total; } diff --git a/libbb/xfuncs.c b/libbb/xfuncs.c index 1dbd7521b..773e718b8 100644 --- a/libbb/xfuncs.c +++ b/libbb/xfuncs.c @@ -124,7 +124,7 @@ int ndelay_on(int fd) } // Die with an error message if we can't write the entire buffer. -void xwrite(int fd, void *buf, size_t count) +void xwrite(int fd, const void *buf, size_t count) { if (count) { ssize_t size = full_write(fd, buf, count); @@ -146,7 +146,7 @@ off_t xlseek(int fd, off_t offset, int whence) void die_if_ferror(FILE *fp, const char *fn) { if (ferror(fp)) { - bb_error_msg_and_die("%s", fn); + bb_error_msg_and_die("%s: I/O error", fn); } } -- 2.25.1