572 lines
14 KiB
C
572 lines
14 KiB
C
#include "easycsv_p.h"
|
|
#include "easycsv_error.h"
|
|
|
|
/*** Constructors ***/
|
|
|
|
easycsv*
|
|
easycsv_init(const char *fp,
|
|
EASYCSV_MODE mode)
|
|
{
|
|
easycsv *csv = NULL;
|
|
int csv_exist = -1;
|
|
|
|
csv = malloc(sizeof(easycsv));
|
|
|
|
csv->mode = mode;
|
|
csv->file = NULL;
|
|
csv->temp = NULL;
|
|
csv->fp = NULL;
|
|
csv->tmpfp = NULL;
|
|
csv->rows = 0;
|
|
csv->cols = 0;
|
|
|
|
/* Open file according to mode */
|
|
switch (csv->mode) {
|
|
case EASYCSV_R:
|
|
csv->file = fopen(fp, "r");
|
|
break;
|
|
case EASYCSV_W:
|
|
csv->file = fopen(fp, "w");
|
|
break;
|
|
default:
|
|
easycsv_error(EASYCSV_UNKNOWNIOMODE, NULL);
|
|
easycsv_free(csv);
|
|
return NULL;
|
|
}
|
|
|
|
if (csv->file == NULL) {
|
|
easycsv_error(EASYCSV_OPENFAIL, NULL);
|
|
easycsv_free(csv);
|
|
return NULL;
|
|
}
|
|
|
|
csv_exist = access(csv->fp, F_OK);
|
|
|
|
size_t stfp = strlen(fp);
|
|
|
|
/* Allocate memory for char* */
|
|
csv->fp = malloc(stfp + 1); // + 1 for null
|
|
|
|
strcpy(csv->fp, fp);
|
|
|
|
/* Calculate rows and cols if file exists */
|
|
if (csv_exist) {
|
|
csv->rows = easycsv_rows(csv);
|
|
csv->cols = easycsv_columns(csv);
|
|
}
|
|
|
|
if (mode == EASYCSV_W) {
|
|
|
|
do {
|
|
/* Write to temporary file */
|
|
unsigned int i = 1;
|
|
char buffer[21] = "/tmp/easycsv-"; // 13 char, 3 for digits, 4 for .csv, 1 for NULL
|
|
|
|
if (i < 100)
|
|
strncat(buffer, "0", 1);
|
|
|
|
if (i < 10)
|
|
strncat(buffer, "0", 1);
|
|
|
|
sprintf(buffer + strlen(buffer), "%i", i);
|
|
strcat(buffer, ".csv");
|
|
|
|
if (access(buffer, F_OK) < 0) {
|
|
i++;
|
|
}
|
|
else {
|
|
csv->tmpfp = malloc(21);
|
|
strncpy(csv->tmpfp, buffer, 21);
|
|
break;
|
|
}
|
|
|
|
} while (1);
|
|
|
|
csv->temp = fopen(csv->tmpfp, "w");
|
|
if (csv->temp == NULL) {
|
|
easycsv_error(EASYCSV_OPENFAIL, NULL);
|
|
easycsv_free(csv);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
return csv;
|
|
}
|
|
|
|
void
|
|
easycsv_free(easycsv *csv)
|
|
{
|
|
if (csv == NULL) free(csv);
|
|
}
|
|
|
|
/*** Acces, find ***/
|
|
|
|
/*** Acces, read ***/
|
|
|
|
char*
|
|
easycsv_read_value(const easycsv *csv,
|
|
unsigned int col,
|
|
unsigned int row)
|
|
{
|
|
char str_row[BUFSIZ];
|
|
size_t st;
|
|
char *pch, *val;
|
|
if (row == 0) {
|
|
easycsv_error(EASYCSV_ZEROROW, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
if (col == 0) {
|
|
easycsv_error(EASYCSV_ZEROCOL, NULL);
|
|
return NULL;
|
|
}
|
|
|
|
/* Set file pointer to start */
|
|
easycsv_rewind(csv);
|
|
|
|
for (unsigned int i = 1; i < row; i++) {
|
|
fscanf(csv->file, "%*[^\n]\n", NULL);
|
|
}
|
|
fscanf(csv->file, "%s\n", str_row);
|
|
|
|
/* Get first occurance of comma in str, the first value is ommited but not the comma */
|
|
pch = str_row;
|
|
/* Repeat until desired col is found */
|
|
for (unsigned int i = 1; i < col; i++) {
|
|
pch = strpbrk(pch + 1, ",");
|
|
}
|
|
|
|
/* Get span from start of string to first occurence of comma */
|
|
if (col > 1) pch++;
|
|
st = strcspn(pch, ",");
|
|
|
|
val = malloc(BUFSIZ);
|
|
// If 0, no string exists!
|
|
if (st > 0) {
|
|
strncpy(val, pch, st + 1);
|
|
}
|
|
val[st] = '\0';
|
|
|
|
return val;
|
|
}
|
|
|
|
/* char* */
|
|
/* easycsv_readcolumnvalue(const easycsv *csv, */
|
|
/* const char *col, */
|
|
/* const unsigned int row) */
|
|
/* { */
|
|
/* /\* ARGS CHECK *\/ */
|
|
/* if (_easycsv_checkcsvandstring_one(csv->csv, col) < 0) */
|
|
/* return NULL; */
|
|
|
|
/* if (row > csv->csv->rows) { */
|
|
/* _easycsv_printerror(csv->csv, EASYCSV_OVERMAXROW); */
|
|
/* return NULL; */
|
|
/* } */
|
|
/* /\* END ARGS CHECK *\/ */
|
|
|
|
/* int i = _easycsv_getcolumn(csv->csv, col); */
|
|
/* if (i < 1) { */
|
|
/* _easycsv_printerror(csv->csv, EASYCSV_COLNUMFAIL); */
|
|
/* return NULL; */
|
|
/* } */
|
|
|
|
/* return easycsv_readvalue(csv, i, row); */
|
|
/* } */
|
|
|
|
/*** Acces, print ***/
|
|
|
|
int
|
|
easycsv_print_rows(const easycsv *csv)
|
|
{
|
|
return csv->rows;
|
|
}
|
|
|
|
int
|
|
easycsv_print_columns(const easycsv *csv)
|
|
{
|
|
return csv->cols;
|
|
}
|
|
|
|
/*** Modifications, sort ***/
|
|
|
|
/*** Modifications, insert ***/
|
|
|
|
/* int */
|
|
/* easycsv_insertvalue(easycsv *csv, */
|
|
/* const char *val, */
|
|
/* const unsigned int col, */
|
|
/* const unsigned int row) */
|
|
/* { */
|
|
/* return easycsv_insertvaluemode(csv, val, col, row, EASYCSV_REPLACE); */
|
|
/* } */
|
|
|
|
/* int */
|
|
/* easycsv_insertvaluemode(easycsv *csv, */
|
|
/* const char *val, */
|
|
/* const unsigned int col, */
|
|
/* const unsigned int row, */
|
|
/* const EASYCSV_VALMODE valmode) */
|
|
/* { */
|
|
/* /\* ARGS CHECK *\/ */
|
|
/* if (_easycsv_checkcsvandstring_one(csv->csv, val) < 0) */
|
|
/* return -1; */
|
|
|
|
/* if (col == 0) { */
|
|
/* _easycsv_printerror(csv->csv, EASYCSV_ZEROCOL); */
|
|
/* return -1; */
|
|
/* } */
|
|
|
|
/* if (row == 0) { */
|
|
/* _easycsv_printerror(csv->csv, EASYCSV_ZEROROW); */
|
|
/* return -1; */
|
|
/* } */
|
|
/* /\* END ARGS CHECK *\/ */
|
|
|
|
/* /\* row extends max *\/ */
|
|
/* if (row > csv->csv->rows) { */
|
|
/* } */
|
|
|
|
/* char *rowstr = _easycsv_getrow(csv->csv, row); */
|
|
/* if (rowstr == NULL) */
|
|
/* return -1; */
|
|
|
|
/* size_t rowstrst = strlen(rowstr); */
|
|
/* size_t st = 0; */
|
|
/* size_t commas = 0; */
|
|
/* char *pch = NULL; */
|
|
/* char *newstr = NULL; */
|
|
|
|
/* /\* column is within limit *\/ */
|
|
/* if (col <= csv->csv->cols) { */
|
|
|
|
/* /\* Set pch to start of value in rowstr *\/ */
|
|
/* pch = _easycsv_setcharptovalue(csv->csv, rowstr, col); */
|
|
/* if (pch == NULL) */
|
|
/* return -1; */
|
|
|
|
/* /\* Calculate size of existing value *\/ */
|
|
/* st = strcspn(pch, ","); */
|
|
|
|
/* newstr = malloc(rowstrst - st + strlen(val) + 1); */
|
|
|
|
/* /\* Copy char to newstr before value (pch) *\/ */
|
|
/* strncpy(newstr, rowstr, pch - rowstr); */
|
|
|
|
/* /\* Insert value *\/ */
|
|
/* if (st != 0) { /\* Occupied cell *\/ */
|
|
/* switch (valmode) { */
|
|
/* case EASYCSV_CONCAT: { */
|
|
/* strncat(newstr, pch, st); */
|
|
/* strcat(newstr, val); */
|
|
/* break; */
|
|
/* } */
|
|
/* case EASYCSV_RCONCAT: { */
|
|
/* strcat(newstr, val); */
|
|
/* strncat(newstr, pch, st); */
|
|
/* break; */
|
|
/* } */
|
|
/* case EASYCSV_REPLACE: */
|
|
/* default: { */
|
|
/* strcat(newstr, val); */
|
|
/* break; */
|
|
/* } */
|
|
/* } */
|
|
/* } */
|
|
/* else { /\* Empty cell *\/ */
|
|
/* strcat(newstr, val); */
|
|
/* } */
|
|
|
|
/* /\* Set pch to after value *\/ */
|
|
/* pch = strchr(rowstr, ','); */
|
|
|
|
/* /\* Calculate length of rest of string *\/ */
|
|
/* st = strlen(pch); */
|
|
|
|
/* /\* Concentate rest of string including NULL char *\/ */
|
|
/* strcat(newstr, pch); */
|
|
/* } */
|
|
/* else { */
|
|
/* commas = col - csv->csv->cols; */
|
|
/* csv->csv->cols = col; */
|
|
/* newstr = malloc(rowstrst + commas + strlen(val) + 1); */
|
|
/* strncpy(newstr, rowstr, rowstrst); */
|
|
/* for (size_t i = 0; i < commas; i++) */
|
|
/* strncat(newstr, ",", 1); */
|
|
/* strcat(newstr, val); // append \0 */
|
|
/* } */
|
|
|
|
/* /\* UPDATE CSV *\/ */
|
|
|
|
/* char *str = NULL; */
|
|
|
|
/* /\* Row within limits *\/ */
|
|
/* if (row <= csv->csv->rows) { */
|
|
|
|
/* /\* Copy rows before newstr in csv to temp *\/ */
|
|
/* for (unsigned int i = 1; i < row; i++) { */
|
|
/* str = _easycsv_getrow(csv->csv, i); */
|
|
/* if (col > csv->csv->cols) { */
|
|
/* str = realloc(str, strlen(str) + commas + 1); */
|
|
/* for (size_t j = 0; j < commas; j++) { */
|
|
/* strncat(str, ",", 1); */
|
|
/* } */
|
|
/* } */
|
|
/* fputs(str, csv->csv->temp); */
|
|
|
|
/* free(str); */
|
|
/* } */
|
|
|
|
/* /\* Print newstr into temp *\/ */
|
|
/* fputs(newstr, csv->csv->temp); */
|
|
|
|
/* /\* Copy the rest of rows *\/ */
|
|
/* for (unsigned int i = row + 1; i <= csv->csv->rows; i++) { */
|
|
/* str = _easycsv_getrow(csv, i); */
|
|
/* if (col > csv->csv->cols) { */
|
|
/* str = realloc(str, strlen(str) + commas + 1); */
|
|
/* for (size_t j = 0; j < commas; j++) { */
|
|
/* strncat(str, ",", 1); */
|
|
/* } */
|
|
/* } */
|
|
/* fputs(str, csv->csv->temp); */
|
|
|
|
/* free(str); */
|
|
/* } */
|
|
/* } */
|
|
/* else { /\* Row exceeds limit *\/ */
|
|
|
|
/* /\* Copy entire file *\/ */
|
|
/* char buf[BUFSIZ]; */
|
|
/* size_t size; */
|
|
/* while (size = fread(buf, 1, BUFSIZ, csv->csv->file)) { */
|
|
/* fwrite(buf, 1, size, csv->csv->temp); */
|
|
/* } */
|
|
|
|
/* /\* Print out commas on rows before newstr *\/ */
|
|
/* for (size_t i = csv->csv->rows; i < row; i++) { */
|
|
/* for (size_t j = 0; j < csv->csv->cols; j++) */
|
|
/* fputc(',', csv->csv->temp); */
|
|
/* fputc('\n', csv->csv->temp); */
|
|
/* } */
|
|
|
|
/* fputs(newstr, csv->csv->temp); */
|
|
/* } */
|
|
|
|
/* /\* Update csv *\/ */
|
|
/* if (_easycsv_update(csv->csv) < 0) */
|
|
/* return -1; */
|
|
|
|
/* /\* END UPDATE CSV *\/ */
|
|
|
|
/* free(rowstr); // including pch */
|
|
/* free(newstr); */
|
|
|
|
/* return 0; */
|
|
/* } */
|
|
|
|
/* int */
|
|
/* easycsv_insertcolumnvalue(easycsv *csv, */
|
|
/* const char *col, */
|
|
/* const unsigned int row, */
|
|
/* const char *val) */
|
|
/* { */
|
|
/* return easycsv_insertcolumnvaluemode(csv, col, row, val, EASYCSV_REPLACE); */
|
|
/* } */
|
|
|
|
/* int */
|
|
/* easycsv_insertcolumnvaluemode(easycsv *csv, */
|
|
/* const char *col, */
|
|
/* const unsigned int row, */
|
|
/* const char *val, */
|
|
/* const EASYCSV_VALMODE valmode) */
|
|
/* { */
|
|
/* /\* ARGS CHECK *\/ */
|
|
/* if (_easycsv_checkcsvandstring_two(csv->csv, col, val) < 0) */
|
|
/* return -1; */
|
|
|
|
/* if (row == 0) { */
|
|
/* _easycsv_printerror(csv->csv, EASYCSV_ZEROROW); */
|
|
/* return -1; */
|
|
/* } */
|
|
/* /\* END ARGS CHECK *\/ */
|
|
|
|
/* int colnum = _easycsv_getcolumn(csv->csv, col); */
|
|
/* if (colnum < 0) { */
|
|
/* _easycsv_printerror(csv->csv, EASYCSV_COLNUMFAIL); */
|
|
/* return -1; */
|
|
/* } */
|
|
|
|
/* return easycsv_insertvaluemode(csv, val, colnum, row, valmode); */
|
|
/* } */
|
|
|
|
/*** Modifications, push ***/
|
|
|
|
/* int */
|
|
/* easycsv_pushcolumn(easycsv *csv, */
|
|
/* const char *col) */
|
|
/* { */
|
|
/* /\* ARGS CHECK *\/ */
|
|
/* if (_easycsv_checkcsvandstring_one(csv->csv, col) < 0) */
|
|
/* return -1; */
|
|
/* /\* END ARGS CHECK *\/ */
|
|
|
|
/* if (csv->mode == EASYCSV_R) { */
|
|
/* _easycsv_printerror(csv->csv, EASYCSV_UNWRITABLE); */
|
|
/* return -1; */
|
|
/* } */
|
|
|
|
/* if ((csv->mode == EASYCSV_W) && (access(csv->csv->fp, F_OK) < 0)) { */
|
|
|
|
/* /\* If the file doesn't exist, just put the col */
|
|
/* in the file *\/ */
|
|
/* if (fputs(col, csv->csv->file) < 0) { */
|
|
/* _easycsv_printerror(csv->csv, EASYCSV_PUSHCOLFAIL); */
|
|
/* return -1; */
|
|
/* } */
|
|
|
|
/* return 0; */
|
|
/* } */
|
|
|
|
/* /\* Grab first row *\/ */
|
|
/* char *str = _easycsv_getrow(csv->csv, 1); */
|
|
/* char *pch = NULL; */
|
|
/* size_t i; */
|
|
|
|
/* /\* Find empty column in first row *\/ */
|
|
/* for (i = 1; i < csv->csv->cols; i++) { */
|
|
/* if (strcspn(pch, ",") == 0) */
|
|
/* break; */
|
|
/* pch = strchr(str, ','); */
|
|
/* pch++; */
|
|
/* } */
|
|
|
|
/* /\* No empty columns in first row *\/ */
|
|
/* if (i == csv->csv->cols) */
|
|
/* i++; */
|
|
|
|
/* return easycsv_insertvalue(csv, col, i, 1); */
|
|
|
|
/* /\*size_t ststr = strlen(str); */
|
|
/* size_t stcol = strlen(col); */
|
|
/* realloc(str, ststr + stcol + 2); // 1 for null and 1 for comma */
|
|
|
|
/* strncat(str + ststr, ",", 1); */
|
|
/* strncat(str + ststr + 1, col, stcol); */
|
|
|
|
/* // Put str in temp file */
|
|
/* if (fputs(str, csv->csv->temp) < 0) { */
|
|
/* _easycsv_printerror(csv->csv, EASYCSV_PUSHCOLFAIL); */
|
|
/* return -1; */
|
|
/* } */
|
|
|
|
/* free(str); */
|
|
|
|
/* // Copy every row following the first into temp */
|
|
/* for (int i = 2; i <= csv->csv->rows; i++) { */
|
|
/* char *row = _easycsv_getrow(csv, i); */
|
|
/* fputs(row, csv->csv->temp); */
|
|
/* free(row); */
|
|
/* }*\/ */
|
|
/* } */
|
|
|
|
/* int */
|
|
/* easycsv_pushcolumnvalue(easycsv *csv, */
|
|
/* const char *col, */
|
|
/* const char *val) */
|
|
/* { */
|
|
/* /\* ARGS CHECK *\/ */
|
|
/* if (_easycsv_checkcsvandstring_two(csv->csv, col, val) < 0) */
|
|
/* return -1; */
|
|
/* /\* ARGS CHECK *\/ */
|
|
|
|
/* int colnum = _easycsv_getcolumn(csv->csv, col); */
|
|
/* if (colnum < 0) { */
|
|
/* _easycsv_printerror(csv->csv, EASYCSV_COLNUMFAIL); */
|
|
/* return -1; */
|
|
/* } */
|
|
|
|
/* /\* Find a free cell under col within csv->csv->cols limits, */
|
|
/* if there is none, generate a new row *\/ */
|
|
/* unsigned int row; */
|
|
|
|
/* EASYCSV_ERRORMSG temp_error = csv->csv->error; */
|
|
/* csv->csv->error = EASYCSV_ERROROFF; */
|
|
/* for (row = 2; row <= csv->csv->cols; row++) */
|
|
/* if (easycsv_readvalue(csv, colnum, row) != NULL) */
|
|
/* break; */
|
|
/* csv->csv->error = temp_error; */
|
|
|
|
/* /\* All rows are filled, generate new row *\/ */
|
|
/* if (row > csv->csv->cols) */
|
|
/* csv->csv->rows++; */
|
|
|
|
/* /\* ROW WILL NOT BE GENERATED \\ row < csv->csv->rows *\/ */
|
|
|
|
/* return easycsv_insertvalue(csv, val, colnum, row); */
|
|
/* } */
|
|
|
|
/*** Modifications, delete ***/
|
|
|
|
/* int */
|
|
/* easycsv_deletevalue(easycsv *csv, */
|
|
/* const unsigned int col, */
|
|
/* const unsigned int row) */
|
|
/* { */
|
|
/* return easycsv_insertvalue(csv, "", col, row); */
|
|
/* } */
|
|
|
|
/* int */
|
|
/* easycsv_deletecolumnint(easycsv *csv, */
|
|
/* const unsigned int col) */
|
|
/* { */
|
|
/* /\* ARGS CHECK *\/ */
|
|
/* if (csv == NULL) { */
|
|
/* _easycsv_printerror(csv->csv, EASYCSV_NULLCSV); */
|
|
/* return -1; */
|
|
/* } */
|
|
|
|
/* if (col == 0) { */
|
|
/* _easycsv_printerror(csv->csv, EASYCSV_ZEROCOL); */
|
|
/* return -1; */
|
|
/* } */
|
|
|
|
/* if (col > csv->csv->cols) { */
|
|
/* _easycsv_printerror(csv->csv, EASYCSV_OVERMAXCOL); */
|
|
/* return -1; */
|
|
/* } */
|
|
/* /\* END ARGS CHECK *\/ */
|
|
|
|
/* for (unsigned int i = 1; i <= csv->csv->cols; i++) { */
|
|
/* if (easycsv_readvalue(csv, col, i) == NULL) */
|
|
/* break; */
|
|
/* if (easycsv_deletevalue(csv, col, i) < 0) */
|
|
/* return -1; */
|
|
/* } */
|
|
|
|
/* return 0; */
|
|
/* } */
|
|
|
|
/* int */
|
|
/* easycsv_deletecolumnstr(easycsv *csv, */
|
|
/* const char *col) */
|
|
/* { */
|
|
/* /\* ARGS CHECK *\/ */
|
|
/* if (_easycsv_checkcsvandstring_one(csv->csv, col) < 0) */
|
|
/* return -1; */
|
|
/* /\* END ARGS CHECK *\/ */
|
|
|
|
/* int colnum = _easycsv_getcolumn(csv->csv, col); */
|
|
/* if (colnum < 0) */
|
|
/* return -1; */
|
|
|
|
/* return easycsv_deletecolumnint(csv, colnum); */
|
|
/* } */
|
|
|
|
/* int */
|
|
/* easycsV_deleterowint(easycsv *csv, */
|
|
/* const unsigned int row) */
|
|
/* { */
|
|
/* return 0; */
|
|
/* } */
|