Skip to content

Instantly share code, notes, and snippets.

@1995eaton
Created December 10, 2014 13:13
Show Gist options
  • Save 1995eaton/452d98050a4629ea028b to your computer and use it in GitHub Desktop.
Save 1995eaton/452d98050a4629ea028b to your computer and use it in GitHub Desktop.
rw: like sponge, but better
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include <poll.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <getopt.h>
#define MKTMP_PATH "/tmp/rewrite-XXXXXX"
static ssize_t TFD_BUFSIZE = 4096;
static struct option long_options[] = {
{ "byte-size", required_argument, 0, 'b' },
};
void block(int fd, int ev)
{
struct pollfd p = { .fd = fd, .events = ev };
poll(&p, 1, -1);
}
int __fd_copy(int ifd, int ofd)
{
assert(ifd >= 0 && ofd >= 0);
char *buf = malloc(TFD_BUFSIZE * sizeof (char)),
*pos;
ssize_t brd, /* bytes read */
bwr; /* bytes written */
while (( brd = read(ifd, buf, TFD_BUFSIZE) )) {
if (brd == -1)
return -1;
pos = buf;
while (brd > 0) {
if ( (bwr = write(ofd, buf, brd)) == -1 ) {
switch (errno) {
case EAGAIN: block(ifd, POLLIN);
case EINTR: continue;
}
free(buf);
return -1;
}
brd -= bwr;
pos += bwr;
}
}
free(buf);
return 0;
}
void __stdout_write(void)
{
char *buf = malloc(TFD_BUFSIZE * sizeof (char));
for (ssize_t brd; ( brd = read(STDIN_FILENO, buf, TFD_BUFSIZE) );) {
if (brd == 0) break;
buf[brd - 1] = 0;
printf("%s", buf);
}
printf("\n");
free(buf);
return;
}
typedef struct {
char *template;
int fd, is_open;
} tfd_t;
void tfd_free(tfd_t *tfd)
{
if (tfd->is_open) {
close(tfd->fd);
unlink(tfd->template);
}
free(tfd->template);
free(tfd);
}
tfd_t *tfd_create(void)
{
tfd_t *tfd = malloc(sizeof (tfd_t));
tfd->template = strdup(MKTMP_PATH);
tfd->is_open = 0;
if ( (tfd->fd = mkstemp(tfd->template)) == -1 )
goto err_create;
tfd->is_open = 1;
return tfd;
err_create:
tfd_free(tfd);
perror("error opening temporary file for writing");
exit(EXIT_FAILURE);
}
void tfd_stream(tfd_t *tfd)
{
assert(tfd->is_open);
if (__fd_copy(STDIN_FILENO, tfd->fd) == -1)
goto err_write;
lseek(tfd->fd, 0, SEEK_SET);
return;
err_write:
tfd_free(tfd);
perror("error copying stdin to temporary file");
exit(EXIT_FAILURE);
}
void tfd_write(tfd_t *tfd, const char *file_name)
{
assert(tfd->is_open);
int ofd = open(file_name, O_RDWR | O_CREAT, 0666);
if (ofd == -1)
goto err_open;
if (__fd_copy(tfd->fd, ofd) == -1)
goto err_write;
return;
err_open:
tfd_free(tfd);
perror("error opening file for redirection");
exit(EXIT_FAILURE);
err_write:
tfd_free(tfd);
perror("error writing to output file");
exit(EXIT_FAILURE);
}
ssize_t __parse_l(const char *s) {
char *retval;
ssize_t result = strtol(s, &retval, 10);
if (*retval != 0 || result <= 0) {
return -1;
}
return result;
}
void print_help(void) {
static const char *help =
"Usage: rw [OPTION]... [FILE]...\n"
"Pipe the output of a command to the same file.\n"
"With no FILE, read standard input.\n\n"
" -b, --byte-size=N\n"
" use a byte-size of N when copying files (default 4096)";
puts(help);
}
int main(int argc, char **argv)
{
for (int c; (c = getopt_long(argc, argv, ":b:",
long_options, NULL)) != -1;)
{
switch (c) {
case 'b':
TFD_BUFSIZE = __parse_l(optarg);
if (TFD_BUFSIZE == -1) {
fprintf(stderr, "invalid number for "
"\"--byte-size\" option\n");
exit(EXIT_FAILURE);
}
break;
case '?':
default:
print_help();
return 0;
}
}
if (optind + 1 == argc) {
tfd_t *tfd = tfd_create();
tfd_stream(tfd);
tfd_write(tfd, argv[optind]);
tfd_free(tfd);
} else if (optind == argc) {
__stdout_write();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment