filespecutils_carbon.cpp
上传用户:dangjiwu
上传日期:2013-07-19
资源大小:42019k
文件大小:42k
源码类别:

Symbian

开发平台:

Visual C++

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