summary refs log tree commit diff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--bin/edi/Makefile2
-rw-r--r--bin/edi/edi.c40
-rw-r--r--bin/edi/edi.h61
-rw-r--r--bin/edi/file.c85
-rw-r--r--bin/edi/log.c18
-rw-r--r--bin/edi/table.c179
6 files changed, 249 insertions, 136 deletions
diff --git a/bin/edi/Makefile b/bin/edi/Makefile
index 2d9128c0..8ad154c6 100644
--- a/bin/edi/Makefile
+++ b/bin/edi/Makefile
@@ -2,6 +2,8 @@ CFLAGS += -Wall -Wextra -Wpedantic
 LDLIBS = -lcursesw
 
 OBJS += buffer.o
+OBJS += edi.o
+OBJS += file.o
 OBJS += log.o
 OBJS += table.o
 
diff --git a/bin/edi/edi.c b/bin/edi/edi.c
new file mode 100644
index 00000000..fe2c2034
--- /dev/null
+++ b/bin/edi/edi.c
@@ -0,0 +1,40 @@
+/* Copyright (C) 2018  Curtis McEnroe <june@causal.agency>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <locale.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#include "edi.h"
+
+int main(int argc, char *argv[]) {
+	setlocale(LC_CTYPE, "");
+
+	if (argc < 2) return EX_USAGE;
+
+	struct File file = fileAlloc(strdup(argv[1]));
+	fileRead(&file);
+
+	const struct Table *table = &file.log.states[file.log.state].table;
+	for (size_t i = 0; i < table->len; ++i) {
+		for (size_t j = 0; j < table->slices[i].len; ++j) {
+			printf("%lc", table->slices[i].ptr[j]);
+		}
+	}
+
+	fileFree(&file);
+}
diff --git a/bin/edi/edi.h b/bin/edi/edi.h
index 412e6d19..d8b1846b 100644
--- a/bin/edi/edi.h
+++ b/bin/edi/edi.h
@@ -17,11 +17,9 @@
 #include <stdlib.h>
 #include <wchar.h>
 
-struct Span {
+static inline struct Span {
 	size_t at, to;
-};
-
-static inline struct Span spanNext(struct Span span, size_t len) {
+} spanNext(struct Span span, size_t len) {
 	return (struct Span) { span.to, span.to + len };
 }
 
@@ -31,15 +29,13 @@ struct Slice {
 };
 
 struct Buffer {
-	size_t cap;
-	size_t len;
+	size_t cap, len;
 	struct Slice slice;
 	struct Block {
 		struct Block *prev;
 		wchar_t chars[];
 	} *block;
 };
-
 struct Buffer bufferAlloc(size_t cap);
 void bufferFree(struct Buffer *buf);
 void bufferInsert(struct Buffer *buf);
@@ -47,43 +43,36 @@ void bufferAppend(struct Buffer *buf, wchar_t ch);
 void bufferDelete(struct Buffer *buf);
 wchar_t *bufferDest(struct Buffer *buf, size_t len);
 
-struct Table {
-	size_t len;
-	size_t hot;
+static const struct Table {
+	size_t cap, len;
+	size_t ins;
 	struct Slice *slices;
-};
-static const struct Table TableEmpty = { 0, 0, NULL };
-
-struct Table tableInsert(struct Table prev, size_t at, struct Slice ins);
-struct Table tableDelete(struct Table prev, struct Span del);
+} TableEmpty;
+struct Table tableAlloc(size_t cap);
+void tableFree(struct Table *table);
+void tablePush(struct Table *table, struct Slice slice);
+struct Table tableInsert(const struct Table *prev, size_t at, struct Slice ins);
+struct Table tableDelete(const struct Table *prev, struct Span del);
+void tableUpdate(struct Table *table, struct Slice ins);
 
 struct Log {
-	size_t cap;
-	size_t len;
-	size_t idx;
+	size_t cap, len;
+	size_t state;
 	struct State {
 		struct Table table;
-		size_t prev;
-		size_t next;
+		size_t prev, next;
 	} *states;
 };
-
 struct Log logAlloc(size_t cap);
 void logFree(struct Log *log);
 void logPush(struct Log *log, struct Table table);
 
-static inline struct Table logTable(struct Log log) {
-	return log.states[log.idx].table;
-}
-static inline void logPrev(struct Log *log) {
-	log->idx = log->states[log->idx].prev;
-}
-static inline void logNext(struct Log *log) {
-	log->idx = log->states[log->idx].next;
-}
-static inline void logBack(struct Log *log) {
-	if (log->idx) log->idx--;
-}
-static inline void logFore(struct Log *log) {
-	if (log->idx + 1 < log->len) log->idx++;
-}
+struct File {
+	char *path;
+	struct Buffer buf;
+	struct Log log;
+	size_t clean;
+};
+struct File fileAlloc(char *path);
+void fileFree(struct File *file);
+void fileRead(struct File *file);
diff --git a/bin/edi/file.c b/bin/edi/file.c
new file mode 100644
index 00000000..322b5616
--- /dev/null
+++ b/bin/edi/file.c
@@ -0,0 +1,85 @@
+/* Copyright (C) 2018  Curtis McEnroe <june@causal.agency>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <wchar.h>
+
+#include "edi.h"
+
+enum {
+	BufCap = 8192,
+	LogCap = 8,
+};
+
+struct File fileAlloc(char *path) {
+	struct File file = {
+		.path = path,
+		.buf = bufferAlloc(BufCap),
+		.log = logAlloc(LogCap),
+		.clean = 0,
+	};
+	if (!path) logPush(&file.log, TableEmpty);
+	return file;
+}
+
+void fileFree(struct File *file) {
+	logFree(&file->log);
+	bufferFree(&file->buf);
+	free(file->path);
+}
+
+static const mbstate_t MBStateInit;
+
+// TODO: Error handling.
+void fileRead(struct File *file) {
+	if (!file->path) return;
+
+	FILE *stream = fopen(file->path, "r");
+	if (!stream) {
+		if (errno != ENOENT) err(EX_NOINPUT, "%s", file->path);
+		logPush(&file->log, TableEmpty);
+		file->clean = file->log.state;
+		return;
+	}
+
+	struct Table table = tableAlloc(1);
+
+	char buf[BufCap];
+	mbstate_t mbState = MBStateInit;
+	while (!feof(stream)) {
+		size_t mbsLen = fread(buf, 1, sizeof(buf), stream);
+		if (ferror(stream)) err(EX_IOERR, "%s", file->path);
+
+		const char *mbs = buf;
+		mbstate_t mbLenState = mbState;
+		size_t wcsLen = mbsnrtowcs(NULL, &mbs, mbsLen, 0, &mbLenState);
+		if (wcsLen == (size_t)-1) err(EX_DATAERR, "%s", file->path);
+
+		wchar_t *wcs = bufferDest(&file->buf, wcsLen);
+		assert(wcsLen == mbsnrtowcs(wcs, &mbs, mbsLen, wcsLen, &mbState));
+
+		tablePush(&table, file->buf.slice);
+	}
+	logPush(&file->log, table);
+	file->clean = file->log.state;
+
+	fclose(stream);
+}
diff --git a/bin/edi/log.c b/bin/edi/log.c
index f31f075f..82cd7ff3 100644
--- a/bin/edi/log.c
+++ b/bin/edi/log.c
@@ -23,25 +23,19 @@
 #include "edi.h"
 
 struct Log logAlloc(size_t cap) {
-	assert(cap);
 	struct State *states = malloc(sizeof(*states) * cap);
 	if (!states) err(EX_OSERR, "malloc");
-	states[0] = (struct State) {
-		.table = TableEmpty,
-		.prev = 0,
-		.next = 0,
-	};
 	return (struct Log) {
 		.cap = cap,
-		.len = 1,
-		.idx = 0,
+		.len = 0,
+		.state = 0,
 		.states = states,
 	};
 }
 
 void logFree(struct Log *log) {
 	for (size_t i = 0; i < log->len; ++i) {
-		free(log->states[i].table.slices);
+		tableFree(&log->states[i].table);
 	}
 	free(log->states);
 }
@@ -55,9 +49,9 @@ void logPush(struct Log *log, struct Table table) {
 	size_t next = log->len++;
 	log->states[next] = (struct State) {
 		.table = table,
-		.prev = log->idx,
+		.prev = log->state,
 		.next = next,
 	};
-	log->states[log->idx].next = next;
-	log->idx = next;
+	log->states[log->state].next = next;
+	log->state = next;
 }
diff --git a/bin/edi/table.c b/bin/edi/table.c
index c4d6ab24..abe61403 100644
--- a/bin/edi/table.c
+++ b/bin/edi/table.c
@@ -21,79 +21,96 @@
 
 #include "edi.h"
 
-static struct Table alloc(size_t cap) {
+struct Table tableAlloc(size_t cap) {
 	struct Slice *slices = malloc(sizeof(*slices) * cap);
 	if (!slices) err(EX_OSERR, "malloc");
-	return (struct Table) { 0, 0, slices };
+	return (struct Table) {
+		.cap = cap,
+		.len = 0,
+		.ins = 0,
+		.slices = slices,
+	};
 }
 
-struct Table tableInsert(struct Table prev, size_t at, struct Slice ins) {
-	if (!prev.len) {
-		struct Table next = alloc(1);
-		next.slices[next.hot = next.len++] = ins;
-		return next;
+void tableFree(struct Table *table) {
+	free(table->slices);
+}
+
+void tablePush(struct Table *table, struct Slice slice) {
+	if (table->len == table->cap) {
+		table->cap *= 2;
+		table->slices = realloc(
+			table->slices,
+			sizeof(*table->slices) * table->cap
+		);
+		if (!table->slices) err(EX_OSERR, "malloc");
 	}
+	table->slices[table->len++] = slice;
+}
 
-	struct Table next = alloc(prev.len + 2);
+struct Table tableInsert(const struct Table *prev, size_t at, struct Slice ins) {
+	struct Table next = tableAlloc(prev->len + 2);
 	struct Span span = { 0, 0 };
-	for (size_t i = 0; i < prev.len; ++i) {
-		span = spanNext(span, prev.slices[i].len);
+	for (size_t i = 0; i < prev->len; ++i) {
+		span = spanNext(span, prev->slices[i].len);
 		if (span.at == at) {
-			next.slices[next.hot = next.len++] = ins;
-			next.slices[next.len++] = prev.slices[i];
+			next.slices[next.ins = next.len++] = ins;
+			next.slices[next.len++] = prev->slices[i];
 		} else if (span.at < at && span.to > at) {
 			next.slices[next.len++] = (struct Slice) {
-				prev.slices[i].ptr,
+				prev->slices[i].ptr,
 				at - span.at,
 			};
-			next.slices[next.hot = next.len++] = ins;
+			next.slices[next.ins = next.len++] = ins;
 			next.slices[next.len++] = (struct Slice) {
-				&prev.slices[i].ptr[at - span.at],
-				prev.slices[i].len - (at - span.at),
+				&prev->slices[i].ptr[at - span.at],
+				prev->slices[i].len - (at - span.at),
 			};
 		} else {
-			next.slices[next.len++] = prev.slices[i];
+			next.slices[next.len++] = prev->slices[i];
 		}
 	}
 	if (span.to == at) {
-		next.slices[next.hot = next.len++] = ins;
+		next.slices[next.ins = next.len++] = ins;
 	}
 	return next;
 }
 
-struct Table tableDelete(struct Table prev, struct Span del) {
-	if (!prev.len) return TableEmpty;
-	struct Table next = alloc(prev.len + 1);
+void tableUpdate(struct Table *table, struct Slice ins) {
+	if (table->ins < table->len) table->slices[table->ins] = ins;
+}
+
+struct Table tableDelete(const struct Table *prev, struct Span del) {
+	struct Table next = tableAlloc(prev->len + 1);
 	struct Span span = { 0, 0 };
-	for (size_t i = 0; i < prev.len; ++i) {
-		span = spanNext(span, prev.slices[i].len);
+	for (size_t i = 0; i < prev->len; ++i) {
+		span = spanNext(span, prev->slices[i].len);
 		if (span.at >= del.at && span.to <= del.to) {
-			(void)prev.slices[i];
-			next.hot = next.len;
+			(void)prev->slices[i];
 		} else if (span.at < del.at && span.to > del.to) {
 			next.slices[next.len++] = (struct Slice) {
-				prev.slices[i].ptr,
+				prev->slices[i].ptr,
 				del.at - span.at,
 			};
-			next.slices[next.hot = next.len++] = (struct Slice) {
-				&prev.slices[i].ptr[del.to - span.at],
-				prev.slices[i].len - (del.to - span.at),
+			next.slices[next.len++] = (struct Slice) {
+				&prev->slices[i].ptr[del.to - span.at],
+				prev->slices[i].len - (del.to - span.at),
 			};
 		} else if (span.at < del.at && span.to > del.at) {
 			next.slices[next.len++] = (struct Slice) {
-				prev.slices[i].ptr,
+				prev->slices[i].ptr,
 				del.at - span.at,
 			};
-			next.hot = next.len;
 		} else if (span.at < del.to && span.to > del.to) {
-			next.slices[next.hot = next.len++] = (struct Slice) {
-				&prev.slices[i].ptr[del.to - span.at],
-				prev.slices[i].len - (del.to - span.at),
+			next.slices[next.len++] = (struct Slice) {
+				&prev->slices[i].ptr[del.to - span.at],
+				prev->slices[i].len - (del.to - span.at),
 			};
 		} else {
-			next.slices[next.len++] = prev.slices[i];
+			next.slices[next.len++] = prev->slices[i];
 		}
 	}
+	next.ins = next.len;
 	return next;
 }
 
@@ -108,66 +125,52 @@ static struct Span span(size_t at, size_t to) {
 	return (struct Span) { at, to };
 }
 
-static int eq(const wchar_t *str, struct Table table) {
-	for (size_t i = 0; i < table.len; ++i) {
-		if (wcsncmp(str, table.slices[i].ptr, table.slices[i].len)) {
+static int eq(const wchar_t *str, const struct Table *table) {
+	for (size_t i = 0; i < table->len; ++i) {
+		if (wcsncmp(str, table->slices[i].ptr, table->slices[i].len)) {
 			return 0;
 		}
-		str = &str[table.slices[i].len];
+		str = &str[table->slices[i].len];
 	}
 	return !str[0];
 }
 
-static int hot(wint_t ch, struct Table table) {
-	if (table.hot == table.len) {
-		return ch == WEOF;
-	} else {
-		return ch == table.slices[table.hot].ptr[0];
-	}
-}
-
 int main() {
-	struct Table abc = tableInsert(TableEmpty, 0, slice(L"ABC"));
-	assert(eq(L"ABC", abc));
-	assert(hot(L'A', abc));
-
-	struct Table dabc = tableInsert(abc, 0, slice(L"D"));
-	struct Table abcd = tableInsert(abc, 3, slice(L"D"));
-	struct Table adbc = tableInsert(abc, 1, slice(L"D"));
-
-	assert(eq(L"DABC", dabc));
-	assert(eq(L"ABCD", abcd));
-	assert(eq(L"ADBC", adbc));
-
-	assert(hot(L'D', dabc));
-	assert(hot(L'D', abcd));
-	assert(hot(L'D', adbc));
-
-	free(dabc.slices);
-	free(abcd.slices);
-	free(adbc.slices);
-
-	struct Table bc = tableDelete(abc, span(0, 1));
-	struct Table ab = tableDelete(abc, span(2, 3));
-	struct Table ac = tableDelete(abc, span(1, 2));
-	struct Table __ = tableDelete(abc, span(0, 3));
-
-	assert(eq(L"BC", bc));
-	assert(eq(L"AB", ab));
-	assert(eq(L"AC", ac));
-	assert(eq(L"",   __));
-
-	assert(hot(L'B', bc));
-	assert(hot(WEOF, ab));
-	assert(hot(L'C', ac));
-	assert(hot(WEOF, __));
-
-	free(bc.slices);
-	free(ab.slices);
-	free(ac.slices);
-	free(__.slices);
-
-	free(abc.slices);
+	struct Table abc = tableInsert(&TableEmpty, 0, slice(L"ABC"));
+	assert(eq(L"ABC", &abc));
+
+	struct Table dabc = tableInsert(&abc, 0, slice(L"D"));
+	struct Table abcd = tableInsert(&abc, 3, slice(L"D"));
+	struct Table adbc = tableInsert(&abc, 1, slice(L"D"));
+
+	assert(eq(L"DABC", &dabc));
+	assert(eq(L"ABCD", &abcd));
+	assert(eq(L"ADBC", &adbc));
+
+	assert(L'D' == dabc.slices[dabc.ins].ptr[0]);
+	assert(L'D' == abcd.slices[abcd.ins].ptr[0]);
+	assert(L'D' == adbc.slices[adbc.ins].ptr[0]);
+
+	tableFree(&dabc);
+	tableFree(&abcd);
+	tableFree(&adbc);
+
+	struct Table bc = tableDelete(&abc, span(0, 1));
+	struct Table ab = tableDelete(&abc, span(2, 3));
+	struct Table ac = tableDelete(&abc, span(1, 2));
+	struct Table __ = tableDelete(&abc, span(0, 3));
+
+	assert(eq(L"BC", &bc));
+	assert(eq(L"AB", &ab));
+	assert(eq(L"AC", &ac));
+	assert(eq(L"",   &__));
+
+	tableFree(&bc);
+	tableFree(&ab);
+	tableFree(&ac);
+	tableFree(&__);
+
+	tableFree(&abc);
 }
 
 #endif