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 (dcd4abaaa37c30c00b15f3f0ef64391f90231a73) (7736 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 "physmem.c"

#define BUFF_SIZE           8192
#define MIN_SPONGE_SIZE     BUFF_SIZE
#define DEFAULT_TMP_NAME    "/tmp/sponge.XXXXXX"
char tmpname[] = DEFAULT_TMP_NAME;

void usage() {
	printf("sponge <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 (strcmp(tmpname, DEFAULT_TMP_NAME)) {
		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_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_tmpfile(FILE *tmpfile, FILE *outfd) {
	char buf[BUFF_SIZE];
	if (fseek(tmpfile, 0, SEEK_SET)) {
		perror("could to seek to start of temporary file");
		fclose(tmpfile);
		exit(1);
	}
	while (fread(buf, BUFF_SIZE, 1, tmpfile) > 0) {
		write_buff_out(buf, BUFF_SIZE, outfd);
	}
	if (ferror(tmpfile)) {
		perror("read temporary file");
		fclose(tmpfile);
		exit(1);
	}
	fclose(tmpfile);
	fclose(outfd);
}

FILE *open_tmpfile(void) {
	struct cs_status cs;
	int tmpfd;
	FILE *tmpfile;
	mode_t mask;

	trapsignals();
	cs = cs_enter();
	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 *tmpfile = 0;
	ssize_t i = 0;
	size_t mem_available = default_sponge_size();

	if (argc > 2 || (argc == 2 && strcmp(argv[1], "-h") == 0)) {
		usage();
	}
	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) {
				if (!tmpfile) {
					tmpfile=open_tmpfile();
				}
				write_buff_tmp(bufstart, bufused, tmpfile);
				bufused = 0;
			}
			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 (argc == 2) {
		outname = argv[1];
	}
	if (tmpfile) {
		struct stat statbuf;

		/* write whatever we have in memory to tmpfile */
		if (bufused) 
			write_buff_tmp(bufstart, bufused, tmpfile);
		fclose(tmpfile);

		if (outname) {
			/* If it's a regular file, or does not yet exist,
			 * attempt a fast rename of the temp file. */
			if ((stat(outname, &statbuf) == 0 &&
			     S_ISREG(statbuf.st_mode)) ||
			    errno == ENOENT) {
				if (rename(tmpname, outname) != 0) {
					/* Slow copy. */
					FILE *outfd = fopen(outname, "w");
						if (outfd < 0) {
						perror("error opening output file");
						exit(1);
					}
					copy_tmpfile(tmpfile, outfd);
				}
			}
		}
		else {
			copy_tmpfile(tmpfile, stdout);
		}
	}
	else {
		FILE *outfd = stdout;
		if (outname) {
			outfd = fopen(outname, "w");
			if (outfd < 0) {
				perror("error opening output file");
				exit(1);
			}
		}
		if (bufused)
			write_buff_out(bufstart, bufused, outfd);
		fclose(outfd);
	}

	return 0;
}


Mode Type Size Ref File
100644 blob 17989 b7b5f53df1412df1e117607f18385b39004cdaa2 COPYING
100644 blob 883 f5142db3609d9e0b4041e9bbd244042206c426df Makefile
100644 blob 1048 ccc257cd3e6190cafffba1b73125677a16beea33 README
100755 blob 687 3abb315c7a9592c44f884cf5817dd062f9804f43 check-isutf8
100755 blob 2607 a695935b24a5f2789c71a8affc4486859a41f737 combine
040000 tree - 46c7b55a7a07e3ee2e69b02ecff6b2cfec14de3b debian
100644 blob 12767 f8fa0240cfe7d245c22dbd09d77be08e479ada1d ifdata.c
100644 blob 7039 5f2837f71cb7ea8e1aa8481d854d975174e19ab9 ifdata.docbook
100644 blob 2345 817b1c510c92e581233bc19dc3c920b6c2c17181 ifne.c
100644 blob 1826 92c7f4835d7c28a65c173273ec734b53fe3fdb41 ifne.docbook
100644 blob 7296 4306c7d7a22b230db58248e3317ada4a1f60bb34 isutf8.c
100644 blob 2894 f9c9eb59e9e15197e686a25a93d8785e4522696a isutf8.docbook
100644 blob 5471 4925409bd548b058f07defe913724868801040df lckdo.c
100644 blob 3261 8a0a4a863aba57a7a4d7b06b69414c25c21dfa17 lckdo.docbook
100644 blob 5777 43ba76aa6f3d24cfe8bb90b3776b044f27df9d37 mispipe.c
100644 blob 2292 b645b2c756f9b79cdde96a4a82c63bd9fd60fbff mispipe.docbook
100644 blob 1040 6ba38f78da10b61c8670b1c450fa769248ef84c4 pee.c
100644 blob 1948 bbae794db60fb651b41727c216d047eef3929869 pee.docbook
100644 blob 7301 a53a2cf1906998c91533f5f5435ceeeeb1a7cd59 physmem.c
100644 blob 7736 dcd4abaaa37c30c00b15f3f0ef64391f90231a73 sponge.c
100644 blob 1757 f9395a72eb845f0c8007dd2777501a305243f579 sponge.docbook
100755 blob 1849 d1cbb16e1c31b3393901b0633c251a6d37ae782b ts
100755 blob 4488 7d382b237593dc88d471db016a3bdd8899f14ec8 vidir
100755 blob 1260 8cee2b1775c19a3e7e046984a325eebfc8b94ecf vipe
100755 blob 2018 7776db2d94f0d91a7783eab259ed9283f8c1703e 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