diff --git a/Makefile b/Makefile index ecbccf4..a8c50dc 100644 --- a/Makefile +++ b/Makefile @@ -1,10 +1,11 @@ -# Makefile for bmake +# Makefile for bmake/openbsd make + +default: dged + .PHONY: default clean check run debug debug-tests install format .OBJDIR: ./build SYNTAX_ENABLE ?= true -default: dged - build: mkdir -p build @@ -35,7 +36,10 @@ datadir = share/dged .SUFFIXES: .SUFFIXES: .c .o .d -CFLAGS += -Werror -g -O2 -std=c99 -I $(.CURDIR)/src -I $(.CURDIR)/src/main -DDATADIR="$(prefix)/$(datadir)" +CFLAGS += -Werror -g -O2 -std=c99 \ + -I $(.CURDIR)/src \ + -I $(.CURDIR)/src/main \ + -DDATADIR="$(prefix)/$(datadir)" ASAN ?= false @@ -69,7 +73,13 @@ PLATFORM_OBJS = $(PLATFORM_SOURCES:.c=.o) MAIN_OBJS = $(MAIN_SOURCES:.c=.o) TEST_OBJS = $(TEST_SOURCES:.c=.o) -FILES = $(DEPS) $(MAIN_OBJS) $(OBJS) dged libdged.a $(TEST_OBJS) $(PLATFORM_OBJS) +FILES = $(DEPS) \ + $(MAIN_OBJS) \ + $(OBJS) \ + $(TEST_OBJS) \ + $(PLATFORM_OBJS) \ + dged \ + libdged.a # dependency generation .c.d: @@ -101,7 +111,12 @@ run-tests: $(TEST_OBJS) $(OBJS) $(CC) $(LDFLAGS) $(TEST_OBJS) $(OBJS) -lm -o run-tests check: run-tests - $(FORMAT_TOOL) --dry-run --Werror $(SOURCES:%.c=../%.c) $(MAIN_SOURCES:%.c=../%.c) $(TEST_SOURCES:%c=../%c) $(HEADERS:%.h=../%.h) + $(FORMAT_TOOL) --dry-run --Werror \ + $(SOURCES:%.c=../%.c) \ + $(PLATFORM_SOURCES:%.c=../%.c) \ + $(MAIN_SOURCES:%.c=../%.c) \ + $(TEST_SOURCES:%c=../%c) \ + $(HEADERS:%.h=../%.h) ./run-tests run: dged @@ -114,7 +129,12 @@ debug-tests: run-tests gdb ./run-tests format: - $(FORMAT_TOOL) -i $(SOURCES:%.c=../%.c) $(MAIN_SOURCES:%.c=../%.c) $(TEST_SOURCES:%c=../%c) $(HEADERS:%.h=../%.h) + $(FORMAT_TOOL) -i \ + $(SOURCES:%.c=../%.c) \ + $(MAIN_SOURCES:%.c=../%.c) \ + $(PLATFORM_SOURCES:%.c=../%.c) \ + $(TEST_SOURCES:%c=../%c) \ + $(HEADERS:%.h=../%.h) clean: rm -f $(FILES) @@ -129,7 +149,7 @@ install: dged install -m 644 $(.CURDIR)/dged.1 $(DESTDIR)/share/man/man1/dged.1 install -d $(DESTDIR)/$(datadir)/grammars - cp -rL $(.OBJDIR)/grammars "$(DESTDIR)/$(datadir)/" + cp -RL $(.OBJDIR)/grammars "$(DESTDIR)/$(datadir)/" docs: doxygen $(.CURDIR)/Doxyfile diff --git a/src/dged/display.c b/src/dged/display.c index ed6fc00..675140a 100644 --- a/src/dged/display.c +++ b/src/dged/display.c @@ -99,13 +99,17 @@ struct display *display_create() { // save old settings struct termios orig_term; - tcgetattr(0, &orig_term); + if (tcgetattr(0, &orig_term) < 0) { + return NULL; + } // set terminal to raw mode - struct termios term = {0}; + struct termios term = orig_term; cfmakeraw(&term); - tcsetattr(0, TCSADRAIN, &term); + if (tcsetattr(0, TCSADRAIN, &term) < 0) { + return NULL; + } struct display *d = calloc(1, sizeof(struct display)); d->orig_term = orig_term; diff --git a/src/dged/reactor-kqueue.c b/src/dged/reactor-kqueue.c index 5543c04..58ab7af 100644 --- a/src/dged/reactor-kqueue.c +++ b/src/dged/reactor-kqueue.c @@ -1,45 +1,141 @@ #include "reactor.h" +#include +#include #include +#include +#include +#include +#include -struct reactor { +#include "minibuffer.h" +struct reactor { + int queue; + struct kevent events[16]; + uint32_t nevents; }; struct reactor *reactor_create() { - struct reactor *reactor = calloc(1, sizeof(struct reactor)); + int queue = kqueue(); + if (queue < 0) { + return NULL; + } + struct reactor *reactor = calloc(1, sizeof(struct reactor)); + reactor->queue = queue; + reactor->nevents = 0; return reactor; } void reactor_destroy(struct reactor *reactor) { + close(reactor->queue); + reactor->queue = -1; free(reactor); } void reactor_update(struct reactor *reactor) { + int events = kevent(reactor->queue, NULL, 0, reactor->events, 16, NULL); + if (events == -1) { + // TODO: what to do here? + return; + } + reactor->nevents = events; } bool reactor_poll_event(struct reactor *reactor, uint32_t ev_id) { + for (uint32_t ei = 0; ei < reactor->nevents; ++ei) { + struct kevent *ev = &reactor->events[ei]; + + if (ev->ident == ev_id) { + return true; + } + } + return false; } -uint32_t reactor_register_interest(struct reactor *reactor, int fd, enum interest interest) { - return -1; +uint32_t reactor_register_interest(struct reactor *reactor, int fd, + enum interest interest) { + struct kevent changes[2] = {0}; + uint32_t nchanges = 0; + + if ((interest & ReadInterest) != 0) { + EV_SET(&changes[0], fd, EVFILT_READ, EV_ADD, 0, 0, NULL); + ++nchanges; + } + + if ((interest & WriteInterest) != 0) { + EV_SET(&changes[1], fd, EVFILT_WRITE, EV_ADD, 0, 0, NULL); + ++nchanges; + } + + if (kevent(reactor->queue, changes, nchanges, NULL, 0, NULL) < 0) { + return -1; + } + + return fd; } -uint32_t reactor_watch_file(struct reactor *reactor, const char *path, uint32_t mask) { - return -1; +uint32_t reactor_watch_file(struct reactor *reactor, const char *path, + uint32_t mask) { + + uint32_t fflags = NOTE_WRITE | NOTE_DELETE | NOTE_RENAME | NOTE_REVOKE; + int fd = open(path, O_RDONLY); + if (fd < 0) { + minibuffer_echo_timeout(4, "failed to watch %s: %s", path, strerror(errno)); + return 0; + } + + struct kevent new_event; + EV_SET(&new_event, fd, EVFILT_VNODE, EV_ADD | EV_CLEAR | EV_ENABLE, fflags, 0, + NULL); + if (kevent(reactor->queue, &new_event, 1, NULL, 0, NULL) < 0) { + return 0; + } + + return fd; } void reactor_unwatch_file(struct reactor *reactor, uint32_t id) { - + // all kevents for this fd are removed automatically when closed + close(id); } bool reactor_next_file_event(struct reactor *reactor, struct file_event *out) { - return false; + + // find the next vnode event and pop it from the events + struct kevent ev; + bool found = false; + for (uint32_t e = 0; e < reactor->nevents; ++e) { + if (reactor->events[e].filter == EVFILT_VNODE) { + ev = reactor->events[e]; + reactor->events[e] = reactor->events[reactor->nevents - 1]; + --reactor->nevents; + found = true; + break; + } + } + + if (!found) { + return false; + } + + out->mask = FileWritten; + if ((ev.fflags & NOTE_DELETE) || (ev.fflags & NOTE_RENAME) || + (ev.fflags & NOTE_REVOKE)) { + out->mask |= LastEvent; + } + + out->id = ev.ident; + return true; } void reactor_unregister_interest(struct reactor *reactor, uint32_t ev_id) { + struct kevent changes[2] = {0}; + EV_SET(&changes[0], ev_id, EVFILT_READ, EV_DELETE, 0, 0, NULL); + EV_SET(&changes[1], ev_id, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); + kevent(reactor->queue, changes, 2, NULL, 0, NULL); } diff --git a/src/dged/text.c b/src/dged/text.c index 82c49bc..3d1078f 100644 --- a/src/dged/text.c +++ b/src/dged/text.c @@ -389,8 +389,8 @@ void text_delete(struct text *text, uint32_t start_line, uint32_t start_col, // in this case we can "overwrite" uint32_t dstbytei = utf8_nbytes(firstline->data, firstline->nbytes, start_col); - memcpy(firstline->data + dstbytei, lastline->data + bytei, - lastline->nbytes - bytei); + memmove(firstline->data + dstbytei, lastline->data + bytei, + lastline->nbytes - bytei); } else { // otherwise we actually have to copy from the last line insert_at(text, start_line, start_col, lastline->data + bytei, diff --git a/src/main/main.c b/src/main/main.c index b0e408d..e634252 100644 --- a/src/main/main.c +++ b/src/main/main.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -93,17 +94,7 @@ void reload_buffer(struct buffer *buffer) { } void update_file_watches(struct reactor *reactor) { - // first, find invalid file watches and try to update them - VEC_FOR_EACH(&g_watched_files, struct watched_file * w) { - if (w->watch_id == INVALID_WATCH) { - message("re-watching: %s", w->buffer->filename); - w->watch_id = - reactor_watch_file(reactor, w->buffer->filename, FileWritten); - reload_buffer(w->buffer); - } - } - - // then pick up any events we might have + // first, pick up any events we might have struct file_event ev; while (reactor_next_file_event(reactor, &ev)) { // find the buffer we need to reload @@ -120,6 +111,16 @@ void update_file_watches(struct reactor *reactor) { } } } + + // then, find invalid file watches and try to update them + VEC_FOR_EACH(&g_watched_files, struct watched_file * w) { + if (w->watch_id == INVALID_WATCH) { + message("re-watching: %s", w->buffer->filename); + w->watch_id = + reactor_watch_file(reactor, w->buffer->filename, FileWritten); + reload_buffer(w->buffer); + } + } } static void usage() { @@ -218,8 +219,16 @@ int main(int argc, char *argv[]) { frame_allocator = frame_allocator_create(16 * 1024 * 1024); struct reactor *reactor = reactor_create(); + if (reactor == NULL) { + fprintf(stderr, "Failed to create event reactor: %s\n", strerror(errno)); + } display = display_create(); + if (display == NULL) { + fprintf(stderr, "Failed to set up display: %s\n", strerror(errno)); + return 9; + } + display_clear(display); signal(SIGWINCH, resized);