summaryrefslogtreecommitdiff
path: root/csv-parse.c
blob: 6feefcbe2f7b72cac64ebbcfa32b57f6632ac5b2 (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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#include "csv-parse.h"

token *tokens = 0;
token *last_tok = 0;

line *lines = 0;
line *last_line = 0;


inline int iseol(char c) {
	return (c == '\n' || c == '\0');
}

token *make_token(char *value) {
	token *new_tok = malloc(sizeof(token));
	(last_tok) ? (last_tok->next = new_tok) : (tokens = new_tok);
	new_tok->value = value;
	new_tok->next = NULL;
	last_tok = new_tok;
	return new_tok;
}

line *get_line(char *str) {
	token *t = NULL;
	int skip = 0; /* Blank column count. */
	int isblank = 0; /* Used to check if this line is blank. */
	int i = 0;
	while (!iseol(str[i])) {
		char *value = NULL;
		switch (str[i]) {
			case '\"': for (++str; str[i] != '\"' && !iseol(str[i]); i++); break;
			default  : for (     ; str[i] != ','  && !iseol(str[i]); i++); break;
		}
		/* Is there anything in this column? */
		if (i) {
			/* Copy the column contents, and null terminate it. */
			value = malloc(i+1);
			memcpy(value, str, i);
			value[i] = '\0';
			/* Create the new column. */
			t = make_token(value);
			t->skip = skip;
			skip = 0;
			t = t->next;
		} else {
			value = NULL;
			skip += (t == NULL);
		}
		str += i+1; /* Shift the start of the line to the start of the next column. */
		i = 0;
	}
	isblank = (tokens == NULL && last_tok == NULL);
	/* Is this line blank? */
	if (!isblank) {
		line *l = malloc(sizeof(line));
		/* If there was a previous line, set the next line to the new line.
		 * Otherwise, set the starting line to the new line. */
		(last_line) ? (last_line->next = l) : (lines = l);
		/* Save the starting, and ending columns for this line. */
		l->tok = tokens;
		l->last_tok = last_tok;

		tokens = NULL;
		last_tok = NULL;

		l->next = NULL;
		last_line = l;
		return l;
	} else {
		return NULL;
	}
}

void parse_csv(FILE *fp) {
	char buf[0x1000];
	int blank_lines = 0;
	line *l;

	while (fgets(buf, sizeof(buf), fp) != NULL && !feof(fp)) {
		l = get_line(buf);
		blank_lines += (l == NULL);
		if (l != NULL) {
			l->skip = blank_lines;
			blank_lines = 0;
		}
	}
}

void free_tokens(token *t, token *lt, int row) {
	token *tok;
	int i = 0;
	int skip = 0;
	while (t != NULL) {
		tok = t;
		free(tok->value);
		t = t->next;
		i++;
		free(tok);
	}
}

void free_lines() {
	line *l = lines;
	line *ln;
	int i = 0;
	while (l != NULL) {
		free_tokens(l->tok, l->last_tok, i);
		i++;
		ln = l;
		l = l->next;
		free(ln);
	}
}