]> git.jsancho.org Git - lugaru.git/blob - Source/MoreFilesX.c
Added GPL license and headers.
[lugaru.git] / Source / MoreFilesX.c
1 /*
2 Copyright (C) 2003, 2010 - Wolfire Games
3
4 This file is part of Lugaru.
5
6 Lugaru is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the License, or (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
14
15 See the GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 */
21
22 /*
23         File:           MoreFilesX.c
24
25         Contains:       A collection of useful high-level File Manager routines
26                                 which use the HFS Plus APIs wherever possible.
27
28         Version:        MoreFilesX 1.0.1
29
30         Copyright:      © 1992-2002 by Apple Computer, Inc., all rights reserved.
31
32         Disclaimer:     IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
33                                 ("Apple") in consideration of your agreement to the following terms, and your
34                                 use, installation, modification or redistribution of this Apple software
35                                 constitutes acceptance of these terms.  If you do not agree with these terms,
36                                 please do not use, install, modify or redistribute this Apple software.
37
38                                 In consideration of your agreement to abide by the following terms, and subject
39                                 to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs
40                                 copyrights in this original Apple software (the "Apple Software"), to use,
41                                 reproduce, modify and redistribute the Apple Software, with or without
42                                 modifications, in source and/or binary forms; provided that if you redistribute
43                                 the Apple Software in its entirety and without modifications, you must retain
44                                 this notice and the following text and disclaimers in all such redistributions of
45                                 the Apple Software.  Neither the name, trademarks, service marks or logos of
46                                 Apple Computer, Inc. may be used to endorse or promote products derived from the
47                                 Apple Software without specific prior written permission from Apple.  Except as
48                                 expressly stated in this notice, no other rights or licenses, express or implied,
49                                 are granted by Apple herein, including but not limited to any patent rights that
50                                 may be infringed by your derivative works or by other works in which the Apple
51                                 Software may be incorporated.
52
53                                 The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
54                                 WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
55                                 WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
56                                 PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
57                                 COMBINATION WITH YOUR PRODUCTS.
58
59                                 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
60                                 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
61                                 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
62                                 ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
63                                 OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
64                                 (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
65                                 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
66
67         File Ownership:
68
69                 DRI:                            Apple Macintosh Developer Technical Support
70
71                 Other Contact:          For bug reports, consult the following page on
72                                                         the World Wide Web:
73                                                                 http://developer.apple.com/bugreporter/
74
75                 Technology:                     DTS Sample Code
76
77         Writers:
78
79                 (JL)    Jim Luther
80
81         Change History (most recent first):
82
83                  <4>     8/22/02        JL              [3016251]  Changed FSMoveRenameObjectUnicode to not use
84                                                                         the Temporary folder because it isn't available on
85                                                                         NFS volumes.
86                  <3>     4/19/02        JL              [2853905]  Fixed #if test around header includes.
87                  <2>     4/19/02        JL              [2850624]  Fixed C++ compile errors and Project Builder
88                                                                         warnings.
89                  <2>     4/19/02        JL              [2853901]  Updated standard disclaimer.
90                  <1>     1/25/02        JL              MoreFilesX 1.0
91 */
92
93 #if defined(__MACH__)
94         #include <Carbon/Carbon.h>
95         #include <string.h>
96 #else
97         #include <Carbon.h>
98         #include <string.h>
99 #endif
100
101 #include "MoreFilesX.h"
102
103 /* Set BuildingMoreFilesXForMacOS9 to 1 if building for Mac OS 9 */
104 #ifndef BuildingMoreFilesXForMacOS9
105         #define BuildingMoreFilesXForMacOS9 0
106 #endif
107
108 /*****************************************************************************/
109
110 #pragma mark ----- Local type definitions -----
111
112 struct FSIterateContainerGlobals
113 {
114         IterateContainerFilterProcPtr   iterateFilter;  /* pointer to IterateFilterProc */
115         FSCatalogInfoBitmap                             whichInfo;              /* fields of the CatalogInfo to get */
116         FSCatalogInfo                                   catalogInfo;    /* FSCatalogInfo */
117         FSRef                                                   ref;                    /* FSRef */
118         FSSpec                                                  spec;                   /* FSSpec */
119         FSSpec                                                  *specPtr;               /* pointer to spec field, or NULL */
120         HFSUniStr255                                    name;                   /* HFSUniStr255 */
121         HFSUniStr255                                    *namePtr;               /* pointer to name field, or NULL */
122         void                                                    *yourDataPtr;   /* a pointer to caller supplied data the filter may need to access */
123         ItemCount                                               maxLevels;              /* maximum levels to iterate through */
124         ItemCount                                               currentLevel;   /* the current level FSIterateContainerLevel is on */
125         Boolean                                                 quitFlag;               /* set to true if filter wants to kill interation */
126         Boolean                                                 containerChanged; /* temporary - set to true if the current container changed during iteration */
127         OSErr                                                   result;                 /* result */
128         ItemCount                                               actualObjects;  /* number of objects returned */
129 };
130 typedef struct FSIterateContainerGlobals FSIterateContainerGlobals;
131
132 struct FSDeleteContainerGlobals
133 {
134         OSErr                                                   result;                 /* result */
135         ItemCount                                               actualObjects;  /* number of objects returned */
136         FSCatalogInfo                                   catalogInfo;    /* FSCatalogInfo */
137 };
138 typedef struct FSDeleteContainerGlobals FSDeleteContainerGlobals;
139
140 /*****************************************************************************/
141
142 #pragma mark ----- Local prototypes -----
143
144 static
145 void
146 FSDeleteContainerLevel(
147         const FSRef *container,
148         FSDeleteContainerGlobals *theGlobals);
149
150 static
151 void
152 FSIterateContainerLevel(
153         FSIterateContainerGlobals *theGlobals);
154
155 static
156 OSErr
157 GenerateUniqueHFSUniStr(
158         long *startSeed,
159         const FSRef *dir1,
160         const FSRef *dir2,
161         HFSUniStr255 *uniqueName);
162
163 /*****************************************************************************/
164
165 #pragma mark ----- File Access Routines -----
166
167 /*****************************************************************************/
168
169 OSErr
170 FSCopyFork(
171         SInt16 srcRefNum,
172         SInt16 dstRefNum,
173         void *copyBufferPtr,
174         ByteCount copyBufferSize)
175 {
176         OSErr           srcResult;
177         OSErr           dstResult;
178         OSErr           result;
179         SInt64          forkSize;
180         ByteCount       readActualCount;
181         
182         /* check input parameters */
183         require_action((NULL != copyBufferPtr) && (0 != copyBufferSize), BadParameter, result = paramErr);
184         
185         /* get source fork size */
186         result = FSGetForkSize(srcRefNum, &forkSize);
187         require_noerr(result, SourceFSGetForkSizeFailed);
188         
189         /* allocate disk space for destination fork */
190         result = FSSetForkSize(dstRefNum, fsFromStart, forkSize);
191         require_noerr(result, DestinationFSSetForkSizeFailed);
192         
193         /* reset source fork's position to 0 */
194         result = FSSetForkPosition(srcRefNum, fsFromStart, 0);
195         require_noerr(result, SourceFSSetForkPositionFailed);
196         
197         /* reset destination fork's position to 0 */
198         result = FSSetForkPosition(dstRefNum, fsFromStart, 0);
199         require_noerr(result, DestinationFSSetForkPositionFailed);
200
201         /* If copyBufferSize is greater than 4K bytes, make it a multiple of 4k bytes */
202         /* This will make writes on local volumes faster */
203         if ( (copyBufferSize >= 0x00001000) && ((copyBufferSize & 0x00000fff) != 0) )
204         {
205                 copyBufferSize &= ~(0x00001000 - 1);
206         }
207         
208         /* copy source to destination */
209         srcResult = dstResult = noErr;
210         while ( (noErr == srcResult) && (noErr == dstResult) )
211         {
212                 srcResult = FSReadFork(srcRefNum, fsAtMark + noCacheMask, 0, copyBufferSize, copyBufferPtr, &readActualCount);
213                 dstResult = FSWriteFork(dstRefNum, fsAtMark + noCacheMask, 0, readActualCount, copyBufferPtr, NULL);
214         }
215         
216         /* make sure there were no errors at the destination */
217         require_noerr_action(dstResult, DestinationFSWriteForkFailed, result = dstResult);
218         
219         /* make sure the error at the source was eofErr */
220         require_action(eofErr == srcResult, SourceResultNotEofErr, result = srcResult);
221         
222         /* everything went as expected */
223         result = noErr;
224
225 SourceResultNotEofErr:
226 DestinationFSWriteForkFailed:
227 DestinationFSSetForkPositionFailed:
228 SourceFSSetForkPositionFailed:
229 DestinationFSSetForkSizeFailed:
230 SourceFSGetForkSizeFailed:
231 BadParameter:
232
233         return ( result );
234 }
235
236 /*****************************************************************************/
237
238 #pragma mark ----- Volume Access Routines -----
239
240 /*****************************************************************************/ 
241
242 OSErr
243 FSGetVolParms(
244         FSVolumeRefNum volRefNum,
245         UInt32 bufferSize,
246         GetVolParmsInfoBuffer *volParmsInfo,
247         UInt32 *actualInfoSize)
248 {
249         OSErr                   result;
250         HParamBlockRec  pb;
251         
252         /* check parameters */
253         require_action((NULL != volParmsInfo) && (NULL != actualInfoSize),
254                 BadParameter, result = paramErr);
255         
256         pb.ioParam.ioNamePtr = NULL;
257         pb.ioParam.ioVRefNum = volRefNum;
258         pb.ioParam.ioBuffer = (Ptr)volParmsInfo;
259         pb.ioParam.ioReqCount = (SInt32)bufferSize;
260         result = PBHGetVolParmsSync(&pb);
261         require_noerr(result, PBHGetVolParmsSync);
262         
263         /* return number of bytes the file system returned in volParmsInfo buffer */
264         *actualInfoSize = (UInt32)pb.ioParam.ioActCount;
265         
266 PBHGetVolParmsSync:
267 BadParameter:
268
269         return ( result );
270 }
271
272 /*****************************************************************************/
273
274 OSErr
275 FSGetVRefNum(
276         const FSRef *ref,
277         FSVolumeRefNum *vRefNum)
278 {
279         OSErr                   result;
280         FSCatalogInfo   catalogInfo;
281         
282         /* check parameters */
283         require_action(NULL != vRefNum, BadParameter, result = paramErr);
284         
285         /* get the volume refNum from the FSRef */
286         result = FSGetCatalogInfo(ref, kFSCatInfoVolume, &catalogInfo, NULL, NULL, NULL);
287         require_noerr(result, FSGetCatalogInfo);
288         
289         /* return volume refNum from catalogInfo */
290         *vRefNum = catalogInfo.volume;
291         
292 FSGetCatalogInfo:
293 BadParameter:
294
295         return ( result );
296 }
297
298 /*****************************************************************************/
299
300 OSErr
301 FSGetVInfo(
302         FSVolumeRefNum volume,
303         HFSUniStr255 *volumeName,       /* can be NULL */
304         UInt64 *freeBytes,                      /* can be NULL */
305         UInt64 *totalBytes)                     /* can be NULL */
306 {
307         OSErr                           result;
308         FSVolumeInfo            info;
309         
310         /* ask for the volume's sizes only if needed */
311         result = FSGetVolumeInfo(volume, 0, NULL,
312                 (((NULL != freeBytes) || (NULL != totalBytes)) ? kFSVolInfoSizes : kFSVolInfoNone),
313                 &info, volumeName, NULL);
314         require_noerr(result, FSGetVolumeInfo);
315         
316         if ( NULL != freeBytes )
317         {
318                 *freeBytes = info.freeBytes;
319         }
320         if ( NULL != totalBytes )
321         {
322                 *totalBytes = info.totalBytes;
323         }
324         
325 FSGetVolumeInfo:
326
327         return ( result );
328 }
329
330 /*****************************************************************************/
331
332 OSErr
333 FSGetVolFileSystemID(
334         FSVolumeRefNum volume,
335         UInt16 *fileSystemID,   /* can be NULL */
336         UInt16 *signature)              /* can be NULL */
337 {
338         OSErr                   result;
339         FSVolumeInfo    info;
340         
341         result = FSGetVolumeInfo(volume, 0, NULL, kFSVolInfoFSInfo, &info, NULL, NULL);
342         require_noerr(result, FSGetVolumeInfo);
343         
344         if ( NULL != fileSystemID )
345         {
346                 *fileSystemID = info.filesystemID;
347         }
348         if ( NULL != signature )
349         {
350                 *signature = info.signature;
351         }
352         
353 FSGetVolumeInfo:
354
355         return ( result );
356 }
357
358 /*****************************************************************************/
359
360 OSErr
361 FSGetMountedVolumes(
362         FSRef ***volumeRefsHandle,      /* pointer to handle of FSRefs */
363         ItemCount *numVolumes)
364 {
365         OSErr           result;
366         OSErr           memResult;
367         ItemCount       volumeIndex;
368         FSRef           ref;
369         
370         /* check parameters */
371         require_action((NULL != volumeRefsHandle) && (NULL != numVolumes),
372                 BadParameter, result = paramErr);
373         
374         /* No volumes yet */
375         *numVolumes = 0;
376         
377         /* Allocate a handle for the results */
378         *volumeRefsHandle = (FSRef **)NewHandle(0);
379         require_action(NULL != *volumeRefsHandle, NewHandle, result = memFullErr);
380         
381         /* Call FSGetVolumeInfo in loop to get all volumes starting with the first */
382         volumeIndex = 1;
383         do
384         {
385                 result = FSGetVolumeInfo(0, volumeIndex, NULL, kFSVolInfoNone, NULL, NULL, &ref);
386                 if ( noErr == result )
387                 {
388                         /* concatenate the FSRef to the end of the handle */
389                         PtrAndHand(&ref, (Handle)*volumeRefsHandle, sizeof(FSRef));
390                         memResult = MemError();
391                         require_noerr_action(memResult, MemoryAllocationFailed, result = memResult);
392                         
393                         ++(*numVolumes);        /* increment the volume count */
394                         ++volumeIndex;          /* and the volumeIndex to get the next volume*/
395                 }
396         } while ( noErr == result );
397         
398         /* nsvErr is OK -- it just means there are no more volumes */
399         require(nsvErr == result, FSGetVolumeInfo);
400                 
401         return ( noErr );
402         
403         /**********************/
404         
405 MemoryAllocationFailed:
406 FSGetVolumeInfo:
407
408         /* dispose of handle if already allocated and clear the outputs */
409         if ( NULL != *volumeRefsHandle )
410         {
411                 DisposeHandle((Handle)*volumeRefsHandle);
412                 *volumeRefsHandle = NULL;
413         }
414         *numVolumes = 0;
415         
416 NewHandle:
417 BadParameter:
418
419         return ( result );
420 }
421
422 /*****************************************************************************/
423
424 #pragma mark ----- FSRef/FSpec/Path/Name Conversion Routines -----
425
426 /*****************************************************************************/
427
428 OSErr
429 FSRefMakeFSSpec(
430         const FSRef *ref,
431         FSSpec *spec)
432 {
433         OSErr   result;
434         
435         /* check parameters */
436         require_action(NULL != spec, BadParameter, result = paramErr);
437         
438         result = FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, NULL, spec, NULL);
439         require_noerr(result, FSGetCatalogInfo);
440         
441 FSGetCatalogInfo:
442 BadParameter:
443
444         return ( result );
445 }
446
447 /*****************************************************************************/
448
449 OSErr
450 FSMakeFSRef(
451         FSVolumeRefNum volRefNum,
452         SInt32 dirID,
453         ConstStr255Param name,
454         FSRef *ref)
455 {
456         OSErr           result;
457         FSRefParam      pb;
458         
459         /* check parameters */
460         require_action(NULL != ref, BadParameter, result = paramErr);
461         
462         pb.ioVRefNum = volRefNum;
463         pb.ioDirID = dirID;
464         pb.ioNamePtr = (StringPtr)name;
465         pb.newRef = ref;
466         result = PBMakeFSRefSync(&pb);
467         require_noerr(result, PBMakeFSRefSync);
468         
469 PBMakeFSRefSync:
470 BadParameter:
471
472         return ( result );
473 }
474
475 /*****************************************************************************/
476
477 OSStatus
478 FSMakePath(
479         SInt16 volRefNum,
480         SInt32 dirID,
481         ConstStr255Param name,
482         UInt8 *path,
483         UInt32 maxPathSize)
484 {
485         OSStatus        result;
486         FSRef           ref;
487         
488         /* check parameters */
489         require_action(NULL != path, BadParameter, result = paramErr);
490         
491         /* convert the inputs to an FSRef */
492         result = FSMakeFSRef(volRefNum, dirID, name, &ref);
493         require_noerr(result, FSMakeFSRef);
494         
495         /* and then convert the FSRef to a path */
496         result = FSRefMakePath(&ref, path, maxPathSize);
497         require_noerr(result, FSRefMakePath);
498         
499 FSRefMakePath:
500 FSMakeFSRef:
501 BadParameter:
502
503         return ( result );
504 }
505
506 /*****************************************************************************/
507
508 OSStatus
509 FSPathMakeFSSpec(
510         const UInt8 *path,
511         FSSpec *spec,
512         Boolean *isDirectory)   /* can be NULL */
513 {
514         OSStatus        result;
515         FSRef           ref;
516         
517         /* check parameters */
518         require_action(NULL != spec, BadParameter, result = paramErr);
519         
520         /* convert the POSIX path to an FSRef */
521         result = FSPathMakeRef(path, &ref, isDirectory);
522         require_noerr(result, FSPathMakeRef);
523         
524         /* and then convert the FSRef to an FSSpec */
525         result = FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, NULL, spec, NULL);
526         require_noerr(result, FSGetCatalogInfo);
527         
528 FSGetCatalogInfo:
529 FSPathMakeRef:
530 BadParameter:
531
532         return ( result );
533 }
534
535 /*****************************************************************************/
536
537 OSErr
538 UnicodeNameGetHFSName(
539         UniCharCount nameLength,
540         const UniChar *name,
541         TextEncoding textEncodingHint,
542         Boolean isVolumeName,
543         Str31 hfsName)
544 {
545         OSStatus                        result;
546         ByteCount                       unicodeByteLength;
547         ByteCount                       unicodeBytesConverted;
548         ByteCount                       actualPascalBytes;
549         UnicodeMapping          uMapping;
550         UnicodeToTextInfo       utInfo;
551         
552         /* check parameters */
553         require_action(NULL != hfsName, BadParameter, result = paramErr);
554         
555         /* make sure output is valid in case we get errors or there's nothing to convert */
556         hfsName[0] = 0;
557         
558         unicodeByteLength = nameLength * sizeof(UniChar);
559         if ( 0 == unicodeByteLength )
560         {
561                 /* do nothing */
562                 result = noErr;
563         }
564         else
565         {
566                 /* if textEncodingHint is kTextEncodingUnknown, get a "default" textEncodingHint */
567                 if ( kTextEncodingUnknown == textEncodingHint )
568                 {
569                         ScriptCode                      script;
570                         RegionCode                      region;
571                         
572                         script = (ScriptCode)GetScriptManagerVariable(smSysScript);
573                         region = (RegionCode)GetScriptManagerVariable(smRegionCode);
574                         result = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, region,
575                                 NULL, &textEncodingHint );
576                         if ( paramErr == result )
577                         {
578                                 /* ok, ignore the region and try again */
579                                 result = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare,
580                                         kTextRegionDontCare, NULL, &textEncodingHint );
581                         }
582                         if ( noErr != result )
583                         {
584                                 /* ok... try something */
585                                 textEncodingHint = kTextEncodingMacRoman;
586                         }
587                 }
588                 
589                 uMapping.unicodeEncoding = CreateTextEncoding(kTextEncodingUnicodeV2_0,
590                         kUnicodeCanonicalDecompVariant, kUnicode16BitFormat);
591                 uMapping.otherEncoding = GetTextEncodingBase(textEncodingHint);
592                 uMapping.mappingVersion = kUnicodeUseHFSPlusMapping;
593         
594                 result = CreateUnicodeToTextInfo(&uMapping, &utInfo);
595                 require_noerr(result, CreateUnicodeToTextInfo);
596                 
597                 result = ConvertFromUnicodeToText(utInfo, unicodeByteLength, name, kUnicodeLooseMappingsMask,
598                         0, NULL, 0, NULL,       /* offsetCounts & offsetArrays */
599                         isVolumeName ? kHFSMaxVolumeNameChars : kHFSMaxFileNameChars,
600                         &unicodeBytesConverted, &actualPascalBytes, &hfsName[1]);
601                 require_noerr(result, ConvertFromUnicodeToText);
602                 
603                 hfsName[0] = (unsigned char)actualPascalBytes;  /* fill in length byte */
604
605 ConvertFromUnicodeToText:
606                 
607                 /* verify the result in debug builds -- there's really not anything you can do if it fails */
608                 verify_noerr(DisposeUnicodeToTextInfo(&utInfo));
609         }
610         
611 CreateUnicodeToTextInfo:        
612 BadParameter:
613
614         return ( result );
615 }
616
617 /*****************************************************************************/
618
619 OSErr
620 HFSNameGetUnicodeName(
621         ConstStr31Param hfsName,
622         TextEncoding textEncodingHint,
623         HFSUniStr255 *unicodeName)
624 {
625         ByteCount                       unicodeByteLength;
626         OSStatus                        result;
627         UnicodeMapping          uMapping;
628         TextToUnicodeInfo       tuInfo;
629         ByteCount                       pascalCharsRead;
630         
631         /* check parameters */
632         require_action(NULL != unicodeName, BadParameter, result = paramErr);
633         
634         /* make sure output is valid in case we get errors or there's nothing to convert */
635         unicodeName->length = 0;
636         
637         if ( 0 == StrLength(hfsName) )
638         {
639                 result = noErr;
640         }
641         else
642         {
643                 /* if textEncodingHint is kTextEncodingUnknown, get a "default" textEncodingHint */
644                 if ( kTextEncodingUnknown == textEncodingHint )
645                 {
646                         ScriptCode                      script;
647                         RegionCode                      region;
648                         
649                         script = GetScriptManagerVariable(smSysScript);
650                         region = GetScriptManagerVariable(smRegionCode);
651                         result = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, region,
652                                 NULL, &textEncodingHint);
653                         if ( paramErr == result )
654                         {
655                                 /* ok, ignore the region and try again */
656                                 result = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare,
657                                         kTextRegionDontCare, NULL, &textEncodingHint);
658                         }
659                         if ( noErr != result )
660                         {
661                                 /* ok... try something */
662                                 textEncodingHint = kTextEncodingMacRoman;
663                         }
664                 }
665                 
666                 uMapping.unicodeEncoding = CreateTextEncoding(kTextEncodingUnicodeV2_0,
667                         kUnicodeCanonicalDecompVariant, kUnicode16BitFormat);
668                 uMapping.otherEncoding = GetTextEncodingBase(textEncodingHint);
669                 uMapping.mappingVersion = kUnicodeUseHFSPlusMapping;
670         
671                 result = CreateTextToUnicodeInfo(&uMapping, &tuInfo);
672                 require_noerr(result, CreateTextToUnicodeInfo);
673                         
674                 result = ConvertFromTextToUnicode(tuInfo, hfsName[0], &hfsName[1],
675                         0,                                                              /* no control flag bits */
676                         0, NULL, 0, NULL,                               /* offsetCounts & offsetArrays */
677                         sizeof(unicodeName->unicode),   /* output buffer size in bytes */
678                         &pascalCharsRead, &unicodeByteLength, unicodeName->unicode);
679                 require_noerr(result, ConvertFromTextToUnicode);
680                 
681                 /* convert from byte count to char count */
682                 unicodeName->length = unicodeByteLength / sizeof(UniChar);
683
684 ConvertFromTextToUnicode:
685
686                 /* verify the result in debug builds -- there's really not anything you can do if it fails */
687                 verify_noerr(DisposeTextToUnicodeInfo(&tuInfo));
688         }
689         
690 CreateTextToUnicodeInfo:
691 BadParameter:
692
693         return ( result );
694 }
695
696 /*****************************************************************************/
697
698 #pragma mark ----- File/Directory Manipulation Routines -----
699
700 /*****************************************************************************/
701
702 Boolean FSRefValid(const FSRef *ref)
703 {
704         return ( noErr == FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, NULL, NULL, NULL) );
705 }
706
707 /*****************************************************************************/
708
709 OSErr
710 FSGetParentRef(
711         const FSRef *ref,
712         FSRef *parentRef)
713 {
714         OSErr   result;
715         FSCatalogInfo   catalogInfo;
716         
717         /* check parameters */
718         require_action(NULL != parentRef, BadParameter, result = paramErr);
719         
720         result = FSGetCatalogInfo(ref, kFSCatInfoNodeID, &catalogInfo, NULL, NULL, parentRef);
721         require_noerr(result, FSGetCatalogInfo);
722         
723         /*
724          * Note: FSRefs always point to real file system objects. So, there cannot
725          * be a FSRef to the parent of volume root directories. Early versions of
726          * Mac OS X do not handle this case correctly and incorrectly return a
727          * FSRef for the parent of volume root directories instead of returning an
728          * invalid FSRef (a cleared FSRef is invalid). The next three lines of code
729          * ensure that you won't run into this bug. WW9D!
730          */
731         if ( fsRtDirID == catalogInfo.nodeID )
732         {
733                 /* clear parentRef and return noErr which is the proper behavior */
734                 memset(parentRef, 0, sizeof(FSRef));
735         }
736
737 FSGetCatalogInfo:
738 BadParameter:
739
740         return ( result );
741 }
742
743 /*****************************************************************************/
744
745 OSErr
746 FSGetFileDirName(
747         const FSRef *ref,
748         HFSUniStr255 *outName)
749 {
750         OSErr   result;
751         
752         /* check parameters */
753         require_action(NULL != outName, BadParameter, result = paramErr);
754         
755         result = FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, outName, NULL, NULL);
756         require_noerr(result, FSGetCatalogInfo);
757         
758 FSGetCatalogInfo:
759 BadParameter:
760
761         return ( result );
762 }
763
764 /*****************************************************************************/
765
766 OSErr
767 FSGetNodeID(
768         const FSRef *ref,
769         long *nodeID,                   /* can be NULL */
770         Boolean *isDirectory)   /* can be NULL */
771 {
772         OSErr                           result;
773         FSCatalogInfo           catalogInfo;
774         FSCatalogInfoBitmap whichInfo;
775         
776         /* determine what catalog information to get */
777         whichInfo = kFSCatInfoNone; /* start with none */
778         if ( NULL != nodeID )
779         {
780                 whichInfo |= kFSCatInfoNodeID;
781         }
782         if ( NULL != isDirectory )
783         {
784                 whichInfo |= kFSCatInfoNodeFlags;
785         }
786         
787         result = FSGetCatalogInfo(ref, whichInfo, &catalogInfo, NULL, NULL, NULL);
788         require_noerr(result, FSGetCatalogInfo);
789         
790         if ( NULL != nodeID )
791         {
792                 *nodeID = catalogInfo.nodeID;
793         }
794         if ( NULL != isDirectory )
795         {
796                 *isDirectory = (0 != (kFSNodeIsDirectoryMask & catalogInfo.nodeFlags));
797         }
798         
799 FSGetCatalogInfo:
800
801         return ( result );
802 }
803
804 /*****************************************************************************/
805
806 OSErr
807 FSGetUserPrivilegesPermissions(
808         const FSRef *ref,
809         UInt8 *userPrivileges,          /* can be NULL */
810         UInt32 permissions[4])          /* can be NULL */
811 {
812         OSErr                   result;
813         FSCatalogInfo   catalogInfo;
814         FSCatalogInfoBitmap whichInfo;
815         
816         /* determine what catalog information to get */
817         whichInfo = kFSCatInfoNone; /* start with none */
818         if ( NULL != userPrivileges )
819         {
820                 whichInfo |= kFSCatInfoUserPrivs;
821         }
822         if ( NULL != permissions )
823         {
824                 whichInfo |= kFSCatInfoPermissions;
825         }
826         
827         result = FSGetCatalogInfo(ref, whichInfo, &catalogInfo, NULL, NULL, NULL);
828         require_noerr(result, FSGetCatalogInfo);
829         
830         if ( NULL != userPrivileges )
831         {
832                 *userPrivileges = catalogInfo.userPrivileges;
833         }
834         if ( NULL != permissions )
835         {
836                 BlockMoveData(&catalogInfo.permissions, permissions, sizeof(UInt32) * 4);
837         }
838         
839 FSGetCatalogInfo:
840
841         return ( result );
842 }
843
844 /*****************************************************************************/
845
846 OSErr
847 FSCheckLock(
848         const FSRef *ref)
849 {
850         OSErr                   result;
851         FSCatalogInfo   catalogInfo;
852         FSVolumeInfo    volumeInfo;
853         
854         /* get nodeFlags and vRefNum for container */
855         result = FSGetCatalogInfo(ref, kFSCatInfoNodeFlags + kFSCatInfoVolume, &catalogInfo, NULL, NULL,NULL);
856         require_noerr(result, FSGetCatalogInfo);
857         
858         /* is file locked? */
859         if ( 0 != (catalogInfo.nodeFlags & kFSNodeLockedMask) )
860         {
861                 result = fLckdErr;      /* file is locked */
862         }
863         else
864         {
865                 /* file isn't locked, but is volume locked? */
866                 
867                 /* get volume flags */
868                 result = FSGetVolumeInfo(catalogInfo.volume, 0, NULL, kFSVolInfoFlags, &volumeInfo, NULL, NULL);
869                 require_noerr(result, FSGetVolumeInfo);
870                 
871                 if ( 0 != (volumeInfo.flags & kFSVolFlagHardwareLockedMask) )
872                 {
873                         result = wPrErr;        /* volume locked by hardware */
874                 }
875                 else if ( 0 != (volumeInfo.flags & kFSVolFlagSoftwareLockedMask) )
876                 {
877                         result = vLckdErr;      /* volume locked by software */
878                 }
879         }
880         
881 FSGetVolumeInfo:
882 FSGetCatalogInfo:
883
884         return ( result );
885 }
886
887 /*****************************************************************************/
888
889 OSErr
890 FSGetForkSizes(
891         const FSRef *ref,
892         UInt64 *dataLogicalSize,        /* can be NULL */
893         UInt64 *rsrcLogicalSize)        /* can be NULL */
894 {
895         OSErr                           result;
896         FSCatalogInfoBitmap whichInfo;
897         FSCatalogInfo           catalogInfo;
898         
899         whichInfo = kFSCatInfoNodeFlags;
900         if ( NULL != dataLogicalSize )
901         {
902                 /* get data fork size */
903                 whichInfo |= kFSCatInfoDataSizes;
904         }
905         if ( NULL != rsrcLogicalSize )
906         {
907                 /* get resource fork size */
908                 whichInfo |= kFSCatInfoRsrcSizes;
909         }
910
911         /* get nodeFlags and catalog info */
912         result = FSGetCatalogInfo(ref, whichInfo, &catalogInfo, NULL, NULL,NULL);
913         require_noerr(result, FSGetCatalogInfo);
914         
915         /* make sure FSRef was to a file */
916         require_action(0 == (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask), FSRefNotFile, result = notAFileErr);
917         
918         if ( NULL != dataLogicalSize )
919         {
920                 /* return data fork size */
921                 *dataLogicalSize = catalogInfo.dataLogicalSize;
922         }
923         if ( NULL != rsrcLogicalSize )
924         {
925                 /* return resource fork size */
926                 *rsrcLogicalSize = catalogInfo.rsrcLogicalSize;
927         }
928         
929 FSRefNotFile:
930 FSGetCatalogInfo:
931
932         return ( result );
933 }
934
935 /*****************************************************************************/
936
937 OSErr
938 FSGetTotalForkSizes(
939         const FSRef *ref,
940         UInt64 *totalLogicalSize,       /* can be NULL */
941         UInt64 *totalPhysicalSize,      /* can be NULL */
942         ItemCount *forkCount)           /* can be NULL */
943 {
944         OSErr                   result;
945         CatPositionRec  forkIterator;
946         SInt64                  forkSize;
947         SInt64                  *forkSizePtr;
948         UInt64                  forkPhysicalSize;
949         UInt64                  *forkPhysicalSizePtr;
950         
951         /* Determine if forkSize needed */
952         if ( NULL != totalLogicalSize)
953         {
954                 *totalLogicalSize = 0;
955                 forkSizePtr = &forkSize;
956         }
957         else
958         {
959                 forkSizePtr = NULL;
960         }
961         
962         /* Determine if forkPhysicalSize is needed */
963         if ( NULL != totalPhysicalSize )
964         {
965                 *totalPhysicalSize = 0;
966                 forkPhysicalSizePtr = &forkPhysicalSize;
967         }
968         else
969         {
970                 forkPhysicalSizePtr = NULL;
971         }
972         
973         /* zero fork count if returning it */
974         if ( NULL != forkCount )
975         {
976                 *forkCount = 0;
977         }
978         
979         /* Iterate through the forks to get the sizes */
980         forkIterator.initialize = 0;
981         do
982         {
983                 result = FSIterateForks(ref, &forkIterator, NULL, forkSizePtr, forkPhysicalSizePtr);
984                 if ( noErr == result )
985                 {
986                         if ( NULL != totalLogicalSize )
987                         {
988                                 *totalLogicalSize += forkSize;
989                         }
990                         
991                         if ( NULL != totalPhysicalSize )
992                         {
993                                 *totalPhysicalSize += forkPhysicalSize;
994                         }
995                         
996                         if ( NULL != forkCount )
997                         {
998                                 ++*forkCount;
999                         }
1000                 }
1001         } while ( noErr == result );
1002         
1003         /* any error result other than errFSNoMoreItems is serious */
1004         require(errFSNoMoreItems == result, FSIterateForks);
1005         
1006         /* Normal exit */
1007         result = noErr;
1008
1009 FSIterateForks:
1010         
1011         return ( result );
1012 }
1013
1014 /*****************************************************************************/
1015
1016 OSErr
1017 FSBumpDate(
1018         const FSRef *ref)
1019 {
1020         OSStatus                result;
1021         FSCatalogInfo   catalogInfo;
1022         UTCDateTime             oldDateTime;
1023 #if !BuildingMoreFilesXForMacOS9
1024         FSRef                   parentRef;
1025         Boolean                 notifyParent;
1026 #endif
1027
1028 #if !BuildingMoreFilesXForMacOS9
1029         /* Get the node flags, the content modification date and time, and the parent ref */
1030         result = FSGetCatalogInfo(ref, kFSCatInfoNodeFlags + kFSCatInfoContentMod, &catalogInfo, NULL, NULL, &parentRef);
1031         require_noerr(result, FSGetCatalogInfo);
1032         
1033         /* Notify the parent if this is a file */
1034         notifyParent = (0 == (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask));
1035 #else
1036         /* Get the content modification date and time */
1037         result = FSGetCatalogInfo(ref, kFSCatInfoContentMod, &catalogInfo, NULL, NULL, NULL);
1038         require_noerr(result, FSGetCatalogInfo);
1039 #endif
1040         
1041         oldDateTime = catalogInfo.contentModDate;
1042
1043         /* Get the current date and time */
1044         result = GetUTCDateTime(&catalogInfo.contentModDate, kUTCDefaultOptions);
1045         require_noerr(result, GetUTCDateTime);
1046         
1047         /* if the old date and time is the the same as the current, bump the seconds by one */
1048         if ( (catalogInfo.contentModDate.fraction == oldDateTime.fraction) &&
1049                  (catalogInfo.contentModDate.lowSeconds == oldDateTime.lowSeconds) &&
1050                  (catalogInfo.contentModDate.highSeconds == oldDateTime.highSeconds) )
1051         {
1052                 ++catalogInfo.contentModDate.lowSeconds;
1053                 if ( 0 == catalogInfo.contentModDate.lowSeconds )
1054                 {
1055                         ++catalogInfo.contentModDate.highSeconds;
1056                 }
1057         }
1058         
1059         /* Bump the content modification date and time */
1060         result = FSSetCatalogInfo(ref, kFSCatInfoContentMod, &catalogInfo);
1061         require_noerr(result, FSSetCatalogInfo);
1062
1063 #if !BuildingMoreFilesXForMacOS9
1064         /*
1065          * The problem with FNNotify is that it is not available under Mac OS 9
1066          * and there's no way to test for that except for looking for the symbol
1067          * or something. So, I'll just conditionalize this for those who care
1068          * to send a notification.
1069          */
1070         
1071         /* Send a notification for the parent of the file, or for the directory */
1072         result = FNNotify(notifyParent ? &parentRef : ref, kFNDirectoryModifiedMessage, kNilOptions);
1073         require_noerr(result, FNNotify);
1074 #endif
1075
1076         /* ignore errors from FSSetCatalogInfo (volume might be write protected) and FNNotify */
1077 FNNotify:
1078 FSSetCatalogInfo:
1079         
1080         return ( noErr );
1081         
1082         /**********************/
1083         
1084 GetUTCDateTime:
1085 FSGetCatalogInfo:
1086
1087         return ( result );
1088 }
1089
1090 /*****************************************************************************/
1091
1092 OSErr
1093 FSGetFinderInfo(
1094         const FSRef *ref,
1095         FinderInfo *info,                                       /* can be NULL */
1096         ExtendedFinderInfo *extendedInfo,       /* can be NULL */
1097         Boolean *isDirectory)                           /* can be NULL */
1098 {
1099         OSErr                           result;
1100         FSCatalogInfo           catalogInfo;
1101         FSCatalogInfoBitmap whichInfo;
1102         
1103         /* determine what catalog information is really needed */
1104         whichInfo = kFSCatInfoNone;
1105         
1106         if ( NULL != info )
1107         {
1108                 /* get FinderInfo */
1109                 whichInfo |= kFSCatInfoFinderInfo;
1110         }
1111         
1112         if ( NULL != extendedInfo )
1113         {
1114                 /* get ExtendedFinderInfo */
1115                 whichInfo |= kFSCatInfoFinderXInfo;
1116         }
1117         
1118         if ( NULL != isDirectory )
1119         {
1120                 whichInfo |= kFSCatInfoNodeFlags;
1121         }
1122         
1123         result = FSGetCatalogInfo(ref, whichInfo, &catalogInfo, NULL, NULL, NULL);
1124         require_noerr(result, FSGetCatalogInfo);
1125         
1126         /* return FinderInfo if requested */
1127         if ( NULL != info )
1128         {
1129                 BlockMoveData(catalogInfo.finderInfo, info, sizeof(FinderInfo));
1130         }
1131         
1132         /* return ExtendedFinderInfo if requested */
1133         if ( NULL != extendedInfo)
1134         {
1135                 BlockMoveData(catalogInfo.extFinderInfo, extendedInfo, sizeof(ExtendedFinderInfo));
1136         }
1137         
1138         /* set isDirectory Boolean if requested */
1139         if ( NULL != isDirectory)
1140         {
1141                 *isDirectory = (0 != (kFSNodeIsDirectoryMask & catalogInfo.nodeFlags));
1142         }
1143         
1144 FSGetCatalogInfo:
1145
1146         return ( result );
1147 }
1148
1149 /*****************************************************************************/
1150
1151 OSErr
1152 FSSetFinderInfo(
1153         const FSRef *ref,
1154         const FinderInfo *info,
1155         const ExtendedFinderInfo *extendedInfo)
1156 {
1157         OSErr                           result;
1158         FSCatalogInfo           catalogInfo;
1159         FSCatalogInfoBitmap whichInfo;
1160         
1161         /* determine what catalog information will be set */
1162         whichInfo = kFSCatInfoNone; /* start with none */
1163         if ( NULL != info )
1164         {
1165                 /* set FinderInfo */
1166                 whichInfo |= kFSCatInfoFinderInfo;
1167                 BlockMoveData(info, catalogInfo.finderInfo, sizeof(FinderInfo));
1168         }
1169         if ( NULL != extendedInfo )
1170         {
1171                 /* set ExtendedFinderInfo */
1172                 whichInfo |= kFSCatInfoFinderXInfo;
1173                 BlockMoveData(extendedInfo, catalogInfo.extFinderInfo, sizeof(ExtendedFinderInfo));
1174         }
1175         
1176         result = FSSetCatalogInfo(ref, whichInfo, &catalogInfo);
1177         require_noerr(result, FSGetCatalogInfo);
1178         
1179 FSGetCatalogInfo:
1180
1181         return ( result );
1182 }
1183
1184 /*****************************************************************************/
1185
1186 OSErr
1187 FSChangeCreatorType(
1188         const FSRef *ref,
1189         OSType fileCreator,
1190         OSType fileType)
1191 {
1192         OSErr                   result;
1193         FSCatalogInfo   catalogInfo;
1194         FSRef                   parentRef;
1195         
1196         /* get nodeFlags, finder info, and parent FSRef */
1197         result = FSGetCatalogInfo(ref, kFSCatInfoNodeFlags + kFSCatInfoFinderInfo, &catalogInfo , NULL, NULL, &parentRef);
1198         require_noerr(result, FSGetCatalogInfo);
1199         
1200         /* make sure FSRef was to a file */
1201         require_action(0 == (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask), FSRefNotFile, result = notAFileErr);
1202         
1203         /* If fileType not 0x00000000, change fileType */
1204         if ( fileType != (OSType)0x00000000 )
1205         {
1206                 ((FileInfo *)&catalogInfo.finderInfo)->fileType = fileType;
1207         }
1208         
1209         /* If creator not 0x00000000, change creator */
1210         if ( fileCreator != (OSType)0x00000000 )
1211         {
1212                 ((FileInfo *)&catalogInfo.finderInfo)->fileCreator = fileCreator;
1213         }
1214         
1215         /* now, save the new information back to disk */
1216         result = FSSetCatalogInfo(ref, kFSCatInfoFinderInfo, &catalogInfo);
1217         require_noerr(result, FSSetCatalogInfo);
1218         
1219         /* and attempt to bump the parent directory's mod date to wake up */
1220         /* the Finder to the change we just made (ignore errors from this) */
1221         verify_noerr(FSBumpDate(&parentRef));
1222         
1223 FSSetCatalogInfo:
1224 FSRefNotFile:
1225 FSGetCatalogInfo:
1226
1227         return ( result );
1228 }
1229
1230 /*****************************************************************************/
1231
1232 OSErr
1233 FSChangeFinderFlags(
1234         const FSRef *ref,
1235         Boolean setBits,
1236         UInt16 flagBits)
1237 {
1238         OSErr                   result;
1239         FSCatalogInfo   catalogInfo;
1240         FSRef                   parentRef;
1241         
1242         /* get the current finderInfo */
1243         result = FSGetCatalogInfo(ref, kFSCatInfoFinderInfo, &catalogInfo, NULL, NULL, &parentRef);
1244         require_noerr(result, FSGetCatalogInfo);
1245         
1246         /* set or clear the appropriate bits in the finderInfo.finderFlags */
1247         if ( setBits )
1248         {
1249                 /* OR in the bits */
1250                 ((FileInfo *)&catalogInfo.finderInfo)->finderFlags |= flagBits;
1251         }
1252         else
1253         {
1254                 /* AND out the bits */
1255                 ((FileInfo *)&catalogInfo.finderInfo)->finderFlags &= ~flagBits;
1256         }
1257         
1258         /* save the modified finderInfo */
1259         result = FSSetCatalogInfo(ref, kFSCatInfoFinderInfo, &catalogInfo);
1260         require_noerr(result, FSSetCatalogInfo);
1261         
1262         /* and attempt to bump the parent directory's mod date to wake up the Finder */
1263         /* to the change we just made (ignore errors from this) */
1264         verify_noerr(FSBumpDate(&parentRef));
1265         
1266 FSSetCatalogInfo:
1267 FSGetCatalogInfo:
1268
1269         return ( result );
1270 }
1271
1272 /*****************************************************************************/
1273
1274 OSErr
1275 FSSetInvisible(
1276         const FSRef *ref)
1277 {
1278         return ( FSChangeFinderFlags(ref, true, kIsInvisible) );
1279 }
1280
1281 OSErr
1282 FSClearInvisible(
1283         const FSRef *ref)
1284 {
1285         return ( FSChangeFinderFlags(ref, false, kIsInvisible) );
1286 }
1287
1288 /*****************************************************************************/
1289
1290 OSErr
1291 FSSetNameLocked(
1292         const FSRef *ref)
1293 {
1294         return ( FSChangeFinderFlags(ref, true, kNameLocked) );
1295 }
1296
1297 OSErr
1298 FSClearNameLocked(
1299         const FSRef *ref)
1300 {
1301         return ( FSChangeFinderFlags(ref, false, kNameLocked) );
1302 }
1303
1304 /*****************************************************************************/
1305
1306 OSErr
1307 FSSetIsStationery(
1308         const FSRef *ref)
1309 {
1310         return ( FSChangeFinderFlags(ref, true, kIsStationery) );
1311 }
1312
1313 OSErr
1314 FSClearIsStationery(
1315         const FSRef *ref)
1316 {
1317         return ( FSChangeFinderFlags(ref, false, kIsStationery) );
1318 }
1319
1320 /*****************************************************************************/
1321
1322 OSErr
1323 FSSetHasCustomIcon(
1324         const FSRef *ref)
1325 {
1326         return ( FSChangeFinderFlags(ref, true, kHasCustomIcon) );
1327 }
1328
1329 OSErr
1330 FSClearHasCustomIcon(
1331         const FSRef *ref)
1332 {
1333         return ( FSChangeFinderFlags(ref, false, kHasCustomIcon) );
1334 }
1335
1336 /*****************************************************************************/
1337
1338 OSErr
1339 FSClearHasBeenInited(
1340         const FSRef *ref)
1341 {
1342         return ( FSChangeFinderFlags(ref, false, kHasBeenInited) );
1343 }
1344
1345 /*****************************************************************************/
1346
1347 OSErr
1348 FSCopyFileMgrAttributes(
1349         const FSRef *sourceRef,
1350         const FSRef *destinationRef,
1351         Boolean copyLockBit)
1352 {
1353         OSErr                   result;
1354         FSCatalogInfo   catalogInfo;
1355         
1356         /* get the source information */
1357         result = FSGetCatalogInfo(sourceRef, kFSCatInfoSettableInfo, &catalogInfo, NULL, NULL, NULL);
1358         require_noerr(result, FSGetCatalogInfo);
1359         
1360         /* don't copy the hasBeenInited bit; clear it */
1361         ((FileInfo *)&catalogInfo.finderInfo)->finderFlags &= ~kHasBeenInited;
1362         
1363         /* should the locked bit be copied? */
1364         if ( !copyLockBit )
1365         {
1366                 /* no, make sure the locked bit is clear */
1367                 catalogInfo.nodeFlags &= ~kFSNodeLockedMask;
1368         }
1369                 
1370         /* set the destination information */
1371         result = FSSetCatalogInfo(destinationRef, kFSCatInfoSettableInfo, &catalogInfo);
1372         require_noerr(result, FSSetCatalogInfo);
1373         
1374 FSSetCatalogInfo:
1375 FSGetCatalogInfo:
1376
1377         return ( result );
1378 }
1379
1380 /*****************************************************************************/
1381
1382 OSErr
1383 FSMoveRenameObjectUnicode(
1384         const FSRef *ref,
1385         const FSRef *destDirectory,
1386         UniCharCount nameLength,
1387         const UniChar *name,                    /* can be NULL (no rename during move) */
1388         TextEncoding textEncodingHint,
1389         FSRef *newRef)                                  /* if function fails along the way, newRef is final location of file */
1390 {
1391         OSErr                   result;
1392         FSVolumeRefNum  vRefNum;
1393         FSCatalogInfo   catalogInfo;
1394         FSRef                   originalDirectory;
1395         TextEncoding    originalTextEncodingHint;
1396         HFSUniStr255    originalName;
1397         HFSUniStr255    uniqueName;             /* unique name given to object while moving it to destination */
1398         long                    theSeed;                /* the seed for generating unique names */
1399         
1400         /* check parameters */
1401         require_action(NULL != newRef, BadParameter, result = paramErr);
1402         
1403         /* newRef = input to start with */
1404         BlockMoveData(ref, newRef, sizeof(FSRef));
1405         
1406         /* get destDirectory's vRefNum */
1407         result = FSGetCatalogInfo(destDirectory, kFSCatInfoVolume, &catalogInfo, NULL, NULL, NULL);
1408         require_noerr(result, DestinationBad);
1409         
1410         /* save vRefNum */
1411         vRefNum = catalogInfo.volume;
1412         
1413         /* get ref's vRefNum, TextEncoding, name and parent directory*/
1414         result = FSGetCatalogInfo(ref, kFSCatInfoTextEncoding + kFSCatInfoVolume, &catalogInfo, &originalName, NULL, &originalDirectory);
1415         require_noerr(result, SourceBad);
1416         
1417         /* save TextEncoding */
1418         originalTextEncodingHint = catalogInfo.textEncodingHint;
1419         
1420         /* make sure ref and destDirectory are on same volume */
1421         require_action(vRefNum == catalogInfo.volume, NotSameVolume, result = diffVolErr);
1422         
1423         /* Skip a few steps if we're not renaming */
1424         if ( NULL != name )
1425         {
1426                 /* generate a name that is unique in both directories */
1427                 theSeed = 0x4a696d4c;   /* a fine unlikely filename */
1428                 
1429                 result = GenerateUniqueHFSUniStr(&theSeed, &originalDirectory, destDirectory, &uniqueName);
1430                 require_noerr(result, GenerateUniqueHFSUniStrFailed);
1431                 
1432                 /* Rename the object to uniqueName */
1433                 result = FSRenameUnicode(ref, uniqueName.length, uniqueName.unicode, kTextEncodingUnknown, newRef);
1434                 require_noerr(result, FSRenameUnicodeBeforeMoveFailed);
1435                 
1436                 /* Move object to its new home */
1437                 result = FSMoveObject(newRef, destDirectory, newRef);
1438                 require_noerr(result, FSMoveObjectAfterRenameFailed);
1439                 
1440                 /* Rename the object to new name */
1441                 result = FSRenameUnicode(ref, nameLength, name, textEncodingHint, newRef);
1442                 require_noerr(result, FSRenameUnicodeAfterMoveFailed);
1443         }
1444         else
1445         {
1446                 /* Move object to its new home */
1447                 result = FSMoveObject(newRef, destDirectory, newRef);
1448                 require_noerr(result, FSMoveObjectNoRenameFailed);
1449         }
1450         
1451         return ( result );
1452         
1453         /*************/
1454
1455 /*
1456  * failure handling code when renaming
1457  */
1458
1459 FSRenameUnicodeAfterMoveFailed:
1460
1461         /* Error handling: move object back to original location - ignore errors */
1462         verify_noerr(FSMoveObject(newRef, &originalDirectory, newRef));
1463         
1464 FSMoveObjectAfterRenameFailed:
1465
1466         /* Error handling: rename object back to original name - ignore errors */
1467         verify_noerr(FSRenameUnicode(newRef, originalName.length, originalName.unicode, originalTextEncodingHint, newRef));
1468         
1469 FSRenameUnicodeBeforeMoveFailed:
1470 GenerateUniqueHFSUniStrFailed:
1471
1472 /*
1473  * failure handling code for renaming or not
1474  */
1475 FSMoveObjectNoRenameFailed:
1476 NotSameVolume:
1477 SourceBad:
1478 DestinationBad:
1479 BadParameter:
1480
1481         return ( result );
1482 }
1483
1484 /*****************************************************************************/
1485
1486 /*
1487         The FSDeleteContainerLevel function deletes the contents of a container
1488         directory. All files and subdirectories in the specified container are
1489         deleted. If a locked file or directory is encountered, it is unlocked
1490         and then deleted. If any unexpected errors are encountered,
1491         FSDeleteContainerLevel quits and returns to the caller.
1492         
1493         container                       --> FSRef to a directory.
1494         theGlobals                      --> A pointer to a FSDeleteContainerGlobals struct
1495                                                         which contains the variables that do not need to
1496                                                         be allocated each time FSDeleteContainerLevel
1497                                                         recurses. That lets FSDeleteContainerLevel use
1498                                                         less stack space per recursion level.
1499 */
1500
1501 static
1502 void
1503 FSDeleteContainerLevel(
1504         const FSRef *container,
1505         FSDeleteContainerGlobals *theGlobals)
1506 {
1507         /* level locals */
1508         FSIterator                                      iterator;
1509         FSRef                                           itemToDelete;
1510         UInt16                                          nodeFlags;
1511         
1512         /* Open FSIterator for flat access and give delete optimization hint */
1513         theGlobals->result = FSOpenIterator(container, kFSIterateFlat + kFSIterateDelete, &iterator);
1514         require_noerr(theGlobals->result, FSOpenIterator);
1515         
1516         /* delete the contents of the directory */
1517         do
1518         {
1519                 /* get 1 item to delete */
1520                 theGlobals->result = FSGetCatalogInfoBulk(iterator, 1, &theGlobals->actualObjects,
1521                                                                 NULL, kFSCatInfoNodeFlags, &theGlobals->catalogInfo,
1522                                                                 &itemToDelete, NULL, NULL);
1523                 if ( (noErr == theGlobals->result) && (1 == theGlobals->actualObjects) )
1524                 {
1525                         /* save node flags in local in case we have to recurse */
1526                         nodeFlags = theGlobals->catalogInfo.nodeFlags;
1527                         
1528                         /* is it a file or directory? */
1529                         if ( 0 != (nodeFlags & kFSNodeIsDirectoryMask) )
1530                         {
1531                                 /* it's a directory -- delete its contents before attempting to delete it */
1532                                 FSDeleteContainerLevel(&itemToDelete, theGlobals);
1533                         }
1534                         /* are we still OK to delete? */
1535                         if ( noErr == theGlobals->result )
1536                         {
1537                                 /* is item locked? */
1538                                 if ( 0 != (nodeFlags & kFSNodeLockedMask) )
1539                                 {
1540                                         /* then attempt to unlock it (ignore result since FSDeleteObject will set it correctly) */
1541                                         theGlobals->catalogInfo.nodeFlags = nodeFlags & ~kFSNodeLockedMask;
1542                                         (void) FSSetCatalogInfo(&itemToDelete, kFSCatInfoNodeFlags, &theGlobals->catalogInfo);
1543                                 }
1544                                 /* delete the item */
1545                                 theGlobals->result = FSDeleteObject(&itemToDelete);
1546                         }
1547                 }
1548         } while ( noErr == theGlobals->result );
1549         
1550         /* we found the end of the items normally, so return noErr */
1551         if ( errFSNoMoreItems == theGlobals->result )
1552         {
1553                 theGlobals->result = noErr;
1554         }
1555         
1556         /* close the FSIterator (closing an open iterator should never fail) */
1557         verify_noerr(FSCloseIterator(iterator));
1558
1559 FSOpenIterator:
1560
1561         return;
1562 }
1563
1564 /*****************************************************************************/
1565
1566 OSErr
1567 FSDeleteContainerContents(
1568         const FSRef *container)
1569 {
1570         FSDeleteContainerGlobals        theGlobals;
1571         
1572         /* delete container's contents */
1573         FSDeleteContainerLevel(container, &theGlobals);
1574         
1575         return ( theGlobals.result );
1576 }
1577
1578 /*****************************************************************************/
1579
1580 OSErr
1581 FSDeleteContainer(
1582         const FSRef *container)
1583 {
1584         OSErr                   result;
1585         FSCatalogInfo   catalogInfo;
1586         
1587         /* get nodeFlags for container */
1588         result = FSGetCatalogInfo(container, kFSCatInfoNodeFlags, &catalogInfo, NULL, NULL,NULL);
1589         require_noerr(result, FSGetCatalogInfo);
1590         
1591         /* make sure container is a directory */
1592         require_action(0 != (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask), ContainerNotDirectory, result = dirNFErr);
1593         
1594         /* delete container's contents */
1595         result = FSDeleteContainerContents(container);
1596         require_noerr(result, FSDeleteContainerContents);
1597         
1598         /* is container locked? */
1599         if ( 0 != (catalogInfo.nodeFlags & kFSNodeLockedMask) )
1600         {
1601                 /* then attempt to unlock container (ignore result since FSDeleteObject will set it correctly) */
1602                 catalogInfo.nodeFlags &= ~kFSNodeLockedMask;
1603                 (void) FSSetCatalogInfo(container, kFSCatInfoNodeFlags, &catalogInfo);
1604         }
1605         
1606         /* delete the container */
1607         result = FSDeleteObject(container);
1608         
1609 FSDeleteContainerContents:
1610 ContainerNotDirectory:
1611 FSGetCatalogInfo:
1612
1613         return ( result );
1614 }
1615
1616 /*****************************************************************************/
1617
1618 /*
1619         The FSIterateContainerLevel function iterates the contents of a container
1620         directory and calls a IterateContainerFilterProc function once for each
1621         file and directory found.
1622         
1623         theGlobals                      --> A pointer to a FSIterateContainerGlobals struct
1624                                                         which contains the variables needed globally by
1625                                                         all recusion levels of FSIterateContainerLevel.
1626                                                         That makes FSIterateContainer thread safe since
1627                                                         each call to it uses its own global world.
1628                                                         It also contains the variables that do not need
1629                                                         to be allocated each time FSIterateContainerLevel
1630                                                         recurses. That lets FSIterateContainerLevel use
1631                                                         less stack space per recursion level.
1632 */
1633
1634 static
1635 void
1636 FSIterateContainerLevel(
1637         FSIterateContainerGlobals *theGlobals)
1638 {       
1639         FSIterator      iterator;
1640         
1641         /* If maxLevels is zero, we aren't checking levels */
1642         /* If currentLevel < maxLevels, look at this level */
1643         if ( (theGlobals->maxLevels == 0) ||
1644                  (theGlobals->currentLevel < theGlobals->maxLevels) )
1645         {
1646                 /* Open FSIterator for flat access to theGlobals->ref */
1647                 theGlobals->result = FSOpenIterator(&theGlobals->ref, kFSIterateFlat, &iterator);
1648                 require_noerr(theGlobals->result, FSOpenIterator);
1649                 
1650                 ++theGlobals->currentLevel; /* Go to next level */
1651                 
1652                 /* Call FSGetCatalogInfoBulk in loop to get all items in the container */
1653                 do
1654                 {
1655                         theGlobals->result = FSGetCatalogInfoBulk(iterator, 1, &theGlobals->actualObjects,
1656                                 &theGlobals->containerChanged, theGlobals->whichInfo, &theGlobals->catalogInfo,
1657                                 &theGlobals->ref, theGlobals->specPtr, theGlobals->namePtr);
1658                         if ( (noErr == theGlobals->result || errFSNoMoreItems == theGlobals->result) &&
1659                                 (0 != theGlobals->actualObjects) )
1660                         {
1661                                 /* Call the IterateFilterProc */
1662                                 theGlobals->quitFlag = CallIterateContainerFilterProc(theGlobals->iterateFilter,
1663                                         theGlobals->containerChanged, theGlobals->currentLevel,
1664                                         &theGlobals->catalogInfo, &theGlobals->ref,
1665                                         theGlobals->specPtr, theGlobals->namePtr, theGlobals->yourDataPtr);
1666                                 /* Is it a directory? */
1667                                 if ( 0 != (theGlobals->catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) )
1668                                 {
1669                                         /* Keep going? */
1670                                         if ( !theGlobals->quitFlag )
1671                                         {
1672                                                 /* Dive again if the IterateFilterProc didn't say "quit" */
1673                                                 FSIterateContainerLevel(theGlobals);
1674                                         }
1675                                 }
1676                         }
1677                         /* time to fall back a level? */
1678                 } while ( (noErr == theGlobals->result) && (!theGlobals->quitFlag) );
1679                 
1680                 /* errFSNoMoreItems is OK - it only means we hit the end of this level */
1681                 /* afpAccessDenied is OK, too - it only means we cannot see inside a directory */
1682                 if ( (errFSNoMoreItems == theGlobals->result) ||
1683                          (afpAccessDenied == theGlobals->result) )
1684                 {
1685                         theGlobals->result = noErr;
1686                 }
1687                 
1688                 --theGlobals->currentLevel; /* Return to previous level as we leave */
1689                 
1690                 /* Close the FSIterator (closing an open iterator should never fail) */
1691                 verify_noerr(FSCloseIterator(iterator));
1692         }
1693         
1694 FSOpenIterator:
1695
1696         return;
1697 }
1698
1699 /*****************************************************************************/
1700
1701 OSErr
1702 FSIterateContainer(
1703         const FSRef *container,
1704         ItemCount maxLevels,
1705         FSCatalogInfoBitmap whichInfo,
1706         Boolean wantFSSpec,
1707         Boolean wantName,
1708         IterateContainerFilterProcPtr iterateFilter,
1709         void *yourDataPtr)
1710 {
1711         OSErr                                           result;
1712         FSIterateContainerGlobals       theGlobals;
1713         
1714         /* make sure there is an iterateFilter */
1715         require_action(iterateFilter != NULL, NoIterateFilter, result = paramErr);
1716         
1717         /*
1718          * set up the globals we need to access from the recursive routine
1719          */
1720         theGlobals.iterateFilter = iterateFilter;
1721         /* we need the node flags no matter what was requested so we can detect files vs. directories */
1722         theGlobals.whichInfo = whichInfo | kFSCatInfoNodeFlags;
1723         /* start with input container -- the first OpenIterator will ensure it is a directory */
1724         theGlobals.ref = *container;
1725         if ( wantFSSpec )
1726         {
1727                 theGlobals.specPtr = &theGlobals.spec;
1728         }
1729         else
1730         {
1731                 theGlobals.specPtr = NULL;
1732         }
1733         if ( wantName )
1734         {
1735                 theGlobals.namePtr = &theGlobals.name;
1736         }
1737         else
1738         {
1739                 theGlobals.namePtr = NULL;
1740         }
1741         theGlobals.yourDataPtr = yourDataPtr;
1742         theGlobals.maxLevels = maxLevels;
1743         theGlobals.currentLevel = 0;
1744         theGlobals.quitFlag = false;
1745         theGlobals.containerChanged = false;
1746         theGlobals.result = noErr;
1747         theGlobals.actualObjects = 0;
1748         
1749         /* here we go into recursion land... */
1750         FSIterateContainerLevel(&theGlobals);
1751         result = theGlobals.result;
1752         require_noerr(result, FSIterateContainerLevel);
1753         
1754 FSIterateContainerLevel:
1755 NoIterateFilter:
1756
1757         return ( result );
1758 }
1759
1760 /*****************************************************************************/
1761
1762 OSErr
1763 FSGetDirectoryItems(
1764         const FSRef *container,
1765         FSRef ***refsHandle,    /* pointer to handle of FSRefs */
1766         ItemCount *numRefs,
1767         Boolean *containerChanged)
1768 {
1769         /* Grab items 10 at a time. */
1770         enum { kMaxItemsPerBulkCall = 10 };
1771         
1772         OSErr           result;
1773         OSErr           memResult;
1774         FSIterator      iterator;
1775         FSRef           refs[kMaxItemsPerBulkCall];
1776         ItemCount       actualObjects;
1777         Boolean         changed;
1778         
1779         /* check parameters */
1780         require_action((NULL != refsHandle) && (NULL != numRefs) && (NULL != containerChanged),
1781                 BadParameter, result = paramErr);
1782         
1783         *numRefs = 0;
1784         *containerChanged = false;
1785         *refsHandle = (FSRef **)NewHandle(0);
1786         require_action(NULL != *refsHandle, NewHandle, result = memFullErr);
1787         
1788         /* open an FSIterator */
1789         result = FSOpenIterator(container, kFSIterateFlat, &iterator);
1790         require_noerr(result, FSOpenIterator);
1791         
1792         /* Call FSGetCatalogInfoBulk in loop to get all items in the container */
1793         do
1794         {
1795                 result = FSGetCatalogInfoBulk(iterator, kMaxItemsPerBulkCall, &actualObjects,
1796                                         &changed, kFSCatInfoNone,  NULL,  refs, NULL, NULL);
1797                 
1798                 /* if the container changed, set containerChanged for output, but keep going */
1799                 if ( changed )
1800                 {
1801                         *containerChanged = changed;
1802                 }
1803                 
1804                 /* any result other than noErr and errFSNoMoreItems is serious */
1805                 require((noErr == result) || (errFSNoMoreItems == result), FSGetCatalogInfoBulk);
1806                 
1807                 /* add objects to output array and count */
1808                 if ( 0 != actualObjects )
1809                 {
1810                         /* concatenate the FSRefs to the end of the      handle */
1811                         PtrAndHand(refs, (Handle)*refsHandle, actualObjects * sizeof(FSRef));
1812                         memResult = MemError();
1813                         require_noerr_action(memResult, MemoryAllocationFailed, result = memResult);
1814                         
1815                         *numRefs += actualObjects;
1816                 }
1817         } while ( noErr == result );
1818         
1819         verify_noerr(FSCloseIterator(iterator)); /* closing an open iterator should never fail, but... */
1820         
1821         return ( noErr );
1822         
1823         /**********************/
1824         
1825 MemoryAllocationFailed:
1826 FSGetCatalogInfoBulk:
1827
1828         /* close the iterator */
1829         verify_noerr(FSCloseIterator(iterator));
1830
1831 FSOpenIterator:
1832         /* dispose of handle if already allocated and clear the outputs */
1833         if ( NULL != *refsHandle )
1834         {
1835                 DisposeHandle((Handle)*refsHandle);
1836                 *refsHandle = NULL;
1837         }
1838         *numRefs = 0;
1839         
1840 NewHandle:
1841 BadParameter:
1842
1843         return ( result );
1844 }
1845
1846 /*****************************************************************************/
1847
1848 /*
1849         The GenerateUniqueName function generates a HFSUniStr255 name that is
1850         unique in both dir1 and dir2.
1851         
1852         startSeed                       -->     A pointer to a long which is used to generate the
1853                                                         unique name.
1854                                                 <--     It is modified on output to a value which should
1855                                                         be used to generate the next unique name.
1856         dir1                            -->     The first directory.
1857         dir2                            -->     The second directory.
1858         uniqueName                      <--     A pointer to a HFSUniStr255 where the unique name
1859                                                         is to be returned.
1860 */
1861
1862 static
1863 OSErr
1864 GenerateUniqueHFSUniStr(
1865         long *startSeed,
1866         const FSRef *dir1,
1867         const FSRef *dir2,
1868         HFSUniStr255 *uniqueName)
1869 {
1870         OSErr                   result;
1871         long                    i;
1872         FSRefParam              pb;
1873         FSRef                   newRef;
1874         unsigned char   hexStr[17] = "0123456789ABCDEF";
1875         
1876         /* set up the parameter block */
1877         pb.name = uniqueName->unicode;
1878         pb.nameLength = 8;      /* always 8 characters */
1879         pb.textEncodingHint = kTextEncodingUnknown;
1880         pb.newRef = &newRef;
1881
1882         /* loop until we get fnfErr with a filename in both directories */
1883         result = noErr;
1884         while ( fnfErr != result )
1885         {
1886                 /* convert startSeed to 8 character Unicode string */
1887                 uniqueName->length = 8;
1888                 for ( i = 0; i < 8; ++i )
1889                 {
1890                         uniqueName->unicode[i] = hexStr[((*startSeed >> ((7-i)*4)) & 0xf)];
1891                 }
1892                 
1893                 /* try in dir1 */
1894                 pb.ref = dir1;
1895                 result = PBMakeFSRefUnicodeSync(&pb);
1896                 if ( fnfErr == result )
1897                 {
1898                         /* try in dir2 */
1899                         pb.ref = dir2;
1900                         result = PBMakeFSRefUnicodeSync(&pb);
1901                         if ( fnfErr != result )
1902                         {
1903                                 /* exit if anything other than noErr or fnfErr */
1904                                 require_noerr(result, Dir2PBMakeFSRefUnicodeSyncFailed);
1905                         }
1906                 }
1907                 else
1908                 {
1909                         /* exit if anything other than noErr or fnfErr */
1910                         require_noerr(result, Dir1PBMakeFSRefUnicodeSyncFailed);
1911                 }
1912                 
1913                 /* increment seed for next pass through loop, */
1914                 /* or for next call to GenerateUniqueHFSUniStr */
1915                 ++(*startSeed);
1916         }
1917         
1918         /* we have a unique file name which doesn't exist in dir1 or dir2 */
1919         result = noErr;
1920         
1921 Dir2PBMakeFSRefUnicodeSyncFailed:
1922 Dir1PBMakeFSRefUnicodeSyncFailed:
1923
1924         return ( result );
1925 }
1926
1927 /*****************************************************************************/
1928
1929 OSErr
1930 FSExchangeObjectsCompat(
1931         const FSRef *sourceRef,
1932         const FSRef *destRef,
1933         FSRef *newSourceRef,
1934         FSRef *newDestRef)
1935 {
1936         enum
1937         {
1938                 /* get all settable info except for mod dates, plus the volume refNum and parent directory ID */
1939                 kGetCatInformationMask = (kFSCatInfoSettableInfo |
1940                                                                   kFSCatInfoVolume |
1941                                                                   kFSCatInfoParentDirID) &
1942                                                                  ~(kFSCatInfoContentMod | kFSCatInfoAttrMod),
1943                 /* set everything possible except for mod dates */
1944                 kSetCatinformationMask = kFSCatInfoSettableInfo &
1945                                                                  ~(kFSCatInfoContentMod | kFSCatInfoAttrMod)
1946         };
1947         
1948         OSErr                                   result;
1949         GetVolParmsInfoBuffer   volParmsInfo;
1950         UInt32                                  infoSize;
1951         FSCatalogInfo                   sourceCatalogInfo;      /* source file's catalog information */
1952         FSCatalogInfo                   destCatalogInfo;        /* destination file's catalog information */
1953         HFSUniStr255                    sourceName;                     /* source file's Unicode name */
1954         HFSUniStr255                    destName;                       /* destination file's Unicode name */
1955         FSRef                                   sourceCurrentRef;       /* FSRef to current location of source file throughout this function */
1956         FSRef                                   destCurrentRef;         /* FSRef to current location of destination file throughout this function */
1957         FSRef                                   sourceParentRef;        /* FSRef to parent directory of source file */
1958         FSRef                                   destParentRef;          /* FSRef to parent directory of destination file */
1959         HFSUniStr255                    sourceUniqueName;       /* unique name given to source file while exchanging it with destination */
1960         HFSUniStr255                    destUniqueName;         /* unique name given to destination file while exchanging it with source */
1961         long                                    theSeed;                        /* the seed for generating unique names */
1962         Boolean                                 sameParentDirs;         /* true if source and destinatin parent directory is the same */
1963         
1964         /* check parameters */
1965         require_action((NULL != newSourceRef) && (NULL != newDestRef), BadParameter, result = paramErr);
1966         
1967         /* output refs and current refs = input refs to start with */
1968         BlockMoveData(sourceRef, newSourceRef, sizeof(FSRef));
1969         BlockMoveData(sourceRef, &sourceCurrentRef, sizeof(FSRef));
1970         
1971         BlockMoveData(destRef, newDestRef, sizeof(FSRef));
1972         BlockMoveData(destRef, &destCurrentRef, sizeof(FSRef));
1973
1974         /* get source volume's vRefNum */
1975         result = FSGetCatalogInfo(&sourceCurrentRef, kFSCatInfoVolume, &sourceCatalogInfo, NULL, NULL, NULL);
1976         require_noerr(result, DetermineSourceVRefNumFailed);
1977         
1978         /* see if that volume supports FSExchangeObjects */
1979         result = FSGetVolParms(sourceCatalogInfo.volume, sizeof(GetVolParmsInfoBuffer),
1980                 &volParmsInfo, &infoSize);
1981         if ( (noErr == result) && VolSupportsFSExchangeObjects(&volParmsInfo) )
1982         {
1983                 /* yes - use FSExchangeObjects */
1984                 result = FSExchangeObjects(sourceRef, destRef);
1985         }
1986         else
1987         {
1988                 /* no - emulate FSExchangeObjects */
1989                 
1990                 /* Note: The compatibility case won't work for files with *Btree control blocks. */
1991                 /* Right now the only *Btree files are created by the system. */
1992                 
1993                 /* get all catalog information and Unicode names for each file */
1994                 result = FSGetCatalogInfo(&sourceCurrentRef, kGetCatInformationMask, &sourceCatalogInfo, &sourceName, NULL, &sourceParentRef);
1995                 require_noerr(result, SourceFSGetCatalogInfoFailed);
1996                 
1997                 result = FSGetCatalogInfo(&destCurrentRef, kGetCatInformationMask, &destCatalogInfo, &destName, NULL, &destParentRef);
1998                 require_noerr(result, DestFSGetCatalogInfoFailed);
1999                 
2000                 /* make sure source and destination are on same volume */
2001                 require_action(sourceCatalogInfo.volume == destCatalogInfo.volume, NotSameVolume, result = diffVolErr);
2002                 
2003                 /* make sure both files are *really* files */
2004                 require_action((0 == (sourceCatalogInfo.nodeFlags & kFSNodeIsDirectoryMask)) &&
2005                                            (0 == (destCatalogInfo.nodeFlags & kFSNodeIsDirectoryMask)), NotAFile, result = notAFileErr);
2006                 
2007                 /* generate 2 names that are unique in both directories */
2008                 theSeed = 0x4a696d4c;   /* a fine unlikely filename */
2009                 
2010                 result = GenerateUniqueHFSUniStr(&theSeed, &sourceParentRef, &destParentRef, &sourceUniqueName);
2011                 require_noerr(result, GenerateUniqueHFSUniStr1Failed);
2012                 
2013                 result = GenerateUniqueHFSUniStr(&theSeed, &sourceParentRef, &destParentRef, &destUniqueName);
2014                 require_noerr(result, GenerateUniqueHFSUniStr2Failed);
2015
2016                 /* rename sourceCurrentRef to sourceUniqueName */
2017                 result = FSRenameUnicode(&sourceCurrentRef, sourceUniqueName.length, sourceUniqueName.unicode, kTextEncodingUnknown, newSourceRef);
2018                 require_noerr(result, FSRenameUnicode1Failed);
2019                 BlockMoveData(newSourceRef, &sourceCurrentRef, sizeof(FSRef));
2020                 
2021                 /* rename destCurrentRef to destUniqueName */
2022                 result = FSRenameUnicode(&destCurrentRef, destUniqueName.length, destUniqueName.unicode, kTextEncodingUnknown, newDestRef);
2023                 require_noerr(result, FSRenameUnicode2Failed);
2024                 BlockMoveData(newDestRef, &destCurrentRef, sizeof(FSRef));
2025                 
2026                 /* are the source and destination parent directories the same? */
2027                 sameParentDirs = ( sourceCatalogInfo.parentDirID == destCatalogInfo.parentDirID );
2028                 if ( !sameParentDirs )
2029                 {
2030                         /* move source file to dest parent directory */
2031                         result = FSMoveObject(&sourceCurrentRef, &destParentRef, newSourceRef);
2032                         require_noerr(result, FSMoveObject1Failed);
2033                         BlockMoveData(newSourceRef, &sourceCurrentRef, sizeof(FSRef));
2034                         
2035                         /* move dest file to source parent directory */
2036                         result = FSMoveObject(&destCurrentRef, &sourceParentRef, newDestRef);
2037                         require_noerr(result, FSMoveObject2Failed);
2038                         BlockMoveData(newDestRef, &destCurrentRef, sizeof(FSRef));
2039                 }
2040                 
2041                 /* At this point, the files are in their new locations (if they were moved). */
2042                 /* The source file is named sourceUniqueName and is in the directory referred to */
2043                 /* by destParentRef. The destination file is named destUniqueName and is in the */
2044                 /* directory referred to by sourceParentRef. */
2045                                 
2046                 /* give source file the dest file's catalog information except for mod dates */
2047                 result = FSSetCatalogInfo(&sourceCurrentRef, kSetCatinformationMask, &destCatalogInfo);
2048                 require_noerr(result, FSSetCatalogInfo1Failed);
2049                 
2050                 /* give dest file the source file's catalog information except for mod dates */
2051                 result = FSSetCatalogInfo(&destCurrentRef, kSetCatinformationMask, &sourceCatalogInfo);
2052                 require_noerr(result, FSSetCatalogInfo2Failed);
2053                 
2054                 /* rename source file with dest file's name */
2055                 result = FSRenameUnicode(&sourceCurrentRef, destName.length, destName.unicode, destCatalogInfo.textEncodingHint, newSourceRef);
2056                 require_noerr(result, FSRenameUnicode3Failed);
2057                 BlockMoveData(newSourceRef, &sourceCurrentRef, sizeof(FSRef));
2058                 
2059                 /* rename dest file with source file's name */
2060                 result = FSRenameUnicode(&destCurrentRef, sourceName.length, sourceName.unicode, sourceCatalogInfo.textEncodingHint, newDestRef);
2061                 require_noerr(result, FSRenameUnicode4Failed);
2062                 
2063                 /* we're done with no errors, so swap newSourceRef and newDestRef */
2064                 BlockMoveData(newDestRef, newSourceRef, sizeof(FSRef));
2065                 BlockMoveData(&sourceCurrentRef, newDestRef, sizeof(FSRef));
2066         }
2067         
2068         return ( result );
2069         
2070         /**********************/
2071
2072 /* If there are any failures while emulating FSExchangeObjects, attempt to reverse any steps */
2073 /* already taken. In any case, newSourceRef and newDestRef will refer to the files in whatever */
2074 /* state and location they ended up in so that both files can be found by the calling code. */
2075         
2076 FSRenameUnicode4Failed:
2077
2078         /* attempt to rename source file to sourceUniqueName */
2079         if ( noErr == FSRenameUnicode(&sourceCurrentRef, sourceUniqueName.length, sourceUniqueName.unicode, kTextEncodingUnknown, newSourceRef) )
2080         {
2081                 BlockMoveData(newSourceRef, &sourceCurrentRef, sizeof(FSRef));
2082         }
2083
2084 FSRenameUnicode3Failed:
2085
2086         /* attempt to restore dest file's catalog information */
2087         verify_noerr(FSSetCatalogInfo(&destCurrentRef, kFSCatInfoSettableInfo, &destCatalogInfo));
2088
2089 FSSetCatalogInfo2Failed:
2090
2091         /* attempt to restore source file's catalog information */
2092         verify_noerr(FSSetCatalogInfo(&sourceCurrentRef, kFSCatInfoSettableInfo, &sourceCatalogInfo));
2093
2094 FSSetCatalogInfo1Failed:
2095
2096         if ( !sameParentDirs )
2097         {
2098                 /* attempt to move dest file back to dest directory */
2099                 if ( noErr == FSMoveObject(&destCurrentRef, &destParentRef, newDestRef) )
2100                 {
2101                         BlockMoveData(newDestRef, &destCurrentRef, sizeof(FSRef));
2102                 }
2103         }
2104
2105 FSMoveObject2Failed:
2106
2107         if ( !sameParentDirs )
2108         {
2109                 /* attempt to move source file back to source directory */
2110                 if ( noErr == FSMoveObject(&sourceCurrentRef, &sourceParentRef, newSourceRef) )
2111                 {
2112                         BlockMoveData(newSourceRef, &sourceCurrentRef, sizeof(FSRef));
2113                 }
2114         }
2115
2116 FSMoveObject1Failed:
2117
2118         /* attempt to rename dest file to original name */
2119         verify_noerr(FSRenameUnicode(&destCurrentRef, destName.length, destName.unicode, destCatalogInfo.textEncodingHint, newDestRef));
2120
2121 FSRenameUnicode2Failed:
2122
2123         /* attempt to rename source file to original name */
2124         verify_noerr(FSRenameUnicode(&sourceCurrentRef, sourceName.length, sourceName.unicode, sourceCatalogInfo.textEncodingHint, newSourceRef));
2125
2126 FSRenameUnicode1Failed:
2127 GenerateUniqueHFSUniStr2Failed:
2128 GenerateUniqueHFSUniStr1Failed:
2129 NotAFile:
2130 NotSameVolume:
2131 DestFSGetCatalogInfoFailed:
2132 SourceFSGetCatalogInfoFailed:
2133 DetermineSourceVRefNumFailed:   
2134 BadParameter:
2135
2136         return ( result );
2137 }
2138
2139 /*****************************************************************************/
2140
2141 #pragma mark ----- Shared Environment Routines -----
2142
2143 /*****************************************************************************/
2144
2145 OSErr
2146 FSLockRange(
2147         SInt16 refNum,
2148         SInt32 rangeLength,
2149         SInt32 rangeStart)
2150 {
2151         OSErr                   result;
2152         ParamBlockRec   pb;
2153
2154         pb.ioParam.ioRefNum = refNum;
2155         pb.ioParam.ioReqCount = rangeLength;
2156         pb.ioParam.ioPosMode = fsFromStart;
2157         pb.ioParam.ioPosOffset = rangeStart;
2158         result = PBLockRangeSync(&pb);
2159         require_noerr(result, PBLockRangeSync);
2160         
2161 PBLockRangeSync:
2162
2163         return ( result );
2164 }
2165
2166 /*****************************************************************************/
2167
2168 OSErr
2169 FSUnlockRange(
2170         SInt16 refNum,
2171         SInt32 rangeLength,
2172         SInt32 rangeStart)
2173 {
2174         OSErr                   result;
2175         ParamBlockRec   pb;
2176
2177         pb.ioParam.ioRefNum = refNum;
2178         pb.ioParam.ioReqCount = rangeLength;
2179         pb.ioParam.ioPosMode = fsFromStart;
2180         pb.ioParam.ioPosOffset = rangeStart;
2181         result = PBUnlockRangeSync(&pb);
2182         require_noerr(result, PBUnlockRangeSync);
2183         
2184 PBUnlockRangeSync:
2185
2186         return ( result );
2187 }
2188
2189 /*****************************************************************************/
2190
2191 OSErr
2192 FSGetDirAccess(
2193         const FSRef *ref,
2194         SInt32 *ownerID,                /* can be NULL */
2195         SInt32 *groupID,                /* can be NULL */
2196         SInt32 *accessRights)   /* can be NULL */
2197 {
2198         OSErr                   result;
2199         FSSpec                  spec;
2200         HParamBlockRec  pb;
2201         
2202         /* get FSSpec from FSRef */
2203         result = FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, NULL, &spec, NULL);
2204         require_noerr(result, FSGetCatalogInfo);
2205         
2206         /* get directory access info for FSSpec */
2207         pb.accessParam.ioNamePtr = (StringPtr)spec.name;
2208         pb.accessParam.ioVRefNum = spec.vRefNum;
2209         pb.fileParam.ioDirID = spec.parID;
2210         result = PBHGetDirAccessSync(&pb);
2211         require_noerr(result, PBHGetDirAccessSync);
2212         
2213         /* return the IDs and access rights */
2214         if ( NULL != ownerID )
2215         {
2216                 *ownerID = pb.accessParam.ioACOwnerID;
2217         }
2218         if ( NULL != groupID )
2219         {
2220                 *groupID = pb.accessParam.ioACGroupID;
2221         }
2222         if ( NULL != accessRights )
2223         {
2224                 *accessRights = pb.accessParam.ioACAccess;
2225         }
2226         
2227 PBHGetDirAccessSync:
2228 FSGetCatalogInfo:
2229
2230         return ( result );
2231 }
2232
2233 /*****************************************************************************/
2234
2235 OSErr
2236 FSSetDirAccess(
2237         const FSRef *ref,
2238         SInt32 ownerID,
2239         SInt32 groupID,
2240         SInt32 accessRights)
2241 {
2242         OSErr                   result;
2243         FSSpec                  spec;
2244         HParamBlockRec  pb;
2245
2246         enum
2247         {
2248                 /* Just the bits that can be set */
2249                 kSetDirAccessSettableMask = (kioACAccessBlankAccessMask +
2250                         kioACAccessEveryoneWriteMask + kioACAccessEveryoneReadMask + kioACAccessEveryoneSearchMask +
2251                         kioACAccessGroupWriteMask + kioACAccessGroupReadMask + kioACAccessGroupSearchMask +
2252                         kioACAccessOwnerWriteMask + kioACAccessOwnerReadMask + kioACAccessOwnerSearchMask)
2253         };
2254         
2255         /* get FSSpec from FSRef */
2256         result = FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, NULL, &spec, NULL);
2257         require_noerr(result, FSGetCatalogInfo);
2258         
2259         /* set directory access info for FSSpec */
2260         pb.accessParam.ioNamePtr = (StringPtr)spec.name;
2261         pb.accessParam.ioVRefNum = spec.vRefNum;
2262         pb.fileParam.ioDirID = spec.parID;
2263         pb.accessParam.ioACOwnerID = ownerID;
2264         pb.accessParam.ioACGroupID = groupID;
2265         pb.accessParam.ioACAccess = accessRights & kSetDirAccessSettableMask;
2266         result = PBHSetDirAccessSync(&pb);
2267         require_noerr(result, PBHSetDirAccessSync);
2268         
2269 PBHSetDirAccessSync:
2270 FSGetCatalogInfo:
2271
2272         return ( result );
2273 }
2274
2275 /*****************************************************************************/
2276
2277 OSErr
2278 FSGetVolMountInfoSize(
2279         FSVolumeRefNum volRefNum,
2280         SInt16 *size)
2281 {
2282         OSErr                   result;
2283         ParamBlockRec   pb;
2284
2285         /* check parameters */
2286         require_action(NULL != size, BadParameter, result = paramErr);
2287         
2288         pb.ioParam.ioNamePtr = NULL;
2289         pb.ioParam.ioVRefNum = volRefNum;
2290         pb.ioParam.ioBuffer = (Ptr)size;
2291         result = PBGetVolMountInfoSize(&pb);
2292         require_noerr(result, PBGetVolMountInfoSize);
2293         
2294 PBGetVolMountInfoSize:
2295 BadParameter:
2296
2297         return ( result );
2298 }
2299
2300 /*****************************************************************************/
2301
2302 OSErr
2303 FSGetVolMountInfo(
2304         FSVolumeRefNum volRefNum,
2305         void *volMountInfo)
2306 {
2307         OSErr                   result;
2308         ParamBlockRec   pb;
2309
2310         /* check parameters */
2311         require_action(NULL != volMountInfo, BadParameter, result = paramErr);
2312         
2313         pb.ioParam.ioNamePtr = NULL;
2314         pb.ioParam.ioVRefNum = volRefNum;
2315         pb.ioParam.ioBuffer = (Ptr)volMountInfo;
2316         result = PBGetVolMountInfo(&pb);
2317         require_noerr(result, PBGetVolMountInfo);
2318         
2319 PBGetVolMountInfo:
2320 BadParameter:
2321
2322         return ( result );
2323 }
2324
2325 /*****************************************************************************/
2326
2327 OSErr
2328 FSVolumeMount(
2329         const void *volMountInfo,
2330         FSVolumeRefNum *volRefNum)
2331 {
2332         OSErr                   result;
2333         ParamBlockRec   pb;
2334
2335         /* check parameters */
2336         require_action(NULL != volRefNum, BadParameter, result = paramErr);
2337         
2338         pb.ioParam.ioBuffer = (Ptr)volMountInfo;
2339         result = PBVolumeMount(&pb);
2340         require_noerr(result, PBVolumeMount);
2341         
2342         /* return the volume reference number */
2343         *volRefNum = pb.ioParam.ioVRefNum;
2344
2345 PBVolumeMount:
2346 BadParameter:
2347
2348         return ( result );
2349 }
2350
2351 /*****************************************************************************/
2352
2353 OSErr
2354 FSMapID(
2355         FSVolumeRefNum volRefNum,
2356         SInt32 ugID,
2357         SInt16 objType,
2358         Str31 name)
2359 {
2360         OSErr                   result;
2361         HParamBlockRec  pb;
2362
2363         /* check parameters */
2364         require_action(NULL != name, BadParameter, result = paramErr);
2365         
2366         pb.objParam.ioNamePtr = NULL;
2367         pb.objParam.ioVRefNum = volRefNum;
2368         pb.objParam.ioObjType = objType;
2369         pb.objParam.ioObjNamePtr = name;
2370         pb.objParam.ioObjID = ugID;
2371         result = PBHMapIDSync(&pb);
2372         require_noerr(result, PBHMapIDSync);
2373         
2374 PBHMapIDSync:
2375 BadParameter:
2376
2377         return ( result );
2378 }
2379
2380 /*****************************************************************************/
2381
2382 OSErr
2383 FSMapName(
2384         FSVolumeRefNum volRefNum,
2385         ConstStr255Param name,
2386         SInt16 objType,
2387         SInt32 *ugID)
2388 {
2389         OSErr                   result;
2390         HParamBlockRec  pb;
2391
2392         /* check parameters */
2393         require_action(NULL != ugID, BadParameter, result = paramErr);
2394         
2395         pb.objParam.ioNamePtr = NULL;
2396         pb.objParam.ioVRefNum = volRefNum;
2397         pb.objParam.ioObjType = objType;
2398         pb.objParam.ioObjNamePtr = (StringPtr)name;
2399         result = PBHMapNameSync(&pb);
2400         require_noerr(result, PBHMapNameSync);
2401         
2402         /* return the user or group ID */
2403         *ugID = pb.objParam.ioObjID;
2404         
2405 PBHMapNameSync:
2406 BadParameter:
2407
2408         return ( result );
2409 }
2410
2411 /*****************************************************************************/
2412
2413 OSErr
2414 FSCopyFile(
2415         const FSRef *srcFileRef,
2416         const FSRef *dstDirectoryRef,
2417         UniCharCount nameLength,
2418         const UniChar *copyName,        /* can be NULL (no rename during copy) */
2419         TextEncoding textEncodingHint,
2420         FSRef *newRef)                          /* can be NULL */
2421 {
2422         OSErr                                   result;
2423         FSSpec                                  srcFileSpec;
2424         FSCatalogInfo                   catalogInfo;
2425         HParamBlockRec                  pb;
2426         Str31                                   hfsName;
2427         GetVolParmsInfoBuffer   volParmsInfo;
2428         UInt32                                  infoSize;
2429         
2430         /* get source FSSpec from source FSRef */
2431         result = FSGetCatalogInfo(srcFileRef, kFSCatInfoNone, NULL, NULL, &srcFileSpec, NULL);
2432         require_noerr(result, FSGetCatalogInfo_srcFileRef);
2433         
2434         /* Make sure the volume supports CopyFile */
2435         result = FSGetVolParms(srcFileSpec.vRefNum, sizeof(GetVolParmsInfoBuffer),
2436                 &volParmsInfo, &infoSize);
2437         require_action((noErr == result) && VolHasCopyFile(&volParmsInfo),
2438                 NoCopyFileSupport, result = paramErr);
2439
2440         /* get destination volume reference number and destination directory ID from destination FSRef */
2441         result = FSGetCatalogInfo(dstDirectoryRef, kFSCatInfoVolume + kFSCatInfoNodeID,
2442                 &catalogInfo, NULL, NULL, NULL);
2443         require_noerr(result, FSGetCatalogInfo_dstDirectoryRef);
2444         
2445         /* tell the server to copy the object */
2446         pb.copyParam.ioVRefNum = srcFileSpec.vRefNum;
2447         pb.copyParam.ioDirID = srcFileSpec.parID;
2448         pb.copyParam.ioNamePtr = (StringPtr)srcFileSpec.name;
2449         pb.copyParam.ioDstVRefNum = catalogInfo.volume;
2450         pb.copyParam.ioNewDirID = (long)catalogInfo.nodeID;
2451         pb.copyParam.ioNewName = NULL;
2452         if ( NULL != copyName )
2453         {
2454                 result = UnicodeNameGetHFSName(nameLength, copyName, textEncodingHint, false, hfsName);
2455                 require_noerr(result, UnicodeNameGetHFSName);
2456                 
2457                 pb.copyParam.ioCopyName = hfsName;
2458         }
2459         else
2460         {
2461                 pb.copyParam.ioCopyName = NULL;
2462         }
2463         result = PBHCopyFileSync(&pb);
2464         require_noerr(result, PBHCopyFileSync);
2465         
2466         if ( NULL != newRef )
2467         {
2468                 verify_noerr(FSMakeFSRef(pb.copyParam.ioDstVRefNum, pb.copyParam.ioNewDirID,
2469                         pb.copyParam.ioCopyName, newRef));
2470         }
2471                 
2472 PBHCopyFileSync:
2473 UnicodeNameGetHFSName:
2474 FSGetCatalogInfo_dstDirectoryRef:
2475 NoCopyFileSupport:
2476 FSGetCatalogInfo_srcFileRef:
2477
2478         return ( result );
2479 }
2480
2481 /*****************************************************************************/
2482
2483 OSErr
2484 FSMoveRename(
2485         const FSRef *srcFileRef,
2486         const FSRef *dstDirectoryRef,
2487         UniCharCount nameLength,
2488         const UniChar *moveName,        /* can be NULL (no rename during move) */
2489         TextEncoding textEncodingHint,
2490         FSRef *newRef)                          /* can be NULL */
2491 {
2492         OSErr                                   result;
2493         FSSpec                                  srcFileSpec;
2494         FSCatalogInfo                   catalogInfo;
2495         HParamBlockRec                  pb;
2496         Str31                                   hfsName;
2497         GetVolParmsInfoBuffer   volParmsInfo;
2498         UInt32                                  infoSize;
2499         
2500         /* get source FSSpec from source FSRef */
2501         result = FSGetCatalogInfo(srcFileRef, kFSCatInfoNone, NULL, NULL, &srcFileSpec, NULL);
2502         require_noerr(result, FSGetCatalogInfo_srcFileRef);
2503         
2504         /* Make sure the volume supports MoveRename */
2505         result = FSGetVolParms(srcFileSpec.vRefNum, sizeof(GetVolParmsInfoBuffer),
2506                 &volParmsInfo, &infoSize);
2507         require_action((noErr == result) && VolHasMoveRename(&volParmsInfo),
2508                 NoMoveRenameSupport, result = paramErr);
2509
2510         /* get destination volume reference number and destination directory ID from destination FSRef */
2511         result = FSGetCatalogInfo(dstDirectoryRef, kFSCatInfoVolume + kFSCatInfoNodeID,
2512                 &catalogInfo, NULL, NULL, NULL);
2513         require_noerr(result, FSGetCatalogInfo_dstDirectoryRef);
2514         
2515         /* make sure the source and destination are on the same volume */
2516         require_action(srcFileSpec.vRefNum == catalogInfo.volume, NotSameVolume, result = diffVolErr);
2517         
2518         /* tell the server to move and rename the object */
2519         pb.copyParam.ioVRefNum = srcFileSpec.vRefNum;
2520         pb.copyParam.ioDirID = srcFileSpec.parID;
2521         pb.copyParam.ioNamePtr = (StringPtr)srcFileSpec.name;
2522         pb.copyParam.ioNewDirID = (long)catalogInfo.nodeID;
2523         pb.copyParam.ioNewName = NULL;
2524         if ( NULL != moveName )
2525         {
2526                 result = UnicodeNameGetHFSName(nameLength, moveName, textEncodingHint, false, hfsName);
2527                 require_noerr(result, UnicodeNameGetHFSName);
2528                 
2529                 pb.copyParam.ioCopyName = hfsName;
2530         }
2531         else
2532         {
2533                 pb.copyParam.ioCopyName = NULL;
2534         }
2535         result = PBHMoveRenameSync(&pb);
2536         require_noerr(result, PBHMoveRenameSync);
2537         
2538         if ( NULL != newRef )
2539         {
2540                 verify_noerr(FSMakeFSRef(pb.copyParam.ioVRefNum, pb.copyParam.ioNewDirID,
2541                         pb.copyParam.ioCopyName, newRef));
2542         }
2543         
2544 PBHMoveRenameSync:
2545 UnicodeNameGetHFSName:
2546 NotSameVolume:
2547 FSGetCatalogInfo_dstDirectoryRef:
2548 NoMoveRenameSupport:
2549 FSGetCatalogInfo_srcFileRef:
2550
2551         return ( result );
2552 }
2553
2554 /*****************************************************************************/
2555
2556 #pragma mark ----- File ID Routines -----
2557
2558 /*****************************************************************************/
2559
2560 OSErr
2561 FSResolveFileIDRef(
2562         FSVolumeRefNum volRefNum,
2563         SInt32 fileID,
2564         FSRef *ref)
2565 {
2566         OSErr           result;
2567         FIDParam        pb;
2568         Str255          tempStr;
2569         
2570         /* check parameters */
2571         require_action(NULL != ref, BadParameter, result = paramErr);
2572         
2573         /* resolve the file ID reference */
2574         tempStr[0] = 0;
2575         pb.ioNamePtr = tempStr;
2576         pb.ioVRefNum = volRefNum;
2577         pb.ioFileID = fileID;
2578         result = PBResolveFileIDRefSync((HParmBlkPtr)&pb);
2579         require_noerr(result, PBResolveFileIDRefSync);
2580         
2581         /* and then make an FSRef to the file */
2582         result = FSMakeFSRef(volRefNum, pb.ioSrcDirID, tempStr, ref);
2583         require_noerr(result, FSMakeFSRef);
2584         
2585 FSMakeFSRef:
2586 PBResolveFileIDRefSync:
2587 BadParameter:
2588
2589         return ( result );
2590 }
2591
2592 /*****************************************************************************/
2593
2594 OSErr
2595 FSCreateFileIDRef(
2596         const FSRef *ref,
2597         SInt32 *fileID)
2598 {
2599         OSErr           result;
2600         FSSpec          spec;
2601         FIDParam        pb;
2602         
2603         /* check parameters */
2604         require_action(NULL != fileID, BadParameter, result = paramErr);
2605         
2606         /* Get an FSSpec from the FSRef */
2607         result = FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, NULL, &spec, NULL);
2608         require_noerr(result, FSGetCatalogInfo);
2609         
2610         /* Create (or get) the file ID reference using the FSSpec */
2611         pb.ioNamePtr = (StringPtr)spec.name;
2612         pb.ioVRefNum = spec.vRefNum;
2613         pb.ioSrcDirID = spec.parID;
2614         result = PBCreateFileIDRefSync((HParmBlkPtr)&pb);
2615         require((noErr == result) || (fidExists == result) || (afpIDExists == result),
2616                 PBCreateFileIDRefSync);
2617         
2618         /* return the file ID reference */
2619         *fileID = pb.ioFileID;
2620         
2621 PBCreateFileIDRefSync:
2622 FSGetCatalogInfo:
2623 BadParameter:
2624
2625         return ( result );
2626 }
2627
2628 /*****************************************************************************/
2629
2630 #pragma mark ----- Utility Routines -----
2631
2632 /*****************************************************************************/
2633
2634 Ptr
2635 GetTempBuffer(
2636         ByteCount buffReqSize,
2637         ByteCount *buffActSize)
2638 {
2639         enum
2640         {
2641                 kSlopMemory = 0x00008000        /* 32K - Amount of free memory to leave when allocating buffers */
2642         };
2643         
2644         Ptr tempPtr;
2645         
2646         /* check parameters */
2647         require_action(NULL != buffActSize, BadParameter, tempPtr = NULL);
2648         
2649         /* Make request a multiple of 4K bytes */
2650         buffReqSize = buffReqSize & 0xfffff000;
2651         
2652         if ( buffReqSize < 0x00001000 )
2653         {
2654                 /* Request was smaller than 4K bytes - make it 4K */
2655                 buffReqSize = 0x00001000;
2656         }
2657         
2658         /* Attempt to allocate the memory */
2659         tempPtr = NewPtr(buffReqSize);
2660         
2661         /* If request failed, go to backup plan */
2662         if ( (tempPtr == NULL) && (buffReqSize > 0x00001000) )
2663         {
2664                 /*
2665                 **      Try to get largest 4K byte block available
2666                 **      leaving some slop for the toolbox if possible
2667                 */
2668                 long freeMemory = (FreeMem() - kSlopMemory) & 0xfffff000;
2669                 
2670                 buffReqSize = MaxBlock() & 0xfffff000;
2671                 
2672                 if ( buffReqSize > freeMemory )
2673                 {
2674                         buffReqSize = freeMemory;
2675                 }
2676                 
2677                 if ( buffReqSize == 0 )
2678                 {
2679                         buffReqSize = 0x00001000;
2680                 }
2681                 
2682                 tempPtr = NewPtr(buffReqSize);
2683         }
2684         
2685         /* Return bytes allocated */
2686         if ( tempPtr != NULL )
2687         {
2688                 *buffActSize = buffReqSize;
2689         }
2690         else
2691         {
2692                 *buffActSize = 0;
2693         }
2694         
2695 BadParameter:
2696
2697         return ( tempPtr );
2698 }
2699
2700 /*****************************************************************************/
2701
2702 OSErr
2703 FileRefNumGetFSRef(
2704         short refNum,
2705         FSRef *ref)
2706 {
2707         return ( FSGetForkCBInfo(refNum, 0, NULL, NULL, NULL, ref, NULL) );
2708 }
2709
2710 /*****************************************************************************/
2711
2712 OSErr
2713 FSSetDefault(
2714         const FSRef *newDefault,
2715         FSRef *oldDefault)
2716 {
2717         OSErr                   result;
2718         FSVolumeRefNum  vRefNum;
2719         long                    dirID;
2720         FSCatalogInfo   catalogInfo;
2721         
2722         /* check parameters */
2723         require_action((NULL != newDefault) && (NULL != oldDefault), BadParameter, result = paramErr);
2724         
2725         /* Get nodeFlags, vRefNum and dirID (nodeID) of newDefault */
2726         result = FSGetCatalogInfo(newDefault,
2727                 kFSCatInfoNodeFlags + kFSCatInfoVolume + kFSCatInfoNodeID,
2728                 &catalogInfo, NULL, NULL, NULL);
2729         require_noerr(result, FSGetCatalogInfo);
2730         
2731         /* Make sure newDefault is a directory */
2732         require_action(0 != (kFSNodeIsDirectoryMask & catalogInfo.nodeFlags), NewDefaultNotDirectory,
2733                 result = dirNFErr);
2734         
2735         /* Get the current working directory. */
2736         result = HGetVol(NULL, &vRefNum, &dirID);
2737         require_noerr(result, HGetVol);
2738         
2739         /* Return the oldDefault FSRef */
2740         result = FSMakeFSRef(vRefNum, dirID, NULL, oldDefault);
2741         require_noerr(result, FSMakeFSRef);
2742         
2743         /* Set the new current working directory */
2744         result = HSetVol(NULL, catalogInfo.volume, catalogInfo.nodeID);
2745         require_noerr(result, HSetVol);
2746
2747 HSetVol:
2748 FSMakeFSRef:
2749 HGetVol:
2750 NewDefaultNotDirectory:
2751 FSGetCatalogInfo:
2752 BadParameter:
2753
2754         return ( result );
2755 }
2756
2757 /*****************************************************************************/
2758
2759 OSErr
2760 FSRestoreDefault(
2761         const FSRef *oldDefault)
2762 {
2763         OSErr                   result;
2764         FSCatalogInfo   catalogInfo;
2765         
2766         /* check parameters */
2767         require_action(NULL != oldDefault, BadParameter, result = paramErr);
2768         
2769         /* Get nodeFlags, vRefNum and dirID (nodeID) of oldDefault */
2770         result = FSGetCatalogInfo(oldDefault,
2771                 kFSCatInfoNodeFlags + kFSCatInfoVolume + kFSCatInfoNodeID,
2772                 &catalogInfo, NULL, NULL, NULL);
2773         require_noerr(result, FSGetCatalogInfo);
2774         
2775         /* Make sure oldDefault is a directory */
2776         require_action(0 != (kFSNodeIsDirectoryMask & catalogInfo.nodeFlags), OldDefaultNotDirectory,
2777                 result = dirNFErr);
2778         
2779         /* Set the current working directory to oldDefault */
2780         result = HSetVol(NULL, catalogInfo.volume, catalogInfo.nodeID);
2781         require_noerr(result, HSetVol);
2782
2783 HSetVol:
2784 OldDefaultNotDirectory:
2785 FSGetCatalogInfo:
2786 BadParameter:
2787
2788         return ( result );
2789 }
2790
2791 /*****************************************************************************/