libcm is a C development framework with an emphasis on audio signal processing applications.
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

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