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

cmFile.c 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614
  1. #include "cmPrefix.h"
  2. #include "cmGlobal.h"
  3. #include "cmRpt.h"
  4. #include "cmErr.h"
  5. #include "cmFile.h"
  6. #include "cmMem.h"
  7. #include "cmMallocDebug.h"
  8. #include <sys/stat.h>
  9. cmFileH_t cmFileNullHandle = { NULL };
  10. typedef struct
  11. {
  12. FILE* fp;
  13. cmErr_t err;
  14. cmChar_t* fnStr;
  15. } cmFile_t;
  16. cmFile_t* _cmFileHandleToPtr( cmFileH_t h )
  17. {
  18. cmFile_t* p = (cmFile_t*)h.h;
  19. assert(p != NULL);
  20. return p;
  21. }
  22. cmFileRC_t _cmFileError( cmFile_t* p, cmFileRC_t rc, int errNumb, const cmChar_t* msg )
  23. {
  24. if(errNumb == 0)
  25. rc = cmErrMsg(&p->err,rc,"%s on file '%s'",msg,p->fnStr);
  26. else
  27. rc = cmErrMsg(&p->err,rc,"%s on file '%s'\nSystem Msg:%s",msg,p->fnStr,strerror(errNumb));
  28. return rc;
  29. }
  30. cmFileRC_t cmFileOpen( cmFileH_t* hp, const cmChar_t* fn, enum cmFileOpenFlags_t flags, cmRpt_t* rpt )
  31. {
  32. char mode[] = "/0/0/0";
  33. cmFile_t* p = NULL;
  34. cmErr_t err;
  35. cmFileRC_t rc;
  36. if((rc = cmFileClose(hp)) != kOkFileRC )
  37. return rc;
  38. cmErrSetup(&err,rpt,"File");
  39. hp->h = NULL;
  40. if( cmIsFlag(flags,kReadFileFl) )
  41. mode[0]='r';
  42. else
  43. if( cmIsFlag(flags,kWriteFileFl) )
  44. mode[0]='w';
  45. else
  46. if( cmIsFlag(flags,kAppendFileFl) )
  47. mode[0]='a';
  48. else
  49. cmErrMsg(&err,kInvalidFlagFileRC,"File open flags must contain 'kReadFileFl','kWriteFileFl', or 'kAppendFileFl'.");
  50. if( cmIsFlag(flags,kUpdateFileFl) )
  51. mode[1]='+';
  52. if( fn == NULL )
  53. return cmErrMsg(&err,kObjAllocFailFileRC,"File object allocation failed due to empty file name.");
  54. unsigned byteCnt = sizeof(cmFile_t) + strlen(fn) + 1;
  55. if((p = (cmFile_t*)cmMemMallocZ(byteCnt)) == NULL )
  56. return cmErrMsg(&err,kObjAllocFailFileRC,"File object allocation failed for file '%s'.",cmStringNullGuard(fn));
  57. cmErrClone(&p->err,&err);
  58. p->fnStr = (cmChar_t*)(p+1);
  59. strcpy(p->fnStr,fn);
  60. errno = 0;
  61. if((p->fp = fopen(fn,mode)) == NULL )
  62. {
  63. cmFileRC_t rc = _cmFileError(p,kOpenFailFileRC,errno,"File open failed");
  64. cmMemFree(p);
  65. return rc;
  66. }
  67. hp->h = p;
  68. return kOkFileRC;
  69. }
  70. cmFileRC_t cmFileClose( cmFileH_t* hp )
  71. {
  72. if( cmFileIsValid(*hp) == false )
  73. return kOkFileRC;
  74. cmFile_t* p = _cmFileHandleToPtr(*hp);
  75. errno = 0;
  76. if( p->fp != NULL )
  77. if( fclose(p->fp) != 0 )
  78. return _cmFileError(p,kCloseFailFileRC,errno,"File close failed");
  79. cmMemFree(p);
  80. hp->h = NULL;
  81. return kOkFileRC;
  82. }
  83. bool cmFileIsValid( cmFileH_t h )
  84. { return h.h != NULL; }
  85. cmFileRC_t cmFileRead( cmFileH_t h, void* buf, unsigned bufByteCnt )
  86. {
  87. cmFile_t* p = _cmFileHandleToPtr(h);
  88. errno = 0;
  89. if( fread(buf,bufByteCnt,1,p->fp) != 1 )
  90. return _cmFileError(p,kReadFailFileRC,errno,"File read failed");
  91. return kOkFileRC;
  92. }
  93. cmFileRC_t cmFileWrite( cmFileH_t h, const void* buf, unsigned bufByteCnt )
  94. {
  95. cmFile_t* p = _cmFileHandleToPtr(h);
  96. errno = 0;
  97. if( fwrite(buf,bufByteCnt,1,p->fp) != 1 )
  98. return _cmFileError(p,kWriteFailFileRC,errno,"File write failed");
  99. return kOkFileRC;
  100. }
  101. cmFileRC_t cmFileSeek( cmFileH_t h, enum cmFileSeekFlags_t flags, int offsByteCnt )
  102. {
  103. cmFile_t* p = _cmFileHandleToPtr(h);
  104. unsigned fileflags = 0;
  105. if( cmIsFlag(flags,kBeginFileFl) )
  106. fileflags = SEEK_SET;
  107. else
  108. if( cmIsFlag(flags,kCurFileFl) )
  109. fileflags = SEEK_CUR;
  110. else
  111. if( cmIsFlag(flags,kEndFileFl) )
  112. fileflags = SEEK_END;
  113. else
  114. return cmErrMsg(&p->err,kInvalidFlagFileRC,"Invalid file seek flag on '%s'.",p->fnStr);
  115. errno = 0;
  116. if( fseek(p->fp,offsByteCnt,fileflags) != 0 )
  117. return _cmFileError(p,kSeekFailFileRC,errno,"File seek failed");
  118. return kOkFileRC;
  119. }
  120. cmFileRC_t cmFileTell( cmFileH_t h, long* offsPtr )
  121. {
  122. assert( offsPtr != NULL );
  123. *offsPtr = -1;
  124. cmFile_t* p = _cmFileHandleToPtr(h);
  125. errno = 0;
  126. if((*offsPtr = ftell(p->fp)) == -1)
  127. return _cmFileError(p,kTellFailFileRC,errno,"File tell failed");
  128. return kOkFileRC;
  129. }
  130. bool cmFileEof( cmFileH_t h )
  131. { return feof( _cmFileHandleToPtr(h)->fp ) != 0; }
  132. unsigned cmFileByteCount( cmFileH_t h )
  133. {
  134. struct stat sr;
  135. int f;
  136. cmFile_t* p = _cmFileHandleToPtr(h);
  137. const cmChar_t errMsg[] = "File byte count request failed.";
  138. errno = 0;
  139. if((f = fileno(p->fp)) == -1)
  140. {
  141. _cmFileError(p,kHandleInvalidFileRC,errno,errMsg);
  142. return 0;
  143. }
  144. if(fstat(f,&sr) == -1)
  145. {
  146. _cmFileError(p,kStatFailFileRC,errno,errMsg);
  147. return 0;
  148. }
  149. return sr.st_size;
  150. }
  151. cmFileRC_t cmFileByteCountFn( const cmChar_t* fn, cmRpt_t* rpt, unsigned* fileByteCntPtr )
  152. {
  153. assert( fileByteCntPtr != NULL );
  154. cmFileRC_t rc;
  155. cmFileH_t h = cmFileNullHandle;
  156. if((rc = cmFileOpen(&h,fn,kReadFileFl,rpt)) != kOkFileRC )
  157. return rc;
  158. if( fileByteCntPtr != NULL)
  159. *fileByteCntPtr = cmFileByteCount(h);
  160. cmFileClose(&h);
  161. return rc;
  162. }
  163. cmFileRC_t cmFileCompare( const cmChar_t* fn0, const cmChar_t* fn1, cmRpt_t* rpt, bool* isEqualPtr )
  164. {
  165. cmFileRC_t rc = kOkFileRC;
  166. unsigned bufByteCnt = 2048;
  167. cmFileH_t h0 = cmFileNullHandle;
  168. cmFileH_t h1 = cmFileNullHandle;
  169. char b0[ bufByteCnt ];
  170. char b1[ bufByteCnt ];
  171. assert(isEqualPtr != NULL );
  172. *isEqualPtr = true;
  173. if((rc = cmFileOpen(&h0,fn0,kReadFileFl,rpt)) != kOkFileRC )
  174. goto errLabel;
  175. if((rc = cmFileOpen(&h1,fn1,kReadFileFl,rpt)) != kOkFileRC )
  176. goto errLabel;
  177. cmFile_t* p0 = _cmFileHandleToPtr(h0);
  178. cmFile_t* p1 = _cmFileHandleToPtr(h1);
  179. while(1)
  180. {
  181. size_t n0 = fread(b0,1,bufByteCnt,p0->fp);
  182. size_t n1 = fread(b1,1,bufByteCnt,p1->fp);
  183. if( n0 != n1 || memcmp(b0,b1,n0)!=0 )
  184. {
  185. *isEqualPtr = false;
  186. break;
  187. }
  188. if( n0 != bufByteCnt || n1 != bufByteCnt )
  189. break;
  190. }
  191. errLabel:
  192. cmFileClose(&h0);
  193. cmFileClose(&h1);
  194. return rc;
  195. }
  196. const cmChar_t* cmFileName( cmFileH_t h )
  197. {
  198. cmFile_t* p = _cmFileHandleToPtr(h);
  199. return p->fnStr;
  200. }
  201. cmFileRC_t cmFileFnWrite( const cmChar_t* fn, cmRpt_t* rpt, const void* buf, unsigned bufByteCnt )
  202. {
  203. cmFileH_t h = cmFileNullHandle;
  204. cmFileRC_t rc;
  205. if((rc = cmFileOpen(&h,fn,kWriteFileFl,rpt)) != kOkFileRC )
  206. goto errLabel;
  207. rc = cmFileWrite(h,buf,bufByteCnt);
  208. errLabel:
  209. cmFileClose(&h);
  210. return rc;
  211. }
  212. cmChar_t* _cmFileToBuf( cmFileH_t h, unsigned nn, unsigned* bufByteCntPtr )
  213. {
  214. errno = 0;
  215. unsigned n = cmFileByteCount(h);
  216. cmChar_t* buf = NULL;
  217. cmFile_t* p = _cmFileHandleToPtr(h);
  218. // if the file size calculation is ok
  219. if( errno != 0 )
  220. {
  221. _cmFileError(p,kBufAllocFailFileRC,errno,"Invalid file buffer length.");
  222. goto errLabel;
  223. }
  224. // allocate the read target buffer
  225. if((buf = cmMemAlloc(cmChar_t,n+nn)) == NULL)
  226. {
  227. _cmFileError(p,kBufAllocFailFileRC,0,"Read buffer allocation failed.");
  228. goto errLabel;
  229. }
  230. // read the file
  231. if( cmFileRead(h,buf,n) != kOkFileRC )
  232. goto errLabel;
  233. // zero memory after the file data
  234. memset(buf+n,0,nn);
  235. if( bufByteCntPtr != NULL )
  236. *bufByteCntPtr = n;
  237. return buf;
  238. errLabel:
  239. if( bufByteCntPtr != NULL )
  240. *bufByteCntPtr = 0;
  241. cmMemFree(buf);
  242. return NULL;
  243. }
  244. cmChar_t* _cmFileFnToBuf( const cmChar_t* fn, cmRpt_t* rpt, unsigned nn, unsigned* bufByteCntPtr )
  245. {
  246. cmFileH_t h = cmFileNullHandle;
  247. cmChar_t* buf = NULL;
  248. if( cmFileOpen(&h,fn,kReadFileFl | kBinaryFileFl,rpt) != kOkFileRC )
  249. goto errLabel;
  250. buf = _cmFileToBuf(h,nn,bufByteCntPtr);
  251. errLabel:
  252. cmFileClose(&h);
  253. return buf;
  254. }
  255. cmChar_t* cmFileToBuf( cmFileH_t h, unsigned* bufByteCntPtr )
  256. { return _cmFileToBuf(h,0,bufByteCntPtr); }
  257. cmChar_t* cmFileFnToBuf( const cmChar_t* fn, cmRpt_t* rpt, unsigned* bufByteCntPtr )
  258. { return _cmFileFnToBuf(fn,rpt,0,bufByteCntPtr); }
  259. cmChar_t* cmFileToStr( cmFileH_t h, unsigned* bufByteCntPtr )
  260. { return _cmFileToBuf(h,1,bufByteCntPtr); }
  261. cmChar_t* cmFileFnToStr( const cmChar_t* fn, cmRpt_t* rpt, unsigned* bufByteCntPtr )
  262. { return _cmFileFnToBuf(fn,rpt,1,bufByteCntPtr); }
  263. cmFileRC_t cmFileLineCount( cmFileH_t h, unsigned* lineCntPtr )
  264. {
  265. cmFileRC_t rc = kOkFileRC;
  266. cmFile_t* p = _cmFileHandleToPtr(h);
  267. unsigned lineCnt = 0;
  268. long offs;
  269. int c;
  270. assert( lineCntPtr != NULL );
  271. *lineCntPtr = 0;
  272. if((rc = cmFileTell(h,&offs)) != kOkFileRC )
  273. return rc;
  274. errno = 0;
  275. while(1)
  276. {
  277. c = fgetc(p->fp);
  278. if( c == EOF )
  279. {
  280. if( errno )
  281. rc =_cmFileError(p,kReadFailFileRC,errno,"File read char failed");
  282. else
  283. ++lineCnt; // add one in case the last line isn't terminated with a '\n'.
  284. break;
  285. }
  286. // if an end-of-line was encountered
  287. if( c == '\n' )
  288. ++lineCnt;
  289. }
  290. if((rc = cmFileSeek(h,kBeginFileFl,offs)) != kOkFileRC )
  291. return rc;
  292. *lineCntPtr = lineCnt;
  293. return rc;
  294. }
  295. cmFileRC_t _cmFileGetLine( cmFile_t* p, cmChar_t* buf, unsigned* bufByteCntPtr )
  296. {
  297. // fgets() reads up to n-1 bytes into buf[]
  298. if( fgets(buf,*bufByteCntPtr,p->fp) == NULL )
  299. {
  300. // an read error or EOF condition occurred
  301. *bufByteCntPtr = 0;
  302. if( !feof(p->fp ) )
  303. return _cmFileError(p,kReadFailFileRC,errno,"File read line failed");
  304. return kReadFailFileRC;
  305. }
  306. return kOkFileRC;
  307. }
  308. cmFileRC_t cmFileGetLine( cmFileH_t h, cmChar_t* buf, unsigned* bufByteCntPtr )
  309. {
  310. assert( bufByteCntPtr != NULL );
  311. cmFile_t* p = _cmFileHandleToPtr(h);
  312. unsigned tn = 128;
  313. cmChar_t t[ tn ];
  314. unsigned on = *bufByteCntPtr;
  315. long offs;
  316. cmFileRC_t rc;
  317. // store the current file offset
  318. if((rc = cmFileTell(h,&offs)) != kOkFileRC )
  319. return rc;
  320. // if no buffer was given then use t[]
  321. if( buf == NULL || *bufByteCntPtr == 0 )
  322. {
  323. *bufByteCntPtr = tn;
  324. buf = t;
  325. }
  326. // fill the buffer from the current line
  327. if((rc = _cmFileGetLine(p,buf,bufByteCntPtr)) != kOkFileRC )
  328. return rc;
  329. // get length of the string in the buffer
  330. // (this is one less than the count of bytes written to the buffer)
  331. unsigned n = strlen(buf);
  332. // if the provided buffer was large enough to read the entire string
  333. if( on > n+1 )
  334. {
  335. //*bufByteCntPtr = n+1;
  336. return kOkFileRC;
  337. }
  338. //
  339. // the provided buffer was not large enough
  340. //
  341. // m tracks the length of the string
  342. unsigned m = n;
  343. while( n+1 == *bufByteCntPtr )
  344. {
  345. // fill the buffer from the current line
  346. if((rc = _cmFileGetLine(p,buf,bufByteCntPtr)) != kOkFileRC )
  347. return rc;
  348. n = strlen(buf);
  349. m += n;
  350. }
  351. // restore the original file offset
  352. if((rc = cmFileSeek(h,kBeginFileFl,offs)) != kOkFileRC )
  353. return rc;
  354. // add 1 for /0, 1 for /n and 1 to detect buf-too-short
  355. *bufByteCntPtr = m+3;
  356. return kBufTooSmallFileRC;
  357. }
  358. cmFileRC_t cmFileGetLineAuto( cmFileH_t h, cmChar_t** bufPtrPtr, unsigned* bufByteCntPtr )
  359. {
  360. cmFileRC_t rc = kOkFileRC;
  361. bool fl = true;
  362. cmChar_t* buf = *bufPtrPtr;
  363. *bufPtrPtr = NULL;
  364. while(fl)
  365. {
  366. fl = false;
  367. switch( rc = cmFileGetLine(h,buf,bufByteCntPtr) )
  368. {
  369. case kOkFileRC:
  370. {
  371. *bufPtrPtr = buf;
  372. }
  373. break;
  374. case kBufTooSmallFileRC:
  375. buf = cmMemResizeZ(cmChar_t,buf,*bufByteCntPtr);
  376. fl = true;
  377. break;
  378. default:
  379. cmMemFree(buf);
  380. break;
  381. }
  382. }
  383. return rc;
  384. }
  385. cmFileRC_t cmFileReadChar( cmFileH_t h, char* buf, unsigned cnt )
  386. { return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
  387. cmFileRC_t cmFileReadUChar( cmFileH_t h, unsigned char* buf, unsigned cnt )
  388. { return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
  389. cmFileRC_t cmFileReadShort( cmFileH_t h, short* buf, unsigned cnt )
  390. { return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
  391. cmFileRC_t cmFileReadUShort( cmFileH_t h, unsigned short* buf, unsigned cnt )
  392. { return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
  393. cmFileRC_t cmFileReadLong( cmFileH_t h, long* buf, unsigned cnt )
  394. { return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
  395. cmFileRC_t cmFileReadULong( cmFileH_t h, unsigned long* buf, unsigned cnt )
  396. { return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
  397. cmFileRC_t cmFileReadInt( cmFileH_t h, int* buf, unsigned cnt )
  398. { return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
  399. cmFileRC_t cmFileReadUInt( cmFileH_t h, unsigned int* buf, unsigned cnt )
  400. { return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
  401. cmFileRC_t cmFileReadFloat( cmFileH_t h, float* buf, unsigned cnt )
  402. { return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
  403. cmFileRC_t cmFileReadDouble( cmFileH_t h, double* buf, unsigned cnt )
  404. { return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
  405. cmFileRC_t cmFileReadBool( cmFileH_t h, bool* buf, unsigned cnt )
  406. { return cmFileRead(h,buf,sizeof(buf[0])*cnt); }
  407. cmFileRC_t cmFileWriteChar( cmFileH_t h, const char* buf, unsigned cnt )
  408. { return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
  409. cmFileRC_t cmFileWriteUChar( cmFileH_t h, const unsigned char* buf, unsigned cnt )
  410. { return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
  411. cmFileRC_t cmFileWriteShort( cmFileH_t h, const short* buf, unsigned cnt )
  412. { return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
  413. cmFileRC_t cmFileWriteUShort( cmFileH_t h, const unsigned short* buf, unsigned cnt )
  414. { return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
  415. cmFileRC_t cmFileWriteLong( cmFileH_t h, const long* buf, unsigned cnt )
  416. { return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
  417. cmFileRC_t cmFileWriteULong( cmFileH_t h, const unsigned long* buf, unsigned cnt )
  418. { return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
  419. cmFileRC_t cmFileWriteInt( cmFileH_t h, const int* buf, unsigned cnt )
  420. { return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
  421. cmFileRC_t cmFileWriteUInt( cmFileH_t h, const unsigned int* buf, unsigned cnt )
  422. { return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
  423. cmFileRC_t cmFileWriteFloat( cmFileH_t h, const float* buf, unsigned cnt )
  424. { return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
  425. cmFileRC_t cmFileWriteDouble( cmFileH_t h, const double* buf, unsigned cnt )
  426. { return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
  427. cmFileRC_t cmFileWriteBool( cmFileH_t h, const bool* buf, unsigned cnt )
  428. { return cmFileWrite(h,buf,sizeof(buf[0])*cnt); }
  429. cmFileRC_t cmFilePrint( cmFileH_t h, const cmChar_t* text )
  430. {
  431. cmFile_t* p = _cmFileHandleToPtr(h);
  432. errno = 0;
  433. if( fputs(text,p->fp) < 0 )
  434. return _cmFileError(p,kPrintFailFileRC,errno,"File print failed");
  435. return kOkFileRC;
  436. }
  437. cmFileRC_t cmFileVPrintf( cmFileH_t h, const cmChar_t* fmt, va_list vl )
  438. {
  439. cmFile_t* p = _cmFileHandleToPtr(h);
  440. if( vfprintf(p->fp,fmt,vl) < 0 )
  441. return _cmFileError(p,kPrintFailFileRC,errno,"File print failed");
  442. return kOkFileRC;
  443. }
  444. cmFileRC_t cmFilePrintf( cmFileH_t h, const cmChar_t* fmt, ... )
  445. {
  446. va_list vl;
  447. va_start(vl,fmt);
  448. cmFileRC_t rc = cmFileVPrintf(h,fmt,vl);
  449. va_end(vl);
  450. return rc;
  451. }