505 lines
11 KiB
C
505 lines
11 KiB
C
#include "_easycsv.h"
|
|
#include "../include/easycsv.h"
|
|
|
|
// typedef struct _easycsv _easycsv;
|
|
// _easycsv* _easycsv_priv_init(const char *fp, const EASYCSV_MODE mode, const EASYCSV_ERRORMSG error);
|
|
|
|
/* CONSTRUCTORS AND DESTRUCTORS */
|
|
|
|
/* (Constructor) Initialise easycsv */
|
|
easycsv*
|
|
easycsv_init(const char *fp,
|
|
const EASYCSV_MODE mode)
|
|
{
|
|
return easycsv_init_errormsg(fp, mode, EASYCSV_ERRORSTDERR);
|
|
}
|
|
|
|
/* (Constructor) Initialise easycsv with error message option */
|
|
easycsv*
|
|
easycsv_init_errormsg(const char *fp,
|
|
const EASYCSV_MODE mode,
|
|
const EASYCSV_ERRORMSG error)
|
|
{
|
|
#ifdef EASYCSV_DEBUG
|
|
csv->start = clock();
|
|
#endif
|
|
|
|
easycsv *csv = malloc(sizeof(easycsv));
|
|
csv->mode = mode;
|
|
csv->_priv = _easycsv_priv_init(fp, mode, error);
|
|
|
|
if (csv->_priv == NULL) {
|
|
easycsv_free(csv);
|
|
return NULL;
|
|
}
|
|
|
|
return csv;
|
|
}
|
|
|
|
/* (Destructor) Free easycsv memory */
|
|
void
|
|
easycsv_free(easycsv *csv)
|
|
{
|
|
/* ARGS CHECK */
|
|
if (csv == NULL)
|
|
return;
|
|
/* END ARGS CHECK */
|
|
|
|
_easycsv_priv_free(csv->_priv);
|
|
free(csv);
|
|
}
|
|
|
|
/* GENERIC ALGORITHMS */
|
|
|
|
/* READ VALUE */
|
|
char*
|
|
easycsv_readvalue(const easycsv *csv,
|
|
const unsigned int col,
|
|
const unsigned int row)
|
|
{
|
|
/* ARGS CHECK */
|
|
if (row == 0) {
|
|
_easycsv_printerror(csv->_priv, EASYCSV_ZEROROW);
|
|
_easycsv_printerror(csv->_priv, EASYCSV_READVALUEFAIL);
|
|
return NULL;
|
|
}
|
|
|
|
if (col == 0) {
|
|
_easycsv_printerror(csv->_priv, EASYCSV_ZEROCOL);
|
|
_easycsv_printerror(csv->_priv, EASYCSV_READVALUEFAIL);
|
|
return NULL;
|
|
}
|
|
/* END ARGS CHECK */
|
|
|
|
char *str = _easycsv_getrow(csv->_priv, row);
|
|
if (str == NULL) {
|
|
_easycsv_printerror(csv->_priv, EASYCSV_ROWNOTEXIST);
|
|
_easycsv_printerror(csv->_priv, EASYCSV_READVALUEFAIL);
|
|
return NULL;
|
|
}
|
|
|
|
char *val = _easycsv_getvalueinrow(csv->_priv, str, col);
|
|
if (val == NULL) {
|
|
_easycsv_printerror(csv->_priv, EASYCSV_READVALUEFAIL);
|
|
return NULL;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
char*
|
|
easycsv_readcolumnvalue(const easycsv *csv,
|
|
const char *col,
|
|
const unsigned int row)
|
|
{
|
|
/* ARGS CHECK */
|
|
if (_easycsv_checkcsvandstring_one(csv->_priv, col) < 0)
|
|
return NULL;
|
|
|
|
if (row > csv->_priv->rows) {
|
|
_easycsv_printerror(csv->_priv, EASYCSV_OVERMAXROW);
|
|
return NULL;
|
|
}
|
|
/* END ARGS CHECK */
|
|
|
|
int i = _easycsv_getcolumn(csv->_priv, col);
|
|
if (i < 1) {
|
|
_easycsv_printerror(csv->_priv, EASYCSV_COLNUMFAIL);
|
|
return NULL;
|
|
}
|
|
|
|
return easycsv_readvalue(csv, i, row);
|
|
}
|
|
|
|
int
|
|
easycsv_printrows(const easycsv *csv)
|
|
{
|
|
return csv->_priv->rows;
|
|
}
|
|
|
|
int
|
|
easycsv_printcolumns(const easycsv *csv)
|
|
{
|
|
return csv->_priv->cols;
|
|
}
|
|
|
|
/* INSERT VALUE -- AT SPECIFIC ROW AND COLUMN */
|
|
|
|
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->_priv, val) < 0)
|
|
return -1;
|
|
|
|
if (col == 0) {
|
|
_easycsv_printerror(csv->_priv, EASYCSV_ZEROCOL);
|
|
return -1;
|
|
}
|
|
|
|
if (row == 0) {
|
|
_easycsv_printerror(csv->_priv, EASYCSV_ZEROROW);
|
|
return -1;
|
|
}
|
|
/* END ARGS CHECK */
|
|
|
|
/* row extends max */
|
|
if (row > csv->_priv->rows) {
|
|
}
|
|
|
|
char *rowstr = _easycsv_getrow(csv->_priv, 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->_priv->cols) {
|
|
|
|
/* Set pch to start of value in rowstr */
|
|
pch = _easycsv_setcharptovalue(csv->_priv, 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->_priv->cols;
|
|
csv->_priv->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->_priv->rows) {
|
|
|
|
/* Copy rows before newstr in csv to temp */
|
|
for (unsigned int i = 1; i < row; i++) {
|
|
str = _easycsv_getrow(csv->_priv, i);
|
|
if (col > csv->_priv->cols) {
|
|
str = realloc(str, strlen(str) + commas + 1);
|
|
for (size_t j = 0; j < commas; j++) {
|
|
strncat(str, ",", 1);
|
|
}
|
|
}
|
|
fputs(str, csv->_priv->temp);
|
|
|
|
free(str);
|
|
}
|
|
|
|
/* Print newstr into temp */
|
|
fputs(newstr, csv->_priv->temp);
|
|
|
|
/* Copy the rest of rows */
|
|
for (unsigned int i = row + 1; i <= csv->_priv->rows; i++) {
|
|
str = _easycsv_getrow(csv, i);
|
|
if (col > csv->_priv->cols) {
|
|
str = realloc(str, strlen(str) + commas + 1);
|
|
for (size_t j = 0; j < commas; j++) {
|
|
strncat(str, ",", 1);
|
|
}
|
|
}
|
|
fputs(str, csv->_priv->temp);
|
|
|
|
free(str);
|
|
}
|
|
}
|
|
else { /* Row exceeds limit */
|
|
|
|
/* Copy entire file */
|
|
char buf[BUFSIZ];
|
|
size_t size;
|
|
while (size = fread(buf, 1, BUFSIZ, csv->_priv->file)) {
|
|
fwrite(buf, 1, size, csv->_priv->temp);
|
|
}
|
|
|
|
/* Print out commas on rows before newstr */
|
|
for (size_t i = csv->_priv->rows; i < row; i++) {
|
|
for (size_t j = 0; j < csv->_priv->cols; j++)
|
|
fputc(',', csv->_priv->temp);
|
|
fputc('\n', csv->_priv->temp);
|
|
}
|
|
|
|
fputs(newstr, csv->_priv->temp);
|
|
}
|
|
|
|
/* Update csv */
|
|
if (_easycsv_update(csv->_priv) < 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->_priv, col, val) < 0)
|
|
return -1;
|
|
|
|
if (row == 0) {
|
|
_easycsv_printerror(csv->_priv, EASYCSV_ZEROROW);
|
|
return -1;
|
|
}
|
|
/* END ARGS CHECK */
|
|
|
|
int colnum = _easycsv_getcolumn(csv->_priv, col);
|
|
if (colnum < 0) {
|
|
_easycsv_printerror(csv->_priv, EASYCSV_COLNUMFAIL);
|
|
return -1;
|
|
}
|
|
|
|
return easycsv_insertvaluemode(csv, val, colnum, row, valmode);
|
|
}
|
|
|
|
/* PUSH VALUE */
|
|
|
|
int
|
|
easycsv_pushcolumn(easycsv *csv,
|
|
const char *col)
|
|
{
|
|
/* ARGS CHECK */
|
|
if (_easycsv_checkcsvandstring_one(csv->_priv, col) < 0)
|
|
return -1;
|
|
/* END ARGS CHECK */
|
|
|
|
if (csv->mode == EASYCSV_R) {
|
|
_easycsv_printerror(csv->_priv, EASYCSV_UNWRITABLE);
|
|
return -1;
|
|
}
|
|
|
|
if ((csv->mode == EASYCSV_W) && (access(csv->_priv->fp, F_OK) < 0)) {
|
|
|
|
/* If the file doesn't exist, just put the col
|
|
in the file */
|
|
if (fputs(col, csv->_priv->file) < 0) {
|
|
_easycsv_printerror(csv->_priv, EASYCSV_PUSHCOLFAIL);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Grab first row */
|
|
char *str = _easycsv_getrow(csv->_priv, 1);
|
|
char *pch = NULL;
|
|
size_t i;
|
|
|
|
/* Find empty column in first row */
|
|
for (i = 1; i < csv->_priv->cols; i++) {
|
|
if (strcspn(pch, ",") == 0)
|
|
break;
|
|
pch = strchr(str, ',');
|
|
pch++;
|
|
}
|
|
|
|
/* No empty columns in first row */
|
|
if (i == csv->_priv->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->_priv->temp) < 0) {
|
|
_easycsv_printerror(csv->_priv, EASYCSV_PUSHCOLFAIL);
|
|
return -1;
|
|
}
|
|
|
|
free(str);
|
|
|
|
// Copy every row following the first into temp
|
|
for (int i = 2; i <= csv->_priv->rows; i++) {
|
|
char *row = _easycsv_getrow(csv, i);
|
|
fputs(row, csv->_priv->temp);
|
|
free(row);
|
|
}*/
|
|
}
|
|
|
|
int
|
|
easycsv_pushcolumnvalue(easycsv *csv,
|
|
const char *col,
|
|
const char *val)
|
|
{
|
|
/* ARGS CHECK */
|
|
if (_easycsv_checkcsvandstring_two(csv->_priv, col, val) < 0)
|
|
return -1;
|
|
/* ARGS CHECK */
|
|
|
|
int colnum = _easycsv_getcolumn(csv->_priv, col);
|
|
if (colnum < 0) {
|
|
_easycsv_printerror(csv->_priv, EASYCSV_COLNUMFAIL);
|
|
return -1;
|
|
}
|
|
|
|
/* Find a free cell under col within csv->_priv->cols limits,
|
|
if there is none, generate a new row */
|
|
unsigned int row;
|
|
|
|
EASYCSV_ERRORMSG temp_error = csv->_priv->error;
|
|
csv->_priv->error = EASYCSV_ERROROFF;
|
|
for (row = 2; row <= csv->_priv->cols; row++)
|
|
if (easycsv_readvalue(csv, colnum, row) != NULL)
|
|
break;
|
|
csv->_priv->error = temp_error;
|
|
|
|
/* All rows are filled, generate new row */
|
|
if (row > csv->_priv->cols)
|
|
csv->_priv->rows++;
|
|
|
|
/* ROW WILL NOT BE GENERATED \\ row < csv->_priv->rows */
|
|
|
|
return easycsv_insertvalue(csv, val, colnum, row);
|
|
}
|
|
|
|
/* DELETE VALUES */
|
|
|
|
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->_priv, EASYCSV_NULLCSV);
|
|
return -1;
|
|
}
|
|
|
|
if (col == 0) {
|
|
_easycsv_printerror(csv->_priv, EASYCSV_ZEROCOL);
|
|
return -1;
|
|
}
|
|
|
|
if (col > csv->_priv->cols) {
|
|
_easycsv_printerror(csv->_priv, EASYCSV_OVERMAXCOL);
|
|
return -1;
|
|
}
|
|
/* END ARGS CHECK */
|
|
|
|
for (unsigned int i = 1; i <= csv->_priv->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->_priv, col) < 0)
|
|
return -1;
|
|
/* END ARGS CHECK */
|
|
|
|
int colnum = _easycsv_getcolumn(csv->_priv, col);
|
|
if (colnum < 0)
|
|
return -1;
|
|
|
|
return easycsv_deletecolumnint(csv, colnum);
|
|
}
|
|
|
|
int
|
|
easycsV_deleterowint(easycsv *csv,
|
|
const unsigned int row)
|
|
{
|
|
return 0;
|
|
}
|