aboutsummaryrefslogtreecommitdiffstats
path: root/src/DSV.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/DSV.c')
-rw-r--r--src/DSV.c188
1 files changed, 188 insertions, 0 deletions
diff --git a/src/DSV.c b/src/DSV.c
new file mode 100644
index 0000000..91cc7ee
--- /dev/null
+++ b/src/DSV.c
@@ -0,0 +1,188 @@
+/* DSV.c
+ *
+ * Copyright (c) 2016 Tharre
+ *
+ * This software may be modified and distributed under the terms
+ * of the MIT license. See the LICENSE file for details.
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.h"
+#include "dbg.h"
+#include "DSV.h"
+
+/* dest must be 2x src */
+size_t encode_string(char *dest, const char *src) {
+ char *start = dest, c;
+
+ while ((c = *(src++))) {
+ switch (c) {
+ case '\n':
+ *(dest++) = '\\';
+ *(dest++) = 'n';
+ break;
+ case '\\':
+ *(dest++) = '\\';
+ *(dest++) = '\\';
+ break;
+ case ':':
+ *(dest++) = '\\';
+ /* fall-through */
+ default:
+ *(dest++) = c;
+ }
+ }
+
+ *dest = '\0';
+ return dest - start;
+}
+
+static void decode_string(char *dest, const char *src, size_t len) {
+ for (size_t i = 0; i < len-1; ++i) {
+ char c = src[i];
+ if (c != '\\') {
+ *(dest++) = c;
+ continue;
+ }
+
+ /* check if we are at the end of the string */
+ if (*src == '\0') {
+ debug("DSV: ignored extra '\\' at end of string\n");
+ break;
+ }
+
+ switch (c = *(src++)) {
+ case 'n':
+ *(dest++) = '\n';
+ break;
+ default:
+ *(dest++) = c;
+ }
+ }
+
+ *dest = '\0';
+}
+
+void dsv_init(struct dsv_ctx *ctx, size_t fields_count) {
+ ctx->fields_count = fields_count;
+ ctx->fields = xmalloc(fields_count * sizeof(char*));
+ ctx->offset = 0;
+ ctx->bufsize = 1024;
+ ctx->buf = xmalloc(ctx->bufsize);
+ ctx->buflen = 0;
+}
+
+void dsv_free(struct dsv_ctx *ctx) {
+ free(ctx->fields);
+ free(ctx->buf);
+}
+
+int dsv_parse_next_line(struct dsv_ctx *context, const char *src, size_t len) {
+ char *newline = memchr(src, '\n', len);
+
+ if (!newline) {
+ context->status = E_NO_NEWLINE_FOUND;
+ return 1;
+ }
+
+ context->processed = newline-src+1;
+ if (context->processed == 1) {
+ debug("DSV: empty newline detected\n");
+ context->status = E_EMPTY_NEWLINE;
+ return 1;
+ }
+
+ char *start = (char*) src;
+ size_t i = 0;
+ ptrdiff_t size;
+
+ while (1) {
+ char *colon = memchr(start, ':', newline-start+1);
+ if (colon)
+ size = colon-start;
+ else
+ size = newline-start;
+
+ if (size <= 0) {
+ if (!size) {
+ debug("DSV: empty field detected\n");
+ context->status = E_EMPTY_FIELD;
+ goto error;
+ }
+
+ break;
+ }
+
+ char *buf = xmalloc(size+1);
+ decode_string(buf, start, size+1);
+ context->fields[i] = buf;
+
+ start += size + 1;
+ ++i;
+ if (i > context->fields_count) {
+ debug("DSV: too many fields\n");
+ context->status = E_TOO_MANY_FIELDS;
+ goto error;
+ }
+ }
+
+ if (i+1 < context->fields_count) {
+ debug("DSV: too few fields (%zu)\n", i+1);
+ context->status = E_TOO_FEW_FIELDS;
+ goto error;
+ }
+
+ context->status = E_SUCCESS;
+ return 0;
+
+error:
+ for (size_t j = 0; j < i; ++j)
+ free(context->fields[j]);
+
+ return 1;
+}
+
+int dsv_parse_file(struct dsv_ctx *ctx, FILE *fp) {
+ size_t read = ctx->buflen;
+ if (!read) {
+ read = fread(ctx->buf, 1, ctx->bufsize, fp);
+ ctx->buflen = read;
+ }
+
+ while (read > 0) {
+ if (dsv_parse_next_line(ctx, ctx->buf+ctx->offset, read-ctx->offset)) {
+ if (ctx->status != E_NO_NEWLINE_FOUND)
+ return 1;
+
+ /* are we using the full buffer already? */
+ if (!ctx->offset) {
+ /* then make it bigger! */
+ ctx->bufsize *= 2;
+ ctx->buf = xrealloc(ctx->buf, ctx->bufsize);
+ } else {
+ /* then move stuff around */
+ memmove(ctx->buf, ctx->buf+ctx->offset, read-ctx->offset);
+ ctx->buflen -= ctx->offset;
+ ctx->offset = 0;
+ }
+
+ read = fread(ctx->buf+ctx->buflen, 1, ctx->bufsize-ctx->buflen, fp);
+ ctx->buflen += read;
+ } else {
+ ctx->offset += ctx->processed;
+ ctx->status = E_SUCCESS;
+ return 0;
+ }
+ }
+
+ if (ferror(fp)) {
+ ctx->status = E_FREAD_ERROR;
+ return 1;
+ }
+
+ return 1;
+}