diff --git a/.gitignore b/.gitignore index b811280..b2d71fd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ bin/* tmp/* +deps/ +*.o diff --git a/Makefile b/Makefile index 630d2ae..0abd9cb 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,12 @@ SRC = $(wildcard src/*.c) DEPS = $(wildcard deps/**/*.c) -OBJS = $(patsubst %.c,%.o,$(DEPS)) +LIBS = $(wildcard lib/**/*.c) +OBJS = $(patsubst %.c,%.o,$(DEPS) $(LIBS)) BINS = $(patsubst src/%,bin/%,$(patsubst %.c,%,$(SRC))) # CFLAGS = -std=c99 -Ideps -Wall -Wno-unused-function -U__STRICT_ANSI__ -CFLAGS = -ggdb -std=c99 -Ideps -Wall -Wno-unused-function -pedantic +CFLAGS = -ggdb -std=c99 -Ideps -Ilib -Wall -Wno-unused-function -pedantic all: $(BINS) diff --git a/lib/libcsv/libcsv.c b/lib/libcsv/libcsv.c new file mode 100644 index 0000000..7f6d245 --- /dev/null +++ b/lib/libcsv/libcsv.c @@ -0,0 +1,233 @@ + +#include "libcsv.h" + +/** + * How to read a CSV file ? + */ + +/** + * Utility function to trim whitespaces from left & right of a string + */ +int trim(char ** str) { + int trimmed; + int n; + int len; + + len = strlen(*str); + n = len - 1; + /* from right */ + while((n>=0) && isspace((*str)[n])) { + (*str)[n] = '\0'; + trimmed += 1; + n--; + } + + /* from left */ + n = 0; + while((n < len) && (isspace((*str)[0]))) { + (*str)[0] = '\0'; + *str = (*str)+1; + trimmed += 1; + n++; + } + return trimmed; +} + + +/** + * De-allocate csv structure + */ +int csv_destroy(CSV * csv) { + if (csv == NULL) { return 0; } + if (csv->table != NULL) { free(csv->table); } + if (csv->delim != NULL) { free(csv->delim); } + free(csv); + return 0; +} + + +/** + * Allocate memory for a CSV structure + */ +CSV * csv_create(unsigned int cols, unsigned int rows) { + CSV * csv; + + csv = malloc(sizeof(CSV)); + csv->rows = rows; + csv->cols = cols; + csv->delim = strdup(","); + + csv->table = malloc(sizeof(char *) * cols * rows); + if (csv->table == NULL) { goto error; } + + memset(csv->table, 0, sizeof(char *) * cols * rows); + + return csv; + +error: + csv_destroy(csv); + return NULL; +} + + +/** + * Get value in CSV table at COL, ROW + */ +char * csv_get(CSV * csv, unsigned int col, unsigned int row) { + unsigned int idx; + idx = col + (row * csv->cols); + return csv->table[idx]; +} + + +/** + * Set value in CSV table at COL, ROW + */ +int csv_set(CSV * csv, unsigned int col, unsigned int row, char * value) { + unsigned int idx; + idx = col + (row * csv->cols); + csv->table[idx] = value; + return 0; +} + +void csv_display(CSV * csv) { + int row, col; + char * content; + if ((csv->rows == 0) || (csv->cols==0)) { + printf("[Empty table]\n"); + return ; + } + + printf("\n[Table cols=%d rows=%d]\n", csv->cols, csv->rows); + for (row=0; rowrows; row++) { + printf("[|"); + for (col=0; colcols; col++) { + content = csv_get(csv, col, row); + printf("%s\t|", content); + } + printf("]\n"); + } + printf("\n"); +} + +/** + * Resize CSV table + */ +int csv_resize(CSV * old_csv, unsigned int new_cols, unsigned int new_rows) { + unsigned int cur_col, + cur_row, + max_cols, + max_rows; + CSV * new_csv; + char * content; + bool in_old, in_new; + + /* Build a new (fake) csv */ + new_csv = csv_create(new_cols, new_rows); + if (new_csv == NULL) { goto error; } + + new_csv->rows = new_rows; + new_csv->cols = new_cols; + + + max_cols = (new_cols > old_csv->cols)? new_cols : old_csv->cols; + max_rows = (new_rows > old_csv->rows)? new_rows : old_csv->rows; + + for (cur_col=0; cur_colcols) && (cur_row < old_csv->rows); + in_new = (cur_col < new_csv->cols) && (cur_row < new_csv->rows); + + if (in_old && in_new) { + /* re-link data */ + content = csv_get(old_csv, cur_col, cur_row); + csv_set(new_csv, cur_col, cur_row, content); + } else if (in_old) { + /* destroy data */ + content = csv_get(old_csv, cur_col, cur_row); + free(content); + } else { /* skip */ } + } + } + /* on rows */ + free(old_csv->table); + old_csv->rows = new_rows; + old_csv->cols = new_cols; + old_csv->table = new_csv->table; + new_csv->table = NULL; + csv_destroy(new_csv); + + return 0; + +error: + printf("Unable to resize CSV table: error %d - %s\n", errno, strerror(errno)); + return -1; +} + + +/** + * Open CSV file and load its content into provided CSV structure + **/ +int csv_open(CSV * csv, char * filename) { + FILE * fp; + unsigned int m_rows; + unsigned int m_cols, cols; + char line[2048]; + char * lineptr; + char * token; + + + fp = fopen(filename, "r"); + if (fp == NULL) { goto error; } + + m_rows = 0; + m_cols = 0; + while(fgets(line, sizeof(line), fp) != NULL) { + m_rows += 1; + cols = 0; + lineptr = line; + while ((token = strtok(lineptr, csv->delim)) != NULL) { + lineptr = NULL; + trim(&token); + cols += 1; + if (cols > m_cols) { m_cols = cols; } + csv_resize(csv, m_cols, m_rows); + csv_set(csv, cols-1, m_rows-1, strdup(token)); + } + } + + fclose(fp); + csv->rows = m_rows; + csv->cols = m_cols; + return 0; + +error: + fclose(fp); + printf("Unable to open %s for reading.", filename); + return -1; +} + + +/** + * Open CSV file and save CSV structure content into it + **/ +int csv_save(CSV * csv, char * filename) { + FILE * fp; + int row, col; + char * content; + + fp = fopen(filename, "w"); + for (row=0; rowrows; row++) { + for (col=0; colcols; col++) { + content = csv_get(csv, col, row); + fprintf(fp, "%s%s", content, + ((col == csv->cols-1) ? "" : csv->delim) ); + } + fprintf(fp, "\n"); + } + + fclose(fp); + return 0; +} + + diff --git a/lib/libcsv/libcsv.h b/lib/libcsv/libcsv.h new file mode 100644 index 0000000..554665a --- /dev/null +++ b/lib/libcsv/libcsv.h @@ -0,0 +1,28 @@ + +#define _GNU_SOURCE +#define bool int +#include +#include /* malloc...*/ +#include /* strtok...*/ +#include +#include + + +typedef struct { + char * delim; + unsigned int rows; + unsigned int cols; + char ** table; +} CSV; + + +/* libcsv.c */ +int trim(char **str); +int csv_destroy(CSV *csv); +CSV *csv_create(unsigned int cols, unsigned int rows); +char *csv_get(CSV *csv, unsigned int col, unsigned int row); +int csv_set(CSV *csv, unsigned int col, unsigned int row, char *value); +void csv_display(CSV *csv); +int csv_resize(CSV *old_csv, unsigned int new_cols, unsigned int new_rows); +int csv_open(CSV *csv, char *filename); +int csv_save(CSV *csv, char *filename); diff --git a/src/csv-data-manipulation.c b/src/csv-data-manipulation.c index 50c558a..7c3a3f2 100644 --- a/src/csv-data-manipulation.c +++ b/src/csv-data-manipulation.c @@ -2,250 +2,11 @@ #define TITLE "CSV data manipulation" #define URL "http://rosettacode.org/wiki/CSV_data_manipulation" -#define _GNU_SOURCE -#define bool int -#include -#include /* malloc...*/ -#include /* strtok...*/ -#include -#include - - -/** - * How to read a CSV file ? - */ - - -typedef struct { - char * delim; - unsigned int rows; - unsigned int cols; - char ** table; -} CSV; - +#include /** - * Utility function to trim whitespaces from left & right of a string + * Test */ -int trim(char ** str) { - int trimmed; - int n; - int len; - - len = strlen(*str); - n = len - 1; - /* from right */ - while((n>=0) && isspace((*str)[n])) { - (*str)[n] = '\0'; - trimmed += 1; - n--; - } - - /* from left */ - n = 0; - while((n < len) && (isspace((*str)[0]))) { - (*str)[0] = '\0'; - *str = (*str)+1; - trimmed += 1; - n++; - } - return trimmed; -} - - -/** - * De-allocate csv structure - */ -int csv_destroy(CSV * csv) { - if (csv == NULL) { return 0; } - if (csv->table != NULL) { free(csv->table); } - if (csv->delim != NULL) { free(csv->delim); } - free(csv); - return 0; -} - - -/** - * Allocate memory for a CSV structure - */ -CSV * csv_create(unsigned int cols, unsigned int rows) { - CSV * csv; - - csv = malloc(sizeof(CSV)); - csv->rows = rows; - csv->cols = cols; - csv->delim = strdup(","); - - csv->table = malloc(sizeof(char *) * cols * rows); - if (csv->table == NULL) { goto error; } - - memset(csv->table, 0, sizeof(char *) * cols * rows); - - return csv; - -error: - csv_destroy(csv); - return NULL; -} - - -/** - * Get value in CSV table at COL, ROW - */ -char * csv_get(CSV * csv, unsigned int col, unsigned int row) { - unsigned int idx; - idx = col + (row * csv->cols); - return csv->table[idx]; -} - - -/** - * Set value in CSV table at COL, ROW - */ -int csv_set(CSV * csv, unsigned int col, unsigned int row, char * value) { - unsigned int idx; - idx = col + (row * csv->cols); - csv->table[idx] = value; - return 0; -} - -void csv_display(CSV * csv) { - int row, col; - char * content; - if ((csv->rows == 0) || (csv->cols==0)) { - printf("[Empty table]\n"); - return ; - } - - printf("\n[Table cols=%d rows=%d]\n", csv->cols, csv->rows); - for (row=0; rowrows; row++) { - printf("[|"); - for (col=0; colcols; col++) { - content = csv_get(csv, col, row); - printf("%s\t|", content); - } - printf("]\n"); - } - printf("\n"); -} - -/* Resize CSV table - * - grow columns: on each row, add missing columns cells - * - grow rows: add now rows, with all columns count - * - reduce columns: remove columns from right - * - reduce lines: remove columns from the end - */ -int csv_resize(CSV * old_csv, unsigned int new_cols, unsigned int new_rows) { - unsigned int cur_col, - cur_row, - max_cols, - max_rows; - CSV * new_csv; - char * content; - bool in_old, in_new; - - /* Build a new (fake) csv */ - new_csv = csv_create(new_cols, new_rows); - if (new_csv == NULL) { goto error; } - - new_csv->rows = new_rows; - new_csv->cols = new_cols; - - - max_cols = (new_cols > old_csv->cols)? new_cols : old_csv->cols; - max_rows = (new_rows > old_csv->rows)? new_rows : old_csv->rows; - - for (cur_col=0; cur_colcols) && (cur_row < old_csv->rows); - in_new = (cur_col < new_csv->cols) && (cur_row < new_csv->rows); - - if (in_old && in_new) { - /* re-link data */ - content = csv_get(old_csv, cur_col, cur_row); - csv_set(new_csv, cur_col, cur_row, content); - } else if (in_old) { - /* destroy data */ - content = csv_get(old_csv, cur_col, cur_row); - free(content); - } else { /* skip */ } - } - } - /* on rows */ - free(old_csv->table); - old_csv->rows = new_rows; - old_csv->cols = new_cols; - old_csv->table = new_csv->table; - new_csv->table = NULL; - csv_destroy(new_csv); - - return 0; - -error: - printf("Unable to resize CSV table: error %d - %s\n", errno, strerror(errno)); - return -1; -} - -/* , char delim='\t' */ -int csv_open(CSV * csv, char * filename) { - FILE * fp; - unsigned int m_rows; - unsigned int m_cols, cols; - char line[2048]; - char * lineptr; - char * token; - - - fp = fopen(filename, "r"); - if (fp == NULL) { goto error; } - - m_rows = 0; - m_cols = 0; - while(fgets(line, sizeof(line), fp) != NULL) { - m_rows += 1; - cols = 0; - lineptr = line; - while ((token = strtok(lineptr, csv->delim)) != NULL) { - lineptr = NULL; - trim(&token); - cols += 1; - if (cols > m_cols) { m_cols = cols; } - csv_resize(csv, m_cols, m_rows); - csv_set(csv, cols-1, m_rows-1, strdup(token)); - } - } - - fclose(fp); - csv->rows = m_rows; - csv->cols = m_cols; - return 0; - -error: - fclose(fp); - printf("Unable to open %s for reading.", filename); - return -1; -} - - -int csv_save(CSV * csv, char * filename) { - FILE * fp; - int row, col; - char * content; - - fp = fopen(filename, "w"); - for (row=0; rowrows; row++) { - for (col=0; colcols; col++) { - content = csv_get(csv, col, row); - fprintf(fp, "%s%s", content, - ((col == csv->cols-1) ? "" : csv->delim) ); - } - fprintf(fp, "\n"); - } - - fclose(fp); - return 0; -} - int main(int argc, char ** argv) { CSV * csv; @@ -262,8 +23,9 @@ int main(int argc, char ** argv) { csv_set(csv, 4, 4, "400"); csv_display(csv); - csv_save(csv, "tmp/csv-data-manupulation.out.csv"); + csv_save(csv, "tmp/csv-data-manipulation.result.csv"); csv_destroy(csv); return 0; } +