add efficient support for tail -f on kqueue, epoll, windows...etc if supported
erg opened this issue · comments
Doug Coleman commented
really naive attempt at tail -f
fails because readln
returns EOF
"resource:README.md" utf8 [
[
readln dup print nl
] loop
] with-file-reader
File monitors would probably get the job done, but there's a more efficine way sometimes.
linux: https://github.com/coreutils/coreutils/blob/master/src/tail.c#L1235
chatgpt actually got it right--try this on macos
// clang follow.c && ./a.out README.md
// echo a >> README.md
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#include <unistd.h>
#include <errno.h>
#define BUF_SIZE 1024
int main(int argc, char *argv[]) {
int fd, kq, nev;
struct kevent change;
struct kevent event;
char buf[BUF_SIZE];
int len;
if (argc != 2) {
fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
exit(EXIT_FAILURE);
}
// Open file
fd = open(argv[1], O_RDONLY);
if (fd == -1) {
perror("Failed to open file");
exit(EXIT_FAILURE);
}
// Create kqueue
kq = kqueue();
if (kq == -1) {
perror("kqueue failed");
exit(EXIT_FAILURE);
}
// Initialize kevent structure
EV_SET(&change, fd, EVFILT_VNODE,
EV_ADD | EV_ENABLE | EV_CLEAR,
NOTE_WRITE | NOTE_EXTEND, 0, 0);
// Move to the end of the file
if (lseek(fd, 0, SEEK_END) == -1) {
perror("lseek failed");
exit(EXIT_FAILURE);
}
// Event loop
while (1) {
nev = kevent(kq, &change, 1, &event, 1, NULL);
if (nev == -1) {
perror("kevent failed");
exit(EXIT_FAILURE);
} else if (nev > 0) {
if (event.fflags & NOTE_WRITE || event.fflags & NOTE_EXTEND) {
while ((len = read(fd, buf, BUF_SIZE)) > 0) {
write(STDOUT_FILENO, buf, len);
}
if (len == -1) {
perror("read failed");
exit(EXIT_FAILURE);
}
}
}
}
// Close file and kqueue
close(fd);
close(kq);
return 0;
}