Index: usr.bin/sponge/Makefile =================================================================== --- /dev/null +++ usr.bin/sponge/Makefile @@ -0,0 +1,5 @@ +# $FreeBSD$ + +PROG= sponge + +.include Index: usr.bin/sponge/sponge.1 =================================================================== --- /dev/null +++ usr.bin/sponge/sponge.1 @@ -0,0 +1,75 @@ +.\" Eitan Adler. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd November 1, 2017 +.Dt SPONGE 1 +.Os +.Sh NAME +.Nm sponge +.Nd buffer stdin and write to stdout +.Sh SYNOPSIS +.Nm +.Op Fl a +.Ar filename +.Sh DESCRIPTION +The +.Nm +utility reads standard in until complete, then opens +the output file and writes to it. +This makes it useful in pipelines that read a file and then write to it. +These options are available: +.Bl -tag -width indent +.It Fl a +Open +.Ar filename +in append mode. +.El +.Pp +If an attempt to allocate memory fails, +.Nm +fails without output. +The file iswritten even if earlier components +of the pipeline failed. +.Sh SEE ALSO +.Xr builtin 1 , +.Xr csh 1 , +.Xr getrusage 2 , +.Xr tee 1 , +.Xr wait 2 +.Sh EXIT STATUS +.Ex -std +.Sh EXAMPLES +A +.Pa file +can be be sorted "in place" by executing +.Cm sort file | sponge file +.Sh HISTORY +The +.Nm +utility was written by +.An Eitan Adler Aq Mt eadler@FreeBSD.org +and first appeared +in +.Fx 12.0 . Index: usr.bin/sponge/sponge.c =================================================================== --- /dev/null +++ usr.bin/sponge/sponge.c @@ -0,0 +1,156 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEFAULT_BUF_SIZE 16384 +#define DEFAULT_BUF_CNT 12 + +static int flag_append = 0; + +static void usage(void); +static void *safe_malloc(size_t size); +static void *safe_calloc(size_t count, size_t size); +static void *safe_reallocf(void *ptr, size_t size); + +static void * +safe_malloc(size_t size) +{ + void *ret; + + ret = malloc(size); + if (ret == NULL) { + err(1, "malloc failed"); + } + return (ret); +} + +static void * +safe_calloc(size_t count, size_t size) +{ + void *ret; + + ret = calloc(count, size); + if (ret == NULL) { + err(1, "calloc failed"); + } + return (ret); +} + +static void * +safe_reallocf(void *ptr, size_t size) +{ + void *ret; + + ret = reallocf(ptr, size); + if (ret == NULL) { + err(1, "reallocf failed"); + } + return (ret); +} + +static void +usage(void) +{ + fprintf(stderr, "usage: sponge [-a] filename\n"); +} + +int +main(int argc, char* argv[]) +{ + struct iovec *iov; + char *buf; + char *outfile; + ssize_t i; + size_t bufcnt; + size_t whichbuf; + size_t bufremain; + long maxiovec; + int fd; + int openflags = O_WRONLY; + int opt; + + while ((opt = getopt(argc, argv, "ah")) != -1) { + switch (opt) { + case 'a': + flag_append = 1; + break; + case 'h': + usage(); + exit(0); + case '?': + default: + usage(); + exit(1); + } + } + + if (optind < argc) { + outfile = argv[optind]; + } + + + bufcnt = DEFAULT_BUF_CNT; + whichbuf = 0; + iov = safe_calloc(bufcnt, sizeof(*iov)); + + for (;;) { + buf = safe_malloc(DEFAULT_BUF_SIZE); + i = read(STDIN_FILENO, buf, DEFAULT_BUF_SIZE); + if (whichbuf == bufcnt) { + bufcnt *= 2; + iov = safe_reallocf(iov, bufcnt * sizeof(*iov)); + } + if (i < 0) { + err(1, "read failed"); + } + if (i == 0) { + free(buf); + break; + } + iov[whichbuf].iov_base = buf; + iov[whichbuf].iov_len = i; + whichbuf++; + } + + if (outfile) { + if (flag_append) { + openflags |= O_APPEND; + } else { + openflags |= O_TRUNC; + } + fd = open(outfile, openflags); + } + else { + fd = STDOUT_FILENO; + } + + if (fd < 0) { + err(1, "failed to open"); + } + + maxiovec = sysconf(_SC_IOV_MAX); + bufcnt = whichbuf; + bufremain = bufcnt; + + while (bufremain > 0) { + whichbuf = (bufremain < maxiovec) ? bufremain : maxiovec; + bufremain -= whichbuf; + + i = writev(fd, iov, whichbuf); + if (i < 0) { + err(1, "failed to write"); + } + } + + if (outfile) { + i = close(fd); + if (i < 0) { + err(1, "failed to close"); + } + } +} Index: usr.bin/tee/tee.1 =================================================================== --- usr.bin/tee/tee.1 +++ usr.bin/tee/tee.1 @@ -72,6 +72,8 @@ except in the event of the .Fl i option. +.Sh SEE ALSO +.Xr sponge 1 .Sh EXIT STATUS .Ex -std .Sh STANDARDS