libcm is a C development framework with an emphasis on audio signal processing applications.
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

cmFile.c 15KB

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