diff options
Diffstat (limited to '')
-rw-r--r-- | bin/edi/.gitignore | 4 | ||||
-rw-r--r-- | bin/edi/Makefile | 35 | ||||
-rw-r--r-- | bin/edi/buffer.c | 168 | ||||
-rw-r--r-- | bin/edi/edi.c | 66 | ||||
-rw-r--r-- | bin/edi/edi.h | 116 | ||||
-rw-r--r-- | bin/edi/file.c | 115 | ||||
-rw-r--r-- | bin/edi/iter.c | 109 | ||||
-rw-r--r-- | bin/edi/log.c | 50 | ||||
-rw-r--r-- | bin/edi/store.c | 211 | ||||
-rw-r--r-- | bin/edi/table.c | 171 |
10 files changed, 0 insertions, 1045 deletions
diff --git a/bin/edi/.gitignore b/bin/edi/.gitignore deleted file mode 100644 index c76928a8..00000000 --- a/bin/edi/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -*.o -*.t -edi -tags diff --git a/bin/edi/Makefile b/bin/edi/Makefile deleted file mode 100644 index e7f16b39..00000000 --- a/bin/edi/Makefile +++ /dev/null @@ -1,35 +0,0 @@ -CFLAGS += -Wall -Wextra -Wpedantic -LDLIBS = -lcursesw - -OBJS += buffer.o -OBJS += edi.o -OBJS += file.o -OBJS += iter.o -OBJS += log.o -OBJS += store.o -OBJS += table.o - -TESTS += buffer.t -TESTS += iter.t -TESTS += table.t - -all: tags edi test - -tags: *.h *.c - ctags -w *.h *.c - -edi: $(OBJS) - $(CC) $(LDFLAGS) $(OBJS) $(LDLIBS) -o $@ - -$(OBJS): edi.h - -test: $(TESTS) - set -e; $(TESTS:%=./%;) - -.SUFFIXES: .t - -.c.t: - $(CC) $(CFLAGS) -DTEST $(LDFLAGS) $< $(LDLIBS) -o $@ - -clean: - rm -f tags edi $(OBJS) $(TESTS) diff --git a/bin/edi/buffer.c b/bin/edi/buffer.c deleted file mode 100644 index 3bd799e8..00000000 --- a/bin/edi/buffer.c +++ /dev/null @@ -1,168 +0,0 @@ -/* Copyright (C) 2018 June 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 <err.h> -#include <stdlib.h> -#include <string.h> -#include <sysexits.h> -#include <wchar.h> - -#include "edi.h" - -static struct Block *blockAlloc(struct Block *prev, size_t cap) { - size_t size = sizeof(struct Block) + sizeof(wchar_t) * cap; - struct Block *block = malloc(size); - if (!block) err(EX_OSERR, "malloc"); - block->prev = prev; - block->len = 0; - return block; -} - -struct Buffer bufferAlloc(size_t cap) { - return (struct Buffer) { - .cap = cap, - .block = blockAlloc(NULL, cap), - }; -} - -void bufferFree(struct Buffer *buf) { - struct Block *prev; - for (struct Block *it = buf->block; it; it = prev) { - prev = it->prev; - free(it); - } -} - -void bufferSlice(struct Buffer *buf) { - buf->slice.ptr = &buf->block->chars[buf->block->len]; - buf->slice.len = 0; -} - -void bufferPush(struct Buffer *buf, wchar_t ch) { - if (buf->block->len == buf->cap) { - if (buf->slice.len == buf->cap) buf->cap *= 2; - buf->block = blockAlloc(buf->block, buf->cap); - memcpy( - buf->block->chars, buf->slice.ptr, - sizeof(wchar_t) * buf->slice.len - ); - buf->slice.ptr = buf->block->chars; - buf->block->len = buf->slice.len; - } - buf->block->chars[buf->block->len++] = ch; - buf->slice.len++; -} - -void bufferPop(struct Buffer *buf) { - if (!buf->slice.len) return; - buf->slice.len--; - buf->block->len--; -} - -wchar_t *bufferDest(struct Buffer *buf, size_t len) { - if (buf->block->len + len > buf->cap) { - while (len > buf->cap) buf->cap *= 2; - buf->block = blockAlloc(buf->block, buf->cap); - } - wchar_t *ptr = &buf->block->chars[buf->block->len]; - buf->slice.ptr = ptr; - buf->slice.len = len; - buf->block->len += len; - return ptr; -} - -void bufferTruncate(struct Buffer *buf, size_t len) { - if (len > buf->slice.len) return; - buf->block->len -= buf->slice.len - len; - buf->slice.len = len; -} - -#ifdef TEST -#include <assert.h> - -int main() { - struct Buffer buf = bufferAlloc(6); - - bufferSlice(&buf); - bufferPush(&buf, L'A'); - bufferPush(&buf, L'B'); - assert(!wcsncmp(L"AB", buf.slice.ptr, buf.slice.len)); - - bufferSlice(&buf); - bufferPush(&buf, L'C'); - bufferPush(&buf, L'D'); - assert(!wcsncmp(L"CD", buf.slice.ptr, buf.slice.len)); - - bufferSlice(&buf); - bufferPush(&buf, L'E'); - bufferPush(&buf, L'F'); - bufferPush(&buf, L'G'); - bufferPush(&buf, L'H'); - assert(!wcsncmp(L"EFGH", buf.slice.ptr, buf.slice.len)); - - bufferFree(&buf); - - buf = bufferAlloc(4); - bufferSlice(&buf); - bufferPush(&buf, L'A'); - bufferPush(&buf, L'B'); - bufferPush(&buf, L'C'); - bufferPush(&buf, L'D'); - bufferPush(&buf, L'E'); - bufferPush(&buf, L'F'); - assert(!wcsncmp(L"ABCDEF", buf.slice.ptr, buf.slice.len)); - bufferFree(&buf); - - buf = bufferAlloc(4); - bufferSlice(&buf); - bufferPush(&buf, L'A'); - bufferPush(&buf, L'B'); - bufferPop(&buf); - assert(!wcsncmp(L"A", buf.slice.ptr, buf.slice.len)); - bufferPush(&buf, L'C'); - assert(!wcsncmp(L"AC", buf.slice.ptr, buf.slice.len)); - bufferFree(&buf); - - buf = bufferAlloc(4); - - wchar_t *dest = bufferDest(&buf, 2); - dest[0] = L'A'; - dest[1] = L'B'; - assert(!wcsncmp(L"AB", buf.slice.ptr, buf.slice.len)); - - dest = bufferDest(&buf, 3); - dest[0] = L'C'; - dest[1] = L'D'; - dest[2] = L'E'; - assert(!wcsncmp(L"CDE", buf.slice.ptr, buf.slice.len)); - - bufferFree(&buf); - - buf = bufferAlloc(4); - dest = bufferDest(&buf, 6); - dest[0] = L'A'; - dest[1] = L'B'; - dest[2] = L'C'; - dest[3] = L'D'; - dest[4] = L'E'; - dest[5] = L'F'; - assert(!wcsncmp(L"ABCDEF", buf.slice.ptr, buf.slice.len)); - bufferTruncate(&buf, 4); - assert(!wcsncmp(L"ABCD", buf.slice.ptr, buf.slice.len)); - bufferFree(&buf); -} - -#endif diff --git a/bin/edi/edi.c b/bin/edi/edi.c deleted file mode 100644 index 1ef2e055..00000000 --- a/bin/edi/edi.c +++ /dev/null @@ -1,66 +0,0 @@ -/* Copyright (C) 2018 June 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 <err.h> -#include <locale.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sysexits.h> - -#include "edi.h" - -static void errorExit(enum Error error, const char *prefix) { - if (error > Errno) errc(EX_IOERR, error - Errno, "%s", prefix); - else errx(EX_DATAERR, "%s: %d", prefix, error); -} - -int main(int argc, char *argv[]) { - setlocale(LC_CTYPE, ""); - - if (argc < 2) return EX_USAGE; - - enum Error error; - - struct File file = fileAlloc(strdup(argv[1])); - error = fileRead(&file); - if (error) errorExit(error, file.path); - - FILE *store = fopen("store.edi", "w"); - if (!store) err(EX_CANTCREAT, "store.edi"); - - error = storeWrite(store, &file.edit); - if (error) errorExit(error, "store.edi"); - - fclose(store); - if (ferror(store)) err(EX_IOERR, "store.edi"); - - store = fopen("store.edi", "r"); - if (!store) err(EX_CANTCREAT, "store.edi"); - - error = storeRead(store, &file.edit); - if (error) errorExit(error, "store.edi"); - - const struct Table *table = logTable(&file.edit.log); - for (struct Iter it = iter(table, 0); it.ch != WEOF; it = iterNext(it)) { - printf("%lc", it.ch); - } - - error = fileWrite(&file); - if (error) errorExit(error, file.path); - - fileFree(&file); -} diff --git a/bin/edi/edi.h b/bin/edi/edi.h deleted file mode 100644 index f35af64a..00000000 --- a/bin/edi/edi.h +++ /dev/null @@ -1,116 +0,0 @@ -/* Copyright (C) 2018 June 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 <stdio.h> -#include <stdlib.h> -#include <wchar.h> - -struct Span { - size_t at, to; -}; -static inline struct Span spanNext(struct Span span, size_t len) { - return (struct Span) { span.to, span.to + len }; -} -static inline struct Span spanPrev(struct Span span, size_t len) { - return (struct Span) { span.at - len, span.at }; -} - -struct Slice { - const wchar_t *ptr; - size_t len; -}; - -struct Buffer { - size_t cap; - struct Slice slice; - struct Block { - struct Block *prev; - size_t len; - wchar_t chars[]; - } *block; -}; -struct Buffer bufferAlloc(size_t cap); -void bufferFree(struct Buffer *buf); -void bufferSlice(struct Buffer *buf); -void bufferPush(struct Buffer *buf, wchar_t ch); -void bufferPop(struct Buffer *buf); -wchar_t *bufferDest(struct Buffer *buf, size_t len); -void bufferTruncate(struct Buffer *buf, size_t len); - -static const struct Table { - size_t cap, len; - struct Slice *slices; -} TableEmpty; -struct Table tableAlloc(size_t cap); -void tableFree(struct Table *table); -void tablePush(struct Table *table, struct Slice slice); -void tableReplace(struct Table *table, struct Slice old, struct Slice new); -struct Table tableInsert(const struct Table *prev, size_t at, struct Slice ins); -struct Table tableDelete(const struct Table *prev, struct Span del); - -struct Iter { - const struct Table *table; - struct Span span; - size_t slice; - size_t at; - wint_t ch; -}; -struct Iter iter(const struct Table *table, size_t at); -struct Iter iterNext(struct Iter it); -struct Iter iterPrev(struct Iter it); - -struct Log { - size_t cap, len; - size_t state; - struct State { - size_t prev, next; - struct Table table; - } *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(const struct Log *log) { - if (log->state == log->len) return NULL; - return &log->states[log->state].table; -} - -struct Edit { - struct Buffer buf; - struct Log log; -}; - -enum Error { - Ok, - StoreMagic, - StoreVersion, - StoreEOF, - FileNoPath, - Errno, -}; - -enum Error storeWrite(FILE *stream, const struct Edit *edit); -enum Error storeRead(FILE *stream, struct Edit *edit); - -struct File { - char *path; - size_t clean; - struct Edit edit; -}; -struct File fileAlloc(char *path); -void fileFree(struct File *file); -enum Error fileRead(struct File *file); -enum Error fileWrite(struct File *file); diff --git a/bin/edi/file.c b/bin/edi/file.c deleted file mode 100644 index 7bcef76d..00000000 --- a/bin/edi/file.c +++ /dev/null @@ -1,115 +0,0 @@ -/* Copyright (C) 2018 June 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 <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <wchar.h> - -#include "edi.h" - -enum { - BufferCap = 8192, - TableCap = 2, - LogCap = 8, -}; - -struct File fileAlloc(char *path) { - struct File file = { - .path = path, - .edit = { - .buf = bufferAlloc(BufferCap), - .log = logAlloc(LogCap), - }, - }; - if (!path) logPush(&file.edit.log, TableEmpty); - return file; -} - -void fileFree(struct File *file) { - logFree(&file->edit.log); - bufferFree(&file->edit.buf); - free(file->path); -} - -static const mbstate_t StateInit; - -enum Error fileRead(struct File *file) { - if (!file->path) return FileNoPath; - - FILE *stream = fopen(file->path, "r"); - if (!stream) { - if (errno != ENOENT) return Errno + errno; - logPush(&file->edit.log, TableEmpty); - file->clean = file->edit.log.state; - return Ok; - } - - struct Table table = tableAlloc(TableCap); - char buf[BufferCap]; - mbstate_t state = StateInit; - while (!feof(stream)) { - size_t mbsLen = fread(buf, 1, sizeof(buf), stream); - if (ferror(stream)) return Errno + errno; - - // FIXME: Handle null bytes specially. - const char *mbs = buf; - wchar_t *wcs = bufferDest(&file->edit.buf, mbsLen); - size_t wcsLen = mbsnrtowcs(wcs, &mbs, mbsLen, mbsLen, &state); - if (wcsLen == (size_t)-1) return Errno + errno; - - bufferTruncate(&file->edit.buf, wcsLen); - tablePush(&table, file->edit.buf.slice); - } - logPush(&file->edit.log, table); - file->clean = file->edit.log.state; - - fclose(stream); - return Ok; -} - -enum Error fileWrite(struct File *file) { - if (!file->path) return FileNoPath; - - FILE *stream = fopen(file->path, "w"); - if (!stream) return Errno + errno; - - const struct Table *table = logTable(&file->edit.log); - assert(table); - - char buf[BufferCap]; - mbstate_t state = StateInit; - for (size_t i = 0; i < table->len; ++i) { - struct Slice slice = table->slices[i]; - while (slice.len) { - // FIXME: Handle null bytes specially. - size_t mbsLen = wcsnrtombs( - buf, &slice.ptr, slice.len, sizeof(buf), &state - ); - if (mbsLen == (size_t)-1) return Errno + errno; - // FIXME: This only works once. - slice.len -= slice.ptr - table->slices[i].ptr; - - fwrite(buf, 1, mbsLen, stream); - if (ferror(stream)) return Errno + errno; - } - } - file->clean = file->edit.log.state; - - fclose(stream); - return (ferror(stream) ? Errno + errno : Ok); -} diff --git a/bin/edi/iter.c b/bin/edi/iter.c deleted file mode 100644 index 6331bb17..00000000 --- a/bin/edi/iter.c +++ /dev/null @@ -1,109 +0,0 @@ -/* Copyright (C) 2018 June 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 <stdlib.h> -#include <wchar.h> - -#include "edi.h" - -struct Iter iter(const struct Table *table, size_t at) { - struct Span span = { 0, 0 }; - size_t slice; - for (slice = 0; slice < table->len; ++slice) { - span = spanNext(span, table->slices[slice].len); - if (span.at <= at && span.to > at) break; - } - return (struct Iter) { - .table = table, - .span = span, - .slice = slice, - .at = (at < span.to ? at : span.to), - .ch = (at < span.to ? table->slices[slice].ptr[at - span.at] : WEOF), - }; -} - -struct Iter iterNext(struct Iter it) { - if (it.at == it.span.to && it.ch == WEOF) return it; - it.at++; - if (it.at == it.span.to) { - if (it.slice + 1 == it.table->len) { - it.ch = WEOF; - return it; - } - it.slice++; - it.span = spanNext(it.span, it.table->slices[it.slice].len); - } - it.ch = it.table->slices[it.slice].ptr[it.at - it.span.at]; - return it; -} - -struct Iter iterPrev(struct Iter it) { - if (it.at > it.span.to && it.ch == WEOF) return it; - it.at--; - if (it.at > it.span.to) { - it.ch = WEOF; - return it; - } - if (it.at < it.span.at) { - it.slice--; - it.span = spanPrev(it.span, it.table->slices[it.slice].len); - } - it.ch = it.table->slices[it.slice].ptr[it.at - it.span.at]; - return it; -} - -#ifdef TEST -#include <assert.h> - -int main() { - struct Slice slices[2] = { - { L"AB", 2 }, - { L"CD", 2 }, - }; - struct Table table = { .len = 2, .slices = slices }; - - assert(L'A' == iter(&table, 0).ch); - assert(L'B' == iter(&table, 1).ch); - assert(L'C' == iter(&table, 2).ch); - assert(L'D' == iter(&table, 3).ch); - assert(WEOF == iter(&table, 4).ch); - - assert(L'B' == iterNext(iter(&table, 0)).ch); - assert(L'C' == iterNext(iter(&table, 1)).ch); - assert(L'D' == iterNext(iter(&table, 2)).ch); - assert(WEOF == iterNext(iter(&table, 5)).ch); - - assert(WEOF == iterPrev(iter(&table, 0)).ch); - assert(L'A' == iterPrev(iter(&table, 1)).ch); - assert(L'B' == iterPrev(iter(&table, 2)).ch); - assert(L'C' == iterPrev(iter(&table, 3)).ch); - - struct Iter it = iter(&table, 3); - it = iterNext(it); - it = iterNext(it); - assert(WEOF == it.ch); - it = iterPrev(it); - assert(L'D' == it.ch); - - it = iter(&table, 0); - it = iterPrev(it); - it = iterPrev(it); - assert(WEOF == it.ch); - it = iterNext(it); - assert(L'A' == it.ch); -} - -#endif diff --git a/bin/edi/log.c b/bin/edi/log.c deleted file mode 100644 index 253554fd..00000000 --- a/bin/edi/log.c +++ /dev/null @@ -1,50 +0,0 @@ -/* Copyright (C) 2018 June 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 <stdint.h> -#include <stdlib.h> -#include <sysexits.h> - -#include "edi.h" - -struct Log logAlloc(size_t cap) { - struct State *states = malloc(sizeof(*states) * cap); - if (!states) err(EX_OSERR, "malloc"); - return (struct Log) { .cap = cap, .states = states }; -} - -void logFree(struct Log *log) { - for (size_t i = 0; i < log->len; ++i) { - tableFree(&log->states[i].table); - } - free(log->states); -} - -void logPush(struct Log *log, struct Table table) { - if (log->len == log->cap) { - log->cap *= 2; - log->states = realloc(log->states, sizeof(*log->states) * log->cap); - if (!log->states) err(EX_OSERR, "realloc"); - } - size_t next = log->len++; - log->states[next].table = table; - log->states[next].prev = log->state; - log->states[next].next = next; - log->states[log->state].next = next; - log->state = next; -} diff --git a/bin/edi/store.c b/bin/edi/store.c deleted file mode 100644 index d7b4e71e..00000000 --- a/bin/edi/store.c +++ /dev/null @@ -1,211 +0,0 @@ -/* Copyright (C) 2018 June 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 <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <wchar.h> - -#include "edi.h" - -static const char Magic[3] = "EDI"; -static const char Version = 1; - -static enum Error -write(FILE *stream, const void *ptr, size_t size, size_t nitems) { - size_t n = fwrite(ptr, size, nitems, stream); - return (n < nitems ? Errno + errno : Ok); -} - -static enum Error writeBuffer(FILE *stream, const struct Buffer *buf) { - size_t len = 0; - const struct Block *block; - for (block = buf->block; block; block = block->prev) { - len += block->len; - } - enum Error error = write(stream, &len, sizeof(len), 1); - if (error) return error; - for (block = buf->block; block; block = block->prev) { - error = write(stream, block->chars, sizeof(wchar_t), block->len); - if (error) return error; - } - return Ok; -} - -static enum Error -writeSlice(FILE *stream, const struct Buffer *buf, struct Slice slice) { - size_t offset = 0; - const struct Block *block = buf->block; - while (block) { - if ( - slice.ptr >= block->chars && slice.ptr < &block->chars[block->len] - ) break; - offset += block->len; - block = block->prev; - } - assert(block); - size_t ptr = offset + (size_t)(slice.ptr - block->chars); - enum Error error = write(stream, &ptr, sizeof(ptr), 1); - if (error) return error; - return write(stream, &slice.len, sizeof(slice.len), 1); -} - -static enum Error -writeTable(FILE *stream, const struct Buffer *buf, const struct Table *table) { - enum Error error = write(stream, &table->len, sizeof(table->len), 1); - for (size_t i = 0; i < table->len; ++i) { - error = writeSlice(stream, buf, table->slices[i]); - if (error) return error; - } - return Ok; -} - -static enum Error -writeState(FILE *stream, const struct Buffer *buf, const struct State *state) { - enum Error error; - error = write(stream, &state->prev, sizeof(state->prev), 1); - if (error) return error; - error = write(stream, &state->next, sizeof(state->next), 1); - if (error) return error; - return writeTable(stream, buf, &state->table); -} - -static enum Error -writeLog(FILE *stream, const struct Buffer *buf, const struct Log *log) { - enum Error error; - error = write(stream, &log->state, sizeof(log->state), 1); - if (error) return error; - error = write(stream, &log->len, sizeof(log->len), 1); - if (error) return error; - for (size_t i = 0; i < log->len; ++i) { - error = writeState(stream, buf, &log->states[i]); - if (error) return error; - } - return Ok; -} - -enum Error storeWrite(FILE *stream, const struct Edit *edit) { - enum Error error; - error = write(stream, Magic, sizeof(Magic), 1); - if (error) return error; - error = write(stream, &Version, sizeof(Version), 1); - if (error) return error; - error = writeBuffer(stream, &edit->buf); - if (error) return error; - return writeLog(stream, &edit->buf, &edit->log); -} - -static enum Error read(FILE *stream, void *ptr, size_t size, size_t nitems) { - size_t n = fread(ptr, size, nitems, stream); - if (ferror(stream)) return Errno + errno; - return (n < nitems ? StoreEOF : Ok); -} - -static enum Error readMagic(FILE *stream) { - char magic[3]; - enum Error error = read(stream, magic, sizeof(magic), 1); - if (error) return error; - return ( - magic[0] != Magic[0] || magic[1] != Magic[1] || magic[2] != Magic[2] - ? StoreMagic - : Ok - ); -} - -static enum Error readVersion(FILE *stream) { - char version; - enum Error error = read(stream, &version, sizeof(version), 1); - if (error) return error; - return (version != Version ? StoreVersion : Ok); -} - -static enum Error readBuffer(FILE *stream, struct Buffer *buf) { - size_t len; - enum Error error = read(stream, &len, sizeof(len), 1); - if (error) return error; - wchar_t *dest = bufferDest(buf, len); - return read(stream, dest, sizeof(wchar_t), len); -} - -static enum Error -readSlice(FILE *stream, struct Buffer *buf, struct Table *table) { - enum Error error; - size_t ptr, len; - error = read(stream, &ptr, sizeof(ptr), 1); - if (error) return error; - error = read(stream, &len, sizeof(len), 1); - if (error) return error; - tablePush(table, (struct Slice) { &buf->slice.ptr[ptr], len }); - return Ok; -} - -static enum Error -readTable(FILE *stream, struct Buffer *buf, struct Table *table) { - size_t len; - enum Error error = read(stream, &len, sizeof(len), 1); - if (error) return error; - *table = tableAlloc(len); - for (size_t i = 0; i < len; ++i) { - error = readSlice(stream, buf, table); - if (error) return error; - } - return Ok; -} - -static enum Error -readState(FILE *stream, struct Buffer *buf, size_t offset, struct Log *log) { - enum Error error; - size_t prev, next; - struct Table table; - error = read(stream, &prev, sizeof(prev), 1); - if (error) return error; - error = read(stream, &next, sizeof(next), 1); - if (error) return error; - error = readTable(stream, buf, &table); - if (error) return error; - logPush(log, table); - log->states[log->state].prev = offset + prev; - log->states[log->state].next = offset + next; - return Ok; -} - -static enum Error readLog(FILE *stream, struct Buffer *buf, struct Log *log) { - enum Error error; - size_t state, len; - error = read(stream, &state, sizeof(state), 1); - if (error) return error; - error = read(stream, &len, sizeof(len), 1); - if (error) return error; - size_t offset = log->len; - for (size_t i = 0; i < len; ++i) { - error = readState(stream, buf, offset, log); - if (error) return error; - } - log->state = offset + state; - return Ok; -} - -enum Error storeRead(FILE *stream, struct Edit *edit) { - enum Error error; - error = readMagic(stream); - if (error) return error; - error = readVersion(stream); - if (error) return error; - error = readBuffer(stream, &edit->buf); - if (error) return error; - return readLog(stream, &edit->buf, &edit->log); -} diff --git a/bin/edi/table.c b/bin/edi/table.c deleted file mode 100644 index 5f7e963e..00000000 --- a/bin/edi/table.c +++ /dev/null @@ -1,171 +0,0 @@ -/* Copyright (C) 2018 June 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 <err.h> -#include <stdlib.h> -#include <sysexits.h> -#include <wchar.h> - -#include "edi.h" - -struct Table tableAlloc(size_t cap) { - struct Slice *slices = malloc(sizeof(*slices) * cap); - if (!slices) err(EX_OSERR, "malloc"); - return (struct Table) { .cap = cap, .slices = slices }; -} - -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; -} - -void tableReplace(struct Table *table, struct Slice old, struct Slice new) { - for (size_t i = 0; i < table->len; ++i) { - if (table->slices[i].ptr != old.ptr) continue; - if (table->slices[i].len != old.len) continue; - table->slices[i] = new; - break; - } -} - -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); - if (span.at == at) { - next.slices[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, - at - span.at, - }; - next.slices[next.len++] = ins; - next.slices[next.len++] = (struct Slice) { - &prev->slices[i].ptr[at - span.at], - prev->slices[i].len - (at - span.at), - }; - } else { - next.slices[next.len++] = prev->slices[i]; - } - } - if (span.to == at) { - next.slices[next.len++] = ins; - } - return next; -} - -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); - if (span.at >= del.at && span.to <= del.to) { - (void)prev->slices[i]; - } else if (span.at < del.at && span.to > del.to) { - next.slices[next.len++] = (struct Slice) { - prev->slices[i].ptr, - del.at - 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, - del.at - span.at, - }; - } else if (span.at < del.to && span.to > del.to) { - 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]; - } - } - return next; -} - -#ifdef TEST -#include <assert.h> - -static struct Slice slice(const wchar_t *str) { - return (struct Slice) { str, wcslen(str) }; -} - -static struct Span span(size_t at, size_t to) { - return (struct Span) { at, to }; -} - -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]; - } - return !str[0]; -} - -int main() { - 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)); - - 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 |