diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile.am | 4 | ||||
| -rw-r--r-- | src/csv_parser.c | 152 | ||||
| -rw-r--r-- | src/csv_parser.h | 92 |
3 files changed, 248 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..417383e --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,4 @@ +noinst_LIBRARIES = libcsv_parser.a + +libcsv_parser_a_SOURCES = csv_parser.c \ + csv_parser.h diff --git a/src/csv_parser.c b/src/csv_parser.c new file mode 100644 index 0000000..29f0529 --- /dev/null +++ b/src/csv_parser.c @@ -0,0 +1,152 @@ +/** + * Copyright (C) 2013 Tadas Vilkeliskis <vilkeliskis.t@gmail.com> + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include <assert.h> +#include <stdlib.h> +#include <csv_parser.h> + + +void csv_parser_init(csv_parser_t *parser) +{ + assert(parser); + parser->state = csvps_line_start; + parser->row = -1; + parser->col = -1; + parser->nread = 0; + parser->data = NULL; +} + + +size_t csv_parser_execute(csv_parser_t *parser, + const csv_parser_settings_t *settings, + const char *data, + size_t data_len) +{ + assert(parser); + assert(settings); + assert(data); + + const char *cursor = data; + const char *field_value = NULL; + const char *data_end = data + data_len; + int r; + parser->nread = 0; + + if (data_len < 1) { + return 0; + } + + while (cursor < data_end) { + char ch = *cursor; + switch (parser->state) { + case csvps_line_start: + field_value = NULL; + parser->row += 1; + parser->col = -1; + if (ch == '\r' || ch == '\n') { + parser->state = csvps_line_end_begin; + } else { + parser->state = csvps_field_start; + } + break; + case csvps_field_start: + parser->col += 1; + parser->state = csvps_field_value; + field_value = cursor; + break; + case csvps_field_value: + if (ch == settings->delimiter) { + parser->state = csvps_field_end; + } else if (ch == '\r' || ch == '\n') { + parser->state = csvps_line_end_begin; + } else { + // If we previously been in csvps_field_value state + // and field_value is not set, set it right away. + // This can happen when we execute parser multiple + // times in the value state. + if (field_value == NULL) { + field_value = cursor; + } + + cursor++; + if (cursor == data_end) { + if (settings->field_cb && field_value) { + r = settings->field_cb(parser, + field_value, + cursor - field_value, + parser->row, + parser->col); + if (r) { + return parser->nread; + } + } + } + parser->nread++; + } + break; + case csvps_field_end: + // callback + if (settings->field_cb && field_value) { + r = settings->field_cb(parser, + field_value, + cursor - field_value, + parser->row, + parser->col); + if (r) { + return parser->nread; + } + } + + parser->state = csvps_field_start; + cursor++; + parser->nread++; + break; + case csvps_line_end_begin: + // callback + if (settings->field_cb && field_value) { + r = settings->field_cb(parser, + field_value, + cursor - field_value, + parser->row, + parser->col); + if (r) { + return parser->nread; + } + } + parser->state = csvps_line_end; + break; + case csvps_line_end: + if (ch == '\r' || ch == '\n') { + cursor++; + parser->nread++; + } else { + parser->state = csvps_line_start; + } + break; + default: + assert(0 && "invalid parser state"); + break; + } + } + + return parser->nread; +} diff --git a/src/csv_parser.h b/src/csv_parser.h new file mode 100644 index 0000000..2e263a2 --- /dev/null +++ b/src/csv_parser.h @@ -0,0 +1,92 @@ +/** + * Copyright (C) 2013 Tadas Vilkeliskis <vilkeliskis.t@gmail.com> + * + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to do + * so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#ifndef CSV_PARSER_INCLUDE_GUARD_E95A7C4D_BE4A_49D1_8607_900F36DD25B8 +#define CSV_PARSER_INCLUDE_GUARD_E95A7C4D_BE4A_49D1_8607_900F36DD25B8 + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> +#include <sys/types.h> + + +typedef enum csv_parser_state_t { + csvps_line_start = 0, + csvps_field_start, + csvps_field_value, + csvps_field_end, + csvps_line_end_begin, + csvps_line_end +} csv_parser_state_t; + + +typedef struct csv_parser_t { + csv_parser_state_t state; + /* csv row */ + int row; + /* csv column */ + int col; + uint32_t nread; + /* user data */ + void *data; +} csv_parser_t; + + +/** + * Field callback interface. + * + * Field callback can be called multiple times for the same row and column. + * It's up to the user to construct full field value. + * + * Return 0 value on success, anything else on error. + */ +typedef int (*csv_parser_field_calback_t)(csv_parser_t *parser, + const char *data, + size_t length, + int row, + int col); + + +typedef struct csv_parser_settings_t { + char delimiter; + csv_parser_field_calback_t field_cb; +} csv_parser_settings_t; + + +void csv_parser_init(csv_parser_t *parser); + +/** + * Executes CSV parser on the given data. Returns number + * of bytes read. + */ +size_t csv_parser_execute(csv_parser_t *parser, + const csv_parser_settings_t *settings, + const char *data, + size_t data_len); + + +#ifdef __cplusplus +} +#endif +#endif /* end of include guard */ |
