#include #include #include #include #include #include #include #define INLINE static inline /* === CSV-STRING Interface === */ typedef struct { char *str; int cap, pos; } csv_string_t; INLINE bool csv_string_init(csv_string_t *csv_string) { assert(csv_string != NULL); static const int INITIAL_CAP = 16; if ( (csv_string->str = malloc(INITIAL_CAP)) == NULL ) { return false; } csv_string->cap = INITIAL_CAP; csv_string->pos = 0; return true; } INLINE void csv_string_reset(csv_string_t *csv_string) { assert(csv_string != NULL); csv_string->pos = 0; } INLINE bool csv_string_empty(csv_string_t *csv_string) { assert(csv_string != NULL); return ( csv_string->pos == 0 ) ? true : false; } INLINE bool csv_string_append(csv_string_t *csv_string, char ch) { assert(csv_string != NULL); if ( csv_string->pos == csv_string->cap ) { /* grow if needed */ int cap = (csv_string->cap * 3) / 2; /* *= 1.5 */ char *str = realloc(csv_string->str, cap); if ( str == NULL ) { return false; } csv_string->str = str; csv_string->cap = cap; } csv_string->str[csv_string->pos++] = ch; /* append char */ return true; } INLINE void csv_string_free(csv_string_t *csv_string) { assert(csv_string != NULL); free(csv_string->str); } /* === CSV-FIELD Interface === */ typedef struct { int *fields; int cap, pos; } csv_field_t; INLINE bool csv_field_init(csv_field_t *csv_field) { assert(csv_field != NULL); static const size_t INITIAL_CAP = 16; if ( (csv_field->fields = calloc(INITIAL_CAP, sizeof(csv_field->fields[0]))) == NULL ) { return false; } csv_field->cap = INITIAL_CAP; csv_field->pos = 0; return true; } INLINE void csv_field_reset(csv_field_t *csv_field) { assert(csv_field != NULL); csv_field->pos = 0; } INLINE bool csv_field_append(csv_field_t *csv_field, int idx) { assert(csv_field != NULL); if ( csv_field->pos == csv_field->cap ) { /* grow if needed */ int cap = (csv_field->cap * 3) / 2; /* *= 1.5 */ int *fields = reallocarray(csv_field->fields, cap, sizeof(csv_field->fields[0])); if ( fields == NULL ) { return false; } csv_field->fields = fields; csv_field->cap = cap; } csv_field->fields[csv_field->pos++] = idx; /* append idx */ return true; } INLINE void csv_field_free(csv_field_t *csv_field) { assert(csv_field != NULL); free(csv_field->fields); } /* === CSV Interface === */ typedef struct { char quote_symbol; char sep_symbol; } csv_options_t; static csv_options_t default_csv_options = { .quote_symbol = '"', .sep_symbol = ',' }; typedef struct { FILE *in; csv_options_t *csv_options; csv_string_t csv_string; csv_field_t csv_field; jmp_buf env; } csv_t; int csv_init(csv_t *csv, FILE *in); int csv_init_opt(csv_t *csv, FILE *in, csv_options_t *csv_options); void csv_free(csv_t *csv); int csv_nfields(csv_t *csv); const char * csv_field(csv_t *csv, int idx); int csv_read(csv_t *csv); void csv_write(csv_t *csv, int n, const char *fields[], FILE *out); int csv_try(csv_t *csv, FILE *in); void csv_throw(csv_t *csv, int x); #define CSV_NO_MEMORY_EXCEPTION (1) #define CSV_OUT_OF_BOUND_EXCEPTION (2) #if 0 #define TRY do { jmp_buf ex_buf__; switch( setjmp(ex_buf__) ) { case 0: while(1) { #define CATCH(x) break; case x: #define FINALLY break; } default: { #define ETRY break; } } }while(0) #define THROW(x) longjmp(ex_buf__, x) #endif #define CSV_TRY(csv, in) do{ switch( csv_try(csv, in) ){ case 0: while(1) { #define CSV_CATCH(x) break; case x: #define CSV_FINALLY break; } default: { #define CSV_ETRY break; } } } while(0) #define CSV_THROW(csv, x) csv_throw(csv, x) int csv_try(csv_t *csv, FILE *in) { int err = csv_init(csv, in); if ( err ) { return err; } err = setjmp(csv->env); return err; } void csv_throw(csv_t *csv, int x) { longjmp(csv->env, x); printf("d\n"); } int csv_init(csv_t *csv, FILE *in) { assert(csv != NULL); return csv_init_opt(csv, in, &default_csv_options); } int csv_init_opt(csv_t *csv, FILE *in, csv_options_t *csv_options) { assert(csv != NULL); assert(in != NULL); csv->in = in; csv->csv_options = ( csv_options != NULL ) ? csv_options : &default_csv_options; if ( !csv_string_init(&csv->csv_string) ) { return -1; } if ( !csv_field_init(&csv->csv_field) ) { csv_string_free(&csv->csv_string); return -1; } return 0; } void csv_free(csv_t *csv) { assert(csv != NULL); csv_string_free(&csv->csv_string); csv_field_free(&csv->csv_field); } int csv_nfields(csv_t *csv) { assert(csv != NULL); if ( csv->csv_string.pos != 0 ) { return csv->csv_field.pos; } return 0; } const char * csv_field(csv_t *csv, int idx) { assert(csv != NULL); //assert(idx >= 0 && idx < csv->csv_field.pos); if ( idx < 0 || idx >= csv->csv_field.pos) { printf("idx: %d\n", idx); CSV_THROW(csv, CSV_OUT_OF_BOUND_EXCEPTION); return NULL; } else { return &csv->csv_string.str[csv->csv_field.fields[idx]]; } } int csv_read(csv_t *csv) { assert(csv != NULL); enum { STATE_START_FIELD, STATE_QUOTED_FIELD, STATE_SIMPLE_FIELD, STATE_END_FIELD, STATE_END_LINE, STATE_END_FILE, }; register const int QUOTE = csv->csv_options->quote_symbol; register const int SEP = csv->csv_options->sep_symbol; csv_string_reset(&csv->csv_string); csv_field_reset(&csv->csv_field); for ( int state = STATE_START_FIELD; ; ) { int ch; switch ( state ) { case STATE_START_FIELD: csv_field_append(&csv->csv_field, csv->csv_string.pos); ch = getc(csv->in); if ( ch == EOF ) { state = STATE_END_FILE; } else if ( ch == '\r' ) { /* Teste auf CR.. */ ch = getc(csv->in); if ( ch != '\n' ) { /* ... LF */ ungetc(ch, csv->in); } state = STATE_END_LINE; } else if ( ch == '\n' ) { state = STATE_END_LINE; } else if ( ch == SEP ) { state = STATE_END_FIELD; } else if ( ch == QUOTE ) { state = STATE_QUOTED_FIELD; } else { csv_string_append(&csv->csv_string, ch); state = STATE_SIMPLE_FIELD; } break; case STATE_QUOTED_FIELD: do { ch = getc(csv->in); if ( ch == EOF ) { state = STATE_END_FILE; } else if ( ch == QUOTE ) { ch = getc(csv->in); if ( ch == EOF ) { state = STATE_END_FILE; } else if ( ch == QUOTE ) { csv_string_append(&csv->csv_string, QUOTE); } else if ( ch == SEP ) { state = STATE_END_FIELD; } else if ( ch == '\r' ) { ch = getc(csv->in); if ( ch != '\n' ) { ungetc(ch, csv->in); } state = STATE_END_LINE; } else if ( ch == '\n' ) { state = STATE_END_LINE; } else { csv_string_append(&csv->csv_string, QUOTE); ungetc(ch, csv->in); /* zuviel gelesenes Zeichen zurückstellen */ } } else { csv_string_append(&csv->csv_string, ch); } } while ( state == STATE_QUOTED_FIELD ); break; case STATE_SIMPLE_FIELD: do { ch = getc(csv->in); if ( ch == EOF ) { state = STATE_END_FILE; } else if ( ch == SEP ) { state = STATE_END_FIELD; } else if ( ch == '\r' ) { ch = getc(csv->in); if ( ch != '\n' ) { ungetc(ch, csv->in); } state = STATE_END_LINE; } else if ( ch == '\n' ) { state = STATE_END_LINE; } else { csv_string_append(&csv->csv_string, ch); } } while ( state == STATE_SIMPLE_FIELD ); break; case STATE_END_FIELD: csv_string_append(&csv->csv_string, '\0'); state = STATE_START_FIELD; break; case STATE_END_LINE: csv_string_append(&csv->csv_string, '\0'); return csv->csv_field.pos; case STATE_END_FILE: if ( csv_string_empty(&csv->csv_string) ) { return -1; /* EOF reached */ } /* EOF wurde gelesen, allerdings sind Daten zur Vearbeitung vorhanden! */ /* => aktuelles Feld abschließen... */ csv_string_append(&csv->csv_string, '\0'); /* ... und die Anzahl der Felder zurückliefern! */ return csv->csv_field.pos; default: assert(!"this should never be happen..."); break; } } /* NOT REACHED */ } static void csv_write_field(csv_t *csv, const char *field, FILE *out) { assert(csv != NULL); const char QUOTE = csv->csv_options->quote_symbol; if ( *field != '\0' ) { putc(QUOTE, out); for ( ; *field != '\0'; ++field ) { if ( *field == QUOTE ) { putc(QUOTE, out); putc(QUOTE, out); } else { putc(*field, out); } } putc(QUOTE, out); } } void csv_write(csv_t *csv, int n, const char *fields[], FILE *out) { assert(csv != NULL); const char SEP = csv->csv_options->quote_symbol; /* process first field */ if ( 0 != n && fields[0] != NULL ) { csv_write_field(csv, fields[0], out); /* process next fields */ for ( int i = 1; i != n && fields[i] != NULL; ++i ) { putc(SEP, out); csv_write_field(csv, fields[i], out); } } fprintf(out, "\r\n"); } int main(void) { csv_t csv[1]; int n, line = 0; clock_t start = clock(); CSV_TRY(csv, stdin) { while ( (n = csv_read(csv)) != -1 ) { csv_field(csv, -1); ++line; } } CSV_CATCH( CSV_NO_MEMORY_EXCEPTION ) { fprintf(stderr, "out of memory!"); csv_free(csv); return EXIT_FAILURE; } CSV_CATCH( CSV_OUT_OF_BOUND_EXCEPTION ) { fprintf(stderr, "out of bound!!\n"); csv_free(csv); return EXIT_FAILURE; } CSV_FINALLY { printf("finally...\n"); csv_free(csv); } CSV_ETRY; // http://groups.di.unipi.it/~nids/docs/longjump_try_trow_catch.html clock_t end = clock(); printf("%d lines processed, duration: %.3lf sec\n", line, ((double)(end - start)) / CLOCKS_PER_SEC); return EXIT_SUCCESS; }