#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/inotify.h>
#include <errno.h>

int main(int argc, char *argv[])
{
    int c;
    int nfd, ffd;
    int res;
	char event_buf[512];
    struct inotify_event *event;
	int event_mask = IN_ALL_EVENTS;
    int event_count = 1;
	int print_files = 0;
	int verbose = 2;
	int width = 80;
	char **file_names;
	int file_count;
	int id_offset = 0;
	int i;
	char *buf;

    do {
        c = getopt(argc, argv, "m:c:pv:w:");
        if (c == EOF)
            break;
        switch (c) {
        case 'm':
            event_mask = strtol(optarg, NULL, 0);
            break;
        case 'c':
            event_count = atoi(optarg);
            break;
		case 'p':
			print_files = 1;
			break;
        case 'v':
            verbose = atoi(optarg);
            break;
        case 'w':
            width = atoi(optarg);
            break;
        case '?':
            fprintf(stderr, "%s: invalid option -%c\n",
                argv[0], optopt);
            exit(1);
        }
    } while (1);

    if (argc <= optind) {
        fprintf(stderr, "Usage: %s [-m eventmask] [-c count] [-p] [-v verbosity] path [path ...]\n", argv[0]);
		return 1;
    }

    nfd = inotify_init();
    if(nfd < 0) {
        fprintf(stderr, "inotify_init failed, %s\n", strerror(errno));
        return 1;
    }
	file_names = argv + optind;
	file_count = argc - optind;
	for(i = 0; i < file_count; i++) {
		res = inotify_add_watch(nfd, file_names[i], event_mask);
		if(res < 0) {
	        fprintf(stderr, "inotify_add_watch failed for %s, %s\n", file_names[i], strerror(errno));
			return 1;
		}
		if(i == 0)
			id_offset = -res;
		if(res + id_offset != i) {
			fprintf(stderr, "%s got unexpected id %d instead of %d\n", file_names[i], res, i);
			return 1;
		}
	}

	buf = malloc(width + 2);
    
    while(1) {
		int event_pos = 0;
        res = read(nfd, event_buf, sizeof(event_buf));
        if(res < (int)sizeof(*event)) {
			if(errno == EINTR)
				continue;
            fprintf(stderr, "could not get event, %s\n", strerror(errno));
            return 1;
        }
		//printf("got %d bytes of event information\n", res);
		while(res >= (int)sizeof(*event)) {
			int event_size;
			event = (struct inotify_event *)(event_buf + event_pos);
			if(verbose >= 2)
		        printf("%s: %08x %08x \"%s\"\n", file_names[event->wd + id_offset], event->mask, event->cookie, event->len ? event->name : "");
			else if(verbose >= 2)
		        printf("%s: %08x \"%s\"\n", file_names[event->wd + id_offset], event->mask, event->len ? event->name : "");
			else if(verbose >= 1)
		        printf("%d: %08x \"%s\"\n", event->wd, event->mask, event->len ? event->name : "");
			if(print_files && (event->mask & IN_MODIFY)) {
				char filename[512];
				ssize_t read_len;
				char *display_name;
				int buflen;
				strcpy(filename, file_names[event->wd + id_offset]);
				if(event->len) {
					strcat(filename, "/");
					strcat(filename, event->name);
				}
				ffd = open(filename, O_RDONLY);
				display_name = (verbose >= 2 || event->len == 0) ? filename : event->name;
				buflen = width - strlen(display_name);
				read_len = read(ffd, buf, buflen);
				if(read_len > 0) {
					if(read_len < buflen && buf[read_len-1] != '\n') {
						buf[read_len] = '\n';
						read_len++;
					}
					if(read_len == buflen) {
						buf[--read_len] = '\0';
						buf[--read_len] = '\n';
						buf[--read_len] = '.';
						buf[--read_len] = '.';
						buf[--read_len] = '.';
					}
					else {
						buf[read_len] = '\0';
					}
					printf("%s: %s", display_name, buf);
				}
				close(ffd);
			}
	        if(event_count && --event_count == 0)
	            return 0;
			event_size = sizeof(*event) + event->len;
			res -= event_size;
			event_pos += event_size;
		}
    }

    return 0;
}
