diff options
| -rw-r--r-- | .gitignore | 3 | ||||
| -rw-r--r-- | Makefile.am | 9 | ||||
| -rwxr-xr-x | autogen.sh | 28 | ||||
| -rw-r--r-- | configure.in | 20 | ||||
| -rw-r--r-- | examples/Makefile.am | 7 | ||||
| -rw-r--r-- | examples/iris.csv | 151 | ||||
| -rw-r--r-- | examples/parser.c | 95 | ||||
| -rw-r--r-- | src/Makefile.am | 4 | ||||
| -rw-r--r-- | src/csv_parser.c | 152 | ||||
| -rw-r--r-- | src/csv_parser.h | 92 | ||||
| -rw-r--r-- | tests/Makefile.am | 8 | ||||
| -rw-r--r-- | tests/main.c | 215 |
12 files changed, 784 insertions, 0 deletions
@@ -1,4 +1,5 @@ *.o +src/libcsv_parser.a config config.log aclocal.m4 @@ -7,3 +8,5 @@ configure config.status Makefile.in Makefile +.deps +.dirstamp diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..4dc0a2c --- /dev/null +++ b/Makefile.am @@ -0,0 +1,9 @@ +ACLOCAL_AMFLAGS = -I config + +SUBDIRS = src + +DIST_SUBDIRS = src + +EXTRA_DIST = \ + autogen.sh \ + LICENSE diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..9e90913 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +# Script to generate all required files from a git checkout + +command -v libtool > /dev/null 2>&1 +if [ $? -ne 0 ]; then + echo "autogen.sh: error: could not find libtool. libtool is required to run autogen.sh." 1>&2 + exit 1 +fi + +command -v autoreconf > /dev/null 2>&1 + +if [ $? -ne 0 ]; then + echo "autogen.sh: error: could not find autoreconf. autoconf and automake are required to run autogen.sh." 1>&2 + exit 1 +fi + +mkdir -p ./config +if [ $? -ne 0 ]; then + echo "autogen.sh: error: could not create directory: ./config." 1>&2 + exit 1 +fi + +autoreconf --install --force --verbose -I config +if [ $? -ne 0 ]; then + echo "autogen.sh: error: autoreconf exited with status $?" 1>&2 + exit 1 +fi diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..99ee5b7 --- /dev/null +++ b/configure.in @@ -0,0 +1,20 @@ +AC_PREREQ([2.69]) +AC_INIT([csv_parser], [0.1.0], [vilkeliskis.t@gmail.com]) +AC_CONFIG_AUX_DIR(config) +AC_CONFIG_MACRO_DIR(config) +AM_INIT_AUTOMAKE(tar-ustar dist-zip foreign subdir-objects) + +AC_PROG_RANLIB +# This lets us use PACKAGE_VERSION in Makefiles +AC_SUBST(PACKAGE_VERSION) + +# Take a copy of original flags +CHIMP_ORIG_CFLAGS="${CFLAGS:-none}" +CHIMP_ORIG_CPPFLAGS="${CPPFLAGS:-none}" +CHIMP_ORIG_CXXFLAGS="${CXXFLAGS:-none}" + +# Checks for programs. +AC_PROG_CC + +AC_CONFIG_FILES([Makefile src/Makefile tests/Makefile examples/Makefile]) +AC_OUTPUT diff --git a/examples/Makefile.am b/examples/Makefile.am new file mode 100644 index 0000000..c643694 --- /dev/null +++ b/examples/Makefile.am @@ -0,0 +1,7 @@ +AM_CPPFLAGS = -I$(top_srcdir)/src + +bin_PROGRAMS = parser + +parser_SOURCES = ../src/csv_parser.c \ + ../src/csv_parser.h \ + parser.c diff --git a/examples/iris.csv b/examples/iris.csv new file mode 100644 index 0000000..0dba57c --- /dev/null +++ b/examples/iris.csv @@ -0,0 +1,151 @@ +sepalLength,sepalWidth,petalLength,petalWidth,species +5.1,3.5,1.4,0.2,setosa +4.9,3.0,1.4,0.2,setosa +4.7,3.2,1.3,0.2,setosa +4.6,3.1,1.5,0.2,setosa +5.0,3.6,1.4,0.2,setosa +5.4,3.9,1.7,0.4,setosa +4.6,3.4,1.4,0.3,setosa +5.0,3.4,1.5,0.2,setosa +4.4,2.9,1.4,0.2,setosa +4.9,3.1,1.5,0.1,setosa +5.4,3.7,1.5,0.2,setosa +4.8,3.4,1.6,0.2,setosa +4.8,3.0,1.4,0.1,setosa +4.3,3.0,1.1,0.1,setosa +5.8,4.0,1.2,0.2,setosa +5.7,4.4,1.5,0.4,setosa +5.4,3.9,1.3,0.4,setosa +5.1,3.5,1.4,0.3,setosa +5.7,3.8,1.7,0.3,setosa +5.1,3.8,1.5,0.3,setosa +5.4,3.4,1.7,0.2,setosa +5.1,3.7,1.5,0.4,setosa +4.6,3.6,1.0,0.2,setosa +5.1,3.3,1.7,0.5,setosa +4.8,3.4,1.9,0.2,setosa +5.0,3.0,1.6,0.2,setosa +5.0,3.4,1.6,0.4,setosa +5.2,3.5,1.5,0.2,setosa +5.2,3.4,1.4,0.2,setosa +4.7,3.2,1.6,0.2,setosa +4.8,3.1,1.6,0.2,setosa +5.4,3.4,1.5,0.4,setosa +5.2,4.1,1.5,0.1,setosa +5.5,4.2,1.4,0.2,setosa +4.9,3.1,1.5,0.1,setosa +5.0,3.2,1.2,0.2,setosa +5.5,3.5,1.3,0.2,setosa +4.9,3.1,1.5,0.1,setosa +4.4,3.0,1.3,0.2,setosa +5.1,3.4,1.5,0.2,setosa +5.0,3.5,1.3,0.3,setosa +4.5,2.3,1.3,0.3,setosa +4.4,3.2,1.3,0.2,setosa +5.0,3.5,1.6,0.6,setosa +5.1,3.8,1.9,0.4,setosa +4.8,3.0,1.4,0.3,setosa +5.1,3.8,1.6,0.2,setosa +4.6,3.2,1.4,0.2,setosa +5.3,3.7,1.5,0.2,setosa +5.0,3.3,1.4,0.2,setosa +7.0,3.2,4.7,1.4,versicolor +6.4,3.2,4.5,1.5,versicolor +6.9,3.1,4.9,1.5,versicolor +5.5,2.3,4.0,1.3,versicolor +6.5,2.8,4.6,1.5,versicolor +5.7,2.8,4.5,1.3,versicolor +6.3,3.3,4.7,1.6,versicolor +4.9,2.4,3.3,1.0,versicolor +6.6,2.9,4.6,1.3,versicolor +5.2,2.7,3.9,1.4,versicolor +5.0,2.0,3.5,1.0,versicolor +5.9,3.0,4.2,1.5,versicolor +6.0,2.2,4.0,1.0,versicolor +6.1,2.9,4.7,1.4,versicolor +5.6,2.9,3.6,1.3,versicolor +6.7,3.1,4.4,1.4,versicolor +5.6,3.0,4.5,1.5,versicolor +5.8,2.7,4.1,1.0,versicolor +6.2,2.2,4.5,1.5,versicolor +5.6,2.5,3.9,1.1,versicolor +5.9,3.2,4.8,1.8,versicolor +6.1,2.8,4.0,1.3,versicolor +6.3,2.5,4.9,1.5,versicolor +6.1,2.8,4.7,1.2,versicolor +6.4,2.9,4.3,1.3,versicolor +6.6,3.0,4.4,1.4,versicolor +6.8,2.8,4.8,1.4,versicolor +6.7,3.0,5.0,1.7,versicolor +6.0,2.9,4.5,1.5,versicolor +5.7,2.6,3.5,1.0,versicolor +5.5,2.4,3.8,1.1,versicolor +5.5,2.4,3.7,1.0,versicolor +5.8,2.7,3.9,1.2,versicolor +6.0,2.7,5.1,1.6,versicolor +5.4,3.0,4.5,1.5,versicolor +6.0,3.4,4.5,1.6,versicolor +6.7,3.1,4.7,1.5,versicolor +6.3,2.3,4.4,1.3,versicolor +5.6,3.0,4.1,1.3,versicolor +5.5,2.5,4.0,1.3,versicolor +5.5,2.6,4.4,1.2,versicolor +6.1,3.0,4.6,1.4,versicolor +5.8,2.6,4.0,1.2,versicolor +5.0,2.3,3.3,1.0,versicolor +5.6,2.7,4.2,1.3,versicolor +5.7,3.0,4.2,1.2,versicolor +5.7,2.9,4.2,1.3,versicolor +6.2,2.9,4.3,1.3,versicolor +5.1,2.5,3.0,1.1,versicolor +5.7,2.8,4.1,1.3,versicolor +6.3,3.3,6.0,2.5,virginica +5.8,2.7,5.1,1.9,virginica +7.1,3.0,5.9,2.1,virginica +6.3,2.9,5.6,1.8,virginica +6.5,3.0,5.8,2.2,virginica +7.6,3.0,6.6,2.1,virginica +4.9,2.5,4.5,1.7,virginica +7.3,2.9,6.3,1.8,virginica +6.7,2.5,5.8,1.8,virginica +7.2,3.6,6.1,2.5,virginica +6.5,3.2,5.1,2.0,virginica +6.4,2.7,5.3,1.9,virginica +6.8,3.0,5.5,2.1,virginica +5.7,2.5,5.0,2.0,virginica +5.8,2.8,5.1,2.4,virginica +6.4,3.2,5.3,2.3,virginica +6.5,3.0,5.5,1.8,virginica +7.7,3.8,6.7,2.2,virginica +7.7,2.6,6.9,2.3,virginica +6.0,2.2,5.0,1.5,virginica +6.9,3.2,5.7,2.3,virginica +5.6,2.8,4.9,2.0,virginica +7.7,2.8,6.7,2.0,virginica +6.3,2.7,4.9,1.8,virginica +6.7,3.3,5.7,2.1,virginica +7.2,3.2,6.0,1.8,virginica +6.2,2.8,4.8,1.8,virginica +6.1,3.0,4.9,1.8,virginica +6.4,2.8,5.6,2.1,virginica +7.2,3.0,5.8,1.6,virginica +7.4,2.8,6.1,1.9,virginica +7.9,3.8,6.4,2.0,virginica +6.4,2.8,5.6,2.2,virginica +6.3,2.8,5.1,1.5,virginica +6.1,2.6,5.6,1.4,virginica +7.7,3.0,6.1,2.3,virginica +6.3,3.4,5.6,2.4,virginica +6.4,3.1,5.5,1.8,virginica +6.0,3.0,4.8,1.8,virginica +6.9,3.1,5.4,2.1,virginica +6.7,3.1,5.6,2.4,virginica +6.9,3.1,5.1,2.3,virginica +5.8,2.7,5.1,1.9,virginica +6.8,3.2,5.9,2.3,virginica +6.7,3.3,5.7,2.5,virginica +6.7,3.0,5.2,2.3,virginica +6.3,2.5,5.0,1.9,virginica +6.5,3.0,5.2,2.0,virginica +6.2,3.4,5.4,2.3,virginica +5.9,3.0,5.1,1.8,virginica diff --git a/examples/parser.c b/examples/parser.c new file mode 100644 index 0000000..50c30d6 --- /dev/null +++ b/examples/parser.c @@ -0,0 +1,95 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <csv_parser.h> + + +typedef struct field_ { + char data[32]; + int row; + int col; + struct field_ *next; +} field_t; + + +typedef struct csv_ { + field_t *head; + field_t *tail; +} csv_t; + + +field_t *new_field(const char *data, size_t length, int row, int col) +{ + field_t *field = malloc(sizeof(field_t)); + memset(field, 0, sizeof(field_t)); + field->row = row; + field->col = col; + strncpy(field->data, data, length); + return field; +} + + +int field_cb(csv_parser_t *parser, const char *data, size_t length, int row, int col) +{ + csv_t *csv = parser->data; + field_t *field; + + if (!csv->tail) { + field = new_field(data, length, row, col); + csv->tail = field; + csv->head = field; + } else { + if (csv->tail->row == row && csv->tail->col == col) { + strncat(csv->tail->data, data, length); + } else { + field = new_field(data, length, row, col); + csv->tail->next = field; + csv->tail = field; + } + } + + return 0; +} + + +int main(int argc, const char *argv[]) +{ + csv_t csv; + field_t *field; + char buffer[64]; + ssize_t nread; + csv_parser_t parser; + csv_parser_settings_t settings; + + csv.head = NULL; + csv.tail = NULL; + + settings.delimiter = ','; + settings.field_cb = field_cb; + + csv_parser_init(&parser); + parser.data = &csv; + + int fd = open("iris.csv", O_RDONLY); + if (fd == -1) { + perror("open"); + return -1; + } + + while ((nread = read(fd, buffer, sizeof(buffer))) > 0) { + csv_parser_execute(&parser, &settings, buffer, nread); + } + + close(fd); + + field = csv.head; + while (field) { + printf("row: %3d, col: %3d, data: %s\n", field->row, field->col, field->data); + field_t *next = field->next; + free(field); + field = next; + } + + return 0; +} 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 */ diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..2b70ec5 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,8 @@ +AM_CPPFLAGS = -I$(top_srcdir)/src + +bin_PROGRAMS = test + +test_LDFLAGS = -lcheck +test_SOURCES = ../src/csv_parser.c \ + ../src/csv_parser.h \ + main.c diff --git a/tests/main.c b/tests/main.c new file mode 100644 index 0000000..685842f --- /dev/null +++ b/tests/main.c @@ -0,0 +1,215 @@ +#include <check.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <csv_parser.h> + + +struct test_results { + int test_case_number; + int *results; +}; + + +// 5 items per row, 3 rows per test case: 15 * 6 slots +static int expected_test_results[] = { + // "" + -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, + // ", " + 0, 1, -1, -1, -1, + -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, + // "," + 0, -1, -1, -1, -1, + -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, + // "\n" + -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, + // "\r\n", + -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, + // "a,b,,c," + 1, 2, 0, 1, -1, + -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, + // "a,b,,c, " + 1, 2, 0, 1, 1, + -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, + // "a,b,\na,b," + 1, 2, 0, -1, -1, + 1, 1, -1, -1, -1, + -1, -1, -1, -1, -1, + // " bc " + 4, -1, -1, -1, -1, + -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, +}; + + +static const int results_size = sizeof(expected_test_results); +static const int result_slots = sizeof(expected_test_results)/sizeof(int); + + +static const char *test_data[] = { + "", + ", ", + ",", + "\n", + "\r\n", + "a,bb,,c,", + "a,bb,,c, ", + "a,bb,\na,b,", + " bc ", +}; + + +static int test_data_end_states[] = { + csvps_line_start, + csvps_field_value, + csvps_field_start, + csvps_line_end, + csvps_line_end, + csvps_field_start, + csvps_field_value, + csvps_field_start, + csvps_field_value, +}; + +static int num_test_cases = sizeof(test_data)/(sizeof(char*)); + + +void init_test_results(int *data, int len) +{ + int i; + for (i = 0; i < len; i++) { + data[i] = -1; + } +} + + +void print_results(int *data, int len) +{ + int i; + for (i = 0; i < len; i++) { + if (i != 0 && i % 5 == 0) { + printf("\n"); + } + printf("%4d ", data[i]); + } + + printf("\n"); +} + + +int field_cb(csv_parser_t *parser, const char *data, size_t length, int row, int col) +{ + struct test_results *results = parser->data; + int slot = results->test_case_number * 15 + row * 5 + col; + + if (results->results[slot] == -1) { + results->results[slot] = length; + } else { + results->results[slot] += length; + } + return 0; +} + + +START_TEST(test_parser_full) +{ + csv_parser_t parser; + csv_parser_settings_t settings; + struct test_results results; + int i; + + settings.delimiter = ','; + settings.field_cb = field_cb; + + results.results = malloc(results_size); + init_test_results(results.results, result_slots); + for (i = 0; i < num_test_cases; i++) { + const char *str = test_data[i]; + int len = strlen(str); + + results.test_case_number = i; + + csv_parser_init(&parser); + parser.data = &results; + + int nread = csv_parser_execute(&parser, &settings, str, len); + ck_assert_int_eq(len, nread); + ck_assert_int_eq(parser.state, test_data_end_states[i]); + } + + printf("Test results:\n"); + print_results(results.results, result_slots); + ck_assert_int_eq(0, memcmp(results.results, expected_test_results, results_size)); + free(results.results); + +} +END_TEST + + +START_TEST(test_parser_chunked) +{ + csv_parser_t parser; + csv_parser_settings_t settings; + struct test_results results; + int i, offset; + + settings.delimiter = ','; + settings.field_cb = field_cb; + + results.results = malloc(results_size); + init_test_results(results.results, result_slots); + for (i = 0; i < num_test_cases; i++) { + const char *str = test_data[i]; + int len = strlen(str); + + results.test_case_number = i; + + csv_parser_init(&parser); + parser.data = &results; + + // parse string one char at a time + for (offset = 0; offset < len; offset++) { + char c = *(str+offset); + int nread = csv_parser_execute(&parser, &settings, &c, 1); + ck_assert_int_eq(1, nread); + } + ck_assert_int_eq(parser.state, test_data_end_states[i]); + } + + printf("Test results:\n"); + print_results(results.results, result_slots); + ck_assert_int_eq(0, memcmp(results.results, expected_test_results, results_size)); + free(results.results); + +} +END_TEST + + +int main(int argc, const char *argv[]) +{ + Suite *s = suite_create("csv_parser"); + TCase *tc = tcase_create("parser"); + tcase_add_test(tc, test_parser_full); + tcase_add_test(tc, test_parser_chunked); + + suite_add_tcase(s, tc); + SRunner *sr = srunner_create(s); + + printf("Expected results:\n"); + print_results(expected_test_results, result_slots); + + srunner_run_all(sr, CK_VERBOSE); + srunner_free(sr); + + return 0; +} |
