1
0
libcm/cmCsv.c

1198 Zeilen
29 KiB
C

#include "cmPrefix.h"
#include "cmGlobal.h"
#include "cmRpt.h"
#include "cmErr.h"
#include "cmCtx.h"
#include "cmMem.h"
#include "cmMallocDebug.h"
#include "cmLex.h"
#include "cmLinkedHeap.h"
#include "cmHashTbl.h"
#include "cmCsv.h"
#include "cmText.h"
enum
{
kCommaLexTId = kUserLexTId+1,
kMaxLexTId // always last in the lex id list
};
typedef struct
{
const char* text;
unsigned id;
} cmCsvToken_t;
cmCsvToken_t _cmCsvTokenArray[] =
{
{ ",", kCommaLexTId },
{ "", kErrorLexTId }
};
// The binder linked list contains a list of records which tracks the first
// (left-most) non-blank column in each row.
typedef struct cmCsvBind_str
{
struct cmCsvBind_str* linkPtr;
cmCsvCell_t* rowPtr;
} cmCsvBind_t;
typedef struct cmCsvUdef_str
{
struct cmCsvUdef_str* linkPtr;
unsigned id;
} cmCsvUdef_t;
typedef struct
{
cmErr_t err;
void* rptDataPtr; //
cmLexH lexH; // parsing lexer
cmHashTblH_t htH; // hash table handle
cmLHeapH_t heapH;
cmCsvBind_t* bindPtr; // base of the binder linked list
cmCsvCell_t* curRowPtr; // used by the parser to track the row being filled
cmCsvUdef_t* udefList;
} cmCsv_t;
cmCsvH_t cmCsvNullHandle = { NULL };
cmCsv_t* _cmCsvHandleToPtr( cmCsvH_t h )
{
cmCsv_t* p = (cmCsv_t*)h.h;
assert( p != NULL );
cmErrClearRC(&p->err);
return p;
}
cmCsvRC_t _cmCsvVError( cmCsv_t* p, cmCsvRC_t rc, const char* fmt, va_list vl )
{ return cmErrVMsg(&p->err,rc,fmt,vl); }
cmCsvRC_t _cmCsvError( cmCsv_t* p, cmCsvRC_t rc, const char* fmt, ... )
{
va_list vl;
va_start(vl,fmt);
rc = _cmCsvVError(p,rc,fmt,vl);
va_end(vl);
return rc;
}
cmCsvRC_t cmCsvInitialize( cmCsvH_t *hp, cmCtx_t* ctx )
{
cmCsvRC_t rc;
cmCsv_t* p = NULL;
unsigned i;
if((rc = cmCsvFinalize(hp)) != kOkCsvRC )
return rc;
// create the base object
p = cmMemAllocZ( cmCsv_t, 1 );
assert(p != NULL);
cmErrSetup(&p->err,&ctx->rpt,"CSV");
// create the hash table
if( cmHashTblCreate(ctx,&p->htH,8192) != kOkHtRC )
{
rc = _cmCsvError(p,kHashTblErrCsvRC,"Hash table creation failed.");
goto errLabel;
}
// allocate the linked heap mgr
if( cmLHeapIsValid(p->heapH = cmLHeapCreate(8192,ctx)) == false )
{
rc = _cmCsvError(p,kMemAllocErrCsvRC,"Linked heap object allocation failed.");
goto errLabel;
}
// allocate the lexer
if(cmLexIsValid(p->lexH = cmLexInit(NULL,0,0,&ctx->rpt)) == false )
{
rc = _cmCsvError(p,kLexErrCsvRC,"Lex allocation failed.");
goto errLabel;
}
// register CSV specific tokens with the lexer
for(i=0; _cmCsvTokenArray[i].id != kErrorLexTId; ++i)
{
cmRC_t lexRC;
if( (lexRC = cmLexRegisterToken(p->lexH, _cmCsvTokenArray[i].id, _cmCsvTokenArray[i].text )) != kOkLexRC )
{
rc = _cmCsvError(p,kLexErrCsvRC,"Lex token registration failed for:'%s'.\nLexer Error:%s",_cmCsvTokenArray[i].text, cmLexRcToMsg(lexRC) );
goto errLabel;
}
}
hp->h = p;
return kOkCsvRC;
errLabel:
cmMemPtrFree(&p);
if( cmLHeapIsValid(p->heapH) )
cmLHeapDestroy(&p->heapH);
if( cmLexIsValid(p->lexH) )
cmLexFinal(&p->lexH);
return rc;
}
cmCsvRC_t cmCsvFinalize( cmCsvH_t *hp )
{
cmRC_t lexRC;
if( hp == NULL || hp->h == NULL )
return kOkCsvRC;
cmCsv_t* p = _cmCsvHandleToPtr(*hp);
// free the internal heap object
cmLHeapDestroy( &p->heapH );
// free the lexer
if((lexRC = cmLexFinal(&p->lexH)) != kOkLexRC )
return _cmCsvError(p,kLexErrCsvRC,"Lexer finalization failed.\nLexer Error:%s",cmLexRcToMsg(lexRC));
// free the hash table
cmHashTblDestroy(&p->htH);
// free the handle
cmMemPtrFree(&hp->h);
return kOkCsvRC;
}
cmCsvRC_t cmCsvInitializeFromFile( cmCsvH_t *hp, const char* fn, unsigned maxRowCnt, cmCtx_t* ctx )
{
cmCsvRC_t rc;
if((rc = cmCsvInitialize(hp,ctx)) != kOkCsvRC )
return rc;
return cmCsvParseFile( *hp, fn, maxRowCnt);
}
bool cmCsvIsValid( cmCsvH_t h)
{ return h.h != NULL; }
cmCsvRC_t cmCsvLastRC( cmCsvH_t h )
{
cmCsv_t* p = _cmCsvHandleToPtr(h);
return cmErrLastRC(&p->err);
}
void cmCsvClearRC( cmCsvH_t h )
{
cmCsv_t* p = _cmCsvHandleToPtr(h);
cmErrClearRC(&p->err);
}
cmCsvRC_t cmCsvParseFile( cmCsvH_t h, const char* fn, unsigned maxRowCnt )
{
cmCsvRC_t rc = kOkCsvRC;
FILE* fp = NULL;
cmCsv_t* p = _cmCsvHandleToPtr(h);
unsigned n = 0;
char* textBuf = NULL;
assert( fn != NULL && p != NULL );
// open the file
if((fp = fopen(fn,"rb")) == NULL )
return _cmCsvError(p,kFileOpenErrCsvRC,"Unable to open the file:'%s'.",fn);
// seek to the end
if( fseek(fp,0,SEEK_END) != 0 )
{
rc= _cmCsvError(p,kFileSeekErrCsvRC,"Unable to seek to the end of '%s'.",fn);
goto errLabel;
}
// get the length of the file
if( (n=ftell(fp)) == 0 )
{
rc = _cmCsvError(p,kFileOpenErrCsvRC,"The file '%s' appears to be empty.",fn);
goto errLabel;
}
// rewind the file
if( fseek(fp,0,SEEK_SET) != 0 )
{
rc = _cmCsvError(p,kFileSeekErrCsvRC,"Unable to seek to the beginning of '%s'.",fn);
goto errLabel;
}
// allocate the text buffer
if((textBuf = cmMemAllocZ( char, n+1)) == NULL )
{
rc = _cmCsvError(p,kMemAllocErrCsvRC,"Unable to allocate the text file buffer for:'%s'.",fn);
goto errLabel;
}
// read the file into the text buffer
if( fread(textBuf,n,1,fp) != 1 )
{
rc = _cmCsvError(p,kFileReadErrCsvRC,"File read failed on:'%s'.",fn);
goto errLabel;
}
rc = cmCsvParse(h,textBuf,n,maxRowCnt);
errLabel:
// close the file
if( fclose(fp) != 0 )
{
rc = _cmCsvError(p,kFileCloseErrCsvRC,"File close failed on:'%s'.",fn);
goto errLabel;
}
// free the buffer
if( textBuf != NULL )
cmMemFree(textBuf);
return rc;
}
cmCsvRC_t _cmCsvAppendNewBindRecd( cmCsv_t* p, cmCsvBind_t** newBindPtrPtr, unsigned* newRowIdxPtr )
{
cmCsvBind_t* nbp;
cmCsvBind_t* bp = p->bindPtr;
unsigned newRowIdx = 0;
if( newBindPtrPtr != NULL )
*newBindPtrPtr = NULL;
if( newRowIdxPtr != NULL )
*newRowIdxPtr = cmInvalidIdx;
// allocate the binder record
if((nbp = cmLHeapAllocZ( p->heapH, sizeof(cmCsvBind_t))) == NULL )
return _cmCsvError(p,kMemAllocErrCsvRC,"Binding record allocation failed.");
// if this is the first binder record
if( p->bindPtr == NULL )
{
p->bindPtr = nbp;
bp = nbp;
}
else
{
newRowIdx = 1;
// iterate to the bottom of the binding
while( bp->linkPtr != NULL )
{
bp = bp->linkPtr;
++newRowIdx;
}
bp->linkPtr = nbp;
}
if( newBindPtrPtr != NULL )
*newBindPtrPtr = nbp;
if( newRowIdxPtr != NULL )
*newRowIdxPtr = newRowIdx;
return kOkCsvRC;
}
cmCsvRC_t _cmCsvCreateBind( cmCsv_t* p, cmCsvCell_t* cp, const char* tokenText, unsigned lexRow, unsigned lexCol )
{
cmCsvRC_t rc;
cmCsvBind_t* nbp;
if((rc = _cmCsvAppendNewBindRecd(p,&nbp,NULL)) != kOkCsvRC )
return _cmCsvError(p,kMemAllocErrCsvRC,"Binding record allocation failed for '%s' on line %i column %i.",tokenText,lexRow,lexCol);
nbp->rowPtr = cp;
return rc;
}
cmCsvRC_t _cmCsvAllocCell( cmCsv_t* p, unsigned symId, unsigned flags, unsigned cellRow, unsigned cellCol, cmCsvCell_t** cpp, unsigned lexTId )
{
cmCsvCell_t* cp;
// allocate cell memory
if(( cp = cmLHeapAllocZ(p->heapH, sizeof(cmCsvCell_t) )) == NULL )
return _cmCsvError(p,kMemAllocErrCsvRC,"Cell allocation failed. for row: %i column %i.",cellRow+1,cellCol+1);
cp->row = cellRow;
cp->col = cellCol;
cp->symId = symId;
cp->flags = flags;
cp->lexTId= lexTId;
*cpp = cp;
return kOkCsvRC;
}
cmCsvRC_t _cmCsvCreateCell( cmCsv_t* p, const char* tokenText, unsigned flags, unsigned cellRow, unsigned cellCol, unsigned lexTId )
{
unsigned symId = cmInvalidId;
cmCsvCell_t* cp = NULL;
unsigned lexRow = cmLexCurrentLineNumber(p->lexH);
unsigned lexCol = cmLexCurrentColumnNumber(p->lexH);
cmCsvRC_t rc = kOkCsvRC;
// register the token text as a symbol
if((symId = cmHashTblStoreStr(p->htH,tokenText)) == cmInvalidId )
return _cmCsvError(p,kHashTblErrCsvRC,"Symbol registration failed. for '%s' on line %i column %i.",tokenText,lexRow,lexCol);
// allocate a cell
if((rc = _cmCsvAllocCell(p,symId,flags,cellRow,cellCol,&cp,lexTId)) != kOkCsvRC )
return rc;
// if this is the first cell in the row
if( p->curRowPtr == NULL )
{
// link in a new binding record
if((rc = _cmCsvCreateBind( p, cp,tokenText,lexRow,lexCol)) != kOkCsvRC )
return rc; // this return will result in a leak from the lheap but it is a fatal error so ignore it
// update the current row ptr
p->curRowPtr = cp;
}
else
{
// iterate to the end of the row
cmCsvCell_t* rp = p->curRowPtr;
while( rp->rowPtr != NULL )
rp = rp->rowPtr;
rp->rowPtr = cp;
}
return rc;
}
const cmCsvUdef_t* _cmCsvFindUdef( cmCsv_t* p, unsigned id )
{
const cmCsvUdef_t* u = p->udefList;
while( u != NULL )
{
if( u->id == id )
return u;
u = u->linkPtr;
}
return NULL;
}
cmCsvRC_t _cmCsvRegUdef( cmCsv_t* p, unsigned id )
{
if( _cmCsvFindUdef(p,id) != NULL )
return _cmCsvError(p,kDuplicateLexCsvId,"%i has already been used as a user defined id.",id);
cmCsvUdef_t* u = cmLhAllocZ(p->heapH,cmCsvUdef_t,1);
u->id = id;
u->linkPtr = p->udefList;
p->udefList = u;
return kOkCsvRC;
}
cmCsvRC_t cmCsvLexRegisterToken( cmCsvH_t h, unsigned id, const cmChar_t* token )
{
cmCsv_t* p = _cmCsvHandleToPtr(h);
if(cmLexRegisterToken(p->lexH,id,token) != kOkLexRC )
return _cmCsvError(p, kLexErrCsvRC,"Error registering user defined token '%s'.",cmStringNullGuard(token));
return _cmCsvRegUdef(p,id);
}
cmCsvRC_t cmCsvLexRegisterMatcher( cmCsvH_t h, unsigned id, cmLexUserMatcherPtr_t matchFunc )
{
cmCsv_t* p = _cmCsvHandleToPtr(h);
if( cmLexRegisterMatcher(p->lexH,id,matchFunc) != kOkLexRC )
return _cmCsvError(p, kLexErrCsvRC,"Error registering user defined matching function.");
return _cmCsvRegUdef(p,id);
}
unsigned cmCsvLexNextAvailId( cmCsvH_t h )
{ return kMaxLexTId; }
cmCsvRC_t cmCsvParse( cmCsvH_t h, const char* buf, unsigned bufCharCnt, unsigned maxRowCnt )
{
cmCsvRC_t rc = kOkCsvRC;
unsigned prvLexRow = 1;
unsigned csvRowIdx = 0;
unsigned csvColIdx = 0;
unsigned lexTId = kErrorLexTId; // cur lex token type id
cmCsv_t* p = _cmCsvHandleToPtr(h);
// assign the text buffer and reset the lexer
if((rc = cmLexSetTextBuffer( p->lexH, buf, bufCharCnt )) != kOkLexRC )
return _cmCsvError( p, kLexErrCsvRC, "Error setting lexer buffer.\nLexer Error:%s",cmLexRcToMsg(rc));
// get the next token
while( (lexTId = cmLexGetNextToken( p->lexH )) != kErrorLexTId && (lexTId != kEofLexTId ) && (rc==kOkCsvRC) && (maxRowCnt==0 || csvRowIdx<maxRowCnt))
{
unsigned flags = 0;
unsigned curLexRow = cmLexCurrentLineNumber(p->lexH);
// if we are starting a new row
if( curLexRow != prvLexRow )
{
prvLexRow = curLexRow;
++csvRowIdx;
csvColIdx = 0;
p->curRowPtr = NULL; // force a new binding record
}
// copy the token text in a buffer
unsigned n = cmLexTokenCharCount(p->lexH);
char buf[n+1];
strncpy(buf, cmLexTokenText(p->lexH), n );
buf[n]=0;
// do cell data type specific processing
switch( lexTId )
{
case kCommaLexTId:
++csvColIdx;
break;
case kRealLexTId: flags = kRealCsvTFl; break;
case kIntLexTId: flags = kIntCsvTFl; break;
case kHexLexTId: flags = kHexCsvTFl; break;
case kIdentLexTId:flags = kIdentCsvTFl; break;
case kQStrLexTId: flags = kStrCsvTFl; break;
default:
{
const cmCsvUdef_t* u;
if((u = _cmCsvFindUdef(p,lexTId)) == NULL )
rc = _cmCsvError(p, kSyntaxErrCsvRC, "Unrecognized token type: '%s' for token: '%s' row:%i col:%i.",cmLexIdToLabel(p->lexH,lexTId),buf,csvRowIdx+1,csvColIdx+1);
else
flags = kStrCsvTFl | kUdefCsvTFl;
}
}
// if no error occurred and the token is not a comma and the cell is not empty
if( rc == kOkCsvRC && lexTId != kCommaLexTId && strlen(buf) > 0 )
if((rc = _cmCsvCreateCell( p, buf,flags, csvRowIdx, csvColIdx, lexTId )) != kOkCsvRC )
rc = _cmCsvError(p,rc,"CSV cell create fail on row:%i col:%i\n",csvRowIdx+1,csvColIdx+1);
}
if( lexTId == kErrorLexTId )
rc = _cmCsvError(p, kSyntaxErrCsvRC,"The lexer encountered an unrecognized token row:%i col:%i.",csvRowIdx+1,csvColIdx+1);
return rc;
}
unsigned cmCsvRowCount( cmCsvH_t h )
{
cmCsv_t* p = _cmCsvHandleToPtr(h);
unsigned rowCnt = 0;
cmCsvBind_t* bp = p->bindPtr;
for(; bp != NULL; ++rowCnt )
bp = bp->linkPtr;
return rowCnt;
}
cmCsvCell_t* cmCsvRowPtr( cmCsvH_t h, unsigned row )
{
cmCsv_t* p = _cmCsvHandleToPtr(h);
cmCsvBind_t* bp = p->bindPtr;
while( bp != NULL )
{
// note: bp->rowPtr of blank rows will be NULL
if( bp->rowPtr!=NULL && bp->rowPtr->row == row )
return bp->rowPtr;
bp = bp->linkPtr;
}
return NULL;
}
cmCsvCell_t* cmCsvCellPtr( cmCsvH_t h, unsigned row, unsigned col )
{
cmCsvCell_t* cp;
if((cp = cmCsvRowPtr(h,row)) == NULL )
return NULL;
while( cp != NULL )
{
if( cp->col == col )
return cp;
cp = cp->rowPtr;
}
return NULL;
}
cmCsvCell_t* _cmCsvCellPtr( cmCsvH_t h, unsigned row, unsigned col )
{
cmCsvCell_t* cp;
if((cp = cmCsvCellPtr(h,row,col)) == NULL )
_cmCsvError(_cmCsvHandleToPtr(h),kCellNotFoundCsvRC,"Cell at row:%i col:%i not found.",row,col);
return cp;
}
const char* cmCsvCellSymText( cmCsvH_t h, unsigned symId )
{
cmCsv_t* p = _cmCsvHandleToPtr(h);
const char* cp;
if((cp = cmHashTblStr(p->htH,symId)) == NULL )
_cmCsvError(p,kHashTblErrCsvRC,"The text associated with the symbol '%i' was not found.",symId);
return cp;
}
cmCsvRC_t cmCsvCellSymInt( cmCsvH_t h, unsigned symId, int* vp )
{
const char* cp;
cmCsv_t* p = _cmCsvHandleToPtr(h);
if((cp = cmCsvCellSymText(h,symId)) == NULL )
return kHashTblErrCsvRC;
if( cmTextToInt(cp,vp,&p->err) != kOkTxRC )
return _cmCsvError(p,kDataCvtErrCsvRC,"CSV text to int value failed.");
return kOkCsvRC;
}
cmCsvRC_t cmCsvCellSymUInt( cmCsvH_t h, unsigned symId, unsigned* vp )
{
const char* cp;
cmCsv_t* p = _cmCsvHandleToPtr(h);
if((cp = cmCsvCellSymText(h,symId)) == NULL )
return kHashTblErrCsvRC;
if( cmTextToUInt(cp,vp,&p->err) != kOkTxRC )
return _cmCsvError(p,kDataCvtErrCsvRC,"CSV text to uint value failed.");
return kOkCsvRC;
}
cmCsvRC_t cmCsvCellSymFloat( cmCsvH_t h, unsigned symId, float* vp )
{
const char* cp;
cmCsv_t* p = _cmCsvHandleToPtr(h);
if((cp = cmCsvCellSymText(h,symId)) == NULL )
return kHashTblErrCsvRC;
if( cmTextToFloat(cp,vp,&p->err) != kOkTxRC )
return _cmCsvError(p,kDataCvtErrCsvRC,"CSV text to float value failed.");
return kOkCsvRC;
}
cmCsvRC_t cmCsvCellSymDouble( cmCsvH_t h, unsigned symId, double* vp )
{
const char* cp;
cmCsv_t* p = _cmCsvHandleToPtr(h);
if((cp = cmCsvCellSymText(h,symId)) == NULL )
return kHashTblErrCsvRC;
if( cmTextToDouble(cp,vp,&p->err) != kOkTxRC )
return _cmCsvError(p,kDataCvtErrCsvRC,"CSV text to double value failed.");
return kOkCsvRC;
}
const char* cmCsvCellText( cmCsvH_t h, unsigned row, unsigned col )
{
cmCsvCell_t* cp;
if((cp = cmCsvCellPtr(h,row,col)) == NULL )
return NULL;
return cmCsvCellSymText(h,cp->symId);
}
int cmCsvCellInt( cmCsvH_t h, unsigned row, unsigned col )
{
cmCsvCell_t* cp;
int v;
if((cp = cmCsvCellPtr(h,row,col)) == NULL )
return INT_MAX;
if(cmCsvCellSymInt(h,cp->symId,&v) != kOkCsvRC )
return INT_MAX;
return v;
}
unsigned cmCsvCellUInt( cmCsvH_t h, unsigned row, unsigned col )
{
cmCsvCell_t* cp;
unsigned v;
if((cp = cmCsvCellPtr(h,row,col)) == NULL )
return UINT_MAX;
if(cmCsvCellSymUInt(h,cp->symId,&v) != kOkCsvRC )
return UINT_MAX;
return v;
}
float cmCsvCellFloat( cmCsvH_t h, unsigned row, unsigned col )
{
cmCsvCell_t* cp;
float v;
if((cp = cmCsvCellPtr(h,row,col)) == NULL )
return FLT_MAX;
if(cmCsvCellSymFloat(h,cp->symId,&v) != kOkCsvRC )
return FLT_MAX;
return v;
}
double cmCsvCellDouble(cmCsvH_t h, unsigned row, unsigned col )
{
cmCsvCell_t* cp;
double v;
if((cp = cmCsvCellPtr(h,row,col)) == NULL )
return DBL_MAX;
if(cmCsvCellSymDouble(h,cp->symId,&v) != kOkCsvRC )
return DBL_MAX;
return v;
}
unsigned cmCsvInsertSymText( cmCsvH_t h, const char* text )
{
cmCsv_t* p = _cmCsvHandleToPtr(h);
unsigned symId;
if((symId = cmHashTblStoreStr(p->htH,text)) == cmInvalidId )
_cmCsvError(p,kHashTblErrCsvRC,"'%s' could not be inserted into the symbol table.",text);
return symId;
}
unsigned cmCsvInsertSymInt( cmCsvH_t h, int v )
{
const char* fmt = "%i";
unsigned n = snprintf(NULL,0,fmt,v)+1;
char buf[n];
buf[0]= 0;
if( snprintf(buf,n,fmt,v) == n-1 )
return cmCsvInsertSymText(h,buf);
_cmCsvError(_cmCsvHandleToPtr(h),kDataCvtErrCsvRC,"The integer %i could not be converted to text.",v);
return cmInvalidId;
}
unsigned cmCsvInsertSymUInt( cmCsvH_t h, unsigned v )
{
const char* fmt = "%i";
unsigned n = snprintf(NULL,0,fmt,v)+1;
char buf[n];
buf[0]= 0;
if( snprintf(buf,n,fmt,v) == n-1 )
return cmCsvInsertSymText(h,buf);
_cmCsvError(_cmCsvHandleToPtr(h),kDataCvtErrCsvRC,"The unsigned int %i could not be converted to text.",v);
return cmInvalidId;
}
unsigned cmCsvInsertSymHex( cmCsvH_t h, unsigned v )
{
const char* fmt = "0x%x";
unsigned n = snprintf(NULL,0,fmt,v)+1;
char buf[n];
buf[0]= 0;
if( snprintf(buf,n,fmt,v) == n-1 )
return cmCsvInsertSymText(h,buf);
_cmCsvError(_cmCsvHandleToPtr(h),kDataCvtErrCsvRC,"The unsigned int 0x%x could not be converted to text.",v);
return cmInvalidId;
}
unsigned cmCsvInsertSymFloat( cmCsvH_t h, float v )
{
const char* fmt = "%f";
unsigned n = snprintf(NULL,0,fmt,v)+1;
char buf[n];
buf[0] = 0;
if( snprintf(buf,n,fmt,v) == n-1 )
return cmCsvInsertSymText(h,buf);
_cmCsvError(_cmCsvHandleToPtr(h),kDataCvtErrCsvRC,"The float %f could not be converted to text.",v);
return cmInvalidId;
}
unsigned cmCsvInsertSymDouble( cmCsvH_t h, double v )
{
const char* fmt = "%f";
unsigned n = snprintf(NULL,0,fmt,v)+1;
char buf[n];
buf[0]= 0;
if( snprintf(buf,n,fmt,v) == n-1 )
return cmCsvInsertSymText(h,buf);
_cmCsvError(_cmCsvHandleToPtr(h),kDataCvtErrCsvRC,"The double %f could not be converted to text.",v);
return cmInvalidId;
}
cmCsvRC_t cmCsvSetCellText( cmCsvH_t h, unsigned row, unsigned col, const char* text )
{
cmCsvCell_t* cp;
unsigned symId;
if((cp = _cmCsvCellPtr(h,row,col)) == NULL )
return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
if((symId = cmCsvInsertSymText(h,text)) == cmInvalidId )
return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
cp->symId = symId;
cp->flags &= !kTypeTMask;
cp->flags |= kStrCsvTFl;
return kOkCsvRC;
}
cmCsvRC_t cmCsvSetCellInt( cmCsvH_t h, unsigned row, unsigned col, int v )
{
cmCsvCell_t* cp;
unsigned symId;
if((cp = _cmCsvCellPtr(h,row,col)) == NULL )
return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
if((symId = cmCsvInsertSymInt(h,v)) == cmInvalidId )
return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
cp->symId = symId;
cp->flags &= !kTypeTMask;
cp->flags |= kIntCsvTFl;
return kOkCsvRC;
}
cmCsvRC_t cmCsvSetCellUInt( cmCsvH_t h, unsigned row, unsigned col, unsigned v )
{
cmCsvCell_t* cp;
unsigned symId;
if((cp = _cmCsvCellPtr(h,row,col)) == NULL )
return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
if((symId = cmCsvInsertSymUInt(h,v)) == cmInvalidId )
return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
cp->symId = symId;
cp->flags &= !kTypeTMask;
cp->flags |= kIntCsvTFl;
return kOkCsvRC;
}
cmCsvRC_t cmCsvSetCellHex( cmCsvH_t h, unsigned row, unsigned col, unsigned v )
{
cmCsvCell_t* cp;
unsigned symId;
if((cp = _cmCsvCellPtr(h,row,col)) == NULL )
return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
if((symId = cmCsvInsertSymHex(h,v)) == cmInvalidId )
return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
cp->symId = symId;
cp->flags &= !kTypeTMask;
cp->flags |= kIntCsvTFl;
return kOkCsvRC;
}
cmCsvRC_t cmCsvSetCellFloat( cmCsvH_t h, unsigned row, unsigned col, float v )
{
cmCsvCell_t* cp;
unsigned symId;
if((cp = _cmCsvCellPtr(h,row,col)) == NULL )
return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
if((symId = cmCsvInsertSymFloat(h,v)) == cmInvalidId )
return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
cp->symId = symId;
cp->flags &= !kTypeTMask;
cp->flags |= kRealCsvTFl;
return kOkCsvRC;
}
cmCsvRC_t cmCsvSetCellDouble( cmCsvH_t h, unsigned row, unsigned col, double v )
{
cmCsvCell_t* cp;
unsigned symId;
if((cp = _cmCsvCellPtr(h,row,col)) == NULL )
return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
if((symId = cmCsvInsertSymDouble(h,v)) == cmInvalidId )
return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
cp->symId = symId;
cp->flags &= !kTypeTMask;
cp->flags |= kRealCsvTFl;
return kOkCsvRC;
}
cmCsvRC_t cmCsvInsertRowBefore( cmCsvH_t h, unsigned row, cmCsvCell_t** cellPtrPtr, unsigned symId, unsigned flags, unsigned lexTId )
{
cmCsvRC_t rc = kOkCsvRC;
cmCsv_t* p = _cmCsvHandleToPtr(h);
cmCsvBind_t* bp = p->bindPtr;
cmCsvBind_t* nbp = NULL;
unsigned i = 0;
if( cellPtrPtr != NULL )
*cellPtrPtr = NULL;
// allocate the binder record
if((nbp = cmLHeapAllocZ( p->heapH, sizeof(cmCsvBind_t))) == NULL )
return _cmCsvError(p,kMemAllocErrCsvRC,"Binding record allocation failed row %i column %i.",row,0);
// if a new first row is being inserted
if( row == 0 )
{
bp = p->bindPtr;
nbp->linkPtr = p->bindPtr;
p->bindPtr = nbp;
}
else
{
bp = p->bindPtr;
// iterate to the row before the new row
for(i=0; bp != NULL; ++i )
{
if( i == (row-1) || bp->linkPtr == NULL )
break;
bp = bp->linkPtr;
}
assert( bp != NULL );
nbp->linkPtr = bp->linkPtr;
bp->linkPtr = nbp;
bp = bp->linkPtr;
}
// update the row numbers in all cells below the new row
while( bp != NULL )
{
cmCsvCell_t* cp = bp->rowPtr;
while( cp != NULL )
{
cp->row += 1;
cp = cp->rowPtr;
}
bp = bp->linkPtr;
}
// allocate the first cell in the new row
if(cellPtrPtr != NULL && symId != cmInvalidId )
{
if((rc = _cmCsvAllocCell(p, symId, flags, row, 0, cellPtrPtr, lexTId )) != kOkCsvRC )
return rc;
nbp->rowPtr = *cellPtrPtr;
}
return rc;
}
cmCsvRC_t cmCsvAppendRow( cmCsvH_t h, cmCsvCell_t** cellPtrPtr, unsigned symId, unsigned flags, unsigned lexTId )
{
cmCsvRC_t rc = kOkCsvRC;
cmCsv_t* p = _cmCsvHandleToPtr(h);
cmCsvBind_t* nbp = NULL;
unsigned newRowIdx = cmInvalidIdx;
if( cellPtrPtr != NULL )
*cellPtrPtr = NULL;
if((rc = _cmCsvAppendNewBindRecd(p,&nbp,&newRowIdx)) != kOkCsvRC )
return rc;
// allocate the first cell in the new row
if(cellPtrPtr != NULL && symId != cmInvalidId )
{
if((rc = _cmCsvAllocCell(p, symId, flags, newRowIdx, 0, cellPtrPtr, lexTId )) != kOkCsvRC )
return rc;
nbp->rowPtr = *cellPtrPtr;
}
return rc;
}
cmCsvRC_t cmCsvInsertColAfter( cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, unsigned symId, unsigned flags, unsigned lexTId )
{
cmCsvRC_t rc = kOkCsvRC;
cmCsvCell_t* ncp = NULL;
cmCsvCell_t* cp = NULL;
cmCsv_t* p = _cmCsvHandleToPtr(h);
unsigned col;
if(cellPtrPtr != NULL )
*cellPtrPtr = NULL;
// allocate the new cell
if((rc = _cmCsvAllocCell(p, symId, flags, leftCellPtr->row, leftCellPtr->col+1, &ncp, lexTId )) != kOkCsvRC )
return rc;
// update the col values of cells to right of new cell
cp = leftCellPtr->rowPtr;
col = leftCellPtr->col + 1;
for(; cp != NULL; ++col )
{
// don't update any col numbers after a blank (missing) column
if( cp->col == col )
cp->col += 1;
cp = cp->rowPtr;
}
// link in the new cell
ncp->rowPtr = leftCellPtr->rowPtr;
leftCellPtr->rowPtr = ncp;
if(cellPtrPtr != NULL )
*cellPtrPtr = ncp;
return rc;
}
cmCsvRC_t cmCsvInsertTextColAfter( cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, const char* text, unsigned lexTId )
{
cmCsvRC_t rc;
cmCsvCell_t* ncp;
if( cellPtrPtr != NULL )
*cellPtrPtr = NULL;
if((rc = cmCsvInsertColAfter(h, leftCellPtr, &ncp, cmInvalidId, 0, lexTId )) == kOkCsvRC )
if((rc = cmCsvSetCellText(h, ncp->row, ncp->col, text )) == kOkCsvRC )
if( cellPtrPtr != NULL )
*cellPtrPtr = ncp;
return rc;
}
cmCsvRC_t cmCsvInsertIntColAfter( cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, int val, unsigned lexTId )
{
cmCsvRC_t rc;
cmCsvCell_t* ncp;
if( cellPtrPtr != NULL )
*cellPtrPtr = NULL;
if((rc = cmCsvInsertColAfter(h, leftCellPtr, &ncp, cmInvalidId, 0, lexTId )) == kOkCsvRC )
if((rc = cmCsvSetCellInt(h, ncp->row, ncp->col, val )) == kOkCsvRC )
if( cellPtrPtr != NULL )
*cellPtrPtr = ncp;
return rc;
}
cmCsvRC_t cmCsvInsertUIntColAfter( cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, unsigned val, unsigned lexTId )
{
cmCsvRC_t rc;
cmCsvCell_t* ncp;
if( cellPtrPtr != NULL )
*cellPtrPtr = NULL;
if((rc = cmCsvInsertColAfter(h, leftCellPtr, &ncp, cmInvalidId, 0, lexTId )) == kOkCsvRC )
if((rc = cmCsvSetCellUInt(h, ncp->row, ncp->col, val )) == kOkCsvRC )
if( cellPtrPtr != NULL )
*cellPtrPtr = ncp;
return rc;
}
cmCsvRC_t cmCsvInsertHexColAfter( cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, unsigned val, unsigned lexTId )
{
cmCsvRC_t rc;
cmCsvCell_t* ncp;
if( cellPtrPtr != NULL )
*cellPtrPtr = NULL;
if((rc = cmCsvInsertColAfter(h, leftCellPtr, &ncp, cmInvalidId, 0, lexTId )) == kOkCsvRC )
if((rc = cmCsvSetCellHex(h, ncp->row, ncp->col, val )) == kOkCsvRC )
if( cellPtrPtr != NULL )
*cellPtrPtr = ncp;
return rc;
}
cmCsvRC_t cmCsvInsertFloatColAfter( cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, float val, unsigned lexTId )
{
cmCsvRC_t rc;
cmCsvCell_t* ncp;
if( cellPtrPtr != NULL )
*cellPtrPtr = NULL;
if((rc = cmCsvInsertColAfter(h, leftCellPtr, &ncp, cmInvalidId, 0, lexTId )) == kOkCsvRC )
if((rc = cmCsvSetCellFloat(h, ncp->row, ncp->col, val )) == kOkCsvRC )
if( cellPtrPtr != NULL )
*cellPtrPtr = ncp;
return rc;
}
cmCsvRC_t cmCsvInsertDoubleColAfter( cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, double val, unsigned lexTId )
{
cmCsvRC_t rc;
cmCsvCell_t* ncp;
if( cellPtrPtr != NULL )
*cellPtrPtr = NULL;
if((rc = cmCsvInsertColAfter(h, leftCellPtr, &ncp, cmInvalidId, 0, lexTId )) == kOkCsvRC )
if((rc = cmCsvSetCellDouble(h, ncp->row, ncp->col, val )) == kOkCsvRC )
if( cellPtrPtr != NULL )
*cellPtrPtr = ncp;
return rc;
}
cmCsvRC_t cmCsvWrite( cmCsvH_t h, const char* fn )
{
FILE* fp = NULL;
cmCsvRC_t rc = kOkCsvRC;
cmCsv_t* p = _cmCsvHandleToPtr(h);
cmCsvBind_t* bp = p->bindPtr;
if((fp = fopen(fn,"wt")) == NULL )
return _cmCsvError(p,kFileCreateErrCsvRC,"Unable to create the output CSV file:'%s'.",fn);
bp = p->bindPtr;
// for each row
while( bp != NULL )
{
cmCsvCell_t* cp = bp->rowPtr;
unsigned col = 0;
// for each column
for(; cp != NULL; ++col )
{
// skip blank columns
if( cp->col == col )
{
const char* tp;
if((tp = cmHashTblStr(p->htH,cp->symId)) == NULL )
return _cmCsvError(p,kHashTblErrCsvRC,"Unable to locate the symbol text for cell at row:%i col:%i.",cp->row,cp->col);
if( cmIsFlag(cp->flags,kTextTMask) )
fprintf(fp,"\"");
fputs(tp,fp);
if( cmIsFlag(cp->flags,kTextTMask) )
fprintf(fp,"\"");
cp = cp->rowPtr;
}
if( cp == NULL )
fprintf(fp,"\n"); // end of row
else
fprintf(fp,","); // between columns
}
bp = bp->linkPtr;
}
fclose(fp);
return rc;
}
cmCsvRC_t cmCsvPrint( cmCsvH_t h, unsigned rowCnt )
{
cmCsv_t* p = _cmCsvHandleToPtr(h);
cmCsvBind_t* bp = p->bindPtr;
unsigned i;
for(i=0; bp!=NULL && i<rowCnt; ++i,bp=bp->linkPtr)
{
cmCsvCell_t* cp = bp->rowPtr;
unsigned j;
for(j=0; cp!=NULL; ++j)
{
if( cp->col == j )
{
const char* tp;
if((tp = cmHashTblStr(p->htH,cp->symId)) == NULL )
_cmCsvError(p,kHashTblErrCsvRC,"The text associated with the symbol '%i' was not found.",cp->symId);
fputs(tp,stdin);
}
cp=cp->rowPtr;
if( cp == NULL )
printf("\n");
else
printf(",");
}
}
return kOkCsvRC;
}