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,74 @@ +.\" 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 01, 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, and then opens +the output file, and then writes to it. +This makes it valuable in pipelines that write back to the +same file they read. +The following options are available: +.Bl -tag -width indent +.It Fl a +Opens +.Ar filename +in append mode. +.El +.Pp +In the event that malloc fails +.Nm +fails without output. +.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 +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,145 @@ +#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) { + perror("malloc failed"); + exit(1); + } + return ret; +} + +static void* +safe_calloc(size_t count, size_t size) +{ + void* ret; + ret = calloc(count, size); + if (ret == NULL) { + perror("calloc failed"); + exit(1); + } + return ret; +} + +static void* +safe_reallocf(void *ptr, size_t size) +{ + void* ret; + ret = reallocf(ptr, size); + if (ret == NULL) { + perror("reallocf failed"); + exit(1); + } + 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; + 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)); + + while (true) { + 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) { + perror("read failed"); + exit(1); + } + if (i == 0) { + free(buf); + break; + } + iov[whichbuf].iov_base = buf; + iov[whichbuf].iov_len = i; + whichbuf++; + buf = safe_malloc(DEFAULT_BUF_SIZE); + } + + if (outfile) { + if (flag_append) { + openflags |= O_APPEND; + } else { + openflags |= O_TRUNC; + } + fd = open(outfile, openflags); + } + else { + fd = STDOUT_FILENO; + } + + if (fd < 0) { + perror("failed to open"); + exit(1); + } + + i = writev(fd, iov, whichbuf); + if (i < 0) { + perror("Failed to write"); + exit(1); + } + + if (outfile) { + close(fd); + } +} 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