aboutsummaryrefslogtreecommitdiff
path: root/csv.c
diff options
context:
space:
mode:
Diffstat (limited to 'csv.c')
-rw-r--r--csv.c108
1 files changed, 57 insertions, 51 deletions
diff --git a/csv.c b/csv.c
index 71c0e0f..2948911 100644
--- a/csv.c
+++ b/csv.c
@@ -1,29 +1,30 @@
1#include "csv.h"
2
3#include <assert.h>
1#include <stdio.h> 4#include <stdio.h>
2#include <stdlib.h> 5#include <stdlib.h>
3#include <assert.h>
4#include <string.h> 6#include <string.h>
5#include "csv.h"
6 7
7#ifndef CSV_DEFAULT_DELIMITER 8#ifndef CSV_DEFAULT_DELIMITER
8#define CSV_DEFAULT_DELIMITER '"' 9# define CSV_DEFAULT_DELIMITER '"'
9#endif 10#endif
10 11
11#ifndef CSV_DEFAULT_SEPARATOR 12#ifndef CSV_DEFAULT_SEPARATOR
12#define CSV_DEFAULT_SEPARATOR ',' 13# define CSV_DEFAULT_SEPARATOR ','
13#endif 14#endif
14 15
15#if defined(__GNUC__) || defined(__clang__) 16#if defined(__GNUC__) || defined(__clang__)
16#define mul_overflow __builtin_mul_overflow 17# define mul_overflow __builtin_mul_overflow
17#else 18#else
18static inline int 19static inline int
19mul_overflow(size_t a, size_t b, size_t *out) 20mul_overflow(size_t a, size_t b, size_t *out)
20{ 21{
21 *out = a * b; 22 *out = a * b;
22 return ( a != 0 && (*out / a) != b ); 23 return (a != 0 && (*out / a) != b);
23} 24}
24#endif 25#endif
25 26
26#define UNUSED(x) (void)(sizeof((x), 0)) 27#define UNUSED(x) (void) (sizeof((x), 0))
27 28
28// === CSV-MEMORY Interface === 29// === CSV-MEMORY Interface ===
29 30
@@ -66,16 +67,16 @@ csv_mem_free(void *ptr, size_t n, size_t sz, void *cb_arg)
66const csv_options_t csv_default_options = { 67const csv_options_t csv_default_options = {
67 .field_delimiter = CSV_DEFAULT_DELIMITER, 68 .field_delimiter = CSV_DEFAULT_DELIMITER,
68 .field_separator = CSV_DEFAULT_SEPARATOR, 69 .field_separator = CSV_DEFAULT_SEPARATOR,
69 .cb_error = NULL, 70 .cb_error = NULL,
70 .cb_allocate = csv_mem_allocate, 71 .cb_allocate = csv_mem_allocate,
71 .cb_reallocate = csv_mem_reallocate, 72 .cb_reallocate = csv_mem_reallocate,
72 .cb_free = csv_mem_free 73 .cb_free = csv_mem_free
73}; 74};
74 75
75// === CSV-ERROR Interface === 76// === CSV-ERROR Interface ===
76 77
77static void 78static void
78csv_fatal_error(csv_err_t csv_err, const csv_options_t * const csv_options) 79csv_fatal_error(csv_err_t csv_err, const csv_options_t *const csv_options)
79{ 80{
80 if ( csv_options->cb_error != NULL ) { 81 if ( csv_options->cb_error != NULL ) {
81 (*csv_options->cb_error)(csv_err, csv_options->cb_error_arg); 82 (*csv_options->cb_error)(csv_err, csv_options->cb_error_arg);
@@ -109,27 +110,27 @@ csv_string_empty(csv_string_t *csv_string)
109{ 110{
110 assert(csv_string != NULL); 111 assert(csv_string != NULL);
111 112
112 return ( csv_string->pos == 0 ) ? 1 : 0; 113 return (csv_string->pos == 0) ? 1 : 0;
113} 114}
114 115
115static inline void 116static inline void
116csv_string_append(csv_string_t *csv_string, char ch, const csv_options_t * const csv_options) 117csv_string_append(csv_string_t *csv_string, char ch, const csv_options_t *const csv_options)
117{ 118{
118 assert(csv_string != NULL); 119 assert(csv_string != NULL);
119 assert(csv_options != NULL); 120 assert(csv_options != NULL);
120 121
121 static const int INITIAL_CAP = 16; 122 static const int INITIAL_CAP = 16;
122 123
123 if ( csv_string->pos == csv_string->cap ) { // grow if needed 124 if ( csv_string->pos == csv_string->cap ) { // grow if needed
124 if ( csv_string->str == NULL ) { // first call? 125 if ( csv_string->str == NULL ) { // first call?
125 if ( (csv_string->str = csv_options->cb_allocate(INITIAL_CAP, 1, csv_options->cb_memory_arg)) == NULL ) { 126 if ( (csv_string->str = csv_options->cb_allocate(INITIAL_CAP, 1, csv_options->cb_memory_arg)) == NULL ) {
126 csv_fatal_error(CSV_ERR_OUT_OF_MEMORY, csv_options); 127 csv_fatal_error(CSV_ERR_OUT_OF_MEMORY, csv_options);
127 return; 128 return;
128 } 129 }
129 csv_string->cap = INITIAL_CAP; 130 csv_string->cap = INITIAL_CAP;
130 } 131 }
131 else { // subsequent call 132 else { // subsequent call
132 int cap = (csv_string->cap * 3) / 2; // *= 1.5 133 int cap = (csv_string->cap * 3) / 2; // *= 1.5
133 char *str = csv_options->cb_reallocate(csv_string->str, cap, 1, csv_options->cb_memory_arg); 134 char *str = csv_options->cb_reallocate(csv_string->str, cap, 1, csv_options->cb_memory_arg);
134 if ( str == NULL ) { 135 if ( str == NULL ) {
135 csv_fatal_error(CSV_ERR_OUT_OF_MEMORY, csv_options); 136 csv_fatal_error(CSV_ERR_OUT_OF_MEMORY, csv_options);
@@ -140,11 +141,11 @@ csv_string_append(csv_string_t *csv_string, char ch, const csv_options_t * const
140 } 141 }
141 } 142 }
142 143
143 csv_string->str[csv_string->pos++] = ch; // append char 144 csv_string->str[csv_string->pos++] = ch; // append char
144} 145}
145 146
146static inline void 147static inline void
147csv_string_free(csv_string_t *csv_string, const csv_options_t * const csv_options) 148csv_string_free(csv_string_t *csv_string, const csv_options_t *const csv_options)
148{ 149{
149 assert(csv_string != NULL); 150 assert(csv_string != NULL);
150 assert(csv_options != NULL); 151 assert(csv_options != NULL);
@@ -163,8 +164,8 @@ csv_field_init(csv_field_t *csv_field)
163 assert(csv_field != NULL); 164 assert(csv_field != NULL);
164 165
165 csv_field->fields = NULL; 166 csv_field->fields = NULL;
166 csv_field->cap = 0; 167 csv_field->cap = 0;
167 csv_field->pos = 0; 168 csv_field->pos = 0;
168} 169}
169 170
170static inline void 171static inline void
@@ -176,30 +177,30 @@ csv_field_reset(csv_field_t *csv_field)
176} 177}
177 178
178static inline void 179static inline void
179csv_field_append(csv_field_t *csv_field, int idx, const csv_options_t * const csv_options) 180csv_field_append(csv_field_t *csv_field, int idx, const csv_options_t *const csv_options)
180{ 181{
181 assert(csv_field != NULL); 182 assert(csv_field != NULL);
182 assert(csv_options != NULL); 183 assert(csv_options != NULL);
183 184
184 static const size_t INITIAL_CAP = 16; 185 static const size_t INITIAL_CAP = 16;
185 186
186 if ( csv_field->pos == csv_field->cap ) { // grow if needed 187 if ( csv_field->pos == csv_field->cap ) { // grow if needed
187 if ( csv_field->fields == NULL ) { // first call 188 if ( csv_field->fields == NULL ) { // first call
188 if ( (csv_field->fields = csv_options->cb_allocate(INITIAL_CAP, sizeof(csv_field->fields[0]), csv_options->cb_memory_arg)) == NULL ) { 189 if ( (csv_field->fields = csv_options->cb_allocate(INITIAL_CAP, sizeof(csv_field->fields[0]), csv_options->cb_memory_arg)) == NULL ) {
189 csv_fatal_error(CSV_ERR_OUT_OF_MEMORY, csv_options); 190 csv_fatal_error(CSV_ERR_OUT_OF_MEMORY, csv_options);
190 return; 191 return;
191 } 192 }
192 csv_field->cap = INITIAL_CAP; 193 csv_field->cap = INITIAL_CAP;
193 } 194 }
194 else { // subsequent call 195 else { // subsequent call
195 int cap = (csv_field->cap * 3) / 2; // *= 1.5 196 int cap = (csv_field->cap * 3) / 2; // *= 1.5
196 int *fields = csv_options->cb_reallocate(csv_field->fields, cap, sizeof(csv_field->fields[0]), csv_options->cb_memory_arg); 197 int *fields = csv_options->cb_reallocate(csv_field->fields, cap, sizeof(csv_field->fields[0]), csv_options->cb_memory_arg);
197 if ( fields == NULL ) { 198 if ( fields == NULL ) {
198 csv_fatal_error(CSV_ERR_OUT_OF_MEMORY, csv_options); 199 csv_fatal_error(CSV_ERR_OUT_OF_MEMORY, csv_options);
199 return; 200 return;
200 } 201 }
201 csv_field->fields = fields; 202 csv_field->fields = fields;
202 csv_field->cap = cap; 203 csv_field->cap = cap;
203 } 204 }
204 } 205 }
205 206
@@ -207,7 +208,7 @@ csv_field_append(csv_field_t *csv_field, int idx, const csv_options_t * const cs
207} 208}
208 209
209static inline void 210static inline void
210csv_field_free(csv_field_t *csv_field, const csv_options_t * const csv_options) 211csv_field_free(csv_field_t *csv_field, const csv_options_t *const csv_options)
211{ 212{
212 assert(csv_field != NULL); 213 assert(csv_field != NULL);
213 assert(csv_options != NULL); 214 assert(csv_options != NULL);
@@ -228,7 +229,7 @@ csv_init(csv_t *csv)
228} 229}
229 230
230void 231void
231csv_init_opt(csv_t *csv, const csv_options_t * const csv_options) 232csv_init_opt(csv_t *csv, const csv_options_t *const csv_options)
232{ 233{
233 assert(csv != NULL); 234 assert(csv != NULL);
234 235
@@ -255,7 +256,7 @@ csv_cleanup(csv_t *csv)
255} 256}
256 257
257int 258int
258csv_nfields(const csv_t * const csv) 259csv_nfields(const csv_t *const csv)
259{ 260{
260 assert(csv != NULL); 261 assert(csv != NULL);
261 262
@@ -266,7 +267,7 @@ csv_nfields(const csv_t * const csv)
266} 267}
267 268
268const char * 269const char *
269csv_field(const csv_t * const csv, int idx) 270csv_field(const csv_t *const csv, int idx)
270{ 271{
271 assert(csv != NULL); 272 assert(csv != NULL);
272 assert(idx >= 0 && idx < csv->csv_field.pos); 273 assert(idx >= 0 && idx < csv->csv_field.pos);
@@ -310,12 +311,12 @@ csv_read(csv_t *csv, FILE *in)
310 }; 311 };
311 312
312 register const int DELIM = csv->csv_options->field_delimiter; 313 register const int DELIM = csv->csv_options->field_delimiter;
313 register const int SEP = csv->csv_options->field_separator; 314 register const int SEP = csv->csv_options->field_separator;
314 315
315 csv_string_reset(&csv->csv_string); 316 csv_string_reset(&csv->csv_string);
316 csv_field_reset(&csv->csv_field); 317 csv_field_reset(&csv->csv_field);
317 318
318 for ( int state = STATE_START_FIELD; ; ) { 319 for ( int state = STATE_START_FIELD;; ) {
319 int ch; 320 int ch;
320 321
321 switch ( state ) { 322 switch ( state ) {
@@ -326,9 +327,9 @@ csv_read(csv_t *csv, FILE *in)
326 if ( ch == EOF ) { 327 if ( ch == EOF ) {
327 state = STATE_END_FILE; 328 state = STATE_END_FILE;
328 } 329 }
329 else if ( ch == '\r' ) { // test for CR.. 330 else if ( ch == '\r' ) { // test for CR..
330 ch = getc(in); 331 ch = getc(in);
331 if ( ch != '\n' ) { // ..LF 332 if ( ch != '\n' ) { // ..LF
332 ungetc(ch, in); 333 ungetc(ch, in);
333 } 334 }
334 state = STATE_END_LINE; 335 state = STATE_END_LINE;
@@ -379,7 +380,7 @@ csv_read(csv_t *csv, FILE *in)
379 } 380 }
380 else { 381 else {
381 csv_string_append(&csv->csv_string, DELIM, csv->csv_options); 382 csv_string_append(&csv->csv_string, DELIM, csv->csv_options);
382 ungetc(ch, in); // we have read too far... Put the character back! 383 ungetc(ch, in); // we have read too far... Put the character back!
383 } 384 }
384 } 385 }
385 else { 386 else {
@@ -434,7 +435,7 @@ csv_read(csv_t *csv, FILE *in)
434 435
435 if ( csv_string_empty(&csv->csv_string) ) { 436 if ( csv_string_empty(&csv->csv_string) ) {
436 csv_cleanup(csv); 437 csv_cleanup(csv);
437 return EOF; // EOF reached 438 return EOF; // EOF reached
438 } 439 }
439 440
440 /* 441 /*
@@ -458,18 +459,24 @@ const char *
458csv_err_str(csv_err_t csv_err) 459csv_err_str(csv_err_t csv_err)
459{ 460{
460 switch ( csv_err ) { 461 switch ( csv_err ) {
461 case CSV_ERR_OK: return "no error"; 462 case CSV_ERR_OK:
462 case CSV_ERR_OUT_OF_MEMORY: return "out of memory"; 463 return "no error";
463 case CSV_ERR_OUT_OF_RANGE: return "index out of range"; 464 case CSV_ERR_OUT_OF_MEMORY:
464 case CSV_ERR_IO_READ: return "read error"; 465 return "out of memory";
465 case CSV_ERR_IO_WRITE: return "write error"; 466 case CSV_ERR_OUT_OF_RANGE:
466 default: return "unknown error"; 467 return "index out of range";
468 case CSV_ERR_IO_READ:
469 return "read error";
470 case CSV_ERR_IO_WRITE:
471 return "write error";
472 default:
473 return "unknown error";
467 } 474 }
468 // NOT REACHED 475 // NOT REACHED
469} 476}
470 477
471static void 478static void
472csv_write_field(FILE *out, const char *field, const csv_options_t * const csv_options) 479csv_write_field(FILE *out, const char *field, const csv_options_t *const csv_options)
473{ 480{
474 assert(csv_options != NULL); 481 assert(csv_options != NULL);
475 assert(field != NULL); 482 assert(field != NULL);
@@ -499,7 +506,7 @@ csv_write_field(FILE *out, const char *field, const csv_options_t * const csv_op
499} 506}
500 507
501void 508void
502csv_write(FILE *out, const csv_t * const csv) 509csv_write(FILE *out, const csv_t *const csv)
503{ 510{
504 assert(out != NULL); 511 assert(out != NULL);
505 assert(csv != NULL); 512 assert(csv != NULL);
@@ -510,8 +517,8 @@ csv_write(FILE *out, const csv_t * const csv)
510 register const int SEP = csv->csv_options->field_separator; 517 register const int SEP = csv->csv_options->field_separator;
511 518
512 if ( 0 < csv->csv_field.pos ) { 519 if ( 0 < csv->csv_field.pos ) {
513 const char *str = csv->csv_string.str; 520 const char *str = csv->csv_string.str;
514 const int *field = csv->csv_field.fields; 521 const int * field = csv->csv_field.fields;
515 522
516 csv_write_field(out, &str[*field++], csv->csv_options); 523 csv_write_field(out, &str[*field++], csv->csv_options);
517 524
@@ -529,7 +536,7 @@ csv_write(FILE *out, const csv_t * const csv)
529} 536}
530 537
531void 538void
532csv_write_ex(FILE *out, int n, const char *fields[], const csv_options_t * csv_options) 539csv_write_ex(FILE *out, int n, const char *fields[], const csv_options_t *csv_options)
533{ 540{
534 assert(out != NULL); 541 assert(out != NULL);
535 assert(fields != NULL); 542 assert(fields != NULL);
@@ -558,4 +565,3 @@ csv_write_ex(FILE *out, int n, const char *fields[], const csv_options_t * csv_o
558 csv_fatal_error(CSV_ERR_IO_WRITE, csv_options); 565 csv_fatal_error(CSV_ERR_IO_WRITE, csv_options);
559 } 566 }
560} 567}
561