From 6e16a2036ec72d55ca6e280105aad2d2705cc91f Mon Sep 17 00:00:00 2001 From: Nicholas De Nova Date: Sun, 25 Sep 2016 20:07:27 -0500 Subject: [PATCH] Add tee(1). --- utils/.gitignore | 1 + utils/Makefile | 1 + utils/tee.c | 157 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 159 insertions(+) create mode 100644 utils/tee.c diff --git a/utils/.gitignore b/utils/.gitignore index 5c8d51d1..8a5d2ffe 100644 --- a/utils/.gitignore +++ b/utils/.gitignore @@ -42,6 +42,7 @@ sleep sort stat tail +tee time touch tr diff --git a/utils/Makefile b/utils/Makefile index 359d0c6a..53f10fe3 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -54,6 +54,7 @@ sleep \ sort \ stat \ tail \ +tee \ time \ touch \ tr \ diff --git a/utils/tee.c b/utils/tee.c new file mode 100644 index 00000000..67399491 --- /dev/null +++ b/utils/tee.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2016 Nicholas De Nova. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * tee.c + * Write stdin to stdout and the specified output files. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void compact_arguments(int* argc, char*** argv) +{ + for ( int i = 0; i < *argc; i++ ) + { + while ( i < *argc && !(*argv)[i] ) + { + for ( int n = i; n < *argc; n++ ) + (*argv)[n] = (*argv)[n+1]; + (*argc)--; + } + } +} + +int main(int argc, char* argv[]) +{ + bool append = false; + bool ignore_interrupts = false; + + for ( int i = 1; i < argc; i++ ) + { + const char* arg = argv[i]; + if ( arg[0] != '-' || !arg[1] ) + continue; + argv[i] = NULL; + if ( !strcmp(arg, "--") ) + break; + if ( arg[1] != '-' ) + { + char c; + while ( (c = *++arg) ) + switch ( c ) + { + case 'a': append = true; break; + case 'i': ignore_interrupts = true; break; + default: + errx(1, "unknown option -- '%c'", c); + } + } + else if ( !strcmp(arg, "--append") ) + append = true; + else if ( !strcmp(arg, "--ignore-interrupts") ) + ignore_interrupts = true; + else + errx(1, "unknown option: %s", arg); + } + + compact_arguments(&argc, &argv); + + if ( ignore_interrupts ) + signal(SIGINT, SIG_IGN); + + int files_count = argc - 1; + char buffer[65536]; + bool ok = true; + bool stdout_ok = true; + + int* fds = malloc(files_count * sizeof(*fds)); + if ( !fds ) + err(1, "malloc"); + + for ( int i = 0; i < files_count; i++ ) + { + int oflags = O_CREAT | O_WRONLY | (append ? O_APPEND : O_TRUNC); + fds[i] = open(argv[i + 1], oflags, 0666); + if ( fds[i] < 0 ) + { + warn("%s", argv[i + 1]); + ok = false; + } + } + + while ( true ) + { + ssize_t bytes_read = read(0, buffer, sizeof(buffer)); + if ( bytes_read < 0 ) + err(1, "stdin"); + if ( bytes_read == 0 ) + break; + ssize_t stdout_written = 0; + while ( stdout_written < bytes_read ) + { + ssize_t stdout_left = bytes_read - stdout_written; + ssize_t stdout_amount; + + if ( stdout_ok ) + { + stdout_amount = write(1, buffer + stdout_written, stdout_left); + if ( stdout_amount < 0 ) + { + warn("stdout"); + stdout_ok = false; + ok = false; + } + } + + if ( !stdout_ok ) + stdout_amount = stdout_left; + + for ( int i = 0; i < files_count; i++ ) + { + if ( fds[i] < 0 ) + continue; + + ssize_t bytes_written = 0; + while ( bytes_written < stdout_amount ) + { + char* outgoing = buffer + stdout_written + bytes_written; + size_t left = stdout_amount - bytes_written; + ssize_t amount = write(fds[i], outgoing, left); + if ( amount < 0 ) + { + warn("%s", argv[i + 1]); + close(fds[i]); + fds[i] = -1; + ok = false; + break; + } + bytes_written += amount; + } + } + stdout_written += stdout_amount; + } + } + + return ok ? 0 : 1; +}