#include "csv.h" #include #include #include struct csv_reader { const char *p; const char *end; }; typedef struct { char *data; size_t len; size_t cap; } csv_buf_t; csv_reader_t *csv_reader_new(const char *data, size_t len) { csv_reader_t *reader; if (data == NULL) return NULL; reader = calloc(1, sizeof(*reader)); if (reader == NULL) return NULL; reader->p = data; reader->end = data + len; if (len >= 3 && memcmp(data, "\xEF\xBB\xBF", 3) == 0) reader->p += 3; return reader; } void csv_reader_free(csv_reader_t *reader) { free(reader); } void csv_row_free(csv_row_t *row) { size_t i; if (row == NULL) return; for (i = 0; i < row->count; i++) free(row->fields[i]); free(row->fields); row->fields = NULL; row->count = 0; } static bool buf_push(csv_buf_t *buf, char c) { if (buf->len + 2 > buf->cap) { size_t cap = buf->cap ? buf->cap * 2 : 32; char *grown = realloc(buf->data, cap); if (grown == NULL) return false; buf->data = grown; buf->cap = cap; } buf->data[buf->len++] = c; buf->data[buf->len] = '\0'; return true; } static bool row_push(csv_row_t *row, size_t *cap, char *field) { if (row->count + 1 > *cap) { size_t next = *cap ? *cap * 2 : 16; char **grown = realloc(row->fields, next * sizeof(*grown)); if (grown == NULL) return false; row->fields = grown; *cap = next; } row->fields[row->count++] = field; return true; } static char *parse_field(csv_reader_t *reader) { csv_buf_t field = {NULL, 0, 0}; bool quoted = false; if (!buf_push(&field, '\0')) return NULL; field.len = 0; field.data[0] = '\0'; if (reader->p < reader->end && *reader->p == '"') { quoted = true; reader->p++; } while (reader->p < reader->end) { char c = *reader->p; if (quoted) { reader->p++; if (c != '"') { if (!buf_push(&field, c)) goto fail; continue; } if (reader->p < reader->end && *reader->p == '"') { reader->p++; if (!buf_push(&field, '"')) goto fail; continue; } quoted = false; continue; } if (c == ',' || c == '\n' || c == '\r') break; reader->p++; if (!buf_push(&field, c)) goto fail; } if (quoted) goto fail; return field.data; fail: free(field.data); return NULL; } int csv_reader_next(csv_reader_t *reader, csv_row_t *out) { csv_row_t row = {NULL, 0}; size_t cap = 0; if (reader == NULL || out == NULL) return -1; if (reader->p >= reader->end) return 0; for (;;) { char *field = parse_field(reader); if (field == NULL) { csv_row_free(&row); return -1; } if (!row_push(&row, &cap, field)) { free(field); csv_row_free(&row); return -1; } if (reader->p >= reader->end) break; if (*reader->p == ',') { reader->p++; continue; } if (*reader->p == '\r') reader->p++; if (reader->p < reader->end && *reader->p == '\n') reader->p++; break; } *out = row; return 1; }