-
Notifications
You must be signed in to change notification settings - Fork 0
/
csv.c
128 lines (122 loc) · 3.74 KB
/
csv.c
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cgic.h>
#include "csv.h"
#include "list.h"
#include "alloc.h"
#include "util.h"
#include "simple_string.h"
#define MAX_WIDTH 1000
typedef enum _mode {
CSV_MODE_SEPARATER,
CSV_MODE_DATA,
CSV_MODE_QUOTED_DATA
} CsvMode;
#define is_space(c) (c == ' ' || c == '\t')
static void decord_csv_field(char* src, char* dist)
{
bool quote = false;
char* p = src;
char* p_dist = dist;
/* delete prefix spaces. */
while (*p != '\0') {
if (!(strlen(dist) == 0 && is_space(*p))) { /* the first space is not adeed. */
if (strlen(dist) > 0 || *p != '"') { /* the first quote is not added. */
if (strlen(dist) == 0 || !(*p == '"' && *(p - 1) == '"')) { /* the doubled quote is not added. */
*p_dist++ = *p;
}
} else if (strlen(dist) == 0 && *p == '"') {
quote = true; /* into quote mode */
}
}
p++;
}
p_dist--;
/* delete suffix spaces. */
while (is_space(*p_dist)) {
*p_dist-- = '\0';
}
/* if quoate mode and last is quote, then delete. */
if (quote && *p_dist == '"') {
*p_dist = '\0';
}
}
Csv* csv_new(char* content)
{
CsvMode mode = CSV_MODE_DATA;
char* p = content;
char* mark;
CsvLine* line;
int field_count = 0;
Csv* csv = xalloc(sizeof(Csv));
/* create first line. */
list_alloc(csv->lines, CsvLine, NULL, NULL);
line = list_new_element(csv->lines);
list_alloc(line->fields, CsvField, NULL, NULL);
mark = p;
while (1) {
bool data_end = false;
if (mode == CSV_MODE_QUOTED_DATA) {
if (*p == '"' && *(p + 1) == '"') {
/* escaped quote */
p++;
} else if (*p == '"') {
/* quote mode ends */
mode = CSV_MODE_DATA;
}
} else if (mode == CSV_MODE_DATA && *p == '"') {
mode = CSV_MODE_QUOTED_DATA;
}
if (mode == CSV_MODE_DATA) {
data_end = (*p == '\0' || *p == '\n' || *p == ',');
if (data_end) {
CsvField* field = list_new_element(line->fields);
char buf[p - mark + 1];
char buf_decorded[p - mark + 1];
memset(buf, '\0', p - mark + 1);
memset(buf_decorded, '\0', p - mark + 1);
field->data = string_new();
strncpy(buf, mark, p - mark);
decord_csv_field(buf, buf_decorded);
string_append(field->data, buf_decorded);
list_add(line->fields, field);
field_count++;
if (csv->field_count < field_count)
csv->field_count = field_count;
}
}
if (mode != CSV_MODE_QUOTED_DATA && *p == '\n') {
list_add(csv->lines, line);
csv->line_count++;
field_count = 0;
line = list_new_element(csv->lines);
list_alloc(line->fields, CsvField, NULL, NULL); /* initialize line */
} else if (*p == '\0') {
list_add(csv->lines, line);
csv->line_count++;
break;
}
if (data_end) {
mark = p + 1; /* marking start point for next field. */
}
p++;
}
return csv;
}
void csv_free(Csv* csv)
{
Iterator* it;
foreach (it, csv->lines) {
CsvLine* line = it->element;
Iterator* it_field;
foreach (it_field, line->fields) {
CsvField* field = it_field->element;
string_free(field->data);
}
list_free(line->fields);
}
list_free(csv->lines);
xfree(csv);
}
/* vim: set ts=4 sw=4 sts=4 expandtab fenc=utf-8: */