pcw / cw.c

#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/inotify.h>

#define STDIN  0
#define STDOUT 1
#define STDERR 2
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#define MAX(a,b) (((a) > (b)) ? (a) : (b))

static FILE *in = NULL, *out = NULL;
static char buf[4096];

static void sigwinch(int unused)
{
	struct winsize ws;

	if (ioctl(STDERR, TIOCGWINSZ, &ws) < 0)
		warn("failed ioctl");

	printf("SIZE %d %d\n", ws.ws_row, ws.ws_col);
	fflush(stdout);

	rewind(in);

	while (fgets(buf, sizeof(buf), in))
		fputs(buf, stdout);
	fflush(stdout);
}

int main(int argc, char **argv)
{
	int evq, len, fd;
	int in_wd = 0, out_wd = 0;
	struct inotify_event *cur;
	fd_set rd;

	if (argc != 3) {
		printf("Usage: cw infile outfile\n");
		printf("cw-"VERSION" © Evan Gates\n");
		exit(1);
	}

	if ((evq = inotify_init()) < 0)
		err(1, "failed inotify_init()");

	if ((in = fopen(argv[1], "r")) == NULL)
		err(1, "failed to fopen %s", argv[1]);
	if ((in_wd = inotify_add_watch(evq, argv[1], IN_MODIFY)) < 0)
		err(1, "failed inotify_add_watch on %s", argv[1]);

	if ((fd = open(argv[2], O_WRONLY)) < 0)
		err(1, "failed to open %s", argv[2]);
	if ((out = fdopen(fd, "w")) == NULL)
		err(1, "failed to fdopen %s on fd %d", argv[2], fd);
	if ((out_wd = inotify_add_watch(evq, argv[2], IN_CLOSE_NOWRITE)) < 0)
		err(1, "failed inotify_add_watch on %s", argv[2]);

	sigwinch(0);
	if (signal(SIGWINCH, sigwinch) == SIG_ERR)
		err(1, "failed to install SIGWINCH handler");

	for (;;) {
		FD_ZERO(&rd);
		FD_SET(evq, &rd);
		FD_SET(fileno(stdin), &rd);

		if (select(evq + 1, &rd, 0, 0, NULL) < 0) {
			if (errno == EINTR)
				continue;
			else
				warn("error on select");
		}

		if (FD_ISSET(evq, &rd)) {
			if ((len = read(evq, buf, sizeof(buf))) < 0 && errno != EINTR)
				warn("failed to read from inotify event queue");
			for (cur = (struct inotify_event *)buf;
				 (char *)cur - buf < len;
				 cur = (struct inotify_event *)((char *)cur + sizeof(struct inotify_event) + cur->len))
				if(cur->wd == out_wd)
					errx(1, "outfile %s no longer being read", argv[2]);

			while (fgets(buf, sizeof(buf), in))
				fputs(buf, stdout);
			fflush(stdout);
		}
		if (FD_ISSET(fileno(stdin), &rd)) {
			if (fgets(buf, sizeof(buf), stdin))
				fputs(buf, out);
			fflush(out);
		}
	}
	return 0;
}

/* vim: set ts=4 sw=4 noexpandtab: */
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.