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

cmFileSys.c 30KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285
  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 "cmLinkedHeap.h"
  9. #include "cmFileSys.h"
  10. #include <sys/stat.h>
  11. #include <errno.h>
  12. #include <libgen.h> // basename(), dirname()
  13. #include <dirent.h> // opendir()/readdir()
  14. #include <limits.h> // PATH_MAX
  15. #include <sys/types.h> // mkdir
  16. #ifdef OS_OSX
  17. #include "osx/cmFileSysOsx.h"
  18. #endif
  19. #ifdef OS_LINUX
  20. #include "linux/cmFileSysLinux.h"
  21. #endif
  22. cmFileSysH_t cmFileSysNullHandle = {NULL};
  23. typedef struct
  24. {
  25. cmErr_t err;
  26. cmLHeapH_t heapH;
  27. #ifdef OS_OSX
  28. _cmFsOsx_t* p;
  29. #endif
  30. #ifdef OS_LINUX
  31. _cmFsLinux_t* p;
  32. #endif
  33. } cmFs_t;
  34. cmFsRC_t _cmFileSysError( cmFs_t* p, cmFsRC_t rc, int sysErr, const cmChar_t* fmt, ... )
  35. {
  36. va_list vl;
  37. va_start(vl,fmt);
  38. if( sysErr == 0 )
  39. rc = cmErrVMsg(&p->err,rc,fmt,vl);
  40. else
  41. {
  42. const unsigned bufCharCnt = 511;
  43. cmChar_t buf[bufCharCnt+1];
  44. vsnprintf(buf,bufCharCnt,fmt,vl);
  45. rc = cmErrMsg(&p->err,rc,"%s\nSystem Msg:%s",buf,strerror(sysErr));
  46. }
  47. va_end(vl);
  48. return rc;
  49. }
  50. cmFs_t* _cmFileSysHandleToPtr( cmFileSysH_t h )
  51. {
  52. cmFs_t* p = (cmFs_t*)h.h;
  53. assert( p != NULL);
  54. return p;
  55. }
  56. cmFsRC_t _cmFileSysFinalize( cmFs_t* p )
  57. {
  58. cmFsRC_t rc = kOkFsRC;
  59. if( cmLHeapIsValid(p->heapH) )
  60. cmLHeapDestroy(&p->heapH);
  61. #ifdef OS_OSX
  62. if( p->p != NULL )
  63. if((rc = _cmOsxFileSysFinalize(p->p) ) != kOkFsRC )
  64. {
  65. _cmFileSysError(p,kOsxFailFsRC,0,"The OSX file system finalization failed.");
  66. return rc;
  67. }
  68. #endif
  69. #ifdef OS_LINUX
  70. if( p->p != NULL )
  71. if((rc = _cmLinuxFileSysFinalize(p->p) ) != kOkFsRC )
  72. {
  73. _cmFileSysError(p,kLinuxFailFsRC,0,"The Linux file system finalization failed.");
  74. return rc;
  75. }
  76. #endif
  77. cmMemPtrFree(&p);
  78. return rc;
  79. }
  80. cmFsRC_t cmFileSysInitialize( cmFileSysH_t* hp, cmCtx_t* ctx, const cmChar_t* appNameStr )
  81. {
  82. cmFs_t* p;
  83. cmFsRC_t rc;
  84. cmErr_t err;
  85. if((rc = cmFileSysFinalize(hp)) != kOkFsRC )
  86. return rc;
  87. cmErrSetup(&err,&ctx->rpt,"File System");
  88. if((p = cmMemAllocZ( cmFs_t, 1 )) == NULL )
  89. return cmErrMsg(&err,kMemAllocErrFsRC,"Unable to allocate the file system object.");
  90. cmErrClone(&p->err,&err);
  91. if(cmLHeapIsValid( p->heapH = cmLHeapCreate(1024,ctx)) == false )
  92. {
  93. rc = _cmFileSysError(p,kLHeapAllocErrFsRC,0,"Unable to allocate the linked heap.");
  94. goto errLabel;
  95. }
  96. #ifdef OS_OSX
  97. if( (rc = _cmOsxFileSysInit(&p->p, p->heapH, &p->err)) != kOkFsRC )
  98. {
  99. rc = _cmFileSysError(p,kOsxFailFsRC,0,"OSX file system initialization failed.");
  100. goto errLabel;
  101. }
  102. #endif
  103. #ifdef OS_LINUX
  104. if( (rc = _cmLinuxFileSysInit(&p->p, p->heapH, &p->err)) != kOkFsRC )
  105. {
  106. rc = _cmFileSysError(p,kLinuxFailFsRC,0,"Linux file system initialization failed.");
  107. goto errLabel;
  108. }
  109. else
  110. {
  111. #endif
  112. hp->h = p;
  113. #ifdef OS_LINUX
  114. cmChar_t hidAppNameStr[ strlen(appNameStr) + 2 ];
  115. strcpy(hidAppNameStr,".");
  116. strcat(hidAppNameStr,appNameStr);
  117. p->p->prefDir = cmFileSysMakeFn( *hp, p->p->prefDir, hidAppNameStr, NULL, NULL );
  118. // the resource directory must exist before the program can start
  119. p->p->rsrcDir = cmFileSysMakeFn( *hp, p->p->rsrcDir, appNameStr, NULL, NULL );
  120. }
  121. #endif
  122. errLabel:
  123. if( rc != kOkFsRC )
  124. return _cmFileSysFinalize(p);
  125. return kOkFsRC;
  126. }
  127. cmFsRC_t cmFileSysFinalize( cmFileSysH_t* hp )
  128. {
  129. cmFsRC_t rc;
  130. if( hp==NULL || cmFileSysIsValid(*hp) == false )
  131. return kOkFsRC;
  132. cmFs_t* p = _cmFileSysHandleToPtr(*hp);
  133. if((rc = _cmFileSysFinalize(p)) != kOkFsRC )
  134. return rc;
  135. hp->h = NULL;
  136. return rc;
  137. }
  138. const cmChar_t* cmFileSysPrefsDir( cmFileSysH_t h )
  139. {
  140. cmFs_t* p = _cmFileSysHandleToPtr(h);
  141. #if defined OS_OSX || defined OS_LINUX
  142. return p->p->prefDir;
  143. #else
  144. return NULL;
  145. #endif
  146. }
  147. const cmChar_t* cmFileSysRsrcDir( cmFileSysH_t h )
  148. {
  149. cmFs_t* p = _cmFileSysHandleToPtr(h);
  150. #if defined OS_OSX || defined OS_LINUX
  151. return p->p->rsrcDir;
  152. #else
  153. return NULL;
  154. #endif
  155. }
  156. const cmChar_t* cmFileSysUserDir( cmFileSysH_t h )
  157. {
  158. cmFs_t* p = _cmFileSysHandleToPtr(h);
  159. #if defined OS_OSX || defined OS_LINUX
  160. return p->p->userDir;
  161. #else
  162. return NULL;
  163. #endif
  164. }
  165. bool cmFileSysIsValid( cmFileSysH_t h )
  166. { return h.h != NULL; }
  167. bool _cmFileSysIsDir( cmFs_t* p, const cmChar_t* dirStr )
  168. {
  169. struct stat s;
  170. errno = 0;
  171. if( stat(dirStr,&s) != 0 )
  172. {
  173. // if the dir does not exist
  174. if( errno == ENOENT )
  175. return false;
  176. _cmFileSysError( p, kStatFailFsRC, errno, "'stat' failed on '%s'",dirStr);
  177. return false;
  178. }
  179. return S_ISDIR(s.st_mode);
  180. }
  181. bool cmFileSysIsDir( cmFileSysH_t h, const cmChar_t* dirStr )
  182. {
  183. cmFs_t* p = _cmFileSysHandleToPtr(h);
  184. return _cmFileSysIsDir(p,dirStr);
  185. }
  186. bool _cmFileSysIsFile( cmFs_t* p, const cmChar_t* fnStr )
  187. {
  188. struct stat s;
  189. errno = 0;
  190. if( stat(fnStr,&s) != 0 )
  191. {
  192. // if the file does not exist
  193. if( errno == ENOENT )
  194. return false;
  195. _cmFileSysError( p, kStatFailFsRC, errno, "'stat' failed on '%s'.",fnStr);
  196. return false;
  197. }
  198. return S_ISREG(s.st_mode);
  199. }
  200. bool cmFileSysIsFile( cmFileSysH_t h, const cmChar_t* fnStr )
  201. {
  202. cmFs_t* p = _cmFileSysHandleToPtr(h);
  203. return _cmFileSysIsFile(p,fnStr);
  204. }
  205. bool _cmFileSysIsLink( cmFs_t* p, const cmChar_t* fnStr )
  206. {
  207. struct stat s;
  208. errno = 0;
  209. if( stat(fnStr,&s) != 0 )
  210. {
  211. // if the file does not exist
  212. if( errno == ENOENT )
  213. return false;
  214. _cmFileSysError( p, kStatFailFsRC, errno, "'stat' failed on '%s'.",fnStr);
  215. return false;
  216. }
  217. return S_ISLNK(s.st_mode);
  218. }
  219. bool cmFileSysIsLink( cmFileSysH_t h, const cmChar_t* fnStr )
  220. {
  221. cmFs_t* p = _cmFileSysHandleToPtr(h);
  222. return _cmFileSysIsLink(p,fnStr);
  223. }
  224. bool _cmFileSysConcat( cmChar_t* rp, unsigned rn, char sepChar, const cmChar_t* suffixStr )
  225. {
  226. unsigned m = strlen(rp);
  227. // m==0 if no sep needs to be inserted or removed
  228. //if( m == 0 )
  229. // return false;
  230. if( m != 0 )
  231. {
  232. // if a sep char needs to be inserted
  233. if( rp[m-1] != sepChar && suffixStr[0] != sepChar )
  234. {
  235. assert((m+1)<rn);
  236. if((m+1)>=rn)
  237. return false;
  238. rp[m] = sepChar;
  239. rp[m+1]= 0;
  240. ++m;
  241. }
  242. else
  243. // if a sep char needs to be removed
  244. if( rp[m-1] == sepChar && suffixStr[0] == sepChar )
  245. {
  246. rp[m-1] = 0;
  247. --m;
  248. }
  249. }
  250. assert( rn>=m && strlen(rp)+strlen(suffixStr) <= rn );
  251. strncat(rp,suffixStr,rn-m);
  252. return true;
  253. }
  254. const cmChar_t* cmFileSysVMakeFn( cmFileSysH_t h, const cmChar_t* dir, const cmChar_t* fn, const cmChar_t* ext, va_list vl )
  255. {
  256. cmFsRC_t rc = kOkFsRC;
  257. cmChar_t* rp = NULL;
  258. const cmChar_t* dp = NULL;
  259. unsigned n = 0;
  260. cmFs_t* p = _cmFileSysHandleToPtr(h);
  261. char pathSep = cmPathSeparatorChar[0];
  262. char extSep = '.';
  263. va_list vl_t;
  264. va_copy(vl_t,vl);
  265. assert( fn != NULL );
  266. // get prefix directory length
  267. if( dir != NULL )
  268. n += strlen(dir) + 1; // add 1 for ending sep
  269. // get file name length
  270. n += strlen(fn);
  271. // get extension length
  272. if( ext != NULL )
  273. n += strlen(ext) + 1; // add 1 for period
  274. // get length of all var args dir's
  275. while( (dp = va_arg(vl,const cmChar_t*)) != NULL )
  276. n += strlen(dp) + 1; // add 1 for ending sep
  277. // add 1 for terminating zero and allocate memory
  278. if((rp = cmLHeapAllocZ( p->heapH, n+1 )) == NULL )
  279. {
  280. rc = _cmFileSysError(p,kMemAllocErrFsRC,0,"Unable to allocate file name memory.");
  281. goto errLabel;
  282. }
  283. va_copy(vl,vl_t);
  284. rp[n] = 0;
  285. rp[0] = 0;
  286. // copy out the prefix dir
  287. if( dir != NULL )
  288. strncat(rp,dir,n-strlen(rp));
  289. // copy out ecmh of the var arg's directories
  290. while((dp = va_arg(vl,const cmChar_t*)) != NULL )
  291. if(!_cmFileSysConcat(rp,n,pathSep,dp) )
  292. {
  293. assert(0);
  294. rc = _cmFileSysError(p,kAssertFailFsRC,0,"Assert failed.");
  295. goto errLabel;
  296. }
  297. // copy out the file name
  298. if(!_cmFileSysConcat(rp,n,pathSep,fn))
  299. {
  300. assert(0);
  301. rc = _cmFileSysError(p,kAssertFailFsRC,0,"Assert failed.");
  302. goto errLabel;
  303. }
  304. // copy out the extension
  305. if( ext != NULL )
  306. if(!_cmFileSysConcat(rp,n,extSep,ext))
  307. {
  308. assert(0);
  309. rc = _cmFileSysError(p,kAssertFailFsRC,0,"Assert failed.");
  310. goto errLabel;
  311. }
  312. assert(strlen(rp)<=n);
  313. errLabel:
  314. if( rc != kOkFsRC && rp != NULL )
  315. cmLHeapFree(p->heapH, rp );
  316. return rp;
  317. }
  318. const cmChar_t* cmFileSysMakeFn( cmFileSysH_t h, const cmChar_t* dir, const cmChar_t* fn, const cmChar_t* ext, ... )
  319. {
  320. va_list vl;
  321. va_start(vl,ext);
  322. const cmChar_t* retPtr = cmFileSysVMakeFn(h,dir,fn,ext,vl);
  323. va_end(vl);
  324. return retPtr;
  325. }
  326. void cmFileSysFreeFn( cmFileSysH_t h, const cmChar_t* fn )
  327. {
  328. cmFs_t* p = _cmFileSysHandleToPtr(h);
  329. if( fn == NULL )
  330. return;
  331. cmLHeapFree(p->heapH, (void*)fn);
  332. }
  333. cmFsRC_t cmFileSysMkDir( cmFileSysH_t h, const cmChar_t* dir )
  334. {
  335. cmFs_t* p = _cmFileSysHandleToPtr(h);
  336. if( mkdir(dir, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0 )
  337. return _cmFileSysError( p, kMkDirFailFsRC, errno, "The attempt to create the directory '%s' failed.",dir);
  338. return kOkFsRC;
  339. }
  340. cmFsRC_t cmFileSysMkDirAll( cmFileSysH_t h, const cmChar_t* dir )
  341. {
  342. cmFsRC_t rc = kOkFsRC;
  343. cmFs_t* p = _cmFileSysHandleToPtr(h);
  344. cmChar_t** a = NULL;
  345. unsigned i;
  346. if((a = cmFileSysDirParts(h,dir)) == NULL )
  347. return _cmFileSysError(p, kInvalidDirFsRC, 0, "The directory '%s' could not be parsed.",cmStringNullGuard(dir));
  348. for(i=0; rc==kOkFsRC && a[i]!=NULL; ++i)
  349. {
  350. cmChar_t* d = cmFileSysFormDir(h,a,i+1);
  351. if( cmFileSysIsDir(h,d)==false )
  352. if((rc = cmFileSysMkDir(h,d)) != kOkFsRC )
  353. break;
  354. cmFileSysFreeDir(h,d);
  355. }
  356. cmFileSysFreeDirParts(h,a);
  357. return rc;
  358. }
  359. cmFileSysPathPart_t* cmFileSysPathParts( cmFileSysH_t h, const cmChar_t* pathStr )
  360. {
  361. int n = 0; // char's in pathStr
  362. int dn = 0; // char's in the dir part
  363. int fn = 0; // char's in the name part
  364. int en = 0; // char's in the ext part
  365. cmChar_t* cp = NULL;
  366. cmFileSysPathPart_t* rp = NULL;
  367. cmFs_t* p = _cmFileSysHandleToPtr(h);
  368. if( pathStr==NULL )
  369. return NULL;
  370. // skip leading white space
  371. while( *pathStr )
  372. {
  373. if( isspace(*pathStr ) )
  374. ++pathStr;
  375. else
  376. break;
  377. }
  378. // get the length of pathStr
  379. if((n = strlen(pathStr)) == 0 )
  380. return NULL;
  381. // remove trailing spaces
  382. for(; n>0; --n)
  383. {
  384. if( isspace(pathStr[n-1]) )
  385. --n;
  386. else
  387. break;
  388. }
  389. //
  390. if( n == 0 )
  391. return NULL;
  392. char buf[n+1];
  393. buf[n] = 0;
  394. // Get the last word (which may be a file name) from pathStr.
  395. // (pathStr must be copied into a buf because basename()
  396. // is allowed to change the values in its arg.)
  397. strncpy(buf,pathStr,n);
  398. cp = basename(buf);
  399. if( cp != NULL )
  400. {
  401. cmChar_t* ep;
  402. // does the last word have a period in it
  403. if( (ep = strchr(cp,'.')) != NULL )
  404. {
  405. *ep = 0; // end the file name at the period
  406. ++ep; // set the ext marker
  407. en = strlen(ep); // get the length of the ext
  408. }
  409. fn = strlen(cp); //get the length of the file name
  410. }
  411. // Get the directory part.
  412. // ( pathStr must be copied into a buf because dirname()
  413. // is allowed to change the values in its arg.)
  414. strncpy(buf,pathStr,n);
  415. // if the last char in pathStr[] is '/' then the whole string is a dir.
  416. // (this is not the answer that dirname() and basename() would give).
  417. if( pathStr[n-1] == cmPathSeparatorChar[0] )
  418. {
  419. fn = 0;
  420. en = 0;
  421. dn = n;
  422. cp = buf;
  423. }
  424. else
  425. {
  426. cp = dirname(buf);
  427. }
  428. if( cp != NULL )
  429. dn = strlen(cp);
  430. // get the total size of the returned memory. (add 3 for ecmh possible terminating zero)
  431. n = sizeof(cmFileSysPathPart_t) + dn + fn + en + 3;
  432. // alloc memory
  433. if((rp = (cmFileSysPathPart_t*)cmLHeapAllocZ( p->heapH, n )) == NULL )
  434. {
  435. _cmFileSysError( p, kLHeapAllocErrFsRC, 0, "Unable to allocate the file system path part record for '%s'.",pathStr);
  436. return NULL;
  437. }
  438. // set the return pointers for ecmh of the parts
  439. rp->dirStr = (const cmChar_t* )(rp + 1);
  440. rp->fnStr = rp->dirStr + dn + 1;
  441. rp->extStr = rp->fnStr + fn + 1;
  442. // if there is a directory part
  443. if( dn>0 )
  444. strcpy((cmChar_t*)rp->dirStr,cp);
  445. else
  446. rp->dirStr = NULL;
  447. if( fn || en )
  448. {
  449. // Get the trailing word again.
  450. // pathStr must be copied into a buf because basename() may
  451. // is allowed to change the values in its arg.
  452. strncpy(buf,pathStr,n);
  453. cp = basename(buf);
  454. if( cp != NULL )
  455. {
  456. cmChar_t* ep;
  457. if( (ep = strchr(cp,'.')) != NULL )
  458. {
  459. *ep = 0;
  460. ++ep;
  461. assert( strlen(ep) == en );
  462. strcpy((cmChar_t*)rp->extStr,ep);
  463. }
  464. assert( strlen(cp) == fn );
  465. if(fn)
  466. strcpy((cmChar_t*)rp->fnStr,cp);
  467. }
  468. }
  469. if( fn == 0 )
  470. rp->fnStr = NULL;
  471. if( en == 0 )
  472. rp->extStr = NULL;
  473. return rp;
  474. }
  475. void cmFileSysFreePathParts( cmFileSysH_t h, cmFileSysPathPart_t* pp )
  476. {
  477. if( pp == NULL )
  478. return;
  479. cmFs_t* p = _cmFileSysHandleToPtr(h);
  480. cmLHeapFree(p->heapH, (void*)pp );
  481. }
  482. cmChar_t** cmFileSysDirParts( cmFileSysH_t h, const cmChar_t* dirStr )
  483. {
  484. cmFs_t* p = _cmFileSysHandleToPtr(h);
  485. const cmChar_t* s = dirStr;
  486. const cmChar_t* ep = dirStr + strlen(dirStr);
  487. char pathSep = cmPathSeparatorChar[0];
  488. unsigned n = 2;
  489. unsigned i = 0;
  490. // skip leading white space or pathsep
  491. while( isspace(*s) && s < ep )
  492. ++s;
  493. // if the directory string is empty
  494. if( s >= ep )
  495. return NULL;
  496. // set the beginning of the input dirStr past any leading white space
  497. dirStr = s;
  498. // count the number of dir segments - this might overcount the
  499. // number of segments if there are multiple repeated path seperator
  500. // char's - but this is ok because 'n' is only used to determine
  501. // the size of the returned array - which will simply have some
  502. // NULL entries at the end.
  503. for(; s < ep; ++s )
  504. if( *s == pathSep )
  505. ++n;
  506. // allocate the array
  507. cmChar_t** a = cmLhAllocZ(p->heapH,cmChar_t*,n);
  508. // reset the cur location to the begin of the buf
  509. s = dirStr;
  510. // if the path starts at the root
  511. if( *s == pathSep )
  512. {
  513. a[0] = cmPathSeparatorChar; // cmPathSeparatorChar is a static string in cmGlobal.h
  514. i = 1;
  515. ++s;
  516. }
  517. for(; i<n && s<ep; ++i)
  518. {
  519. const cmChar_t* s1;
  520. if(( s1 = strchr(s,pathSep)) == NULL )
  521. s1 = ep;
  522. if( s1!=s )
  523. {
  524. unsigned sn = (s1 - s)+1;
  525. a[i] = cmLhAlloc(p->heapH,cmChar_t,sn);
  526. strncpy(a[i],s,sn-1);
  527. a[i][sn-1] = 0;
  528. }
  529. s = s1+1;
  530. }
  531. return a;
  532. }
  533. void cmFileSysFreeDirParts( cmFileSysH_t h, cmChar_t** dirStrArray )
  534. {
  535. if( dirStrArray == NULL )
  536. return;
  537. cmFs_t* p = _cmFileSysHandleToPtr(h);
  538. unsigned i;
  539. for(i=0; dirStrArray[i]!=NULL; ++i)
  540. {
  541. // cmPathSeparatorChar is statically alloc'd in cmGlobal.h
  542. if( dirStrArray[i] != cmPathSeparatorChar )
  543. cmLHeapFree(p->heapH,dirStrArray[i]);
  544. }
  545. cmLHeapFree(p->heapH, (void*)dirStrArray );
  546. }
  547. unsigned cmFileSysDirPartsCount(cmFileSysH_t h, cmChar_t** dirStrArray )
  548. {
  549. unsigned i = 0;
  550. if( dirStrArray == NULL )
  551. return 0;
  552. while(dirStrArray[i] != NULL )
  553. ++i;
  554. return i;
  555. }
  556. cmChar_t* cmFileSysFormDir( cmFileSysH_t h, cmChar_t** a, unsigned m )
  557. {
  558. cmFs_t* p = _cmFileSysHandleToPtr(h);
  559. unsigned n;
  560. unsigned i;
  561. // determine the length of the return string
  562. for(i=0,n=0; a[i]!=NULL && i<m; ++i)
  563. n += strlen(a[i]) + strlen(cmPathSeparatorChar);
  564. if( i<m && a[i] == NULL )
  565. {
  566. _cmFileSysError(p,kInvalidDirFsRC,0,"cmFileSysFormDir() cannot form a directory string from %i parts when only %i exist.",m,i);
  567. return NULL;
  568. }
  569. n += 1;
  570. // allocate the return string
  571. cmChar_t* r = cmLhAllocZ(p->heapH,cmChar_t,n);
  572. const cmChar_t* ep = r + n;
  573. // form the return string
  574. for(i=0; a[i]!=NULL && i<m; ++i)
  575. {
  576. strcat(r,a[i]);
  577. if( strcmp(a[i],cmPathSeparatorChar)!=0 && a[i+1]!=NULL )
  578. strcat(r,cmPathSeparatorChar);
  579. assert( r + strlen(r) <= ep );
  580. }
  581. return r;
  582. }
  583. void cmFileSysFreeDir( cmFileSysH_t h, const cmChar_t* dir )
  584. {
  585. if( dir == NULL )
  586. return;
  587. cmFs_t* p = _cmFileSysHandleToPtr(h);
  588. cmLHeapFree(p->heapH,(void*)dir);
  589. }
  590. typedef struct
  591. {
  592. cmFs_t* p;
  593. unsigned filterFlags;
  594. cmFileSysDirEntry_t* rp;
  595. cmChar_t* dataPtr;
  596. cmChar_t* endPtr;
  597. unsigned entryCnt;
  598. unsigned entryIdx;
  599. unsigned dataByteCnt;
  600. unsigned passIdx;
  601. } cmFileSysDeRecd_t;
  602. cmFsRC_t _cmFileSysDirGetEntries( cmFileSysDeRecd_t* drp, const cmChar_t* dirStr )
  603. {
  604. cmFsRC_t rc = kOkFsRC;
  605. DIR* dirp = NULL;
  606. struct dirent* dp = NULL;
  607. char curDirPtr[] = "./";
  608. unsigned dn = 0;
  609. if( dirStr == NULL || strlen(dirStr) == 0 )
  610. dirStr = curDirPtr;
  611. unsigned fnCharCnt= strlen(dirStr) + PATH_MAX;
  612. char fn[ fnCharCnt + 1 ];
  613. // copy the directory into fn[] ...
  614. fn[0] =0;
  615. fn[fnCharCnt] = 0;
  616. strcpy(fn,dirStr);
  617. assert( strlen(fn)+2 < fnCharCnt );
  618. // ... and be sure that it is terminated with a path sep char
  619. if( fn[ strlen(fn)-1 ] != cmPathSeparatorChar[0] )
  620. strcat(fn,cmPathSeparatorChar);
  621. // file names will be appended to the path at this location
  622. unsigned fni = strlen(fn);
  623. // open the directory
  624. if((dirp = opendir(dirStr)) == NULL)
  625. {
  626. rc = _cmFileSysError(drp->p,kOpenDirFailFsRC,errno,"Unable to open the directory:'%s'.",dirStr);
  627. goto errLabel;
  628. }
  629. // get the next directory entry
  630. while((dp = readdir(dirp)) != NULL )
  631. {
  632. // validate d_name
  633. if( (dp->d_name != NULL) && ((dn = strlen(dp->d_name)) > 0) )
  634. {
  635. unsigned flags = 0;
  636. // handle cases where d_name begins with '.'
  637. if( dp->d_name[0] == '.' )
  638. {
  639. if( strcmp(dp->d_name,".") == 0 )
  640. {
  641. if( cmIsFlag(drp->filterFlags,kCurDirFsFl) == false )
  642. continue;
  643. flags |= kCurDirFsFl;
  644. }
  645. if( strcmp(dp->d_name,"..") == 0 )
  646. {
  647. if( cmIsFlag(drp->filterFlags,kParentDirFsFl) == false )
  648. continue;
  649. flags |= kParentDirFsFl;
  650. }
  651. if( flags == 0 )
  652. {
  653. if( cmIsFlag(drp->filterFlags,kInvisibleFsFl) == false )
  654. continue;
  655. flags |= kInvisibleFsFl;
  656. }
  657. }
  658. fn[fni] = 0;
  659. strncat( fn, dp->d_name, fnCharCnt-fni );
  660. unsigned fnN = strlen(fn);
  661. // if the filename is too long for the buffer
  662. if( fnN > fnCharCnt )
  663. {
  664. rc = _cmFileSysError(drp->p, kFnTooLongFsRC, errno, "The directory entry:'%s' was too long to be processed.",dp->d_name);
  665. goto errLabel;
  666. }
  667. // is the entry a file
  668. if( _cmFileSysIsFile(drp->p,fn) )
  669. {
  670. if( cmIsFlag(drp->filterFlags,kFileFsFl)==false )
  671. continue;
  672. flags |= kFileFsFl;
  673. }
  674. else
  675. {
  676. // is the entry a dir
  677. if( _cmFileSysIsDir(drp->p,fn) )
  678. {
  679. if( cmIsFlag(drp->filterFlags,kDirFsFl) == false)
  680. continue;
  681. flags |= kDirFsFl;
  682. if( cmIsFlag(drp->filterFlags,kRecurseFsFl) )
  683. if((rc = _cmFileSysDirGetEntries(drp,fn)) != kOkFsRC )
  684. goto errLabel;
  685. }
  686. }
  687. assert(flags != 0);
  688. if( drp->passIdx == 0 )
  689. {
  690. ++drp->entryCnt;
  691. // add 1 for the name terminating zero
  692. drp->dataByteCnt += sizeof(cmFileSysDirEntry_t) + 1;
  693. if( cmIsFlag(drp->filterFlags,kFullPathFsFl) )
  694. drp->dataByteCnt += fnN;
  695. else
  696. drp->dataByteCnt += dn;
  697. }
  698. else
  699. {
  700. assert( drp->passIdx == 1 );
  701. assert( drp->entryIdx < drp->entryCnt );
  702. unsigned n = 0;
  703. if( cmIsFlag(drp->filterFlags,kFullPathFsFl) )
  704. {
  705. n = fnN+1;
  706. assert( drp->dataPtr + n <= drp->endPtr );
  707. strcpy(drp->dataPtr,fn);
  708. }
  709. else
  710. {
  711. n = dn+1;
  712. assert( drp->dataPtr + n <= drp->endPtr );
  713. strcpy(drp->dataPtr,dp->d_name);
  714. }
  715. drp->rp[ drp->entryIdx ].flags = flags;
  716. drp->rp[ drp->entryIdx ].name = drp->dataPtr;
  717. drp->dataPtr += n;
  718. assert( drp->dataPtr <= drp->endPtr);
  719. ++drp->entryIdx;
  720. }
  721. }
  722. }
  723. errLabel:
  724. return rc;
  725. }
  726. cmFileSysDirEntry_t* cmFileSysDirEntries( cmFileSysH_t h, const cmChar_t* dirStr, unsigned filterFlags, unsigned* dirEntryCntPtr )
  727. {
  728. cmFsRC_t rc = kOkFsRC;
  729. cmFileSysDeRecd_t r;
  730. memset(&r,0,sizeof(r));
  731. r.p = _cmFileSysHandleToPtr(h);
  732. r.filterFlags = filterFlags;
  733. assert( dirEntryCntPtr != NULL );
  734. *dirEntryCntPtr = 0;
  735. for(r.passIdx=0; r.passIdx<2; ++r.passIdx)
  736. {
  737. if((rc = _cmFileSysDirGetEntries( &r, dirStr )) != kOkFsRC )
  738. goto errLabel;
  739. if( r.passIdx == 0 )
  740. {
  741. // allocate memory to hold the return values
  742. if(( r.rp = (cmFileSysDirEntry_t *)cmLHeapAllocZ( r.p->heapH, r.dataByteCnt )) == NULL )
  743. {
  744. rc= _cmFileSysError( r.p, kMemAllocErrFsRC, 0, "Unable to allocate %i bytes of dir entry memory.",r.dataByteCnt);
  745. goto errLabel;
  746. }
  747. r.dataPtr = (cmChar_t*)(r.rp + r.entryCnt);
  748. r.endPtr = ((cmChar_t*)r.rp) + r.dataByteCnt;
  749. }
  750. }
  751. errLabel:
  752. if( rc == kOkFsRC )
  753. {
  754. assert( r.entryIdx == r.entryCnt );
  755. *dirEntryCntPtr = r.entryCnt;
  756. }
  757. else
  758. {
  759. if( r.rp != NULL )
  760. cmLHeapFree(r.p->heapH,r.rp);
  761. r.rp = NULL;
  762. }
  763. return r.rp;
  764. }
  765. void cmFileSysDirFreeEntries( cmFileSysH_t h, cmFileSysDirEntry_t* dp )
  766. {
  767. cmFs_t* p = _cmFileSysHandleToPtr(h);
  768. if( dp != NULL )
  769. cmLHeapFree(p->heapH,dp);
  770. }
  771. cmFsRC_t cmFileSysErrorCode( cmFileSysH_t h )
  772. {
  773. cmFs_t* p = _cmFileSysHandleToPtr(h);
  774. return cmErrLastRC(&p->err);
  775. }
  776. //
  777. //======================================================================================================
  778. // Begin global versions
  779. //
  780. cmFileSysH_t _cmFsH = { NULL };
  781. cmFsRC_t cmFsInitialize( cmCtx_t* ctx, const cmChar_t* appNameStr )
  782. { return cmFileSysInitialize(&_cmFsH,ctx,appNameStr); }
  783. cmFsRC_t cmFsFinalize()
  784. { return cmFileSysFinalize(&_cmFsH); }
  785. const cmChar_t* cmFsPrefsDir()
  786. { return cmFileSysPrefsDir(_cmFsH); }
  787. const cmChar_t* cmFsRsrcDir()
  788. { return cmFileSysRsrcDir(_cmFsH); }
  789. const cmChar_t* cmFsUserDir()
  790. { return cmFileSysUserDir(_cmFsH); }
  791. bool cmFsIsDir( const cmChar_t* dirStr )
  792. { return cmFileSysIsDir(_cmFsH,dirStr); }
  793. bool cmFsIsFile( const cmChar_t* fnStr )
  794. { return cmFileSysIsFile(_cmFsH,fnStr); }
  795. bool cmFsIsLink( const cmChar_t* fnStr )
  796. { return cmFileSysIsLink(_cmFsH,fnStr); }
  797. const cmChar_t* cmFsVMakeFn( const cmChar_t* dirPrefix, const cmChar_t* fn, const cmChar_t* ext, va_list vl )
  798. { return cmFileSysVMakeFn(_cmFsH,dirPrefix,fn,ext,vl); }
  799. const cmChar_t* cmFsMakeFn( const cmChar_t* dirPrefix, const cmChar_t* fn, const cmChar_t* ext, ... )
  800. {
  801. va_list vl;
  802. va_start(vl,ext);
  803. const cmChar_t* retPtr = cmFsVMakeFn(dirPrefix,fn,ext,vl);
  804. va_end(vl);
  805. return retPtr;
  806. }
  807. void cmFsFreeFn( const cmChar_t* fn )
  808. { cmFileSysFreeFn(_cmFsH, fn); }
  809. cmFsRC_t cmFsMkDir( const cmChar_t* dir )
  810. { return cmFileSysMkDir(_cmFsH,dir); }
  811. cmFsRC_t cmFsMkDirAll( const cmChar_t* dir )
  812. { return cmFileSysMkDirAll(_cmFsH,dir); }
  813. cmFileSysPathPart_t* cmFsPathParts( const cmChar_t* pathNameStr )
  814. { return cmFileSysPathParts(_cmFsH,pathNameStr); }
  815. void cmFsFreePathParts( cmFileSysPathPart_t* p )
  816. { cmFileSysFreePathParts(_cmFsH,p); }
  817. cmChar_t** cmFsDirParts( const cmChar_t* dirStr )
  818. { return cmFileSysDirParts(_cmFsH,dirStr); }
  819. void cmFsFreeDirParts( cmChar_t** dirStrArray )
  820. { cmFileSysFreeDirParts(_cmFsH,dirStrArray); }
  821. unsigned cmFsDirPartsCount( cmChar_t** dirStrArray )
  822. { return cmFileSysDirPartsCount(_cmFsH, dirStrArray); }
  823. cmChar_t* cmFsFormDir( cmChar_t** dirStrArray, unsigned n )
  824. { return cmFileSysFormDir(_cmFsH,dirStrArray,n); }
  825. void cmFsFreeDir( const cmChar_t* dir )
  826. { cmFileSysFreeDir(_cmFsH,dir); }
  827. cmFileSysDirEntry_t* cmFsDirEntries( const cmChar_t* dirStr, unsigned includeFlags, unsigned* dirEntryCntPtr )
  828. { return cmFileSysDirEntries(_cmFsH,dirStr,includeFlags,dirEntryCntPtr); }
  829. void cmFsDirFreeEntries( cmFileSysDirEntry_t* p )
  830. { cmFileSysDirFreeEntries(_cmFsH,p); }
  831. cmFsRC_t cmFsErrorCode()
  832. { return cmFileSysErrorCode(_cmFsH); }
  833. // end global version
  834. //======================================================================================================
  835. //
  836. //{ { label:cmFileSysEx }
  837. //(
  838. //
  839. // cmFileSysTest() function gives usage and testing examples
  840. // for some of the cmFileSys functions.
  841. // Note that the HOME_DIR macro should be set to your true
  842. // $HOME directroy and SRC_DIR should be set to any existing
  843. // and accessible directory.
  844. //
  845. //)
  846. //(
  847. #if defined(OS_OSX)
  848. #define HOME_DIR "/Users/kevin"
  849. #else
  850. #define HOME_DIR "/home/kevin"
  851. #endif
  852. #define SRC_DIR HOME_DIR"/src"
  853. void _cmFileSysTestFnParser(
  854. cmFileSysH_t h,
  855. cmRpt_t* rpt,
  856. const cmChar_t* fn );
  857. cmFsRC_t cmFileSysTest( cmCtx_t* ctx )
  858. {
  859. // The global heap manager must have been initialized
  860. // via cmMdInitialize() prior to running this function.
  861. cmFsRC_t rc = kOkFsRC;
  862. cmFileSysH_t h = cmFileSysNullHandle;
  863. const char dir0[] = SRC_DIR;
  864. const char dir1[] = HOME_DIR"/blah";
  865. const char file0[] = HOME_DIR"/.emacs";
  866. const char file1[] = HOME_DIR"/blah.txt";
  867. const char not[] = " not ";
  868. const char e[] = " ";
  869. bool fl = false;
  870. const cmChar_t* fn = NULL;
  871. // Initialize the file system.
  872. if((rc = cmFileSysInitialize(&h,ctx,"fs_test")) != kOkFsRC )
  873. return rc;
  874. //----------------------------------------------------------
  875. // Print the standard directories
  876. //
  877. printf("Prefs Dir:%s\n",cmFsPrefsDir());
  878. printf("Rsrc Dir: %s\n",cmFsRsrcDir());
  879. printf("User Dir: %s\n",cmFsUserDir());
  880. //----------------------------------------------------------
  881. // Run the file system type checker
  882. //
  883. fl = cmFileSysIsDir(h,dir0);
  884. printf("'%s' is%sa directory.\n",dir0, (fl ? e : not));
  885. fl = cmFileSysIsDir(h,dir1);
  886. printf("'%s' is%sa directory.\n",dir1, (fl ? e : not));
  887. fl = cmFileSysIsFile(h,file0);
  888. printf("'%s' is%sa file.\n",file0, (fl ? e : not));
  889. fl = cmFileSysIsFile(h,file1);
  890. printf("'%s' is%sa file.\n",file1, (fl ? e : not));
  891. //----------------------------------------------------------
  892. // Test the file name creation functions
  893. //
  894. if((fn = cmFileSysMakeFn(h,HOME_DIR,"cmFileSys",
  895. "c","src","cm","src",NULL)) != NULL)
  896. {
  897. printf("File:'%s'\n",fn);
  898. }
  899. cmFileSysFreeFn(h,fn);
  900. if((fn = cmFileSysMakeFn(h,HOME_DIR,"cmFileSys",
  901. ".c","/src/","/cm/","/src/",NULL)) != NULL )
  902. {
  903. printf("File:'%s'\n",fn);
  904. }
  905. cmFileSysFreeFn(h,fn);
  906. //----------------------------------------------------------
  907. // Test the file name parsing functions
  908. //
  909. _cmFileSysTestFnParser(h,&ctx->rpt,
  910. HOME_DIR"/src/cm/src/cmFileSys.c");
  911. _cmFileSysTestFnParser(h,&ctx->rpt,
  912. HOME_DIR"/src/cm/src/cmFileSys");
  913. _cmFileSysTestFnParser(h,&ctx->rpt,
  914. HOME_DIR"/src/cm/src/cmFileSys/");
  915. _cmFileSysTestFnParser(h,&ctx->rpt,"cmFileSys.c");
  916. _cmFileSysTestFnParser(h,&ctx->rpt,"/");
  917. _cmFileSysTestFnParser(h,&ctx->rpt," ");
  918. //----------------------------------------------------------
  919. // Test the directory tree walking routines.
  920. //
  921. cmRptPrintf(&ctx->rpt,"Dir Entry Test:\n");
  922. cmFileSysDirEntry_t* dep;
  923. unsigned dirEntCnt;
  924. unsigned filterFlags = kDirFsFl
  925. | kFileFsFl
  926. | kRecurseFsFl
  927. | kFullPathFsFl;
  928. if((dep = cmFileSysDirEntries(h,SRC_DIR"/doc",filterFlags,
  929. &dirEntCnt)) != NULL)
  930. {
  931. unsigned i;
  932. for(i=0; i<dirEntCnt; ++i)
  933. cmRptPrintf(&ctx->rpt,"%s\n",dep[i].name);
  934. cmFileSysDirFreeEntries(h,dep);
  935. }
  936. //----------------------------------------------------------
  937. // Test the directory parsing/building routines.
  938. //
  939. cmRptPrintf(&ctx->rpt,"Dir Parsing routings:\n");
  940. cmChar_t** a;
  941. unsigned j;
  942. for(j=0; j<2; ++j)
  943. {
  944. const cmChar_t* dstr = dir0 + j;
  945. if((a = cmFileSysDirParts(h,dstr)) == NULL)
  946. cmRptPrint(&ctx->rpt,"cmFileSysDirParts() failed.\n");
  947. else
  948. {
  949. unsigned i;
  950. cmRptPrintf(&ctx->rpt,"Input:%s\n",dstr);
  951. for(i=0; a[i]!=NULL; ++i)
  952. cmRptPrintf(&ctx->rpt,"%i : %s\n",i,a[i]);
  953. cmChar_t* d;
  954. if((d = cmFileSysFormDir(h,a,
  955. cmFileSysDirPartsCount(h,a))) != NULL )
  956. {
  957. cmRptPrintf(&ctx->rpt,"Reformed:%s\n",d);
  958. }
  959. cmFileSysFreeDirParts(h,a);
  960. }
  961. }
  962. //----------------------------------------------------------
  963. // Test the extended mkdir routine.
  964. //
  965. if( cmFileSysMkDirAll(h, HOME_DIR"/temp/doc/doc" )!=kOkFsRC )
  966. {
  967. cmRptPrint(&ctx->rpt,"cmFileSysMkDirAll() failed.\n");
  968. }
  969. // finalize the file system
  970. if((rc = cmFileSysFinalize(&h)) != kOkFsRC )
  971. return rc;
  972. cmRptPrintf(&ctx->rpt,"File Test done\n");
  973. return rc;
  974. }
  975. // Parse a file name and print the results.
  976. // Called by cmFileSysTest().
  977. void _cmFileSysTestFnParser(
  978. cmFileSysH_t h,
  979. cmRpt_t* rpt,
  980. const cmChar_t* fn )
  981. {
  982. cmFileSysPathPart_t* pp;
  983. cmRptPrintf(rpt,"Fn Parse Test:%s\n",fn);
  984. if((pp = cmFileSysPathParts(h,fn)) != NULL )
  985. {
  986. if(pp->dirStr != NULL)
  987. cmRptPrintf(rpt,"Dir:%s\n",pp->dirStr);
  988. if(pp->fnStr != NULL)
  989. cmRptPrintf(rpt,"Fn:%s\n",pp->fnStr);
  990. if(pp->extStr != NULL )
  991. cmRptPrintf(rpt,"Ext:%s\n",pp->extStr);
  992. cmFileSysFreePathParts(h,pp);
  993. }
  994. cmRptPrintf(rpt,"\n");
  995. }
  996. //)
  997. //}