libcm is a C development framework with an emphasis on audio signal processing applications.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

cmCsv.c 30KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231
  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 "cmHashTbl.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. cmHashTblH_t htH; // hash table handle
  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 hash table
  81. if( cmHashTblCreate(ctx,&p->htH,8192) != kOkHtRC )
  82. {
  83. rc = _cmCsvError(p,kHashTblErrCsvRC,"Hash table creation failed.");
  84. goto errLabel;
  85. }
  86. // allocate the linked heap mgr
  87. if( cmLHeapIsValid(p->heapH = cmLHeapCreate(8192,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 hash table
  130. cmHashTblDestroy(&p->htH);
  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+1,cellCol+1);
  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 = cmHashTblStoreStr(p->htH,tokenText)) == cmInvalidId )
  275. return _cmCsvError(p,kHashTblErrCsvRC,"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' row:%i col:%i.",cmLexIdToLabel(p->lexH,lexTId),buf,csvRowIdx+1,csvColIdx+1);
  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. if((rc = _cmCsvCreateCell( p, buf,flags, csvRowIdx, csvColIdx, lexTId )) != kOkCsvRC )
  387. rc = _cmCsvError(p,rc,"CSV cell create fail on row:%i col:%i\n",csvRowIdx+1,csvColIdx+1);
  388. }
  389. if( lexTId == kErrorLexTId )
  390. rc = _cmCsvError(p, kSyntaxErrCsvRC,"The lexer encountered an unrecognized token row:%i col:%i.",csvRowIdx+1,csvColIdx+1);
  391. return rc;
  392. }
  393. unsigned cmCsvRowCount( cmCsvH_t h )
  394. {
  395. cmCsv_t* p = _cmCsvHandleToPtr(h);
  396. unsigned rowCnt = 0;
  397. cmCsvBind_t* bp = p->bindPtr;
  398. for(; bp != NULL; ++rowCnt )
  399. bp = bp->linkPtr;
  400. return rowCnt;
  401. }
  402. cmCsvCell_t* cmCsvRowPtr( cmCsvH_t h, unsigned row )
  403. {
  404. cmCsv_t* p = _cmCsvHandleToPtr(h);
  405. cmCsvBind_t* bp = p->bindPtr;
  406. while( bp != NULL )
  407. {
  408. // note: bp->rowPtr of blank rows will be NULL
  409. if( bp->rowPtr!=NULL && bp->rowPtr->row == row )
  410. return bp->rowPtr;
  411. bp = bp->linkPtr;
  412. }
  413. return NULL;
  414. }
  415. cmCsvCell_t* cmCsvCellPtr( cmCsvH_t h, unsigned row, unsigned col )
  416. {
  417. cmCsvCell_t* cp;
  418. if((cp = cmCsvRowPtr(h,row)) == NULL )
  419. return NULL;
  420. while( cp != NULL )
  421. {
  422. if( cp->col == col )
  423. return cp;
  424. cp = cp->rowPtr;
  425. }
  426. return NULL;
  427. }
  428. cmCsvCell_t* _cmCsvCellPtr( cmCsvH_t h, unsigned row, unsigned col )
  429. {
  430. cmCsvCell_t* cp;
  431. if((cp = cmCsvCellPtr(h,row,col)) == NULL )
  432. _cmCsvError(_cmCsvHandleToPtr(h),kCellNotFoundCsvRC,"Cell at row:%i col:%i not found.",row,col);
  433. return cp;
  434. }
  435. const char* cmCsvCellSymText( cmCsvH_t h, unsigned symId )
  436. {
  437. cmCsv_t* p = _cmCsvHandleToPtr(h);
  438. const char* cp;
  439. if((cp = cmHashTblStr(p->htH,symId)) == NULL )
  440. _cmCsvError(p,kHashTblErrCsvRC,"The text associated with the symbol '%i' was not found.",symId);
  441. return cp;
  442. }
  443. cmCsvRC_t cmCsvCellSymInt( cmCsvH_t h, unsigned symId, int* vp )
  444. {
  445. const char* cp;
  446. cmCsv_t* p = _cmCsvHandleToPtr(h);
  447. if((cp = cmCsvCellSymText(h,symId)) == NULL )
  448. return kHashTblErrCsvRC;
  449. if( cmTextToInt(cp,vp,&p->err) != kOkTxRC )
  450. return _cmCsvError(p,kDataCvtErrCsvRC,"CSV text to int value failed.");
  451. return kOkCsvRC;
  452. }
  453. cmCsvRC_t cmCsvCellSymUInt( cmCsvH_t h, unsigned symId, unsigned* vp )
  454. {
  455. const char* cp;
  456. cmCsv_t* p = _cmCsvHandleToPtr(h);
  457. if((cp = cmCsvCellSymText(h,symId)) == NULL )
  458. return kHashTblErrCsvRC;
  459. if( cmTextToUInt(cp,vp,&p->err) != kOkTxRC )
  460. return _cmCsvError(p,kDataCvtErrCsvRC,"CSV text to uint value failed.");
  461. return kOkCsvRC;
  462. }
  463. cmCsvRC_t cmCsvCellSymFloat( cmCsvH_t h, unsigned symId, float* vp )
  464. {
  465. const char* cp;
  466. cmCsv_t* p = _cmCsvHandleToPtr(h);
  467. if((cp = cmCsvCellSymText(h,symId)) == NULL )
  468. return kHashTblErrCsvRC;
  469. if( cmTextToFloat(cp,vp,&p->err) != kOkTxRC )
  470. return _cmCsvError(p,kDataCvtErrCsvRC,"CSV text to float value failed.");
  471. return kOkCsvRC;
  472. }
  473. cmCsvRC_t cmCsvCellSymDouble( cmCsvH_t h, unsigned symId, double* vp )
  474. {
  475. const char* cp;
  476. cmCsv_t* p = _cmCsvHandleToPtr(h);
  477. if((cp = cmCsvCellSymText(h,symId)) == NULL )
  478. return kHashTblErrCsvRC;
  479. if( cmTextToDouble(cp,vp,&p->err) != kOkTxRC )
  480. return _cmCsvError(p,kDataCvtErrCsvRC,"CSV text to double value failed.");
  481. return kOkCsvRC;
  482. }
  483. const char* cmCsvCellText( cmCsvH_t h, unsigned row, unsigned col )
  484. {
  485. cmCsvCell_t* cp;
  486. if((cp = cmCsvCellPtr(h,row,col)) == NULL )
  487. return NULL;
  488. return cmCsvCellSymText(h,cp->symId);
  489. }
  490. int cmCsvCellInt( cmCsvH_t h, unsigned row, unsigned col )
  491. {
  492. cmCsvCell_t* cp;
  493. int v;
  494. if((cp = cmCsvCellPtr(h,row,col)) == NULL )
  495. return INT_MAX;
  496. if(cmCsvCellSymInt(h,cp->symId,&v) != kOkCsvRC )
  497. return INT_MAX;
  498. return v;
  499. }
  500. unsigned cmCsvCellUInt( cmCsvH_t h, unsigned row, unsigned col )
  501. {
  502. cmCsvCell_t* cp;
  503. unsigned v;
  504. if((cp = cmCsvCellPtr(h,row,col)) == NULL )
  505. return UINT_MAX;
  506. if(cmCsvCellSymUInt(h,cp->symId,&v) != kOkCsvRC )
  507. return UINT_MAX;
  508. return v;
  509. }
  510. float cmCsvCellFloat( cmCsvH_t h, unsigned row, unsigned col )
  511. {
  512. cmCsvCell_t* cp;
  513. float v;
  514. if((cp = cmCsvCellPtr(h,row,col)) == NULL )
  515. return FLT_MAX;
  516. if(cmCsvCellSymFloat(h,cp->symId,&v) != kOkCsvRC )
  517. return FLT_MAX;
  518. return v;
  519. }
  520. double cmCsvCellDouble(cmCsvH_t h, unsigned row, unsigned col )
  521. {
  522. cmCsvCell_t* cp;
  523. double v;
  524. if((cp = cmCsvCellPtr(h,row,col)) == NULL )
  525. return DBL_MAX;
  526. if(cmCsvCellSymDouble(h,cp->symId,&v) != kOkCsvRC )
  527. return DBL_MAX;
  528. return v;
  529. }
  530. unsigned cmCsvInsertSymText( cmCsvH_t h, const char* text )
  531. {
  532. cmCsv_t* p = _cmCsvHandleToPtr(h);
  533. unsigned symId;
  534. if((symId = cmHashTblStoreStr(p->htH,text)) == cmInvalidId )
  535. _cmCsvError(p,kHashTblErrCsvRC,"'%s' could not be inserted into the symbol table.",text);
  536. return symId;
  537. }
  538. unsigned cmCsvInsertSymInt( cmCsvH_t h, int v )
  539. {
  540. const char* fmt = "%i";
  541. unsigned n = snprintf(NULL,0,fmt,v)+1;
  542. char buf[n];
  543. buf[0]= 0;
  544. if( snprintf(buf,n,fmt,v) == n-1 )
  545. return cmCsvInsertSymText(h,buf);
  546. _cmCsvError(_cmCsvHandleToPtr(h),kDataCvtErrCsvRC,"The integer %i could not be converted to text.",v);
  547. return cmInvalidId;
  548. }
  549. unsigned cmCsvInsertSymUInt( cmCsvH_t h, unsigned v )
  550. {
  551. const char* fmt = "%i";
  552. unsigned n = snprintf(NULL,0,fmt,v)+1;
  553. char buf[n];
  554. buf[0]= 0;
  555. if( snprintf(buf,n,fmt,v) == n-1 )
  556. return cmCsvInsertSymText(h,buf);
  557. _cmCsvError(_cmCsvHandleToPtr(h),kDataCvtErrCsvRC,"The unsigned int %i could not be converted to text.",v);
  558. return cmInvalidId;
  559. }
  560. unsigned cmCsvInsertSymHex( cmCsvH_t h, unsigned v )
  561. {
  562. const char* fmt = "0x%x";
  563. unsigned n = snprintf(NULL,0,fmt,v)+1;
  564. char buf[n];
  565. buf[0]= 0;
  566. if( snprintf(buf,n,fmt,v) == n-1 )
  567. return cmCsvInsertSymText(h,buf);
  568. _cmCsvError(_cmCsvHandleToPtr(h),kDataCvtErrCsvRC,"The unsigned int 0x%x could not be converted to text.",v);
  569. return cmInvalidId;
  570. }
  571. unsigned cmCsvInsertSymFloat( cmCsvH_t h, float v )
  572. {
  573. const char* fmt = "%f";
  574. unsigned n = snprintf(NULL,0,fmt,v)+1;
  575. char buf[n];
  576. buf[0] = 0;
  577. if( snprintf(buf,n,fmt,v) == n-1 )
  578. return cmCsvInsertSymText(h,buf);
  579. _cmCsvError(_cmCsvHandleToPtr(h),kDataCvtErrCsvRC,"The float %f could not be converted to text.",v);
  580. return cmInvalidId;
  581. }
  582. unsigned cmCsvInsertSymDouble( cmCsvH_t h, double v )
  583. {
  584. const char* fmt = "%f";
  585. unsigned n = snprintf(NULL,0,fmt,v)+1;
  586. char buf[n];
  587. buf[0]= 0;
  588. if( snprintf(buf,n,fmt,v) == n-1 )
  589. return cmCsvInsertSymText(h,buf);
  590. _cmCsvError(_cmCsvHandleToPtr(h),kDataCvtErrCsvRC,"The double %f could not be converted to text.",v);
  591. return cmInvalidId;
  592. }
  593. cmCsvRC_t cmCsvSetCellIdent( cmCsvH_t h, unsigned row, unsigned col, const char* text )
  594. {
  595. cmCsvCell_t* cp;
  596. unsigned symId;
  597. if((cp = _cmCsvCellPtr(h,row,col)) == NULL )
  598. return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
  599. if((symId = cmCsvInsertSymText(h,text)) == cmInvalidId )
  600. return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
  601. cp->symId = symId;
  602. cp->flags &= !kTypeTMask;
  603. cp->flags |= kIdentCsvTFl;
  604. return kOkCsvRC;
  605. }
  606. cmCsvRC_t cmCsvSetCellText( cmCsvH_t h, unsigned row, unsigned col, const char* text )
  607. {
  608. cmCsvCell_t* cp;
  609. unsigned symId;
  610. if((cp = _cmCsvCellPtr(h,row,col)) == NULL )
  611. return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
  612. if((symId = cmCsvInsertSymText(h,text)) == cmInvalidId )
  613. return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
  614. cp->symId = symId;
  615. cp->flags &= !kTypeTMask;
  616. cp->flags |= kStrCsvTFl;
  617. return kOkCsvRC;
  618. }
  619. cmCsvRC_t cmCsvSetCellInt( cmCsvH_t h, unsigned row, unsigned col, int v )
  620. {
  621. cmCsvCell_t* cp;
  622. unsigned symId;
  623. if((cp = _cmCsvCellPtr(h,row,col)) == NULL )
  624. return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
  625. if((symId = cmCsvInsertSymInt(h,v)) == cmInvalidId )
  626. return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
  627. cp->symId = symId;
  628. cp->flags &= !kTypeTMask;
  629. cp->flags |= kIntCsvTFl;
  630. return kOkCsvRC;
  631. }
  632. cmCsvRC_t cmCsvSetCellUInt( cmCsvH_t h, unsigned row, unsigned col, unsigned v )
  633. {
  634. cmCsvCell_t* cp;
  635. unsigned symId;
  636. if((cp = _cmCsvCellPtr(h,row,col)) == NULL )
  637. return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
  638. if((symId = cmCsvInsertSymUInt(h,v)) == cmInvalidId )
  639. return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
  640. cp->symId = symId;
  641. cp->flags &= !kTypeTMask;
  642. cp->flags |= kIntCsvTFl;
  643. return kOkCsvRC;
  644. }
  645. cmCsvRC_t cmCsvSetCellHex( cmCsvH_t h, unsigned row, unsigned col, unsigned v )
  646. {
  647. cmCsvCell_t* cp;
  648. unsigned symId;
  649. if((cp = _cmCsvCellPtr(h,row,col)) == NULL )
  650. return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
  651. if((symId = cmCsvInsertSymHex(h,v)) == cmInvalidId )
  652. return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
  653. cp->symId = symId;
  654. cp->flags &= !kTypeTMask;
  655. cp->flags |= kIntCsvTFl;
  656. return kOkCsvRC;
  657. }
  658. cmCsvRC_t cmCsvSetCellFloat( cmCsvH_t h, unsigned row, unsigned col, float v )
  659. {
  660. cmCsvCell_t* cp;
  661. unsigned symId;
  662. if((cp = _cmCsvCellPtr(h,row,col)) == NULL )
  663. return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
  664. if((symId = cmCsvInsertSymFloat(h,v)) == cmInvalidId )
  665. return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
  666. cp->symId = symId;
  667. cp->flags &= !kTypeTMask;
  668. cp->flags |= kRealCsvTFl;
  669. return kOkCsvRC;
  670. }
  671. cmCsvRC_t cmCsvSetCellDouble( cmCsvH_t h, unsigned row, unsigned col, double v )
  672. {
  673. cmCsvCell_t* cp;
  674. unsigned symId;
  675. if((cp = _cmCsvCellPtr(h,row,col)) == NULL )
  676. return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
  677. if((symId = cmCsvInsertSymDouble(h,v)) == cmInvalidId )
  678. return cmErrLastRC(&_cmCsvHandleToPtr(h)->err);
  679. cp->symId = symId;
  680. cp->flags &= !kTypeTMask;
  681. cp->flags |= kRealCsvTFl;
  682. return kOkCsvRC;
  683. }
  684. cmCsvRC_t cmCsvInsertRowBefore( cmCsvH_t h, unsigned row, cmCsvCell_t** cellPtrPtr, unsigned symId, unsigned flags, unsigned lexTId )
  685. {
  686. cmCsvRC_t rc = kOkCsvRC;
  687. cmCsv_t* p = _cmCsvHandleToPtr(h);
  688. cmCsvBind_t* bp = p->bindPtr;
  689. cmCsvBind_t* nbp = NULL;
  690. unsigned i = 0;
  691. if( cellPtrPtr != NULL )
  692. *cellPtrPtr = NULL;
  693. // allocate the binder record
  694. if((nbp = cmLHeapAllocZ( p->heapH, sizeof(cmCsvBind_t))) == NULL )
  695. return _cmCsvError(p,kMemAllocErrCsvRC,"Binding record allocation failed row %i column %i.",row,0);
  696. // if a new first row is being inserted
  697. if( row == 0 )
  698. {
  699. bp = p->bindPtr;
  700. nbp->linkPtr = p->bindPtr;
  701. p->bindPtr = nbp;
  702. }
  703. else
  704. {
  705. bp = p->bindPtr;
  706. // iterate to the row before the new row
  707. for(i=0; bp != NULL; ++i )
  708. {
  709. if( i == (row-1) || bp->linkPtr == NULL )
  710. break;
  711. bp = bp->linkPtr;
  712. }
  713. assert( bp != NULL );
  714. nbp->linkPtr = bp->linkPtr;
  715. bp->linkPtr = nbp;
  716. bp = bp->linkPtr;
  717. }
  718. // update the row numbers in all cells below the new row
  719. while( bp != NULL )
  720. {
  721. cmCsvCell_t* cp = bp->rowPtr;
  722. while( cp != NULL )
  723. {
  724. cp->row += 1;
  725. cp = cp->rowPtr;
  726. }
  727. bp = bp->linkPtr;
  728. }
  729. // allocate the first cell in the new row
  730. if(cellPtrPtr != NULL && symId != cmInvalidId )
  731. {
  732. if((rc = _cmCsvAllocCell(p, symId, flags, row, 0, cellPtrPtr, lexTId )) != kOkCsvRC )
  733. return rc;
  734. nbp->rowPtr = *cellPtrPtr;
  735. }
  736. return rc;
  737. }
  738. cmCsvRC_t cmCsvAppendRow( cmCsvH_t h, cmCsvCell_t** cellPtrPtr, unsigned symId, unsigned flags, unsigned lexTId )
  739. {
  740. cmCsvRC_t rc = kOkCsvRC;
  741. cmCsv_t* p = _cmCsvHandleToPtr(h);
  742. cmCsvBind_t* nbp = NULL;
  743. unsigned newRowIdx = cmInvalidIdx;
  744. if( cellPtrPtr != NULL )
  745. *cellPtrPtr = NULL;
  746. if((rc = _cmCsvAppendNewBindRecd(p,&nbp,&newRowIdx)) != kOkCsvRC )
  747. return rc;
  748. // allocate the first cell in the new row
  749. if(cellPtrPtr != NULL && symId != cmInvalidId )
  750. {
  751. if((rc = _cmCsvAllocCell(p, symId, flags, newRowIdx, 0, cellPtrPtr, lexTId )) != kOkCsvRC )
  752. return rc;
  753. nbp->rowPtr = *cellPtrPtr;
  754. }
  755. return rc;
  756. }
  757. cmCsvRC_t cmCsvInsertColAfter( cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, unsigned symId, unsigned flags, unsigned lexTId )
  758. {
  759. cmCsvRC_t rc = kOkCsvRC;
  760. cmCsvCell_t* ncp = NULL;
  761. cmCsvCell_t* cp = NULL;
  762. cmCsv_t* p = _cmCsvHandleToPtr(h);
  763. unsigned col;
  764. if(cellPtrPtr != NULL )
  765. *cellPtrPtr = NULL;
  766. // allocate the new cell
  767. if((rc = _cmCsvAllocCell(p, symId, flags, leftCellPtr->row, leftCellPtr->col+1, &ncp, lexTId )) != kOkCsvRC )
  768. return rc;
  769. // update the col values of cells to right of new cell
  770. cp = leftCellPtr->rowPtr;
  771. col = leftCellPtr->col + 1;
  772. for(; cp != NULL; ++col )
  773. {
  774. // don't update any col numbers after a blank (missing) column
  775. if( cp->col == col )
  776. cp->col += 1;
  777. cp = cp->rowPtr;
  778. }
  779. // link in the new cell
  780. ncp->rowPtr = leftCellPtr->rowPtr;
  781. leftCellPtr->rowPtr = ncp;
  782. if(cellPtrPtr != NULL )
  783. *cellPtrPtr = ncp;
  784. return rc;
  785. }
  786. cmCsvRC_t cmCsvInsertIdentColAfter( cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, const char* text, unsigned lexTId )
  787. {
  788. cmCsvRC_t rc;
  789. cmCsvCell_t* ncp;
  790. if( cellPtrPtr != NULL )
  791. *cellPtrPtr = NULL;
  792. if((rc = cmCsvInsertColAfter(h, leftCellPtr, &ncp, cmInvalidId, 0, lexTId )) == kOkCsvRC )
  793. if((rc = cmCsvSetCellIdent(h, ncp->row, ncp->col, text )) == kOkCsvRC )
  794. if( cellPtrPtr != NULL )
  795. *cellPtrPtr = ncp;
  796. return rc;
  797. }
  798. cmCsvRC_t cmCsvInsertQTextColAfter( cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, const char* text, unsigned lexTId )
  799. {
  800. cmCsvRC_t rc;
  801. cmCsvCell_t* ncp;
  802. if( cellPtrPtr != NULL )
  803. *cellPtrPtr = NULL;
  804. if((rc = cmCsvInsertColAfter(h, leftCellPtr, &ncp, cmInvalidId, 0, lexTId )) == kOkCsvRC )
  805. if((rc = cmCsvSetCellText(h, ncp->row, ncp->col, text )) == kOkCsvRC )
  806. if( cellPtrPtr != NULL )
  807. *cellPtrPtr = ncp;
  808. return rc;
  809. }
  810. cmCsvRC_t cmCsvInsertIntColAfter( cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, int val, unsigned lexTId )
  811. {
  812. cmCsvRC_t rc;
  813. cmCsvCell_t* ncp;
  814. if( cellPtrPtr != NULL )
  815. *cellPtrPtr = NULL;
  816. if((rc = cmCsvInsertColAfter(h, leftCellPtr, &ncp, cmInvalidId, 0, lexTId )) == kOkCsvRC )
  817. if((rc = cmCsvSetCellInt(h, ncp->row, ncp->col, val )) == kOkCsvRC )
  818. if( cellPtrPtr != NULL )
  819. *cellPtrPtr = ncp;
  820. return rc;
  821. }
  822. cmCsvRC_t cmCsvInsertUIntColAfter( cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, unsigned val, unsigned lexTId )
  823. {
  824. cmCsvRC_t rc;
  825. cmCsvCell_t* ncp;
  826. if( cellPtrPtr != NULL )
  827. *cellPtrPtr = NULL;
  828. if((rc = cmCsvInsertColAfter(h, leftCellPtr, &ncp, cmInvalidId, 0, lexTId )) == kOkCsvRC )
  829. if((rc = cmCsvSetCellUInt(h, ncp->row, ncp->col, val )) == kOkCsvRC )
  830. if( cellPtrPtr != NULL )
  831. *cellPtrPtr = ncp;
  832. return rc;
  833. }
  834. cmCsvRC_t cmCsvInsertHexColAfter( cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, unsigned val, unsigned lexTId )
  835. {
  836. cmCsvRC_t rc;
  837. cmCsvCell_t* ncp;
  838. if( cellPtrPtr != NULL )
  839. *cellPtrPtr = NULL;
  840. if((rc = cmCsvInsertColAfter(h, leftCellPtr, &ncp, cmInvalidId, 0, lexTId )) == kOkCsvRC )
  841. if((rc = cmCsvSetCellHex(h, ncp->row, ncp->col, val )) == kOkCsvRC )
  842. if( cellPtrPtr != NULL )
  843. *cellPtrPtr = ncp;
  844. return rc;
  845. }
  846. cmCsvRC_t cmCsvInsertFloatColAfter( cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, float val, unsigned lexTId )
  847. {
  848. cmCsvRC_t rc;
  849. cmCsvCell_t* ncp;
  850. if( cellPtrPtr != NULL )
  851. *cellPtrPtr = NULL;
  852. if((rc = cmCsvInsertColAfter(h, leftCellPtr, &ncp, cmInvalidId, 0, lexTId )) == kOkCsvRC )
  853. if((rc = cmCsvSetCellFloat(h, ncp->row, ncp->col, val )) == kOkCsvRC )
  854. if( cellPtrPtr != NULL )
  855. *cellPtrPtr = ncp;
  856. return rc;
  857. }
  858. cmCsvRC_t cmCsvInsertDoubleColAfter( cmCsvH_t h, cmCsvCell_t* leftCellPtr, cmCsvCell_t** cellPtrPtr, double val, unsigned lexTId )
  859. {
  860. cmCsvRC_t rc;
  861. cmCsvCell_t* ncp;
  862. if( cellPtrPtr != NULL )
  863. *cellPtrPtr = NULL;
  864. if((rc = cmCsvInsertColAfter(h, leftCellPtr, &ncp, cmInvalidId, 0, lexTId )) == kOkCsvRC )
  865. if((rc = cmCsvSetCellDouble(h, ncp->row, ncp->col, val )) == kOkCsvRC )
  866. if( cellPtrPtr != NULL )
  867. *cellPtrPtr = ncp;
  868. return rc;
  869. }
  870. cmCsvRC_t cmCsvWrite( cmCsvH_t h, const char* fn )
  871. {
  872. FILE* fp = NULL;
  873. cmCsvRC_t rc = kOkCsvRC;
  874. cmCsv_t* p = _cmCsvHandleToPtr(h);
  875. cmCsvBind_t* bp = p->bindPtr;
  876. if((fp = fopen(fn,"wt")) == NULL )
  877. return _cmCsvError(p,kFileCreateErrCsvRC,"Unable to create the output CSV file:'%s'.",fn);
  878. bp = p->bindPtr;
  879. // for each row
  880. while( bp != NULL )
  881. {
  882. cmCsvCell_t* cp = bp->rowPtr;
  883. unsigned col = 0;
  884. // for each column
  885. for(; cp != NULL; ++col )
  886. {
  887. // skip blank columns
  888. if( cp->col == col )
  889. {
  890. const char* tp;
  891. if((tp = cmHashTblStr(p->htH,cp->symId)) == NULL )
  892. return _cmCsvError(p,kHashTblErrCsvRC,"Unable to locate the symbol text for cell at row:%i col:%i.",cp->row,cp->col);
  893. if( cmIsFlag(cp->flags,kStrCsvTFl) )
  894. fprintf(fp,"\"");
  895. fputs(tp,fp);
  896. if( cmIsFlag(cp->flags,kStrCsvTFl) )
  897. fprintf(fp,"\"");
  898. cp = cp->rowPtr;
  899. }
  900. if( cp == NULL )
  901. fprintf(fp,"\n"); // end of row
  902. else
  903. fprintf(fp,","); // between columns
  904. }
  905. bp = bp->linkPtr;
  906. }
  907. fclose(fp);
  908. return rc;
  909. }
  910. cmCsvRC_t cmCsvPrint( cmCsvH_t h, unsigned rowCnt )
  911. {
  912. cmCsv_t* p = _cmCsvHandleToPtr(h);
  913. cmCsvBind_t* bp = p->bindPtr;
  914. unsigned i;
  915. for(i=0; bp!=NULL && i<rowCnt; ++i,bp=bp->linkPtr)
  916. {
  917. cmCsvCell_t* cp = bp->rowPtr;
  918. unsigned j;
  919. for(j=0; cp!=NULL; ++j)
  920. {
  921. if( cp->col == j )
  922. {
  923. const char* tp;
  924. if((tp = cmHashTblStr(p->htH,cp->symId)) == NULL )
  925. _cmCsvError(p,kHashTblErrCsvRC,"The text associated with the symbol '%i' was not found.",cp->symId);
  926. fputs(tp,stdin);
  927. }
  928. cp=cp->rowPtr;
  929. if( cp == NULL )
  930. printf("\n");
  931. else
  932. printf(",");
  933. }
  934. }
  935. return kOkCsvRC;
  936. }