libcm is a C development framework with an emphasis on audio signal processing applications.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cmCsv.c 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146
  1. #include "cmPrefix.h"
  2. #include "cmGlobal.h"
  3. #include "cmRpt.h"
  4. #include "cmErr.h"
  5. #include "cmCtx.h"
  6. #include "cmMem.h"
  7. #include "cmMallocDebug.h"
  8. #include "cmLex.h"
  9. #include "cmLinkedHeap.h"
  10. #include "cmSymTbl.h"
  11. #include "cmCsv.h"
  12. #include "cmText.h"
  13. enum
  14. {
  15. kCommaLexTId = kUserLexTId+1,
  16. kMaxLexTId // always last in the lex id list
  17. };
  18. typedef struct
  19. {
  20. const char* text;
  21. unsigned id;
  22. } cmCsvToken_t;
  23. cmCsvToken_t _cmCsvTokenArray[] =
  24. {
  25. { ",", kCommaLexTId },
  26. { "", kErrorLexTId }
  27. };
  28. // The binder linked list contains a list of records which tracks the first
  29. // (left-most) non-blank column in each row.
  30. typedef struct cmCsvBind_str
  31. {
  32. struct cmCsvBind_str* linkPtr;
  33. cmCsvCell_t* rowPtr;
  34. } cmCsvBind_t;
  35. typedef struct cmCsvUdef_str
  36. {
  37. struct cmCsvUdef_str* linkPtr;
  38. unsigned id;
  39. } cmCsvUdef_t;
  40. typedef struct
  41. {
  42. cmErr_t err;
  43. void* rptDataPtr; //
  44. cmLexH lexH; // parsing lexer
  45. cmSymTblH_t symTblH; // all XML identifiers and data is stored as a symbol in this table
  46. cmLHeapH_t heapH;
  47. cmCsvBind_t* bindPtr; // base of the binder linked list
  48. cmCsvCell_t* curRowPtr; // used by the parser to track the row being filled
  49. cmCsvUdef_t* udefList;
  50. } cmCsv_t;
  51. cmCsvH_t cmCsvNullHandle = { NULL };
  52. cmCsv_t* _cmCsvHandleToPtr( cmCsvH_t h )
  53. {
  54. cmCsv_t* p = (cmCsv_t*)h.h;
  55. assert( p != NULL );
  56. cmErrClearRC(&p->err);
  57. return p;
  58. }
  59. cmCsvRC_t _cmCsvVError( cmCsv_t* p, cmCsvRC_t rc, const char* fmt, va_list vl )
  60. { return cmErrVMsg(&p->err,rc,fmt,vl); }
  61. cmCsvRC_t _cmCsvError( cmCsv_t* p, cmCsvRC_t rc, const char* fmt, ... )
  62. {
  63. va_list vl;
  64. va_start(vl,fmt);
  65. rc = _cmCsvVError(p,rc,fmt,vl);
  66. va_end(vl);
  67. return rc;
  68. }
  69. cmCsvRC_t cmCsvInitialize( cmCsvH_t *hp, cmCtx_t* ctx )
  70. {
  71. cmCsvRC_t rc;
  72. cmCsv_t* p = NULL;
  73. unsigned i;
  74. if((rc = cmCsvFinalize(hp)) != kOkCsvRC )
  75. return rc;
  76. // create the base object
  77. p = cmMemAllocZ( cmCsv_t, 1 );
  78. assert(p != NULL);
  79. cmErrSetup(&p->err,&ctx->rpt,"CSV");
  80. // create the symbol table
  81. if( cmSymTblIsValid(p->symTblH = cmSymTblCreate(cmSymTblNullHandle,0,ctx)) == false )
  82. {
  83. rc = _cmCsvError(p,kSymTblErrCsvRC,"Symbol table creation failed.");
  84. goto errLabel;
  85. }
  86. // allocate the linked heap mgr
  87. if( cmLHeapIsValid(p->heapH = cmLHeapCreate(1024,ctx)) == false )
  88. {
  89. rc = _cmCsvError(p,kMemAllocErrCsvRC,"Linked heap object allocation failed.");
  90. goto errLabel;
  91. }
  92. // allocate the lexer
  93. if(cmLexIsValid(p->lexH = cmLexInit(NULL,0,0,&ctx->rpt)) == false )
  94. {
  95. rc = _cmCsvError(p,kLexErrCsvRC,"Lex allocation failed.");
  96. goto errLabel;
  97. }
  98. // register CSV specific tokens with the lexer
  99. for(i=0; _cmCsvTokenArray[i].id != kErrorLexTId; ++i)
  100. {
  101. cmRC_t lexRC;
  102. if( (lexRC = cmLexRegisterToken(p->lexH, _cmCsvTokenArray[i].id, _cmCsvTokenArray[i].text )) != kOkLexRC )
  103. {
  104. rc = _cmCsvError(p,kLexErrCsvRC,"Lex token registration failed for:'%s'.\nLexer Error:%s",_cmCsvTokenArray[i].text, cmLexRcToMsg(lexRC) );
  105. goto errLabel;
  106. }
  107. }
  108. hp->h = p;
  109. return kOkCsvRC;
  110. errLabel:
  111. cmMemPtrFree(&p);
  112. if( cmLHeapIsValid(p->heapH) )
  113. cmLHeapDestroy(&p->heapH);
  114. if( cmLexIsValid(p->lexH) )
  115. cmLexFinal(&p->lexH);
  116. return rc;
  117. }
  118. cmCsvRC_t cmCsvFinalize( cmCsvH_t *hp )
  119. {
  120. cmRC_t lexRC;
  121. if( hp == NULL || hp->h == NULL )
  122. return kOkCsvRC;
  123. cmCsv_t* p = _cmCsvHandleToPtr(*hp);
  124. // free the internal heap object
  125. cmLHeapDestroy( &p->heapH );
  126. // free the lexer
  127. if((lexRC = cmLexFinal(&p->lexH)) != kOkLexRC )
  128. return _cmCsvError(p,kLexErrCsvRC,"Lexer finalization failed.\nLexer Error:%s",cmLexRcToMsg(lexRC));
  129. // free the symbol table
  130. cmSymTblDestroy(&p->symTblH);
  131. // free the handle
  132. cmMemPtrFree(&hp->h);
  133. return kOkCsvRC;
  134. }
  135. cmCsvRC_t cmCsvInitializeFromFile( cmCsvH_t *hp, const char* fn, unsigned maxRowCnt, cmCtx_t* ctx )
  136. {
  137. cmCsvRC_t rc;
  138. if((rc = cmCsvInitialize(hp,ctx)) != kOkCsvRC )
  139. return rc;
  140. return cmCsvParseFile( *hp, fn, maxRowCnt);
  141. }
  142. bool cmCsvIsValid( cmCsvH_t h)
  143. { return h.h != NULL; }
  144. cmCsvRC_t cmCsvLastRC( cmCsvH_t h )
  145. {
  146. cmCsv_t* p = _cmCsvHandleToPtr(h);
  147. return cmErrLastRC(&p->err);
  148. }
  149. void cmCsvClearRC( cmCsvH_t h )
  150. {
  151. cmCsv_t* p = _cmCsvHandleToPtr(h);
  152. cmErrClearRC(&p->err);
  153. }
  154. cmCsvRC_t cmCsvParseFile( cmCsvH_t h, const char* fn, unsigned maxRowCnt )
  155. {
  156. cmCsvRC_t rc = kOkCsvRC;
  157. FILE* fp = NULL;
  158. cmCsv_t* p = _cmCsvHandleToPtr(h);
  159. unsigned n = 0;
  160. char* textBuf = NULL;
  161. assert( fn != NULL && p != NULL );
  162. // open the file
  163. if((fp = fopen(fn,"rb")) == NULL )
  164. return _cmCsvError(p,kFileOpenErrCsvRC,"Unable to open the file:'%s'.",fn);
  165. // seek to the end
  166. if( fseek(fp,0,SEEK_END) != 0 )
  167. {
  168. rc= _cmCsvError(p,kFileSeekErrCsvRC,"Unable to seek to the end of '%s'.",fn);
  169. goto errLabel;
  170. }
  171. // get the length of the file
  172. if( (n=ftell(fp)) == 0 )
  173. {
  174. rc = _cmCsvError(p,kFileOpenErrCsvRC,"The file '%s' appears to be empty.",fn);
  175. goto errLabel;
  176. }
  177. // rewind the file
  178. if( fseek(fp,0,SEEK_SET) != 0 )
  179. {
  180. rc = _cmCsvError(p,kFileSeekErrCsvRC,"Unable to seek to the beginning of '%s'.",fn);
  181. goto errLabel;
  182. }
  183. // allocate the text buffer
  184. if((textBuf = cmMemAllocZ( char, n+1)) == NULL )
  185. {
  186. rc = _cmCsvError(p,kMemAllocErrCsvRC,"Unable to allocate the text file buffer for:'%s'.",fn);
  187. goto errLabel;
  188. }
  189. // read the file into the text buffer
  190. if( fread(textBuf,n,1,fp) != 1 )
  191. {
  192. rc = _cmCsvError(p,kFileReadErrCsvRC,"File read failed on:'%s'.",fn);
  193. goto errLabel;
  194. }
  195. rc = cmCsvParse(h,textBuf,n,maxRowCnt);
  196. errLabel:
  197. // close the file
  198. if( fclose(fp) != 0 )
  199. {
  200. rc = _cmCsvError(p,kFileCloseErrCsvRC,"File close failed on:'%s'.",fn);
  201. goto errLabel;
  202. }
  203. // free the buffer
  204. if( textBuf != NULL )
  205. cmMemFree(textBuf);
  206. return rc;
  207. }
  208. cmCsvRC_t _cmCsvAppendNewBindRecd( cmCsv_t* p, cmCsvBind_t** newBindPtrPtr, unsigned* newRowIdxPtr )
  209. {
  210. cmCsvBind_t* nbp;
  211. cmCsvBind_t* bp = p->bindPtr;
  212. unsigned newRowIdx = 0;
  213. if( newBindPtrPtr != NULL )
  214. *newBindPtrPtr = NULL;
  215. if( newRowIdxPtr != NULL )
  216. *newRowIdxPtr = cmInvalidIdx;
  217. // allocate the binder record
  218. if((nbp = cmLHeapAllocZ( p->heapH, sizeof(cmCsvBind_t))) == NULL )
  219. return _cmCsvError(p,kMemAllocErrCsvRC,"Binding record allocation failed.");
  220. // if this is the first binder record
  221. if( p->bindPtr == NULL )
  222. {
  223. p->bindPtr = nbp;
  224. bp = nbp;
  225. }
  226. else
  227. {
  228. newRowIdx = 1;
  229. // iterate to the bottom of the binding
  230. while( bp->linkPtr != NULL )
  231. {
  232. bp = bp->linkPtr;
  233. ++newRowIdx;
  234. }
  235. bp->linkPtr = nbp;
  236. }
  237. if( newBindPtrPtr != NULL )
  238. *newBindPtrPtr = nbp;
  239. if( newRowIdxPtr != NULL )
  240. *newRowIdxPtr = newRowIdx;
  241. return kOkCsvRC;
  242. }
  243. cmCsvRC_t _cmCsvCreateBind( cmCsv_t* p, cmCsvCell_t* cp, const char* tokenText, unsigned lexRow, unsigned lexCol )
  244. {
  245. cmCsvRC_t rc;
  246. cmCsvBind_t* nbp;
  247. if((rc = _cmCsvAppendNewBindRecd(p,&nbp,NULL)) != kOkCsvRC )
  248. return _cmCsvError(p,kMemAllocErrCsvRC,"Binding record allocation failed for '%s' on line %i column %i.",tokenText,lexRow,lexCol);
  249. nbp->rowPtr = cp;
  250. return rc;
  251. }
  252. cmCsvRC_t _cmCsvAllocCell( cmCsv_t* p, unsigned symId, unsigned flags, unsigned cellRow, unsigned cellCol, cmCsvCell_t** cpp, unsigned lexTId )
  253. {
  254. cmCsvCell_t* cp;
  255. // allocate cell memory
  256. if(( cp = cmLHeapAllocZ(p->heapH, sizeof(cmCsvCell_t) )) == NULL )
  257. return _cmCsvError(p,kMemAllocErrCsvRC,"Cell allocation failed. for row: %i column %i.",cellRow,cellCol);
  258. cp->row = cellRow;
  259. cp->col = cellCol;
  260. cp->symId = symId;
  261. cp->flags = flags;
  262. cp->lexTId= lexTId;
  263. *cpp = cp;
  264. return kOkCsvRC;
  265. }
  266. cmCsvRC_t _cmCsvCreateCell( cmCsv_t* p, const char* tokenText, unsigned flags, unsigned cellRow, unsigned cellCol, unsigned lexTId )
  267. {
  268. unsigned symId = cmInvalidId;
  269. cmCsvCell_t* cp = NULL;
  270. unsigned lexRow = cmLexCurrentLineNumber(p->lexH);
  271. unsigned lexCol = cmLexCurrentColumnNumber(p->lexH);
  272. cmCsvRC_t rc = kOkCsvRC;
  273. // register the token text as a symbol
  274. if((symId = cmSymTblRegisterSymbol(p->symTblH,tokenText)) == cmInvalidId )
  275. return _cmCsvError(p,kSymTblErrCsvRC,"Symbol registration failed. for '%s' on line %i column %i.",tokenText,lexRow,lexCol);
  276. // allocate a cell
  277. if((rc = _cmCsvAllocCell(p,symId,flags,cellRow,cellCol,&cp,lexTId)) != kOkCsvRC )
  278. return rc;
  279. // if this is the first cell in the row
  280. if( p->curRowPtr == NULL )
  281. {
  282. // link in a new binding record
  283. if((rc = _cmCsvCreateBind( p, cp,tokenText,lexRow,lexCol)) != kOkCsvRC )
  284. return rc; // this return will result in a leak from the lheap but it is a fatal error so ignore it
  285. // update the current row ptr
  286. p->curRowPtr = cp;
  287. }
  288. else
  289. {
  290. // iterate to the end of the row
  291. cmCsvCell_t* rp = p->curRowPtr;
  292. while( rp->rowPtr != NULL )
  293. rp = rp->rowPtr;
  294. rp->rowPtr = cp;
  295. }
  296. return rc;
  297. }
  298. const cmCsvUdef_t* _cmCsvFindUdef( cmCsv_t* p, unsigned id )
  299. {
  300. const cmCsvUdef_t* u = p->udefList;
  301. while( u != NULL )
  302. {
  303. if( u->id == id )
  304. return u;
  305. u = u->linkPtr;
  306. }
  307. return NULL;
  308. }
  309. cmCsvRC_t _cmCsvRegUdef( cmCsv_t* p, unsigned id )
  310. {
  311. if( _cmCsvFindUdef(p,id) != NULL )
  312. return _cmCsvError(p,kDuplicateLexCsvId,"%i has already been used as a user defined id.",id);
  313. cmCsvUdef_t* u = cmLhAllocZ(p->heapH,cmCsvUdef_t,1);
  314. u->id = id;
  315. u->linkPtr = p->udefList;
  316. p->udefList = u;
  317. return kOkCsvRC;
  318. }
  319. cmCsvRC_t cmCsvLexRegisterToken( cmCsvH_t h, unsigned id, const cmChar_t* token )
  320. {
  321. cmCsv_t* p = _cmCsvHandleToPtr(h);
  322. if(cmLexRegisterToken(p->lexH,id,token) != kOkLexRC )
  323. return _cmCsvError(p, kLexErrCsvRC,"Error registering user defined token '%s'.",cmStringNullGuard(token));
  324. return _cmCsvRegUdef(p,id);
  325. }
  326. cmCsvRC_t cmCsvLexRegisterMatcher( cmCsvH_t h, unsigned id, cmLexUserMatcherPtr_t matchFunc )
  327. {
  328. cmCsv_t* p = _cmCsvHandleToPtr(h);
  329. if( cmLexRegisterMatcher(p->lexH,id,matchFunc) != kOkLexRC )
  330. return _cmCsvError(p, kLexErrCsvRC,"Error registering user defined matching function.");
  331. return _cmCsvRegUdef(p,id);
  332. }
  333. unsigned cmCsvLexNextAvailId( cmCsvH_t h )
  334. { return kMaxLexTId; }
  335. cmCsvRC_t cmCsvParse( cmCsvH_t h, const char* buf, unsigned bufCharCnt, unsigned maxRowCnt )
  336. {
  337. cmCsvRC_t rc = kOkCsvRC;
  338. unsigned prvLexRow = 1;
  339. unsigned csvRowIdx = 0;
  340. unsigned csvColIdx = 0;
  341. unsigned lexTId = kErrorLexTId; // cur lex token type id
  342. cmCsv_t* p = _cmCsvHandleToPtr(h);
  343. // assign the text buffer and reset the lexer
  344. if((rc = cmLexSetTextBuffer( p->lexH, buf, bufCharCnt )) != kOkLexRC )
  345. return _cmCsvError( p, kLexErrCsvRC, "Error setting lexer buffer.\nLexer Error:%s",cmLexRcToMsg(rc));
  346. // get the next token
  347. while( (lexTId = cmLexGetNextToken( p->lexH )) != kErrorLexTId && (lexTId != kEofLexTId ) && (rc==kOkCsvRC) && (maxRowCnt==0 || csvRowIdx<maxRowCnt))
  348. {
  349. unsigned flags = 0;
  350. unsigned curLexRow = cmLexCurrentLineNumber(p->lexH);
  351. // if we are starting a new row
  352. if( curLexRow != prvLexRow )
  353. {
  354. prvLexRow = curLexRow;
  355. ++csvRowIdx;
  356. csvColIdx = 0;
  357. p->curRowPtr = NULL; // force a new binding record
  358. }
  359. // copy the token text in a buffer
  360. unsigned n = cmLexTokenCharCount(p->lexH);
  361. char buf[n+1];
  362. strncpy(buf, cmLexTokenText(p->lexH), n );
  363. buf[n]=0;
  364. // do cell data type specific processing
  365. switch( lexTId )
  366. {
  367. case kCommaLexTId:
  368. ++csvColIdx;
  369. break;
  370. case kRealLexTId: flags = kRealCsvTFl; break;
  371. case kIntLexTId: flags = kIntCsvTFl; break;
  372. case kHexLexTId: flags = kHexCsvTFl; break;
  373. case kIdentLexTId:flags = kIdentCsvTFl; break;
  374. case kQStrLexTId: flags = kStrCsvTFl; break;
  375. default:
  376. {
  377. const cmCsvUdef_t* u;
  378. if((u = _cmCsvFindUdef(p,lexTId)) == NULL )
  379. rc = _cmCsvError(p, kSyntaxErrCsvRC, "Unrecognized token type: '%s' for token: '%s'.",cmLexIdToLabel(p->lexH,lexTId),buf);
  380. else
  381. flags = kStrCsvTFl | kUdefCsvTFl;
  382. }
  383. }
  384. // if no error occurred and the token is not a comma and the cell is not empty
  385. if( rc == kOkCsvRC && lexTId != kCommaLexTId && strlen(buf) > 0 )
  386. rc = _cmCsvCreateCell( p, buf,flags, csvRowIdx, csvColIdx, lexTId );
  387. }
  388. return rc;
  389. }
  390. unsigned cmCsvRowCount( cmCsvH_t h )
  391. {
  392. cmCsv_t* p = _cmCsvHandleToPtr(h);
  393. unsigned rowCnt = 0;
  394. cmCsvBind_t* bp = p->bindPtr;
  395. for(; bp != NULL; ++rowCnt )
  396. bp = bp->linkPtr;
  397. return rowCnt;
  398. }
  399. cmCsvCell_t* cmCsvRowPtr( cmCsvH_t h, unsigned row )
  400. {
  401. cmCsv_t* p = _cmCsvHandleToPtr(h);
  402. cmCsvBind_t* bp = p->bindPtr;
  403. while( bp != NULL )
  404. {
  405. // note: bp->rowPtr of blank rows will be NULL
  406. if( bp->rowPtr!=NULL && bp->rowPtr->row == row )
  407. return bp->rowPtr;
  408. bp = bp->linkPtr;
  409. }
  410. return NULL;
  411. }
  412. cmCsvCell_t* cmCsvCellPtr( cmCsvH_t h, unsigned row, unsigned col )
  413. {
  414. cmCsvCell_t* cp;
  415. if((cp = cmCsvRowPtr(h,row)) == NULL )
  416. return NULL;
  417. while( cp != NULL )
  418. {
  419. if( cp->col == col )
  420. return cp;
  421. cp = cp->rowPtr;
  422. }
  423. return NULL;
  424. }
  425. cmCsvCell_t* _cmCsvCellPtr( cmCsvH_t h, unsigned row, unsigned col )
  426. {
  427. cmCsvCell_t* cp;
  428. if((cp = cmCsvCellPtr(h,row,col)) == NULL )
  429. _cmCsvError(_cmCsvHandleToPtr(h),kCellNotFoundCsvRC,"Cell at row:%i col:%i not found.",row,col);
  430. return cp;
  431. }
  432. const char* cmCsvCellSymText( cmCsvH_t h, unsigned symId )
  433. {
  434. cmCsv_t* p = _cmCsvHandleToPtr(h);
  435. const char* cp;
  436. if((cp = cmSymTblLabel(p->symTblH,symId)) == NULL )
  437. _cmCsvError(p,kSymTblErrCsvRC,"The text associated with the symbol '%i' was not found.",symId);
  438. return cp;
  439. }
  440. cmCsvRC_t cmCsvCellSymInt( cmCsvH_t h, unsigned symId, int* vp )
  441. {
  442. const char* cp;
  443. cmCsv_t* p = _cmCsvHandleToPtr(h);
  444. if((cp = cmCsvCellSymText(h,symId)) == NULL )
  445. return kSymTblErrCsvRC;
  446. if( cmTextToInt(cp,vp,&p->err) != kOkTxRC )
  447. return _cmCsvError(p,kDataCvtErrCsvRC,"CSV text to int value failed.");
  448. return kOkCsvRC;
  449. }
  450. cmCsvRC_t cmCsvCellSymUInt( cmCsvH_t h, unsigned symId, unsigned* vp )
  451. {
  452. const char* cp;
  453. cmCsv_t* p = _cmCsvHandleToPtr(h);
  454. if((cp = cmCsvCellSymText(h,symId)) == NULL )
  455. return kSymTblErrCsvRC;
  456. if( cmTextToUInt(cp,vp,&p->err) != kOkTxRC )
  457. return _cmCsvError(p,kDataCvtErrCsvRC,"CSV text to uint value failed.");
  458. return kOkCsvRC;
  459. }
  460. cmCsvRC_t cmCsvCellSymFloat( cmCsvH_t h, unsigned symId, float* vp )
  461. {
  462. const char* cp;
  463. cmCsv_t* p = _cmCsvHandleToPtr(h);
  464. if((cp = cmCsvCellSymText(h,symId)) == NULL )
  465. return kSymTblErrCsvRC;
  466. if( cmTextToFloat(cp,vp,&p->err) != kOkTxRC )
  467. return _cmCsvError(p,kDataCvtErrCsvRC,"CSV text to float value failed.");
  468. return kOkCsvRC;
  469. }
  470. cmCsvRC_t cmCsvCellSymDouble( cmCsvH_t h, unsigned symId, double* vp )
  471. {
  472. const char* cp;
  473. cmCsv_t* p = _cmCsvHandleToPtr(h);
  474. if((cp = cmCsvCellSymText(h,symId)) == NULL )
  475. return kSymTblErrCsvRC;
  476. if( cmTextToDouble(cp,vp,&p->err) != kOkTxRC )
  477. return _cmCsvError(p,kDataCvtErrCsvRC,"CSV text to double value failed.");
  478. return kOkCsvRC;
  479. }
  480. const char* cmCsvCellText( cmCsvH_t h, unsigned row, unsigned col )
  481. {
  482. cmCsvCell_t* cp;
  483. if((cp = cmCsvCellPtr(h,row,col)) == NULL )
  484. return NULL;
  485. return cmCsvCellSymText(h,cp->symId);
  486. }
  487. int cmCsvCellInt( cmCsvH_t h, unsigned row, unsigned col )
  488. {
  489. cmCsvCell_t* cp;
  490. int v;
  491. if((cp = cmCsvCellPtr(h,row,col)) == NULL )
  492. return INT_MAX;
  493. if(cmCsvCellSymInt(h,cp->symId,&v) != kOkCsvRC )
  494. return INT_MAX;
  495. return v;
  496. }
  497. unsigned cmCsvCellUInt( cmCsvH_t h, unsigned row, unsigned col )
  498. {
  499. cmCsvCell_t* cp;
  500. unsigned v;
  501. if((cp = cmCsvCellPtr(h,row,col)) == NULL )
  502. return UINT_MAX;
  503. if(cmCsvCellSymUInt(h,cp->symId,&v) != kOkCsvRC )
  504. return UINT_MAX;
  505. return v;
  506. }
  507. float cmCsvCellFloat( cmCsvH_t h, unsigned row, unsigned col )
  508. {
  509. cmCsvCell_t* cp;
  510. float v;
  511. if((cp = cmCsvCellPtr(h,row,col)) == NULL )
  512. return FLT_MAX;
  513. if(cmCsvCellSymFloat(h,cp->symId,&v) != kOkCsvRC )
  514. return FLT_MAX;
  515. return v;
  516. }
  517. double cmCsvCellDouble(cmCsvH_t h, unsigned row, unsigned col )
  518. {
  519. cmCsvCell_t* cp;
  520. double v;
  521. if((cp = cmCsvCellPtr(h,row,col)) == NULL )
  522. return DBL_MAX;
  523. if(cmCsvCellSymDouble(h,cp->symId,&v) != kOkCsvRC )
  524. return DBL_MAX;
  525. return v;
  526. }
  527. unsigned cmCsvInsertSymText( cmCsvH_t h, const char* text )
  528. {
  529. cmCsv_t* p = _cmCsvHandleToPtr(h);
  530. unsigned symId;
  531. if((symId = cmSymTblRegisterSymbol(p->symTblH,text)) == cmInvalidId )
  532. _cmCsvError(p,kSymTblErrCsvRC,"'%s' could not be inserted into the symbol table.",text);
  533. return symId;
  534. }
  535. unsigned cmCsvInsertSymInt( cmCsvH_t h, int v )
  536. {
  537. const char* fmt = "%i";
  538. unsigned n = snprintf(NULL,0,fmt,v)+1;
  539. char buf[n];
  540. buf[0]= 0;
  541. if( snprintf(buf,n,fmt,v) == n-1 )
  542. return cmCsvInsertSymText(h,buf);
  543. _cmCsvError(_cmCsvHandleToPtr(h),kDataCvtErrCsvRC,"The integer %i could not be converted to text.",v);
  544. return cmInvalidId;
  545. }
  546. unsigned cmCsvInsertSymUInt( cmCsvH_t h, unsigned v )
  547. {
  548. const char* fmt = "%i";
  549. unsigned n = snprintf(NULL,0,fmt,v)+1;
  550. char buf[n];
  551. buf[0]= 0;
  552. if( snprintf(buf,n,fmt,v) == n-1 )
  553. return cmCsvInsertSymText(h,buf);
  554. _cmCsvError(_cmCsvHandleToPtr(h),kDataCvtErrCsvRC,"The unsigned int %i could not be converted to text.",v);
  555. return cmInvalidId;
  556. }
  557. unsigned cmCsvInsertSymFloat( cmCsvH_t h, float v )
  558. {
  559. const char* fmt = "%f";
  560. unsigned n = snprintf(NULL,0,fmt,v)+1;
  561. char buf[n];
  562. buf[0] = 0;
  563. if( snprintf(buf,n,fmt,v) == n-1 )
  564. return cmCsvInsertSymText(h,buf);
  565. _cmCsvError(_cmCsvHandleToPtr(h),kDataCvtErrCsvRC,"The float %f could not be converted to text.",v);
  566. return cmInvalidId;
  567. }
  568. unsigned cmCsvInsertSymDouble( cmCsvH_t h, double v )
  569. {
  570. const char* fmt = "%f";
  571. unsigned n = snprintf(NULL,0,fmt,v)+1;
  572. char buf[n];
  573. buf[0]= 0;
  574. if( snprintf(buf,n,fmt,v) == n-1 )
  575. return cmCsvInsertSymText(h,buf);
  576. _cmCsvError(_cmCsvHandleToPtr(h),kDataCvtErrCsvRC,"The double %f could not be converted to text.",v);
  577. return cmInvalidId;
  578. }
  579. cmCsvRC_t cmCsvSetCellText( cmCsvH_t h, unsigned row, unsigned col, const char* text )
  580. {
  581. cmCsvCell_t* cp;
  582. unsigned symId;
  583. if((cp = _cmCsvCellPtr(h,row,col)) == NULL )
  584. return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
  585. if((symId = cmCsvInsertSymText(h,text)) == cmInvalidId )
  586. return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
  587. cp->symId = symId;
  588. cp->flags &= !kTypeTMask;
  589. cp->flags |= kStrCsvTFl;
  590. return kOkCsvRC;
  591. }
  592. cmCsvRC_t cmCsvSetCellInt( cmCsvH_t h, unsigned row, unsigned col, int v )
  593. {
  594. cmCsvCell_t* cp;
  595. unsigned symId;
  596. if((cp = _cmCsvCellPtr(h,row,col)) == NULL )
  597. return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
  598. if((symId = cmCsvInsertSymInt(h,v)) == cmInvalidId )
  599. return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
  600. cp->symId = symId;
  601. cp->flags &= !kTypeTMask;
  602. cp->flags |= kIntCsvTFl;
  603. return kOkCsvRC;
  604. }
  605. cmCsvRC_t cmCsvSetCellUInt( cmCsvH_t h, unsigned row, unsigned col, unsigned v )
  606. {
  607. cmCsvCell_t* cp;
  608. unsigned symId;
  609. if((cp = _cmCsvCellPtr(h,row,col)) == NULL )
  610. return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
  611. if((symId = cmCsvInsertSymUInt(h,v)) == cmInvalidId )
  612. return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
  613. cp->symId = symId;
  614. cp->flags &= !kTypeTMask;
  615. cp->flags |= kIntCsvTFl;
  616. return kOkCsvRC;
  617. }
  618. cmCsvRC_t cmCsvSetCellFloat( cmCsvH_t h, unsigned row, unsigned col, float v )
  619. {
  620. cmCsvCell_t* cp;
  621. unsigned symId;
  622. if((cp = _cmCsvCellPtr(h,row,col)) == NULL )
  623. return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
  624. if((symId = cmCsvInsertSymFloat(h,v)) == cmInvalidId )
  625. return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
  626. cp->symId = symId;
  627. cp->flags &= !kTypeTMask;
  628. cp->flags |= kRealCsvTFl;
  629. return kOkCsvRC;
  630. }
  631. cmCsvRC_t cmCsvSetCellDouble( cmCsvH_t h, unsigned row, unsigned col, double v )
  632. {
  633. cmCsvCell_t* cp;
  634. unsigned symId;
  635. if((cp = _cmCsvCellPtr(h,row,col)) == NULL )
  636. return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
  637. if((symId = cmCsvInsertSymDouble(h,v)) == cmInvalidId )
  638. return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
  639. cp->symId = symId;
  640. cp->flags &= !kTypeTMask;
  641. cp->flags |= kRealCsvTFl;
  642. return kOkCsvRC;
  643. }
  644. cmCsvRC_t cmCsvInsertRowBefore( cmCsvH_t h, unsigned row, cmCsvCell_t** cellPtrPtr, unsigned symId, unsigned flags, unsigned lexTId )
  645. {
  646. cmCsvRC_t rc = kOkCsvRC;
  647. cmCsv_t* p = _cmCsvHandleToPtr(h);
  648. cmCsvBind_t* bp = p->bindPtr;
  649. cmCsvBind_t* nbp = NULL;
  650. unsigned i = 0;
  651. if( cellPtrPtr != NULL )
  652. *cellPtrPtr = NULL;
  653. // allocate the binder record
  654. if((nbp = cmLHeapAllocZ( p->heapH, sizeof(cmCsvBind_t))) == NULL )
  655. return _cmCsvError(p,kMemAllocErrCsvRC,"Binding record allocation failed row %i column %i.",row,0);
  656. // if a new first row is being inserted
  657. if( row == 0 )
  658. {
  659. bp = p->bindPtr;
  660. nbp->linkPtr = p->bindPtr;
  661. p->bindPtr = nbp;
  662. }
  663. else
  664. {
  665. bp = p->bindPtr;
  666. // iterate to the row before the new row
  667. for(i=0; bp != NULL; ++i )
  668. {
  669. if( i == (row-1) || bp->linkPtr == NULL )
  670. break;
  671. bp = bp->linkPtr;
  672. }
  673. assert( bp != NULL );
  674. nbp->linkPtr = bp->linkPtr;
  675. bp->linkPtr = nbp;
  676. bp = bp->linkPtr;
  677. }
  678. // update the row numbers in all cells below the new row
  679. while( bp != NULL )
  680. {
  681. cmCsvCell_t* cp = bp->rowPtr;
  682. while( cp != NULL )
  683. {
  684. cp->row += 1;
  685. cp = cp->rowPtr;
  686. }
  687. bp = bp->linkPtr;
  688. }
  689. // allocate the first cell in the new row
  690. if(cellPtrPtr != NULL && symId != cmInvalidId )
  691. {
  692. if((rc = _cmCsvAllocCell(p, symId, flags, row, 0, cellPtrPtr, lexTId )) != kOkCsvRC )
  693. return rc;
  694. nbp->rowPtr = *cellPtrPtr;
  695. }
  696. return rc;
  697. }
  698. cmCsvRC_t cmCsvAppendRow( cmCsvH_t h, cmCsvCell_t** cellPtrPtr, unsigned symId, unsigned flags, unsigned lexTId )
  699. {
  700. cmCsvRC_t rc = kOkCsvRC;
  701. cmCsv_t* p = _cmCsvHandleToPtr(h);
  702. cmCsvBind_t* nbp = NULL;
  703. unsigned newRowIdx = cmInvalidIdx;
  704. if( cellPtrPtr != NULL )
  705. *cellPtrPtr = NULL;
  706. if((rc = _cmCsvAppendNewBindRecd(p,&nbp,&newRowIdx)) != kOkCsvRC )
  707. return rc;
  708. // allocate the first cell in the new row
  709. if(cellPtrPtr != NULL && symId != cmInvalidId )
  710. {
  711. if((rc = _cmCsvAllocCell(p, symId, flags, newRowIdx, 0, cellPtrPtr, lexTId )) != kOkCsvRC )
  712. return rc;
  713. nbp->rowPtr = *cellPtrPtr;
  714. }
  715. return rc;
  716. }
  717. cmCsvRC_t cmCsvInsertColAfter( cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, unsigned symId, unsigned flags, unsigned lexTId )
  718. {
  719. cmCsvRC_t rc = kOkCsvRC;
  720. cmCsvCell_t* ncp = NULL;
  721. cmCsvCell_t* cp = NULL;
  722. cmCsv_t* p = _cmCsvHandleToPtr(h);
  723. unsigned col;
  724. if(cellPtrPtr != NULL )
  725. *cellPtrPtr = NULL;
  726. // allocate the new cell
  727. if((rc = _cmCsvAllocCell(p, symId, flags, leftCellPtr->row, leftCellPtr->col+1, &ncp, lexTId )) != kOkCsvRC )
  728. return rc;
  729. // update the col values of cells to right of new cell
  730. cp = leftCellPtr->rowPtr;
  731. col = leftCellPtr->col + 1;
  732. for(; cp != NULL; ++col )
  733. {
  734. // don't update any col numbers after a blank (missing) column
  735. if( cp->col == col )
  736. cp->col += 1;
  737. cp = cp->rowPtr;
  738. }
  739. // link in the new cell
  740. ncp->rowPtr = leftCellPtr->rowPtr;
  741. leftCellPtr->rowPtr = ncp;
  742. if(cellPtrPtr != NULL )
  743. *cellPtrPtr = ncp;
  744. return rc;
  745. }
  746. cmCsvRC_t cmCsvInsertTextColAfter( cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, const char* text, unsigned lexTId )
  747. {
  748. cmCsvRC_t rc;
  749. cmCsvCell_t* ncp;
  750. if( cellPtrPtr != NULL )
  751. cellPtrPtr = NULL;
  752. if((rc = cmCsvInsertColAfter(h, leftCellPtr, &ncp, cmInvalidId, 0, lexTId )) == kOkCsvRC )
  753. if((rc = cmCsvSetCellText(h, ncp->row, ncp->col, text )) == kOkCsvRC )
  754. if( cellPtrPtr != NULL )
  755. *cellPtrPtr = ncp;
  756. return rc;
  757. }
  758. cmCsvRC_t cmCsvInsertIntColAfter( cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, int val, unsigned lexTId )
  759. {
  760. cmCsvRC_t rc;
  761. cmCsvCell_t* ncp;
  762. if( cellPtrPtr != NULL )
  763. cellPtrPtr = NULL;
  764. if((rc = cmCsvInsertColAfter(h, leftCellPtr, &ncp, cmInvalidId, 0, lexTId )) == kOkCsvRC )
  765. if((rc = cmCsvSetCellInt(h, ncp->row, ncp->col, val )) == kOkCsvRC )
  766. if( cellPtrPtr != NULL )
  767. *cellPtrPtr = ncp;
  768. return rc;
  769. }
  770. cmCsvRC_t cmCsvInsertUIntColAfter( cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, unsigned val, unsigned lexTId )
  771. {
  772. cmCsvRC_t rc;
  773. cmCsvCell_t* ncp;
  774. if( cellPtrPtr != NULL )
  775. cellPtrPtr = NULL;
  776. if((rc = cmCsvInsertColAfter(h, leftCellPtr, &ncp, cmInvalidId, 0, lexTId )) == kOkCsvRC )
  777. if((rc = cmCsvSetCellUInt(h, ncp->row, ncp->col, val )) == kOkCsvRC )
  778. if( cellPtrPtr != NULL )
  779. *cellPtrPtr = ncp;
  780. return rc;
  781. }
  782. cmCsvRC_t cmCsvInsertFloatColAfter( cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, float val, unsigned lexTId )
  783. {
  784. cmCsvRC_t rc;
  785. cmCsvCell_t* ncp;
  786. if( cellPtrPtr != NULL )
  787. cellPtrPtr = NULL;
  788. if((rc = cmCsvInsertColAfter(h, leftCellPtr, &ncp, cmInvalidId, 0, lexTId )) == kOkCsvRC )
  789. if((rc = cmCsvSetCellFloat(h, ncp->row, ncp->col, val )) == kOkCsvRC )
  790. if( cellPtrPtr != NULL )
  791. *cellPtrPtr = ncp;
  792. return rc;
  793. }
  794. cmCsvRC_t cmCsvInsertDoubleColAfter( cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, double val, unsigned lexTId )
  795. {
  796. cmCsvRC_t rc;
  797. cmCsvCell_t* ncp;
  798. if( cellPtrPtr != NULL )
  799. cellPtrPtr = NULL;
  800. if((rc = cmCsvInsertColAfter(h, leftCellPtr, &ncp, cmInvalidId, 0, lexTId )) == kOkCsvRC )
  801. if((rc = cmCsvSetCellDouble(h, ncp->row, ncp->col, val )) == kOkCsvRC )
  802. if( cellPtrPtr != NULL )
  803. *cellPtrPtr = ncp;
  804. return rc;
  805. }
  806. cmCsvRC_t cmCsvWrite( cmCsvH_t h, const char* fn )
  807. {
  808. FILE* fp = NULL;
  809. cmCsvRC_t rc = kOkCsvRC;
  810. cmCsv_t* p = _cmCsvHandleToPtr(h);
  811. cmCsvBind_t* bp = p->bindPtr;
  812. if((fp = fopen(fn,"wt")) == NULL )
  813. return _cmCsvError(p,kFileCreateErrCsvRC,"Unable to create the output CSV file:'%s'.",fn);
  814. bp = p->bindPtr;
  815. // for each row
  816. while( bp != NULL )
  817. {
  818. cmCsvCell_t* cp = bp->rowPtr;
  819. unsigned col = 0;
  820. // for each column
  821. for(; cp != NULL; ++col )
  822. {
  823. // skip blank columns
  824. if( cp->col == col )
  825. {
  826. const char* tp;
  827. if((tp = cmSymTblLabel(p->symTblH,cp->symId)) == NULL )
  828. return _cmCsvError(p,kSymTblErrCsvRC,"Unable to locate the symbol text for cell at row:%i col:%i.",cp->row,cp->col);
  829. if( cmIsFlag(cp->flags,kTextTMask) )
  830. fprintf(fp,"\"");
  831. fputs(tp,fp);
  832. if( cmIsFlag(cp->flags,kTextTMask) )
  833. fprintf(fp,"\"");
  834. cp = cp->rowPtr;
  835. }
  836. if( cp == NULL )
  837. fprintf(fp,"\n"); // end of row
  838. else
  839. fprintf(fp,","); // between columns
  840. }
  841. bp = bp->linkPtr;
  842. }
  843. fclose(fp);
  844. return rc;
  845. }
  846. cmCsvRC_t cmCsvPrint( cmCsvH_t h, unsigned rowCnt )
  847. {
  848. cmCsv_t* p = _cmCsvHandleToPtr(h);
  849. cmCsvBind_t* bp = p->bindPtr;
  850. unsigned i;
  851. for(i=0; bp!=NULL && i<rowCnt; ++i,bp=bp->linkPtr)
  852. {
  853. cmCsvCell_t* cp = bp->rowPtr;
  854. unsigned j;
  855. for(j=0; cp!=NULL; ++j)
  856. {
  857. if( cp->col == j )
  858. {
  859. const char* tp;
  860. if((tp = cmSymTblLabel(p->symTblH,cp->symId)) == NULL )
  861. _cmCsvError(p,kSymTblErrCsvRC,"The text associated with the symbol '%i' was not found.",cp->symId);
  862. fputs(tp,stdin);
  863. }
  864. cp=cp->rowPtr;
  865. if( cp == NULL )
  866. printf("\n");
  867. else
  868. printf(",");
  869. }
  870. }
  871. return kOkCsvRC;
  872. }