#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 *csv); 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 *_priv); memset(_priv, 0, sizeof(*_priv)); // set all members to NULL or 0 _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_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); } 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 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 */ unsigned 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_checkcsvandstringtwo(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; }