#include <stdio.h>
#include <sys/inotify.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <stdlib.h>
#include <which.h>
#include <spawn.h>

#define spawn(prog, argv, envp) posix_spawn(prog, argv, NULL, NULL, argv, envp)

static _Bool run = ~0;
void siginthandler(int signum){
	run = 0;
}

ssize_t uninterruptible_read(int fd, void *buf, size_t len){
	ssize_t b, sum=0;
read_was_interrupted:
	b = read(fd, buf, len);
	sum += b;
	if(b == len) return sum;
	if(b == -1 && errno != EINTR) return -1;
	if(b == 0) return sum; //premature EOF
	if(!run) return 0;
	buf = (void*)((char*)buf + b);
	len -= b;
	puts("Resuming from interrupt.");
	goto read_was_interrupted;
}

int main(const int argc, char *const argv[], char *const envp[]){
	if(argc < 4){
		if(argc == 2){
			puts("Usage:   inotifyexec modulo file command\n"
			     "Example: inotifyexec 2 dok.tex pdflatex -halt-on-error dok.tex\n"
			     "Example: inotifyexec 1 testfile echo Test how \\\n"
			     "         many times this gets printed each save.\n"
			     "Explanation:\n"
			     "Execute command (don't put it in one string)\n"
			     "every modulo times when file changes, where\n"
			     "modulo is the number of modifications your\n"
			     "editor performs on the file when you save it.");
		}
		return 0;
	}
	/* Register handler for SIGINT */
	struct sigaction action_sigint;
	memset(&action_sigint, 0, sizeof(action_sigint));
	action_sigint.sa_handler = siginthandler;
	sigaction(SIGINT, &action_sigint, NULL);

	int fd = inotify_init();
	if(fd == -1){
		perror("inotify_init");
		return -1;
	}
	int wd = inotify_add_watch(fd, argv[2], IN_MODIFY);
	if(wd == -1){
		perror(argv[2]);
		return -1;
	}
	struct inotify_event event;
	int modulo = atoi(argv[1]), i=0;
	char *prog = which(argv[3]);
	if(prog == NULL) return -1;

	for(;;){
		int bytes = uninterruptible_read(fd, &event, sizeof(event));
		if(!run) break;
		if(bytes < (signed)sizeof(event)){
			fputs("Premature end of input\n", stderr);
			break;
		}
		//some text editors modify multiple times:
		if(++i == modulo){
			spawn(prog, argv+3, envp);
			i = 0;
		}
	}
	free(prog);
	inotify_rm_watch(fd, wd);
	close(fd);
	puts("Exiting.");
	return 0;
}

