munchcat/main.c
2026-08-27 22:44:17 +05:00

149 lines
3.4 KiB
C

#define _GNU_SOURCE
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
void eat(int fd) {
char buf[0x10000];
off_t pos = lseek(fd, 0, SEEK_CUR);
errno = 0; // no errors :-)
off_t eatpos = 0;
while (1) {
ssize_t s = -1;
while (s == -1) {
errno = 0;
s = read(fd, buf, sizeof(buf));
if (s == -1 && pos != -1) { // position unspecified in case of error
fprintf(stderr, "munchcat: read(): %s\n", strerror(errno));
usleep(100 * 1000); // save some cpu if theres error loop forever
lseek(fd, pos + s, SEEK_SET);
}
}
if (s == 0)
break;
{
char *p = buf;
ssize_t ps = s;
while (ps > 0) {
errno = 0;
ssize_t wrc = write(STDOUT_FILENO, p, ps);
if (wrc == -1) {
fprintf(stderr, "munchcat: write(): %s\n", strerror(errno));
usleep(100 * 1000); // save some cpu if theres error loop forever
} else {
ps -= wrc;
p += wrc;
}
}
}
if (pos != -1) {
off_t endpos = (pos + s) & ~(off_t)0xFFFF;
if (endpos > eatpos) {
errno = 0;
fallocate(fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE, eatpos, endpos - eatpos);
}
eatpos = endpos;
pos += s;
}
}
/*
if (pos != -1 && eatpos < pos) {
errno = 0;
fallocate(fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE, eatpos, pos - eatpos);
}
*/
ftruncate(fd, 0);
}
int main(int argc, const char *argv[]) {
const char *files[256] = { "/dev/stdin", 0 };
{
bool optp = true;
for (const char **p = argv + 1, **f = files; *p != NULL; ++p) {
if ((*p)[0] != '-' || ((*p)[0] == '-' && (*p)[1] == '\0') || !optp) {
const char *file = *p;
if (!strcmp(*p, "-"))
file = "/dev/stdin";
if ((f - files) < sizeof(files) / sizeof(*files) - 1) {
*f++ = file;
} else {
fprintf(stderr, "munchcat: too many inputs\n");
return 1;
}
continue;
}
if (!strcmp(*p, "--")) {
optp = false;
} else if (!strcmp(*p, "--help")) {
fprintf(stdout,
"Usage: munchcat [OPTION]... [FILE]...\n"
"Concatenate FILE(s) to standard output, EATING them in the process.\n"
"\n"
"With no FILE, or when FILE is -, read standard input.\n"
"\n"
" --help display this help and exit\n"
"\n"
"Examples:\n"
" munchcat the-entire-internet.sqlite Eat the entire internet.\n"
" munchcat /mnt/a/big.gz > /mnt/b/big.gz Move a file from one disk to another,\n"
" freeing up space as you go.\n"
"\n"
"WARNING: After processing by this program any files used as input will have\n"
"their contents destroyed. Kimapr.NET cannot be held liable for any damages\n"
"created by inappropriate usage of the munchcat program.\n"
"\n"
"WARNING: Content destruction will not occur if your filesystem does not support\n"
"sparse files. Regardless of sparse file support this program is not a\n"
"substitute for a proper secure erase mechanism. Kimapr.NET cannot be held\n"
"liable for any data leaks or other damage resulting from inappropriate usage of\n"
"the munchcat program for erasure of sensitive data.\n"
);
return 0;
}
}
}
for (const char **p = files; *p != NULL; ++p) {
int fd = open(*p, O_RDWR, 0);
if (fd == -1) {
fd = open(*p, O_RDONLY, 0);
}
if (fd == -1) {
fprintf(stderr, "munchcat: %s: %s\n", *p, strerror(errno));
return 1;
}
eat(fd);
}
return 0;
}