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 (242298deb641650bd25e8b68b52ca72f82becfa4) (8570 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
char *tmpname = NULL;

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 (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_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 *outfile, char *buf, size_t size) {
	ssize_t i;
	if (lseek(fileno(tmpfile), 0, SEEK_SET)) {
		perror("could to seek to start of temporary file");
		fclose(tmpfile);
		exit(1);
	}
	while ((i = read(fileno(tmpfile), buf, size)) > 0) {
		write_buff_out(buf, i, outfile);
	}
	if (i == -1) {
		perror("read temporary file");
		fclose(tmpfile);
		exit(1);
	}
	fclose(tmpfile);
	fclose(outfile);
}

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();

	if (argc > 2 || (argc == 2 && strcmp(argv[1], "-h") == 0)) {
		usage();
	}
	if (argc == 2) {
		outname = argv[1];
	}

	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 (tmpfile) {
		struct stat statbuf;

		/* write whatever we have in memory to tmpfile */
		if (bufused) 
			write_buff_tmp(bufstart, bufused, tmpfile);
		if (fflush(tmpfile) != 0) {
			perror("fflush");
			exit(1);
		}

		if (outname) {
			/* If it's a regular file, or does not yet exist,
			 * attempt a fast rename of the temp file. */
			if (((lstat(outname, &statbuf) == 0 &&
			      S_ISREG(statbuf.st_mode) &&
			      ! S_ISLNK(statbuf.st_mode)
			     ) || errno == ENOENT) &&
			    rename(tmpname, outname) == 0) {
				/* Fix renamed file mode to match either
				 * the old file mode, or the default file
				 * mode for a newly created file. */
				mode_t mode;
				if (errno != ENOENT) {
					mode = statbuf.st_mode;
				}
				else {
					mode_t mask = umask(0);
					umask(mask);
					mode = 0666 & ~mask;
				}
				if (chmod(outname, mode) != 0) {
					perror("chmod");
					exit(1);
				}
				return(0);
			}
			
			/* Fall back to slow copy. */
			outfile = fopen(outname, "w");
			if (!outfile) {
				perror("error opening output file");
				exit(1);
			}
			copy_tmpfile(tmpfile, outfile, bufstart, bufsize);
		}
		else {
			copy_tmpfile(tmpfile, stdout, bufstart, bufsize);
		}
	}
	else {
		if (outname) {
			outfile = fopen(outname, "w");
			if (!outfile) {
				perror("error opening output file");
				exit(1);
			}
		}
		else {
			outfile = stdout;
		}
		if (bufused)
			write_buff_out(bufstart, bufused, outfile);
		fclose(outfile);
	}

	return 0;
}


Mode Type Size Ref File
100644 blob 44 5d425843f23db3bb6970a55c953f345e3a8c8fe1 .gitattributes
100644 blob 17989 b7b5f53df1412df1e117607f18385b39004cdaa2 COPYING
100644 blob 1038 377121d56b79b9fbc42cabe86f4c3bae0d44bd2f Makefile
100644 blob 1106 548acf2240b3ea1ad6276e39ec195133a8b6d4c6 README
100755 blob 806 83a4eed00f82e3bcc81856149b47cffc4091f9aa check-isutf8
100755 blob 2607 a695935b24a5f2789c71a8affc4486859a41f737 combine
040000 tree - eebe48e402250fe244bf2856a5f7434178808e51 debian
100644 blob 13073 2de98a0b19372bff63be861b5adc755fa52fc74d ifdata.c
100644 blob 7234 963943ee1bd1ae2ae3b087663a5e1d6cd961f246 ifdata.docbook
100644 blob 3006 d8ecea9b8bc416154533572e1ce85a0385b7af10 ifne.c
100644 blob 2360 41fa9abe7a23b63f5afd110dcd0b3f78b0e4c531 ifne.docbook
100644 blob 7581 c5f5eeb667c425c3ef02516712c08acb72f3f557 isutf8.c
100644 blob 2986 58355a2cd465fe70dd7722c28c1dc62ed85ef7e8 isutf8.docbook
100644 blob 5471 4925409bd548b058f07defe913724868801040df lckdo.c
100644 blob 3532 effe84d29436f6749b7b0614b6a59c700f287ccf lckdo.docbook
100644 blob 5783 d183d04a5f249072da9be3e1d30d4e205e1be021 mispipe.c
100644 blob 2464 bd8faa8601fe24b72f93985249be2163513f44f7 mispipe.docbook
100644 blob 5318 d283b96101fbc89ef214436bab316cf1092536f9 parallel.c
100644 blob 3598 d3ffcce639cc0880bb1aebb973354d7a6b54dbcd parallel.docbook
100644 blob 1040 6ba38f78da10b61c8670b1c450fa769248ef84c4 pee.c
100644 blob 2225 fcb159aa0f9e44536c321b92e0ad589be04fd856 pee.docbook
100644 blob 7301 a53a2cf1906998c91533f5f5435ceeeeb1a7cd59 physmem.c
100644 blob 8570 242298deb641650bd25e8b68b52ca72f82becfa4 sponge.c
100644 blob 1859 24c432ac20eb502e58cf609037a3cfa4d1dc7a5f sponge.docbook
100755 blob 2572 042cc18b8a0b4649874b229c039b7b9d79806bfc ts
100755 blob 4495 a77739f27d8cab6843471de92857fe5064f9ace4 vidir
100755 blob 1402 fd61049c5ce903a1de06870cadf18a7c2b1e4137 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