nicolas / debian.moreutils (public) (License: GPL-2, GPL-2+, Expat, BSD-2-Clause, Public Domain) (since 2018-09-25) (hash sha1)
Debian packaging of joeyh's moreutils

/sponge.c (09d3971fdb020de4da825625bd6dbbec8521d803) (9200 bytes) (mode 100644) (type blob)

/*
 *  sponge.c - read in all available info from stdin, then output it to
 *  file named on the command line
 *
 *  Copyright ©  2006  Tollef Fog Heen
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  version 2 as published by the Free Software Foundation.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
 * USA
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
/* MAX() */
#include <sys/param.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <sys/resource.h>
/* SIZE_MAX */
#include <stdint.h> 
#include <signal.h>
#include <getopt.h>

#include "physmem.c"

#define BUFF_SIZE           8192
#define MIN_SPONGE_SIZE     BUFF_SIZE
char *tmpname = NULL;

void usage() {
	printf("sponge [-a] <file>: soak up all input from stdin and write it "
	       "to <file>\n");
	exit(0);
}

/* all the signal stuff copied from gnu sort */

/* The set of signals that are caught.  */
static sigset_t caught_signals;

/* Critical section status.  */
struct cs_status {
	int valid; // was bool
	sigset_t sigs;
};

/* Enter a critical section.  */
static struct cs_status cs_enter (void) {
	struct cs_status status;
	status.valid = (sigprocmask(SIG_BLOCK, &caught_signals, &status.sigs) == 0);
	return status;
}

/* Leave a critical section.  */
static void cs_leave (struct cs_status status) {
	if (status.valid) {
		/* Ignore failure when restoring the signal mask. */
		sigprocmask(SIG_SETMASK, &status.sigs, NULL);
	}
}

static void cleanup () {
	if (tmpname) {
		unlink(tmpname);
	}
}

static void onexit_cleanup (void) {
	struct cs_status cs = cs_enter();
	cleanup();
	cs_leave(cs);
}

static void sighandler (int sig) {
	if (! SA_NOCLDSTOP)
		signal(sig, SIG_IGN);

	cleanup();

	signal(sig, SIG_DFL);
	raise(sig);
}

/* taken from coreutils sort */
static size_t default_sponge_size (void) {
	/* Let MEM be available memory or 1/8 of total memory, whichever
	   is greater.  */
	double avail = physmem_available();
	double total = physmem_total();
	double mem = MAX(avail, total / 8);
	struct rlimit rlimit;

	/* Let SIZE be MEM, but no more than the maximum object size or
	   system resource limits.  Avoid the MIN macro here, as it is not
	   quite right when only one argument is floating point.  Don't
	   bother to check for values like RLIM_INFINITY since in practice
	   they are not much less than SIZE_MAX.  */
	size_t size = SIZE_MAX;
	if (mem < size)
		size = mem;
	if (getrlimit(RLIMIT_DATA, &rlimit) == 0 && rlimit.rlim_cur < size)
		size = rlimit.rlim_cur;
#ifdef RLIMIT_AS
	if (getrlimit(RLIMIT_AS, &rlimit) == 0 && rlimit.rlim_cur < size)
		size = rlimit.rlim_cur;
#endif

	/* Leave a large safety margin for the above limits, as failure can
	   occur when they are exceeded.  */
	size /= 2;

#ifdef RLIMIT_RSS
	/* Leave a 1/16 margin for RSS to leave room for code, stack, etc.
	   Exceeding RSS is not fatal, but can be quite slow.  */
	if (getrlimit(RLIMIT_RSS, &rlimit) == 0 && rlimit.rlim_cur / 16 * 15 < size)
		size = rlimit.rlim_cur / 16 * 15;
#endif

	/* Use no less than the minimum. */
	return MAX (size, MIN_SPONGE_SIZE);
}

void trapsignals (void) {
	ssize_t i = 0;
	static int const sig[] = {
		/* The usual suspects.  */
		SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGQUIT, SIGTERM,
#ifdef SIGPOLL
		SIGPOLL,
#endif
#ifdef SIGPROF
		SIGPROF,
#endif
#ifdef SIGVTALRM
		SIGVTALRM,
#endif
#ifdef SIGXCPU
		SIGXCPU,
#endif
#ifdef SIGXFSZ
		SIGXFSZ,
#endif
	};
	int nsigs = sizeof(sig) / sizeof(sig[0]);

#if SA_NOCLDSTOP
	struct sigaction act;

	sigemptyset(&caught_signals);
	for (i = 0; i < nsigs; i++) {
		sigaction(sig[i], NULL, &act);
		if (act.sa_handler != SIG_IGN)
			sigaddset(&caught_signals, sig[i]);
		}
		
		act.sa_handler = sighandler;
		act.sa_mask = caught_signals;
		act.sa_flags = 0;

		for (i = 0; i < nsigs; i++)
			if (sigismember(&caught_signals, sig[i]))
				sigaction(sig[i], &act, NULL);
#else
	for (i = 0; i < nsigs; i++)
		if (signal(sig[i], SIG_IGN) != SIG_IGN) {
			signal(sig[i], sighandler);
			siginterrupt (sig[i], 1);
		}
#endif
}

static void write_buff_tmp(char* buff, size_t length, FILE *fd) {
	if (fwrite(buff, length, 1, fd) < 1) {
		perror("error writing buffer to temporary file");
		fclose(fd);
		exit(1);
	}
}
		
static void write_buff_tmp_finish (char* buff, size_t length, FILE *fd) {
	if (length) 
		write_buff_tmp(buff, length, fd);
	if (fflush(fd) != 0) {
		perror("fflush");
		exit(1);
	}
}

static void write_buff_out (char* buff, size_t length, FILE *fd) {
	if (fwrite(buff, length, 1, fd) < 1) {
		perror("error writing buffer to output file");
		fclose(fd);
		exit(1);
	}
}

static void copy_file (FILE *infile, FILE *outfile, char *buf, size_t size) {
	ssize_t i;
	while ((i = read(fileno(infile), buf, size)) > 0) {
		write_buff_out(buf, i, outfile);
	}
	if (i == -1) {
		perror("read file");
		fclose(infile);
		exit(1);
	}
}

static void copy_tmpfile (FILE *tmpfile, FILE *outfile, char *buf, size_t size) {
	if (lseek(fileno(tmpfile), 0, SEEK_SET)) {
		perror("could to seek to start of file");
		fclose(tmpfile);
		exit(1);
	}
	copy_file(tmpfile, outfile, buf, size);
	if (fclose(tmpfile) != 0) {
		perror("read temporary file");
		exit(1);
	}
	if (fclose(outfile) != 0) {
		perror("error writing buffer to output file");
		exit(1);
	}
}

FILE *open_tmpfile (void) {
	struct cs_status cs;
	int tmpfd;
	FILE *tmpfile;
	mode_t mask;
	char *tmpdir;
	char const * const template="%s/sponge.XXXXXX";

	trapsignals();
	cs = cs_enter();
	tmpdir = getenv("TMPDIR");
	if (tmpdir == NULL)
		tmpdir = "/tmp";
	/* Subtract 2 for `%s' and add 1 for the trailing NULL. */
	tmpname=malloc(strlen(tmpdir) + strlen(template) - 2 + 1);
	if (! tmpname) {
		perror("failed to allocate memory");
		exit(1);
	}
	sprintf(tmpname, template, tmpdir);
	mask=umask(077);
	tmpfd = mkstemp(tmpname);
	umask(mask);
	atexit(onexit_cleanup); // solaris on_exit(onexit_cleanup, 0);
	cs_leave(cs);

	if (tmpfd < 0) {
		perror("mkstemp failed");
		exit(1);
	}
	tmpfile = fdopen(tmpfd, "w+");
	if (! tmpfile) {
		perror("fdopen");
		exit(1);
	}
	return tmpfile;
}

int main (int argc, char **argv) {
	char *buf, *bufstart, *outname = NULL;
	size_t bufsize = BUFF_SIZE;
	size_t bufused = 0;
	FILE *outfile, *tmpfile = 0;
	ssize_t i = 0;
	size_t mem_available = default_sponge_size();
	int tmpfile_used=0;
	int append=0;
	int opt;

	while ((opt = getopt(argc, argv, "ha")) != -1) {
		switch (opt) {
			case 'h':
				usage();
				break;
			case 'a':
				append=1;
		}
	}
	if (optind < argc)
		outname = argv[optind];
	
	tmpfile = open_tmpfile();
	bufstart = buf = malloc(bufsize);
	if (!buf) {
		perror("failed to allocate memory");
		exit(1);
	}
	while ((i = read(0, buf, bufsize - bufused)) > 0) {
		bufused = bufused+i;
		if (bufused == bufsize) {
			if ((bufsize*2) >= mem_available) {
				write_buff_tmp(bufstart, bufused, tmpfile);
				bufused = 0;
				tmpfile_used = 1;
			}
			else {
				bufsize *= 2;
				bufstart = realloc(bufstart, bufsize);
				if (!bufstart) {
					perror("failed to realloc memory");
					exit(1);
				}
			}
		}
		buf = bufstart + bufused;
	}
	if (i < 0) {
		perror("failed to read from stdin");
		exit(1);
	}

	if (outname) {
		mode_t mode;
		struct stat statbuf;
		int exists = (lstat(outname, &statbuf) == 0);
		int regfile = exists && S_ISREG(statbuf.st_mode) && ! S_ISLNK(statbuf.st_mode);

		if (append && regfile) {
			char *tmpbuf = malloc(bufsize);
			if (!tmpbuf) {
				perror("failed to allocate memory");
				exit(1);
			}
			outfile = fopen(outname, "r");
			copy_file(outfile, tmpfile, tmpbuf, bufsize);
			fclose(outfile);
		}
		
		write_buff_tmp_finish(bufstart, bufused, tmpfile);

		/* Set temp file mode to match either
		 * the old file mode, or the default file
		 * mode for a newly created file. */
		if (exists) {
			mode = statbuf.st_mode;
		}
		else {
			mode_t mask = umask(0);
			umask(mask);
			mode = 0666 & ~mask;
		}
		if (chmod(tmpname, mode) != 0) {
			perror("chmod");
			exit(1);
		}

		/* If it's a regular file, or does not yet exist,
		 * attempt a fast rename of the temp file. */
		if ((regfile || ! exists) &&
		    rename(tmpname, outname) == 0) {
			tmpname=NULL; /* don't try to cleanup tmpname */
		}
		else {	
			/* Fall back to slow copy. */
			outfile = fopen(outname, append ? "a" : "w");
			if (!outfile) {
				perror("error opening output file");
				exit(1);
			}
			copy_tmpfile(tmpfile, outfile, bufstart, bufsize);
		}
	}
	else {
		if (tmpfile_used) {
			write_buff_tmp_finish(bufstart, bufused, tmpfile);
			copy_tmpfile(tmpfile, stdout, bufstart, bufsize);
		}
		else if (bufused) {
			/* buffer direct to stdout, no tmpfile */
			write_buff_out(bufstart, bufused, stdout);
		}
	}

	return 0;
}


Mode Type Size Ref File
100644 blob 44 5d425843f23db3bb6970a55c953f345e3a8c8fe1 .gitattributes
100644 blob 17989 b7b5f53df1412df1e117607f18385b39004cdaa2 COPYING
100644 blob 1113 4f8af10c6b1b101e9fb3046bc3164ae1d6f5ab2d Makefile
100644 blob 1181 1bb90af3a954062b79c41840a150e8580dfec37a README
100755 blob 806 83a4eed00f82e3bcc81856149b47cffc4091f9aa check-isutf8
100755 blob 1330 f70091a75952981a4585a46cac6f1c73d37f0c70 chronic
100755 blob 3047 7f311d7aa58631795fcad87aa8fcc0f416a01796 combine
040000 tree - e0e5c3e9323435fc50b3822aa1ae90513ca34636 debian
100644 blob 5102 d2f68a1ac365a0c8df88d4b64a400f63fffe4c69 errno.c
100644 blob 3877 8d9b4acf28e4e85244dc43dce7fbf35bb2171f80 errno.docbook
100644 blob 13634 4210b75832b3d83f063daf7e6fff977a5478b1a4 ifdata.c
100644 blob 7339 47f414301c47a69a81694c3b5affd71261207d49 ifdata.docbook
100644 blob 3027 ff648cc55865f5bf9af76b9622b52b08a1b489fb ifne.c
100644 blob 2465 e9c45692b7b95e77ea0184732f883d278c9415f6 ifne.docbook
100644 blob 7598 971112457c834ca97fdecb5880dc7c749dc8ca44 isutf8.c
100644 blob 3088 f0d0dbe51a3e1afc2616807d631bcd15ccd07cbe isutf8.docbook
100644 blob 5471 4925409bd548b058f07defe913724868801040df lckdo.c
100644 blob 3637 3fbf797b177d6933ce106cce2e8368e5094702ab lckdo.docbook
100644 blob 5790 c5245316bf1ff46260e10caf09703f3dc7bc7db7 mispipe.c
100644 blob 2566 d65d1a45b1b47ba8d6fc7c87211cf4b26eb0feed mispipe.docbook
100644 blob 7131 af85a334d4b2e3c5750af05eedb14bba435c67be parallel.c
100644 blob 3992 87e58787b21e94d007f4f62b038296599e995f2f parallel.docbook
100644 blob 1188 a8565c0245914cfa8bc8fb2c46dcb83c7f1dd8ab pee.c
100644 blob 2324 f554ad05e29361c56ececa40f00a402559f4be9e pee.docbook
100644 blob 7301 a53a2cf1906998c91533f5f5435ceeeeb1a7cd59 physmem.c
100644 blob 9200 09d3971fdb020de4da825625bd6dbbec8521d803 sponge.c
100644 blob 2865 31bc6dbc244e5e5313a2b34871877878fce24827 sponge.docbook
100755 blob 3893 3ce73c9b1d051f42231f90a6ccad454acfdaba79 ts
100755 blob 4746 c7f12392fe90ef5c31ebf4a2952922127559073c vidir
100755 blob 1403 2bf640dbd13f46dea540cbc49b8295d07d25a883 vipe
100755 blob 2518 98d1445a5f8106f04be690f85d802a7f6decfd13 zrun
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/nicolas/debian.moreutils

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/nicolas/debian.moreutils

Clone this repository using git:
git clone git://git.rocketgit.com/user/nicolas/debian.moreutils

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main