aboutsummaryrefslogtreecommitdiff
path: root/csv-test.c
diff options
context:
space:
mode:
authorThomas Schmucker <ts@its1.de>2020-06-14 16:40:13 +0200
committerThomas Schmucker <ts@its1.de>2020-06-14 16:40:13 +0200
commitd0a8e4d7c49f3d082981318f37181da2d10d7886 (patch)
tree7479c15aa3122918815811790325f4af5c922ff6 /csv-test.c
parent6c4fef24e2ccd1a583e7d4ce4e07b4097512e860 (diff)
downloadlibcsv-d0a8e4d7c49f3d082981318f37181da2d10d7886.tar.gz
libcsv-d0a8e4d7c49f3d082981318f37181da2d10d7886.tar.bz2
libcsv-d0a8e4d7c49f3d082981318f37181da2d10d7886.zip
Fehlerbehandlung hinzugefügt
Diffstat (limited to 'csv-test.c')
-rw-r--r--csv-test.c223
1 files changed, 131 insertions, 92 deletions
diff --git a/csv-test.c b/csv-test.c
index 3da0afe..3c9836d 100644
--- a/csv-test.c
+++ b/csv-test.c
@@ -3,6 +3,8 @@
3#include <string.h> 3#include <string.h>
4#include <time.h> 4#include <time.h>
5#include <stdbool.h> 5#include <stdbool.h>
6#include <setjmp.h>
7#include <assert.h>
6 8
7#define INLINE static inline 9#define INLINE static inline
8 10
@@ -13,48 +15,70 @@ typedef struct {
13 int cap, pos; 15 int cap, pos;
14} csv_string_t; 16} csv_string_t;
15 17
16INLINE void 18INLINE bool
17csv_string_init(csv_string_t *csv_string) 19csv_string_init(csv_string_t *csv_string, jmp_buf env)
18{ 20{
21 assert(csv_string != NULL);
22
19 static const int INITIAL_CAP = 16; 23 static const int INITIAL_CAP = 16;
20 24
21 if ( (csv_string->str = malloc(INITIAL_CAP)) != NULL ) { 25 if ( (csv_string->str = malloc(INITIAL_CAP)) == NULL ) {
22 csv_string->cap = INITIAL_CAP; 26 if ( env ) {
23 csv_string->pos = 0; 27 longjmp(env, 66);
28 }
29 return false;
24 } 30 }
31
32 csv_string->cap = INITIAL_CAP;
33 csv_string->pos = 0;
34
35 return true;
25} 36}
26 37
27INLINE void 38INLINE void
28csv_string_reset(csv_string_t *csv_string) 39csv_string_reset(csv_string_t *csv_string)
29{ 40{
41 assert(csv_string != NULL);
42
30 csv_string->pos = 0; 43 csv_string->pos = 0;
31} 44}
32 45
33INLINE bool 46INLINE bool
34csv_string_empty(csv_string_t *csv_string) 47csv_string_empty(csv_string_t *csv_string)
35{ 48{
49 assert(csv_string != NULL);
50
36 return ( csv_string->pos == 0 ) ? true : false; 51 return ( csv_string->pos == 0 ) ? true : false;
37} 52}
38 53
39INLINE void 54INLINE bool
40csv_string_append(csv_string_t *csv_string, int ch) 55csv_string_append(csv_string_t *csv_string, char ch, jmp_buf env)
41{ 56{
57 assert(csv_string != NULL);
58
42 if ( csv_string->pos == csv_string->cap ) { /* grow if needed */ 59 if ( csv_string->pos == csv_string->cap ) { /* grow if needed */
43 int cap = (csv_string->cap * 3) / 2; /* *= 1.5 */ 60 int cap = (csv_string->cap * 3) / 2; /* *= 1.5 */
44 char *str = realloc(csv_string->str, cap); 61 char *str = realloc(csv_string->str, cap);
45 if ( str == NULL ) { 62 if ( str == NULL ) {
46 return; 63 if ( env ) {
64 longjmp(env, 68);
65 }
66 return false;
47 } 67 }
48 csv_string->str = str; 68 csv_string->str = str;
49 csv_string->cap = cap; 69 csv_string->cap = cap;
50 } 70 }
51 71
52 csv_string->str[csv_string->pos++] = ch; /* append char */ 72 csv_string->str[csv_string->pos++] = ch; /* append char */
73
74 return true;
53} 75}
54 76
55INLINE void 77INLINE void
56csv_string_free(csv_string_t *csv_string) 78csv_string_free(csv_string_t *csv_string)
57{ 79{
80 assert(csv_string != NULL);
81
58 free(csv_string->str); 82 free(csv_string->str);
59} 83}
60 84
@@ -65,42 +89,62 @@ typedef struct {
65 int cap, pos; 89 int cap, pos;
66} csv_field_t; 90} csv_field_t;
67 91
68INLINE void 92INLINE bool
69csv_field_init(csv_field_t *csv_field) 93csv_field_init(csv_field_t *csv_field, jmp_buf env)
70{ 94{
95 assert(csv_field != NULL);
96
71 static const size_t INITIAL_CAP = 16; 97 static const size_t INITIAL_CAP = 16;
72 98
73 if ( (csv_field->fields = calloc(INITIAL_CAP, sizeof(csv_field->fields[0]))) != NULL ) { 99 if ( (csv_field->fields = calloc(INITIAL_CAP, sizeof(csv_field->fields[0]))) == NULL ) {
74 csv_field->cap = INITIAL_CAP; 100 if ( env ) {
75 csv_field->pos = 0; 101 longjmp(env, 88);
102 }
103 return false;
76 } 104 }
105
106 csv_field->cap = INITIAL_CAP;
107 csv_field->pos = 0;
108
109 return true;
77} 110}
78 111
79INLINE void 112INLINE void
80csv_field_reset(csv_field_t *csv_field) 113csv_field_reset(csv_field_t *csv_field)
81{ 114{
115 assert(csv_field != NULL);
116
82 csv_field->pos = 0; 117 csv_field->pos = 0;
83} 118}
84 119
85INLINE void 120INLINE bool
86csv_field_append(csv_field_t *csv_field, int idx) 121csv_field_append(csv_field_t *csv_field, int idx, jmp_buf env)
87{ 122{
123 assert(csv_field != NULL);
124
88 if ( csv_field->pos == csv_field->cap ) { /* grow if needed */ 125 if ( csv_field->pos == csv_field->cap ) { /* grow if needed */
89 int cap = (csv_field->cap * 3) / 2; /* *= 1.5 */ 126 int cap = (csv_field->cap * 3) / 2; /* *= 1.5 */
90 int *fields = reallocarray(csv_field->fields, cap, sizeof(csv_field->fields[0])); 127 int *fields = reallocarray(csv_field->fields, cap, sizeof(csv_field->fields[0]));
91 if ( fields == NULL ) { 128 if ( fields == NULL ) {
92 return; 129 if ( env ) {
130 longjmp(env, 66);
131 }
132 return false;
93 } 133 }
94 csv_field->fields = fields; 134 csv_field->fields = fields;
95 csv_field->cap = cap; 135 csv_field->cap = cap;
96 } 136 }
97 137
98 csv_field->fields[csv_field->pos++] = idx; /* append idx */ 138 csv_field->fields[csv_field->pos++] = idx; /* append idx */
139
140 return true;
99} 141}
100 142
101INLINE void 143INLINE void
102csv_field_free(csv_field_t *csv_field) 144csv_field_free(csv_field_t *csv_field)
103{ 145{
146 assert(csv_field != NULL);
147
104 free(csv_field->fields); 148 free(csv_field->fields);
105} 149}
106 150
@@ -117,29 +161,59 @@ static csv_options_t default_csv_options = {
117}; 161};
118 162
119typedef struct { 163typedef struct {
164 FILE *in;
120 csv_options_t *csv_options; 165 csv_options_t *csv_options;
121 csv_string_t csv_string; 166 csv_string_t csv_string;
122 csv_field_t csv_field; 167 csv_field_t csv_field;
168 jmp_buf env;
123} csv_t; 169} csv_t;
124 170
125void 171int csv_init(csv_t *csv, FILE *in);
126csv_init_opt(csv_t *csv, csv_options_t *csv_options) 172int csv_init_opt(csv_t *csv, FILE *in, csv_options_t *csv_options);
173void csv_free(csv_t *csv);
174int csv_nfields(csv_t *csv);
175const char * csv_field(csv_t *csv, int idx);
176int csv_read(csv_t *csv);
177void csv_write(csv_t *csv, int n, const char *fields[], FILE *out);
178
179int
180csv_init(csv_t *csv, FILE *in)
127{ 181{
128 csv->csv_options = ( csv_options != NULL ) ? csv_options : &default_csv_options; 182 assert(csv != NULL);
129 183
130 csv_string_init(&csv->csv_string); 184 return csv_init_opt(csv, in, &default_csv_options);
131 csv_field_init(&csv->csv_field);
132} 185}
133 186
134void 187int
135csv_init(csv_t *csv) 188csv_init_opt(csv_t *csv, FILE *in, csv_options_t *csv_options)
136{ 189{
137 csv_init_opt(csv, &default_csv_options); 190 assert(csv != NULL);
191
192 csv->in = in;
193 csv->csv_options = ( csv_options != NULL ) ? csv_options : &default_csv_options;
194
195 int err = setjmp(csv->env);
196
197 if ( !err ) {
198 if ( !csv_string_init(&csv->csv_string, NULL) ) {
199 return -1;
200 }
201 if ( !csv_field_init(&csv->csv_field, NULL) ) {
202 csv_string_free(&csv->csv_string);
203 return -2;
204 }
205 }
206 else {
207 csv_free(csv);
208 }
209 return err;
138} 210}
139 211
140void 212void
141csv_free(csv_t *csv) 213csv_free(csv_t *csv)
142{ 214{
215 assert(csv != NULL);
216
143 csv_string_free(&csv->csv_string); 217 csv_string_free(&csv->csv_string);
144 csv_field_free(&csv->csv_field); 218 csv_field_free(&csv->csv_field);
145} 219}
@@ -147,6 +221,8 @@ csv_free(csv_t *csv)
147int 221int
148csv_nfields(csv_t *csv) 222csv_nfields(csv_t *csv)
149{ 223{
224 assert(csv != NULL);
225
150 if ( csv->csv_string.pos != 0 ) { 226 if ( csv->csv_string.pos != 0 ) {
151 return csv->csv_field.pos; 227 return csv->csv_field.pos;
152 } 228 }
@@ -156,12 +232,16 @@ csv_nfields(csv_t *csv)
156const char * 232const char *
157csv_field(csv_t *csv, int idx) 233csv_field(csv_t *csv, int idx)
158{ 234{
235 assert(csv != NULL);
236
159 return &csv->csv_string.str[csv->csv_field.fields[idx]]; 237 return &csv->csv_string.str[csv->csv_field.fields[idx]];
160} 238}
161 239
162int 240int
163csv_read(csv_t *csv, FILE *in) 241csv_read(csv_t *csv)
164{ 242{
243 assert(csv != NULL);
244
165 enum { 245 enum {
166 STATE_START_FIELD, 246 STATE_START_FIELD,
167 STATE_QUOTED_FIELD, 247 STATE_QUOTED_FIELD,
@@ -182,16 +262,16 @@ csv_read(csv_t *csv, FILE *in)
182 262
183 switch ( state ) { 263 switch ( state ) {
184 case STATE_START_FIELD: 264 case STATE_START_FIELD:
185 csv_field_append(&csv->csv_field, csv->csv_string.pos); 265 csv_field_append(&csv->csv_field, csv->csv_string.pos, csv->env);
186 266
187 ch = getc(in); 267 ch = getc(csv->in);
188 if ( ch == EOF ) { 268 if ( ch == EOF ) {
189 state = STATE_END_FILE; 269 state = STATE_END_FILE;
190 } 270 }
191 else if ( ch == '\r' ) { /* Teste auf CR.. */ 271 else if ( ch == '\r' ) { /* Teste auf CR.. */
192 ch = getc(in); 272 ch = getc(csv->in);
193 if ( ch != '\n' ) { /* ... LF */ 273 if ( ch != '\n' ) { /* ... LF */
194 ungetc(ch, in); 274 ungetc(ch, csv->in);
195 } 275 }
196 state = STATE_END_LINE; 276 state = STATE_END_LINE;
197 } 277 }
@@ -205,32 +285,32 @@ csv_read(csv_t *csv, FILE *in)
205 state = STATE_QUOTED_FIELD; 285 state = STATE_QUOTED_FIELD;
206 } 286 }
207 else { 287 else {
208 csv_string_append(&csv->csv_string, ch); 288 csv_string_append(&csv->csv_string, ch, csv->env);
209 state = STATE_SIMPLE_FIELD; 289 state = STATE_SIMPLE_FIELD;
210 } 290 }
211 break; 291 break;
212 292
213 case STATE_QUOTED_FIELD: 293 case STATE_QUOTED_FIELD:
214 do { 294 do {
215 ch = getc(in); 295 ch = getc(csv->in);
216 if ( ch == EOF ) { 296 if ( ch == EOF ) {
217 state = STATE_END_FILE; 297 state = STATE_END_FILE;
218 } 298 }
219 else if ( ch == QUOTE ) { 299 else if ( ch == QUOTE ) {
220 ch = getc(in); 300 ch = getc(csv->in);
221 if ( ch == EOF ) { 301 if ( ch == EOF ) {
222 state = STATE_END_FILE; 302 state = STATE_END_FILE;
223 } 303 }
224 else if ( ch == QUOTE ) { 304 else if ( ch == QUOTE ) {
225 csv_string_append(&csv->csv_string, QUOTE); 305 csv_string_append(&csv->csv_string, QUOTE, csv->env);
226 } 306 }
227 else if ( ch == SEP ) { 307 else if ( ch == SEP ) {
228 state = STATE_END_FIELD; 308 state = STATE_END_FIELD;
229 } 309 }
230 else if ( ch == '\r' ) { 310 else if ( ch == '\r' ) {
231 ch = getc(in); 311 ch = getc(csv->in);
232 if ( ch != '\n' ) { 312 if ( ch != '\n' ) {
233 ungetc(ch, in); 313 ungetc(ch, csv->in);
234 } 314 }
235 state = STATE_END_LINE; 315 state = STATE_END_LINE;
236 } 316 }
@@ -238,19 +318,19 @@ csv_read(csv_t *csv, FILE *in)
238 state = STATE_END_LINE; 318 state = STATE_END_LINE;
239 } 319 }
240 else { 320 else {
241 csv_string_append(&csv->csv_string, QUOTE); 321 csv_string_append(&csv->csv_string, QUOTE, csv->env);
242 ungetc(ch, in); /* zuviel gelesenes Zeichen zurückstellen */ 322 ungetc(ch, csv->in); /* zuviel gelesenes Zeichen zurückstellen */
243 } 323 }
244 } 324 }
245 else { 325 else {
246 csv_string_append(&csv->csv_string, ch); 326 csv_string_append(&csv->csv_string, ch, csv->env);
247 } 327 }
248 } while ( state == STATE_QUOTED_FIELD ); 328 } while ( state == STATE_QUOTED_FIELD );
249 break; 329 break;
250 330
251 case STATE_SIMPLE_FIELD: 331 case STATE_SIMPLE_FIELD:
252 do { 332 do {
253 ch = getc(in); 333 ch = getc(csv->in);
254 if ( ch == EOF ) { 334 if ( ch == EOF ) {
255 state = STATE_END_FILE; 335 state = STATE_END_FILE;
256 } 336 }
@@ -258,9 +338,9 @@ csv_read(csv_t *csv, FILE *in)
258 state = STATE_END_FIELD; 338 state = STATE_END_FIELD;
259 } 339 }
260 else if ( ch == '\r' ) { 340 else if ( ch == '\r' ) {
261 ch = getc(in); 341 ch = getc(csv->in);
262 if ( ch != '\n' ) { 342 if ( ch != '\n' ) {
263 ungetc(ch, in); 343 ungetc(ch, csv->in);
264 } 344 }
265 state = STATE_END_LINE; 345 state = STATE_END_LINE;
266 } 346 }
@@ -268,18 +348,18 @@ csv_read(csv_t *csv, FILE *in)
268 state = STATE_END_LINE; 348 state = STATE_END_LINE;
269 } 349 }
270 else { 350 else {
271 csv_string_append(&csv->csv_string, ch); 351 csv_string_append(&csv->csv_string, ch, csv->env);
272 } 352 }
273 } while ( state == STATE_SIMPLE_FIELD ); 353 } while ( state == STATE_SIMPLE_FIELD );
274 break; 354 break;
275 355
276 case STATE_END_FIELD: 356 case STATE_END_FIELD:
277 csv_string_append(&csv->csv_string, '\0'); 357 csv_string_append(&csv->csv_string, '\0', csv->env);
278 state = STATE_START_FIELD; 358 state = STATE_START_FIELD;
279 break; 359 break;
280 360
281 case STATE_END_LINE: 361 case STATE_END_LINE:
282 csv_string_append(&csv->csv_string, '\0'); 362 csv_string_append(&csv->csv_string, '\0', csv->env);
283 return csv->csv_field.pos; 363 return csv->csv_field.pos;
284 364
285 case STATE_END_FILE: 365 case STATE_END_FILE:
@@ -289,7 +369,7 @@ csv_read(csv_t *csv, FILE *in)
289 369
290 /* EOF wurde gelesen, allerdings sind Daten zur Vearbeitung vorhanden! */ 370 /* EOF wurde gelesen, allerdings sind Daten zur Vearbeitung vorhanden! */
291 /* => aktuelles Feld abschließen... */ 371 /* => aktuelles Feld abschließen... */
292 csv_string_append(&csv->csv_string, '\0'); 372 csv_string_append(&csv->csv_string, '\0', csv->env);
293 373
294 /* ... und die Anzahl der Felder zurückliefern! */ 374 /* ... und die Anzahl der Felder zurückliefern! */
295 return csv->csv_field.pos; 375 return csv->csv_field.pos;
@@ -301,6 +381,8 @@ csv_read(csv_t *csv, FILE *in)
301static void 381static void
302csv_write_field(csv_t *csv, const char *field, FILE *out) 382csv_write_field(csv_t *csv, const char *field, FILE *out)
303{ 383{
384 assert(csv != NULL);
385
304 const char QUOTE = csv->csv_options->quote_symbol; 386 const char QUOTE = csv->csv_options->quote_symbol;
305 387
306 if ( *field != '\0' ) { 388 if ( *field != '\0' ) {
@@ -323,6 +405,8 @@ csv_write_field(csv_t *csv, const char *field, FILE *out)
323void 405void
324csv_write(csv_t *csv, int n, const char *fields[], FILE *out) 406csv_write(csv_t *csv, int n, const char *fields[], FILE *out)
325{ 407{
408 assert(csv != NULL);
409
326 const char SEP = csv->csv_options->quote_symbol; 410 const char SEP = csv->csv_options->quote_symbol;
327 411
328 /* process first field */ 412 /* process first field */
@@ -344,54 +428,10 @@ main(void)
344 csv_t csv[1]; 428 csv_t csv[1];
345 int n, line = 0; 429 int n, line = 0;
346 430
347#if 0
348 char data[] = "\"\"aaa\",\"b\"\"bb\",\"ccc\"\n"
349 "zzz,,yyy,xxx\n"
350 ",\n"
351 "";
352 FILE *in = fmemopen(data, strlen(data), "rb");
353
354 if ( in == NULL ) {
355 printf("no data...\n");
356 return 0;
357 }
358
359 csv_init(csv);
360 while ( (n = csv_read(csv, in)) != -1 ) {
361 printf("%4d: ", ++line);
362
363 for ( int i = 0; i < n; ++i ) {
364 printf("'%s' ", csv_field(csv, i));
365 }
366 putchar('\n');
367
368 if ( line == 100 )
369 break;
370 }
371 csv_free(csv);
372#endif
373
374#if 0
375 csv_init(csv);
376 while ( (n = csv_read(csv, stdin)) != -1 ) {
377 printf("%4d: ", ++line);
378
379 for ( int i = 0; i < n; ++i ) {
380 printf("'%s' ", csv_field(csv, i));
381 }
382 putchar('\n');
383
384 if ( line == 100 )
385 break;
386 }
387 csv_free(csv);
388#endif
389
390#if 1
391 clock_t start = clock(); 431 clock_t start = clock();
392 432
393 csv_init(csv); 433 csv_init(csv, stdin);
394 while ( (n = csv_read(csv, stdin)) != -1 ) { 434 while ( (n = csv_read(csv)) != -1 ) {
395 ++line; 435 ++line;
396 } 436 }
397 csv_free(csv); 437 csv_free(csv);
@@ -401,5 +441,4 @@ main(void)
401 printf("%d lines processed, duration: %.3lf sec\n", line, ((double)(end - start)) / CLOCKS_PER_SEC); 441 printf("%d lines processed, duration: %.3lf sec\n", line, ((double)(end - start)) / CLOCKS_PER_SEC);
402 442
403 return EXIT_SUCCESS; 443 return EXIT_SUCCESS;
404#endif
405} 444}