
commit
e1d1e9f723
10 changed files with 2050 additions and 0 deletions
@ -0,0 +1,824 @@
|
||||
#include "easycsv.h" |
||||
|
||||
/* Flags */ |
||||
typedef enum { |
||||
EASYCSV_NONE, |
||||
EASYCSV_STRING, |
||||
EASYCSV_INT, |
||||
EASYCSV_UNKNOWN |
||||
} _EASYCSV_TYPE; |
||||
|
||||
typedef enum { |
||||
|
||||
/* Generic errors */ |
||||
EASYCSV_NOERROR = 0, // no error
|
||||
EASYCSV_NULLCSV, // easycsv* is NULL
|
||||
EASYCSV_NULLPTR, // generic pointer is NULL
|
||||
EASYCSV_EMPTYSTRING, // empty string
|
||||
EASYCSV_OVERMAXROW, // int exceeds row limit
|
||||
EASYCSV_OVERMAXCOL, // int exceeds col limit
|
||||
EASYCSV_ZEROROW, |
||||
EASYCSV_ZEROCOL, |
||||
|
||||
/* File input/output errors */ |
||||
EASYCSV_UNKNOWNMODE, // unknown file IO mode
|
||||
EASYCSV_OPENFAIL, // fail to open
|
||||
EASYCSV_REOPENFAIL, // fail to reopen
|
||||
EASYCSV_EMPTYCSV, // csv is empty or does not exist
|
||||
EASYCSV_UNWRITABLE, // csv mode not at EASYCSV_W
|
||||
EASYCSV_UNREADABLE, // csv mode not at EASYCSV_R
|
||||
EASYCSV_UPDATEFAIL, // update file fail
|
||||
EASYCSV_FILEPTRFAIL, // move file pointer fail
|
||||
|
||||
/* Non-existant elements */ |
||||
EASYCSV_ROWNOTEXIST, // row does not exist
|
||||
EASYCSV_COLNOTEXIST, // column does not exist
|
||||
|
||||
/* Subroutine failure */ |
||||
EASYCSV_PUSHCOLFAIL, // push column value fail
|
||||
EASYCSV_COLNUMFAIL // column number retrieval fail
|
||||
} EASYCSV_ERROR; |
||||
|
||||
typedef struct _easycsv { |
||||
FILE *file; // original CSV file
|
||||
FILE *temp; // temporary CSV file for writing
|
||||
char *fp; |
||||
char *tmpfp; |
||||
unsigned int rows; |
||||
unsigned int cols; |
||||
EASYCSV_ERRORMSG error; |
||||
#ifdef EASYCSV_DEBUG |
||||
clock_t start; |
||||
#endif |
||||
} _easycsv; |
||||
|
||||
/* PRIVATE FUNCTIONS */ |
||||
|
||||
/* Constructor of private members */ |
||||
static _easycsv *_easycsv_priv_init(const char*, const EASYCSV_MODE, const EASYCSV_ERRORMSG); |
||||
|
||||
/* Destructor of private members */ |
||||
static void _easycsv_priv_free(_easycsv*); |
||||
|
||||
/* Verifies mode of file */ |
||||
// static int _easycsv_checkmode(struct easycsv*, const char);
|
||||
|
||||
/* Copies data from temp FILE to file FILE */ |
||||
static int _easycsv_update(easycsv*); |
||||
|
||||
/* Rewind easycsv, checks for readability */ |
||||
static int _easycsv_rewind(_easycsv*, const EASYCSV_MODE); |
||||
|
||||
/* Returns string of a specific row */ |
||||
static char *_easycsv_getrow(const easycsv*, const unsigned int); |
||||
|
||||
/* Return column number of a named column */ |
||||
static int _easycsv_getcolumn(const easycsv*, const char*); |
||||
|
||||
/* Verifies the type of the value (eg: string or int) */ |
||||
static int _easycsv_checktype(const easycsv*, const int, const int); |
||||
|
||||
/* Verifies if there is a value or not */ |
||||
static int _easycsv_checkifvalue(const easycsv*, const int, const int); |
||||
|
||||
/* */
|
||||
// static char *_easycsv_getvalueinrow(const int, const char*);
|
||||
|
||||
/* Calculate rows */ |
||||
static int _easycsv_rows(_easycsv*, const EASYCSV_MODE); |
||||
|
||||
/* Calculate columns*/ |
||||
static int _easycsv_columns(_easycsv*, const EASYCSV_MODE); |
||||
|
||||
static void _easycsv_printerror(const _easycsv*, const EASYCSV_ERROR); |
||||
|
||||
/* Print error from _easycsv struct in stderr */ |
||||
static void _easycsv_geterror(const _easycsv*, const EASYCSV_ERROR, FILE*); |
||||
|
||||
/* Check if easycsv* and const char* are NULL */ |
||||
static int _easycsv_checkcsvandstring_one(const easycsv*, const char*); |
||||
|
||||
/* Check if easycsv* and two const char* are NULL*/ |
||||
static int _easycsv_checkcsvandstring_two(const easycsv*, const char*, const char*); |
||||
|
||||
/* Verifies if the string is not NULL or empty, returns 0 on success and -1 on failure */ |
||||
// static int _easycsv_checkstring(const char*);
|
||||
|
||||
/* Verifies if easycsv is not NULL or unallocated */ |
||||
// static int _easycsv_checkeasycsv(const struct easycsv*);
|
||||
|
||||
/* Verifies if int is */ |
||||
// static int _easycsv_checkunsigned(const int);
|
||||
|
||||
/*
|
||||
* ==================== |
||||
* FUNCTION DEFINITIONS |
||||
* ==================== |
||||
*/ |
||||
|
||||
/* PUBLIC FUNCTIONS */ |
||||
|
||||
easycsv* |
||||
easycsv_init(const char *fp, const EASYCSV_MODE mode) |
||||
{ |
||||
return easycsv_init_errormsg(fp, mode, EASYCSV_ERRORSTDERR); |
||||
} |
||||
|
||||
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 *csv); |
||||
csv->mode = mode; |
||||
csv->_priv = _easycsv_priv_init(fp, mode, error); |
||||
|
||||
if (csv->_priv == NULL) { |
||||
easycsv_free(csv); |
||||
return NULL; |
||||
} |
||||
|
||||
return csv; |
||||
} |
||||
|
||||
void |
||||
easycsv_free(easycsv *csv) |
||||
{ |
||||
if (csv == NULL) return; |
||||
_easycsv_priv_free(csv->_priv); |
||||
free(csv); |
||||
} |
||||
|
||||
_easycsv* |
||||
_easycsv_priv_init(const char *fp, const EASYCSV_MODE mode, const EASYCSV_ERRORMSG error) |
||||
{ |
||||
_easycsv *_priv = malloc(sizeof *_priv); |
||||
memset(_priv, 0, sizeof(*_priv)); // set all members to NULL
|
||||
|
||||
_priv->error = error; |
||||
|
||||
/* Open file according to mode */ |
||||
switch (mode) { |
||||
case EASYCSV_R: |
||||
case EASYCSV_W: { |
||||
_priv->file = fopen(fp, "r"); |
||||
break; |
||||
} |
||||
default: { |
||||
_easycsv_printerror(_priv, EASYCSV_UNKNOWNMODE); |
||||
_easycsv_priv_free(_priv); |
||||
return NULL; |
||||
} |
||||
|
||||
if (_priv->file == NULL) |
||||
{ |
||||
_easycsv_printerror(_priv, EASYCSV_OPENFAIL); |
||||
_easycsv_priv_free(_priv); |
||||
return NULL; |
||||
} |
||||
|
||||
size_t stfp = strlen(fp); |
||||
|
||||
/* Allocate memory for char* */ |
||||
_priv->fp = malloc(stfp + 1); // + 1 for null
|
||||
|
||||
strncpy(_priv->fp, fp, stfp); |
||||
|
||||
/* Calculate rows and cols if file exists */ |
||||
if (access(_priv->fp, F_OK) == 0) { |
||||
_priv->rows = _easycsv_rows(_priv, mode); |
||||
_priv->cols = _easycsv_columns(_priv, mode); |
||||
} |
||||
else { |
||||
_priv->rows = 0; |
||||
_priv->cols = 0; |
||||
} |
||||
|
||||
if (mode == EASYCSV_W) { |
||||
|
||||
/* csv->tmpfp = malloc(16 + stfp + 1); */ |
||||
/* strncpy(csv->tmpfp, prefix, 16); */ |
||||
/* strncat(csv->tmpfp, fp, stfp); */ |
||||
|
||||
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
|
||||
char *pbuffer = &buffer[13]; // don't free, is used for digit con
|
||||
|
||||
if (i < 100) { |
||||
strncpy(pbuffer, "0", 1); |
||||
pbuffer++; |
||||
} |
||||
|
||||
if (i < 10) { |
||||
strncpy(pbuffer, "0", 1); |
||||
pbuffer++; |
||||
} |
||||
|
||||
sprintf(pbuffer, "%i", i); |
||||
strncpy(++pbuffer, ".csv", 4); |
||||
buffer[20] = '\0'; |
||||
|
||||
if (access(buffer, F_OK) < 0) { |
||||
i++; |
||||
} |
||||
else { |
||||
_priv->tmpfp = malloc(21); |
||||
strncpy(_priv->tmpfp, buffer, 21); |
||||
break; |
||||
} |
||||
|
||||
} while (1); |
||||
|
||||
_priv->temp = fopen(_priv->tmpfp, "w"); |
||||
if (_priv->temp == NULL) { |
||||
_easycsv_printerror(_priv, EASYCSV_OPENFAIL); |
||||
_easycsv_priv_free(_priv); |
||||
return NULL; |
||||
} |
||||
/*
|
||||
#ifdef EASYCSV_DEBUG |
||||
printf("[%i] easycsv_debug: temp file %s opened\n", clock() - csv->start, csv->tmpfp); |
||||
#endif*/ |
||||
/*
|
||||
|
||||
if (freopen(csv->fp, csv->mode, csv->file) == NULL) { |
||||
fprintf(stderr, "easycsv: failed to set temporary file %s to %s mode\n", csv->tmpfp, csv->mode); |
||||
return NULL; |
||||
} |
||||
|
||||
if (freopen(csv->tmpfp, csv->mode, csv->temp) == NULL) { |
||||
fprintf(stderr, "easycsv: failed to set temporary file %s to %s mode\n", csv->tmpfp, csv->mode); |
||||
return NULL; |
||||
} |
||||
*/ |
||||
|
||||
} |
||||
|
||||
return _priv; |
||||
} |
||||
|
||||
void |
||||
_easycsv_priv_free(_easycsv *_priv) |
||||
{ |
||||
if (_priv == NULL) return; |
||||
if (_priv->file != NULL) fclose(_priv->file); |
||||
if (_priv->temp != NULL) fclose(_priv->temp); |
||||
if (_priv->fp != NULL) free(_priv->fp); |
||||
if (_priv->tmpfp != NULL) free(_priv->tmpfp); |
||||
free(_priv); |
||||
} |
||||
|
||||
|
||||
int |
||||
easycsv_pushcolumn(easycsv *csv, const char *col) |
||||
{ |
||||
if (_easycsv_checkcsvandstring_one(csv, col) < 0) return -1; |
||||
|
||||
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; |
||||
} |
||||
|
||||
char *str = _easycsv_getrow(csv, 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); |
||||
/*
|
||||
#ifdef EASYCSV_DEBUG |
||||
printf("[%i] easycsv_debug: push column str: %s", clock() - csv->_priv->start, str); |
||||
#endif |
||||
*/ |
||||
/* 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); |
||||
} |
||||
|
||||
/* Update the file */ |
||||
if (_easycsv_update(csv) < 0) { |
||||
_easycsv_printerror(csv->_priv, EASYCSV_UPDATEFAIL); |
||||
return -1; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int |
||||
easycsv_pushcolumnvalue(const easycsv *csv, const char *col, const char *val) |
||||
{ |
||||
if (_easycsv_checkcsvandstring_two(csv, col, val) < 0) return -1; |
||||
|
||||
int colnum = _easycsv_getcolumn(csv, 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; |
||||
|
||||
for (row = 2; row <= csv->_priv->cols; row++) { |
||||
if (easycsv_readvalue(csv, colnum, row) != NULL) break; |
||||
} |
||||
|
||||
/* All rows are filled, generate new row */ |
||||
if (row == csv->_priv->cols) { |
||||
row++; |
||||
|
||||
/* Set file pointer to end */ |
||||
if (fseek(csv->_priv->file, 0L, SEEK_END) < 0) { |
||||
_easycsv_printerror(csv->_priv, EASYCSV_FILEPTRFAIL); |
||||
return -1; |
||||
} |
||||
|
||||
char *rowstr = malloc(csv->_priv->cols + strlen(val) + 2); // 1 extra for '\n'
|
||||
|
||||
strncpy(rowstr, "\n", 1); |
||||
|
||||
for (unsigned int i = 1; i < colnum; i++) { |
||||
strncpy(rowstr, ",", 1); |
||||
} |
||||
|
||||
strncpy(rowstr, val, strlen(val)); |
||||
|
||||
unsigned int colsleft = csv->_priv->cols - colnum; |
||||
|
||||
for (unsigned i = 1; i < colsleft; i++) { |
||||
strncpy(rowstr, ",", 1); |
||||
} |
||||
|
||||
strncpy(rowstr, "/0", 1); |
||||
|
||||
csv->_priv->rows++; |
||||
} |
||||
|
||||
/* Fill in rows before */ |
||||
|
||||
for (unsigned int i = 1; i < row; i++) { |
||||
char *str = _easycsv_getrow(csv, i); |
||||
if (fputs(str, csv->_priv->temp) == EOF) { |
||||
fprintf(stderr, "easycsv: failed to write \"%s\" in temporary file %s for %s\n", str, csv->_priv->tmpfp, csv->_priv->fp); |
||||
return -1; |
||||
} |
||||
|
||||
free(str); |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int |
||||
easycsv_insertcolumnvalue(const easycsv *csv, const char *col, const unsigned int row, const char *val, const EASYCSV_VALMODE valmode) |
||||
{ |
||||
if (_easycsv_checkcsvandstring_two(csv, col, val) < 0) return -1; |
||||
|
||||
int colnum = _easycsv_getcolumn(csv, col); |
||||
if (colnum < 0) { |
||||
_easycsv_printerror(csv->_priv, EASYCSV_COLNUMFAIL); |
||||
return -1; |
||||
} |
||||
|
||||
char *rowstr = _easycsv_getrow(csv, row); |
||||
char *pch = rowstr; |
||||
|
||||
for (unsigned int i = 1; i < colnum; i++) { |
||||
pch = strchr(pch, ','); |
||||
pch++; |
||||
} |
||||
|
||||
size_t st = strcspn(pch, ','); |
||||
|
||||
/* Empty cell */ |
||||
if (st == 0) { |
||||
|
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int |
||||
easycsv_printrows(const easycsv *csv) |
||||
{ |
||||
return csv->_priv->rows; |
||||
} |
||||
|
||||
int |
||||
easycsv_printcolumns(const easycsv *csv) |
||||
{ |
||||
return csv->_priv->cols; |
||||
} |
||||
|
||||
int |
||||
_easycsv_rows(_easycsv *_priv, const EASYCSV_MODE mode) |
||||
{ |
||||
// no need to check _priv for NULL
|
||||
|
||||
if (_easycsv_rewind(_priv, mode) < 0) return -1; |
||||
|
||||
int rows = 1; |
||||
char c; |
||||
|
||||
/* Go through each character in the file and count the number of \n
|
||||
in it */ |
||||
while ((c = fgetc(_priv->file)) != EOF) { |
||||
if (c == '\n') rows++; |
||||
} |
||||
|
||||
return rows; |
||||
} |
||||
|
||||
int |
||||
_easycsv_columns(_easycsv *_priv, const EASYCSV_MODE mode) |
||||
{ |
||||
// no need to check _priv for NULL
|
||||
|
||||
/* Prepare it for reading */ |
||||
if (_easycsv_rewind(_priv, mode) < 0) return -1; |
||||
|
||||
/*
|
||||
1. check if empty file |
||||
2. check if only one column -> 0 commas |
||||
3. if >1 column, n commas = n+1 columns |
||||
*/ |
||||
|
||||
unsigned int col = 1; |
||||
char c; |
||||
|
||||
while ((c = fgetc(_priv->file)) != '\n') { |
||||
if (c == ',') col++; |
||||
} |
||||
|
||||
return col; |
||||
} |
||||
|
||||
const char* |
||||
easycsv_readcolumnvalue(const easycsv *csv, const char *col, const unsigned int row) |
||||
{ |
||||
if (_easycsv_checkcsvandstring_one(csv, col) < 0) return NULL; |
||||
|
||||
if (row > csv->_priv->rows) { |
||||
_easycsv_printerror(csv->_priv, EASYCSV_OVERMAXROW); |
||||
return NULL; |
||||
} |
||||
|
||||
int i = _easycsv_getcolumn(csv, col); |
||||
if (i < 1) { |
||||
_easycsv_printerror(csv->_priv, EASYCSV_COLNUMFAIL); |
||||
return NULL; |
||||
} |
||||
|
||||
return easycsv_readvalue(csv, i, row); |
||||
} |
||||
|
||||
const char* |
||||
easycsv_readvalue(const easycsv *csv, const unsigned int col, const unsigned int row) |
||||
{ |
||||
if (row == 0) { |
||||
_easycsv_printerror(csv->_priv, EASYCSV_ZEROROW); |
||||
return NULL; |
||||
} |
||||
|
||||
if (col == 0) { |
||||
_easycsv_printerror(csv->_priv, EASYCSV_ZEROCOL); |
||||
return NULL; |
||||
} |
||||
|
||||
char *str = _easycsv_getrow(csv, row); |
||||
if (str == NULL) { |
||||
_easycsv_printerror(csv->_priv, EASYCSV_ROWNOTEXIST); |
||||
return NULL; |
||||
} |
||||
/*
|
||||
#ifdef EASYCSV_DEBUG |
||||
printf("[%i] rv_col: %i\nstr: %s\n", clock() - csv->_priv->start, col, str); |
||||
#endif |
||||
*/ |
||||
/* Return if first col */ |
||||
if (col == 1) { |
||||
size_t st = strcspn(str, ","); |
||||
if (st == 0) { |
||||
free(str); |
||||
return NULL; |
||||
}
|
||||
char *pch = malloc(st); |
||||
strncpy(pch, str, st); |
||||
free(str); |
||||
return pch; |
||||
} |
||||
|
||||
/* Get first occurance of comma in str,
|
||||
the first value is ommited but not the comma */ |
||||
char *pch = strpbrk(str, ","); |
||||
|
||||
/* Repeat until desired col is found */ |
||||
for (int i = 2; i < col; i++) {
|
||||
pch = strpbrk(pch + 1, ","); |
||||
} |
||||
|
||||
/* Get span from start of string to first occurence of comma */ |
||||
size_t st = strcspn(pch + 1, ","); |
||||
|
||||
/* If 0, no string exists! */ |
||||
if (st == 0) { |
||||
free(str); |
||||
return NULL; |
||||
} |
||||
|
||||
char *val = malloc(st + 1); |
||||
strncpy(val, pch + 1, st); |
||||
strncat(val, "\0", 1); |
||||
free(str); |
||||
|
||||
return val; |
||||
} |
||||
|
||||
/* PRIVATE FUNCTIONS */ |
||||
|
||||
/*
|
||||
static int _easycsv_checkmode(struct easycsv *csv, const char mode) |
||||
{ |
||||
if (strchr(csv->mode, mode) == NULL) |
||||
{ |
||||
return -1; |
||||
} |
||||
} |
||||
*/ |
||||
|
||||
int |
||||
_easycsv_update(easycsv *csv) |
||||
{ |
||||
if (csv == NULL) { |
||||
_easycsv_printerror(csv->_priv, EASYCSV_NULLCSV); |
||||
return -1; |
||||
} |
||||
|
||||
/* Set temp file to read binary */ |
||||
if (freopen(csv->_priv->tmpfp, "rb", csv->_priv->temp) == NULL) { |
||||
_easycsv_printerror(csv->_priv, EASYCSV_REOPENFAIL); |
||||
easycsv_free(csv); |
||||
return -1; |
||||
} |
||||
|
||||
/* Set file to write binary */ |
||||
if (freopen(csv->_priv->fp, "wb", csv->_priv->file) == NULL) { |
||||
_easycsv_printerror(csv->_priv, EASYCSV_REOPENFAIL); |
||||
easycsv_free(csv); |
||||
return -1; |
||||
} |
||||
|
||||
char buf[BUFSIZ]; |
||||
size_t size;
|
||||
while (size = fread(buf, 1, BUFSIZ, csv->_priv->temp)) { |
||||
fwrite(buf, 1, size, csv->_priv->file); |
||||
} |
||||
|
||||
/* Set temp file back to write */ |
||||
if (freopen(csv->_priv->tmpfp, "w", csv->_priv->temp) == NULL) { |
||||
_easycsv_printerror(csv->_priv, EASYCSV_REOPENFAIL); |
||||
easycsv_free(csv); |
||||
return -1; |
||||
} |
||||
|
||||
/* Set file back to read */ |
||||
if (freopen(csv->_priv->fp, "r", csv->_priv->file) == NULL) { |
||||
_easycsv_printerror(csv->_priv, EASYCSV_REOPENFAIL); |
||||
easycsv_free(csv); |
||||
return -1; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
char* |
||||
_easycsv_getrow(const easycsv *csv, const unsigned int row) |
||||
{ |
||||
if (csv == NULL) { |
||||
_easycsv_printerror(csv->_priv, EASYCSV_NULLCSV); |
||||
return NULL; |
||||
} |
||||
|
||||
if (row > csv->_priv->rows) { |
||||
_easycsv_printerror(csv->_priv, EASYCSV_OVERMAXROW); |
||||
return NULL; |
||||
} |
||||
|
||||
if (row == 0) { |
||||
_easycsv_printerror(csv->_priv, EASYCSV_ZEROROW); |
||||
return NULL; |
||||
} |
||||
|
||||
/* Allocate memory */ |
||||
char *str = malloc(BUFSIZ); |
||||
|
||||
/* Set file pointer to start */ |
||||
rewind(csv->_priv->file); |
||||
|
||||
for (int i = 1; i < row; i++) |
||||
{ |
||||
/* skip until row is reached */ |
||||
fscanf(csv->_priv->file, "%*[^\n]\n", NULL); |
||||
} |
||||
|
||||
/* Grab the row and store it in str */ |
||||
fscanf(csv->_priv->file, "%s\n", str); |
||||
|
||||
// printf("row: %s\n", str);
|
||||
|
||||
return str; |
||||
} |
||||
|
||||
int |
||||
_easycsv_rewind(_easycsv *_priv, const EASYCSV_MODE mode) |
||||
{
|
||||
/* Check if empty file */ |
||||
if (fscanf(_priv->file, "\n") == EOF) { |
||||
_easycsv_printerror(_priv, EASYCSV_EMPTYCSV); |
||||
return -1; |
||||
} |
||||
|
||||
/* Check if file is readable */ |
||||
if (mode != EASYCSV_R) { |
||||
_easycsv_printerror(_priv, EASYCSV_UNREADABLE); |
||||
return -1; |
||||
} |
||||
|
||||
/* Set file pointer to the start */ |
||||
rewind(_priv->file); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int |
||||
_easycsv_getcolumn(const easycsv *csv, const char *col) |
||||
{ |
||||
if (_easycsv_checkcsvandstring_one(csv, col) < 0) return -1; |
||||
|
||||
/* Grab str of row 1 */ |
||||
char *firstrow = _easycsv_getrow(csv, 1);
|
||||
|
||||
if (firstrow == NULL) { |
||||
_easycsv_printerror(csv->_priv, EASYCSV_ROWNOTEXIST); |
||||
return -1; |
||||
} |
||||
|
||||
unsigned int commas = 0; |
||||
|
||||
// printf("FIRST COLUMN: %s\n", firstrow);
|
||||
|
||||
/* Find first occurance of col in firstrow */ |
||||
char *str = strstr(firstrow, col); |
||||
|
||||
if (str == NULL) { |
||||
_easycsv_printerror(csv->_priv, EASYCSV_COLNOTEXIST); |
||||
return -1; |
||||
} |
||||
|
||||
/* Count numbers of commas following str */ |
||||
char *c = strpbrk(str, ","); |
||||
while (c != NULL) { |
||||
commas++; |
||||
c = strpbrk(c + 1, ","); |
||||
} |
||||
|
||||
/* no need to free c as it is already NULL at this point */ |
||||
// free((char*) str); apparently invalid pointer
|
||||
|
||||
// printf("ROW: %i\nCOL: %i\n", row, csv->_priv->cols - commas);
|
||||
/*
|
||||
#ifdef EASYCSV_DEBUG |
||||
printf("[%i] rcv_commas: %i\n", clock() - csv->_priv->start, commas); |
||||
#endif |
||||
*/ |
||||
free(firstrow); |
||||
|
||||
return csv->_priv->cols - commas; |
||||
} |
||||
|
||||
/*
|
||||
int _easycsv_checkifvalue(struct easycsv *csv, const int col, const int row) |
||||
{ |
||||
const char *rowstr = _easycsv_getrow(csv, row); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
char *_easycsv_getvalueinrow(const int col, const char *row) |
||||
{ |
||||
char *pbrk = strchr(row, ','); |
||||
for (unsigned int i = 1; i < col; i++) { |
||||
|
||||
}
|
||||
} |
||||
*/ |
||||
|
||||
void |
||||
_easycsv_printerror(const _easycsv *_priv, const EASYCSV_ERROR error) |
||||
{
|
||||
switch (_priv->error) { |
||||
case EASYCSV_ERROROFF: return; |
||||
case EASYCSV_ERRORSTDOUT: _easycsv_geterror(_priv, error, stdout); break; |
||||
case EASYCSV_ERRORSTDERR:
|
||||
default: _easycsv_geterror(_priv, error, stderr); return; |
||||
} |
||||
} |
||||
|
||||
void |
||||
_easycsv_geterror(const _easycsv *_priv, const EASYCSV_ERROR error, FILE *fs) |
||||
{ |
||||
#ifdef EASYCSV_DEBUG |
||||
fprintf(stderr, "[%i] ", clock() - _priv->start); |
||||
#endif |
||||
|
||||
fputs("easycsv: ", fs); |
||||
|
||||
switch (error) { |
||||
case EASYCSV_NOERROR: fputs("no error", fs); break; |
||||
case EASYCSV_NULLCSV: fputs("easycsv pointer is NULL", fs); break; |
||||
case EASYCSV_NULLPTR: fputs("pointer is NULL", fs); break; |
||||
case EASYCSV_EMPTYSTRING: fputs("string is empty", fs); break; |
||||
case EASYCSV_OVERMAXROW: fprintf(fs, "int exceeds row limit %i", _priv->rows); break; |
||||
case EASYCSV_OVERMAXCOL: fprintf(fs, "int exceeds column limit %i", _priv->cols); break; |
||||
case EASYCSV_ZEROROW: fputs("parameterised row number is zero", fs); break; |
||||
case EASYCSV_ZEROCOL: fputs("parameterised column number is zero", fs); break; |
||||
|
||||
case EASYCSV_UNKNOWNMODE: fputs("unknown file IO mode", fs); break; |
||||
case EASYCSV_OPENFAIL: fputs("failed to open file", fs); break; |
||||
case EASYCSV_REOPENFAIL: fputs("failed to reopen file", fs); break; |
||||
case EASYCSV_EMPTYCSV: fputs("CSV file is empty", fs); break; |
||||
case EASYCSV_UNWRITABLE: fputs("CSV file is not in a writable mode", fs); break; |
||||
case EASYCSV_UNREADABLE: fputs("CSV file is not in a readable mode", fs); break; |
||||
case EASYCSV_UPDATEFAIL: fputs("CSV file has failed to update", fs); break; |
||||
case EASYCSV_FILEPTRFAIL: fputs("failed to move FILE pointer", fs); break; |
||||
|
||||
case EASYCSV_ROWNOTEXIST: fputs("given row does not exist", fs); break; |
||||
case EASYCSV_COLNOTEXIST: fputs("given column does not exist", fs); break; |
||||
|
||||
case EASYCSV_PUSHCOLFAIL: fputs("failed to push value under column", fs); break; |
||||
case EASYCSV_COLNUMFAIL: fputs("failed to determine the column number of a value in the first row", fs); break; |
||||
default: fputs("unknown error, easycsv* is possibly NULL", fs); break; |
||||
} |
||||
|
||||
if (_priv->fp != NULL) { |
||||
fprintf(fs, " in %s", _priv->fp); |
||||
} |
||||
|
||||
fputc('\n', fs); |
||||
} |
||||
|
||||
int |
||||
_easycsv_checkcsvandstring_one(const easycsv *csv, const char *one) |
||||
{ |
||||
if (csv == NULL) { |
||||
_easycsv_printerror(csv->_priv, EASYCSV_NULLCSV); |
||||
return -1; |
||||
} |
||||
|
||||
if ((one == NULL) || (one[0] == '\0')) { |
||||
_easycsv_printerror(csv->_priv, EASYCSV_EMPTYSTRING); |
||||
return -1; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int |
||||
_easycsv_checkcsvandstringtwo(const easycsv *csv, const char *one, const char *two) |
||||
{ |
||||
if (_easycsv_checkcsvandstring_one(csv, one) < 0) return -1; |
||||
|
||||
if ((two == NULL) || (two[0] == '\0')) { |
||||
_easycsv_printerror(csv->_priv, EASYCSV_EMPTYSTRING); |
||||
return -1; |
||||
} |
||||
|
||||
return 0; |
||||
} |
@ -0,0 +1,878 @@
|
||||
#include "easycsv.h" |
||||
|
||||
/* Flags */ |
||||
typedef enum { |
||||
EASYCSV_NULL, |
||||
EASYCSV_STRING, |
||||
EASYCSV_INT |
||||
} _EASYCSV_TYPE; |
||||
|
||||
typedef enum { |
||||
|
||||
/* Generic errors */ |
||||
EASYCSV_NOERROR = 0, // no error
|
||||
EASYCSV_NULLPTR, // pointer is NULL
|
||||
EASYCSV_EMPTYSTRING, // empty string
|
||||
EASYCSV_OVERMAXROW, // int exceeds row limit
|
||||
EASYCSV_OVERMAXCOL, // int exceeds col limit
|
||||
|
||||
/* File input/output errors */ |
||||
EASYCSV_UNKNOWNMODE, // unknown file IO mode
|
||||
EASYCSV_OPENFAIL, // fail to open
|
||||
EASYCSV_REOPENFAIL, // fail to reopen
|
||||
EASYCSV_EMPTYCSV, // csv is empty or does not exist
|
||||
EASYCSV_UNWRITABLE, // csv mode not at EASYCSV_W
|
||||
EASYCSV_UNREADABLE, // csv mode not at EASYCSV_R
|
||||
EASYCSV_UPDATEFAIL, // update file fail
|
||||
EASYCSV_FILEPTRFAIL, // move file pointer fail
|
||||
|
||||
/* Non-existant elements */ |
||||
EASYCSV_ROWNOTEXIST, // row does not exist
|
||||
EASYCSV_COLNOTEXIST, // column does not exist
|
||||
|
||||
/* Subroutine failure */ |
||||
EASYCSV_PUSHCOLFAIL, // push column value fail
|
||||
EASYCSV_COLNUMFAIL // column number retrieval fail
|
||||
} EASYCSV_ERROR; |
||||
|
||||
typedef struct _easycsv { |
||||
FILE *file; // original CSV file
|
||||
FILE *temp; // temporary CSV file for writing
|
||||
char *fp; |
||||
char *tmpfp; |
||||
unsigned int rows; |
||||
unsigned int cols; |
||||
EASYCSV_ERRORMSG error; |
||||
#ifdef EASYCSV_DEBUG |
||||
clock_t start; |
||||
#endif |
||||
} _easycsv; |
||||
|
||||
/* PRIVATE FUNCTIONS */ |
||||
|
||||
/* Constructor of private members */ |
||||
static _easycsv *_easycsv_priv_init(const char*, const EASYCSV_MODE, const EASYCSV_ERRORMSG); |
||||
|
||||
/* Destructor of private members */ |
||||
static void _easycsv_priv_free(_easycsv*); |
||||
|
||||
/* Verifies mode of file */ |
||||
// static int _easycsv_checkmode(struct easycsv*, const char);
|
||||
|
||||
/* Copies data from temp FILE to file FILE */ |
||||
static int _easycsv_update(easycsv*); |
||||
|
||||
/* Rewind easycsv, checks for readability */ |
||||
static int _easycsv_rewind(_easycsv*, const EASYCSV_MODE); |
||||
|
||||
/* Returns string of a specific row */ |
||||
static char *_easycsv_getrow(const easycsv*, const unsigned int); |
||||
|
||||
/* Return column number of a named column */ |
||||
static int _easycsv_getcolumn(const easycsv*, const char*); |
||||
|
||||
/* Verifies the type of the value (eg: string or int) */ |
||||
static int _easycsv_checktype(const easycsv*, const int, const int); |
||||
|
||||
/* Verifies if there is a value or not */ |
||||
static int _easycsv_checkifvalue(const easycsv*, const int, const int); |
||||
|
||||
/* */
|
||||
// static char *_easycsv_getvalueinrow(const int, const char*);
|
||||
|
||||
/* Calculate rows */ |
||||
static int _easycsv_rows(_easycsv*, const EASYCSV_MODE); |
||||
|
||||
/* Calculate columns*/ |
||||
static int _easycsv_columns(_easycsv*, const EASYCSV_MODE); |
||||
|
||||
static void _easycsv_printerror(const _easycsv*, const EASYCSV_ERROR); |
||||
|
||||
/* Print error from _easycsv struct in stderr */ |
||||
static void _easycsv_geterror(const _easycsv*, const EASYCSV_ERROR, FILE*); |
||||
|
||||
/* Verifies if the string is not NULL or empty, returns 0 on success and -1 on failure */ |
||||
// static int _easycsv_checkstring(const char*);
|
||||
|
||||
/* Verifies if easycsv is not NULL or unallocated */ |
||||
// static int _easycsv_checkeasycsv(const struct easycsv*);
|
||||
|
||||
/* Verifies if int is */ |
||||
// static int _easycsv_checkunsigned(const int);
|
||||
|
||||
/*
|
||||
* ==================== |
||||
* FUNCTION DEFINITIONS |
||||
* ==================== |
||||
*/ |
||||
|
||||
/* PUBLIC FUNCTIONS */ |
||||
|
||||
easycsv* |
||||
easycsv_init(const char *fp, const EASYCSV_MODE mode) |
||||
{ |
||||
return easycsv_init_errormsg(fp, mode, EASYCSV_ERRORSTDERR); |
||||
} |
||||
|
||||
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 *csv); |
||||
csv->mode = mode; |
||||
csv->_priv = _easycsv_priv_init(fp, mode, error); |
||||
|
||||
if (csv->_priv == NULL) { |
||||
easycsv_free(csv); |
||||
return NULL; |
||||
} |
||||
|
||||
return csv; |
||||
} |
||||
|
||||
void |
||||
easycsv_free(easycsv *csv) |
||||
{ |
||||
if (csv == NULL) return; |
||||
_easycsv_priv_free(csv->_priv); |
||||
free(csv); |
||||
} |
||||
|
||||
_easycsv* |
||||
_easycsv_priv_init(const char *fp, const EASYCSV_MODE mode, const EASYCSV_ERRORMSG error) |
||||
{ |
||||
_easycsv *_priv = malloc(sizeof *_priv); |
||||
memset(_priv, 0, sizeof(*_priv)); // set all members to NULL
|
||||
|
||||
_priv->error = error; |
||||
|
||||
/* Open file according to mode */ |
||||
switch (mode) { |
||||
case EASYCSV_R: |
||||
case EASYCSV_W: { |
||||
_priv->file = fopen(fp, "r"); |
||||
break; |
||||
} |
||||
default: { |
||||
_easycsv_printerror(_priv, EASYCSV_UNKNOWNMODE); |
||||
_easycsv_priv_free(_priv); |
||||
return NULL; |
||||
} |
||||
|
||||
if (_priv->file == NULL) |
||||
{ |
||||
_easycsv_printerror(_priv, EASYCSV_OPENFAIL); |
||||
_easycsv_priv_free(_priv); |
||||
return NULL; |
||||
} |
||||
|
||||
size_t stfp = strlen(fp); |
||||
|
||||
/* Allocate memory for char* */ |
||||
_priv->fp = malloc(stfp + 1); // + 1 for null
|
||||
|
||||
strncpy(_priv->fp, fp, stfp); |
||||
|
||||
/* Calculate rows and cols if file exists */ |
||||
if (access(_priv->fp, F_OK) == 0) { |
||||
_priv->rows = _easycsv_rows(_priv, mode); |
||||
_priv->cols = _easycsv_columns(_priv, mode); |
||||
} |
||||
else { |
||||
_priv->rows = 0; |
||||
_priv->cols = 0; |
||||
} |
||||
|
||||
if (mode == EASYCSV_W) { |
||||
|
||||
/* csv->tmpfp = malloc(16 + stfp + 1); */ |
||||
/* strncpy(csv->tmpfp, prefix, 16); */ |
||||
/* strncat(csv->tmpfp, fp, stfp); */ |
||||
|
||||
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
|
||||
char *pbuffer = &buffer[13]; // don't free, is used for digit con
|
||||
|
||||
if (i < 100) { |
||||
strncpy(pbuffer, "0", 1); |
||||
pbuffer++; |
||||
} |
||||
|
||||
if (i < 10) { |
||||
strncpy(pbuffer, "0", 1); |
||||
pbuffer++; |
||||
} |
||||
|
||||
sprintf(pbuffer, "%i", i); |
||||
strncpy(++pbuffer, ".csv", 4); |
||||
buffer[20] = '\0'; |
||||
|
||||
if (access(buffer, F_OK) < 0) { |
||||
i++; |
||||
} |
||||
else { |
||||
_priv->tmpfp = malloc(21); |
||||
strncpy(_priv->tmpfp, buffer, 21); |
||||
break; |
||||
} |
||||
|
||||
} while (1); |
||||
|
||||
_priv->temp = fopen(_priv->tmpfp, "w"); |
||||
if (_priv->temp == NULL) { |
||||
_easycsv_printerror(_priv, ) |
||||
_easycsv_priv_free(_priv); |
||||
return NULL; |
||||
} |
||||
|
||||
#ifdef EASYCSV_DEBUG |
||||
printf("[%i] easycsv_debug: temp file %s opened\n", clock() - csv->start, csv->tmpfp); |
||||
#endif |
||||
/*
|
||||
|
||||
if (freopen(csv->fp, csv->mode, csv->file) == NULL) { |
||||
fprintf(stderr, "easycsv: failed to set temporary file %s to %s mode\n", csv->tmpfp, csv->mode); |
||||
return NULL; |
||||
} |
||||
|
||||
if (freopen(csv->tmpfp, csv->mode, csv->temp) == NULL) { |
||||
fprintf(stderr, "easycsv: failed to set temporary file %s to %s mode\n", csv->tmpfp, csv->mode); |
||||
return NULL; |
||||
} |
||||
*/ |
||||
|
||||
} |
||||
|
||||
return _priv; |
||||
} |
||||
|
||||
void |
||||
_easycsv_priv_free(_easycsv *_priv) |
||||
{ |
||||
if (_priv == NULL) return; |
||||
if (_priv->file != NULL) fclose(_priv->file); |
||||
if (_priv->temp != NULL) fclose(_priv->temp); |
||||
if (_priv->fp != NULL) free(_priv->fp); |
||||
if (_priv->tmpfp != NULL) free(_priv->tmpfp); |
||||
free(_priv); |
||||
} |
||||
|
||||
|
||||
int |
||||
easycsv_pushcolumn(easycsv *csv, const char *col) |
||||
{ |
||||
if (csv == NULL) { |
||||
fprintf(stderr, "easycsv: csv is NULL in %s\n", csv->_priv->fp); |
||||
return -1; |
||||
} |
||||
|
||||
if ((col == NULL) || (col[0] == '\0')) { |
||||
fprintf(stderr, "easycsv: col is either NULL or empty in %s\n", csv->_priv->fp); |
||||
return -1; |
||||
} |
||||
|
||||
if (csv->mode == EASYCSV_R) { |
||||
#ifdef EASYCSV_DEBUG |
||||
fprintf(stderr, "[%i] ", clock() - csv->_priv->start); |
||||
#endif |
||||
fprintf(stderr, "easycsv: %s set to read\n", csv->_priv->fp); |
||||
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) { |
||||
#ifdef EASYCSV_DEBUG |
||||
fprintf(stderr, "[%i] ", clock() - csv->_priv->start); |
||||
#endif |
||||
fprintf(stderr, "easycsv: failed to push column %s in %s\n", col, csv->_priv->fp); |
||||
return -1; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
char *str = _easycsv_getrow(csv, 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); |
||||
|
||||
#ifdef EASYCSV_DEBUG |
||||
printf("[%i] easycsv_debug: push column str: %s", clock() - csv->_priv->start, str); |
||||
#endif |
||||
|
||||
/* Put str in temp file */ |
||||
if (fputs(str, csv->_priv->temp) < 0) { |
||||
#ifdef EASYCSV_DEBUG |
||||
fprintf(stderr, "[%i] ", clock() - csv->_priv->start); |
||||
#endif |
||||
fprintf(stderr, "easycsv: failed to push column %s in %s\n", col, csv->_priv->tmpfp); |
||||
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); |
||||
} |
||||
|
||||
/* Update the file */ |
||||
if (_easycsv_update(csv) < 0) { |
||||
#ifdef EASYCSV_DEBUG |
||||
fprintf(stderr, "[%i] ", clock() - csv->_priv->start); |
||||
#endif |
||||
fprintf(stderr, "easycsv: failed to update %s\n", csv->_priv->fp); |
||||
return -1; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int |
||||
easycsv_pushcolumnvalue(const easycsv *csv, const char *col, const char *val) |
||||
{ |
||||
if (csv == NULL) { |
||||
fprintf(stderr, "easycsv: csv is NULL in %s\n", csv->_priv->fp); |
||||
return -1; |
||||
} |
||||
|
||||
if ((col == NULL) || (col[0] == '\0')) { |
||||
fprintf(stderr, "easycsv: col is either NULL or empty in %s\n", csv->_priv->fp); |
||||
return -1; |
||||
} |
||||
|
||||
if ((val == NULL) || (val[0] == '\0')) { |
||||
fprintf(stderr, "easycsv: val is either NULL or empty in %s\n", csv->_priv->fp); |
||||
return -1; |
||||
} |
||||
|
||||
int colnum = _easycsv_getcolumn(csv, col); |
||||
|
||||
if (colnum < 0) { |
||||
fprintf(stderr, "easycsv: error occured when fetcing col number in %s", csv->_priv->fp); |
||||
return -1; |
||||
} |
||||
|
||||
/* Find a free cell under col within csv->_priv->cols limits, if there is none, generate a new
|
||||
row */ |
||||
unsigned int row; |
||||
|
||||
for (row = 2; row <= csv->_priv->cols; row++) { |
||||
if (easycsv_readvalue(csv, colnum, row) != NULL) break; |
||||
} |
||||
|
||||
/* All rows are filled, generate new row */ |
||||
if (row == csv->_priv->cols) { |
||||
row++; |
||||
|
||||
/* Set file pointer to end */ |
||||
if (fseek(csv->_priv->file, 0L, SEEK_END) < 0) { |
||||
fprintf(stderr, "easycsv: failed to set file pointer to end in %s", csv->_priv->fp); |
||||
return -1; |
||||
} |
||||
|
||||
char *rowstr = malloc(csv->_priv->cols + strlen(val) + 2); // 1 extra for '\n'
|
||||
|
||||
strncpy(rowstr, "\n", 1); |
||||
|
||||
for (unsigned int i = 1; i < colnum; i++) { |
||||
strncpy(rowstr, ",", 1); |
||||
} |
||||
|
||||
strncpy(rowstr, val, strlen(val)); |
||||
|
||||
unsigned int colsleft = csv->_priv->cols - colnum; |
||||
|
||||
for (unsigned i = 1; i < colsleft; i++) { |
||||
strncpy(rowstr, ",", 1); |
||||
} |
||||
|
||||
strncpy(rowstr, "/0", 1); |
||||
|
||||
csv->_priv->rows++; |
||||
} |
||||
|
||||
/* Fill in rows before */ |
||||
|
||||
for (unsigned int i = 1; i < row; i++) { |
||||
char *str = _easycsv_getrow(csv, i); |
||||
if (fputs(str, csv->_priv->temp) == EOF) { |
||||
fprintf(stderr, "easycsv: failed to write \"%s\" in temporary file %s for %s\n", str, csv->_priv->tmpfp, csv->_priv->fp); |
||||
return -1; |
||||
} |
||||
|
||||
free(str); |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int |
||||
easycsv_insertcolumnvalue(const easycsv *csv, const char *col, const unsigned int row, const char *val, const EASYCSV_VALMODE valmode) |
||||
{ |
||||
if (csv == NULL) { |
||||
fprintf(stderr, "easycsv: csv is NULL in %s\n", csv->_priv->fp); |
||||
return -1; |
||||
} |
||||
|
||||
if ((col == NULL) || (col[0] == '\0')) { |
||||
fprintf(stderr, "easycsv: col is either NULL or empty in %s\n", csv->_priv->fp); |
||||
return -1; |
||||
} |
||||
|
||||
if ((val == NULL) || (val[0] == '\0')) { |
||||
fprintf(stderr, "easycsv: val is either NULL or empty in %s\n", csv->_priv->fp); |
||||
return -1; |
||||
} |
||||
|
||||
int colnum = _easycsv_getcolumn(csv, col); |
||||
if (colnum < 0) { |
||||
fprintf(stderr, "easycsv: failed to get colnum of %s in %s", col, csv->_priv->fp); |
||||
return -1; |
||||
} |
||||
|
||||
char *rowstr = _easycsv_getrow(csv, row); |
||||
char *pch = rowstr; |
||||
|
||||
for (unsigned int i = 1; i < colnum; i++) { |
||||
pch = strchr(pch, ','); |
||||
pch++; |
||||
} |
||||
|
||||
size_t st = strcspn(pch, ','); |
||||
|
||||
/* Empty cell */ |
||||
if (st == 0) { |
||||
|
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int |
||||
easycsv_printrows(const easycsv *csv) |
||||
{ |
||||
return csv->_priv->rows; |
||||
} |
||||
|
||||
int |
||||
easycsv_printcolumns(const easycsv *csv) |
||||
{ |
||||
return csv->_priv->cols; |
||||
} |
||||
|
||||
int |
||||
_easycsv_rows(_easycsv *_priv, const EASYCSV_MODE mode) |
||||
{ |
||||
if (_priv == NULL) { |
||||
fprintf(stderr, "easycsv: csv is NULL in %s\n", _priv->fp); |
||||
return -1; |
||||
} |
||||
|
||||
/* Prepare it for reading */ |
||||
if (_easycsv_rewind(_priv, mode) < 0) return -1; |
||||
|
||||
int rows = 1; |
||||
char c; |
||||
|
||||
/* Go through each character in the file and count the number of \n
|
||||
in it */ |
||||
while ((c = fgetc(_priv->file)) != EOF) { |
||||
if (c == '\n') rows++; |
||||
} |
||||
|
||||
return rows; |
||||
} |
||||
|
||||
int |
||||
_easycsv_columns(_easycsv *_priv, const EASYCSV_MODE mode) |
||||
{ |
||||
if (_priv == NULL) { |
||||
fprintf(stderr, "easycsv: csv is NULL in %s\n", _priv->fp); |
||||
return -1; |
||||
} |
||||
|
||||
/* Prepare it for reading */ |
||||
if (_easycsv_rewind(_priv, mode) < 0) return -1; |
||||
|
||||
/*
|
||||
1. check if empty file |
||||
2. check if only one column -> 0 commas |
||||
3. if >1 column, n commas = n+1 columns |
||||
*/ |
||||
|
||||
unsigned int col = 1; |
||||
char c; |
||||
|
||||
while ((c = fgetc(_priv->file)) != '\n') { |
||||
if (c == ',') col++; |
||||
} |
||||
|
||||
return col; |
||||
} |
||||
|
||||
const char* |
||||
easycsv_readcolumnvalue(const easycsv *csv, const char *col, const unsigned int row) |
||||
{ |
||||
if (csv == NULL) { |
||||
fprintf(stderr, "easycsv: csv is NULL in %s\n", csv->_priv->fp); |
||||
return NULL; |
||||
} |
||||
|
||||
if (row > csv->_priv->rows) { |
||||
fprintf(stderr, "easycsv: row exceeds max in %s\n", csv->_priv->fp); |
||||
return NULL; |
||||
} |
||||
|
||||
int i = _easycsv_getcolumn(csv, col); |
||||
if (i < 1) { |
||||
return NULL; |
||||
} |
||||
|
||||
return easycsv_readvalue(csv, i, row); |
||||
} |
||||
|
||||
const char* |
||||
easycsv_readvalue(const easycsv *csv, const unsigned int col, const unsigned int row) |
||||
{
|
||||
if (col == 0 || row == 0) { |
||||
fputs("easycsv: row or col is 0\n", stderr); |
||||
return NULL; |
||||
} |
||||
|
||||
char *str = _easycsv_getrow(csv, row); |
||||
if (str == NULL) { |
||||
fprintf(stderr, "easycsv: row %i does not exist in %s\n", row, csv->_priv->fp); |
||||
return NULL; |
||||
} |
||||
|
||||
#ifdef EASYCSV_DEBUG |
||||
printf("[%i] rv_col: %i\nstr: %s\n", clock() - csv->_priv->start, col, str); |
||||
#endif |
||||
|
||||
/* Return if first col */ |
||||
if (col == 1) { |
||||
size_t st = strcspn(str, ","); |
||||
if (st == 0) { |
||||
free(str); |
||||
return NULL; |
||||
}
|
||||
char *pch = malloc(st); |
||||
strncpy(pch, str, st); |
||||
free(str); |
||||
return pch; |
||||
} |
||||
|
||||
/* Get first occurance of comma in str,
|
||||
the first value is ommited but not the comma */ |
||||
char *pch = strpbrk(str, ","); |
||||
|
||||
/* Repeat until desired col is found */ |
||||
for (int i = 2; i < col; i++) {
|
||||
pch = strpbrk(pch + 1, ","); |
||||
} |
||||
|
||||
/* Get span from start of string to first occurence of comma */ |
||||
size_t st = strcspn(pch + 1, ","); |
||||
|
||||
/* If 0, no string exists! */ |
||||
if (st == 0) { |
||||
free(str); |
||||
return NULL; |
||||
} |
||||
|
||||
char *val = malloc(st + 1); |
||||
strncpy(val, pch + 1, st); |
||||
strncat(val, "\0", 1); |
||||
free(str); |
||||
|
||||
return val; |
||||
} |
||||
|
||||
void |
||||
easycsv_geterror(const easycsv *csv) |
||||
{ |
||||
_easycsv_pgeterror(csv->_priv); |
||||
} |
||||
|
||||
void |
||||
easycsv_fgeterror(const easycsv *csv, FILE *fs) |
||||
{ |
||||
_easycsv_fpgeterror(csv->_priv, fs); |
||||
} |
||||
|
||||
/* PRIVATE FUNCTIONS */ |
||||
|
||||
/*
|
||||