diff options
Diffstat (limited to 'src/io')
| -rw-r--r-- | src/io/lines.c | 92 | ||||
| -rw-r--r-- | src/io/lines.h | 25 | 
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);  | 
