summaryrefslogtreecommitdiff
path: root/src/io/lines.c
blob: e5de6df784f4da681e636fee331590d7c5f1b5bc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#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();
    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;
  bool havenewline = true;
  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();
    }

    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;
        havenewline = false;
        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 + havenewline;
  return true;
}

void file_lines_close(struct file_lines *fl) {
  free(fl->buffer);
}