summaryrefslogtreecommitdiff
path: root/src/io
diff options
context:
space:
mode:
Diffstat (limited to 'src/io')
-rw-r--r--src/io/lines.c92
-rw-r--r--src/io/lines.h25
2 files changed, 117 insertions, 0 deletions
diff --git a/src/io/lines.c b/src/io/lines.c
new file mode 100644
index 0000000..6367c1d
--- /dev/null
+++ b/src/io/lines.c
@@ -0,0 +1,92 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "lines.h"
+#include "util/debug.h"
+#include "util/error.h"
+
+
+bool file_lines_open(const char *progname, const char *fname, FILE *f, struct file_lines *dst) {
+ dst->f = f;
+
+ dst->cap = 4096;
+ dst->buffer = malloc(dst->cap);
+ if (!dst->buffer) {
+ print_error_nomem(progname);
+ return false;
+ }
+
+ dst->cursor = 0;
+ dst->readfrom = (size_t)-1;
+ dst->progname = progname;
+ dst->fname = fname;
+ return true;
+}
+
+bool file_lines_read(struct file_lines *fl, struct string_view *dst) {
+ // Return already-read line if we have any
+ if (fl->readfrom != (size_t)-1) {
+ char *p = memchr(fl->buffer + fl->readfrom, '\n', fl->cursor - fl->readfrom);
+ if (p != NULL) {
+ dst->s = fl->buffer + fl->readfrom;
+ dst->len = p - (fl->buffer + fl->readfrom);
+ fl->readfrom = p - fl->buffer + 1;
+ DEBUG("next line from %zu len %zu\n", fl->readfrom, dst->len);
+ return true;
+ }
+
+ DEBUG("no more newlines, moving back from %zu len %zu\n", fl->readfrom, fl->cursor - fl->readfrom);
+ // move back whatever we have left
+ memmove(fl->buffer, fl->buffer + fl->readfrom, fl->cursor - fl->readfrom);
+ fl->cursor -= fl->readfrom;
+ }
+
+ DEBUG("cursor=%zu\n", fl->cursor);
+
+ // read a line
+ size_t linelen = 0;
+ while (true) {
+ // grow buffer when small
+ if (fl->cap - fl->cursor < 1024) {
+ fl->cap *= 2;
+ if (fl->cap == 0) {
+ fprintf(stderr, "%s: Regel te lang\n", fl->progname);
+ exit(1);
+ }
+ DEBUG("realloc to cap %zu\n", fl->cap);
+ fl->buffer = realloc(fl->buffer, fl->cap);
+ if (!fl->buffer) print_error_nomem(fl->progname);
+ }
+
+ size_t nr = fread(fl->buffer + fl->cursor, 1, fl->cap - fl->cursor, fl->f);
+ DEBUG("nr=%zu\n", nr);
+ if (nr == 0) {
+ if (feof(fl->f)) {
+ if (fl->cursor == 0) return false;
+ linelen = fl->cursor;
+ break;
+ }
+ if (fl->fname) fprintf(stderr, "%s: Fout bij lezen van bestand '%s'\n", fl->progname, fl->fname);
+ else fprintf(stderr, "%s: Fout bij lezen van standaardinvoer\n", fl->progname);
+ exit(1);
+ }
+ char *p = memchr(fl->buffer + fl->cursor, '\n', nr);
+ fl->cursor += nr;
+ if (p != NULL) {
+ linelen = p - fl->buffer;
+ break;
+ }
+ }
+
+ DEBUG("line with len %zu (cursor=%zu)\n", linelen, fl->cursor);
+
+ // we have a newline at buffer + linelen; return lines in buffer
+ dst->s = fl->buffer;
+ dst->len = linelen;
+ fl->readfrom = linelen + 1;
+ return true;
+}
+
+void file_lines_close(struct file_lines *fl) {
+ free(fl->buffer);
+}
diff --git a/src/io/lines.h b/src/io/lines.h
new file mode 100644
index 0000000..b09b756
--- /dev/null
+++ b/src/io/lines.h
@@ -0,0 +1,25 @@
+#pragma once
+
+#include <stdio.h>
+#include <stdbool.h>
+
+#include "util/string_view.h"
+
+
+struct file_lines {
+ const char *progname, *fname;
+
+ FILE *f;
+ size_t cap;
+ char *buffer;
+ size_t cursor;
+ size_t readfrom; // if -1, no newline in buffer
+};
+
+// Returns false on EOF. FILE remains ownership of the caller.
+bool file_lines_open(const char *progname, const char *fname, FILE *f, struct file_lines *dst);
+
+// Returns whether successful
+bool file_lines_read(struct file_lines *fl, struct string_view *dst);
+
+void file_lines_close(struct file_lines *fl);