filespecutils_carbon.cpp
上传用户:zhongxx05
上传日期:2007-06-06
资源大小:33641k
文件大小:42k
源码类别:

Symbian

开发平台:

C/C++

  1. /* ***** BEGIN LICENSE BLOCK ***** 
  2.  * Version: RCSL 1.0/RPSL 1.0 
  3.  *  
  4.  * Portions Copyright (c) 1995-2002 RealNetworks, Inc. All Rights Reserved. 
  5.  *      
  6.  * The contents of this file, and the files included with this file, are 
  7.  * subject to the current version of the RealNetworks Public Source License 
  8.  * Version 1.0 (the "RPSL") available at 
  9.  * http://www.helixcommunity.org/content/rpsl unless you have licensed 
  10.  * the file under the RealNetworks Community Source License Version 1.0 
  11.  * (the "RCSL") available at http://www.helixcommunity.org/content/rcsl, 
  12.  * in which case the RCSL will apply. You may also obtain the license terms 
  13.  * directly from RealNetworks.  You may not use this file except in 
  14.  * compliance with the RPSL or, if you have a valid RCSL with RealNetworks 
  15.  * applicable to this file, the RCSL.  Please see the applicable RPSL or 
  16.  * RCSL for the rights, obligations and limitations governing use of the 
  17.  * contents of the file.  
  18.  *  
  19.  * This file is part of the Helix DNA Technology. RealNetworks is the 
  20.  * developer of the Original Code and owns the copyrights in the portions 
  21.  * it created. 
  22.  *  
  23.  * This file, and the files included with this file, is distributed and made 
  24.  * available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 
  25.  * EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS ALL SUCH WARRANTIES, 
  26.  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS 
  27.  * FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 
  28.  * 
  29.  * Technology Compatibility Kit Test Suite(s) Location: 
  30.  *    http://www.helixcommunity.org/content/tck 
  31.  * 
  32.  * Contributor(s): 
  33.  *  
  34.  * ***** END LICENSE BLOCK ***** */ 
  35. #include "filespec.h"
  36. #include "filespecutils.h"
  37. #include "hxstring.h"
  38. #include "hxtick.h"
  39. #include "hxrand.h"
  40. #include "carray.h"
  41. #include "platform/mac/MoreFilesX.h"
  42. // ------------------------------------------------------------------------------------
  43. //
  44. // GetFreeSpaceOnDisk
  45. //
  46. // ------------------------------------------------------------------------------------
  47. HX_RESULT CHXFileSpecUtils::GetFreeSpaceOnDisk(const CHXDirSpecifier& volSpec, INT64& freeSpace)
  48. {
  49. HX_RESULT err;
  50. FSRef volRef;
  51. FSVolumeRefNum vRefNum;
  52. UInt64 freeBytes;
  53. UInt64 totalBytes;
  54. require_return(volSpec.IsSet(), HXR_INVALID_PARAMETER);
  55. volRef = (FSRef) volSpec;
  56. err = FSGetVRefNum(&volRef, &vRefNum);
  57. if (err == noErr)
  58. {
  59. HFSUniStr255* kDontCareVolName = NULL;
  60. err = FSGetVInfo(vRefNum, kDontCareVolName, &freeBytes, &totalBytes);
  61. if (err == noErr)
  62. {
  63. freeSpace = *(long long *) &freeBytes;
  64. }
  65. }
  66. return err;
  67. }
  68. // ------------------------------------------------------------------------------------
  69. //
  70. // GetTotalSpaceOnDisk
  71. //
  72. // ------------------------------------------------------------------------------------
  73. HX_RESULT CHXFileSpecUtils::GetTotalSpaceOnDisk(const CHXDirSpecifier& volSpec, INT64& totalSpace)
  74. {
  75. HX_RESULT err;
  76. FSRef volRef;
  77. FSVolumeRefNum vRefNum;
  78. UInt64 freeBytes;
  79. UInt64 totalBytes;
  80. require_return(volSpec.IsSet(), HXR_INVALID_PARAMETER);
  81. volRef = (FSRef) volSpec;
  82. err = FSGetVRefNum(&volRef, &vRefNum);
  83. if (err == noErr)
  84. {
  85. HFSUniStr255* kDontCareVolName = NULL;
  86. err = FSGetVInfo(vRefNum, kDontCareVolName, &freeBytes, &totalBytes);
  87. if (err == noErr)
  88. {
  89. totalSpace = *(long long *) &totalBytes;
  90. }
  91. }
  92. return err;
  93. }
  94. // ------------------------------------------------------------------------------------
  95. //
  96. // IsFileLocal
  97. // IsDirectoryLocal
  98. //
  99. // ------------------------------------------------------------------------------------
  100. BOOL CHXFileSpecUtils::IsDiskLocal(const CHXDirSpecifier& volSpec)
  101. {
  102. HParamBlockRec pb;
  103. HX_RESULT err;
  104. GetVolParmsInfoBuffer buff;
  105. BOOL bLocalVol;
  106. short vRefNum;
  107. require_return(volSpec.IsSet(), FALSE);
  108. FSRef volRef;
  109. volRef = (FSRef) volSpec;
  110. err = FSGetVRefNum(&volRef, &vRefNum);
  111. if (err != noErr)
  112. {
  113. return TRUE;
  114. }
  115. ZeroInit(&pb);
  116. pb.ioParam.ioVRefNum = vRefNum;
  117. pb.ioParam.ioBuffer = (Ptr) &buff;
  118. pb.ioParam.ioReqCount = sizeof(buff);
  119. err = PBHGetVolParmsSync(&pb);
  120. if (err == noErr)
  121. {
  122. bLocalVol = (buff.vMServerAdr == 0);
  123. }
  124. else
  125. {
  126. // error occurred, default to true
  127. bLocalVol = TRUE;
  128. }
  129. return bLocalVol;
  130. }
  131. // ------------------------------------------------------------------------------------
  132. //
  133. // IsDiskEjectable
  134. //
  135. // ------------------------------------------------------------------------------------
  136. BOOL CHXFileSpecUtils::IsDiskEjectable(const CHXDirSpecifier& volSpec)
  137. {
  138. OSErr err;
  139. short vRefNum;
  140. GetVolParmsInfoBuffer  volParmsInfo;
  141. HParamBlockRec pb;
  142. FSRef  volRef;
  143. require_return(volSpec.IsSet(), FALSE);
  144. volRef = (FSRef) volSpec;
  145. err = FSGetVRefNum(&volRef, &vRefNum);
  146. require_noerr_return(err, FALSE);
  147. ZeroInit(&volParmsInfo);
  148. ZeroInit(&pb);
  149. pb.ioParam.ioVRefNum = vRefNum;
  150. pb.ioParam.ioBuffer = (Ptr) &volParmsInfo;
  151. pb.ioParam.ioReqCount = sizeof(volParmsInfo);
  152. err = PBHGetVolParmsSync(&pb);
  153. if (err == noErr)
  154. {
  155. // we require version 3 of the info buffer to rely on the vMExtendedAttributes
  156. if (volParmsInfo.vMVersion >= 3)
  157. {
  158. if (volParmsInfo.vMExtendedAttributes & (1L << bIsEjectable))
  159. {
  160. return TRUE;
  161. }
  162. }
  163. }
  164. return FALSE;
  165. }
  166. // ------------------------------------------------------------------------------------
  167. //
  168. // IsDiskWritable
  169. //
  170. // ------------------------------------------------------------------------------------
  171. BOOL CHXFileSpecUtils::IsDiskWritable(const CHXDirSpecifier& volSpec)
  172. {
  173. FSVolumeInfo volumeInfo;
  174. BOOL bWritable;
  175. OSErr err;
  176. FSRef volRef;
  177. FSVolumeRefNum vRefNum;
  178. require_return(volSpec.IsSet(), FALSE);
  179. bWritable = TRUE;
  180. volRef = (FSRef) volSpec;
  181. err = FSGetVRefNum(&volRef, &vRefNum);
  182. check_noerr(err);
  183. if (err == noErr)
  184. {
  185. err = FSGetVolumeInfo(vRefNum, 0, NULL, kFSVolInfoFlags, &volumeInfo, NULL, NULL);
  186. if (err == noErr)
  187. {
  188. if ( 0 != (volumeInfo.flags & kFSVolFlagHardwareLockedMask) )
  189. {
  190. bWritable = FALSE;
  191. }
  192. else if ( 0 != (volumeInfo.flags & kFSVolFlagSoftwareLockedMask) )
  193. {
  194. bWritable = FALSE;
  195. }
  196. }
  197. }
  198. return bWritable;
  199. }
  200. // ------------------------------------------------------------------------------------
  201. //
  202. // GetFileSize
  203. //
  204. // ------------------------------------------------------------------------------------
  205. HX_RESULT CHXFileSpecUtils::GetFileSize(const CHXFileSpecifier& fileSpec, INT64& fSize)
  206. {
  207. require_return(fileSpec.IsSet(), HXR_INVALID_PARAMETER);
  208. HX_RESULT err;
  209. FSRef targetRef;
  210. UInt64 logicalSize, physicalSize;
  211. ItemCount numForks;
  212. targetRef = fileSpec;
  213. err = FSGetTotalForkSizes(&targetRef, &logicalSize, &physicalSize, &numForks);
  214. if (err == noErr)
  215. {
  216. fSize = (INT64) logicalSize;
  217. }
  218. return err;
  219. }
  220. // ------------------------------------------------------------------------------------
  221. //
  222. // GetDirectorySize
  223. //
  224. // ------------------------------------------------------------------------------------
  225. HX_RESULT CHXFileSpecUtils::GetDirectorySize(const CHXDirSpecifier& dirSpec, BOOL shouldDescend, INT64& fSize)
  226. {
  227. // TODO: revise for Carbon
  228. // rather than literally recurse through the directory tree, this routine
  229. // will keep an array of directories yet to be added to the total size
  230. require_return(dirSpec.IsSet(), HXR_INVALID_PARAMETER);
  231. CHXPtrArray specArray;
  232. CHXDirSpecifier *pCurrDirSpec;
  233. INT64 totalSize;
  234. // push a copy of the initial directory spec into the array; 
  235. // it will be deleted when it is popped off
  236. pCurrDirSpec = new CHXDirSpecifier(dirSpec);
  237. require_nonnull_return(pCurrDirSpec, HXR_OUTOFMEMORY);
  238. specArray.Add(pCurrDirSpec);
  239. totalSize = 0;
  240. while (!specArray.IsEmpty())
  241. {
  242. FSSpec targetFSSpec;
  243. OSErr err;
  244. CInfoPBRec pb;
  245. short vRefNum;
  246. long currentDirID, index;
  247. Str63 fileName;
  248. Boolean bDoneWithThisDirectory;
  249. // grab a dirSpec from the array, delete the object, 
  250. // and step through all items in the directory
  251. pCurrDirSpec = (CHXDirSpecifier *) specArray.ElementAt(0);
  252. check_nonnull(pCurrDirSpec);
  253. if (pCurrDirSpec)
  254. {
  255. targetFSSpec = *pCurrDirSpec;
  256. currentDirID = pCurrDirSpec->GetDirID();
  257. vRefNum = targetFSSpec.vRefNum;
  258. // remove this dirSpec from our array and delete the object
  259. specArray.RemoveAt(0);
  260. HX_DELETE(pCurrDirSpec);
  261. check(vRefNum != 0 && currentDirID != 0);
  262. if (vRefNum != 0 && currentDirID != 0)
  263. {
  264. // step through all items in this directory
  265. index = 0;
  266. bDoneWithThisDirectory = false;
  267. while (!bDoneWithThisDirectory)
  268. {
  269. index++;
  270. ZeroInit(&pb);
  271. fileName[0] = 0;
  272. pb.hFileInfo.ioVRefNum = vRefNum;
  273. pb.hFileInfo.ioDirID = currentDirID;
  274. pb.hFileInfo.ioNamePtr = fileName;
  275. pb.hFileInfo.ioFDirIndex = index;
  276. err = PBGetCatInfoSync(&pb);
  277. if (err != noErr)
  278. {
  279. // no more items in this directory
  280. bDoneWithThisDirectory = true;
  281. }
  282. else
  283. {
  284. if ((pb.hFileInfo.ioFlAttrib & ioDirMask) == 0)
  285. {
  286. // it's a file; add its size
  287. totalSize += (INT64) pb.hFileInfo.ioFlLgLen; // data fork
  288. totalSize += (INT64) pb.hFileInfo.ioFlRLgLen; // resource fork
  289. }
  290. else
  291. {
  292. // it's a directory; add a dirSpec for it to the array
  293. if (shouldDescend)
  294. {
  295. CHXDirSpecifier *pNewDirSpec;
  296. err = FSMakeFSSpec(vRefNum, currentDirID, fileName, &targetFSSpec);
  297. check_noerr(err);
  298. pNewDirSpec = new CHXDirSpecifier(targetFSSpec);
  299. check_nonnull(pNewDirSpec);
  300. if (pNewDirSpec)
  301. {
  302. specArray.Add(pNewDirSpec);
  303. }
  304. }
  305. }
  306. }
  307. } // while (!bDoneWithThisDirectory)
  308. }
  309. }
  310. } // while(!specArray.IsEmpty())
  311. fSize = totalSize;
  312. return HXR_OK;
  313. }
  314. // ------------------------------------------------------------------------------------
  315. //
  316. // GetCurrentApplication
  317. // GetCurrentApplicationDir
  318. //
  319. // ------------------------------------------------------------------------------------
  320. CHXFileSpecifier CHXFileSpecUtils::GetCurrentApplication()
  321. {
  322. // TODO revise for Carbon, perhaps with GetProcessBundleLocation
  323. // if we're a bundle
  324. OSErr err;
  325. FSSpec  appFSSpec;
  326. ProcessSerialNumber  appPSN = { 0, kCurrentProcess };
  327. ProcessInfoRec  appPIR;
  328. CHXFileSpecifier  appSpec;
  329. appPIR.processInfoLength = sizeof(ProcessInfoRec);
  330. appPIR.processAppSpec = &appFSSpec;
  331. appPIR.processName = NULL;
  332. err = GetProcessInformation(&appPSN, &appPIR);
  333. check_noerr(err);
  334. if (err == noErr)
  335. {
  336. appSpec = appFSSpec;
  337. }
  338. return appSpec;
  339. }
  340. CHXDirSpecifier CHXFileSpecUtils::GetCurrentApplicationDir()
  341. {
  342. CHXFileSpecifier  appSpec;
  343. CHXDirSpecifier  dirSpec;
  344. appSpec = GetCurrentApplication();
  345. if (appSpec.IsSet())
  346. {
  347. dirSpec = appSpec.GetParentDirectory();
  348. }
  349. return dirSpec;
  350. }
  351. // ------------------------------------------------------------------------------------
  352. //
  353. // FileExists
  354. // DirectoryExists
  355. //
  356. // ------------------------------------------------------------------------------------
  357. BOOL CHXFileSpecUtils::FileExists(const CHXFileSpecifier& fileSpec)
  358. {
  359. FSRef  fileRef;
  360. Boolean isDirectory;
  361. long * kDontWantNodeID = NULL;
  362. if (!fileSpec.IsSet())
  363. {
  364. return FALSE;
  365. }
  366. fileRef = (FSRef) fileSpec;
  367.   return (FSGetNodeID(&fileRef, kDontWantNodeID, &isDirectory) == noErr) && !isDirectory;
  368. }
  369. BOOL CHXFileSpecUtils::DirectoryExists(const CHXDirSpecifier& dirSpec)
  370. {
  371. FSRef  dirRef;
  372. Boolean isDirectory;
  373. long * kDontWantNodeID = NULL;
  374. if (!dirSpec.IsSet())
  375. {
  376. return FALSE;
  377. }
  378. dirRef = (FSRef) dirSpec;
  379. return (FSGetNodeID(&dirRef, kDontWantNodeID, &isDirectory) == noErr) && isDirectory;
  380. }
  381. // ------------------------------------------------------------------------------------
  382. //
  383. // CreateDirectory
  384. //
  385. // ------------------------------------------------------------------------------------
  386. HX_RESULT CHXFileSpecUtils::CreateDir(const CHXDirSpecifier& dirSpec)
  387. {
  388. require_return(dirSpec.IsSet(), HXR_INVALID_PARAMETER);
  389. OSErr  err;
  390. FSRef  parentRef;
  391. CHXDirSpecifier parentSpec;
  392. HFSUniStr255  hfsUni;
  393. UInt32 dirID;
  394. FSCatalogInfo* kNotSettingCatInfo = NULL;
  395. FSRef* kDontWantNewRef = NULL;
  396. FSSpec* kDontWantFSSpec = NULL;
  397. err = HXR_INVALID_PARAMETER;
  398. // we need an FSRef for the parent where we will be creating the directory,
  399. // and an HFSUniStr255 of the name of the directory to be created
  400. parentSpec = dirSpec.GetParentDirectory();
  401. require(parentSpec.IsSet(), CantGetParent);
  402. parentRef = (FSRef) parentSpec;
  403. hfsUni = dirSpec.GetNameHFSUniStr255();
  404. require(hfsUni.length != 0, CantGetUnicodeName);
  405. err = FSCreateDirectoryUnicode(&parentRef, hfsUni.length, hfsUni.unicode, 
  406. kFSCatInfoNone, kNotSettingCatInfo, kDontWantNewRef, kDontWantFSSpec, &dirID);
  407. require_noerr(err, CantMakeDir);
  408. return HXR_OK;
  409. CantMakeDir:
  410. CantGetUnicodeName:
  411. CantGetParent:
  412. return err;   
  413. }
  414. // ------------------------------------------------------------------------------------
  415. //
  416. // RemoveDir - deletes an empty directory
  417. //
  418. // ------------------------------------------------------------------------------------
  419. HX_RESULT CHXFileSpecUtils::RemoveDir(const CHXDirSpecifier& dirSpec)
  420. {
  421. FSRef  dirRef;
  422. OSErr err;
  423. require_return(dirSpec.IsSet(), HXR_INVALID_PARAMETER);
  424. dirRef = (FSRef) dirSpec;
  425. err = FSDeleteObject(&dirRef);
  426. return err;
  427. }
  428. // ------------------------------------------------------------------------------------
  429. //
  430. // RemoveFile
  431. //
  432. // ------------------------------------------------------------------------------------
  433. HX_RESULT CHXFileSpecUtils::RemoveFile(const CHXFileSpecifier& fileSpec)
  434. {
  435. FSRef  fileRef;
  436. OSErr err;
  437. require_return(fileSpec.IsSet(), HXR_INVALID_PARAMETER);
  438. fileRef = (FSRef) fileSpec;
  439. err = FSDeleteObject(&fileRef);
  440. return err;
  441. }
  442. // ------------------------------------------------------------------------------------
  443. //
  444. // MakeFileReadOnly/MakeFileNotReadOnly
  445. //
  446. // ------------------------------------------------------------------------------------
  447. HX_RESULT CHXFileSpecUtils::MakeFileReadOnly(const CHXFileSpecifier& fileSpec)
  448. {
  449. FSSpec  fileFSSpec;
  450. OSErr err;
  451. require_return(fileSpec.IsSet(), HXR_INVALID_PARAMETER);
  452. fileFSSpec = (FSSpec) fileSpec;
  453. err = FSpSetFLock(&fileFSSpec); // is there an FSRef variant for this?
  454. if (err)
  455. {
  456. err = HXR_FAIL; // poor-man's OSErr-to-HX_RESULT conversion
  457. }
  458. return err;
  459. }
  460. HX_RESULT CHXFileSpecUtils::MakeFileNotReadOnly(const CHXFileSpecifier& fileSpec)
  461. {
  462. FSSpec  fileFSSpec;
  463. OSErr err;
  464. require_return(fileSpec.IsSet(), HXR_INVALID_PARAMETER);
  465. fileFSSpec = (FSSpec) fileSpec;
  466. err = FSpRstFLock(&fileFSSpec); // is there an FSRef variant for this?
  467. if (err)
  468. {
  469. err = HXR_FAIL; // poor-man's OSErr-to-HX_RESULT conversion
  470. }
  471. return err;
  472. }
  473. // ------------------------------------------------------------------------------------
  474. //
  475. // RenameMoveFile
  476. //
  477. // ------------------------------------------------------------------------------------
  478. HX_RESULT CHXFileSpecUtils::RenameMoveFile(CHXFileSpecifier& fileSpec, const char* pNewNameIfAny, 
  479. const CHXDirSpecifier* pNewDirectoryIfAny)
  480. {
  481. HX_RESULT res;
  482. CHXDirSpecifier fileDir, destDir;
  483. CHXFileSpecifier interimFileSpec;
  484. CHXString strNewName;
  485. FSRef fileRef, interimRef, targetRef;
  486. HFSUniStr255  newUniName;
  487. BOOL bRenaming, bMoving, bRenamed, bMoved;
  488. res = HXR_FAIL;
  489. bRenamed = bMoved = FALSE;
  490. require_return(fileSpec.IsSet(), HXR_INVALID_PARAMETER);
  491. fileDir = fileSpec.GetParentDirectory();
  492. if (pNewDirectoryIfAny)
  493. {
  494. destDir = *pNewDirectoryIfAny;
  495. bMoving = (destDir != fileDir) ? TRUE : FALSE;
  496. }
  497. else
  498. {
  499. destDir = fileDir;
  500. bMoving = FALSE;
  501. }
  502. if (pNewNameIfAny)
  503. {
  504. strNewName = pNewNameIfAny;
  505. bRenaming = (strNewName != fileSpec.GetName()) ? TRUE : FALSE;
  506. }
  507. else
  508. {
  509. strNewName = fileSpec.GetName();
  510. bRenaming = FALSE;
  511. }
  512. // ensure there's not already something there with this name
  513. CHXFileSpecifier specTestDest = destDir.SpecifyChildFile(strNewName);
  514. require( (!CHXFileSpecUtils::FileExists(specTestDest)), Bail);
  515. // try renaming, then move, then rename if we couldn't originally
  516. fileRef = (FSRef) fileSpec;
  517. // rename
  518. interimFileSpec = fileDir.SpecifyChildFile(strNewName); // new name in old directory
  519. if (bRenaming && interimFileSpec.IsSet() && !CHXFileSpecUtils::FileExists(interimFileSpec))
  520. {
  521. newUniName = interimFileSpec.GetNameHFSUniStr255();
  522. res = FSRenameUnicode(&fileRef, newUniName.length, newUniName.unicode, 
  523. kTextEncodingUnknown, &interimRef);
  524. if (res == noErr)
  525. {
  526. bRenamed = TRUE;
  527. fileRef = interimRef;
  528. }
  529. }
  530. // move
  531. if (bMoving)
  532. {
  533. targetRef = (FSRef) destDir;
  534. res = FSMoveObject(&fileRef, &targetRef, &interimRef);
  535. if (res == noErr)
  536. {
  537. bMoved = TRUE;
  538. fileRef = interimRef;
  539. }
  540. }
  541. // rename -- don't try to rename if we were supposed to move & didn't
  542. if (bRenaming && !bRenamed && (bMoved || !bMoving))
  543. {
  544. interimFileSpec = destDir.SpecifyChildFile(strNewName); // new name in new directory
  545. newUniName = interimFileSpec.GetNameHFSUniStr255();
  546. res = FSRenameUnicode(&fileRef, newUniName.length, newUniName.unicode, 
  547. kTextEncodingUnknown, &interimRef);
  548. if (res == noErr)
  549. {
  550. bRenamed = TRUE;
  551. fileRef = interimRef;
  552. }
  553. }
  554. if ((bRenamed || !bRenaming) && (bMoved || !bMoving))
  555. {
  556. fileSpec = fileRef;
  557. res = HXR_OK;
  558. }
  559. else
  560. {
  561. res = HXR_FAIL;
  562. }
  563. Bail:
  564. return res;
  565. }
  566. // ------------------------------------------------------------------------------------
  567. //
  568. // read/write files
  569. //
  570. // ------------------------------------------------------------------------------------
  571. static HX_RESULT WriteFileInternal(CHXFileSpecifier& fileSpec, const void *pBuff, UInt32 length, BOOL bReplaceExistingFile);
  572. static HX_RESULT ReadFileInternal(const CHXFileSpecifier& fileSpec, IHXBuffer*& pOutBuffer);
  573. HX_RESULT CHXFileSpecUtils::WriteBinaryFile(CHXFileSpecifier& fileSpec, IHXBuffer* inBuffer, BOOL bReplaceExistingFile)
  574. {
  575. return WriteFileInternal(fileSpec, (void *) inBuffer->GetBuffer(), inBuffer->GetSize(), bReplaceExistingFile);
  576. }
  577. HX_RESULT CHXFileSpecUtils::WriteTextFile(CHXFileSpecifier& fileSpec, const CHXString& inStr, BOOL bReplaceExistingFile)
  578. {
  579. return WriteFileInternal(fileSpec, (const char *) inStr, inStr.GetLength(), bReplaceExistingFile);
  580. }
  581. HX_RESULT WriteFileInternal(CHXFileSpecifier& fileSpec, const void *pBuff, UInt32 length, BOOL bReplaceExistingFile)
  582. {
  583. check_nonnull(pBuff);
  584. require_return(fileSpec.IsSet(), HXR_INVALID_PARAMETER);
  585. long * kDontWantNodeID = NULL;
  586. FSCatalogInfo * kDontSetCatInfo = NULL;
  587. FSSpec * kDontWantFileFSSpec = NULL;
  588. CHXDirSpecifier parentDirSpec;
  589. Boolean targetIsDir;
  590. Boolean targetAlreadyExists;
  591. HFSUniStr255 uniName, dataForkName;
  592. FSRef fileFSRef;
  593. SInt16 forkRefNum;
  594. OSErr err;
  595. // if an item is there already, bail if we're in dont replace mode
  596. // or if the item is a directory
  597. fileFSRef = (FSRef) fileSpec;
  598. targetAlreadyExists = (FSGetNodeID(&fileFSRef, kDontWantNodeID, &targetIsDir) == noErr);
  599. // before we delete an old file, save the parent and filename info so we can create the new one
  600. // (since the FSRef for the file in the old file spec becomes invalid once we delete the file)
  601. parentDirSpec = fileSpec.GetParentDirectory();
  602. uniName = fileSpec.GetNameHFSUniStr255();
  603. if (targetAlreadyExists)
  604. {
  605. if (targetIsDir) return HXR_INVALID_PATH;
  606. if (!bReplaceExistingFile) return HXR_FILE_EXISTS;
  607. if (bReplaceExistingFile) 
  608. {
  609. err = FSDeleteObject(&fileFSRef);
  610. }
  611. // create, open, write into the file, and close it
  612. err = FSCreateFileUnicode((FSRef *) parentDirSpec, uniName.length, uniName.unicode,
  613. kFSCatInfoNone, kDontSetCatInfo, &fileFSRef, kDontWantFileFSSpec);
  614. if (err == noErr)
  615. {
  616. dataForkName.length = 0;
  617. err = FSGetDataForkName(&dataForkName);
  618. check_noerr(err);
  619. err = FSOpenFork(&fileFSRef, dataForkName.length, dataForkName.unicode,
  620. fsWrPerm, &forkRefNum);
  621. if (err == noErr)
  622. {
  623. long ioBytes = length;
  624. err = FSWrite(forkRefNum, &ioBytes, pBuff);
  625. check_noerr(err);
  626. FSCloseFork(forkRefNum);
  627. }
  628. if (err == noErr)
  629. {
  630. // update the file spec to point to the new file
  631. fileSpec = fileFSRef;
  632. }
  633. else
  634. {
  635. // we failed to write it; delete it
  636. (void) FSDeleteObject(&fileFSRef);
  637. }
  638. }
  639. return (err == noErr ? HXR_OK : HXR_FAIL);
  640. }
  641. HX_RESULT CHXFileSpecUtils::ReadBinaryFile(const CHXFileSpecifier& fileSpec, IHXBuffer*& pOutBuffer)
  642. {
  643. return ReadFileInternal(fileSpec, pOutBuffer);
  644. }
  645. HX_RESULT CHXFileSpecUtils::ReadTextFile(const CHXFileSpecifier& fileSpec, CHXString& outStr)
  646. {
  647. HX_RESULT res;
  648. IHXBuffer *pBuff = NULL;
  649. res = ReadFileInternal(fileSpec, pBuff);
  650. if (SUCCEEDED(res))
  651. {
  652. outStr = CHXString((const char *) pBuff->GetBuffer(), (int) pBuff->GetSize());
  653. HX_RELEASE(pBuff);
  654. }
  655. return res;
  656. }
  657. HX_RESULT ReadFileInternal(const CHXFileSpecifier& fileSpec, IHXBuffer*& pOutBuffer)
  658. {
  659. require_return(fileSpec.IsSet(), HXR_INVALID_PARAMETER);
  660. HFSUniStr255 dataForkName;
  661. FSRef fileFSRef;
  662. SInt16 forkRefNum = -1;
  663. SInt64 forkSize;
  664. OSStatus err;
  665. pOutBuffer = new CHXBuffer();
  666. require_nonnull_action(pOutBuffer, Bail, err = HXR_OUTOFMEMORY);
  667. pOutBuffer->AddRef();
  668. dataForkName.length = 0;
  669. err = FSGetDataForkName(&dataForkName);
  670. require_noerr(err, Bail);
  671. fileFSRef = (FSRef) fileSpec;
  672. err = FSOpenFork(&fileFSRef, dataForkName.length, dataForkName.unicode,
  673. fsRdPerm, &forkRefNum);
  674. require_noerr_quiet(err, Bail);
  675. err = FSGetForkSize(forkRefNum, &forkSize);
  676. require_noerr(err, Bail);
  677. require_action(forkSize <= 0x07FFFFFFF, Bail, err = HXR_INVALID_PATH); // file is too big for a 32-bit signed length
  678. err = pOutBuffer->SetSize((UInt32) forkSize);
  679. require_noerr(err, Bail);
  680. {
  681.             long ioBytes = forkSize;
  682.             
  683.             err = FSRead(forkRefNum, &ioBytes, pOutBuffer->GetBuffer());
  684.             require_noerr(err, Bail);
  685.                     
  686.             (void) FSCloseFork(forkRefNum);
  687.             
  688.             return HXR_OK;
  689.         }
  690. Bail:
  691. HX_RELEASE(pOutBuffer);
  692. if (forkRefNum != -1) (void) FSCloseFork(forkRefNum);
  693. return HXR_FAIL;
  694. }
  695. // ------------------------------------------------------------------------------------
  696. //
  697. // GetFileType
  698. //
  699. // ------------------------------------------------------------------------------------
  700. FOURCC CHXFileSpecUtils::GetFileType(const CHXFileSpecifier& fileSpec)
  701. {
  702. FOURCC fileType;
  703. OSErr err;
  704. require_return(fileSpec.IsSet(), 0);
  705. fileType = 0;
  706. FSRef fileRef;
  707. FinderInfo fndrInfo;
  708. Boolean isDir;
  709. ExtendedFinderInfo * kDontWantExtendedInfo = NULL;
  710. fileRef = (FSRef) fileSpec;
  711. err = FSGetFinderInfo(&fileRef, &fndrInfo, kDontWantExtendedInfo, &isDir);
  712. if ((err == noErr) && !isDir)
  713. {
  714. fileType = fndrInfo.file.fileType;
  715. }
  716. return fileType;
  717. }
  718. // ------------------------------------------------------------------------------------
  719. //
  720. // MakeNameLegal
  721. //
  722. // returns TRUE if the name was changed
  723. //
  724. // ------------------------------------------------------------------------------------
  725. BOOL CHXFileSpecUtils::MakeNameLegal(char *pszName)
  726. {
  727. const char *badChars = ":";
  728. const char replacementChar = '-';
  729. const long maxNameLength = 255;
  730. long len, idx;
  731. BOOL bChanged;
  732. require_nonnull_return(pszName, FALSE);
  733. bChanged = FALSE;
  734. len = strlen(pszName);
  735. // replace any illegal characters
  736. for (idx = 0; idx < len; idx++)
  737. {
  738. if (strchr(badChars, pszName[idx]))
  739. {
  740. pszName[idx] = replacementChar;
  741. bChanged = TRUE;
  742. }
  743. }
  744. // be sure the name isn't too long
  745. if (len > maxNameLength)
  746. {
  747. pszName[maxNameLength] = 0;
  748. bChanged = TRUE;
  749. }
  750. return bChanged;
  751. }
  752. // ------------------------------------------------------------------------------------
  753. //
  754. // FindFolder
  755. //
  756. // ------------------------------------------------------------------------------------
  757. CHXDirSpecifier CHXFileSpecUtils::MacFindFolder(short vRefNum, FolderType foldType)
  758. {
  759. CHXDirSpecifier foundDirSpec;
  760. OSErr err;
  761. FSRef folderRef;
  762. err = ::FSFindFolder(vRefNum, foldType, kCreateFolder, &folderRef);
  763. if (err == noErr)
  764. {
  765. foundDirSpec = folderRef;
  766. }
  767. return foundDirSpec;
  768. }
  769. CHXFileSpecifier CHXFileSpecUtils::SpecifyFileWithMacFindFolder(short vRefNum, FolderType foldType, const char *pszChildFile)
  770. {
  771. CHXDirSpecifier parentDir;
  772. CHXFileSpecifier targetFile;
  773. parentDir = CHXFileSpecUtils::MacFindFolder(vRefNum, foldType);
  774. check(parentDir.IsSet());
  775. if (CHXFileSpecUtils::DirectoryExists(parentDir))
  776. {
  777. targetFile = parentDir.SpecifyChildFile(pszChildFile);
  778. }
  779. return targetFile;
  780. }
  781. CHXDirSpecifier CHXFileSpecUtils::SpecifyFolderWithMacFindFolder(short vRefNum, FolderType foldType, const char *pszChildFolder)
  782. {
  783. CHXDirSpecifier parentDir;
  784. CHXDirSpecifier targetDir;
  785. parentDir = CHXFileSpecUtils::MacFindFolder(vRefNum, foldType);
  786. check(parentDir.IsSet());
  787. if (CHXFileSpecUtils::DirectoryExists(parentDir))
  788. {
  789. targetDir = parentDir.SpecifyChildDirectory(pszChildFolder);
  790. }
  791. return targetDir;
  792. }
  793. // ------------------------------------------------------------------------------------
  794. //
  795. // ResolveFileSpecifierAlias
  796. // ResolveDirSpecifierAlias
  797. //
  798. // These resolve a file spec to an alias file in place
  799. //
  800. // ------------------------------------------------------------------------------------
  801. HX_RESULT CHXFileSpecUtils::ResolveFileSpecifierAlias(CHXFileSpecifier& fileSpec)
  802. {
  803. HX_RESULT res;
  804. res = HXR_FAIL;
  805. // GR 7/8/02
  806. // check that the file exists before resolving it in hopes of avoiding
  807. // a mysterious crash on 10.1.5
  808. if (CHXFileSpecUtils::FileExists(fileSpec))
  809. {
  810. FSRef fileRef;
  811. OSErr err;
  812. Boolean bIsFolder;
  813. Boolean bWasAliased;
  814. const Boolean kShouldResolveChains = true;
  815. fileRef = (FSRef) fileSpec;
  816. err = FSResolveAliasFileWithMountFlags(&fileRef, kShouldResolveChains,
  817. &bIsFolder, &bWasAliased, kResolveAliasFileNoUI);
  818. if ((err == noErr) && !bIsFolder)
  819. {
  820. res = HXR_OK;
  821. if (bWasAliased)
  822. {
  823. fileSpec = fileRef;
  824. }
  825. }
  826. else
  827. {
  828. // error occurred
  829. }
  830. }
  831. return res;
  832. }
  833. HX_RESULT CHXFileSpecUtils::ResolveDirSpecifierAlias(CHXDirSpecifier& dirSpec)
  834. {
  835. HX_RESULT res;
  836. res = HXR_FAIL;
  837. if (dirSpec.IsSet())
  838. {
  839. FSRef dirRef;
  840. OSErr err;
  841. Boolean bIsFolder;
  842. Boolean bWasAliased;
  843. const Boolean kShouldResolveChains = true;
  844. dirRef = dirSpec;
  845. err = FSResolveAliasFileWithMountFlags(&dirRef, kShouldResolveChains,
  846. &bIsFolder, &bWasAliased, kResolveAliasFileNoUI);
  847. if ((err == noErr) && bIsFolder)
  848. {
  849. res = HXR_OK;
  850. if (bWasAliased)
  851. {
  852. dirSpec = dirRef;
  853. }
  854. }
  855. else
  856. {
  857. // error occurred
  858. }
  859. }
  860. return res;
  861. }
  862. // ------------------------------------------------------------------------------------
  863. //
  864. // MoveFileToTrash
  865. // MoveFolderToTrash
  866. // MoveFileToFolderWithRenaming
  867. // MoveFolderToFolderWithRenaming
  868. //
  869. // ------------------------------------------------------------------------------------
  870. static HX_RESULT MoveToFolderWithRenamingInternal(const FSRef* itemRef, const CHXDirSpecifier& targetSpec, BOOL bDeleteIfCantMove, FSRef& outNewItemRef);
  871. HX_RESULT CHXFileSpecUtils::MoveFileToTrash(const CHXFileSpecifier& fileSpec) 
  872. {
  873. if (!CHXFileSpecUtils::FileExists(fileSpec)) return HXR_INVALID_PARAMETER;
  874. CHXDirSpecifier trashSpec;
  875. FSRef itemRef, newItemRef;
  876. const BOOL bDeleteIfCantMove = TRUE;
  877. itemRef = (FSRef) fileSpec;
  878. trashSpec = CHXFileSpecUtils::MacFindFolder(fileSpec.GetVRefNum(), kTrashFolderType);
  879. return MoveToFolderWithRenamingInternal(&itemRef, trashSpec, bDeleteIfCantMove, newItemRef);
  880. }
  881. HX_RESULT CHXFileSpecUtils::MoveFolderToTrash(const CHXDirSpecifier& dirSpec) 
  882. {
  883. if (!CHXFileSpecUtils::DirectoryExists(dirSpec)) return HXR_INVALID_PARAMETER;
  884. CHXDirSpecifier trashSpec;
  885. FSRef itemRef, newItemRef;
  886. const BOOL bDeleteIfCantMove = TRUE;
  887. itemRef = (FSRef) dirSpec;
  888. trashSpec = CHXFileSpecUtils::MacFindFolder(dirSpec.GetVRefNum(), kTrashFolderType);
  889. return MoveToFolderWithRenamingInternal(&itemRef, trashSpec, bDeleteIfCantMove, newItemRef);
  890. }
  891. HX_RESULT CHXFileSpecUtils::MoveFileToCleanupAtStartup(const CHXFileSpecifier& fileSpec, BOOL bDeleteIfCantMove) 
  892. {
  893. CHXFileSpecifier nonConstFileSpec(fileSpec);
  894. return MoveFileToCleanupAtStartup(nonConstFileSpec, bDeleteIfCantMove);
  895. }
  896. HX_RESULT CHXFileSpecUtils::MoveFileToCleanupAtStartup(CHXFileSpecifier& fileSpec, BOOL bDeleteIfCantMove) 
  897. {
  898. if (!CHXFileSpecUtils::FileExists(fileSpec)) return HXR_INVALID_PARAMETER;
  899. CHXDirSpecifier cleanupSpec;
  900. FSRef itemRef, newItemRef;
  901. HX_RESULT res;
  902. itemRef = (FSRef) fileSpec;
  903. cleanupSpec = CHXFileSpecUtils::MacFindFolder(fileSpec.GetVRefNum(), kChewableItemsFolderType);
  904. res = MoveToFolderWithRenamingInternal(&itemRef, cleanupSpec, bDeleteIfCantMove, newItemRef);
  905. if (SUCCEEDED(res))
  906. {
  907. fileSpec = newItemRef;
  908. }
  909. return res;
  910. }
  911. HX_RESULT CHXFileSpecUtils::MoveFolderToCleanupAtStartup(const CHXDirSpecifier& dirSpec, BOOL bDeleteIfCantMove) 
  912. {
  913. CHXDirSpecifier nonConstDirSpec(dirSpec);
  914. return MoveFolderToCleanupAtStartup(nonConstDirSpec, bDeleteIfCantMove);
  915. }
  916. HX_RESULT CHXFileSpecUtils::MoveFolderToCleanupAtStartup(CHXDirSpecifier& dirSpec, BOOL bDeleteIfCantMove) 
  917. {
  918. if (!CHXFileSpecUtils::DirectoryExists(dirSpec)) return HXR_INVALID_PARAMETER;
  919. CHXDirSpecifier cleanupSpec;
  920. FSRef itemRef, newItemRef;
  921. HX_RESULT res;
  922. itemRef = (FSRef) dirSpec;
  923. cleanupSpec = CHXFileSpecUtils::MacFindFolder(dirSpec.GetVRefNum(), kChewableItemsFolderType);
  924. res = MoveToFolderWithRenamingInternal(&itemRef, cleanupSpec, bDeleteIfCantMove, newItemRef);
  925. if (SUCCEEDED(res))
  926. {
  927. dirSpec = newItemRef;
  928. }
  929. return res;
  930. }
  931. HX_RESULT CHXFileSpecUtils::MoveFileToFolderWithRenaming(CHXFileSpecifier& fileSpec, const CHXDirSpecifier& targetSpec, BOOL bDeleteIfCantMove) 
  932. {
  933. if (!CHXFileSpecUtils::FileExists(fileSpec)) return HXR_INVALID_PARAMETER;
  934. if (!CHXFileSpecUtils::DirectoryExists(targetSpec)) return HXR_INVALID_PARAMETER;
  935. FSRef itemRef, newItemRef;
  936. HX_RESULT res;
  937. itemRef = (FSRef) fileSpec;
  938. res = MoveToFolderWithRenamingInternal(&itemRef, targetSpec, bDeleteIfCantMove, newItemRef);
  939. if (SUCCEEDED(res))
  940. {
  941. fileSpec = newItemRef;
  942. }
  943. return res;
  944. }
  945. HX_RESULT CHXFileSpecUtils::MoveFolderToFolderWithRenaming(CHXDirSpecifier& dirSpec, const CHXDirSpecifier& targetSpec, BOOL bDeleteIfCantMove) 
  946. {
  947. if (!CHXFileSpecUtils::DirectoryExists(dirSpec)) return HXR_INVALID_PARAMETER;
  948. if (!CHXFileSpecUtils::DirectoryExists(targetSpec)) return HXR_INVALID_PARAMETER;
  949. FSRef itemRef, newItemRef;
  950. HX_RESULT res;
  951. itemRef = (FSRef) dirSpec;
  952. res = MoveToFolderWithRenamingInternal(&itemRef, targetSpec, bDeleteIfCantMove, newItemRef);
  953. if (SUCCEEDED(res))
  954. {
  955. dirSpec = newItemRef;
  956. }
  957. return res;
  958. }
  959. static HX_RESULT MoveToFolderWithRenamingInternal(const FSRef* itemRef, const CHXDirSpecifier& targetSpec, BOOL bDeleteIfCantMove, FSRef& outNewItemRef) 
  960. {
  961. check_nonnull(itemRef);
  962. HX_RESULT pnres;
  963. OSErr err;
  964. pnres = HXR_FAILED;
  965. ZeroInit(&outNewItemRef);
  966. if (!targetSpec.IsSet())
  967. {
  968. // targetSpec wasn't set, nowhere to move to
  969. if (bDeleteIfCantMove)
  970. {
  971. err = FSDeleteObject(itemRef);
  972. if (err == noErr)
  973. {
  974. pnres = HXR_OK;
  975. }
  976. }
  977. }
  978. else
  979. {
  980. // targetSpec is set
  981. //
  982. // try to move the file to the target
  983. FSRef newItemRef, targetRef, newNameRef;
  984. targetRef = (FSRef) targetSpec;
  985. err = FSMoveObject(itemRef, &targetRef, &newItemRef);
  986. if (err == noErr)
  987. {
  988. outNewItemRef = newItemRef;
  989. pnres = HXR_OK;
  990. }
  991. else if (err != dupFNErr)
  992. {
  993. // catmove failed for some unknown reason, not a name conflict
  994. if (bDeleteIfCantMove)
  995. {
  996. err = FSDeleteObject(itemRef);
  997. if (err == noErr)
  998. {
  999. pnres = HXR_OK;
  1000. }
  1001. }
  1002. }
  1003. else
  1004. {
  1005. // there's a name conflict; find a unique name for the file we're moving in the
  1006. // target folder, rename it, and then move it again
  1007. CHXString strName, strTemplate;
  1008. CHXFileSpecifier specNewNamedFile;
  1009. HFSUniStr255 uniName, newUniName;
  1010. INT32 periodOffset;
  1011. const char* const kWildcard = "%-%-%";
  1012. err = FSGetFileDirName(itemRef, &uniName);
  1013. strName.SetFromHFSUniStr255(uniName, CFStringGetSystemEncoding()); // make a template like "filename_%-%-%"
  1014. periodOffset = strName.ReverseFind('.');
  1015. if (periodOffset != -1)
  1016. {
  1017. // put the _2 or whatever just before the extension
  1018. strTemplate = strName.Left(periodOffset);
  1019. strTemplate += "_";
  1020. strTemplate += kWildcard;
  1021. strTemplate += strName.Mid(periodOffset);
  1022. }
  1023. else
  1024. {
  1025. // put the _2 or whatever after the filename
  1026. strTemplate = strName + "_";
  1027. strTemplate += kWildcard;
  1028. }
  1029. specNewNamedFile = CHXFileSpecUtils::GetUniqueFileSpec(targetSpec, strName, strTemplate, kWildcard);
  1030. newUniName = specNewNamedFile.GetNameHFSUniStr255();
  1031. err = FSRenameUnicode(itemRef, newUniName.length, newUniName.unicode, kTextEncodingUnknown, &newNameRef);
  1032. if (err == noErr)
  1033. {
  1034. err = FSMoveObject(&newNameRef, &targetRef, &newItemRef);
  1035. if (err == noErr)
  1036. {
  1037. outNewItemRef = newItemRef;
  1038. pnres = HXR_OK;
  1039. }
  1040. else if (err != noErr && bDeleteIfCantMove)
  1041. {
  1042. // couldn't move it; delete the renamed file
  1043. err = FSDeleteObject(&newNameRef);
  1044. if (err == noErr)
  1045. {
  1046. pnres = HXR_OK;
  1047. }
  1048. }
  1049. else
  1050. {
  1051. // couldn't move it; change the name back
  1052. err = FSRenameUnicode(&newNameRef, uniName.length, uniName.unicode, 
  1053. kTextEncodingUnknown, &newNameRef);
  1054. }
  1055. }
  1056. else
  1057. {
  1058. // rename failed for some reason
  1059. if (bDeleteIfCantMove)
  1060. {
  1061. // couldn't move it; delete the renamed file
  1062. err = FSDeleteObject(itemRef);
  1063. if (err == noErr)
  1064. {
  1065. pnres = HXR_OK;
  1066. }
  1067. }
  1068. }
  1069. }
  1070. return pnres;
  1071. }
  1072. // ------------------------------------------------------------------------------------
  1073. //
  1074. // GetSystemTempDirectory
  1075. //
  1076. // ------------------------------------------------------------------------------------
  1077. CHXDirSpecifier CHXFileSpecUtils::GetSystemTempDirectory()
  1078. {
  1079. return CHXFileSpecUtils::MacFindFolder(kOnAppropriateDisk, kChewableItemsFolderType);
  1080. }
  1081. // ------------------------------------------------------------------------------------
  1082. //
  1083. // GetUniqueFileSpec
  1084. // GetUniqueTempFileSpec
  1085. //
  1086. // ------------------------------------------------------------------------------------
  1087. static CHXFileSpecifier GetUniqueFileSpecInternal(const CHXDirSpecifier& locationSpec, 
  1088. const char *pszNameFirst, const char *pszTemplate, 
  1089. const char *pszWildcard, UINT32 nStartNum);
  1090. const UINT32 kNumWrapValue = 9999+1; // limit insertions to 4-digit numbers
  1091. CHXFileSpecifier CHXFileSpecUtils::GetUniqueFileSpec(const CHXDirSpecifier& locationSpec, 
  1092. const char *pszNameFirst, const char *pszTemplate, 
  1093. const char *pszWildcard)
  1094. {
  1095. return GetUniqueFileSpecInternal(locationSpec, pszNameFirst, pszTemplate, pszWildcard, 0);
  1096. }
  1097. CHXFileSpecifier CHXFileSpecUtils::GetUniqueTempFileSpec(const CHXDirSpecifier& locationSpec, 
  1098. const char *pszTemplate, const char *pszWildcard)
  1099. {
  1100. CMultiplePrimeRandom  rand(HX_GET_TICKCOUNT());
  1101. UINT32 num;
  1102. num = rand.GetRandomNumber();
  1103. num %= kNumWrapValue;
  1104. // skip 0, which means "don't substitute for the wildcard", and 1
  1105. if (num == 0 || num == 1) num = 2;
  1106. return GetUniqueFileSpecInternal(locationSpec, NULL, pszTemplate, pszWildcard, num);
  1107. }
  1108. static CHXFileSpecifier GetUniqueFileSpecInternal(const CHXDirSpecifier& locationSpec, 
  1109. const char *pszNameFirst, const char *pszTemplate, 
  1110. const char *pszWildcard, UINT32 nStartNum)
  1111. {
  1112. CHXFileSpecifier  resultFileSpec;
  1113. require_return(locationSpec.IsSet(), resultFileSpec);
  1114. require_return(pszTemplate != NULL && pszWildcard != NULL, resultFileSpec);
  1115. require_return(pszNameFirst != NULL || nStartNum != 0, resultFileSpec);
  1116. CHXFileSpecifier  testFileSpec;
  1117. CHXDirSpecifier  testDirSpec;
  1118. CHXString strNumber;
  1119. CHXString strName;
  1120. UINT32 nCurrentNum;
  1121. nCurrentNum = nStartNum;
  1122. while (1) 
  1123. {
  1124. // if the number is non-zero, make a string from the template;
  1125. // if the number is zero, user the initial name string
  1126. if (nCurrentNum == 0)
  1127. {
  1128. // replace the wildcard in the template with the number string
  1129. strName = pszNameFirst;
  1130. }
  1131. else
  1132. {
  1133. // replace the wildcard in the template with the number string
  1134. strNumber.Empty();
  1135. strNumber.AppendULONG(nCurrentNum);
  1136. strName = pszTemplate;
  1137. strName.FindAndReplace(pszWildcard, strNumber); // replace first wildcard with number string
  1138. }
  1139. // test if a file or directory exists with that name
  1140. testFileSpec = locationSpec.SpecifyChildFile(strName);
  1141. testDirSpec = locationSpec.SpecifyChildDirectory(strName);
  1142. if (CHXFileSpecUtils::FileExists(testFileSpec)
  1143. || CHXFileSpecUtils::DirectoryExists(testDirSpec))
  1144. {
  1145. // an item already has that name, so increment & wrap the number
  1146. nCurrentNum++;
  1147. nCurrentNum %= kNumWrapValue;
  1148. // don't use 0 again, and skip 1 since "MyFile2.txt" logically follows "MyFile.txt"
  1149. if (nCurrentNum == 0 || nCurrentNum == 1) 
  1150. {
  1151. nCurrentNum = 2; 
  1152. }
  1153. // a quick sanity check
  1154. if (nCurrentNum == nStartNum)
  1155. {
  1156. check(!"GetUniqueFileSpecInternal number wrapped");
  1157. break;
  1158. }
  1159. }
  1160. else
  1161. {
  1162. // the name is unique
  1163. resultFileSpec = testFileSpec;
  1164. break;
  1165. }
  1166. } // while
  1167. return resultFileSpec;
  1168. }
  1169. CHXDirSpecifier 
  1170. CHXFileSpecUtils::GetAppDataDir(const char* szAppName)
  1171. {
  1172. // we'll use a com.RealNetworks.data folder in user's prefs
  1173. CHXDirSpecifier targetDir;
  1174. CHXDirSpecifier prefsDir = CHXFileSpecUtils::MacFindFolder(kUserDomain, kPreferencesFolderType);
  1175. CHXDirSpecifier dataDir = prefsDir.SpecifyChildDirectory( "com.RealNetworks.data" );
  1176. if (!CHXFileSpecUtils::DirectoryExists(dataDir))
  1177. {
  1178.     CHXFileSpecUtils::CreateDir(dataDir);
  1179. }
  1180. if (szAppName == NULL || *szAppName == 0)
  1181. {
  1182.     targetDir = dataDir;
  1183. }
  1184. else
  1185. {
  1186.     targetDir = dataDir.SpecifyChildDirectory(szAppName);
  1187.     if (!CHXFileSpecUtils::DirectoryExists(targetDir))
  1188.     {
  1189. CHXFileSpecUtils::CreateDir(targetDir);
  1190.     }
  1191. }
  1192. HX_ASSERT(CHXFileSpecUtils::DirectoryExists(targetDir));
  1193. return targetDir;
  1194. }
  1195. #ifdef _DEBUG
  1196. void CHXFileSpecUtils::TestMacFileSpecUtils()
  1197. {
  1198. CHXString drive1Name = "Tenspot:";
  1199. CHXString drive2Name = "Sandy:";
  1200. CHXString driveEjectableLockedName = "Dev.CD Feb 01 TC Disk 1:";
  1201. CHXString driveServerName = "grobbins:";
  1202. CHXDirSpecifier dirServerSpec = drive2Name;
  1203. dirServerSpec = "Dev.CD Feb 01 TC Disk 1:Tool Chest:fake";
  1204. dirServerSpec = "Sandy:fake:";
  1205. //dirServerSpec = "grobbins:Sites:";
  1206. //dirServerSpec = "grobbins:Sites:fake";
  1207. CHXDirSpecifier dirServer2Spec = driveServerName;
  1208. CHXFileSpecifier file1Spec = "Tenspot:123456789a123456789b123456789c123456789d.png";
  1209. CHXFileSpecifier file2Spec = "Tenspot:123456789a123456789b123456789c123456789d:LVd";
  1210. CHXFileSpecifier fileFake1Spec = "Tenspot:123456789a123456789b123456789c123456789d:BOO";
  1211. CHXDirSpecifier dirFake1Spec = "Tenspot:123456789a123456789b123456789c123456789d:BOO Folder";
  1212. CHXFileSpecifier fromNameSpec = "RealPlayer.xSYM";
  1213. CHXFileSpecifier fromPartialPath = ":RealPlayer.xSYM";
  1214. CHXFileSpecifier fromBadName = "BOO";
  1215. HX_RESULT res;
  1216. INT64 spaceFree, spaceTotal, fileSize;
  1217. BOOL bFlag;
  1218. res = CHXFileSpecUtils::GetFreeSpaceOnDisk(drive1Name, spaceFree);
  1219. res = CHXFileSpecUtils::GetTotalSpaceOnDisk(drive1Name, spaceTotal);
  1220. check(spaceTotal > spaceFree);
  1221. bFlag = CHXFileSpecUtils::IsDiskEjectable(drive1Name);
  1222. check(!bFlag);
  1223. bFlag = CHXFileSpecUtils::IsDiskEjectable(driveEjectableLockedName);
  1224. check(bFlag);
  1225. bFlag = CHXFileSpecUtils::IsDiskWritable(drive1Name);
  1226. check(bFlag);
  1227. bFlag = CHXFileSpecUtils::IsDiskWritable(driveEjectableLockedName);
  1228. check(!bFlag);
  1229. bFlag = CHXFileSpecUtils::IsDiskLocal(drive1Name);
  1230. check(bFlag);
  1231. //bFlag = CHXFileSpecUtils::IsDiskLocal(driveServerName);  this fails because HFS<-->FSRef fails on mounted vols
  1232. //check(!bFlag);
  1233. res = CHXFileSpecUtils::GetFileSize(file1Spec, fileSize);
  1234. res = CHXFileSpecUtils::GetFileSize(file2Spec, fileSize);
  1235. res = CHXFileSpecUtils::GetFileSize(fileFake1Spec, fileSize);
  1236. res = CHXFileSpecUtils::FileExists(file1Spec);
  1237. res = CHXFileSpecUtils::FileExists(file2Spec);
  1238. res = CHXFileSpecUtils::FileExists(fileFake1Spec);
  1239. bFlag = CHXFileSpecUtils::DirectoryExists(dirFake1Spec);
  1240. check(!bFlag);
  1241. res = CHXFileSpecUtils::CreateDir(dirFake1Spec);
  1242. bFlag = CHXFileSpecUtils::DirectoryExists(dirFake1Spec);
  1243. check(bFlag);
  1244. res = CHXFileSpecUtils::RemoveDir(dirFake1Spec);
  1245. bFlag = CHXFileSpecUtils::DirectoryExists(dirFake1Spec);
  1246. check(!bFlag);
  1247. CHXDirSpecifier ffDir = CHXFileSpecUtils::SpecifyFolderWithMacFindFolder(file1Spec.GetVRefNum(), kPreferencesFolderType, "Real Folder");
  1248. res = CHXFileSpecUtils::CreateDir(ffDir);
  1249. bFlag = CHXFileSpecUtils::DirectoryExists(ffDir);
  1250. check(bFlag);
  1251. res = CHXFileSpecUtils::MoveFolderToCleanupAtStartup(ffDir);
  1252. bFlag = CHXFileSpecUtils::DirectoryExists(ffDir);
  1253. check(!bFlag);
  1254. }
  1255. #endif