EasyCSV/src/easycsv.c

1188 lines
26 KiB
C

#include "easycsv.h"
/* Generic type definitions for internal use */
typedef uint32_t row_t; // max length of 2^32 - 1
typedef uint32_t column_t;
typedef char* rowstr; // non-const, to free it after usage
/* I may possibly need to make types for rows,
iterators and columns */
/* Flags denoting cell type */
typedef enum {
EASYCSV_NONE,
EASYCSV_STRING,
EASYCSV_INT,
EASYCSV_FLOAT,
EASYCSV_UNKNOWNTYPE
} _EASYCSV_TYPE;
/* Error flags */
typedef enum {
/* Generic errors */
EASYCSV_NOERROR, // no error
EASYCSV_NULLCSV, // easycsv* is NULL
EASYCSV_NULLPTR, // generic pointer is NULL
EASYCSV_EMPTYSTRING, // empty string
EASYCSV_EMPTYVALUE, // value is empty
EASYCSV_OVERMAXROW, // int exceeds row limit
EASYCSV_OVERMAXCOL, // int exceeds col limit
EASYCSV_ZEROROW,
EASYCSV_ZEROCOL,
/* File input/output errors */
EASYCSV_UNKNOWNIOMODE, // 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 = temp -> file
EASYCSV_UPDATETEMPFAIL, // update = -> temp
EASYCSV_FILEPTRFAIL, // move file pointer fail
/* Non-existant elements */
EASYCSV_ROWNOTEXIST, // row does not exist
EASYCSV_COLNOTEXIST, // column does not exist
/* User-facing failure */
EASYCSV_PUSHCOLFAIL, // push column value fail
EASYCSV_COLNUMFAIL, // column number retrieval fail
EASYCSV_READVALUEFAIL, // read value fail
} EASYCSV_ERROR;
/* Private easycsv members */
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) Initialise _easycsv */
static _easycsv*
_easycsv_priv_init(const char*,
const EASYCSV_MODE,
const EASYCSV_ERRORMSG);
/* (Destructor) Free _easycsv memory */
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,
includes the '\n' character as well! */
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 _EASYCSV_TYPE
_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);
/* Grab const char* in row */
static char*
_easycsv_getvalueinrow(const _easycsv*,
const char*,
const unsigned int);
/* Returns char pointer to start of value in rowstr */
static char*
_easycsv_setcharptovalue(const _easycsv*,
const char*,
const unsigned int);
/* Insert value in row in specific column */
static char*
_easycsv_insertvalueinrow(const _easycsv*,
const char*,
const char*,
const unsigned int);
/* 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 == */
/* 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, 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, 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, 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, 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, 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, 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) < 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, col, val) < 0)
return -1;
if (row == 0) {
_easycsv_printerror(csv->_priv, EASYCSV_ZEROROW);
return -1;
}
/* END ARGS CHECK */
int colnum = _easycsv_getcolumn(csv, 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, 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, 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, col, val) < 0)
return -1;
/* ARGS CHECK */
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;
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, col) < 0)
return -1;
/* END ARGS CHECK */
int colnum = _easycsv_getcolumn(csv, col);
if (colnum < 0)
return -1;
return easycsv_deletecolumnint(csv, colnum);
}
int
easycsV_deleterowint(easycsv *csv,
const unsigned int row)
{
return 0;
}
/* PRIVATE FUNCTIONS */
/* (Constructor) Initialise _easycsv */
_easycsv*
_easycsv_priv_init(const char *fp,
const EASYCSV_MODE mode,
const EASYCSV_ERRORMSG error)
{
_easycsv *_priv = malloc(sizeof(_easycsv));
_priv->file = NULL;
_priv->temp = NULL;
_priv->fp = NULL;
_priv->tmpfp = NULL;
_priv->rows = 0;
_priv->cols = 0;
#ifdef EASYCSV_DEBUG
_priv->start = 0;
#endif
_priv->error = error;
/* Open file according to mode */
switch (mode) {
case EASYCSV_R: {
_priv->file = fopen(fp, "r");
break;
}
case EASYCSV_W: {
_priv->file = fopen(fp, "w");
break;
}
default: {
_easycsv_printerror(_priv, EASYCSV_UNKNOWNIOMODE);
_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);
}
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
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 {
_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;
}
/* (Destructor) Free _easycsv memory */
void
_easycsv_priv_free(_easycsv *_priv)
{
/* ARGS CHECK */
if (_priv == NULL)
return;
/* ARGS CHECK */
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_update(easycsv *csv)
{
/* ARGS CHECK */
if (csv == NULL) {
_easycsv_printerror(csv->_priv, EASYCSV_NULLCSV);
return -1;
}
/* ARGS CHECK */
/* 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;
}
/* Copy entire file */
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;
}
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
*/
int col = 1;
char c;
while ((c = fgetc(_priv->file)) != '\n') {
if (c == ',') col++;
}
return col;
}
char*
_easycsv_getrow(const easycsv *csv,
const unsigned int row)
{
/* ARGS CHECK */
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;
}
/* END ARGS CHECK */
/* 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)
{
/* ARGS CHECK */
if (_easycsv_checkcsvandstring_one(csv, col) < 0)
return -1;
/* ARGS CHECK */
/* 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 _easycsv *_priv,
const char *row,
const unsigned int col)
{
size_t st;
char *pch = NULL;
/* If not first column */
if (col != 1) {
/* Get first occurance of comma in str,
the first value is ommited but not the comma */
char *pch = strpbrk(row, ",");
/* 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 */
st = strcspn(pch + 1, ",");
/* If 0, no string exists! */
if (st == 0) {
_easycsv_printerror(_priv, EASYCSV_EMPTYVALUE);
return NULL;
}
char *val = malloc(st + 1);
strncpy(val, pch + 1, st);
strncat(val, "\0", 1);
return val;
}
static char*
_easycsv_setcharptovalue(const _easycsv *_priv,
const char *rowstr,
const unsigned int col)
{
char *pch = rowstr;
for (unsigned int i = 1; i < col; i++) {
pch = strchr(rowstr, ',');
if (pch == NULL) {
_easycsv_printerror(_priv, EASYCSV_NULLPTR);
return NULL;
}
pch++;
}
return pch;
}
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) {
/* Generic errors */
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_EMPTYVALUE: fputs("value in CSV file 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;
/* File input/output errors */
case EASYCSV_UNKNOWNIOMODE: 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_UPDATETEMPFAIL: fputs("failed to update temp CSV file", fs); break;
case EASYCSV_FILEPTRFAIL: fputs("failed to move FILE pointer", fs); break;
/* Non-existant elements */
case EASYCSV_ROWNOTEXIST: fputs("given row does not exist", fs); break;
case EASYCSV_COLNOTEXIST: fputs("given column does not exist", fs); break;
/* User-facing failure */
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) {
_easycsv_printerror(csv->_priv, EASYCSV_EMPTYSTRING);
return -1;
}
return 0;
}
int
_easycsv_checkcsvandstring_two(const easycsv *csv,
const char *one,
const char *two)
{
if (_easycsv_checkcsvandstring_one(csv, one) < 0)
return -1;
if (two == NULL) {
_easycsv_printerror(csv->_priv, EASYCSV_EMPTYSTRING);
return -1;
}
return 0;
}