libcm is a C development framework with an emphasis on audio signal processing applications.
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

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. }