diff options
Diffstat (limited to 'csv.c')
| -rw-r--r-- | csv.c | 108 |
1 files changed, 57 insertions, 51 deletions
| @@ -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 |
| 18 | static inline int | 19 | static inline int |
| 19 | mul_overflow(size_t a, size_t b, size_t *out) | 20 | mul_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) | |||
| 66 | const csv_options_t csv_default_options = { | 67 | const 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 | ||
| 77 | static void | 78 | static void |
| 78 | csv_fatal_error(csv_err_t csv_err, const csv_options_t * const csv_options) | 79 | csv_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 | ||
| 115 | static inline void | 116 | static inline void |
| 116 | csv_string_append(csv_string_t *csv_string, char ch, const csv_options_t * const csv_options) | 117 | csv_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 | ||
| 146 | static inline void | 147 | static inline void |
| 147 | csv_string_free(csv_string_t *csv_string, const csv_options_t * const csv_options) | 148 | csv_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 | ||
| 170 | static inline void | 171 | static inline void |
| @@ -176,30 +177,30 @@ csv_field_reset(csv_field_t *csv_field) | |||
| 176 | } | 177 | } |
| 177 | 178 | ||
| 178 | static inline void | 179 | static inline void |
| 179 | csv_field_append(csv_field_t *csv_field, int idx, const csv_options_t * const csv_options) | 180 | csv_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 | ||
| 209 | static inline void | 210 | static inline void |
| 210 | csv_field_free(csv_field_t *csv_field, const csv_options_t * const csv_options) | 211 | csv_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 | ||
| 230 | void | 231 | void |
| 231 | csv_init_opt(csv_t *csv, const csv_options_t * const csv_options) | 232 | csv_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 | ||
| 257 | int | 258 | int |
| 258 | csv_nfields(const csv_t * const csv) | 259 | csv_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 | ||
| 268 | const char * | 269 | const char * |
| 269 | csv_field(const csv_t * const csv, int idx) | 270 | csv_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 * | |||
| 458 | csv_err_str(csv_err_t csv_err) | 459 | csv_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 | ||
| 471 | static void | 478 | static void |
| 472 | csv_write_field(FILE *out, const char *field, const csv_options_t * const csv_options) | 479 | csv_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 | ||
| 501 | void | 508 | void |
| 502 | csv_write(FILE *out, const csv_t * const csv) | 509 | csv_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 | ||
| 531 | void | 538 | void |
| 532 | csv_write_ex(FILE *out, int n, const char *fields[], const csv_options_t * csv_options) | 539 | csv_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 | |||
