summaryrefslogtreecommitdiff
path: root/src/omd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/omd.c')
-rw-r--r--src/omd.c145
1 files changed, 145 insertions, 0 deletions
diff --git a/src/omd.c b/src/omd.c
new file mode 100644
index 0000000..5568d27
--- /dev/null
+++ b/src/omd.c
@@ -0,0 +1,145 @@
+#include <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "util/versie.h"
+#include "util/error.h"
+#include "util/debug.h"
+
+static void usage(FILE *f) {
+ fprintf(f,
+ "Gebruik: omd [-hV] <bestand...>\n"
+ "\n"
+ " -h Toon deze hulptekst\n"
+ " -V Toon versienummer\n");
+}
+
+// Returns pointer to argument array containing the file names
+static char** parse_options(int argc, char **argv) {
+ int opt;
+ while ((opt = getopt(argc, argv, "hV")) != -1) {
+ switch (opt) {
+ case 'h':
+ usage(stdout);
+ exit(0);
+
+ case 'V':
+ drukkedoos_print_versie(stdout, "omd");
+ exit(0);
+
+ case '?':
+ usage(stderr);
+ exit(1);
+ }
+ }
+
+ return argv + optind;
+}
+
+static void reverse_in_place(char *buf, size_t len) {
+ for (size_t i = 0, j = len - 1; i < len / 2; i++, j--) {
+ char c = buf[i];
+ buf[i] = buf[j];
+ buf[j] = c;
+ }
+}
+
+static void process(const char *fname, FILE *f) {
+ size_t cap = 4096;
+ char *buffer = malloc(cap);
+ if (!buffer) print_error_nomem("omd");
+
+ size_t cursor = 0;
+
+ while (true) {
+ DEBUG("cursor=%zu\n", cursor);
+
+ // read a line
+ size_t linelen = 0;
+ while (true) {
+ // grow buffer when small
+ if (cap - cursor < 1024) {
+ cap *= 2;
+ if (cap == 0) {
+ fprintf(stderr, "omd: Regel te lang\n");
+ exit(1);
+ }
+ DEBUG("realloc to cap %zu\n", cap);
+ buffer = realloc(buffer, cap);
+ if (!buffer) print_error_nomem("omd");
+ }
+
+ size_t nr = fread(buffer + cursor, 1, cap - cursor, f);
+ DEBUG("nr=%zu\n", nr);
+ if (nr == 0) {
+ if (feof(f)) {
+ if (cursor == 0) return;
+ linelen = cursor;
+ break;
+ }
+ if (fname) fprintf(stderr, "omd: Fout bij lezen van bestand '%s'\n", fname);
+ else fprintf(stderr, "omd: Fout bij lezen van standaardinvoer\n");
+ exit(1);
+ }
+ char *p = memchr(buffer + cursor, '\n', nr);
+ cursor += nr;
+ if (p != NULL) {
+ linelen = p - buffer;
+ break;
+ }
+ }
+
+ DEBUG("line with len %zu (cursor=%zu)\n", linelen, cursor);
+
+ // we have a newline at cursor+linelen; print all lines that we already have in full
+ size_t linestart = 0;
+ while (true) {
+ reverse_in_place(buffer + linestart, linelen);
+ if (linestart + linelen < cap && buffer[linestart + linelen] == '\n') {
+ size_t nw = fwrite(buffer + linestart, 1, linelen + 1, stdout);
+ if (nw < linelen + 1) exit(1);
+ } else {
+ size_t nw = fwrite(buffer + linestart, 1, linelen, stdout);
+ if (nw < linelen) exit(1);
+ putchar('\n');
+ // no newline in the buffer but nevertheless a completed line means that input is at EOF
+ goto cleanup;
+ }
+
+ linestart += linelen + 1;
+ char *p = memchr(buffer + linestart, '\n', cursor - linestart);
+ if (p == NULL) {
+ DEBUG("no more newlines, moving back from %zu len %zu\n", linestart, cursor - linestart);
+ // move back whatever we have left
+ memmove(buffer, buffer + linestart, cursor - linestart);
+ cursor -= linestart;
+ break;
+ }
+ linelen = p - (buffer + linestart);
+ DEBUG("next line from %zu len %zu\n", linestart, linelen);
+ }
+ }
+
+cleanup:
+ free(buffer);
+}
+
+int entry_omd(int argc, char **argv) {
+ char **args = parse_options(argc, argv);
+ if (*args == NULL) {
+ process(NULL, stdin);
+ } else {
+ while (*args != NULL) {
+ FILE *f = fopen(*args, "r");
+ if (!f) {
+ fprintf(stderr, "omd: Kon bestand '%s' niet openen\n", *args);
+ return 1;
+ }
+ process(*args, f);
+ fclose(f);
+ args++;
+ }
+ }
+ return 0;
+}