diff options
author | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
---|---|---|
committer | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
commit | 38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch) | |
tree | adca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/mac_updater | |
parent | README.txt (diff) | |
download | meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2 meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz |
Second Life viewer sources 1.13.2.12
Diffstat (limited to 'linden/indra/mac_updater')
-rw-r--r-- | linden/indra/mac_updater/FSCopyObject.c | 1986 | ||||
-rw-r--r-- | linden/indra/mac_updater/FSCopyObject.h | 285 | ||||
-rw-r--r-- | linden/indra/mac_updater/GenLinkedList.c | 212 | ||||
-rw-r--r-- | linden/indra/mac_updater/GenLinkedList.h | 120 | ||||
-rw-r--r-- | linden/indra/mac_updater/MoreFilesX.c | 2765 | ||||
-rw-r--r-- | linden/indra/mac_updater/MoreFilesX.h | 1848 | ||||
-rw-r--r-- | linden/indra/mac_updater/mac_updater.cpp | 1106 |
7 files changed, 8322 insertions, 0 deletions
diff --git a/linden/indra/mac_updater/FSCopyObject.c b/linden/indra/mac_updater/FSCopyObject.c new file mode 100644 index 0000000..b9210d2 --- /dev/null +++ b/linden/indra/mac_updater/FSCopyObject.c | |||
@@ -0,0 +1,1986 @@ | |||
1 | /* | ||
2 | File: FSCopyObject.c | ||
3 | |||
4 | Contains: A Copy/Delete Files/Folders engine which uses HFS+ API's. | ||
5 | This code takes some tricks/techniques from MoreFilesX and | ||
6 | MPFileCopy, wraps them all up into an easy to use API, and | ||
7 | adds a bunch of features. It will run on Mac OS 9.1 through | ||
8 | 9.2.x and 10.1.x and up (Classic, Carbon and Mach-O) | ||
9 | |||
10 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. | ||
11 | ("Apple") in consideration of your agreement to the following terms, and your | ||
12 | use, installation, modification or redistribution of this Apple software | ||
13 | constitutes acceptance of these terms. If you do not agree with these terms, | ||
14 | please do not use, install, modify or redistribute this Apple software. | ||
15 | |||
16 | In consideration of your agreement to abide by the following terms, and subject | ||
17 | to these terms, Apple grants you a personal, non-exclusive license, under Apple�s | ||
18 | copyrights in this original Apple software (the "Apple Software"), to use, | ||
19 | reproduce, modify and redistribute the Apple Software, with or without | ||
20 | modifications, in source and/or binary forms; provided that if you redistribute | ||
21 | the Apple Software in its entirety and without modifications, you must retain | ||
22 | this notice and the following text and disclaimers in all such redistributions of | ||
23 | the Apple Software. Neither the name, trademarks, service marks or logos of | ||
24 | Apple Computer, Inc. may be used to endorse or promote products derived from the | ||
25 | Apple Software without specific prior written permission from Apple. Except as | ||
26 | expressly stated in this notice, no other rights or licenses, express or implied, | ||
27 | are granted by Apple herein, including but not limited to any patent rights that | ||
28 | may be infringed by your derivative works or by other works in which the Apple | ||
29 | Software may be incorporated. | ||
30 | |||
31 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO | ||
32 | WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED | ||
33 | WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
34 | PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN | ||
35 | COMBINATION WITH YOUR PRODUCTS. | ||
36 | |||
37 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR | ||
38 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE | ||
39 | GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
40 | ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION | ||
41 | OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT | ||
42 | (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN | ||
43 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
44 | |||
45 | Copyright � 2002-2004 Apple Computer, Inc., All Rights Reserved | ||
46 | */ | ||
47 | |||
48 | #include "FSCopyObject.h" | ||
49 | #include "GenLinkedList.h" | ||
50 | #if !TARGET_API_MAC_OSX | ||
51 | #include <UnicodeConverter.h> | ||
52 | #endif | ||
53 | #include <stddef.h> | ||
54 | #include <string.h> | ||
55 | |||
56 | #pragma mark ----- Tunable Parameters ----- | ||
57 | |||
58 | /* The following constants control the behavior of the copy engine. */ | ||
59 | |||
60 | enum { /* BufferSizeForVolSpeed */ | ||
61 | /* kDefaultCopyBufferSize = 2L * 1024 * 1024,*/ /* 2MB, Fast but not very responsive. */ | ||
62 | kDefaultCopyBufferSize = 256L * 1024, /* 256kB, Slower but can still use machine. */ | ||
63 | kMaximumCopyBufferSize = 2L * 1024 * 1024, | ||
64 | kMinimumCopyBufferSize = 1024 | ||
65 | }; | ||
66 | |||
67 | enum { /* CheckForDestInsideSrc */ | ||
68 | errFSDestInsideSource = -1234 | ||
69 | }; | ||
70 | |||
71 | enum { | ||
72 | /* for use with PBHGetDirAccess in IsDropBox */ | ||
73 | kPrivilegesMask = kioACAccessUserWriteMask | kioACAccessUserReadMask | kioACAccessUserSearchMask, | ||
74 | |||
75 | /* for use with FSGetCatalogInfo and FSPermissionInfo->mode */ | ||
76 | /* from sys/stat.h... note -- sys/stat.h definitions are in octal */ | ||
77 | /* */ | ||
78 | /* You can use these values to adjust the users/groups permissions */ | ||
79 | /* on a file/folder with FSSetCatalogInfo and extracting the */ | ||
80 | /* kFSCatInfoPermissions field. See code below for examples */ | ||
81 | kRWXUserAccessMask = 0x01C0, | ||
82 | kReadAccessUser = 0x0100, | ||
83 | kWriteAccessUser = 0x0080, | ||
84 | kExecuteAccessUser = 0x0040, | ||
85 | |||
86 | kRWXGroupAccessMask = 0x0038, | ||
87 | kReadAccessGroup = 0x0020, | ||
88 | kWriteAccessGroup = 0x0010, | ||
89 | kExecuteAccessGroup = 0x0008, | ||
90 | |||
91 | kRWXOtherAccessMask = 0x0007, | ||
92 | kReadAccessOther = 0x0004, | ||
93 | kWriteAccessOther = 0x0002, | ||
94 | kExecuteAccessOther = 0x0001, | ||
95 | |||
96 | kDropFolderValue = kWriteAccessOther | kExecuteAccessOther | ||
97 | }; | ||
98 | |||
99 | #define kNumObjects 80 | ||
100 | |||
101 | #define VolHasCopyFile(volParms) (((volParms)->vMAttrib & (1L << bHasCopyFile)) != 0) | ||
102 | |||
103 | #pragma mark ----- Struct Definitions ----- | ||
104 | |||
105 | /* The CopyParams data structure holds the copy buffer used */ | ||
106 | /* when copying the forks over, as well as special case */ | ||
107 | /* info on the destination */ | ||
108 | struct CopyParams { | ||
109 | void *copyBuffer; | ||
110 | ByteCount copyBufferSize; | ||
111 | Boolean copyingToDropFolder; | ||
112 | Boolean copyingToLocalVolume; | ||
113 | Boolean volHasCopyFile; | ||
114 | DupeAction dupeAction; | ||
115 | }; | ||
116 | typedef struct CopyParams CopyParams; | ||
117 | |||
118 | /* The FilterParams data structure holds the date and info */ | ||
119 | /* that the caller wants passed into the Filter Proc, as well */ | ||
120 | /* as the Filter Proc Pointer itself */ | ||
121 | struct FilterParams { | ||
122 | FSCatalogInfoBitmap whichInfo; | ||
123 | CopyObjectFilterProcPtr filterProcPtr; | ||
124 | Boolean containerChanged; | ||
125 | Boolean wantSpec; | ||
126 | Boolean wantName; | ||
127 | void *yourDataPtr; | ||
128 | }; | ||
129 | typedef struct FilterParams FilterParams; | ||
130 | |||
131 | /* The ForkTracker data structure holds information about a specific fork, */ | ||
132 | /* specifically the name and the refnum. We use this to build a list of */ | ||
133 | /* all the forks before we start copying them. We need to do this because */ | ||
134 | /* if we're copying into a drop folder, we must open all the forks before */ | ||
135 | /* we start copying data into any of them. */ | ||
136 | /* Plus it's a convenient way to keep track of all the forks... */ | ||
137 | struct ForkTracker { | ||
138 | HFSUniStr255 forkName; | ||
139 | SInt64 forkSize; | ||
140 | SInt16 forkDestRefNum; | ||
141 | }; | ||
142 | typedef struct ForkTracker ForkTracker; | ||
143 | typedef ForkTracker *ForkTrackerPtr; | ||
144 | |||
145 | /* The FolderListData data structure holds FSRefs to the source and */ | ||
146 | /* coorisponding destination folder, as well as which level its on */ | ||
147 | /* for use in ProcessFolderList. */ | ||
148 | struct FolderListData | ||
149 | { | ||
150 | FSRef sourceDirRef; | ||
151 | FSRef destDirRef; | ||
152 | UInt32 level; | ||
153 | }; | ||
154 | typedef struct FolderListData FolderListData; | ||
155 | |||
156 | /* The FSCopyFolderGlobals data structure holds the information needed to */ | ||
157 | /* copy a directory */ | ||
158 | struct FSCopyFolderGlobals | ||
159 | { | ||
160 | FSRef *sourceDirRef; | ||
161 | FSRef *destDirRef; | ||
162 | |||
163 | FSCatalogInfo *catInfoList; | ||
164 | FSRef *srcRefList; | ||
165 | HFSUniStr255 *nameList; | ||
166 | |||
167 | GenLinkedList folderList; | ||
168 | GenIteratorPtr folderListIter; | ||
169 | |||
170 | CopyParams *copyParams; | ||
171 | FilterParams *filterParams; | ||
172 | Boolean containerChanged; | ||
173 | |||
174 | ItemCount maxLevels; | ||
175 | ItemCount currentLevel; | ||
176 | }; | ||
177 | typedef struct FSCopyFolderGlobals FSCopyFolderGlobals; | ||
178 | |||
179 | /* The FSDeleteObjectGlobals data structure holds information needed to */ | ||
180 | /* recursively delete a directory */ | ||
181 | struct FSDeleteObjectGlobals | ||
182 | { | ||
183 | FSCatalogInfo catalogInfo; /* FSCatalogInfo */ | ||
184 | ItemCount actualObjects; /* number of objects returned */ | ||
185 | OSErr result; /* result */ | ||
186 | }; | ||
187 | typedef struct FSDeleteObjectGlobals FSDeleteObjectGlobals; | ||
188 | |||
189 | #pragma mark ----- Local Prototypes ----- | ||
190 | |||
191 | static OSErr FSCopyObjectPreflight ( const FSRef *source, | ||
192 | const FSRef *destDir, | ||
193 | const DupeAction dupeAction, | ||
194 | FSCatalogInfo *sourceCatInfo, | ||
195 | CopyParams *copyParams, /* can be NULL */ | ||
196 | HFSUniStr255 *newObjectName, | ||
197 | FSRef *deleteMeRef, | ||
198 | Boolean *isReplacing, | ||
199 | Boolean *isDirectory ); | ||
200 | |||
201 | static OSErr FSCopyFile ( const FSRef *source, | ||
202 | const FSRef *destDir, | ||
203 | const FSCatalogInfo *sourceCatInfo, | ||
204 | const HFSUniStr255 *newFileName, | ||
205 | CopyParams *copyParams, | ||
206 | FilterParams *filterParams, | ||
207 | FSRef *newFileRef, /* can be NULL */ | ||
208 | FSSpec *newFileSpec ); /* can be NULL */ | ||
209 | |||
210 | static OSErr CopyFile ( const FSRef *source, | ||
211 | FSCatalogInfo *sourceCatInfo, | ||
212 | const FSRef *destDir, | ||
213 | const HFSUniStr255 *destName, /* can be NULL */ | ||
214 | CopyParams *copyParams, | ||
215 | FSRef *newRef, /* can be NULL */ | ||
216 | FSSpec *newSpec ); /* can be NULL */ | ||
217 | |||
218 | static OSErr FSUsePBHCopyFile ( const FSRef *srcFileRef, | ||
219 | const FSRef *dstDirectoryRef, | ||
220 | const HFSUniStr255 *destName, /* can be NULL (no rename during copy) */ | ||
221 | TextEncoding textEncodingHint, | ||
222 | FSRef *newRef, /* can be NULL */ | ||
223 | FSSpec *newSpec ); /* can be NULL */ | ||
224 | |||
225 | static OSErr DoCopyFile ( const FSRef *source, | ||
226 | FSCatalogInfo *sourceCatInfo, | ||
227 | const FSRef *destDir, | ||
228 | const HFSUniStr255 *destName, | ||
229 | CopyParams *params, | ||
230 | FSRef *newRef, /* can be NULL */ | ||
231 | FSSpec *newSpec ); /* can be NULL */ | ||
232 | |||
233 | static OSErr FSCopyFolder ( const FSRef *source, | ||
234 | const FSRef *destDir, | ||
235 | const FSCatalogInfo *sourceCatInfo, | ||
236 | const HFSUniStr255 *newFoldName, | ||
237 | CopyParams *copyParams, | ||
238 | FilterParams *filterParams, | ||
239 | ItemCount maxLevels, | ||
240 | FSRef *outDirRef, /* can be NULL */ | ||
241 | FSSpec *outDirSpec ); /* can be NULL */ | ||
242 | |||
243 | static OSErr ProcessFolderList ( FSCopyFolderGlobals *folderGlobals ); | ||
244 | |||
245 | static OSErr CopyFolder ( FSCopyFolderGlobals *folderGlobals ); | ||
246 | |||
247 | static OSErr CheckForDestInsideSrc ( const FSRef *source, | ||
248 | const FSRef *destDir ); | ||
249 | |||
250 | static OSErr CopyForks ( const FSRef *source, | ||
251 | const FSRef *dest, | ||
252 | CopyParams *params ); | ||
253 | |||
254 | static OSErr CopyForksToDisk ( const FSRef *source, | ||
255 | const FSRef *dest, | ||
256 | CopyParams *params ); | ||
257 | |||
258 | static OSErr CopyForksToDropBox ( const FSRef *source, | ||
259 | const FSRef *dest, | ||
260 | CopyParams *params ); | ||
261 | |||
262 | static OSErr OpenAllForks ( const FSRef *dest, | ||
263 | GenLinkedList *forkList ); | ||
264 | |||
265 | static OSErr WriteFork ( const SInt16 srcRefNum, | ||
266 | const SInt16 destRefNum, | ||
267 | const CopyParams *params, | ||
268 | const SInt64 forkSize ); | ||
269 | |||
270 | static UInt32 CalcBufferSizeForVol ( const GetVolParmsInfoBuffer *volParms, | ||
271 | UInt32 volParmsSize ); | ||
272 | |||
273 | static UInt32 BufferSizeForVolSpeed ( UInt32 volumeBytesPerSecond ); | ||
274 | |||
275 | static OSErr FSDeleteFolder ( const FSRef *container ); | ||
276 | |||
277 | static void FSDeleteFolderLevel ( const FSRef *container, | ||
278 | FSDeleteObjectGlobals *theGlobals ); | ||
279 | |||
280 | static OSErr IsDropBox ( const FSRef *source, | ||
281 | Boolean *isDropBox ); | ||
282 | |||
283 | static OSErr GetMagicBusyCreateDate( UTCDateTime *date ); | ||
284 | |||
285 | static OSErr FSGetVRefNum ( const FSRef *ref, | ||
286 | FSVolumeRefNum *vRefNum ); | ||
287 | |||
288 | static OSErr FSGetVolParms ( FSVolumeRefNum volRefNum, | ||
289 | UInt32 bufferSize, | ||
290 | GetVolParmsInfoBuffer*volParmsInfo, | ||
291 | UInt32 *actualInfoSize ); /* Can Be NULL */ | ||
292 | |||
293 | static OSErr UniStrToPStr ( const HFSUniStr255 *uniStr, | ||
294 | TextEncoding textEncodingHint, | ||
295 | Boolean isVolumeName, | ||
296 | Str255 pStr ); | ||
297 | |||
298 | static OSErr FSMakeFSRef ( FSVolumeRefNum volRefNum, | ||
299 | SInt32 dirID, | ||
300 | ConstStr255Param name, | ||
301 | FSRef *ref ); | ||
302 | |||
303 | static OSErr SetupDestination ( const FSRef *destDir, | ||
304 | const DupeAction dupeAction, | ||
305 | HFSUniStr255 *sourceName, | ||
306 | FSRef *deleteMeRef, | ||
307 | Boolean *isReplacing); | ||
308 | |||
309 | static OSErr GetUniqueName ( const FSRef *destDir, | ||
310 | HFSUniStr255 *sourceName ); | ||
311 | |||
312 | static OSErr GetObjectName ( const FSRef *sourceRef, | ||
313 | HFSUniStr255 *sourceName, | ||
314 | TextEncoding *sourceEncoding ); | ||
315 | |||
316 | static OSErr CreateFolder ( const FSRef *sourceRef, | ||
317 | const FSRef *destDirRef, | ||
318 | const FSCatalogInfo *catalogInfo, | ||
319 | const HFSUniStr255 *folderName, | ||
320 | CopyParams *params, | ||
321 | FSRef *newFSRefPtr, | ||
322 | FSSpec *newFSSpecPtr ); | ||
323 | |||
324 | static OSErr DoCreateFolder ( const FSRef *sourceRef, | ||
325 | const FSRef *destDirRef, | ||
326 | const FSCatalogInfo *catalogInfo, | ||
327 | const HFSUniStr255 *folderName, | ||
328 | CopyParams *params, | ||
329 | FSRef *newFSRefPtr, | ||
330 | FSSpec *newFSSpecPtr); | ||
331 | |||
332 | static pascal void MyDisposeDataProc ( void *pData ); | ||
333 | |||
334 | static pascal void MyCloseForkProc ( void *pData ); | ||
335 | |||
336 | /*****************************************************************************/ | ||
337 | /*****************************************************************************/ | ||
338 | /*****************************************************************************/ | ||
339 | |||
340 | #pragma mark ----- Copy Objects ----- | ||
341 | |||
342 | /* This routine acts as the top level of the copy engine. */ | ||
343 | OSErr FSCopyObject( const FSRef *source, | ||
344 | const FSRef *destDir, | ||
345 | ItemCount maxLevels, | ||
346 | FSCatalogInfoBitmap whichInfo, | ||
347 | DupeAction dupeAction, | ||
348 | const HFSUniStr255 *newObjectName, /* can be NULL */ | ||
349 | Boolean wantFSSpec, | ||
350 | Boolean wantName, | ||
351 | CopyObjectFilterProcPtr filterProcPtr, /* can be NULL */ | ||
352 | void *yourDataPtr, /* can be NULL */ | ||
353 | FSRef *newObjectRef, /* can be NULL */ | ||
354 | FSSpec *newObjectSpec) /* can be NULL */ | ||
355 | { | ||
356 | CopyParams copyParams; | ||
357 | FilterParams filterParams; | ||
358 | FSCatalogInfo sourceCatInfo; | ||
359 | HFSUniStr255 sourceName, | ||
360 | tmpObjectName; | ||
361 | FSRef tmpObjectRef, | ||
362 | deleteMeRef; | ||
363 | Boolean isDirectory = false, | ||
364 | isReplacing = false; | ||
365 | OSErr err = ( source != NULL && destDir != NULL ) ? noErr : paramErr; | ||
366 | |||
367 | /* Zero out these two FSRefs in case an error occurs before or */ | ||
368 | /* inside FSCopyObjectPreflight. Paranoia mainly... */ | ||
369 | BlockZero( &deleteMeRef, sizeof( FSRef ) ); | ||
370 | BlockZero( &tmpObjectRef, sizeof( FSRef ) ); | ||
371 | |||
372 | /* setup filterParams */ | ||
373 | filterParams.whichInfo = whichInfo; | ||
374 | filterParams.filterProcPtr = filterProcPtr; | ||
375 | filterParams.wantSpec = ( filterProcPtr && wantFSSpec ); /* only get this info if */ | ||
376 | filterParams.wantName = ( filterProcPtr && wantName ); /* a filterProc is provied */ | ||
377 | filterParams.yourDataPtr = yourDataPtr; | ||
378 | |||
379 | /* Get and store away the name of the source object */ | ||
380 | /* and setup the initial name of the new object */ | ||
381 | if( err == noErr ) | ||
382 | err = GetObjectName( source, &sourceName, NULL ); | ||
383 | if( err == noErr ) | ||
384 | tmpObjectName = (newObjectName != NULL) ? *newObjectName : sourceName; | ||
385 | |||
386 | if( err == noErr ) /* preflight/prep the destination and our internal variables */ | ||
387 | err = FSCopyObjectPreflight( source, destDir, dupeAction, &sourceCatInfo, ©Params, &tmpObjectName, &deleteMeRef, &isReplacing, &isDirectory ); | ||
388 | |||
389 | /* now that we have some info, lets print it */ | ||
390 | if( err == noErr ) | ||
391 | { | ||
392 | dwarning(( "%s -- err: %d, maxLevels: %u, whichInfo: %08x,\n", __FUNCTION__, err, (unsigned int)maxLevels, (int)whichInfo )); | ||
393 | dwarning(( "\t\t\t\tdupeAction: %s, wantSpec: %s, wantName: %s,\n", ((dupeAction == kDupeActionReplace) ? "replace" : ((dupeAction == kDupeActionRename) ? "rename" : "standard")), (filterParams.wantSpec)?"yes":"no", (filterParams.wantName)?"yes":"no" )); | ||
394 | dwarning(( "\t\t\t\tfilterProcPtr: 0x%08x, yourDataPtr: 0x%08x,\n", (unsigned int)filterProcPtr, (unsigned int)yourDataPtr )); | ||
395 | dwarning(( "\t\t\t\tnewObjectRef: 0x%08x, newObjectSpec: 0x%08x,\n", (unsigned int)newObjectRef, (unsigned int)newObjectSpec )); | ||
396 | dwarning(( "\t\t\t\tcopyBufferSize: %dkB, isDirectory: %s, isLocal: %s,\n", (int)copyParams.copyBufferSize/1024, (isDirectory)?"yes":"no", (copyParams.copyingToLocalVolume)?"yes":"no" )); | ||
397 | dwarning(( "\t\t\t\tisDropBox: %s, PBHCopyFileSync supported: %s\n\n", (copyParams.copyingToDropFolder)?"yes":"no", (copyParams.volHasCopyFile)?"yes":"no" )); | ||
398 | } | ||
399 | |||
400 | if( err == noErr ) /* now copy the file/folder... */ | ||
401 | { /* is it a folder? */ | ||
402 | if ( isDirectory ) | ||
403 | { /* yes */ | ||
404 | err = CheckForDestInsideSrc(source, destDir); | ||
405 | if( err == noErr ) | ||
406 | err = FSCopyFolder( source, destDir, &sourceCatInfo, &tmpObjectName, ©Params, &filterParams, maxLevels, &tmpObjectRef, newObjectSpec ); | ||
407 | } | ||
408 | else /* no */ | ||
409 | err = FSCopyFile(source, destDir, &sourceCatInfo, &tmpObjectName, ©Params, &filterParams, &tmpObjectRef, newObjectSpec); | ||
410 | } | ||
411 | |||
412 | /* if an object existed in the destination with the same name as */ | ||
413 | /* the source and the caller wants to replace it, we had renamed it */ | ||
414 | /* to ".DeleteMe" earlier. If no errors, we delete it, else delete */ | ||
415 | /* the one we just created and rename the origenal back to its */ | ||
416 | /* origenal name. */ | ||
417 | /* */ | ||
418 | /* This is done mainly to cover the case of the source being in the */ | ||
419 | /* destination directory when kDupeActionReplace is selected */ | ||
420 | /* (3188701) */ | ||
421 | if( copyParams.dupeAction == kDupeActionReplace && isReplacing == true ) | ||
422 | { | ||
423 | dwarning(("%s -- Cleaning up, this might take a moment. err : %d\n", __FUNCTION__, err)); | ||
424 | |||
425 | if( err == noErr ) | ||
426 | err = FSDeleteObjects( &deleteMeRef ); | ||
427 | else | ||
428 | { /* not much we can do if the delete or rename fails, we need to preserve */ | ||
429 | /* the origenal error code that got us here. */ | ||
430 | /* */ | ||
431 | /* If an error occurs before or inside SetupDestination, newFileRef and */ | ||
432 | /* deleteMeRef will be invalid so the delete and rename will simply fail */ | ||
433 | /* leaving the source and destination unchanged */ | ||
434 | myverify_noerr( FSDeleteObjects( &tmpObjectRef ) ); | ||
435 | myverify_noerr( FSRenameUnicode( &deleteMeRef, sourceName.length, sourceName.unicode, sourceCatInfo.textEncodingHint, NULL ) ); | ||
436 | } | ||
437 | } | ||
438 | |||
439 | if( err == noErr && newObjectRef != NULL ) | ||
440 | *newObjectRef = tmpObjectRef; | ||
441 | |||
442 | /* Clean up for space and safety... Who me? */ | ||
443 | if( copyParams.copyBuffer != NULL ) | ||
444 | DisposePtr((char*)copyParams.copyBuffer); | ||
445 | |||
446 | mycheck_noerr( err ); | ||
447 | |||
448 | return err; | ||
449 | } | ||
450 | |||
451 | /*****************************************************************************/ | ||
452 | |||
453 | /* Does a little preflighting (as the name suggests) to figure out the optimal */ | ||
454 | /* buffer size, if its a drop box, on a remote volume etc */ | ||
455 | static OSErr FSCopyObjectPreflight( const FSRef *source, | ||
456 | const FSRef *destDir, | ||
457 | const DupeAction dupeAction, | ||
458 | FSCatalogInfo *sourceCatInfo, | ||
459 | CopyParams *copyParams, | ||
460 | HFSUniStr255 *newObjectName, | ||
461 | FSRef *deleteMeRef, | ||
462 | Boolean *isReplacing, | ||
463 | Boolean *isDirectory) | ||
464 | { | ||
465 | GetVolParmsInfoBuffer srcVolParms, | ||
466 | destVolParms; | ||
467 | UInt32 srcVolParmsSize = 0, | ||
468 | destVolParmsSize = 0; | ||
469 | FSVolumeRefNum srcVRefNum = 0, | ||
470 | destVRefNum = 0; | ||
471 | OSErr err = ( source != NULL && destDir != NULL && | ||
472 | sourceCatInfo != NULL && copyParams != NULL && | ||
473 | newObjectName != NULL && deleteMeRef != NULL && | ||
474 | isDirectory != NULL ) ? noErr : paramErr; | ||
475 | |||
476 | BlockZero( copyParams, sizeof( CopyParams ) ); | ||
477 | |||
478 | copyParams->dupeAction = dupeAction; | ||
479 | |||
480 | if( err == noErr ) /* Get the info we will need later about the source object */ | ||
481 | err = FSGetCatalogInfo( source, kFSCatInfoSettableInfo, sourceCatInfo, NULL, NULL, NULL ); | ||
482 | if( err == noErr ) /* get the source's vRefNum */ | ||
483 | err = FSGetVRefNum( source, &srcVRefNum ); | ||
484 | if( err == noErr ) /* get the source's volParams */ | ||
485 | err = FSGetVolParms( srcVRefNum, sizeof(GetVolParmsInfoBuffer), &srcVolParms, &srcVolParmsSize ); | ||
486 | if( err == noErr ) /* get the destination's vRefNum */ | ||
487 | err = FSGetVRefNum( destDir, &destVRefNum ); | ||
488 | if( err == noErr ) | ||
489 | { | ||
490 | /* Calculate the optimal copy buffer size for the src vol */ | ||
491 | copyParams->copyBufferSize = CalcBufferSizeForVol( &srcVolParms, srcVolParmsSize ); | ||
492 | |||
493 | /* if src and dest on different volumes, get its vol parms */ | ||
494 | /* and calculate its optimal buffer size */ | ||
495 | /* else destVolParms = srcVolParms */ | ||
496 | if( srcVRefNum != destVRefNum ) | ||
497 | { | ||
498 | err = FSGetVolParms( destVRefNum, sizeof(GetVolParmsInfoBuffer), &destVolParms, &destVolParmsSize ); | ||
499 | if( err == noErr ) | ||
500 | { | ||
501 | ByteCount tmpBufferSize = CalcBufferSizeForVol( &destVolParms, destVolParmsSize ); | ||
502 | if( tmpBufferSize < copyParams->copyBufferSize ) | ||
503 | copyParams->copyBufferSize = tmpBufferSize; | ||
504 | } | ||
505 | } | ||
506 | else | ||
507 | destVolParms = srcVolParms; | ||
508 | } | ||
509 | if( err == noErr ) | ||
510 | err = ((copyParams->copyBuffer = NewPtr( copyParams->copyBufferSize )) != NULL ) ? noErr : MemError(); | ||
511 | |||
512 | /* figure out if source is a file or folder */ | ||
513 | /* if it is on a local volume, */ | ||
514 | /* if destination is a drop box */ | ||
515 | /* if source and dest are on same server */ | ||
516 | /* and if it supports PBHCopyFile */ | ||
517 | if( err == noErr ) /* is the destination a Drop Box */ | ||
518 | err = IsDropBox( destDir, ©Params->copyingToDropFolder ); | ||
519 | if( err == noErr ) | ||
520 | { | ||
521 | /* Is it a directory */ | ||
522 | *isDirectory = ((sourceCatInfo->nodeFlags & kFSNodeIsDirectoryMask) != 0); | ||
523 | /* destVolParms.vMServerAdr is non-zero for remote volumes */ | ||
524 | copyParams->copyingToLocalVolume = (destVolParms.vMServerAdr == 0); | ||
525 | if( !copyParams->copyingToLocalVolume ) | ||
526 | { | ||
527 | /* If the destination is on a remote volume, and source and dest are on */ | ||
528 | /* the same server, then it might support PBHCopyFileSync */ | ||
529 | /* If not, then PBHCopyFileSync won't work */ | ||
530 | |||
531 | /* figure out if the volumes support PBHCopyFileSync */ | ||
532 | copyParams->volHasCopyFile = ( err == noErr && destVolParms.vMServerAdr == srcVolParms.vMServerAdr ) ? | ||
533 | VolHasCopyFile(&srcVolParms) : false; | ||
534 | } | ||
535 | } | ||
536 | |||
537 | if( err == noErr ) | ||
538 | err = SetupDestination( destDir, copyParams->dupeAction, newObjectName, deleteMeRef, isReplacing ); | ||
539 | |||
540 | return err; | ||
541 | } | ||
542 | |||
543 | #pragma mark ----- Copy Files ----- | ||
544 | |||
545 | /*****************************************************************************/ | ||
546 | |||
547 | static OSErr FSCopyFile( const FSRef *source, | ||
548 | const FSRef *destDir, | ||
549 | const FSCatalogInfo *sourceCatInfo, | ||
550 | const HFSUniStr255 *newFileName, | ||
551 | CopyParams *copyParams, | ||
552 | FilterParams *filterParams, | ||
553 | FSRef *outFileRef, | ||
554 | FSSpec *outFileSpec ) | ||
555 | { | ||
556 | FSCatalogInfo catInfo = *sourceCatInfo; | ||
557 | FSRef newFileRef; | ||
558 | FSSpec newFileSpec; | ||
559 | OSErr err = ( source != NULL && destDir != NULL && | ||
560 | copyParams != NULL && filterParams != NULL ) ? noErr : paramErr; | ||
561 | |||
562 | /* If you would like a Pre-Copy filter (i.e to weed out objects */ | ||
563 | /* you don't want to copy) you should add it here */ | ||
564 | |||
565 | if( err == noErr ) /* copy the file over */ | ||
566 | err = CopyFile( source, &catInfo, destDir, newFileName, copyParams, &newFileRef, (filterParams->wantSpec || outFileSpec) ? &newFileSpec : NULL ); | ||
567 | |||
568 | /* Call the IterateFilterProc _after_ the new file was created even if an error occured. */ | ||
569 | /* Note: if an error occured above, the FSRef and other info might not be valid */ | ||
570 | if( filterParams->filterProcPtr != NULL ) | ||
571 | { | ||
572 | /* get the extra info the user wanted on the new file that we don't have */ | ||
573 | if( err == noErr && (filterParams->whichInfo & ~kFSCatInfoSettableInfo) != kFSCatInfoNone ) | ||
574 | err = FSGetCatalogInfo( &newFileRef, filterParams->whichInfo & ~kFSCatInfoSettableInfo, &catInfo, NULL, NULL, NULL ); | ||
575 | |||
576 | err = CallCopyObjectFilterProc( filterParams->filterProcPtr, false, 0, err, &catInfo, &newFileRef, | ||
577 | (filterParams->wantSpec) ? &newFileSpec : NULL, | ||
578 | (filterParams->wantName) ? newFileName : NULL, | ||
579 | filterParams->yourDataPtr); | ||
580 | } | ||
581 | |||
582 | if( err == noErr ) | ||
583 | { | ||
584 | if( outFileRef != NULL ) | ||
585 | *outFileRef = newFileRef; | ||
586 | if( outFileSpec != NULL ) | ||
587 | *outFileSpec = newFileSpec; | ||
588 | } | ||
589 | |||
590 | mycheck_noerr(err); | ||
591 | |||
592 | return err; | ||
593 | } | ||
594 | |||
595 | /*****************************************************************************/ | ||
596 | |||
597 | static OSErr CopyFile( const FSRef *source, | ||
598 | FSCatalogInfo *sourceCatInfo, | ||
599 | const FSRef *destDir, | ||
600 | const HFSUniStr255 *destName, /* can be NULL */ | ||
601 | CopyParams *params, | ||
602 | FSRef *newFile, /* can be NULL */ | ||
603 | FSSpec *newSpec ) /* can be NULL */ | ||
604 | { | ||
605 | OSErr err = paramErr; | ||
606 | |||
607 | /* Clear the "inited" bit so that the Finder positions the icon for us. */ | ||
608 | ((FInfo *)(sourceCatInfo->finderInfo))->fdFlags &= ~kHasBeenInited; | ||
609 | |||
610 | /* if the volumes support PBHCopyFileSync, try to use it */ | ||
611 | if( params->volHasCopyFile == true ) | ||
612 | err = FSUsePBHCopyFile( source, destDir, destName, kTextEncodingUnknown, newFile, newSpec ); | ||
613 | |||
614 | /* if PBHCopyFile didn't work or not supported, */ | ||
615 | if( err != noErr ) /* then try old school file transfer */ | ||
616 | err = DoCopyFile( source, sourceCatInfo, destDir, destName, params, newFile, newSpec ); | ||
617 | |||
618 | mycheck_noerr(err); | ||
619 | |||
620 | return err; | ||
621 | } | ||
622 | |||
623 | /*****************************************************************************/ | ||
624 | |||
625 | /* Wrapper function for PBHCopyFileSync */ | ||
626 | static OSErr FSUsePBHCopyFile( const FSRef *srcFileRef, | ||
627 | const FSRef *dstDirectoryRef, | ||
628 | const HFSUniStr255 *destName, /* can be NULL */ | ||
629 | TextEncoding textEncodingHint, | ||
630 | FSRef *newRef, /* can be NULL */ | ||
631 | FSSpec *newSpec) /* can be NULL */ | ||
632 | { | ||
633 | FSSpec srcFileSpec; | ||
634 | FSCatalogInfo catalogInfo; | ||
635 | HParamBlockRec pb; | ||
636 | Str255 hfsName; | ||
637 | OSErr err = ( srcFileRef != NULL && dstDirectoryRef != NULL ) ? noErr : paramErr; | ||
638 | |||
639 | if( err == noErr ) /* get FSSpec of source FSRef */ | ||
640 | err = FSGetCatalogInfo(srcFileRef, kFSCatInfoNone, NULL, NULL, &srcFileSpec, NULL); | ||
641 | if( err == noErr ) /* get the destination vRefNum and nodeID (nodeID is the dirID) */ | ||
642 | err = FSGetCatalogInfo(dstDirectoryRef, kFSCatInfoVolume | kFSCatInfoNodeID, &catalogInfo, NULL, NULL, NULL); | ||
643 | if( err == noErr ) /* gather all the info needed */ | ||
644 | { | ||
645 | pb.copyParam.ioVRefNum = srcFileSpec.vRefNum; | ||
646 | pb.copyParam.ioDirID = srcFileSpec.parID; | ||
647 | pb.copyParam.ioNamePtr = (StringPtr)srcFileSpec.name; | ||
648 | pb.copyParam.ioDstVRefNum = catalogInfo.volume; | ||
649 | pb.copyParam.ioNewDirID = (long)catalogInfo.nodeID; | ||
650 | pb.copyParam.ioNewName = NULL; | ||
651 | if( destName != NULL ) | ||
652 | err = UniStrToPStr( destName, textEncodingHint, false, hfsName ); | ||
653 | pb.copyParam.ioCopyName = ( destName != NULL && err == noErr ) ? hfsName : NULL; | ||
654 | } | ||
655 | if( err == noErr ) /* tell the server to copy the object */ | ||
656 | err = PBHCopyFileSync(&pb); | ||
657 | |||
658 | if( err == noErr ) | ||
659 | { | ||
660 | if( newSpec != NULL ) /* caller wants an FSSpec, so make it */ | ||
661 | myverify_noerr(FSMakeFSSpec( pb.copyParam.ioDstVRefNum, pb.copyParam.ioNewDirID, pb.copyParam.ioCopyName, newSpec)); | ||
662 | if( newRef != NULL ) /* caller wants an FSRef, so make it */ | ||
663 | myverify_noerr(FSMakeFSRef( pb.copyParam.ioDstVRefNum, pb.copyParam.ioNewDirID, pb.copyParam.ioCopyName, newRef)); | ||
664 | } | ||
665 | |||
666 | if( err != paramErr ) /* returning paramErr is ok, it means PBHCopyFileSync was not supported */ | ||
667 | mycheck_noerr(err); | ||
668 | |||
669 | return err; | ||
670 | } | ||
671 | |||
672 | /*****************************************************************************/ | ||
673 | |||
674 | /* Copies a file referenced by source to the directory referenced by */ | ||
675 | /* destDir. destName is the name the file we are going to copy to the */ | ||
676 | /* destination. sourceCatInfo is the catalog info of the file, which */ | ||
677 | /* is passed in as an optimization (we could get it by doing a */ | ||
678 | /* FSGetCatalogInfo but the caller has already done that so we might as */ | ||
679 | /* well take advantage of that). */ | ||
680 | /* */ | ||
681 | static OSErr DoCopyFile(const FSRef *source, | ||
682 | FSCatalogInfo *sourceCatInfo, | ||
683 | const FSRef *destDir, | ||
684 | const HFSUniStr255 *destName, | ||
685 | CopyParams *params, | ||
686 | FSRef *newRef, | ||
687 | FSSpec *newSpec ) | ||
688 | { | ||
689 | FSRef dest; | ||
690 | FSSpec tmpSpec; | ||
691 | FSPermissionInfo originalPermissions; | ||
692 | OSType originalFileType = 'xxxx'; | ||
693 | UInt16 originalNodeFlags = kFSCatInfoNone; | ||
694 | Boolean getSpec; | ||
695 | OSErr err = noErr; | ||
696 | |||
697 | /* If we're copying to a drop folder, we won't be able to reset this */ | ||
698 | /* information once the copy is done, so we don't mess it up in */ | ||
699 | /* the first place. We still clear the locked bit though; items dropped */ | ||
700 | /* into a drop folder always become unlocked. */ | ||
701 | if (!params->copyingToDropFolder) | ||
702 | { | ||
703 | /* Remember to clear the file's type, so the Finder doesn't */ | ||
704 | /* look at the file until we're done. */ | ||
705 | originalFileType = ((FInfo *) &sourceCatInfo->finderInfo)->fdType; | ||
706 | ((FInfo *) &sourceCatInfo->finderInfo)->fdType = kFirstMagicBusyFiletype; | ||
707 | |||
708 | /* Remember and clear the file's locked status, so that we can */ | ||
709 | /* actually write the forks we're about to create. */ | ||
710 | originalNodeFlags = sourceCatInfo->nodeFlags; | ||
711 | } | ||
712 | sourceCatInfo->nodeFlags &= ~kFSNodeLockedMask; | ||
713 | |||
714 | /* figure out if we should get the FSSpec to the new file or not */ | ||
715 | /* If the caller asked for it, or if we need it for symlinks */ | ||
716 | getSpec = ( ( newSpec != NULL ) || ( !params->copyingToDropFolder && originalFileType == 'slnk' && ((FInfo *) &sourceCatInfo->finderInfo)->fdCreator == 'rhap' ) ); | ||
717 | |||
718 | /* we need to have user level read/write/execute access to the file we are */ | ||
719 | /* going to create otherwise FSCreateFileUnicode will return */ | ||
720 | /* -5000 (afpAccessDenied), and the FSRef returned will be invalid, yet */ | ||
721 | /* the file is created (size 0k)... bug? */ | ||
722 | originalPermissions = *((FSPermissionInfo*)sourceCatInfo->permissions); | ||
723 | ((FSPermissionInfo*)sourceCatInfo->permissions)->mode |= kRWXUserAccessMask; | ||
724 | |||
725 | /* Classic only supports 9.1 and higher, so we don't have to worry */ | ||
726 | /* about 2397324 */ | ||
727 | if( err == noErr ) | ||
728 | err = FSCreateFileUnicode(destDir, destName->length, destName->unicode, kFSCatInfoSettableInfo, sourceCatInfo, &dest, ( getSpec ) ? &tmpSpec : NULL ); | ||
729 | if( err == noErr ) /* Copy the forks over to the new file */ | ||
730 | err = CopyForks(source, &dest, params); | ||
731 | |||
732 | /* Restore the original file type, creation and modification dates, */ | ||
733 | /* locked status and permissions. */ | ||
734 | /* This is one of the places where we need to handle drop */ | ||
735 | /* folders as a special case because this FSSetCatalogInfo will fail for */ | ||
736 | /* an item in a drop folder, so we don't even attempt it. */ | ||
737 | if (err == noErr && !params->copyingToDropFolder) | ||
738 | { | ||
739 | ((FInfo *) &sourceCatInfo->finderInfo)->fdType = originalFileType; | ||
740 | sourceCatInfo->nodeFlags = originalNodeFlags; | ||
741 | *((FSPermissionInfo*)sourceCatInfo->permissions) = originalPermissions; | ||
742 | |||
743 | /* 2796751, FSSetCatalogInfo returns -36 when setting the Finder Info */ | ||
744 | /* for a symlink. To workaround this, when the file is a */ | ||
745 | /* symlink (slnk/rhap) we will finish the copy in two steps. First */ | ||
746 | /* setting everything but the Finder Info on the file, then calling */ | ||
747 | /* FSpSetFInfo to set the Finder Info for the file. I would rather use */ | ||
748 | /* an FSRef function to set the Finder Info, but FSSetCatalogInfo is */ | ||
749 | /* the only one... catch-22... */ | ||
750 | /* */ | ||
751 | /* The Carbon File Manager always sets the type/creator of a symlink to */ | ||
752 | /* slnk/rhap if the file is a symlink we do the two step, if it isn't */ | ||
753 | /* we use FSSetCatalogInfo to do all the work. */ | ||
754 | if ((originalFileType == 'slnk') && (((FInfo *) &sourceCatInfo->finderInfo)->fdCreator == 'rhap')) | ||
755 | { /* Its a symlink */ | ||
756 | /* set all the info, except the Finder info */ | ||
757 | err = FSSetCatalogInfo(&dest, kFSCatInfoNodeFlags | kFSCatInfoPermissions, sourceCatInfo); | ||
758 | if ( err == noErr ) /* set the Finder Info to that file */ | ||
759 | err = FSpSetFInfo( &tmpSpec, ((FInfo *) &sourceCatInfo->finderInfo) ); | ||
760 | } | ||
761 | else /* its a regular file */ | ||
762 | err = FSSetCatalogInfo(&dest, kFSCatInfoNodeFlags | kFSCatInfoFinderInfo | kFSCatInfoPermissions, sourceCatInfo); | ||
763 | } | ||
764 | |||
765 | /* If we created the file and the copy failed, try to clean up by */ | ||
766 | /* deleting the file we created. We do this because, while it's */ | ||
767 | /* possible for the copy to fail halfway through and the File Manager */ | ||
768 | /* doesn't really clean up that well in that case, we *really* don't want */ | ||
769 | /* any half-created files being left around. */ | ||
770 | /* if the file already existed, we don't want to delete it */ | ||
771 | if( err == noErr || err == dupFNErr ) | ||
772 | { /* if everything was fine, then return the new file Spec/Ref */ | ||
773 | if( newRef != NULL ) | ||
774 | *newRef = dest; | ||
775 | if( newSpec != NULL ) | ||
776 | *newSpec = tmpSpec; | ||
777 | } | ||
778 | else | ||
779 | myverify_noerr( FSDeleteObjects(&dest) ); | ||
780 | |||
781 | mycheck_noerr(err); | ||
782 | |||
783 | return err; | ||
784 | } | ||
785 | |||
786 | /*****************************************************************************/ | ||
787 | |||
788 | #pragma mark ----- Copy Folders ----- | ||
789 | |||
790 | static OSErr FSCopyFolder( const FSRef *source, | ||
791 | const FSRef *destDir, | ||
792 | const FSCatalogInfo *sourceCatInfo, | ||
793 | const HFSUniStr255 *newObjectName, | ||
794 | CopyParams *copyParams, | ||
795 | FilterParams *filterParams, | ||
796 | ItemCount maxLevels, | ||
797 | FSRef *outDirRef, | ||
798 | FSSpec *outDirSpec ) | ||
799 | { | ||
800 | FSCopyFolderGlobals folderGlobals; | ||
801 | FolderListData *tmpListData = NULL; | ||
802 | FSCatalogInfo catInfo = *sourceCatInfo; | ||
803 | FSRef newDirRef; | ||
804 | FSSpec newDirSpec; | ||
805 | OSErr err; | ||
806 | |||
807 | /* setup folder globals */ | ||
808 | folderGlobals.catInfoList = (FSCatalogInfo*) NewPtr( sizeof( FSCatalogInfo ) * kNumObjects ); | ||
809 | folderGlobals.srcRefList = (FSRef*) NewPtr( sizeof( FSRef ) * kNumObjects ); | ||
810 | folderGlobals.nameList = (HFSUniStr255*) NewPtr( sizeof( HFSUniStr255 ) * kNumObjects ); | ||
811 | folderGlobals.folderListIter = NULL; | ||
812 | folderGlobals.copyParams = copyParams; | ||
813 | folderGlobals.filterParams = filterParams; | ||
814 | folderGlobals.maxLevels = maxLevels; | ||
815 | folderGlobals.currentLevel = 0; | ||
816 | |||
817 | /* if any of the NewPtr calls failed, we MUST bail */ | ||
818 | err = ( folderGlobals.catInfoList != NULL && | ||
819 | folderGlobals.srcRefList != NULL && | ||
820 | folderGlobals.nameList != NULL ) ? noErr : memFullErr; | ||
821 | |||
822 | /* init the linked list we will use to keep track of the folders */ | ||
823 | InitLinkedList( &folderGlobals.folderList, MyDisposeDataProc ); | ||
824 | |||
825 | if( err == noErr && !copyParams->copyingToDropFolder ) | ||
826 | err = GetMagicBusyCreateDate( &catInfo.createDate ); | ||
827 | if( err == noErr ) /* create the directory */ | ||
828 | err = DoCreateFolder( source, destDir, &catInfo, newObjectName, folderGlobals.copyParams, &newDirRef, (filterParams->wantSpec || outDirSpec ) ? &newDirSpec : NULL ); | ||
829 | |||
830 | /* Note: if an error occured above, the FSRef and other info might not be valid */ | ||
831 | if( filterParams->filterProcPtr != NULL ) | ||
832 | { | ||
833 | /* get the info the user wanted about the source directory we don't have */ | ||
834 | if( err == noErr && (filterParams->whichInfo & ~kFSCatInfoSettableInfo) != kFSCatInfoNone ) | ||
835 | err = FSGetCatalogInfo(&newDirRef, filterParams->whichInfo & ~kFSCatInfoSettableInfo, &catInfo, NULL, NULL, NULL); | ||
836 | |||
837 | err = CallCopyObjectFilterProc(filterParams->filterProcPtr, false, folderGlobals.currentLevel, | ||
838 | err, &catInfo, &newDirRef, | ||
839 | ( filterParams->wantSpec ) ? &newDirSpec : NULL, | ||
840 | ( filterParams->wantName ) ? newObjectName : NULL, | ||
841 | filterParams->yourDataPtr); | ||
842 | } | ||
843 | if( err == noErr ) /* create the memory for this folder */ | ||
844 | err = ( ( tmpListData = (FolderListData*) NewPtr( sizeof( FolderListData ) ) ) != NULL ) ? noErr : MemError(); | ||
845 | if( err == noErr ) | ||
846 | { /* setup the folder info */ | ||
847 | tmpListData->sourceDirRef = *source; | ||
848 | tmpListData->destDirRef = newDirRef; | ||
849 | tmpListData->level = folderGlobals.currentLevel; | ||
850 | /* add this folder to the list to give ProcessFolderList something to chew on */ | ||
851 | err = AddToTail( &folderGlobals.folderList, tmpListData ); | ||
852 | if( err == noErr ) /* tmpListData added successfully */ | ||
853 | err = ProcessFolderList( &folderGlobals ); | ||
854 | else /* error occured, so dispose of memory */ | ||
855 | DisposePtr( (char*) tmpListData ); | ||
856 | } | ||
857 | |||
858 | dwarning(("\n%s -- %u folders were found\n", __FUNCTION__, (unsigned int)GetNumberOfItems( &folderGlobals.folderList ) )); | ||
859 | |||
860 | /* when we're done destroy the list and free up any memory we allocated */ | ||
861 | DestroyList( &folderGlobals.folderList ); | ||
862 | |||
863 | /* now that the copy is complete, we can set things back to normal */ | ||
864 | /* for the directory we just created. */ | ||
865 | /* We have to do this only for the top directory of the copy */ | ||
866 | /* all subdirectories were created all at once */ | ||
867 | if( err == noErr && !folderGlobals.copyParams->copyingToDropFolder ) | ||
868 | err = FSSetCatalogInfo( &newDirRef, kFSCatInfoCreateDate | kFSCatInfoPermissions, sourceCatInfo ); | ||
869 | |||
870 | /* Copy went as planned, and caller wants an FSRef/FSSpec to the new directory */ | ||
871 | if( err == noErr ) | ||
872 | { | ||
873 | if( outDirRef != NULL) | ||
874 | *outDirRef = newDirRef; | ||
875 | if( outDirSpec != NULL ) | ||
876 | *outDirSpec = newDirSpec; | ||
877 | } | ||
878 | |||
879 | /* clean up for space and safety, who me? */ | ||
880 | if( folderGlobals.catInfoList ) | ||
881 | DisposePtr( (char*) folderGlobals.catInfoList ); | ||
882 | if( folderGlobals.srcRefList ) | ||
883 | DisposePtr( (char*) folderGlobals.srcRefList ); | ||
884 | if( folderGlobals.nameList ) | ||
885 | DisposePtr( (char*) folderGlobals.nameList ); | ||
886 | |||
887 | mycheck_noerr(err); | ||
888 | |||
889 | return ( err ); | ||
890 | } | ||
891 | |||
892 | /*****************************************************************************/ | ||
893 | |||
894 | /* We now store a list of all the folders/subfolders we encounter in the source */ | ||
895 | /* Each node in the list contains an FSRef to the source, an FSRef to the */ | ||
896 | /* mirror folder in the destination, and the level in the source that folder */ | ||
897 | /* is on. This is done so that we can use FSGetCatalogInfoBulk to its full */ | ||
898 | /* potential (getting items in bulk). We copy the source one folder at a time. */ | ||
899 | /* Copying over the contents of each folder before we continue on to the next */ | ||
900 | /* folder in the list. This allows us to use the File Manager's own caching */ | ||
901 | /* system to our advantage. */ | ||
902 | static OSErr ProcessFolderList( FSCopyFolderGlobals *folderGlobals ) | ||
903 | { | ||
904 | FolderListData *folderListData; | ||
905 | OSErr err = noErr; | ||
906 | |||
907 | /* iterate through the list of folders and copy over each one individually */ | ||
908 | for( InitIterator( &folderGlobals->folderList, &folderGlobals->folderListIter ); folderGlobals->folderListIter != NULL && err == noErr; Next( &folderGlobals->folderListIter ) ) | ||
909 | { | ||
910 | /* Get the data for this folder */ | ||
911 | folderListData = (FolderListData*) GetData( folderGlobals->folderListIter ); | ||
912 | if( folderListData != NULL ) | ||
913 | { | ||
914 | #if DEBUG && !TARGET_API_MAC_OS8 | ||
915 | { | ||
916 | char path[1024]; | ||
917 | myverify_noerr(FSRefMakePath( &(folderListData->sourceDirRef), (unsigned char*)path, 1024 )); | ||
918 | dwarning(("\n\n%s -- Copying contents of\n\t%s\n", __FUNCTION__, path)); | ||
919 | myverify_noerr(FSRefMakePath( &(folderListData->destDirRef), (unsigned char*)path, 1024 )); | ||
920 | dwarning(("\t\tto\n\t%s\n", path)); | ||
921 | } | ||
922 | #endif | ||
923 | |||
924 | /* stuff the data into our globals */ | ||
925 | folderGlobals->sourceDirRef = &(folderListData->sourceDirRef); | ||
926 | folderGlobals->destDirRef = &(folderListData->destDirRef); | ||
927 | folderGlobals->currentLevel = folderListData->level; | ||
928 | |||
929 | /* Copy over this folder and add any subfolders to our list of folders */ | ||
930 | /* so they will get processed later */ | ||
931 | err = CopyFolder( folderGlobals ); | ||
932 | } | ||
933 | } | ||
934 | |||
935 | return err; | ||
936 | } | ||
937 | |||
938 | /*****************************************************************************/ | ||
939 | |||
940 | /* Copy the contents of the source into the destination. If any subfolders */ | ||
941 | /* are found, add them to a local list of folders during the loop stage */ | ||
942 | /* Once the copy is done, insert the local list into the global list right */ | ||
943 | /* after the current position in the list. This is done so we don't jump */ | ||
944 | /* all over the disk getting the different folders to copy */ | ||
945 | static OSErr CopyFolder( FSCopyFolderGlobals *folderGlobals ) | ||
946 | { | ||
947 | GenLinkedList tmpList; | ||
948 | FolderListData *tmpListData = NULL; | ||
949 | FilterParams *filterPtr = folderGlobals->filterParams; | ||
950 | FSIterator iterator; | ||
951 | FSRef newRef; | ||
952 | FSSpec newSpec; | ||
953 | UInt32 actualObjects; | ||
954 | OSErr err, | ||
955 | junkErr; | ||
956 | int i; | ||
957 | |||
958 | /* Init the local list */ | ||
959 | InitLinkedList( &tmpList, MyDisposeDataProc); | ||
960 | |||
961 | err = FSOpenIterator( folderGlobals->sourceDirRef, kFSIterateFlat, &iterator ); | ||
962 | if( err == noErr ) | ||
963 | { | ||
964 | do | ||
965 | { | ||
966 | /* grab a bunch of objects (kNumObjects) from this folder and copy them over */ | ||
967 | err = FSGetCatalogInfoBulk( iterator, kNumObjects, &actualObjects, &filterPtr->containerChanged, | ||
968 | kFSCatInfoSettableInfo, folderGlobals->catInfoList, folderGlobals->srcRefList, | ||
969 | NULL, folderGlobals->nameList ); | ||
970 | if( ( err == noErr || err == errFSNoMoreItems ) && | ||
971 | ( actualObjects != 0 ) ) | ||
972 | { | ||
973 | dwarning(("%s -- actualObjects retrieved from FSGetCatalogInfoBulk: %u\n",__FUNCTION__, (unsigned int)actualObjects )); | ||
974 | |||
975 | /* iterate over the objects actually returned */ | ||
976 | for( i = 0; i < actualObjects; i++ ) | ||
977 | { | ||
978 | /* Any errors in here will be passed to the filter proc */ | ||
979 | /* we don't want an error in here to prematurely cancel the copy */ | ||
980 | |||
981 | /* If you would like a Pre-Copy filter (i.e to weed out objects */ | ||
982 | /* you don't want to copy) you should add it here */ | ||
983 | |||
984 | /* Is the new object a directory? */ | ||
985 | if( ( folderGlobals->catInfoList[i].nodeFlags & kFSNodeIsDirectoryMask ) != 0 ) | ||
986 | { /* yes */ | ||
987 | junkErr = CreateFolder( &folderGlobals->srcRefList[i], folderGlobals->destDirRef, | ||
988 | &folderGlobals->catInfoList[i], &folderGlobals->nameList[i], | ||
989 | folderGlobals->copyParams, &newRef, (filterPtr->wantSpec) ? &newSpec : NULL ); | ||
990 | /* If maxLevels is zero, we aren't checking levels */ | ||
991 | /* If currentLevel+1 < maxLevels, add this folder to the list */ | ||
992 | if( folderGlobals->maxLevels == 0 || (folderGlobals->currentLevel + 1) < folderGlobals->maxLevels ) | ||
993 | { | ||
994 | if( junkErr == noErr ) /* Create memory for folder list data */ | ||
995 | junkErr = ( ( tmpListData = (FolderListData*) NewPtr( sizeof( FolderListData ) ) ) != NULL ) ? noErr : MemError(); | ||
996 | if( junkErr == noErr ) | ||
997 | { /* Setup the folder list data */ | ||
998 | tmpListData->sourceDirRef = folderGlobals->srcRefList[i]; | ||
999 | tmpListData->destDirRef = newRef; | ||
1000 | tmpListData->level = folderGlobals->currentLevel + 1; | ||
1001 | |||
1002 | /* Add it to the local list */ | ||
1003 | junkErr = AddToTail( &tmpList, tmpListData ); | ||
1004 | } | ||
1005 | /* If an error occured and memory was created, we need to dispose of it */ | ||
1006 | /* since it was not added to the list */ | ||
1007 | if( junkErr != noErr && tmpListData != NULL ) | ||
1008 | DisposePtr( (char*) tmpListData ); | ||
1009 | } | ||
1010 | } | ||
1011 | else | ||
1012 | { /* no */ | ||
1013 | junkErr = CopyFile( &folderGlobals->srcRefList[i], &folderGlobals->catInfoList[i], | ||
1014 | folderGlobals->destDirRef, &folderGlobals->nameList[i], | ||
1015 | folderGlobals->copyParams, &newRef, ( filterPtr->wantSpec ) ? &newSpec : NULL ); | ||
1016 | } | ||
1017 | |||
1018 | /* Note: if an error occured above, the FSRef and other info might not be valid */ | ||
1019 | if( filterPtr->filterProcPtr != NULL ) | ||
1020 | { | ||
1021 | if( junkErr == noErr && (filterPtr->whichInfo & ~kFSCatInfoSettableInfo) != kFSCatInfoNone ) /* get the extra info about the new object that the user wanted that we don't already have */ | ||
1022 | junkErr = FSGetCatalogInfo( &newRef, filterPtr->whichInfo & ~kFSCatInfoSettableInfo, &folderGlobals->catInfoList[i], NULL, NULL, NULL ); | ||
1023 | |||
1024 | err = CallCopyObjectFilterProc( filterPtr->filterProcPtr, filterPtr->containerChanged, | ||
1025 | folderGlobals->currentLevel, junkErr, | ||
1026 | &folderGlobals->catInfoList[i], &newRef, | ||
1027 | ( filterPtr->wantSpec ) ? &newSpec : NULL, | ||
1028 | ( filterPtr->wantName ) ? &folderGlobals->nameList[i] : NULL, | ||
1029 | filterPtr->yourDataPtr); | ||
1030 | } | ||
1031 | } | ||
1032 | } | ||
1033 | }while( err == noErr ); | ||
1034 | |||
1035 | /* errFSNoMoreItems is OK - it only means we hit the end of this level */ | ||
1036 | /* afpAccessDenied is OK too - it only means we cannot see inside the directory */ | ||
1037 | if( err == errFSNoMoreItems || err == afpAccessDenied ) | ||
1038 | err = noErr; | ||
1039 | |||
1040 | /* Insert the local list of folders from the current folder into our global list. Even */ | ||
1041 | /* if no items were added to the local list (due to error, or empty folder), InsertList */ | ||
1042 | /* handles it correctly. We add the local list even if an error occurred. It will get */ | ||
1043 | /* disposed of when the global list is destroyed. Doesn't hurt to have a couple extra */ | ||
1044 | /* steps when we're going to bail anyways. */ | ||
1045 | InsertList( &folderGlobals->folderList, &tmpList, folderGlobals->folderListIter ); | ||
1046 | |||
1047 | /* Close the FSIterator (closing an open iterator should never fail) */ | ||
1048 | (void) FSCloseIterator(iterator); | ||
1049 | } | ||
1050 | |||
1051 | mycheck_noerr( err ); | ||
1052 | |||
1053 | return err; | ||
1054 | } | ||
1055 | |||
1056 | /*****************************************************************************/ | ||
1057 | |||
1058 | /* Determines whether the destination directory is equal to the source */ | ||
1059 | /* item, or whether it's nested inside the source item. Returns a */ | ||
1060 | /* errFSDestInsideSource if that's the case. We do this to prevent */ | ||
1061 | /* endless recursion while copying. */ | ||
1062 | /* */ | ||
1063 | static OSErr CheckForDestInsideSrc( const FSRef *source, | ||
1064 | const FSRef *destDir) | ||
1065 | { | ||
1066 | FSRef thisDir = *destDir; | ||
1067 | FSCatalogInfo thisDirInfo; | ||
1068 | Boolean done = false; | ||
1069 | OSErr err; | ||
1070 | |||
1071 | do | ||
1072 | { | ||
1073 | err = FSCompareFSRefs(source, &thisDir); | ||
1074 | if (err == noErr) | ||
1075 | err = errFSDestInsideSource; | ||
1076 | else if (err == diffVolErr) | ||
1077 | { | ||
1078 | err = noErr; | ||
1079 | done = true; | ||
1080 | } | ||
1081 | else if (err == errFSRefsDifferent) | ||
1082 | { | ||
1083 | /* This is somewhat tricky. We can ask for the parent of thisDir */ | ||
1084 | /* by setting the parentRef parameter to FSGetCatalogInfo but, if */ | ||
1085 | /* thisDir is the volume's FSRef, this will give us back junk. */ | ||
1086 | /* So we also ask for the parent's dir ID to be returned in the */ | ||
1087 | /* FSCatalogInfo record, and then check that against the node */ | ||
1088 | /* ID of the root's parent (ie 1). If we match that, we've made */ | ||
1089 | /* it to the top of the hierarchy without hitting source, so */ | ||
1090 | /* we leave with no error. */ | ||
1091 | |||
1092 | err = FSGetCatalogInfo(&thisDir, kFSCatInfoParentDirID, &thisDirInfo, NULL, NULL, &thisDir); | ||
1093 | if( ( err == noErr ) && ( thisDirInfo.parentDirID == fsRtParID ) ) | ||
1094 | done = true; | ||
1095 | } | ||
1096 | } while ( err == noErr && ! done ); | ||
1097 | |||
1098 | mycheck_noerr( err ); | ||
1099 | |||
1100 | return err; | ||
1101 | } | ||
1102 | |||
1103 | /*****************************************************************************/ | ||
1104 | |||
1105 | #pragma mark ----- Copy Forks ----- | ||
1106 | |||
1107 | /* This is where the majority of the work is done. I special cased */ | ||
1108 | /* DropBoxes in order to use FSIterateForks to its full potential for */ | ||
1109 | /* the more common case (read/write permissions). It also simplifies */ | ||
1110 | /* the code to have it seperate. */ | ||
1111 | static OSErr CopyForks( const FSRef *source, | ||
1112 | const FSRef *dest, | ||
1113 | CopyParams *params) | ||
1114 | { | ||
1115 | OSErr err; | ||
1116 | |||
1117 | err = ( !params->copyingToDropFolder ) ? CopyForksToDisk ( source, dest, params ) : | ||
1118 | CopyForksToDropBox ( source, dest, params ); | ||
1119 | |||
1120 | mycheck_noerr( err ); | ||
1121 | |||
1122 | return err; | ||
1123 | } | ||
1124 | |||
1125 | /* Open each fork individually and copy them over to the destination */ | ||
1126 | static OSErr CopyForksToDisk( const FSRef *source, | ||
1127 | const FSRef *dest, | ||
1128 | CopyParams *params ) | ||
1129 | { | ||
1130 | HFSUniStr255 forkName; | ||
1131 | CatPositionRec iterator; | ||
1132 | SInt64 forkSize; | ||
1133 | SInt16 srcRefNum, | ||
1134 | destRefNum; | ||
1135 | OSErr err; | ||
1136 | |||
1137 | /* need to initialize the iterator before using it */ | ||
1138 | iterator.initialize = 0; | ||
1139 | |||
1140 | do | ||
1141 | { | ||
1142 | err = FSIterateForks( source, &iterator, &forkName, &forkSize, NULL ); | ||
1143 | |||
1144 | /* Create the fork. Note: Data and Resource forks are automatically */ | ||
1145 | /* created when the file is created. FSCreateFork returns noErr for them */ | ||
1146 | /* We also want to create the fork even if there is no data to preserve */ | ||
1147 | /* empty forks */ | ||
1148 | if( err == noErr ) | ||
1149 | err = FSCreateFork( dest, forkName.length, forkName.unicode ); | ||
1150 | |||
1151 | /* Mac OS 9.0 has a bug (in the AppleShare external file system, */ | ||
1152 | /* I think) [2410374] that causes FSCreateFork to return an errFSForkExists */ | ||
1153 | /* error even though the fork is empty. The following code swallows */ | ||
1154 | /* the error (which is harmless) in that case. */ | ||
1155 | if( err == errFSForkExists && !params->copyingToLocalVolume ) | ||
1156 | err = noErr; | ||
1157 | |||
1158 | /* The remainder of this code only applies if there is actual data */ | ||
1159 | /* in the source fork. */ | ||
1160 | |||
1161 | if( err == noErr && forkSize > 0 ) | ||
1162 | { | ||
1163 | destRefNum = srcRefNum = 0; | ||
1164 | |||
1165 | /* Open the destination fork */ | ||
1166 | err = FSOpenFork(dest, forkName.length, forkName.unicode, fsWrPerm, &destRefNum); | ||
1167 | if( err == noErr ) /* Open the source fork */ | ||
1168 | err = FSOpenFork(source, forkName.length, forkName.unicode, fsRdPerm, &srcRefNum); | ||
1169 | if( err == noErr ) /* Write the fork to disk */ | ||
1170 | err = WriteFork( srcRefNum, destRefNum, params, forkSize ); | ||
1171 | |||
1172 | if( destRefNum != 0 ) /* Close the destination fork */ | ||
1173 | myverify_noerr( FSCloseFork( destRefNum ) ); | ||
1174 | if( srcRefNum != 0 ) /* Close the source fork */ | ||
1175 | myverify_noerr( FSCloseFork( srcRefNum ) ); | ||
1176 | } | ||
1177 | } | ||
1178 | while( err == noErr ); | ||
1179 | |||
1180 | if( err == errFSNoMoreItems ) | ||
1181 | err = noErr; | ||
1182 | |||
1183 | mycheck_noerr( err ); | ||
1184 | |||
1185 | return err; | ||
1186 | } | ||
1187 | |||
1188 | /* If we're copying to a DropBox, we have to handle the copy process a little */ | ||
1189 | /* differently then when we are copying to a regular folder. */ | ||
1190 | static OSErr CopyForksToDropBox( const FSRef *source, | ||
1191 | const FSRef *dest, | ||
1192 | CopyParams *params ) | ||
1193 | { | ||
1194 | GenLinkedList forkList; | ||
1195 | GenIteratorPtr pIter; | ||
1196 | ForkTrackerPtr forkPtr; | ||
1197 | SInt16 srcRefNum; | ||
1198 | OSErr err; | ||
1199 | |||
1200 | InitLinkedList( &forkList, MyCloseForkProc ); | ||
1201 | |||
1202 | /* If we're copying into a drop folder, open up all of those forks. */ | ||
1203 | /* We have to do this because once we've started writing to a fork */ | ||
1204 | /* in a drop folder, we can't open any more forks. */ | ||
1205 | err = OpenAllForks( dest, &forkList ); | ||
1206 | |||
1207 | /* Copy each fork over to the destination */ | ||
1208 | for( InitIterator( &forkList, &pIter ); pIter != NULL && err == noErr; Next( &pIter ) ) | ||
1209 | { | ||
1210 | srcRefNum = 0; | ||
1211 | forkPtr = GetData( pIter ); | ||
1212 | /* Open the source fork */ | ||
1213 | err = FSOpenFork(source, forkPtr->forkName.length, forkPtr->forkName.unicode, fsRdPerm, &srcRefNum); | ||
1214 | if( err == noErr ) /* Write the data over */ | ||
1215 | err = WriteFork( srcRefNum, forkPtr->forkDestRefNum, params, forkPtr->forkSize ); | ||
1216 | |||
1217 | if( srcRefNum != 0 ) /* Close the source fork */ | ||
1218 | myverify_noerr( FSCloseFork( srcRefNum ) ); | ||
1219 | } | ||
1220 | /* we're done, so destroy the list even if an error occured */ | ||
1221 | /* the DisposeDataProc will close any open forks */ | ||
1222 | DestroyList( &forkList ); | ||
1223 | |||
1224 | mycheck_noerr( err ); | ||
1225 | |||
1226 | return err; | ||
1227 | } | ||
1228 | |||
1229 | /*****************************************************************************/ | ||
1230 | |||
1231 | /* Create and open all the forks in the destination file. We need to do this when */ | ||
1232 | /* we're copying into a drop folder, where you must open all the forks before starting */ | ||
1233 | /* to write to any of them. */ | ||
1234 | /* */ | ||
1235 | /* IMPORTANT: If it fails, this routine won't close forks that opened successfully. */ | ||
1236 | /* Make sure that the DisposeDataProc for the forkList closed any open forks */ | ||
1237 | /* Or you close each one manually before destroying the list */ | ||
1238 | static OSErr OpenAllForks( const FSRef *dest, | ||
1239 | GenLinkedList *forkList ) | ||
1240 | { | ||
1241 | ForkTrackerPtr forkPtr; | ||
1242 | HFSUniStr255 forkName; | ||
1243 | CatPositionRec iterator; | ||
1244 | SInt64 forkSize; | ||
1245 | OSErr err = ( dest != NULL && forkList != NULL ) ? noErr : paramErr; | ||
1246 | |||
1247 | /* need to initialize the iterator before using it */ | ||
1248 | iterator.initialize = 0; | ||
1249 | |||
1250 | /* Iterate over the list of forks */ | ||
1251 | while( err == noErr ) | ||
1252 | { | ||
1253 | forkPtr = NULL; /* init forkPtr */ | ||
1254 | |||
1255 | err = FSIterateForks( dest, &iterator, &forkName, &forkSize, NULL ); | ||
1256 | if( err == noErr ) | ||
1257 | err = ( forkPtr = (ForkTrackerPtr) NewPtr( sizeof( ForkTracker ) ) ) != NULL ? noErr : MemError(); | ||
1258 | if( err == noErr ) | ||
1259 | { | ||
1260 | forkPtr->forkName = forkName; | ||
1261 | forkPtr->forkSize = forkSize; | ||
1262 | forkPtr->forkDestRefNum = 0; | ||
1263 | |||
1264 | /* Create the fork. Note: Data and Resource forks are automatically */ | ||
1265 | /* created when the file is created. FSCreateFork returns noErr for them */ | ||
1266 | /* We also want to create the fork even if there is no data to preserve */ | ||
1267 | /* empty forks */ | ||
1268 | err = FSCreateFork( dest, forkName.length, forkName.unicode ); | ||
1269 | |||
1270 | /* Swallow afpAccessDenied because this operation causes the external file */ | ||
1271 | /* system compatibility shim in Mac OS 9 to generate a GetCatInfo request */ | ||
1272 | /* to the AppleShare external file system, which in turn causes an AFP */ | ||
1273 | /* GetFileDirParms request on the wire, which the AFP server bounces with */ | ||
1274 | /* afpAccessDenied because the file is in a drop folder. As there's no */ | ||
1275 | /* native support for non-classic forks in current AFP, there's no way I */ | ||
1276 | /* can decide how I should handle this in a non-test case. So I just */ | ||
1277 | /* swallow the error and hope that when native AFP support arrives, the */ | ||
1278 | /* right thing will happen. */ | ||
1279 | if( err == afpAccessDenied ) | ||
1280 | err = noErr; | ||
1281 | |||
1282 | /* only open the fork if the fork has some data */ | ||
1283 | if( err == noErr && forkPtr->forkSize > 0 ) | ||
1284 | err = FSOpenFork( dest, forkPtr->forkName.length, forkPtr->forkName.unicode, fsWrPerm, &forkPtr->forkDestRefNum ); | ||
1285 | |||
1286 | /* if everything is ok, add this fork to the list */ | ||
1287 | if( err == noErr ) | ||
1288 | err = AddToTail( forkList, forkPtr ); | ||
1289 | } | ||
1290 | |||
1291 | if( err != noErr && forkPtr != NULL ) | ||
1292 | DisposePtr( (char*) forkPtr ); | ||
1293 | } | ||
1294 | |||
1295 | if( err == errFSNoMoreItems ) | ||
1296 | err = noErr; | ||
1297 | |||
1298 | mycheck_noerr( err ); | ||
1299 | |||
1300 | return err; | ||
1301 | } | ||
1302 | |||
1303 | /*****************************************************************************/ | ||
1304 | |||
1305 | /* Writes the fork from the source, references by srcRefNum, to the destination fork */ | ||
1306 | /* references by destRefNum */ | ||
1307 | static OSErr WriteFork( const SInt16 srcRefNum, | ||
1308 | const SInt16 destRefNum, | ||
1309 | const CopyParams *params, | ||
1310 | const SInt64 forkSize ) | ||
1311 | { | ||
1312 | UInt64 bytesRemaining; | ||
1313 | UInt64 bytesToReadThisTime; | ||
1314 | UInt64 bytesToWriteThisTime; | ||
1315 | OSErr err; | ||
1316 | |||
1317 | |||
1318 | /* Here we create space for the entire fork on the destination volume. */ | ||
1319 | /* FSAllocateFork has the right semantics on both traditional Mac OS */ | ||
1320 | /* and Mac OS X. On traditional Mac OS it will allocate space for the */ | ||
1321 | /* file in one hit without any other special action. On Mac OS X, */ | ||
1322 | /* FSAllocateFork is preferable to FSSetForkSize because it prevents */ | ||
1323 | /* the system from zero filling the bytes that were added to the end */ | ||
1324 | /* of the fork (which would be waste because we're about to write over */ | ||
1325 | /* those bytes anyway. */ | ||
1326 | err = FSAllocateFork(destRefNum, kFSAllocNoRoundUpMask, fsFromStart, 0, forkSize, NULL); | ||
1327 | |||
1328 | /* Copy the file from the source to the destination in chunks of */ | ||
1329 | /* no more than params->copyBufferSize bytes. This is fairly */ | ||
1330 | /* boring code except for the bytesToReadThisTime/bytesToWriteThisTime */ | ||
1331 | /* distinction. On the last chunk, we round bytesToWriteThisTime */ | ||
1332 | /* up to the next 512 byte boundary and then, after we exit the loop, */ | ||
1333 | /* we set the file's EOF back to the real location (if the fork size */ | ||
1334 | /* is not a multiple of 512 bytes). */ | ||
1335 | /* */ | ||
1336 | /* This technique works around a 'bug' in the traditional Mac OS File Manager, */ | ||
1337 | /* where the File Manager will put the last 512-byte block of a large write into */ | ||
1338 | /* the cache (even if we specifically request no caching) if that block is not */ | ||
1339 | /* full. If the block goes into the cache it will eventually have to be */ | ||
1340 | /* flushed, which causes sub-optimal disk performance. */ | ||
1341 | /* */ | ||
1342 | /* This is only done if the destination volume is local. For a network */ | ||
1343 | /* volume, it's better to just write the last bytes directly. */ | ||
1344 | /* */ | ||
1345 | /* This is extreme over-optimization given the other limits of this */ | ||
1346 | /* sample, but I will hopefully get to the other limits eventually. */ | ||
1347 | bytesRemaining = forkSize; | ||
1348 | while( err == noErr && bytesRemaining != 0 ) | ||
1349 | { | ||
1350 | if( bytesRemaining > params->copyBufferSize ) | ||
1351 | { | ||
1352 | bytesToReadThisTime = params->copyBufferSize; | ||
1353 | bytesToWriteThisTime = bytesToReadThisTime; | ||
1354 | } | ||
1355 | else | ||
1356 | { | ||
1357 | bytesToReadThisTime = bytesRemaining; | ||
1358 | bytesToWriteThisTime = ( params->copyingToLocalVolume ) ? | ||
1359 | ( (bytesRemaining + 0x01FF ) & ~0x01FF ) : bytesRemaining; | ||
1360 | } | ||
1361 | |||
1362 | err = FSReadFork( srcRefNum, fsAtMark + noCacheMask, 0, bytesToReadThisTime, params->copyBuffer, NULL ); | ||
1363 | if( err == noErr ) | ||
1364 | err = FSWriteFork( destRefNum, fsAtMark + noCacheMask, 0, bytesToWriteThisTime, params->copyBuffer, NULL ); | ||
1365 | if( err == noErr ) | ||
1366 | bytesRemaining -= bytesToReadThisTime; | ||
1367 | } | ||
1368 | |||
1369 | if (err == noErr && params->copyingToLocalVolume && ( forkSize & 0x01FF ) != 0 ) | ||
1370 | err = FSSetForkSize( destRefNum, fsFromStart, forkSize ); | ||
1371 | |||
1372 | return err; | ||
1373 | } | ||
1374 | |||
1375 | /*****************************************************************************/ | ||
1376 | |||
1377 | #pragma mark ----- Calculate Buffer Size ----- | ||
1378 | |||
1379 | /* This routine calculates the appropriate buffer size for */ | ||
1380 | /* the given volParms. It's a simple composition of FSGetVolParms */ | ||
1381 | /* BufferSizeForVolSpeed. */ | ||
1382 | static UInt32 CalcBufferSizeForVol(const GetVolParmsInfoBuffer *volParms, UInt32 volParmsSize) | ||
1383 | { | ||
1384 | UInt32 volumeBytesPerSecond = 0; | ||
1385 | |||
1386 | /* Version 1 of the GetVolParmsInfoBuffer included the vMAttrib */ | ||
1387 | /* field, so we don't really need to test actualSize. A noErr */ | ||
1388 | /* result indicates that we have the info we need. This is */ | ||
1389 | /* just a paranoia check. */ | ||
1390 | |||
1391 | mycheck(volParmsSize >= offsetof(GetVolParmsInfoBuffer, vMVolumeGrade)); | ||
1392 | |||
1393 | /* On the other hand, vMVolumeGrade was not introduced until */ | ||
1394 | /* version 2 of the GetVolParmsInfoBuffer, so we have to explicitly */ | ||
1395 | /* test whether we got a useful value. */ | ||
1396 | |||
1397 | if( ( volParmsSize >= offsetof(GetVolParmsInfoBuffer, vMForeignPrivID) ) && | ||
1398 | ( volParms->vMVolumeGrade <= 0 ) ) | ||
1399 | { | ||
1400 | volumeBytesPerSecond = -volParms->vMVolumeGrade; | ||
1401 | } | ||
1402 | |||
1403 | return BufferSizeForVolSpeed(volumeBytesPerSecond); | ||
1404 | } | ||
1405 | |||
1406 | /*****************************************************************************/ | ||
1407 | |||
1408 | /* Calculate an appropriate copy buffer size based on the volumes */ | ||
1409 | /* rated speed. Our target is to use a buffer that takes 0.25 */ | ||
1410 | /* seconds to fill. This is necessary because the volume might be */ | ||
1411 | /* mounted over a very slow link (like ARA), and if we do a 256 KB */ | ||
1412 | /* read over an ARA link we'll block the File Manager queue for */ | ||
1413 | /* so long that other clients (who might have innocently just */ | ||
1414 | /* called PBGetCatInfoSync) will block for a noticeable amount of time. */ | ||
1415 | /* */ | ||
1416 | /* Note that volumeBytesPerSecond might be 0, in which case we assume */ | ||
1417 | /* some default value. */ | ||
1418 | static UInt32 BufferSizeForVolSpeed(UInt32 volumeBytesPerSecond) | ||
1419 | { | ||
1420 | ByteCount bufferSize; | ||
1421 | |||
1422 | if (volumeBytesPerSecond == 0) | ||
1423 | bufferSize = kDefaultCopyBufferSize; | ||
1424 | else | ||
1425 | { /* We want to issue a single read that takes 0.25 of a second, */ | ||
1426 | /* so devide the bytes per second by 4. */ | ||
1427 | bufferSize = volumeBytesPerSecond / 4; | ||
1428 | } | ||
1429 | |||
1430 | /* Round bufferSize down to 512 byte boundary. */ | ||
1431 | bufferSize &= ~0x01FF; | ||
1432 | |||
1433 | /* Clip to sensible limits. */ | ||
1434 | if (bufferSize < kMinimumCopyBufferSize) | ||
1435 | bufferSize = kMinimumCopyBufferSize; | ||
1436 | else if (bufferSize > kMaximumCopyBufferSize) | ||
1437 | bufferSize = kMaximumCopyBufferSize; | ||
1438 | |||
1439 | return bufferSize; | ||
1440 | } | ||
1441 | |||
1442 | /*****************************************************************************/ | ||
1443 | |||
1444 | #pragma mark ----- Delete Objects ----- | ||
1445 | |||
1446 | OSErr FSDeleteObjects( const FSRef *source ) | ||
1447 | { | ||
1448 | FSCatalogInfo catalogInfo; | ||
1449 | OSErr err = ( source != NULL ) ? noErr : paramErr; | ||
1450 | |||
1451 | #if DEBUG && !TARGET_API_MAC_OS8 | ||
1452 | if( err == noErr ) | ||
1453 | { | ||
1454 | char path[1024]; | ||
1455 | myverify_noerr(FSRefMakePath( source, (unsigned char*)path, 1024 )); | ||
1456 | dwarning(("\n%s -- Deleting %s\n", __FUNCTION__, path)); | ||
1457 | } | ||
1458 | #endif | ||
1459 | |||
1460 | /* get nodeFlags for container */ | ||
1461 | if( err == noErr ) | ||
1462 | err = FSGetCatalogInfo(source, kFSCatInfoNodeFlags, &catalogInfo, NULL, NULL,NULL); | ||
1463 | if( err == noErr && (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0 ) | ||
1464 | { /* its a directory, so delete its contents before we delete it */ | ||
1465 | err = FSDeleteFolder(source); | ||
1466 | } | ||
1467 | if( err == noErr && (catalogInfo.nodeFlags & kFSNodeLockedMask) != 0 ) /* is object locked? */ | ||
1468 | { /* then attempt to unlock the object (ignore err since FSDeleteObject will set it correctly) */ | ||
1469 | catalogInfo.nodeFlags &= ~kFSNodeLockedMask; | ||
1470 | (void) FSSetCatalogInfo(source, kFSCatInfoNodeFlags, &catalogInfo); | ||
1471 | } | ||
1472 | if( err == noErr ) /* delete the object (if it was a directory it is now empty, so we can delete it) */ | ||
1473 | err = FSDeleteObject(source); | ||
1474 | |||
1475 | mycheck_noerr( err ); | ||
1476 | |||
1477 | return ( err ); | ||
1478 | } | ||
1479 | |||
1480 | /*****************************************************************************/ | ||
1481 | |||
1482 | #pragma mark ----- Delete Folders ----- | ||
1483 | |||
1484 | static OSErr FSDeleteFolder( const FSRef *container ) | ||
1485 | { | ||
1486 | FSDeleteObjectGlobals theGlobals; | ||
1487 | |||
1488 | theGlobals.result = ( container != NULL ) ? noErr : paramErr; | ||
1489 | |||
1490 | /* delete container's contents */ | ||
1491 | if( theGlobals.result == noErr ) | ||
1492 | FSDeleteFolderLevel(container, &theGlobals); | ||
1493 | |||
1494 | mycheck_noerr( theGlobals.result ); | ||
1495 | |||
1496 | return ( theGlobals.result ); | ||
1497 | } | ||
1498 | |||
1499 | /*****************************************************************************/ | ||
1500 | |||
1501 | static void FSDeleteFolderLevel(const FSRef *container, | ||
1502 | FSDeleteObjectGlobals *theGlobals ) | ||
1503 | { | ||
1504 | FSIterator iterator; | ||
1505 | FSRef itemToDelete; | ||
1506 | UInt16 nodeFlags; | ||
1507 | |||
1508 | /* Open FSIterator for flat access and give delete optimization hint */ | ||
1509 | theGlobals->result = FSOpenIterator(container, kFSIterateFlat + kFSIterateDelete, &iterator); | ||
1510 | if ( theGlobals->result == noErr ) | ||
1511 | { | ||
1512 | do /* delete the contents of the directory */ | ||
1513 | { | ||
1514 | /* get 1 item to delete */ | ||
1515 | theGlobals->result = FSGetCatalogInfoBulk( iterator, 1, &theGlobals->actualObjects, | ||
1516 | NULL, kFSCatInfoNodeFlags, &theGlobals->catalogInfo, | ||
1517 | &itemToDelete, NULL, NULL); | ||
1518 | if ( (theGlobals->result == noErr) && (theGlobals->actualObjects == 1) ) | ||
1519 | { | ||
1520 | /* save node flags in local in case we have to recurse */ | ||
1521 | nodeFlags = theGlobals->catalogInfo.nodeFlags; | ||
1522 | |||
1523 | /* is it a directory? */ | ||
1524 | if ( (nodeFlags & kFSNodeIsDirectoryMask) != 0 ) | ||
1525 | { /* yes -- delete its contents before attempting to delete it */ | ||
1526 | FSDeleteFolderLevel(&itemToDelete, theGlobals); | ||
1527 | } | ||
1528 | if ( theGlobals->result == noErr) /* are we still OK to delete? */ | ||
1529 | { | ||
1530 | if ( (nodeFlags & kFSNodeLockedMask) != 0 ) /* is item locked? */ | ||
1531 | { /* then attempt to unlock it (ignore result since FSDeleteObject will set it correctly) */ | ||
1532 | theGlobals->catalogInfo.nodeFlags = nodeFlags & ~kFSNodeLockedMask; | ||
1533 | (void) FSSetCatalogInfo(&itemToDelete, kFSCatInfoNodeFlags, &theGlobals->catalogInfo); | ||
1534 | } | ||
1535 | /* delete the item */ | ||
1536 | theGlobals->result = FSDeleteObject(&itemToDelete); | ||
1537 | } | ||
1538 | } | ||
1539 | } while ( theGlobals->result == noErr ); | ||
1540 | |||
1541 | /* we found the end of the items normally, so return noErr */ | ||
1542 | if ( theGlobals->result == errFSNoMoreItems ) | ||
1543 | theGlobals->result = noErr; | ||
1544 | |||
1545 | /* close the FSIterator (closing an open iterator should never fail) */ | ||
1546 | myverify_noerr(FSCloseIterator(iterator)); | ||
1547 | } | ||
1548 | |||
1549 | mycheck_noerr( theGlobals->result ); | ||
1550 | |||
1551 | return; | ||
1552 | } | ||
1553 | |||
1554 | /*****************************************************************************/ | ||
1555 | |||
1556 | #pragma mark ----- Utilities ----- | ||
1557 | |||
1558 | /* Figures out if the given directory is a drop box or not */ | ||
1559 | /* if it is, the Copy Engine will behave slightly differently */ | ||
1560 | static OSErr IsDropBox( const FSRef* source, | ||
1561 | Boolean *isDropBox ) | ||
1562 | { | ||
1563 | FSCatalogInfo tmpCatInfo; | ||
1564 | FSSpec sourceSpec; | ||
1565 | Boolean isDrop = false; | ||
1566 | OSErr err; | ||
1567 | |||
1568 | /* get info about the destination, and an FSSpec to it for PBHGetDirAccess */ | ||
1569 | err = FSGetCatalogInfo(source, kFSCatInfoNodeFlags | kFSCatInfoPermissions, &tmpCatInfo, NULL, &sourceSpec, NULL); | ||
1570 | if( err == noErr ) /* make sure the source is a directory */ | ||
1571 | err = ((tmpCatInfo.nodeFlags & kFSNodeIsDirectoryMask) != 0) ? noErr : errFSNotAFolder; | ||
1572 | if( err == noErr ) | ||
1573 | { | ||
1574 | HParamBlockRec hPB; | ||
1575 | |||
1576 | BlockZero( &hPB, sizeof( HParamBlockRec ) ); | ||
1577 | |||
1578 | hPB.accessParam.ioNamePtr = sourceSpec.name; | ||
1579 | hPB.accessParam.ioVRefNum = sourceSpec.vRefNum; | ||
1580 | hPB.accessParam.ioDirID = sourceSpec.parID; | ||
1581 | |||
1582 | /* This is the official way (reads: the way X Finder does it) to figure */ | ||
1583 | /* out the current users access privileges to a given directory */ | ||
1584 | err = PBHGetDirAccessSync(&hPB); | ||
1585 | if( err == noErr ) /* its a drop folder if the current user only has write access */ | ||
1586 | isDrop = (hPB.accessParam.ioACAccess & kPrivilegesMask) == kioACAccessUserWriteMask; | ||
1587 | else if ( err == paramErr ) | ||
1588 | { | ||
1589 | /* There is a bug (2908703) in the Classic File System (not OS 9.x or Carbon) */ | ||
1590 | /* on 10.1.x where PBHGetDirAccessSync sometimes returns paramErr even when the */ | ||
1591 | /* data passed in is correct. This is a workaround/hack for that problem, */ | ||
1592 | /* but is not as accurate. */ | ||
1593 | /* Basically, if "Everyone" has only Write/Search access then its a drop folder */ | ||
1594 | /* that is the most common case when its a drop folder */ | ||
1595 | FSPermissionInfo *tmpPerm = (FSPermissionInfo *)tmpCatInfo.permissions; | ||
1596 | isDrop = ((tmpPerm->mode & kRWXOtherAccessMask) == kDropFolderValue); | ||
1597 | err = noErr; | ||
1598 | } | ||
1599 | } | ||
1600 | |||
1601 | *isDropBox = isDrop; | ||
1602 | |||
1603 | mycheck_noerr( err ); | ||
1604 | |||
1605 | return err; | ||
1606 | } | ||
1607 | |||
1608 | /*****************************************************************************/ | ||
1609 | |||
1610 | /* The copy engine is going to set the item's creation date */ | ||
1611 | /* to kMagicBusyCreationDate while it's copying the item. */ | ||
1612 | /* But kMagicBusyCreationDate is an old-style 32-bit date/time, */ | ||
1613 | /* while the HFS Plus APIs use the new 64-bit date/time. So */ | ||
1614 | /* we have to call a happy UTC utilities routine to convert from */ | ||
1615 | /* the local time kMagicBusyCreationDate to a UTCDateTime */ | ||
1616 | /* gMagicBusyCreationDate, which the File Manager will store */ | ||
1617 | /* on disk and which the Finder we read back using the old */ | ||
1618 | /* APIs, whereupon the File Manager will convert it back */ | ||
1619 | /* to local time (and hopefully get the kMagicBusyCreationDate */ | ||
1620 | /* back!). */ | ||
1621 | static OSErr GetMagicBusyCreateDate( UTCDateTime *date ) | ||
1622 | { | ||
1623 | static UTCDateTime magicDate = { 0, 0xDEADBEEF, 0 }; | ||
1624 | OSErr err = ( date != NULL ) ? noErr : paramErr; | ||
1625 | |||
1626 | if( err == noErr && magicDate.lowSeconds == 0xDEADBEEF ) | ||
1627 | err = ConvertLocalTimeToUTC( kMagicBusyCreationDate, &magicDate.lowSeconds ); | ||
1628 | if( err == noErr ) | ||
1629 | *date = magicDate; | ||
1630 | |||
1631 | mycheck_noerr( err ); | ||
1632 | |||
1633 | return err; | ||
1634 | } | ||
1635 | |||
1636 | /*****************************************************************************/ | ||
1637 | |||
1638 | static OSErr FSGetVRefNum( const FSRef *ref, | ||
1639 | FSVolumeRefNum *vRefNum) | ||
1640 | { | ||
1641 | FSCatalogInfo catalogInfo; | ||
1642 | OSErr err = ( ref != NULL && vRefNum != NULL ) ? noErr : paramErr; | ||
1643 | |||
1644 | if( err == noErr ) /* get the volume refNum from the FSRef */ | ||
1645 | err = FSGetCatalogInfo(ref, kFSCatInfoVolume, &catalogInfo, NULL, NULL, NULL); | ||
1646 | if( err == noErr ) | ||
1647 | *vRefNum = catalogInfo.volume; | ||
1648 | |||
1649 | mycheck_noerr( err ); | ||
1650 | |||
1651 | return err; | ||
1652 | } | ||
1653 | |||
1654 | /*****************************************************************************/ | ||
1655 | |||
1656 | static OSErr FSGetVolParms( FSVolumeRefNum volRefNum, | ||
1657 | UInt32 bufferSize, | ||
1658 | GetVolParmsInfoBuffer *volParmsInfo, | ||
1659 | UInt32 *actualInfoSize) /* Can Be NULL */ | ||
1660 | { | ||
1661 | HParamBlockRec pb; | ||
1662 | OSErr err = ( volParmsInfo != NULL ) ? noErr : paramErr; | ||
1663 | |||
1664 | if( err == noErr ) | ||
1665 | { | ||
1666 | pb.ioParam.ioNamePtr = NULL; | ||
1667 | pb.ioParam.ioVRefNum = volRefNum; | ||
1668 | pb.ioParam.ioBuffer = (Ptr)volParmsInfo; | ||
1669 | pb.ioParam.ioReqCount = (SInt32)bufferSize; | ||
1670 | err = PBHGetVolParmsSync(&pb); | ||
1671 | } | ||
1672 | /* return number of bytes the file system returned in volParmsInfo buffer */ | ||
1673 | if( err == noErr && actualInfoSize != NULL) | ||
1674 | *actualInfoSize = (UInt32)pb.ioParam.ioActCount; | ||
1675 | |||
1676 | mycheck_noerr( err ); | ||
1677 | |||
1678 | return ( err ); | ||
1679 | } | ||
1680 | |||
1681 | /*****************************************************************************/ | ||
1682 | |||
1683 | /* Converts a unicode string to a PString */ | ||
1684 | /* If your code is only for OS X, you can use CFString functions to do all this */ | ||
1685 | /* Since this sample code supports OS 9.1 -> OS X, I have to do this the */ | ||
1686 | /* old fashioned way. */ | ||
1687 | static OSErr UniStrToPStr( const HFSUniStr255 *uniStr, | ||
1688 | TextEncoding textEncodingHint, | ||
1689 | Boolean isVolumeName, | ||
1690 | Str255 pStr ) | ||
1691 | { | ||
1692 | UnicodeMapping uMapping; | ||
1693 | UnicodeToTextInfo utInfo; | ||
1694 | ByteCount unicodeByteLength = 0; | ||
1695 | ByteCount unicodeBytesConverted; | ||
1696 | ByteCount actualPascalBytes; | ||
1697 | OSErr err = (uniStr != NULL && pStr != NULL) ? noErr : paramErr; | ||
1698 | |||
1699 | /* make sure output is valid in case we get errors or there's nothing to convert */ | ||
1700 | pStr[0] = 0; | ||
1701 | |||
1702 | if( err == noErr ) | ||
1703 | unicodeByteLength = uniStr->length * sizeof(UniChar); /* length can be zero, which is fine */ | ||
1704 | if( err == noErr && unicodeByteLength != 0 ) | ||
1705 | { | ||
1706 | /* if textEncodingHint is kTextEncodingUnknown, get a "default" textEncodingHint */ | ||
1707 | if ( kTextEncodingUnknown == textEncodingHint ) | ||
1708 | { | ||
1709 | ScriptCode script; | ||
1710 | RegionCode region; | ||
1711 | |||
1712 | script = (ScriptCode)GetScriptManagerVariable(smSysScript); | ||
1713 | region = (RegionCode)GetScriptManagerVariable(smRegionCode); | ||
1714 | err = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, | ||
1715 | region, NULL, &textEncodingHint ); | ||
1716 | if ( err == paramErr ) | ||
1717 | { /* ok, ignore the region and try again */ | ||
1718 | err = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, | ||
1719 | kTextRegionDontCare, NULL, | ||
1720 | &textEncodingHint ); | ||
1721 | } | ||
1722 | if ( err != noErr ) /* ok... try something */ | ||
1723 | textEncodingHint = kTextEncodingMacRoman; | ||
1724 | } | ||
1725 | |||
1726 | uMapping.unicodeEncoding = CreateTextEncoding( kTextEncodingUnicodeV2_0, | ||
1727 | kUnicodeCanonicalDecompVariant, | ||
1728 | kUnicode16BitFormat); | ||
1729 | uMapping.otherEncoding = GetTextEncodingBase(textEncodingHint); | ||
1730 | uMapping.mappingVersion = kUnicodeUseHFSPlusMapping; | ||
1731 | |||
1732 | err = CreateUnicodeToTextInfo(&uMapping, &utInfo); | ||
1733 | if( err == noErr ) | ||
1734 | { | ||
1735 | err = ConvertFromUnicodeToText( utInfo, unicodeByteLength, uniStr->unicode, kUnicodeLooseMappingsMask, | ||
1736 | 0, NULL, 0, NULL, /* offsetCounts & offsetArrays */ | ||
1737 | isVolumeName ? kHFSMaxVolumeNameChars : kHFSPlusMaxFileNameChars, | ||
1738 | &unicodeBytesConverted, &actualPascalBytes, &pStr[1]); | ||
1739 | } | ||
1740 | if( err == noErr ) | ||
1741 | pStr[0] = actualPascalBytes; | ||
1742 | |||
1743 | /* verify the result in debug builds -- there's really not anything you can do if it fails */ | ||
1744 | myverify_noerr(DisposeUnicodeToTextInfo(&utInfo)); | ||
1745 | } | ||
1746 | |||
1747 | mycheck_noerr( err ); | ||
1748 | |||
1749 | return ( err ); | ||
1750 | } | ||
1751 | |||
1752 | /*****************************************************************************/ | ||
1753 | |||
1754 | /* Yeah I know there is FSpMakeFSRef, but this way I don't have to */ | ||
1755 | /* actually have an FSSpec created to make the FSRef, and this is */ | ||
1756 | /* what FSpMakeFSRef does anyways */ | ||
1757 | static OSErr FSMakeFSRef( FSVolumeRefNum volRefNum, | ||
1758 | SInt32 dirID, | ||
1759 | ConstStr255Param name, | ||
1760 | FSRef *ref ) | ||
1761 | { | ||
1762 | FSRefParam pb; | ||
1763 | OSErr err = ( ref != NULL ) ? noErr : paramErr; | ||
1764 | |||
1765 | if( err == noErr ) | ||
1766 | { | ||
1767 | pb.ioVRefNum = volRefNum; | ||
1768 | pb.ioDirID = dirID; | ||
1769 | pb.ioNamePtr = (StringPtr)name; | ||
1770 | pb.newRef = ref; | ||
1771 | err = PBMakeFSRefSync(&pb); | ||
1772 | } | ||
1773 | |||
1774 | mycheck_noerr( err ); | ||
1775 | |||
1776 | return ( err ); | ||
1777 | } | ||
1778 | |||
1779 | /*****************************************************************************/ | ||
1780 | |||
1781 | /* This checks the destination to see if an object of the same name as the source */ | ||
1782 | /* exists or not. If it does we have to special handle the DupeActions */ | ||
1783 | /* */ | ||
1784 | /* If kDupeActionReplace we move aside the object by renameing it to ".DeleteMe" */ | ||
1785 | /* so that it will be invisible (X only), and give a suggestion on what to do with */ | ||
1786 | /* it if for some unknown reason it survives the copy and the user finds it. This */ | ||
1787 | /* rename is mainly done to handle the case where the source is in the destination */ | ||
1788 | /* and the user wants to replace. Basically keeping the source around throughout */ | ||
1789 | /* the copy, deleting it afterwards. Its also done cause its a good idea not to */ | ||
1790 | /* dispose of the existing object in case the copy fails */ | ||
1791 | /* */ | ||
1792 | /* If kDupeActionRename, we create a unique name for the new object and pass */ | ||
1793 | /* it back to the caller */ | ||
1794 | static OSErr SetupDestination( const FSRef *destDir, | ||
1795 | const DupeAction dupeAction, | ||
1796 | HFSUniStr255 *sourceName, | ||
1797 | FSRef *deleteMeRef, | ||
1798 | Boolean *isReplacing ) | ||
1799 | { | ||
1800 | FSRef tmpRef; | ||
1801 | OSErr err; | ||
1802 | |||
1803 | /* check if an object of the same name already exists in the destination */ | ||
1804 | err = FSMakeFSRefUnicode( destDir, sourceName->length, sourceName->unicode, kTextEncodingUnknown, &tmpRef ); | ||
1805 | if( err == noErr ) | ||
1806 | { /* if the user wants to replace the existing */ | ||
1807 | /* object, rename it to .DeleteMe first. Delete it */ | ||
1808 | if( dupeAction == kDupeActionReplace ) /* only after copying the new one successfully */ | ||
1809 | { | ||
1810 | err = FSRenameUnicode( &tmpRef, 9, (UniChar*)"\0.\0D\0e\0l\0e\0t\0e\0M\0e", kTextEncodingMacRoman, deleteMeRef ); | ||
1811 | *isReplacing = ( err == noErr ) ? true : false; | ||
1812 | } | ||
1813 | else if( dupeAction == kDupeActionRename ) /* if the user wants to just rename it */ | ||
1814 | err = GetUniqueName( destDir, sourceName ); /* then we get a unique name for the new object */ | ||
1815 | } | ||
1816 | else if ( err == fnfErr ) /* if no object exists then */ | ||
1817 | err = noErr; /* continue with no error */ | ||
1818 | |||
1819 | return err; | ||
1820 | } | ||
1821 | |||
1822 | /*****************************************************************************/ | ||
1823 | |||
1824 | /* Given a directory and a name, GetUniqueName will check if an object */ | ||
1825 | /* with the same name already exists, and if it does it will create */ | ||
1826 | /* a new, unique name for and return it. */ | ||
1827 | /* it simply appends a number to the end of the name. It is not */ | ||
1828 | /* fool proof, and it is limited... I'll take care of that in a */ | ||
1829 | /* later release */ | ||
1830 | /* If anyone has any suggestions/better techniques I would love to hear */ | ||
1831 | /* about them */ | ||
1832 | static OSErr GetUniqueName( const FSRef *destDir, | ||
1833 | HFSUniStr255 *sourceName ) | ||
1834 | { | ||
1835 | HFSUniStr255 tmpName = *sourceName; | ||
1836 | FSRef tmpRef; | ||
1837 | unsigned char hexStr[17] = "123456789"; /* yeah, only 9... I'm lazy, sosumi */ | ||
1838 | long count = 0; | ||
1839 | int index; | ||
1840 | OSErr err; | ||
1841 | |||
1842 | /* find the dot, if there is one */ | ||
1843 | for( index = tmpName.length; index >= 0 && tmpName.unicode[index] != (UniChar) '.'; index-- ) { /* Do Nothing */ } | ||
1844 | |||
1845 | if( index <= 0) /* no dot or first char is a dot (invisible file), so append to end of name */ | ||
1846 | index = tmpName.length; | ||
1847 | else /* shift the extension up two spots to make room for our digits */ | ||
1848 | BlockMoveData( tmpName.unicode + index, tmpName.unicode + index + 2, (tmpName.length - index) * 2 ); | ||
1849 | |||
1850 | /* add the space to the name */ | ||
1851 | tmpName.unicode[ index ] = (UniChar)' '; | ||
1852 | /* we're adding two characters to the name */ | ||
1853 | tmpName.length += 2; | ||
1854 | |||
1855 | do { /* add the digit to the name */ | ||
1856 | tmpName.unicode[ index + 1 ] = hexStr[count]; | ||
1857 | /* check if the file with this new name already exists */ | ||
1858 | err = FSMakeFSRefUnicode( destDir, tmpName.length, tmpName.unicode, kTextEncodingUnknown, &tmpRef ); | ||
1859 | count++; | ||
1860 | } while( err == noErr && count < 10 ); | ||
1861 | |||
1862 | if( err == fnfErr ) | ||
1863 | { | ||
1864 | err = noErr; | ||
1865 | *sourceName = tmpName; | ||
1866 | } | ||
1867 | |||
1868 | return err; | ||
1869 | } | ||
1870 | |||
1871 | /*****************************************************************************/ | ||
1872 | |||
1873 | static OSErr GetObjectName( const FSRef *sourceRef, | ||
1874 | HFSUniStr255 *sourceName, /* can be NULL */ | ||
1875 | TextEncoding *sourceEncoding ) /* can be NULL */ | ||
1876 | { | ||
1877 | FSCatalogInfo catInfo; | ||
1878 | FSCatalogInfoBitmap whichInfo = (sourceEncoding != NULL) ? kFSCatInfoTextEncoding : kFSCatInfoNone; | ||
1879 | OSErr err; | ||
1880 | |||
1881 | err = FSGetCatalogInfo( sourceRef, whichInfo, &catInfo, sourceName, NULL, NULL ); | ||
1882 | if( err == noErr && sourceEncoding != NULL ) | ||
1883 | *sourceEncoding = catInfo.textEncodingHint; | ||
1884 | |||
1885 | return err; | ||
1886 | } | ||
1887 | |||
1888 | /*****************************************************************************/ | ||
1889 | |||
1890 | static OSErr CreateFolder( const FSRef *sourceRef, | ||
1891 | const FSRef *destDirRef, | ||
1892 | const FSCatalogInfo *catalogInfo, | ||
1893 | const HFSUniStr255 *folderName, | ||
1894 | CopyParams *params, | ||
1895 | FSRef *newFSRefPtr, | ||
1896 | FSSpec *newFSSpecPtr ) | ||
1897 | { | ||
1898 | FSCatalogInfo tmpCatInfo; | ||
1899 | FSPermissionInfo origPermissions; | ||
1900 | OSErr err = ( sourceRef != NULL && destDirRef != NULL && catalogInfo != NULL && | ||
1901 | folderName != NULL && newFSRefPtr != NULL ) ? noErr : paramErr; | ||
1902 | |||
1903 | if( err == noErr ) | ||
1904 | { /* store away the catInfo, create date and permissions on the orig folder */ | ||
1905 | tmpCatInfo = *catalogInfo; | ||
1906 | origPermissions = *((FSPermissionInfo*)catalogInfo->permissions); | ||
1907 | } | ||
1908 | if( err == noErr ) /* create the new folder */ | ||
1909 | err = DoCreateFolder( sourceRef, destDirRef, &tmpCatInfo, folderName, params, newFSRefPtr, newFSSpecPtr ); | ||
1910 | if( err == noErr && !params->copyingToDropFolder ) | ||
1911 | { /* if its not a drop box, set the permissions on the new folder */ | ||
1912 | *((FSPermissionInfo*)tmpCatInfo.permissions) = origPermissions; | ||
1913 | err = FSSetCatalogInfo( newFSRefPtr, kFSCatInfoPermissions, &tmpCatInfo ); | ||
1914 | } | ||
1915 | |||
1916 | mycheck_noerr( err ); | ||
1917 | |||
1918 | return err; | ||
1919 | } | ||
1920 | |||
1921 | /*****************************************************************************/ | ||
1922 | |||
1923 | static OSErr DoCreateFolder(const FSRef *sourceRef, | ||
1924 | const FSRef *destDirRef, | ||
1925 | const FSCatalogInfo *catalogInfo, | ||
1926 | const HFSUniStr255 *folderName, | ||
1927 | CopyParams *params, | ||
1928 | FSRef *newFSRefPtr, | ||
1929 | FSSpec *newFSSpecPtr) | ||
1930 | { | ||
1931 | FSCatalogInfo catInfo = *catalogInfo; | ||
1932 | OSErr err; | ||
1933 | |||
1934 | /* Clear the "inited" bit so that the Finder positions the icon for us. */ | ||
1935 | ((FInfo *)(catInfo.finderInfo))->fdFlags &= ~kHasBeenInited; | ||
1936 | |||
1937 | /* we need to have user level read/write/execute access to the folder we are going to create, */ | ||
1938 | /* otherwise FSCreateDirectoryUnicode will return -5000 (afpAccessDenied), */ | ||
1939 | /* and the FSRef returned will be invalid, yet the folder is created... bug? */ | ||
1940 | ((FSPermissionInfo*) catInfo.permissions)->mode |= kRWXUserAccessMask; | ||
1941 | |||
1942 | err = FSCreateDirectoryUnicode( destDirRef, folderName->length, | ||
1943 | folderName->unicode, kFSCatInfoSettableInfo, | ||
1944 | &catInfo, newFSRefPtr, | ||
1945 | newFSSpecPtr, NULL); | ||
1946 | |||
1947 | /* With the new APIs, folders can have forks as well as files. Before */ | ||
1948 | /* we start copying items in the folder, we must copy over the forks */ | ||
1949 | /* Currently, MacOS doesn't support any file systems that have forks in */ | ||
1950 | /* folders, but the API supports it so (for possible future */ | ||
1951 | /* compatability) I kept this in here. */ | ||
1952 | if( err == noErr ) | ||
1953 | err = CopyForks( sourceRef, newFSRefPtr, params ); | ||
1954 | |||
1955 | mycheck_noerr( err ); | ||
1956 | |||
1957 | return err; | ||
1958 | } | ||
1959 | |||
1960 | /*****************************************************************************/ | ||
1961 | |||
1962 | /* This is the DisposeDataProc that is used by the GenLinkedList in FSCopyFolder */ | ||
1963 | /* Simply disposes of the data we created and returns */ | ||
1964 | static pascal void MyDisposeDataProc( void *pData ) | ||
1965 | { | ||
1966 | if( pData != NULL ) | ||
1967 | DisposePtr( (char*) pData ); | ||
1968 | } | ||
1969 | |||
1970 | /*****************************************************************************/ | ||
1971 | |||
1972 | /* This is the DisposeDataProc that is used by the GenLinkedList in CopyItemForks */ | ||
1973 | /* Simply closes the resource fork (if opened, != 0) and disposes of the memory */ | ||
1974 | static pascal void MyCloseForkProc( void *pData ) | ||
1975 | { | ||
1976 | SInt16 refNum; | ||
1977 | |||
1978 | if( pData == NULL ) | ||
1979 | return; | ||
1980 | |||
1981 | refNum = ((ForkTrackerPtr)pData)->forkDestRefNum; | ||
1982 | if( refNum != 0 ) | ||
1983 | myverify_noerr( FSCloseFork( refNum ) ); /* the fork was opened, so close it */ | ||
1984 | |||
1985 | DisposePtr( (char*) pData ); | ||
1986 | } \ No newline at end of file | ||
diff --git a/linden/indra/mac_updater/FSCopyObject.h b/linden/indra/mac_updater/FSCopyObject.h new file mode 100644 index 0000000..6cbbf94 --- /dev/null +++ b/linden/indra/mac_updater/FSCopyObject.h | |||
@@ -0,0 +1,285 @@ | |||
1 | /** | ||
2 | * @file FSCopyObject.h | ||
3 | * @brief Mac OSX updater program | ||
4 | * | ||
5 | * Copyright (c) 2004-2007, Linden Research, Inc. | ||
6 | * | ||
7 | * The source code in this file ("Source Code") is provided by Linden Lab | ||
8 | * to you under the terms of the GNU General Public License, version 2.0 | ||
9 | * ("GPL"), unless you have obtained a separate licensing agreement | ||
10 | * ("Other License"), formally executed by you and Linden Lab. Terms of | ||
11 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
12 | * online at http://secondlife.com/developers/opensource/gplv2 | ||
13 | * | ||
14 | * There are special exceptions to the terms and conditions of the GPL as | ||
15 | * it is applied to this Source Code. View the full text of the exception | ||
16 | * in the file doc/FLOSS-exception.txt in this software distribution, or | ||
17 | * online at http://secondlife.com/developers/opensource/flossexception | ||
18 | * | ||
19 | * By copying, modifying or distributing this software, you acknowledge | ||
20 | * that you have read and understood your obligations described above, | ||
21 | * and agree to abide by those obligations. | ||
22 | * | ||
23 | * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
24 | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
25 | * COMPLETENESS OR PERFORMANCE. | ||
26 | */ | ||
27 | |||
28 | /* | ||
29 | File: FSCopyObject.h | ||
30 | |||
31 | Contains: A Copy/Delete Files/Folders engine which uses the HFS+ API's | ||
32 | |||
33 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. | ||
34 | ("Apple") in consideration of your agreement to the following terms, and your | ||
35 | use, installation, modification or redistribution of this Apple software | ||
36 | constitutes acceptance of these terms. If you do not agree with these terms, | ||
37 | please do not use, install, modify or redistribute this Apple software. | ||
38 | |||
39 | In consideration of your agreement to abide by the following terms, and subject | ||
40 | to these terms, Apple grants you a personal, non-exclusive license, under Apple�s | ||
41 | copyrights in this original Apple software (the "Apple Software"), to use, | ||
42 | reproduce, modify and redistribute the Apple Software, with or without | ||
43 | modifications, in source and/or binary forms; provided that if you redistribute | ||
44 | the Apple Software in its entirety and without modifications, you must retain | ||
45 | this notice and the following text and disclaimers in all such redistributions of | ||
46 | the Apple Software. Neither the name, trademarks, service marks or logos of | ||
47 | Apple Computer, Inc. may be used to endorse or promote products derived from the | ||
48 | Apple Software without specific prior written permission from Apple. Except as | ||
49 | expressly stated in this notice, no other rights or licenses, express or implied, | ||
50 | are granted by Apple herein, including but not limited to any patent rights that | ||
51 | may be infringed by your derivative works or by other works in which the Apple | ||
52 | Software may be incorporated. | ||
53 | |||
54 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO | ||
55 | WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED | ||
56 | WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
57 | PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN | ||
58 | COMBINATION WITH YOUR PRODUCTS. | ||
59 | |||
60 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR | ||
61 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE | ||
62 | GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
63 | ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION | ||
64 | OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT | ||
65 | (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN | ||
66 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
67 | |||
68 | Copyright � 2002-2004 Apple Computer, Inc., All Rights Reserved | ||
69 | */ | ||
70 | |||
71 | |||
72 | #ifndef __FSCOPYOBJECT_H__ | ||
73 | #define __FSCOPYOBJECT_H__ | ||
74 | |||
75 | #ifdef __cplusplus | ||
76 | extern "C" { | ||
77 | #endif | ||
78 | |||
79 | #if TARGET_API_MAC_OSX || defined( __APPLE_CC__ ) | ||
80 | #include <CoreServices/CoreServices.h> | ||
81 | #endif | ||
82 | |||
83 | #define DEBUG 1 /* set to zero if you don't want debug spew */ | ||
84 | |||
85 | #if DEBUG | ||
86 | #include <stdio.h> | ||
87 | |||
88 | #define QuoteExceptionString(x) #x | ||
89 | |||
90 | #define dwarning(s) do { printf s; fflush(stderr); } while( 0 ) | ||
91 | |||
92 | #define mycheck_noerr( error ) \ | ||
93 | do { \ | ||
94 | if( (OSErr) error != noErr ) { \ | ||
95 | dwarning((QuoteExceptionString(error) " != noErr in File: %s, Function: %s, Line: %d, Error: %d\n", \ | ||
96 | __FILE__, __FUNCTION__, __LINE__, (OSErr) error)); \ | ||
97 | } \ | ||
98 | } while( false ) | ||
99 | |||
100 | #define mycheck( assertion ) \ | ||
101 | do { \ | ||
102 | if( ! assertion ) { \ | ||
103 | dwarning((QuoteExceptionString(assertion) " failed in File: %s, Function: %s, Line: %d\n", \ | ||
104 | __FILE__, __FUNCTION__, __LINE__)); \ | ||
105 | } \ | ||
106 | } while( false ) | ||
107 | |||
108 | #define myverify(assertion) mycheck(assertion) | ||
109 | #define myverify_noerr(assertion) mycheck_noerr( (assertion) ) | ||
110 | #else | ||
111 | #define dwarning(s) | ||
112 | |||
113 | #define mycheck(assertion) | ||
114 | #define mycheck_noerr(err) | ||
115 | #define myverify(assertion) do { (void) (assertion); } while (0) | ||
116 | #define myverify_noerr(assertion) myverify(assertion) | ||
117 | #endif | ||
118 | |||
119 | /* | ||
120 | This code takes some tricks/techniques from MoreFilesX (by Jim Luther) and | ||
121 | MPFileCopy (by Quinn), wraps them all up into an easy to use API, and adds a bunch of | ||
122 | features and bug fixes. It will run on Mac OS 9.1 through 9.2.x and 10.1.x | ||
123 | and up (Classic, Carbon and Mach-O) | ||
124 | */ | ||
125 | |||
126 | /* Different options that FSCopyObject can take during a copy */ | ||
127 | typedef UInt32 DupeAction; | ||
128 | enum { | ||
129 | kDupeActionStandard, /* will do the copy with no frills */ | ||
130 | kDupeActionReplace, /* will delete the existing object and then copy over the new one */ | ||
131 | kDupeActionRename /* will rename the new object if an object of the same name exists */ | ||
132 | }; | ||
133 | |||
134 | /*****************************************************************************/ | ||
135 | |||
136 | #pragma mark CopyObjectFilterProcPtr | ||
137 | |||
138 | /* | ||
139 | This is the prototype for the CallCopyObjectFilterProc function which | ||
140 | is called once for each file and directory found by FSCopyObject. | ||
141 | The CallCopyObjectFilterProc can use the read-only data it receives for | ||
142 | whatever it wants. | ||
143 | |||
144 | The result of the CallCopyObjectFilterProc function indicates if | ||
145 | the copy should be stopped. To stop the copy, return an error; to continue | ||
146 | the copy, return noErr. | ||
147 | |||
148 | The yourDataPtr parameter can point to whatever data structure you might | ||
149 | want to access from within the CallCopyObjectFilterProc. | ||
150 | |||
151 | Note: If an error had occured during the copy of the current object | ||
152 | (currentOSErr != noErr) the FSRef etc might not be valid | ||
153 | |||
154 | containerChanged --> Set to true if the container's contents changed | ||
155 | during iteration. | ||
156 | currentLevel --> The current recursion level into the container. | ||
157 | 1 = the container, 2 = the container's immediate | ||
158 | subdirectories, etc. | ||
159 | currentOSErr --> The current error code, shows the results of the | ||
160 | copy of the current object (ref) | ||
161 | catalogInfo --> The catalog information for the current object. | ||
162 | Only the fields requested by the whichInfo | ||
163 | parameter passed to FSIterateContainer are valid. | ||
164 | ref --> The FSRef to the current object. | ||
165 | spec --> The FSSpec to the current object if the wantFSSpec | ||
166 | parameter passed to FSCopyObject is true. | ||
167 | name --> The name of the current object if the wantName | ||
168 | parameter passed to FSCopyObject is true. | ||
169 | yourDataPtr --> An optional pointer to whatever data structure you | ||
170 | might want to access from within the | ||
171 | CallCopyObjectFilterProc. | ||
172 | result <-- To continue the copy, return noErr | ||
173 | |||
174 | __________ | ||
175 | |||
176 | Also see: FSCopyObject | ||
177 | */ | ||
178 | |||
179 | typedef CALLBACK_API( OSErr , CopyObjectFilterProcPtr ) ( | ||
180 | Boolean containerChanged, | ||
181 | ItemCount currentLevel, | ||
182 | OSErr currentOSErr, | ||
183 | const FSCatalogInfo *catalogInfo, | ||
184 | const FSRef *ref, | ||
185 | const FSSpec *spec, | ||
186 | const HFSUniStr255 *name, | ||
187 | void *yourDataPtr); | ||
188 | |||
189 | |||
190 | /*****************************************************************************/ | ||
191 | |||
192 | #pragma mark CallCopyObjectFilterProc | ||
193 | |||
194 | #define CallCopyObjectFilterProc(userRoutine, containerChanged, currentLevel, currentOSErr, catalogInfo, ref, spec, name, yourDataPtr) \ | ||
195 | (*(userRoutine))((containerChanged), (currentLevel), (currentOSErr), (catalogInfo), (ref), (spec), (name), (yourDataPtr)) | ||
196 | |||
197 | /*****************************************************************************/ | ||
198 | |||
199 | #pragma mark FSCopyObject | ||
200 | |||
201 | /* | ||
202 | The FSCopyObject function takes a source object (can be a file or directory) | ||
203 | and copies it (and its contents if it's a directory) to the new destination | ||
204 | directory. | ||
205 | |||
206 | It will call your CopyObjectFilterProcPtr once for each object copied | ||
207 | |||
208 | The maxLevels parameter is only used when the object is a directory, | ||
209 | ignored otherwise. | ||
210 | It lets you control how deep the recursion goes. | ||
211 | If maxLevels is 1, FSCopyObject only scans the specified directory; | ||
212 | if maxLevels is 2, FSCopyObject scans the specified directory and | ||
213 | one subdirectory below the specified directory; etc. Set maxLevels to | ||
214 | zero to scan all levels. | ||
215 | |||
216 | The yourDataPtr parameter can point to whatever data structure you might | ||
217 | want to access from within your CopyObjectFilterProcPtr. | ||
218 | |||
219 | source --> The FSRef to the object you want to copy | ||
220 | destDir --> The FSRef to the directory you wish to copy source to | ||
221 | maxLevels --> Maximum number of directory levels to scan or | ||
222 | zero to scan all directory levels, ignored if the | ||
223 | object is a file | ||
224 | whichInfo --> The fields of the FSCatalogInfo you wish passed | ||
225 | to you in your CopyObjectFilterProc | ||
226 | dupeAction --> The action to take if an object of the same name exists | ||
227 | in the destination | ||
228 | newName --> The name you want the new object to have. If you pass | ||
229 | in NULL, the source object name will be used. | ||
230 | wantFSSpec --> Set to true if you want the FSSpec to each | ||
231 | object passed to your CopyObjectFilterProc. | ||
232 | wantName --> Set to true if you want the name of each | ||
233 | object passed to your CopyObjectFilterProc. | ||
234 | filterProcPtr --> A pointer to the CopyObjectFilterProc you | ||
235 | want called once for each object found | ||
236 | by FSCopyObject. | ||
237 | yourDataPtr --> An optional pointer to whatever data structure you | ||
238 | might want to access from within the | ||
239 | CopyObjectFilterProc. | ||
240 | newObjectRef --> An optional pointer to an FSRef that, on return, | ||
241 | references the new object. If you don't want this | ||
242 | info returned, pass in NULL | ||
243 | newObjectSpec --> An optional pointer to an FSSPec that, on return, | ||
244 | references the new object. If you don't want this | ||
245 | info returned, pass in NULL | ||
246 | */ | ||
247 | |||
248 | OSErr FSCopyObject( const FSRef *source, | ||
249 | const FSRef *destDir, | ||
250 | ItemCount maxLevels, | ||
251 | FSCatalogInfoBitmap whichInfo, | ||
252 | DupeAction dupeAction, | ||
253 | const HFSUniStr255 *newName, /* can be NULL */ | ||
254 | Boolean wantFSSpec, | ||
255 | Boolean wantName, | ||
256 | CopyObjectFilterProcPtr filterProcPtr, /* can be NULL */ | ||
257 | void *yourDataPtr, /* can be NULL */ | ||
258 | FSRef *newObjectRef, /* can be NULL */ | ||
259 | FSSpec *newObjectSpec); /* can be NULL */ | ||
260 | |||
261 | /*****************************************************************************/ | ||
262 | |||
263 | #pragma mark FSDeleteObjects | ||
264 | |||
265 | /* | ||
266 | The FSDeleteObjects function takes an FSRef to a file or directory | ||
267 | and attempts to delete it. If the object is a directory, all files | ||
268 | and subdirectories in the specified directory are deleted. If a | ||
269 | locked file or directory is encountered, it is unlocked and then | ||
270 | deleted. After deleting the directory's contents, the directory | ||
271 | is deleted. If any unexpected errors are encountered, | ||
272 | FSDeleteContainer quits and returns to the caller. | ||
273 | |||
274 | source --> FSRef to an object (can be file or directory). | ||
275 | |||
276 | __________ | ||
277 | */ | ||
278 | |||
279 | OSErr FSDeleteObjects( const FSRef *source ); | ||
280 | |||
281 | #ifdef __cplusplus | ||
282 | } | ||
283 | #endif | ||
284 | |||
285 | #endif | ||
diff --git a/linden/indra/mac_updater/GenLinkedList.c b/linden/indra/mac_updater/GenLinkedList.c new file mode 100644 index 0000000..f1cde85 --- /dev/null +++ b/linden/indra/mac_updater/GenLinkedList.c | |||
@@ -0,0 +1,212 @@ | |||
1 | /* | ||
2 | File: GenLinkedList.c | ||
3 | |||
4 | Contains: Linked List utility routines | ||
5 | |||
6 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. | ||
7 | ("Apple") in consideration of your agreement to the following terms, and your | ||
8 | use, installation, modification or redistribution of this Apple software | ||
9 | constitutes acceptance of these terms. If you do not agree with these terms, | ||
10 | please do not use, install, modify or redistribute this Apple software. | ||
11 | |||
12 | In consideration of your agreement to abide by the following terms, and subject | ||
13 | to these terms, Apple grants you a personal, non-exclusive license, under Apple�s | ||
14 | copyrights in this original Apple software (the "Apple Software"), to use, | ||
15 | reproduce, modify and redistribute the Apple Software, with or without | ||
16 | modifications, in source and/or binary forms; provided that if you redistribute | ||
17 | the Apple Software in its entirety and without modifications, you must retain | ||
18 | this notice and the following text and disclaimers in all such redistributions of | ||
19 | the Apple Software. Neither the name, trademarks, service marks or logos of | ||
20 | Apple Computer, Inc. may be used to endorse or promote products derived from the | ||
21 | Apple Software without specific prior written permission from Apple. Except as | ||
22 | expressly stated in this notice, no other rights or licenses, express or implied, | ||
23 | are granted by Apple herein, including but not limited to any patent rights that | ||
24 | may be infringed by your derivative works or by other works in which the Apple | ||
25 | Software may be incorporated. | ||
26 | |||
27 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO | ||
28 | WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED | ||
29 | WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
30 | PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN | ||
31 | COMBINATION WITH YOUR PRODUCTS. | ||
32 | |||
33 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR | ||
34 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE | ||
35 | GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
36 | ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION | ||
37 | OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT | ||
38 | (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN | ||
39 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
40 | |||
41 | Copyright � 2003-2004 Apple Computer, Inc., All Rights Reserved | ||
42 | */ | ||
43 | |||
44 | #include "GenLinkedList.h" | ||
45 | |||
46 | #pragma mark --- Data Structures --- | ||
47 | |||
48 | /* This is the internal data structure for the nodes in the linked list. */ | ||
49 | /* */ | ||
50 | /* Note: The memory pointed to by pNext is owned by the list and is disposed of */ | ||
51 | /* in DestroyList. It should not be disposed of in any other way. */ | ||
52 | /* */ | ||
53 | /* Note: The memory pointed to by pData is owned by the caller of the linked */ | ||
54 | /* list. The caller is responsible for disposing of this memory. This can be */ | ||
55 | /* done by simply implementing a DisposeDataProc that will be called on each */ | ||
56 | /* node in the list, giving the caller a chance to dispose of any memory */ | ||
57 | /* created. The DisposeDataProc is called from DestroyList */ | ||
58 | struct GenNode | ||
59 | { | ||
60 | struct GenNode *pNext; /* Pointer to the next node in the list */ | ||
61 | GenDataPtr pData; /* The data for this node, owned by caller */ | ||
62 | }; | ||
63 | typedef struct GenNode GenNode; | ||
64 | |||
65 | #pragma mark --- List Implementation --- | ||
66 | |||
67 | /* Initializes the given GenLinkedList to an empty list. This MUST be */ | ||
68 | /* called before any operations are performed on the list, otherwise bad things */ | ||
69 | /* will happen. */ | ||
70 | void InitLinkedList( GenLinkedList *pList, DisposeDataProcPtr disposeProcPtr) | ||
71 | { | ||
72 | if( pList == NULL ) | ||
73 | return; | ||
74 | |||
75 | pList->pHead = pList->pTail = NULL; | ||
76 | pList->NumberOfItems = 0; | ||
77 | pList->DisposeProcPtr = disposeProcPtr; | ||
78 | } | ||
79 | |||
80 | /* returns the current number of items in the given list. */ | ||
81 | /* If pList == NULL, it returns 0 */ | ||
82 | ItemCount GetNumberOfItems( GenLinkedList *pList ) | ||
83 | { | ||
84 | return (pList) ? pList->NumberOfItems : 0; | ||
85 | } | ||
86 | |||
87 | /* Creates a new node, containing pData, and adds it to the tail of pList. */ | ||
88 | /* Note: if an error occurs, pList is unchanged. */ | ||
89 | OSErr AddToTail( GenLinkedList *pList, void *pData ) | ||
90 | { | ||
91 | OSErr err = paramErr; | ||
92 | GenNode *tmpNode = NULL; | ||
93 | |||
94 | if( pList == NULL || pData == NULL ) | ||
95 | return err; | ||
96 | |||
97 | /* create memory for new node, if this fails we _must_ bail */ | ||
98 | err = ( ( tmpNode = (GenNode*) NewPtr( sizeof( GenNode ) ) ) != NULL ) ? noErr : MemError(); | ||
99 | if( err == noErr ) | ||
100 | { | ||
101 | tmpNode->pData = pData; /* Setup new node */ | ||
102 | tmpNode->pNext = NULL; | ||
103 | |||
104 | if( pList->pTail != NULL ) /* more then one item already */ | ||
105 | ((GenNode*) pList->pTail)->pNext = (void*) tmpNode; /* so append to tail */ | ||
106 | else | ||
107 | pList->pHead = (void*) tmpNode; /* no items, so adjust head */ | ||
108 | |||
109 | pList->pTail = (void*) tmpNode; | ||
110 | pList->NumberOfItems += 1; | ||
111 | } | ||
112 | |||
113 | return err; | ||
114 | } | ||
115 | |||
116 | /* Takes pSrcList and inserts it into pDestList at the location pIter points to. */ | ||
117 | /* The lists must have the same DisposeProcPtr, but the Data can be different. If pSrcList */ | ||
118 | /* is empty, it does nothing and just returns */ | ||
119 | /* */ | ||
120 | /* If pIter == NULL, insert pSrcList before the head */ | ||
121 | /* else If pIter == pTail, append pSrcList to the tail */ | ||
122 | /* else insert pSrcList in the middle somewhere */ | ||
123 | /* On return: pSrcList is cleared and is an empty list. */ | ||
124 | /* The data that was owned by pSrcList is now owned by pDestList */ | ||
125 | void InsertList( GenLinkedList *pDestList, GenLinkedList *pSrcList, GenIteratorPtr pIter ) | ||
126 | { | ||
127 | if( pDestList == NULL || pSrcList == NULL || | ||
128 | pSrcList->pHead == NULL || pSrcList->pTail == NULL || | ||
129 | pDestList->DisposeProcPtr != pSrcList->DisposeProcPtr ) | ||
130 | return; | ||
131 | |||
132 | if( pDestList->pHead == NULL && pDestList->pTail == NULL ) /* empty list */ | ||
133 | { | ||
134 | pDestList->pHead = pSrcList->pHead; | ||
135 | pDestList->pTail = pSrcList->pTail; | ||
136 | } | ||
137 | else if( pIter == NULL ) /* insert before head */ | ||
138 | { | ||
139 | /* attach the list */ | ||
140 | ((GenNode*)pSrcList->pTail)->pNext = pDestList->pHead; | ||
141 | /* fix up head */ | ||
142 | pDestList->pHead = pSrcList->pHead; | ||
143 | } | ||
144 | else if( pIter == pDestList->pTail ) /* append to tail */ | ||
145 | { | ||
146 | /* attach the list */ | ||
147 | ((GenNode*)pDestList->pTail)->pNext = pSrcList->pHead; | ||
148 | /* fix up tail */ | ||
149 | pDestList->pTail = pSrcList->pTail; | ||
150 | } | ||
151 | else /* insert in middle somewhere */ | ||
152 | { | ||
153 | GenNode *tmpNode = ((GenNode*)pIter)->pNext; | ||
154 | ((GenNode*)pIter)->pNext = pSrcList->pHead; | ||
155 | ((GenNode*)pSrcList->pTail)->pNext = tmpNode; | ||
156 | } | ||
157 | |||
158 | pDestList->NumberOfItems += pSrcList->NumberOfItems; /* sync up NumberOfItems */ | ||
159 | |||
160 | InitLinkedList( pSrcList, NULL); /* reset the source list */ | ||
161 | } | ||
162 | |||
163 | /* Goes through the list and disposes of any memory we allocated. Calls the DisposeProcPtr,*/ | ||
164 | /* if it exists, to give the caller a chance to free up their memory */ | ||
165 | void DestroyList( GenLinkedList *pList ) | ||
166 | { | ||
167 | GenIteratorPtr pIter = NULL, | ||
168 | pNextIter = NULL; | ||
169 | |||
170 | if( pList == NULL ) | ||
171 | return; | ||
172 | |||
173 | for( InitIterator( pList, &pIter ), pNextIter = pIter; pIter != NULL; pIter = pNextIter ) | ||
174 | { | ||
175 | Next( &pNextIter ); /* get the next node before we blow away the link */ | ||
176 | |||
177 | if( pList->DisposeProcPtr != NULL ) | ||
178 | CallDisposeDataProc( pList->DisposeProcPtr, GetData( pIter ) ); | ||
179 | DisposePtr( (char*) pIter ); | ||
180 | } | ||
181 | |||
182 | InitLinkedList( pList, NULL); | ||
183 | } | ||
184 | |||
185 | /*#############################################*/ | ||
186 | /*#############################################*/ | ||
187 | /*#############################################*/ | ||
188 | |||
189 | #pragma mark - | ||
190 | #pragma mark --- Iterator Implementation --- | ||
191 | |||
192 | /* Initializes pIter to point at the head of pList */ | ||
193 | /* This must be called before performing any operations with pIter */ | ||
194 | void InitIterator( GenLinkedList *pList, GenIteratorPtr *pIter ) | ||
195 | { | ||
196 | if( pList != NULL && pIter != NULL ) | ||
197 | *pIter = pList->pHead; | ||
198 | } | ||
199 | |||
200 | /* On return, pIter points to the next node in the list. NULL if its gone */ | ||
201 | /* past the end of the list */ | ||
202 | void Next( GenIteratorPtr *pIter ) | ||
203 | { | ||
204 | if( pIter != NULL ) | ||
205 | *pIter = ((GenNode*)*pIter)->pNext; | ||
206 | } | ||
207 | |||
208 | /* Returns the data of the current node that pIter points to */ | ||
209 | GenDataPtr GetData( GenIteratorPtr pIter ) | ||
210 | { | ||
211 | return ( pIter != NULL ) ? ((GenNode*)pIter)->pData : NULL; | ||
212 | } \ No newline at end of file | ||
diff --git a/linden/indra/mac_updater/GenLinkedList.h b/linden/indra/mac_updater/GenLinkedList.h new file mode 100644 index 0000000..b5aa572 --- /dev/null +++ b/linden/indra/mac_updater/GenLinkedList.h | |||
@@ -0,0 +1,120 @@ | |||
1 | /** | ||
2 | * @file GenLinkedList.h | ||
3 | * @brief Mac OSX updater program | ||
4 | * | ||
5 | * Copyright (c) 2004-2007, Linden Research, Inc. | ||
6 | * | ||
7 | * The source code in this file ("Source Code") is provided by Linden Lab | ||
8 | * to you under the terms of the GNU General Public License, version 2.0 | ||
9 | * ("GPL"), unless you have obtained a separate licensing agreement | ||
10 | * ("Other License"), formally executed by you and Linden Lab. Terms of | ||
11 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
12 | * online at http://secondlife.com/developers/opensource/gplv2 | ||
13 | * | ||
14 | * There are special exceptions to the terms and conditions of the GPL as | ||
15 | * it is applied to this Source Code. View the full text of the exception | ||
16 | * in the file doc/FLOSS-exception.txt in this software distribution, or | ||
17 | * online at http://secondlife.com/developers/opensource/flossexception | ||
18 | * | ||
19 | * By copying, modifying or distributing this software, you acknowledge | ||
20 | * that you have read and understood your obligations described above, | ||
21 | * and agree to abide by those obligations. | ||
22 | * | ||
23 | * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
24 | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
25 | * COMPLETENESS OR PERFORMANCE. | ||
26 | */ | ||
27 | |||
28 | /* | ||
29 | File: GenLinkedList.h | ||
30 | |||
31 | Contains: Linked List utility routines prototypes | ||
32 | |||
33 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. | ||
34 | ("Apple") in consideration of your agreement to the following terms, and your | ||
35 | use, installation, modification or redistribution of this Apple software | ||
36 | constitutes acceptance of these terms. If you do not agree with these terms, | ||
37 | please do not use, install, modify or redistribute this Apple software. | ||
38 | |||
39 | In consideration of your agreement to abide by the following terms, and subject | ||
40 | to these terms, Apple grants you a personal, non-exclusive license, under Apple�s | ||
41 | copyrights in this original Apple software (the "Apple Software"), to use, | ||
42 | reproduce, modify and redistribute the Apple Software, with or without | ||
43 | modifications, in source and/or binary forms; provided that if you redistribute | ||
44 | the Apple Software in its entirety and without modifications, you must retain | ||
45 | this notice and the following text and disclaimers in all such redistributions of | ||
46 | the Apple Software. Neither the name, trademarks, service marks or logos of | ||
47 | Apple Computer, Inc. may be used to endorse or promote products derived from the | ||
48 | Apple Software without specific prior written permission from Apple. Except as | ||
49 | expressly stated in this notice, no other rights or licenses, express or implied, | ||
50 | are granted by Apple herein, including but not limited to any patent rights that | ||
51 | may be infringed by your derivative works or by other works in which the Apple | ||
52 | Software may be incorporated. | ||
53 | |||
54 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO | ||
55 | WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED | ||
56 | WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
57 | PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN | ||
58 | COMBINATION WITH YOUR PRODUCTS. | ||
59 | |||
60 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR | ||
61 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE | ||
62 | GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
63 | ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION | ||
64 | OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT | ||
65 | (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN | ||
66 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
67 | |||
68 | Copyright � 2003-2004 Apple Computer, Inc., All Rights Reserved | ||
69 | */ | ||
70 | |||
71 | |||
72 | #ifndef __GENLINKEDLIST__ | ||
73 | #define __GENLINKEDLIST__ | ||
74 | |||
75 | #ifdef __cplusplus | ||
76 | extern "C" { | ||
77 | #endif | ||
78 | |||
79 | #if TARGET_API_MAC_OSX || defined( __APPLE_CC__ ) | ||
80 | #include <CoreServices/CoreServices.h> | ||
81 | #endif | ||
82 | |||
83 | /* This is a quick, simple and generic linked list implementation. I tried */ | ||
84 | /* to setup the code so that you could use any linked list implementation you */ | ||
85 | /* want. They just need to support these few functions. */ | ||
86 | |||
87 | typedef void* GenIteratorPtr; | ||
88 | typedef void* GenDataPtr; | ||
89 | |||
90 | /* This is a callback that is called from DestroyList for each node in the */ | ||
91 | /* list. It gives the caller the oportunity to free any memory they might */ | ||
92 | /* allocated in each node. */ | ||
93 | typedef CALLBACK_API( void , DisposeDataProcPtr ) ( GenDataPtr pData ); | ||
94 | |||
95 | #define CallDisposeDataProc( userRoutine, pData ) (*(userRoutine))((pData)) | ||
96 | |||
97 | struct GenLinkedList | ||
98 | { | ||
99 | GenDataPtr pHead; /* Pointer to the head of the list */ | ||
100 | GenDataPtr pTail; /* Pointer to the tail of the list */ | ||
101 | ItemCount NumberOfItems; /* Number of items in the list (mostly for debugging) */ | ||
102 | DisposeDataProcPtr DisposeProcPtr; /* rountine called to dispose of caller data, can be NULL */ | ||
103 | }; | ||
104 | typedef struct GenLinkedList GenLinkedList; | ||
105 | |||
106 | void InitLinkedList ( GenLinkedList *pList, DisposeDataProcPtr disposeProcPtr ); | ||
107 | ItemCount GetNumberOfItems( GenLinkedList *pList ); | ||
108 | OSErr AddToTail ( GenLinkedList *pList, GenDataPtr pData ); | ||
109 | void InsertList ( GenLinkedList *pDestList, GenLinkedList *pSrcList, GenIteratorPtr pIter ); | ||
110 | void DestroyList ( GenLinkedList *pList ); | ||
111 | |||
112 | void InitIterator ( GenLinkedList *pList, GenIteratorPtr *pIter ); | ||
113 | void Next ( GenIteratorPtr *pIter ); | ||
114 | GenDataPtr GetData ( GenIteratorPtr pIter ); | ||
115 | |||
116 | #ifdef __cplusplus | ||
117 | } | ||
118 | #endif | ||
119 | |||
120 | #endif | ||
diff --git a/linden/indra/mac_updater/MoreFilesX.c b/linden/indra/mac_updater/MoreFilesX.c new file mode 100644 index 0000000..33d3424 --- /dev/null +++ b/linden/indra/mac_updater/MoreFilesX.c | |||
@@ -0,0 +1,2765 @@ | |||
1 | /* | ||
2 | File: MoreFilesX.c | ||
3 | |||
4 | Contains: A collection of useful high-level File Manager routines | ||
5 | which use the HFS Plus APIs wherever possible. | ||
6 | |||
7 | Version: MoreFilesX 1.0.1 | ||
8 | |||
9 | Copyright: � 1992-2002 by Apple Computer, Inc., all rights reserved. | ||
10 | |||
11 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. | ||
12 | ("Apple") in consideration of your agreement to the following terms, and your | ||
13 | use, installation, modification or redistribution of this Apple software | ||
14 | constitutes acceptance of these terms. If you do not agree with these terms, | ||
15 | please do not use, install, modify or redistribute this Apple software. | ||
16 | |||
17 | In consideration of your agreement to abide by the following terms, and subject | ||
18 | to these terms, Apple grants you a personal, non-exclusive license, under Apple�s | ||
19 | copyrights in this original Apple software (the "Apple Software"), to use, | ||
20 | reproduce, modify and redistribute the Apple Software, with or without | ||
21 | modifications, in source and/or binary forms; provided that if you redistribute | ||
22 | the Apple Software in its entirety and without modifications, you must retain | ||
23 | this notice and the following text and disclaimers in all such redistributions of | ||
24 | the Apple Software. Neither the name, trademarks, service marks or logos of | ||
25 | Apple Computer, Inc. may be used to endorse or promote products derived from the | ||
26 | Apple Software without specific prior written permission from Apple. Except as | ||
27 | expressly stated in this notice, no other rights or licenses, express or implied, | ||
28 | are granted by Apple herein, including but not limited to any patent rights that | ||
29 | may be infringed by your derivative works or by other works in which the Apple | ||
30 | Software may be incorporated. | ||
31 | |||
32 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO | ||
33 | WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED | ||
34 | WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
35 | PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN | ||
36 | COMBINATION WITH YOUR PRODUCTS. | ||
37 | |||
38 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR | ||
39 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE | ||
40 | GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
41 | ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION | ||
42 | OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT | ||
43 | (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN | ||
44 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
45 | |||
46 | File Ownership: | ||
47 | |||
48 | DRI: Apple Macintosh Developer Technical Support | ||
49 | |||
50 | Other Contact: For bug reports, consult the following page on | ||
51 | the World Wide Web: | ||
52 | http://developer.apple.com/bugreporter/ | ||
53 | |||
54 | Technology: DTS Sample Code | ||
55 | |||
56 | Writers: | ||
57 | |||
58 | (JL) Jim Luther | ||
59 | |||
60 | Change History (most recent first): | ||
61 | |||
62 | <4> 8/22/02 JL [3016251] Changed FSMoveRenameObjectUnicode to not use | ||
63 | the Temporary folder because it isn't available on | ||
64 | NFS volumes. | ||
65 | <3> 4/19/02 JL [2853905] Fixed #if test around header includes. | ||
66 | <2> 4/19/02 JL [2850624] Fixed C++ compile errors and Project Builder | ||
67 | warnings. | ||
68 | <2> 4/19/02 JL [2853901] Updated standard disclaimer. | ||
69 | <1> 1/25/02 JL MoreFilesX 1.0 | ||
70 | */ | ||
71 | |||
72 | #include <Carbon/Carbon.h> | ||
73 | #include <string.h> | ||
74 | |||
75 | #include "MoreFilesX.h" | ||
76 | |||
77 | /* Set BuildingMoreFilesXForMacOS9 to 1 if building for Mac OS 9 */ | ||
78 | #ifndef BuildingMoreFilesXForMacOS9 | ||
79 | #define BuildingMoreFilesXForMacOS9 0 | ||
80 | #endif | ||
81 | |||
82 | /*****************************************************************************/ | ||
83 | |||
84 | #pragma mark ----- Local type definitions ----- | ||
85 | |||
86 | struct FSIterateContainerGlobals | ||
87 | { | ||
88 | IterateContainerFilterProcPtr iterateFilter; /* pointer to IterateFilterProc */ | ||
89 | FSCatalogInfoBitmap whichInfo; /* fields of the CatalogInfo to get */ | ||
90 | FSCatalogInfo catalogInfo; /* FSCatalogInfo */ | ||
91 | FSRef ref; /* FSRef */ | ||
92 | FSSpec spec; /* FSSpec */ | ||
93 | FSSpec *specPtr; /* pointer to spec field, or NULL */ | ||
94 | HFSUniStr255 name; /* HFSUniStr255 */ | ||
95 | HFSUniStr255 *namePtr; /* pointer to name field, or NULL */ | ||
96 | void *yourDataPtr; /* a pointer to caller supplied data the filter may need to access */ | ||
97 | ItemCount maxLevels; /* maximum levels to iterate through */ | ||
98 | ItemCount currentLevel; /* the current level FSIterateContainerLevel is on */ | ||
99 | Boolean quitFlag; /* set to true if filter wants to kill interation */ | ||
100 | Boolean containerChanged; /* temporary - set to true if the current container changed during iteration */ | ||
101 | OSErr result; /* result */ | ||
102 | ItemCount actualObjects; /* number of objects returned */ | ||
103 | }; | ||
104 | typedef struct FSIterateContainerGlobals FSIterateContainerGlobals; | ||
105 | |||
106 | struct FSDeleteContainerGlobals | ||
107 | { | ||
108 | OSErr result; /* result */ | ||
109 | ItemCount actualObjects; /* number of objects returned */ | ||
110 | FSCatalogInfo catalogInfo; /* FSCatalogInfo */ | ||
111 | }; | ||
112 | typedef struct FSDeleteContainerGlobals FSDeleteContainerGlobals; | ||
113 | |||
114 | /*****************************************************************************/ | ||
115 | |||
116 | #pragma mark ----- Local prototypes ----- | ||
117 | |||
118 | static | ||
119 | void | ||
120 | FSDeleteContainerLevel( | ||
121 | const FSRef *container, | ||
122 | FSDeleteContainerGlobals *theGlobals); | ||
123 | |||
124 | static | ||
125 | void | ||
126 | FSIterateContainerLevel( | ||
127 | FSIterateContainerGlobals *theGlobals); | ||
128 | |||
129 | static | ||
130 | OSErr | ||
131 | GenerateUniqueHFSUniStr( | ||
132 | long *startSeed, | ||
133 | const FSRef *dir1, | ||
134 | const FSRef *dir2, | ||
135 | HFSUniStr255 *uniqueName); | ||
136 | |||
137 | /*****************************************************************************/ | ||
138 | |||
139 | #pragma mark ----- File Access Routines ----- | ||
140 | |||
141 | /*****************************************************************************/ | ||
142 | |||
143 | OSErr | ||
144 | FSCopyFork( | ||
145 | SInt16 srcRefNum, | ||
146 | SInt16 dstRefNum, | ||
147 | void *copyBufferPtr, | ||
148 | ByteCount copyBufferSize) | ||
149 | { | ||
150 | OSErr srcResult; | ||
151 | OSErr dstResult; | ||
152 | OSErr result; | ||
153 | SInt64 forkSize; | ||
154 | ByteCount readActualCount; | ||
155 | |||
156 | /* check input parameters */ | ||
157 | require_action((NULL != copyBufferPtr) && (0 != copyBufferSize), BadParameter, result = paramErr); | ||
158 | |||
159 | /* get source fork size */ | ||
160 | result = FSGetForkSize(srcRefNum, &forkSize); | ||
161 | require_noerr(result, SourceFSGetForkSizeFailed); | ||
162 | |||
163 | /* allocate disk space for destination fork */ | ||
164 | result = FSSetForkSize(dstRefNum, fsFromStart, forkSize); | ||
165 | require_noerr(result, DestinationFSSetForkSizeFailed); | ||
166 | |||
167 | /* reset source fork's position to 0 */ | ||
168 | result = FSSetForkPosition(srcRefNum, fsFromStart, 0); | ||
169 | require_noerr(result, SourceFSSetForkPositionFailed); | ||
170 | |||
171 | /* reset destination fork's position to 0 */ | ||
172 | result = FSSetForkPosition(dstRefNum, fsFromStart, 0); | ||
173 | require_noerr(result, DestinationFSSetForkPositionFailed); | ||
174 | |||
175 | /* If copyBufferSize is greater than 4K bytes, make it a multiple of 4k bytes */ | ||
176 | /* This will make writes on local volumes faster */ | ||
177 | if ( (copyBufferSize >= 0x00001000) && ((copyBufferSize & 0x00000fff) != 0) ) | ||
178 | { | ||
179 | copyBufferSize &= ~(0x00001000 - 1); | ||
180 | } | ||
181 | |||
182 | /* copy source to destination */ | ||
183 | srcResult = dstResult = noErr; | ||
184 | while ( (noErr == srcResult) && (noErr == dstResult) ) | ||
185 | { | ||
186 | srcResult = FSReadFork(srcRefNum, fsAtMark + noCacheMask, 0, copyBufferSize, copyBufferPtr, &readActualCount); | ||
187 | dstResult = FSWriteFork(dstRefNum, fsAtMark + noCacheMask, 0, readActualCount, copyBufferPtr, NULL); | ||
188 | } | ||
189 | |||
190 | /* make sure there were no errors at the destination */ | ||
191 | require_noerr_action(dstResult, DestinationFSWriteForkFailed, result = dstResult); | ||
192 | |||
193 | /* make sure the error at the source was eofErr */ | ||
194 | require_action(eofErr == srcResult, SourceResultNotEofErr, result = srcResult); | ||
195 | |||
196 | /* everything went as expected */ | ||
197 | result = noErr; | ||
198 | |||
199 | SourceResultNotEofErr: | ||
200 | DestinationFSWriteForkFailed: | ||
201 | DestinationFSSetForkPositionFailed: | ||
202 | SourceFSSetForkPositionFailed: | ||
203 | DestinationFSSetForkSizeFailed: | ||
204 | SourceFSGetForkSizeFailed: | ||
205 | BadParameter: | ||
206 | |||
207 | return ( result ); | ||
208 | } | ||
209 | |||
210 | /*****************************************************************************/ | ||
211 | |||
212 | #pragma mark ----- Volume Access Routines ----- | ||
213 | |||
214 | /*****************************************************************************/ | ||
215 | |||
216 | OSErr | ||
217 | FSGetVolParms( | ||
218 | FSVolumeRefNum volRefNum, | ||
219 | UInt32 bufferSize, | ||
220 | GetVolParmsInfoBuffer *volParmsInfo, | ||
221 | UInt32 *actualInfoSize) | ||
222 | { | ||
223 | OSErr result; | ||
224 | HParamBlockRec pb; | ||
225 | |||
226 | /* check parameters */ | ||
227 | require_action((NULL != volParmsInfo) && (NULL != actualInfoSize), | ||
228 | BadParameter, result = paramErr); | ||
229 | |||
230 | pb.ioParam.ioNamePtr = NULL; | ||
231 | pb.ioParam.ioVRefNum = volRefNum; | ||
232 | pb.ioParam.ioBuffer = (Ptr)volParmsInfo; | ||
233 | pb.ioParam.ioReqCount = (SInt32)bufferSize; | ||
234 | result = PBHGetVolParmsSync(&pb); | ||
235 | require_noerr(result, PBHGetVolParmsSync); | ||
236 | |||
237 | /* return number of bytes the file system returned in volParmsInfo buffer */ | ||
238 | *actualInfoSize = (UInt32)pb.ioParam.ioActCount; | ||
239 | |||
240 | PBHGetVolParmsSync: | ||
241 | BadParameter: | ||
242 | |||
243 | return ( result ); | ||
244 | } | ||
245 | |||
246 | /*****************************************************************************/ | ||
247 | |||
248 | OSErr | ||
249 | FSGetVRefNum( | ||
250 | const FSRef *ref, | ||
251 | FSVolumeRefNum *vRefNum) | ||
252 | { | ||
253 | OSErr result; | ||
254 | FSCatalogInfo catalogInfo; | ||
255 | |||
256 | /* check parameters */ | ||
257 | require_action(NULL != vRefNum, BadParameter, result = paramErr); | ||
258 | |||
259 | /* get the volume refNum from the FSRef */ | ||
260 | result = FSGetCatalogInfo(ref, kFSCatInfoVolume, &catalogInfo, NULL, NULL, NULL); | ||
261 | require_noerr(result, FSGetCatalogInfo); | ||
262 | |||
263 | /* return volume refNum from catalogInfo */ | ||
264 | *vRefNum = catalogInfo.volume; | ||
265 | |||
266 | FSGetCatalogInfo: | ||
267 | BadParameter: | ||
268 | |||
269 | return ( result ); | ||
270 | } | ||
271 | |||
272 | /*****************************************************************************/ | ||
273 | |||
274 | OSErr | ||
275 | FSGetVInfo( | ||
276 | FSVolumeRefNum volume, | ||
277 | HFSUniStr255 *volumeName, /* can be NULL */ | ||
278 | UInt64 *freeBytes, /* can be NULL */ | ||
279 | UInt64 *totalBytes) /* can be NULL */ | ||
280 | { | ||
281 | OSErr result; | ||
282 | FSVolumeInfo info; | ||
283 | |||
284 | /* ask for the volume's sizes only if needed */ | ||
285 | result = FSGetVolumeInfo(volume, 0, NULL, | ||
286 | (((NULL != freeBytes) || (NULL != totalBytes)) ? kFSVolInfoSizes : kFSVolInfoNone), | ||
287 | &info, volumeName, NULL); | ||
288 | require_noerr(result, FSGetVolumeInfo); | ||
289 | |||
290 | if ( NULL != freeBytes ) | ||
291 | { | ||
292 | *freeBytes = info.freeBytes; | ||
293 | } | ||
294 | if ( NULL != totalBytes ) | ||
295 | { | ||
296 | *totalBytes = info.totalBytes; | ||
297 | } | ||
298 | |||
299 | FSGetVolumeInfo: | ||
300 | |||
301 | return ( result ); | ||
302 | } | ||
303 | |||
304 | /*****************************************************************************/ | ||
305 | |||
306 | OSErr | ||
307 | FSGetVolFileSystemID( | ||
308 | FSVolumeRefNum volume, | ||
309 | UInt16 *fileSystemID, /* can be NULL */ | ||
310 | UInt16 *signature) /* can be NULL */ | ||
311 | { | ||
312 | OSErr result; | ||
313 | FSVolumeInfo info; | ||
314 | |||
315 | result = FSGetVolumeInfo(volume, 0, NULL, kFSVolInfoFSInfo, &info, NULL, NULL); | ||
316 | require_noerr(result, FSGetVolumeInfo); | ||
317 | |||
318 | if ( NULL != fileSystemID ) | ||
319 | { | ||
320 | *fileSystemID = info.filesystemID; | ||
321 | } | ||
322 | if ( NULL != signature ) | ||
323 | { | ||
324 | *signature = info.signature; | ||
325 | } | ||
326 | |||
327 | FSGetVolumeInfo: | ||
328 | |||
329 | return ( result ); | ||
330 | } | ||
331 | |||
332 | /*****************************************************************************/ | ||
333 | |||
334 | OSErr | ||
335 | FSGetMountedVolumes( | ||
336 | FSRef ***volumeRefsHandle, /* pointer to handle of FSRefs */ | ||
337 | ItemCount *numVolumes) | ||
338 | { | ||
339 | OSErr result; | ||
340 | OSErr memResult; | ||
341 | ItemCount volumeIndex; | ||
342 | FSRef ref; | ||
343 | |||
344 | /* check parameters */ | ||
345 | require_action((NULL != volumeRefsHandle) && (NULL != numVolumes), | ||
346 | BadParameter, result = paramErr); | ||
347 | |||
348 | /* No volumes yet */ | ||
349 | *numVolumes = 0; | ||
350 | |||
351 | /* Allocate a handle for the results */ | ||
352 | *volumeRefsHandle = (FSRef **)NewHandle(0); | ||
353 | require_action(NULL != *volumeRefsHandle, NewHandle, result = memFullErr); | ||
354 | |||
355 | /* Call FSGetVolumeInfo in loop to get all volumes starting with the first */ | ||
356 | volumeIndex = 1; | ||
357 | do | ||
358 | { | ||
359 | result = FSGetVolumeInfo(0, volumeIndex, NULL, kFSVolInfoNone, NULL, NULL, &ref); | ||
360 | if ( noErr == result ) | ||
361 | { | ||
362 | /* concatenate the FSRef to the end of the handle */ | ||
363 | PtrAndHand(&ref, (Handle)*volumeRefsHandle, sizeof(FSRef)); | ||
364 | memResult = MemError(); | ||
365 | require_noerr_action(memResult, MemoryAllocationFailed, result = memResult); | ||
366 | |||
367 | ++(*numVolumes); /* increment the volume count */ | ||
368 | ++volumeIndex; /* and the volumeIndex to get the next volume*/ | ||
369 | } | ||
370 | } while ( noErr == result ); | ||
371 | |||
372 | /* nsvErr is OK -- it just means there are no more volumes */ | ||
373 | require(nsvErr == result, FSGetVolumeInfo); | ||
374 | |||
375 | return ( noErr ); | ||
376 | |||
377 | /**********************/ | ||
378 | |||
379 | MemoryAllocationFailed: | ||
380 | FSGetVolumeInfo: | ||
381 | |||
382 | /* dispose of handle if already allocated and clear the outputs */ | ||
383 | if ( NULL != *volumeRefsHandle ) | ||
384 | { | ||
385 | DisposeHandle((Handle)*volumeRefsHandle); | ||
386 | *volumeRefsHandle = NULL; | ||
387 | } | ||
388 | *numVolumes = 0; | ||
389 | |||
390 | NewHandle: | ||
391 | BadParameter: | ||
392 | |||
393 | return ( result ); | ||
394 | } | ||
395 | |||
396 | /*****************************************************************************/ | ||
397 | |||
398 | #pragma mark ----- FSRef/FSpec/Path/Name Conversion Routines ----- | ||
399 | |||
400 | /*****************************************************************************/ | ||
401 | |||
402 | OSErr | ||
403 | FSRefMakeFSSpec( | ||
404 | const FSRef *ref, | ||
405 | FSSpec *spec) | ||
406 | { | ||
407 | OSErr result; | ||
408 | |||
409 | /* check parameters */ | ||
410 | require_action(NULL != spec, BadParameter, result = paramErr); | ||
411 | |||
412 | result = FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, NULL, spec, NULL); | ||
413 | require_noerr(result, FSGetCatalogInfo); | ||
414 | |||
415 | FSGetCatalogInfo: | ||
416 | BadParameter: | ||
417 | |||
418 | return ( result ); | ||
419 | } | ||
420 | |||
421 | /*****************************************************************************/ | ||
422 | |||
423 | OSErr | ||
424 | FSMakeFSRef( | ||
425 | FSVolumeRefNum volRefNum, | ||
426 | SInt32 dirID, | ||
427 | ConstStr255Param name, | ||
428 | FSRef *ref) | ||
429 | { | ||
430 | OSErr result; | ||
431 | FSRefParam pb; | ||
432 | |||
433 | /* check parameters */ | ||
434 | require_action(NULL != ref, BadParameter, result = paramErr); | ||
435 | |||
436 | pb.ioVRefNum = volRefNum; | ||
437 | pb.ioDirID = dirID; | ||
438 | pb.ioNamePtr = (StringPtr)name; | ||
439 | pb.newRef = ref; | ||
440 | result = PBMakeFSRefSync(&pb); | ||
441 | require_noerr(result, PBMakeFSRefSync); | ||
442 | |||
443 | PBMakeFSRefSync: | ||
444 | BadParameter: | ||
445 | |||
446 | return ( result ); | ||
447 | } | ||
448 | |||
449 | /*****************************************************************************/ | ||
450 | |||
451 | OSStatus | ||
452 | FSMakePath( | ||
453 | SInt16 volRefNum, | ||
454 | SInt32 dirID, | ||
455 | ConstStr255Param name, | ||
456 | UInt8 *path, | ||
457 | UInt32 maxPathSize) | ||
458 | { | ||
459 | OSStatus result; | ||
460 | FSRef ref; | ||
461 | |||
462 | /* check parameters */ | ||
463 | require_action(NULL != path, BadParameter, result = paramErr); | ||
464 | |||
465 | /* convert the inputs to an FSRef */ | ||
466 | result = FSMakeFSRef(volRefNum, dirID, name, &ref); | ||
467 | require_noerr(result, FSMakeFSRef); | ||
468 | |||
469 | /* and then convert the FSRef to a path */ | ||
470 | result = FSRefMakePath(&ref, path, maxPathSize); | ||
471 | require_noerr(result, FSRefMakePath); | ||
472 | |||
473 | FSRefMakePath: | ||
474 | FSMakeFSRef: | ||
475 | BadParameter: | ||
476 | |||
477 | return ( result ); | ||
478 | } | ||
479 | |||
480 | /*****************************************************************************/ | ||
481 | |||
482 | OSStatus | ||
483 | FSPathMakeFSSpec( | ||
484 | const UInt8 *path, | ||
485 | FSSpec *spec, | ||
486 | Boolean *isDirectory) /* can be NULL */ | ||
487 | { | ||
488 | OSStatus result; | ||
489 | FSRef ref; | ||
490 | |||
491 | /* check parameters */ | ||
492 | require_action(NULL != spec, BadParameter, result = paramErr); | ||
493 | |||
494 | /* convert the POSIX path to an FSRef */ | ||
495 | result = FSPathMakeRef(path, &ref, isDirectory); | ||
496 | require_noerr(result, FSPathMakeRef); | ||
497 | |||
498 | /* and then convert the FSRef to an FSSpec */ | ||
499 | result = FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, NULL, spec, NULL); | ||
500 | require_noerr(result, FSGetCatalogInfo); | ||
501 | |||
502 | FSGetCatalogInfo: | ||
503 | FSPathMakeRef: | ||
504 | BadParameter: | ||
505 | |||
506 | return ( result ); | ||
507 | } | ||
508 | |||
509 | /*****************************************************************************/ | ||
510 | |||
511 | OSErr | ||
512 | UnicodeNameGetHFSName( | ||
513 | UniCharCount nameLength, | ||
514 | const UniChar *name, | ||
515 | TextEncoding textEncodingHint, | ||
516 | Boolean isVolumeName, | ||
517 | Str31 hfsName) | ||
518 | { | ||
519 | OSStatus result; | ||
520 | ByteCount unicodeByteLength; | ||
521 | ByteCount unicodeBytesConverted; | ||
522 | ByteCount actualPascalBytes; | ||
523 | UnicodeMapping uMapping; | ||
524 | UnicodeToTextInfo utInfo; | ||
525 | |||
526 | /* check parameters */ | ||
527 | require_action(NULL != hfsName, BadParameter, result = paramErr); | ||
528 | |||
529 | /* make sure output is valid in case we get errors or there's nothing to convert */ | ||
530 | hfsName[0] = 0; | ||
531 | |||
532 | unicodeByteLength = nameLength * sizeof(UniChar); | ||
533 | if ( 0 == unicodeByteLength ) | ||
534 | { | ||
535 | /* do nothing */ | ||
536 | result = noErr; | ||
537 | } | ||
538 | else | ||
539 | { | ||
540 | /* if textEncodingHint is kTextEncodingUnknown, get a "default" textEncodingHint */ | ||
541 | if ( kTextEncodingUnknown == textEncodingHint ) | ||
542 | { | ||
543 | ScriptCode script; | ||
544 | RegionCode region; | ||
545 | |||
546 | script = (ScriptCode)GetScriptManagerVariable(smSysScript); | ||
547 | region = (RegionCode)GetScriptManagerVariable(smRegionCode); | ||
548 | result = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, region, | ||
549 | NULL, &textEncodingHint ); | ||
550 | if ( paramErr == result ) | ||
551 | { | ||
552 | /* ok, ignore the region and try again */ | ||
553 | result = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, | ||
554 | kTextRegionDontCare, NULL, &textEncodingHint ); | ||
555 | } | ||
556 | if ( noErr != result ) | ||
557 | { | ||
558 | /* ok... try something */ | ||
559 | textEncodingHint = kTextEncodingMacRoman; | ||
560 | } | ||
561 | } | ||
562 | |||
563 | uMapping.unicodeEncoding = CreateTextEncoding(kTextEncodingUnicodeV2_0, | ||
564 | kUnicodeCanonicalDecompVariant, kUnicode16BitFormat); | ||
565 | uMapping.otherEncoding = GetTextEncodingBase(textEncodingHint); | ||
566 | uMapping.mappingVersion = kUnicodeUseHFSPlusMapping; | ||
567 | |||
568 | result = CreateUnicodeToTextInfo(&uMapping, &utInfo); | ||
569 | require_noerr(result, CreateUnicodeToTextInfo); | ||
570 | |||
571 | result = ConvertFromUnicodeToText(utInfo, unicodeByteLength, name, kUnicodeLooseMappingsMask, | ||
572 | 0, NULL, 0, NULL, /* offsetCounts & offsetArrays */ | ||
573 | isVolumeName ? kHFSMaxVolumeNameChars : kHFSMaxFileNameChars, | ||
574 | &unicodeBytesConverted, &actualPascalBytes, &hfsName[1]); | ||
575 | require_noerr(result, ConvertFromUnicodeToText); | ||
576 | |||
577 | hfsName[0] = (unsigned char)actualPascalBytes; /* fill in length byte */ | ||
578 | |||
579 | ConvertFromUnicodeToText: | ||
580 | |||
581 | /* verify the result in debug builds -- there's really not anything you can do if it fails */ | ||
582 | verify_noerr(DisposeUnicodeToTextInfo(&utInfo)); | ||
583 | } | ||
584 | |||
585 | CreateUnicodeToTextInfo: | ||
586 | BadParameter: | ||
587 | |||
588 | return ( result ); | ||
589 | } | ||
590 | |||
591 | /*****************************************************************************/ | ||
592 | |||
593 | OSErr | ||
594 | HFSNameGetUnicodeName( | ||
595 | ConstStr31Param hfsName, | ||
596 | TextEncoding textEncodingHint, | ||
597 | HFSUniStr255 *unicodeName) | ||
598 | { | ||
599 | ByteCount unicodeByteLength; | ||
600 | OSStatus result; | ||
601 | UnicodeMapping uMapping; | ||
602 | TextToUnicodeInfo tuInfo; | ||
603 | ByteCount pascalCharsRead; | ||
604 | |||
605 | /* check parameters */ | ||
606 | require_action(NULL != unicodeName, BadParameter, result = paramErr); | ||
607 | |||
608 | /* make sure output is valid in case we get errors or there's nothing to convert */ | ||
609 | unicodeName->length = 0; | ||
610 | |||
611 | if ( 0 == StrLength(hfsName) ) | ||
612 | { | ||
613 | result = noErr; | ||
614 | } | ||
615 | else | ||
616 | { | ||
617 | /* if textEncodingHint is kTextEncodingUnknown, get a "default" textEncodingHint */ | ||
618 | if ( kTextEncodingUnknown == textEncodingHint ) | ||
619 | { | ||
620 | ScriptCode script; | ||
621 | RegionCode region; | ||
622 | |||
623 | script = GetScriptManagerVariable(smSysScript); | ||
624 | region = GetScriptManagerVariable(smRegionCode); | ||
625 | result = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, region, | ||
626 | NULL, &textEncodingHint); | ||
627 | if ( paramErr == result ) | ||
628 | { | ||
629 | /* ok, ignore the region and try again */ | ||
630 | result = UpgradeScriptInfoToTextEncoding(script, kTextLanguageDontCare, | ||
631 | kTextRegionDontCare, NULL, &textEncodingHint); | ||
632 | } | ||
633 | if ( noErr != result ) | ||
634 | { | ||
635 | /* ok... try something */ | ||
636 | textEncodingHint = kTextEncodingMacRoman; | ||
637 | } | ||
638 | } | ||
639 | |||
640 | uMapping.unicodeEncoding = CreateTextEncoding(kTextEncodingUnicodeV2_0, | ||
641 | kUnicodeCanonicalDecompVariant, kUnicode16BitFormat); | ||
642 | uMapping.otherEncoding = GetTextEncodingBase(textEncodingHint); | ||
643 | uMapping.mappingVersion = kUnicodeUseHFSPlusMapping; | ||
644 | |||
645 | result = CreateTextToUnicodeInfo(&uMapping, &tuInfo); | ||
646 | require_noerr(result, CreateTextToUnicodeInfo); | ||
647 | |||
648 | result = ConvertFromTextToUnicode(tuInfo, hfsName[0], &hfsName[1], | ||
649 | 0, /* no control flag bits */ | ||
650 | 0, NULL, 0, NULL, /* offsetCounts & offsetArrays */ | ||
651 | sizeof(unicodeName->unicode), /* output buffer size in bytes */ | ||
652 | &pascalCharsRead, &unicodeByteLength, unicodeName->unicode); | ||
653 | require_noerr(result, ConvertFromTextToUnicode); | ||
654 | |||
655 | /* convert from byte count to char count */ | ||
656 | unicodeName->length = unicodeByteLength / sizeof(UniChar); | ||
657 | |||
658 | ConvertFromTextToUnicode: | ||
659 | |||
660 | /* verify the result in debug builds -- there's really not anything you can do if it fails */ | ||
661 | verify_noerr(DisposeTextToUnicodeInfo(&tuInfo)); | ||
662 | } | ||
663 | |||
664 | CreateTextToUnicodeInfo: | ||
665 | BadParameter: | ||
666 | |||
667 | return ( result ); | ||
668 | } | ||
669 | |||
670 | /*****************************************************************************/ | ||
671 | |||
672 | #pragma mark ----- File/Directory Manipulation Routines ----- | ||
673 | |||
674 | /*****************************************************************************/ | ||
675 | |||
676 | Boolean FSRefValid(const FSRef *ref) | ||
677 | { | ||
678 | return ( noErr == FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, NULL, NULL, NULL) ); | ||
679 | } | ||
680 | |||
681 | /*****************************************************************************/ | ||
682 | |||
683 | OSErr | ||
684 | FSGetParentRef( | ||
685 | const FSRef *ref, | ||
686 | FSRef *parentRef) | ||
687 | { | ||
688 | OSErr result; | ||
689 | FSCatalogInfo catalogInfo; | ||
690 | |||
691 | /* check parameters */ | ||
692 | require_action(NULL != parentRef, BadParameter, result = paramErr); | ||
693 | |||
694 | result = FSGetCatalogInfo(ref, kFSCatInfoNodeID, &catalogInfo, NULL, NULL, parentRef); | ||
695 | require_noerr(result, FSGetCatalogInfo); | ||
696 | |||
697 | /* | ||
698 | * Note: FSRefs always point to real file system objects. So, there cannot | ||
699 | * be a FSRef to the parent of volume root directories. Early versions of | ||
700 | * Mac OS X do not handle this case correctly and incorrectly return a | ||
701 | * FSRef for the parent of volume root directories instead of returning an | ||
702 | * invalid FSRef (a cleared FSRef is invalid). The next three lines of code | ||
703 | * ensure that you won't run into this bug. WW9D! | ||
704 | */ | ||
705 | if ( fsRtDirID == catalogInfo.nodeID ) | ||
706 | { | ||
707 | /* clear parentRef and return noErr which is the proper behavior */ | ||
708 | memset(parentRef, 0, sizeof(FSRef)); | ||
709 | } | ||
710 | |||
711 | FSGetCatalogInfo: | ||
712 | BadParameter: | ||
713 | |||
714 | return ( result ); | ||
715 | } | ||
716 | |||
717 | /*****************************************************************************/ | ||
718 | |||
719 | OSErr | ||
720 | FSGetFileDirName( | ||
721 | const FSRef *ref, | ||
722 | HFSUniStr255 *outName) | ||
723 | { | ||
724 | OSErr result; | ||
725 | |||
726 | /* check parameters */ | ||
727 | require_action(NULL != outName, BadParameter, result = paramErr); | ||
728 | |||
729 | result = FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, outName, NULL, NULL); | ||
730 | require_noerr(result, FSGetCatalogInfo); | ||
731 | |||
732 | FSGetCatalogInfo: | ||
733 | BadParameter: | ||
734 | |||
735 | return ( result ); | ||
736 | } | ||
737 | |||
738 | /*****************************************************************************/ | ||
739 | |||
740 | OSErr | ||
741 | FSGetNodeID( | ||
742 | const FSRef *ref, | ||
743 | long *nodeID, /* can be NULL */ | ||
744 | Boolean *isDirectory) /* can be NULL */ | ||
745 | { | ||
746 | OSErr result; | ||
747 | FSCatalogInfo catalogInfo; | ||
748 | FSCatalogInfoBitmap whichInfo; | ||
749 | |||
750 | /* determine what catalog information to get */ | ||
751 | whichInfo = kFSCatInfoNone; /* start with none */ | ||
752 | if ( NULL != nodeID ) | ||
753 | { | ||
754 | whichInfo |= kFSCatInfoNodeID; | ||
755 | } | ||
756 | if ( NULL != isDirectory ) | ||
757 | { | ||
758 | whichInfo |= kFSCatInfoNodeFlags; | ||
759 | } | ||
760 | |||
761 | result = FSGetCatalogInfo(ref, whichInfo, &catalogInfo, NULL, NULL, NULL); | ||
762 | require_noerr(result, FSGetCatalogInfo); | ||
763 | |||
764 | if ( NULL != nodeID ) | ||
765 | { | ||
766 | *nodeID = catalogInfo.nodeID; | ||
767 | } | ||
768 | if ( NULL != isDirectory ) | ||
769 | { | ||
770 | *isDirectory = (0 != (kFSNodeIsDirectoryMask & catalogInfo.nodeFlags)); | ||
771 | } | ||
772 | |||
773 | FSGetCatalogInfo: | ||
774 | |||
775 | return ( result ); | ||
776 | } | ||
777 | |||
778 | /*****************************************************************************/ | ||
779 | |||
780 | OSErr | ||
781 | FSGetUserPrivilegesPermissions( | ||
782 | const FSRef *ref, | ||
783 | UInt8 *userPrivileges, /* can be NULL */ | ||
784 | UInt32 permissions[4]) /* can be NULL */ | ||
785 | { | ||
786 | OSErr result; | ||
787 | FSCatalogInfo catalogInfo; | ||
788 | FSCatalogInfoBitmap whichInfo; | ||
789 | |||
790 | /* determine what catalog information to get */ | ||
791 | whichInfo = kFSCatInfoNone; /* start with none */ | ||
792 | if ( NULL != userPrivileges ) | ||
793 | { | ||
794 | whichInfo |= kFSCatInfoUserPrivs; | ||
795 | } | ||
796 | if ( NULL != permissions ) | ||
797 | { | ||
798 | whichInfo |= kFSCatInfoPermissions; | ||
799 | } | ||
800 | |||
801 | result = FSGetCatalogInfo(ref, whichInfo, &catalogInfo, NULL, NULL, NULL); | ||
802 | require_noerr(result, FSGetCatalogInfo); | ||
803 | |||
804 | if ( NULL != userPrivileges ) | ||
805 | { | ||
806 | *userPrivileges = catalogInfo.userPrivileges; | ||
807 | } | ||
808 | if ( NULL != permissions ) | ||
809 | { | ||
810 | BlockMoveData(&catalogInfo.permissions, permissions, sizeof(UInt32) * 4); | ||
811 | } | ||
812 | |||
813 | FSGetCatalogInfo: | ||
814 | |||
815 | return ( result ); | ||
816 | } | ||
817 | |||
818 | /*****************************************************************************/ | ||
819 | |||
820 | OSErr | ||
821 | FSCheckLock( | ||
822 | const FSRef *ref) | ||
823 | { | ||
824 | OSErr result; | ||
825 | FSCatalogInfo catalogInfo; | ||
826 | FSVolumeInfo volumeInfo; | ||
827 | |||
828 | /* get nodeFlags and vRefNum for container */ | ||
829 | result = FSGetCatalogInfo(ref, kFSCatInfoNodeFlags + kFSCatInfoVolume, &catalogInfo, NULL, NULL,NULL); | ||
830 | require_noerr(result, FSGetCatalogInfo); | ||
831 | |||
832 | /* is file locked? */ | ||
833 | if ( 0 != (catalogInfo.nodeFlags & kFSNodeLockedMask) ) | ||
834 | { | ||
835 | result = fLckdErr; /* file is locked */ | ||
836 | } | ||
837 | else | ||
838 | { | ||
839 | /* file isn't locked, but is volume locked? */ | ||
840 | |||
841 | /* get volume flags */ | ||
842 | result = FSGetVolumeInfo(catalogInfo.volume, 0, NULL, kFSVolInfoFlags, &volumeInfo, NULL, NULL); | ||
843 | require_noerr(result, FSGetVolumeInfo); | ||
844 | |||
845 | if ( 0 != (volumeInfo.flags & kFSVolFlagHardwareLockedMask) ) | ||
846 | { | ||
847 | result = wPrErr; /* volume locked by hardware */ | ||
848 | } | ||
849 | else if ( 0 != (volumeInfo.flags & kFSVolFlagSoftwareLockedMask) ) | ||
850 | { | ||
851 | result = vLckdErr; /* volume locked by software */ | ||
852 | } | ||
853 | } | ||
854 | |||
855 | FSGetVolumeInfo: | ||
856 | FSGetCatalogInfo: | ||
857 | |||
858 | return ( result ); | ||
859 | } | ||
860 | |||
861 | /*****************************************************************************/ | ||
862 | |||
863 | OSErr | ||
864 | FSGetForkSizes( | ||
865 | const FSRef *ref, | ||
866 | UInt64 *dataLogicalSize, /* can be NULL */ | ||
867 | UInt64 *rsrcLogicalSize) /* can be NULL */ | ||
868 | { | ||
869 | OSErr result; | ||
870 | FSCatalogInfoBitmap whichInfo; | ||
871 | FSCatalogInfo catalogInfo; | ||
872 | |||
873 | whichInfo = kFSCatInfoNodeFlags; | ||
874 | if ( NULL != dataLogicalSize ) | ||
875 | { | ||
876 | /* get data fork size */ | ||
877 | whichInfo |= kFSCatInfoDataSizes; | ||
878 | } | ||
879 | if ( NULL != rsrcLogicalSize ) | ||
880 | { | ||
881 | /* get resource fork size */ | ||
882 | whichInfo |= kFSCatInfoRsrcSizes; | ||
883 | } | ||
884 | |||
885 | /* get nodeFlags and catalog info */ | ||
886 | result = FSGetCatalogInfo(ref, whichInfo, &catalogInfo, NULL, NULL,NULL); | ||
887 | require_noerr(result, FSGetCatalogInfo); | ||
888 | |||
889 | /* make sure FSRef was to a file */ | ||
890 | require_action(0 == (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask), FSRefNotFile, result = notAFileErr); | ||
891 | |||
892 | if ( NULL != dataLogicalSize ) | ||
893 | { | ||
894 | /* return data fork size */ | ||
895 | *dataLogicalSize = catalogInfo.dataLogicalSize; | ||
896 | } | ||
897 | if ( NULL != rsrcLogicalSize ) | ||
898 | { | ||
899 | /* return resource fork size */ | ||
900 | *rsrcLogicalSize = catalogInfo.rsrcLogicalSize; | ||
901 | } | ||
902 | |||
903 | FSRefNotFile: | ||
904 | FSGetCatalogInfo: | ||
905 | |||
906 | return ( result ); | ||
907 | } | ||
908 | |||
909 | /*****************************************************************************/ | ||
910 | |||
911 | OSErr | ||
912 | FSGetTotalForkSizes( | ||
913 | const FSRef *ref, | ||
914 | UInt64 *totalLogicalSize, /* can be NULL */ | ||
915 | UInt64 *totalPhysicalSize, /* can be NULL */ | ||
916 | ItemCount *forkCount) /* can be NULL */ | ||
917 | { | ||
918 | OSErr result; | ||
919 | CatPositionRec forkIterator; | ||
920 | SInt64 forkSize; | ||
921 | SInt64 *forkSizePtr; | ||
922 | UInt64 forkPhysicalSize; | ||
923 | UInt64 *forkPhysicalSizePtr; | ||
924 | |||
925 | /* Determine if forkSize needed */ | ||
926 | if ( NULL != totalLogicalSize) | ||
927 | { | ||
928 | *totalLogicalSize = 0; | ||
929 | forkSizePtr = &forkSize; | ||
930 | } | ||
931 | else | ||
932 | { | ||
933 | forkSizePtr = NULL; | ||
934 | } | ||
935 | |||
936 | /* Determine if forkPhysicalSize is needed */ | ||
937 | if ( NULL != totalPhysicalSize ) | ||
938 | { | ||
939 | *totalPhysicalSize = 0; | ||
940 | forkPhysicalSizePtr = &forkPhysicalSize; | ||
941 | } | ||
942 | else | ||
943 | { | ||
944 | forkPhysicalSizePtr = NULL; | ||
945 | } | ||
946 | |||
947 | /* zero fork count if returning it */ | ||
948 | if ( NULL != forkCount ) | ||
949 | { | ||
950 | *forkCount = 0; | ||
951 | } | ||
952 | |||
953 | /* Iterate through the forks to get the sizes */ | ||
954 | forkIterator.initialize = 0; | ||
955 | do | ||
956 | { | ||
957 | result = FSIterateForks(ref, &forkIterator, NULL, forkSizePtr, forkPhysicalSizePtr); | ||
958 | if ( noErr == result ) | ||
959 | { | ||
960 | if ( NULL != totalLogicalSize ) | ||
961 | { | ||
962 | *totalLogicalSize += forkSize; | ||
963 | } | ||
964 | |||
965 | if ( NULL != totalPhysicalSize ) | ||
966 | { | ||
967 | *totalPhysicalSize += forkPhysicalSize; | ||
968 | } | ||
969 | |||
970 | if ( NULL != forkCount ) | ||
971 | { | ||
972 | ++*forkCount; | ||
973 | } | ||
974 | } | ||
975 | } while ( noErr == result ); | ||
976 | |||
977 | /* any error result other than errFSNoMoreItems is serious */ | ||
978 | require(errFSNoMoreItems == result, FSIterateForks); | ||
979 | |||
980 | /* Normal exit */ | ||
981 | result = noErr; | ||
982 | |||
983 | FSIterateForks: | ||
984 | |||
985 | return ( result ); | ||
986 | } | ||
987 | |||
988 | /*****************************************************************************/ | ||
989 | |||
990 | OSErr | ||
991 | FSBumpDate( | ||
992 | const FSRef *ref) | ||
993 | { | ||
994 | OSStatus result; | ||
995 | FSCatalogInfo catalogInfo; | ||
996 | UTCDateTime oldDateTime; | ||
997 | #if !BuildingMoreFilesXForMacOS9 | ||
998 | FSRef parentRef; | ||
999 | Boolean notifyParent; | ||
1000 | #endif | ||
1001 | |||
1002 | #if !BuildingMoreFilesXForMacOS9 | ||
1003 | /* Get the node flags, the content modification date and time, and the parent ref */ | ||
1004 | result = FSGetCatalogInfo(ref, kFSCatInfoNodeFlags + kFSCatInfoContentMod, &catalogInfo, NULL, NULL, &parentRef); | ||
1005 | require_noerr(result, FSGetCatalogInfo); | ||
1006 | |||
1007 | /* Notify the parent if this is a file */ | ||
1008 | notifyParent = (0 == (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask)); | ||
1009 | #else | ||
1010 | /* Get the content modification date and time */ | ||
1011 | result = FSGetCatalogInfo(ref, kFSCatInfoContentMod, &catalogInfo, NULL, NULL, NULL); | ||
1012 | require_noerr(result, FSGetCatalogInfo); | ||
1013 | #endif | ||
1014 | |||
1015 | oldDateTime = catalogInfo.contentModDate; | ||
1016 | |||
1017 | /* Get the current date and time */ | ||
1018 | result = GetUTCDateTime(&catalogInfo.contentModDate, kUTCDefaultOptions); | ||
1019 | require_noerr(result, GetUTCDateTime); | ||
1020 | |||
1021 | /* if the old date and time is the the same as the current, bump the seconds by one */ | ||
1022 | if ( (catalogInfo.contentModDate.fraction == oldDateTime.fraction) && | ||
1023 | (catalogInfo.contentModDate.lowSeconds == oldDateTime.lowSeconds) && | ||
1024 | (catalogInfo.contentModDate.highSeconds == oldDateTime.highSeconds) ) | ||
1025 | { | ||
1026 | ++catalogInfo.contentModDate.lowSeconds; | ||
1027 | if ( 0 == catalogInfo.contentModDate.lowSeconds ) | ||
1028 | { | ||
1029 | ++catalogInfo.contentModDate.highSeconds; | ||
1030 | } | ||
1031 | } | ||
1032 | |||
1033 | /* Bump the content modification date and time */ | ||
1034 | result = FSSetCatalogInfo(ref, kFSCatInfoContentMod, &catalogInfo); | ||
1035 | require_noerr(result, FSSetCatalogInfo); | ||
1036 | |||
1037 | #if !BuildingMoreFilesXForMacOS9 | ||
1038 | /* | ||
1039 | * The problem with FNNotify is that it is not available under Mac OS 9 | ||
1040 | * and there's no way to test for that except for looking for the symbol | ||
1041 | * or something. So, I'll just conditionalize this for those who care | ||
1042 | * to send a notification. | ||
1043 | */ | ||
1044 | |||
1045 | /* Send a notification for the parent of the file, or for the directory */ | ||
1046 | result = FNNotify(notifyParent ? &parentRef : ref, kFNDirectoryModifiedMessage, kNilOptions); | ||
1047 | require_noerr(result, FNNotify); | ||
1048 | #endif | ||
1049 | |||
1050 | /* ignore errors from FSSetCatalogInfo (volume might be write protected) and FNNotify */ | ||
1051 | FNNotify: | ||
1052 | FSSetCatalogInfo: | ||
1053 | |||
1054 | return ( noErr ); | ||
1055 | |||
1056 | /**********************/ | ||
1057 | |||
1058 | GetUTCDateTime: | ||
1059 | FSGetCatalogInfo: | ||
1060 | |||
1061 | return ( result ); | ||
1062 | } | ||
1063 | |||
1064 | /*****************************************************************************/ | ||
1065 | |||
1066 | OSErr | ||
1067 | FSGetFinderInfo( | ||
1068 | const FSRef *ref, | ||
1069 | FinderInfo *info, /* can be NULL */ | ||
1070 | ExtendedFinderInfo *extendedInfo, /* can be NULL */ | ||
1071 | Boolean *isDirectory) /* can be NULL */ | ||
1072 | { | ||
1073 | OSErr result; | ||
1074 | FSCatalogInfo catalogInfo; | ||
1075 | FSCatalogInfoBitmap whichInfo; | ||
1076 | |||
1077 | /* determine what catalog information is really needed */ | ||
1078 | whichInfo = kFSCatInfoNone; | ||
1079 | |||
1080 | if ( NULL != info ) | ||
1081 | { | ||
1082 | /* get FinderInfo */ | ||
1083 | whichInfo |= kFSCatInfoFinderInfo; | ||
1084 | } | ||
1085 | |||
1086 | if ( NULL != extendedInfo ) | ||
1087 | { | ||
1088 | /* get ExtendedFinderInfo */ | ||
1089 | whichInfo |= kFSCatInfoFinderXInfo; | ||
1090 | } | ||
1091 | |||
1092 | if ( NULL != isDirectory ) | ||
1093 | { | ||
1094 | whichInfo |= kFSCatInfoNodeFlags; | ||
1095 | } | ||
1096 | |||
1097 | result = FSGetCatalogInfo(ref, whichInfo, &catalogInfo, NULL, NULL, NULL); | ||
1098 | require_noerr(result, FSGetCatalogInfo); | ||
1099 | |||
1100 | /* return FinderInfo if requested */ | ||
1101 | if ( NULL != info ) | ||
1102 | { | ||
1103 | BlockMoveData(catalogInfo.finderInfo, info, sizeof(FinderInfo)); | ||
1104 | } | ||
1105 | |||
1106 | /* return ExtendedFinderInfo if requested */ | ||
1107 | if ( NULL != extendedInfo) | ||
1108 | { | ||
1109 | BlockMoveData(catalogInfo.extFinderInfo, extendedInfo, sizeof(ExtendedFinderInfo)); | ||
1110 | } | ||
1111 | |||
1112 | /* set isDirectory Boolean if requested */ | ||
1113 | if ( NULL != isDirectory) | ||
1114 | { | ||
1115 | *isDirectory = (0 != (kFSNodeIsDirectoryMask & catalogInfo.nodeFlags)); | ||
1116 | } | ||
1117 | |||
1118 | FSGetCatalogInfo: | ||
1119 | |||
1120 | return ( result ); | ||
1121 | } | ||
1122 | |||
1123 | /*****************************************************************************/ | ||
1124 | |||
1125 | OSErr | ||
1126 | FSSetFinderInfo( | ||
1127 | const FSRef *ref, | ||
1128 | const FinderInfo *info, | ||
1129 | const ExtendedFinderInfo *extendedInfo) | ||
1130 | { | ||
1131 | OSErr result; | ||
1132 | FSCatalogInfo catalogInfo; | ||
1133 | FSCatalogInfoBitmap whichInfo; | ||
1134 | |||
1135 | /* determine what catalog information will be set */ | ||
1136 | whichInfo = kFSCatInfoNone; /* start with none */ | ||
1137 | if ( NULL != info ) | ||
1138 | { | ||
1139 | /* set FinderInfo */ | ||
1140 | whichInfo |= kFSCatInfoFinderInfo; | ||
1141 | BlockMoveData(info, catalogInfo.finderInfo, sizeof(FinderInfo)); | ||
1142 | } | ||
1143 | if ( NULL != extendedInfo ) | ||
1144 | { | ||
1145 | /* set ExtendedFinderInfo */ | ||
1146 | whichInfo |= kFSCatInfoFinderXInfo; | ||
1147 | BlockMoveData(extendedInfo, catalogInfo.extFinderInfo, sizeof(ExtendedFinderInfo)); | ||
1148 | } | ||
1149 | |||
1150 | result = FSSetCatalogInfo(ref, whichInfo, &catalogInfo); | ||
1151 | require_noerr(result, FSGetCatalogInfo); | ||
1152 | |||
1153 | FSGetCatalogInfo: | ||
1154 | |||
1155 | return ( result ); | ||
1156 | } | ||
1157 | |||
1158 | /*****************************************************************************/ | ||
1159 | |||
1160 | OSErr | ||
1161 | FSChangeCreatorType( | ||
1162 | const FSRef *ref, | ||
1163 | OSType fileCreator, | ||
1164 | OSType fileType) | ||
1165 | { | ||
1166 | OSErr result; | ||
1167 | FSCatalogInfo catalogInfo; | ||
1168 | FSRef parentRef; | ||
1169 | |||
1170 | /* get nodeFlags, finder info, and parent FSRef */ | ||
1171 | result = FSGetCatalogInfo(ref, kFSCatInfoNodeFlags + kFSCatInfoFinderInfo, &catalogInfo , NULL, NULL, &parentRef); | ||
1172 | require_noerr(result, FSGetCatalogInfo); | ||
1173 | |||
1174 | /* make sure FSRef was to a file */ | ||
1175 | require_action(0 == (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask), FSRefNotFile, result = notAFileErr); | ||
1176 | |||
1177 | /* If fileType not 0x00000000, change fileType */ | ||
1178 | if ( fileType != (OSType)0x00000000 ) | ||
1179 | { | ||
1180 | ((FileInfo *)&catalogInfo.finderInfo)->fileType = fileType; | ||
1181 | } | ||
1182 | |||
1183 | /* If creator not 0x00000000, change creator */ | ||
1184 | if ( fileCreator != (OSType)0x00000000 ) | ||
1185 | { | ||
1186 | ((FileInfo *)&catalogInfo.finderInfo)->fileCreator = fileCreator; | ||
1187 | } | ||
1188 | |||
1189 | /* now, save the new information back to disk */ | ||
1190 | result = FSSetCatalogInfo(ref, kFSCatInfoFinderInfo, &catalogInfo); | ||
1191 | require_noerr(result, FSSetCatalogInfo); | ||
1192 | |||
1193 | /* and attempt to bump the parent directory's mod date to wake up */ | ||
1194 | /* the Finder to the change we just made (ignore errors from this) */ | ||
1195 | verify_noerr(FSBumpDate(&parentRef)); | ||
1196 | |||
1197 | FSSetCatalogInfo: | ||
1198 | FSRefNotFile: | ||
1199 | FSGetCatalogInfo: | ||
1200 | |||
1201 | return ( result ); | ||
1202 | } | ||
1203 | |||
1204 | /*****************************************************************************/ | ||
1205 | |||
1206 | OSErr | ||
1207 | FSChangeFinderFlags( | ||
1208 | const FSRef *ref, | ||
1209 | Boolean setBits, | ||
1210 | UInt16 flagBits) | ||
1211 | { | ||
1212 | OSErr result; | ||
1213 | FSCatalogInfo catalogInfo; | ||
1214 | FSRef parentRef; | ||
1215 | |||
1216 | /* get the current finderInfo */ | ||
1217 | result = FSGetCatalogInfo(ref, kFSCatInfoFinderInfo, &catalogInfo, NULL, NULL, &parentRef); | ||
1218 | require_noerr(result, FSGetCatalogInfo); | ||
1219 | |||
1220 | /* set or clear the appropriate bits in the finderInfo.finderFlags */ | ||
1221 | if ( setBits ) | ||
1222 | { | ||
1223 | /* OR in the bits */ | ||
1224 | ((FileInfo *)&catalogInfo.finderInfo)->finderFlags |= flagBits; | ||
1225 | } | ||
1226 | else | ||
1227 | { | ||
1228 | /* AND out the bits */ | ||
1229 | ((FileInfo *)&catalogInfo.finderInfo)->finderFlags &= ~flagBits; | ||
1230 | } | ||
1231 | |||
1232 | /* save the modified finderInfo */ | ||
1233 | result = FSSetCatalogInfo(ref, kFSCatInfoFinderInfo, &catalogInfo); | ||
1234 | require_noerr(result, FSSetCatalogInfo); | ||
1235 | |||
1236 | /* and attempt to bump the parent directory's mod date to wake up the Finder */ | ||
1237 | /* to the change we just made (ignore errors from this) */ | ||
1238 | verify_noerr(FSBumpDate(&parentRef)); | ||
1239 | |||
1240 | FSSetCatalogInfo: | ||
1241 | FSGetCatalogInfo: | ||
1242 | |||
1243 | return ( result ); | ||
1244 | } | ||
1245 | |||
1246 | /*****************************************************************************/ | ||
1247 | |||
1248 | OSErr | ||
1249 | FSSetInvisible( | ||
1250 | const FSRef *ref) | ||
1251 | { | ||
1252 | return ( FSChangeFinderFlags(ref, true, kIsInvisible) ); | ||
1253 | } | ||
1254 | |||
1255 | OSErr | ||
1256 | FSClearInvisible( | ||
1257 | const FSRef *ref) | ||
1258 | { | ||
1259 | return ( FSChangeFinderFlags(ref, false, kIsInvisible) ); | ||
1260 | } | ||
1261 | |||
1262 | /*****************************************************************************/ | ||
1263 | |||
1264 | OSErr | ||
1265 | FSSetNameLocked( | ||
1266 | const FSRef *ref) | ||
1267 | { | ||
1268 | return ( FSChangeFinderFlags(ref, true, kNameLocked) ); | ||
1269 | } | ||
1270 | |||
1271 | OSErr | ||
1272 | FSClearNameLocked( | ||
1273 | const FSRef *ref) | ||
1274 | { | ||
1275 | return ( FSChangeFinderFlags(ref, false, kNameLocked) ); | ||
1276 | } | ||
1277 | |||
1278 | /*****************************************************************************/ | ||
1279 | |||
1280 | OSErr | ||
1281 | FSSetIsStationery( | ||
1282 | const FSRef *ref) | ||
1283 | { | ||
1284 | return ( FSChangeFinderFlags(ref, true, kIsStationery) ); | ||
1285 | } | ||
1286 | |||
1287 | OSErr | ||
1288 | FSClearIsStationery( | ||
1289 | const FSRef *ref) | ||
1290 | { | ||
1291 | return ( FSChangeFinderFlags(ref, false, kIsStationery) ); | ||
1292 | } | ||
1293 | |||
1294 | /*****************************************************************************/ | ||
1295 | |||
1296 | OSErr | ||
1297 | FSSetHasCustomIcon( | ||
1298 | const FSRef *ref) | ||
1299 | { | ||
1300 | return ( FSChangeFinderFlags(ref, true, kHasCustomIcon) ); | ||
1301 | } | ||
1302 | |||
1303 | OSErr | ||
1304 | FSClearHasCustomIcon( | ||
1305 | const FSRef *ref) | ||
1306 | { | ||
1307 | return ( FSChangeFinderFlags(ref, false, kHasCustomIcon) ); | ||
1308 | } | ||
1309 | |||
1310 | /*****************************************************************************/ | ||
1311 | |||
1312 | OSErr | ||
1313 | FSClearHasBeenInited( | ||
1314 | const FSRef *ref) | ||
1315 | { | ||
1316 | return ( FSChangeFinderFlags(ref, false, kHasBeenInited) ); | ||
1317 | } | ||
1318 | |||
1319 | /*****************************************************************************/ | ||
1320 | |||
1321 | OSErr | ||
1322 | FSCopyFileMgrAttributes( | ||
1323 | const FSRef *sourceRef, | ||
1324 | const FSRef *destinationRef, | ||
1325 | Boolean copyLockBit) | ||
1326 | { | ||
1327 | OSErr result; | ||
1328 | FSCatalogInfo catalogInfo; | ||
1329 | |||
1330 | /* get the source information */ | ||
1331 | result = FSGetCatalogInfo(sourceRef, kFSCatInfoSettableInfo, &catalogInfo, NULL, NULL, NULL); | ||
1332 | require_noerr(result, FSGetCatalogInfo); | ||
1333 | |||
1334 | /* don't copy the hasBeenInited bit; clear it */ | ||
1335 | ((FileInfo *)&catalogInfo.finderInfo)->finderFlags &= ~kHasBeenInited; | ||
1336 | |||
1337 | /* should the locked bit be copied? */ | ||
1338 | if ( !copyLockBit ) | ||
1339 | { | ||
1340 | /* no, make sure the locked bit is clear */ | ||
1341 | catalogInfo.nodeFlags &= ~kFSNodeLockedMask; | ||
1342 | } | ||
1343 | |||
1344 | /* set the destination information */ | ||
1345 | result = FSSetCatalogInfo(destinationRef, kFSCatInfoSettableInfo, &catalogInfo); | ||
1346 | require_noerr(result, FSSetCatalogInfo); | ||
1347 | |||
1348 | FSSetCatalogInfo: | ||
1349 | FSGetCatalogInfo: | ||
1350 | |||
1351 | return ( result ); | ||
1352 | } | ||
1353 | |||
1354 | /*****************************************************************************/ | ||
1355 | |||
1356 | OSErr | ||
1357 | FSMoveRenameObjectUnicode( | ||
1358 | const FSRef *ref, | ||
1359 | const FSRef *destDirectory, | ||
1360 | UniCharCount nameLength, | ||
1361 | const UniChar *name, /* can be NULL (no rename during move) */ | ||
1362 | TextEncoding textEncodingHint, | ||
1363 | FSRef *newRef) /* if function fails along the way, newRef is final location of file */ | ||
1364 | { | ||
1365 | OSErr result; | ||
1366 | FSVolumeRefNum vRefNum; | ||
1367 | FSCatalogInfo catalogInfo; | ||
1368 | FSRef originalDirectory; | ||
1369 | TextEncoding originalTextEncodingHint; | ||
1370 | HFSUniStr255 originalName; | ||
1371 | HFSUniStr255 uniqueName; /* unique name given to object while moving it to destination */ | ||
1372 | long theSeed; /* the seed for generating unique names */ | ||
1373 | |||
1374 | /* check parameters */ | ||
1375 | require_action(NULL != newRef, BadParameter, result = paramErr); | ||
1376 | |||
1377 | /* newRef = input to start with */ | ||
1378 | BlockMoveData(ref, newRef, sizeof(FSRef)); | ||
1379 | |||
1380 | /* get destDirectory's vRefNum */ | ||
1381 | result = FSGetCatalogInfo(destDirectory, kFSCatInfoVolume, &catalogInfo, NULL, NULL, NULL); | ||
1382 | require_noerr(result, DestinationBad); | ||
1383 | |||
1384 | /* save vRefNum */ | ||
1385 | vRefNum = catalogInfo.volume; | ||
1386 | |||
1387 | /* get ref's vRefNum, TextEncoding, name and parent directory*/ | ||
1388 | result = FSGetCatalogInfo(ref, kFSCatInfoTextEncoding + kFSCatInfoVolume, &catalogInfo, &originalName, NULL, &originalDirectory); | ||
1389 | require_noerr(result, SourceBad); | ||
1390 | |||
1391 | /* save TextEncoding */ | ||
1392 | originalTextEncodingHint = catalogInfo.textEncodingHint; | ||
1393 | |||
1394 | /* make sure ref and destDirectory are on same volume */ | ||
1395 | require_action(vRefNum == catalogInfo.volume, NotSameVolume, result = diffVolErr); | ||
1396 | |||
1397 | /* Skip a few steps if we're not renaming */ | ||
1398 | if ( NULL != name ) | ||
1399 | { | ||
1400 | /* generate a name that is unique in both directories */ | ||
1401 | theSeed = 0x4a696d4c; /* a fine unlikely filename */ | ||
1402 | |||
1403 | result = GenerateUniqueHFSUniStr(&theSeed, &originalDirectory, destDirectory, &uniqueName); | ||
1404 | require_noerr(result, GenerateUniqueHFSUniStrFailed); | ||
1405 | |||
1406 | /* Rename the object to uniqueName */ | ||
1407 | result = FSRenameUnicode(ref, uniqueName.length, uniqueName.unicode, kTextEncodingUnknown, newRef); | ||
1408 | require_noerr(result, FSRenameUnicodeBeforeMoveFailed); | ||
1409 | |||
1410 | /* Move object to its new home */ | ||
1411 | result = FSMoveObject(newRef, destDirectory, newRef); | ||
1412 | require_noerr(result, FSMoveObjectAfterRenameFailed); | ||
1413 | |||
1414 | /* Rename the object to new name */ | ||
1415 | result = FSRenameUnicode(ref, nameLength, name, textEncodingHint, newRef); | ||
1416 | require_noerr(result, FSRenameUnicodeAfterMoveFailed); | ||
1417 | } | ||
1418 | else | ||
1419 | { | ||
1420 | /* Move object to its new home */ | ||
1421 | result = FSMoveObject(newRef, destDirectory, newRef); | ||
1422 | require_noerr(result, FSMoveObjectNoRenameFailed); | ||
1423 | } | ||
1424 | |||
1425 | return ( result ); | ||
1426 | |||
1427 | /*************/ | ||
1428 | |||
1429 | /* | ||
1430 | * failure handling code when renaming | ||
1431 | */ | ||
1432 | |||
1433 | FSRenameUnicodeAfterMoveFailed: | ||
1434 | |||
1435 | /* Error handling: move object back to original location - ignore errors */ | ||
1436 | verify_noerr(FSMoveObject(newRef, &originalDirectory, newRef)); | ||
1437 | |||
1438 | FSMoveObjectAfterRenameFailed: | ||
1439 | |||
1440 | /* Error handling: rename object back to original name - ignore errors */ | ||
1441 | verify_noerr(FSRenameUnicode(newRef, originalName.length, originalName.unicode, originalTextEncodingHint, newRef)); | ||
1442 | |||
1443 | FSRenameUnicodeBeforeMoveFailed: | ||
1444 | GenerateUniqueHFSUniStrFailed: | ||
1445 | |||
1446 | /* | ||
1447 | * failure handling code for renaming or not | ||
1448 | */ | ||
1449 | FSMoveObjectNoRenameFailed: | ||
1450 | NotSameVolume: | ||
1451 | SourceBad: | ||
1452 | DestinationBad: | ||
1453 | BadParameter: | ||
1454 | |||
1455 | return ( result ); | ||
1456 | } | ||
1457 | |||
1458 | /*****************************************************************************/ | ||
1459 | |||
1460 | /* | ||
1461 | The FSDeleteContainerLevel function deletes the contents of a container | ||
1462 | directory. All files and subdirectories in the specified container are | ||
1463 | deleted. If a locked file or directory is encountered, it is unlocked | ||
1464 | and then deleted. If any unexpected errors are encountered, | ||
1465 | FSDeleteContainerLevel quits and returns to the caller. | ||
1466 | |||
1467 | container --> FSRef to a directory. | ||
1468 | theGlobals --> A pointer to a FSDeleteContainerGlobals struct | ||
1469 | which contains the variables that do not need to | ||
1470 | be allocated each time FSDeleteContainerLevel | ||
1471 | recurses. That lets FSDeleteContainerLevel use | ||
1472 | less stack space per recursion level. | ||
1473 | */ | ||
1474 | |||
1475 | static | ||
1476 | void | ||
1477 | FSDeleteContainerLevel( | ||
1478 | const FSRef *container, | ||
1479 | FSDeleteContainerGlobals *theGlobals) | ||
1480 | { | ||
1481 | /* level locals */ | ||
1482 | FSIterator iterator; | ||
1483 | FSRef itemToDelete; | ||
1484 | UInt16 nodeFlags; | ||
1485 | |||
1486 | /* Open FSIterator for flat access and give delete optimization hint */ | ||
1487 | theGlobals->result = FSOpenIterator(container, kFSIterateFlat + kFSIterateDelete, &iterator); | ||
1488 | require_noerr(theGlobals->result, FSOpenIterator); | ||
1489 | |||
1490 | /* delete the contents of the directory */ | ||
1491 | do | ||
1492 | { | ||
1493 | /* get 1 item to delete */ | ||
1494 | theGlobals->result = FSGetCatalogInfoBulk(iterator, 1, &theGlobals->actualObjects, | ||
1495 | NULL, kFSCatInfoNodeFlags, &theGlobals->catalogInfo, | ||
1496 | &itemToDelete, NULL, NULL); | ||
1497 | if ( (noErr == theGlobals->result) && (1 == theGlobals->actualObjects) ) | ||
1498 | { | ||
1499 | /* save node flags in local in case we have to recurse */ | ||
1500 | nodeFlags = theGlobals->catalogInfo.nodeFlags; | ||
1501 | |||
1502 | /* is it a file or directory? */ | ||
1503 | if ( 0 != (nodeFlags & kFSNodeIsDirectoryMask) ) | ||
1504 | { | ||
1505 | /* it's a directory -- delete its contents before attempting to delete it */ | ||
1506 | FSDeleteContainerLevel(&itemToDelete, theGlobals); | ||
1507 | } | ||
1508 | /* are we still OK to delete? */ | ||
1509 | if ( noErr == theGlobals->result ) | ||
1510 | { | ||
1511 | /* is item locked? */ | ||
1512 | if ( 0 != (nodeFlags & kFSNodeLockedMask) ) | ||
1513 | { | ||
1514 | /* then attempt to unlock it (ignore result since FSDeleteObject will set it correctly) */ | ||
1515 | theGlobals->catalogInfo.nodeFlags = nodeFlags & ~kFSNodeLockedMask; | ||
1516 | (void) FSSetCatalogInfo(&itemToDelete, kFSCatInfoNodeFlags, &theGlobals->catalogInfo); | ||
1517 | } | ||
1518 | /* delete the item */ | ||
1519 | theGlobals->result = FSDeleteObject(&itemToDelete); | ||
1520 | } | ||
1521 | } | ||
1522 | } while ( noErr == theGlobals->result ); | ||
1523 | |||
1524 | /* we found the end of the items normally, so return noErr */ | ||
1525 | if ( errFSNoMoreItems == theGlobals->result ) | ||
1526 | { | ||
1527 | theGlobals->result = noErr; | ||
1528 | } | ||
1529 | |||
1530 | /* close the FSIterator (closing an open iterator should never fail) */ | ||
1531 | verify_noerr(FSCloseIterator(iterator)); | ||
1532 | |||
1533 | FSOpenIterator: | ||
1534 | |||
1535 | return; | ||
1536 | } | ||
1537 | |||
1538 | /*****************************************************************************/ | ||
1539 | |||
1540 | OSErr | ||
1541 | FSDeleteContainerContents( | ||
1542 | const FSRef *container) | ||
1543 | { | ||
1544 | FSDeleteContainerGlobals theGlobals; | ||
1545 | |||
1546 | /* delete container's contents */ | ||
1547 | FSDeleteContainerLevel(container, &theGlobals); | ||
1548 | |||
1549 | return ( theGlobals.result ); | ||
1550 | } | ||
1551 | |||
1552 | /*****************************************************************************/ | ||
1553 | |||
1554 | OSErr | ||
1555 | FSDeleteContainer( | ||
1556 | const FSRef *container) | ||
1557 | { | ||
1558 | OSErr result; | ||
1559 | FSCatalogInfo catalogInfo; | ||
1560 | |||
1561 | /* get nodeFlags for container */ | ||
1562 | result = FSGetCatalogInfo(container, kFSCatInfoNodeFlags, &catalogInfo, NULL, NULL,NULL); | ||
1563 | require_noerr(result, FSGetCatalogInfo); | ||
1564 | |||
1565 | /* make sure container is a directory */ | ||
1566 | require_action(0 != (catalogInfo.nodeFlags & kFSNodeIsDirectoryMask), ContainerNotDirectory, result = dirNFErr); | ||
1567 | |||
1568 | /* delete container's contents */ | ||
1569 | result = FSDeleteContainerContents(container); | ||
1570 | require_noerr(result, FSDeleteContainerContents); | ||
1571 | |||
1572 | /* is container locked? */ | ||
1573 | if ( 0 != (catalogInfo.nodeFlags & kFSNodeLockedMask) ) | ||
1574 | { | ||
1575 | /* then attempt to unlock container (ignore result since FSDeleteObject will set it correctly) */ | ||
1576 | catalogInfo.nodeFlags &= ~kFSNodeLockedMask; | ||
1577 | (void) FSSetCatalogInfo(container, kFSCatInfoNodeFlags, &catalogInfo); | ||
1578 | } | ||
1579 | |||
1580 | /* delete the container */ | ||
1581 | result = FSDeleteObject(container); | ||
1582 | |||
1583 | FSDeleteContainerContents: | ||
1584 | ContainerNotDirectory: | ||
1585 | FSGetCatalogInfo: | ||
1586 | |||
1587 | return ( result ); | ||
1588 | } | ||
1589 | |||
1590 | /*****************************************************************************/ | ||
1591 | |||
1592 | /* | ||
1593 | The FSIterateContainerLevel function iterates the contents of a container | ||
1594 | directory and calls a IterateContainerFilterProc function once for each | ||
1595 | file and directory found. | ||
1596 | |||
1597 | theGlobals --> A pointer to a FSIterateContainerGlobals struct | ||
1598 | which contains the variables needed globally by | ||
1599 | all recusion levels of FSIterateContainerLevel. | ||
1600 | That makes FSIterateContainer thread safe since | ||
1601 | each call to it uses its own global world. | ||
1602 | It also contains the variables that do not need | ||
1603 | to be allocated each time FSIterateContainerLevel | ||
1604 | recurses. That lets FSIterateContainerLevel use | ||
1605 | less stack space per recursion level. | ||
1606 | */ | ||
1607 | |||
1608 | static | ||
1609 | void | ||
1610 | FSIterateContainerLevel( | ||
1611 | FSIterateContainerGlobals *theGlobals) | ||
1612 | { | ||
1613 | FSIterator iterator; | ||
1614 | |||
1615 | /* If maxLevels is zero, we aren't checking levels */ | ||
1616 | /* If currentLevel < maxLevels, look at this level */ | ||
1617 | if ( (theGlobals->maxLevels == 0) || | ||
1618 | (theGlobals->currentLevel < theGlobals->maxLevels) ) | ||
1619 | { | ||
1620 | /* Open FSIterator for flat access to theGlobals->ref */ | ||
1621 | theGlobals->result = FSOpenIterator(&theGlobals->ref, kFSIterateFlat, &iterator); | ||
1622 | require_noerr(theGlobals->result, FSOpenIterator); | ||
1623 | |||
1624 | ++theGlobals->currentLevel; /* Go to next level */ | ||
1625 | |||
1626 | /* Call FSGetCatalogInfoBulk in loop to get all items in the container */ | ||
1627 | do | ||
1628 | { | ||
1629 | theGlobals->result = FSGetCatalogInfoBulk(iterator, 1, &theGlobals->actualObjects, | ||
1630 | &theGlobals->containerChanged, theGlobals->whichInfo, &theGlobals->catalogInfo, | ||
1631 | &theGlobals->ref, theGlobals->specPtr, theGlobals->namePtr); | ||
1632 | if ( (noErr == theGlobals->result || errFSNoMoreItems == theGlobals->result) && | ||
1633 | (0 != theGlobals->actualObjects) ) | ||
1634 | { | ||
1635 | /* Call the IterateFilterProc */ | ||
1636 | theGlobals->quitFlag = CallIterateContainerFilterProc(theGlobals->iterateFilter, | ||
1637 | theGlobals->containerChanged, theGlobals->currentLevel, | ||
1638 | &theGlobals->catalogInfo, &theGlobals->ref, | ||
1639 | theGlobals->specPtr, theGlobals->namePtr, theGlobals->yourDataPtr); | ||
1640 | /* Is it a directory? */ | ||
1641 | if ( 0 != (theGlobals->catalogInfo.nodeFlags & kFSNodeIsDirectoryMask) ) | ||
1642 | { | ||
1643 | /* Keep going? */ | ||
1644 | if ( !theGlobals->quitFlag ) | ||
1645 | { | ||
1646 | /* Dive again if the IterateFilterProc didn't say "quit" */ | ||
1647 | FSIterateContainerLevel(theGlobals); | ||
1648 | } | ||
1649 | } | ||
1650 | } | ||
1651 | /* time to fall back a level? */ | ||
1652 | } while ( (noErr == theGlobals->result) && (!theGlobals->quitFlag) ); | ||
1653 | |||
1654 | /* errFSNoMoreItems is OK - it only means we hit the end of this level */ | ||
1655 | /* afpAccessDenied is OK, too - it only means we cannot see inside a directory */ | ||
1656 | if ( (errFSNoMoreItems == theGlobals->result) || | ||
1657 | (afpAccessDenied == theGlobals->result) ) | ||
1658 | { | ||
1659 | theGlobals->result = noErr; | ||
1660 | } | ||
1661 | |||
1662 | --theGlobals->currentLevel; /* Return to previous level as we leave */ | ||
1663 | |||
1664 | /* Close the FSIterator (closing an open iterator should never fail) */ | ||
1665 | verify_noerr(FSCloseIterator(iterator)); | ||
1666 | } | ||
1667 | |||
1668 | FSOpenIterator: | ||
1669 | |||
1670 | return; | ||
1671 | } | ||
1672 | |||
1673 | /*****************************************************************************/ | ||
1674 | |||
1675 | OSErr | ||
1676 | FSIterateContainer( | ||
1677 | const FSRef *container, | ||
1678 | ItemCount maxLevels, | ||
1679 | FSCatalogInfoBitmap whichInfo, | ||
1680 | Boolean wantFSSpec, | ||
1681 | Boolean wantName, | ||
1682 | IterateContainerFilterProcPtr iterateFilter, | ||
1683 | void *yourDataPtr) | ||
1684 | { | ||
1685 | OSErr result; | ||
1686 | FSIterateContainerGlobals theGlobals; | ||
1687 | |||
1688 | /* make sure there is an iterateFilter */ | ||
1689 | require_action(iterateFilter != NULL, NoIterateFilter, result = paramErr); | ||
1690 | |||
1691 | /* | ||
1692 | * set up the globals we need to access from the recursive routine | ||
1693 | */ | ||
1694 | theGlobals.iterateFilter = iterateFilter; | ||
1695 | /* we need the node flags no matter what was requested so we can detect files vs. directories */ | ||
1696 | theGlobals.whichInfo = whichInfo | kFSCatInfoNodeFlags; | ||
1697 | /* start with input container -- the first OpenIterator will ensure it is a directory */ | ||
1698 | theGlobals.ref = *container; | ||
1699 | if ( wantFSSpec ) | ||
1700 | { | ||
1701 | theGlobals.specPtr = &theGlobals.spec; | ||
1702 | } | ||
1703 | else | ||
1704 | { | ||
1705 | theGlobals.specPtr = NULL; | ||
1706 | } | ||
1707 | if ( wantName ) | ||
1708 | { | ||
1709 | theGlobals.namePtr = &theGlobals.name; | ||
1710 | } | ||
1711 | else | ||
1712 | { | ||
1713 | theGlobals.namePtr = NULL; | ||
1714 | } | ||
1715 | theGlobals.yourDataPtr = yourDataPtr; | ||
1716 | theGlobals.maxLevels = maxLevels; | ||
1717 | theGlobals.currentLevel = 0; | ||
1718 | theGlobals.quitFlag = false; | ||
1719 | theGlobals.containerChanged = false; | ||
1720 | theGlobals.result = noErr; | ||
1721 | theGlobals.actualObjects = 0; | ||
1722 | |||
1723 | /* here we go into recursion land... */ | ||
1724 | FSIterateContainerLevel(&theGlobals); | ||
1725 | result = theGlobals.result; | ||
1726 | require_noerr(result, FSIterateContainerLevel); | ||
1727 | |||
1728 | FSIterateContainerLevel: | ||
1729 | NoIterateFilter: | ||
1730 | |||
1731 | return ( result ); | ||
1732 | } | ||
1733 | |||
1734 | /*****************************************************************************/ | ||
1735 | |||
1736 | OSErr | ||
1737 | FSGetDirectoryItems( | ||
1738 | const FSRef *container, | ||
1739 | FSRef ***refsHandle, /* pointer to handle of FSRefs */ | ||
1740 | ItemCount *numRefs, | ||
1741 | Boolean *containerChanged) | ||
1742 | { | ||
1743 | /* Grab items 10 at a time. */ | ||
1744 | enum { kMaxItemsPerBulkCall = 10 }; | ||
1745 | |||
1746 | OSErr result; | ||
1747 | OSErr memResult; | ||
1748 | FSIterator iterator; | ||
1749 | FSRef refs[kMaxItemsPerBulkCall]; | ||
1750 | ItemCount actualObjects; | ||
1751 | Boolean changed; | ||
1752 | |||
1753 | /* check parameters */ | ||
1754 | require_action((NULL != refsHandle) && (NULL != numRefs) && (NULL != containerChanged), | ||
1755 | BadParameter, result = paramErr); | ||
1756 | |||
1757 | *numRefs = 0; | ||
1758 | *containerChanged = false; | ||
1759 | *refsHandle = (FSRef **)NewHandle(0); | ||
1760 | require_action(NULL != *refsHandle, NewHandle, result = memFullErr); | ||
1761 | |||
1762 | /* open an FSIterator */ | ||
1763 | result = FSOpenIterator(container, kFSIterateFlat, &iterator); | ||
1764 | require_noerr(result, FSOpenIterator); | ||
1765 | |||
1766 | /* Call FSGetCatalogInfoBulk in loop to get all items in the container */ | ||
1767 | do | ||
1768 | { | ||
1769 | result = FSGetCatalogInfoBulk(iterator, kMaxItemsPerBulkCall, &actualObjects, | ||
1770 | &changed, kFSCatInfoNone, NULL, refs, NULL, NULL); | ||
1771 | |||
1772 | /* if the container changed, set containerChanged for output, but keep going */ | ||
1773 | if ( changed ) | ||
1774 | { | ||
1775 | *containerChanged = changed; | ||
1776 | } | ||
1777 | |||
1778 | /* any result other than noErr and errFSNoMoreItems is serious */ | ||
1779 | require((noErr == result) || (errFSNoMoreItems == result), FSGetCatalogInfoBulk); | ||
1780 | |||
1781 | /* add objects to output array and count */ | ||
1782 | if ( 0 != actualObjects ) | ||
1783 | { | ||
1784 | /* concatenate the FSRefs to the end of the handle */ | ||
1785 | PtrAndHand(refs, (Handle)*refsHandle, actualObjects * sizeof(FSRef)); | ||
1786 | memResult = MemError(); | ||
1787 | require_noerr_action(memResult, MemoryAllocationFailed, result = memResult); | ||
1788 | |||
1789 | *numRefs += actualObjects; | ||
1790 | } | ||
1791 | } while ( noErr == result ); | ||
1792 | |||
1793 | verify_noerr(FSCloseIterator(iterator)); /* closing an open iterator should never fail, but... */ | ||
1794 | |||
1795 | return ( noErr ); | ||
1796 | |||
1797 | /**********************/ | ||
1798 | |||
1799 | MemoryAllocationFailed: | ||
1800 | FSGetCatalogInfoBulk: | ||
1801 | |||
1802 | /* close the iterator */ | ||
1803 | verify_noerr(FSCloseIterator(iterator)); | ||
1804 | |||
1805 | FSOpenIterator: | ||
1806 | /* dispose of handle if already allocated and clear the outputs */ | ||
1807 | if ( NULL != *refsHandle ) | ||
1808 | { | ||
1809 | DisposeHandle((Handle)*refsHandle); | ||
1810 | *refsHandle = NULL; | ||
1811 | } | ||
1812 | *numRefs = 0; | ||
1813 | |||
1814 | NewHandle: | ||
1815 | BadParameter: | ||
1816 | |||
1817 | return ( result ); | ||
1818 | } | ||
1819 | |||
1820 | /*****************************************************************************/ | ||
1821 | |||
1822 | /* | ||
1823 | The GenerateUniqueName function generates a HFSUniStr255 name that is | ||
1824 | unique in both dir1 and dir2. | ||
1825 | |||
1826 | startSeed --> A pointer to a long which is used to generate the | ||
1827 | unique name. | ||
1828 | <-- It is modified on output to a value which should | ||
1829 | be used to generate the next unique name. | ||
1830 | dir1 --> The first directory. | ||
1831 | dir2 --> The second directory. | ||
1832 | uniqueName <-- A pointer to a HFSUniStr255 where the unique name | ||
1833 | is to be returned. | ||
1834 | */ | ||
1835 | |||
1836 | static | ||
1837 | OSErr | ||
1838 | GenerateUniqueHFSUniStr( | ||
1839 | long *startSeed, | ||
1840 | const FSRef *dir1, | ||
1841 | const FSRef *dir2, | ||
1842 | HFSUniStr255 *uniqueName) | ||
1843 | { | ||
1844 | OSErr result; | ||
1845 | long i; | ||
1846 | FSRefParam pb; | ||
1847 | FSRef newRef; | ||
1848 | unsigned char hexStr[17] = "0123456789ABCDEF"; | ||
1849 | |||
1850 | /* set up the parameter block */ | ||
1851 | pb.name = uniqueName->unicode; | ||
1852 | pb.nameLength = 8; /* always 8 characters */ | ||
1853 | pb.textEncodingHint = kTextEncodingUnknown; | ||
1854 | pb.newRef = &newRef; | ||
1855 | |||
1856 | /* loop until we get fnfErr with a filename in both directories */ | ||
1857 | result = noErr; | ||
1858 | while ( fnfErr != result ) | ||
1859 | { | ||
1860 | /* convert startSeed to 8 character Unicode string */ | ||
1861 | uniqueName->length = 8; | ||
1862 | for ( i = 0; i < 8; ++i ) | ||
1863 | { | ||
1864 | uniqueName->unicode[i] = hexStr[((*startSeed >> ((7-i)*4)) & 0xf)]; | ||
1865 | } | ||
1866 | |||
1867 | /* try in dir1 */ | ||
1868 | pb.ref = dir1; | ||
1869 | result = PBMakeFSRefUnicodeSync(&pb); | ||
1870 | if ( fnfErr == result ) | ||
1871 | { | ||
1872 | /* try in dir2 */ | ||
1873 | pb.ref = dir2; | ||
1874 | result = PBMakeFSRefUnicodeSync(&pb); | ||
1875 | if ( fnfErr != result ) | ||
1876 | { | ||
1877 | /* exit if anything other than noErr or fnfErr */ | ||
1878 | require_noerr(result, Dir2PBMakeFSRefUnicodeSyncFailed); | ||
1879 | } | ||
1880 | } | ||
1881 | else | ||
1882 | { | ||
1883 | /* exit if anything other than noErr or fnfErr */ | ||
1884 | require_noerr(result, Dir1PBMakeFSRefUnicodeSyncFailed); | ||
1885 | } | ||
1886 | |||
1887 | /* increment seed for next pass through loop, */ | ||
1888 | /* or for next call to GenerateUniqueHFSUniStr */ | ||
1889 | ++(*startSeed); | ||
1890 | } | ||
1891 | |||
1892 | /* we have a unique file name which doesn't exist in dir1 or dir2 */ | ||
1893 | result = noErr; | ||
1894 | |||
1895 | Dir2PBMakeFSRefUnicodeSyncFailed: | ||
1896 | Dir1PBMakeFSRefUnicodeSyncFailed: | ||
1897 | |||
1898 | return ( result ); | ||
1899 | } | ||
1900 | |||
1901 | /*****************************************************************************/ | ||
1902 | |||
1903 | OSErr | ||
1904 | FSExchangeObjectsCompat( | ||
1905 | const FSRef *sourceRef, | ||
1906 | const FSRef *destRef, | ||
1907 | FSRef *newSourceRef, | ||
1908 | FSRef *newDestRef) | ||
1909 | { | ||
1910 | enum | ||
1911 | { | ||
1912 | /* get all settable info except for mod dates, plus the volume refNum and parent directory ID */ | ||
1913 | kGetCatInformationMask = (kFSCatInfoSettableInfo | | ||
1914 | kFSCatInfoVolume | | ||
1915 | kFSCatInfoParentDirID) & | ||
1916 | ~(kFSCatInfoContentMod | kFSCatInfoAttrMod), | ||
1917 | /* set everything possible except for mod dates */ | ||
1918 | kSetCatinformationMask = kFSCatInfoSettableInfo & | ||
1919 | ~(kFSCatInfoContentMod | kFSCatInfoAttrMod) | ||
1920 | }; | ||
1921 | |||
1922 | OSErr result; | ||
1923 | GetVolParmsInfoBuffer volParmsInfo; | ||
1924 | UInt32 infoSize; | ||
1925 | FSCatalogInfo sourceCatalogInfo; /* source file's catalog information */ | ||
1926 | FSCatalogInfo destCatalogInfo; /* destination file's catalog information */ | ||
1927 | HFSUniStr255 sourceName; /* source file's Unicode name */ | ||
1928 | HFSUniStr255 destName; /* destination file's Unicode name */ | ||
1929 | FSRef sourceCurrentRef; /* FSRef to current location of source file throughout this function */ | ||
1930 | FSRef destCurrentRef; /* FSRef to current location of destination file throughout this function */ | ||
1931 | FSRef sourceParentRef; /* FSRef to parent directory of source file */ | ||
1932 | FSRef destParentRef; /* FSRef to parent directory of destination file */ | ||
1933 | HFSUniStr255 sourceUniqueName; /* unique name given to source file while exchanging it with destination */ | ||
1934 | HFSUniStr255 destUniqueName; /* unique name given to destination file while exchanging it with source */ | ||
1935 | long theSeed; /* the seed for generating unique names */ | ||
1936 | Boolean sameParentDirs; /* true if source and destinatin parent directory is the same */ | ||
1937 | |||
1938 | /* check parameters */ | ||
1939 | require_action((NULL != newSourceRef) && (NULL != newDestRef), BadParameter, result = paramErr); | ||
1940 | |||
1941 | /* output refs and current refs = input refs to start with */ | ||
1942 | BlockMoveData(sourceRef, newSourceRef, sizeof(FSRef)); | ||
1943 | BlockMoveData(sourceRef, &sourceCurrentRef, sizeof(FSRef)); | ||
1944 | |||
1945 | BlockMoveData(destRef, newDestRef, sizeof(FSRef)); | ||
1946 | BlockMoveData(destRef, &destCurrentRef, sizeof(FSRef)); | ||
1947 | |||
1948 | /* get source volume's vRefNum */ | ||
1949 | result = FSGetCatalogInfo(&sourceCurrentRef, kFSCatInfoVolume, &sourceCatalogInfo, NULL, NULL, NULL); | ||
1950 | require_noerr(result, DetermineSourceVRefNumFailed); | ||
1951 | |||
1952 | /* see if that volume supports FSExchangeObjects */ | ||
1953 | result = FSGetVolParms(sourceCatalogInfo.volume, sizeof(GetVolParmsInfoBuffer), | ||
1954 | &volParmsInfo, &infoSize); | ||
1955 | if ( (noErr == result) && VolSupportsFSExchangeObjects(&volParmsInfo) ) | ||
1956 | { | ||
1957 | /* yes - use FSExchangeObjects */ | ||
1958 | result = FSExchangeObjects(sourceRef, destRef); | ||
1959 | } | ||
1960 | else | ||
1961 | { | ||
1962 | /* no - emulate FSExchangeObjects */ | ||
1963 | |||
1964 | /* Note: The compatibility case won't work for files with *Btree control blocks. */ | ||
1965 | /* Right now the only *Btree files are created by the system. */ | ||
1966 | |||
1967 | /* get all catalog information and Unicode names for each file */ | ||
1968 | result = FSGetCatalogInfo(&sourceCurrentRef, kGetCatInformationMask, &sourceCatalogInfo, &sourceName, NULL, &sourceParentRef); | ||
1969 | require_noerr(result, SourceFSGetCatalogInfoFailed); | ||
1970 | |||
1971 | result = FSGetCatalogInfo(&destCurrentRef, kGetCatInformationMask, &destCatalogInfo, &destName, NULL, &destParentRef); | ||
1972 | require_noerr(result, DestFSGetCatalogInfoFailed); | ||
1973 | |||
1974 | /* make sure source and destination are on same volume */ | ||
1975 | require_action(sourceCatalogInfo.volume == destCatalogInfo.volume, NotSameVolume, result = diffVolErr); | ||
1976 | |||
1977 | /* make sure both files are *really* files */ | ||
1978 | require_action((0 == (sourceCatalogInfo.nodeFlags & kFSNodeIsDirectoryMask)) && | ||
1979 | (0 == (destCatalogInfo.nodeFlags & kFSNodeIsDirectoryMask)), NotAFile, result = notAFileErr); | ||
1980 | |||
1981 | /* generate 2 names that are unique in both directories */ | ||
1982 | theSeed = 0x4a696d4c; /* a fine unlikely filename */ | ||
1983 | |||
1984 | result = GenerateUniqueHFSUniStr(&theSeed, &sourceParentRef, &destParentRef, &sourceUniqueName); | ||
1985 | require_noerr(result, GenerateUniqueHFSUniStr1Failed); | ||
1986 | |||
1987 | result = GenerateUniqueHFSUniStr(&theSeed, &sourceParentRef, &destParentRef, &destUniqueName); | ||
1988 | require_noerr(result, GenerateUniqueHFSUniStr2Failed); | ||
1989 | |||
1990 | /* rename sourceCurrentRef to sourceUniqueName */ | ||
1991 | result = FSRenameUnicode(&sourceCurrentRef, sourceUniqueName.length, sourceUniqueName.unicode, kTextEncodingUnknown, newSourceRef); | ||
1992 | require_noerr(result, FSRenameUnicode1Failed); | ||
1993 | BlockMoveData(newSourceRef, &sourceCurrentRef, sizeof(FSRef)); | ||
1994 | |||
1995 | /* rename destCurrentRef to destUniqueName */ | ||
1996 | result = FSRenameUnicode(&destCurrentRef, destUniqueName.length, destUniqueName.unicode, kTextEncodingUnknown, newDestRef); | ||
1997 | require_noerr(result, FSRenameUnicode2Failed); | ||
1998 | BlockMoveData(newDestRef, &destCurrentRef, sizeof(FSRef)); | ||
1999 | |||
2000 | /* are the source and destination parent directories the same? */ | ||
2001 | sameParentDirs = ( sourceCatalogInfo.parentDirID == destCatalogInfo.parentDirID ); | ||
2002 | if ( !sameParentDirs ) | ||
2003 | { | ||
2004 | /* move source file to dest parent directory */ | ||
2005 | result = FSMoveObject(&sourceCurrentRef, &destParentRef, newSourceRef); | ||
2006 | require_noerr(result, FSMoveObject1Failed); | ||
2007 | BlockMoveData(newSourceRef, &sourceCurrentRef, sizeof(FSRef)); | ||
2008 | |||
2009 | /* move dest file to source parent directory */ | ||
2010 | result = FSMoveObject(&destCurrentRef, &sourceParentRef, newDestRef); | ||
2011 | require_noerr(result, FSMoveObject2Failed); | ||
2012 | BlockMoveData(newDestRef, &destCurrentRef, sizeof(FSRef)); | ||
2013 | } | ||
2014 | |||
2015 | /* At this point, the files are in their new locations (if they were moved). */ | ||
2016 | /* The source file is named sourceUniqueName and is in the directory referred to */ | ||
2017 | /* by destParentRef. The destination file is named destUniqueName and is in the */ | ||
2018 | /* directory referred to by sourceParentRef. */ | ||
2019 | |||
2020 | /* give source file the dest file's catalog information except for mod dates */ | ||
2021 | result = FSSetCatalogInfo(&sourceCurrentRef, kSetCatinformationMask, &destCatalogInfo); | ||
2022 | require_noerr(result, FSSetCatalogInfo1Failed); | ||
2023 | |||
2024 | /* give dest file the source file's catalog information except for mod dates */ | ||
2025 | result = FSSetCatalogInfo(&destCurrentRef, kSetCatinformationMask, &sourceCatalogInfo); | ||
2026 | require_noerr(result, FSSetCatalogInfo2Failed); | ||
2027 | |||
2028 | /* rename source file with dest file's name */ | ||
2029 | result = FSRenameUnicode(&sourceCurrentRef, destName.length, destName.unicode, destCatalogInfo.textEncodingHint, newSourceRef); | ||
2030 | require_noerr(result, FSRenameUnicode3Failed); | ||
2031 | BlockMoveData(newSourceRef, &sourceCurrentRef, sizeof(FSRef)); | ||
2032 | |||
2033 | /* rename dest file with source file's name */ | ||
2034 | result = FSRenameUnicode(&destCurrentRef, sourceName.length, sourceName.unicode, sourceCatalogInfo.textEncodingHint, newDestRef); | ||
2035 | require_noerr(result, FSRenameUnicode4Failed); | ||
2036 | |||
2037 | /* we're done with no errors, so swap newSourceRef and newDestRef */ | ||
2038 | BlockMoveData(newDestRef, newSourceRef, sizeof(FSRef)); | ||
2039 | BlockMoveData(&sourceCurrentRef, newDestRef, sizeof(FSRef)); | ||
2040 | } | ||
2041 | |||
2042 | return ( result ); | ||
2043 | |||
2044 | /**********************/ | ||
2045 | |||
2046 | /* If there are any failures while emulating FSExchangeObjects, attempt to reverse any steps */ | ||
2047 | /* already taken. In any case, newSourceRef and newDestRef will refer to the files in whatever */ | ||
2048 | /* state and location they ended up in so that both files can be found by the calling code. */ | ||
2049 | |||
2050 | FSRenameUnicode4Failed: | ||
2051 | |||
2052 | /* attempt to rename source file to sourceUniqueName */ | ||
2053 | if ( noErr == FSRenameUnicode(&sourceCurrentRef, sourceUniqueName.length, sourceUniqueName.unicode, kTextEncodingUnknown, newSourceRef) ) | ||
2054 | { | ||
2055 | BlockMoveData(newSourceRef, &sourceCurrentRef, sizeof(FSRef)); | ||
2056 | } | ||
2057 | |||
2058 | FSRenameUnicode3Failed: | ||
2059 | |||
2060 | /* attempt to restore dest file's catalog information */ | ||
2061 | verify_noerr(FSSetCatalogInfo(&destCurrentRef, kFSCatInfoSettableInfo, &destCatalogInfo)); | ||
2062 | |||
2063 | FSSetCatalogInfo2Failed: | ||
2064 | |||
2065 | /* attempt to restore source file's catalog information */ | ||
2066 | verify_noerr(FSSetCatalogInfo(&sourceCurrentRef, kFSCatInfoSettableInfo, &sourceCatalogInfo)); | ||
2067 | |||
2068 | FSSetCatalogInfo1Failed: | ||
2069 | |||
2070 | if ( !sameParentDirs ) | ||
2071 | { | ||
2072 | /* attempt to move dest file back to dest directory */ | ||
2073 | if ( noErr == FSMoveObject(&destCurrentRef, &destParentRef, newDestRef) ) | ||
2074 | { | ||
2075 | BlockMoveData(newDestRef, &destCurrentRef, sizeof(FSRef)); | ||
2076 | } | ||
2077 | } | ||
2078 | |||
2079 | FSMoveObject2Failed: | ||
2080 | |||
2081 | if ( !sameParentDirs ) | ||
2082 | { | ||
2083 | /* attempt to move source file back to source directory */ | ||
2084 | if ( noErr == FSMoveObject(&sourceCurrentRef, &sourceParentRef, newSourceRef) ) | ||
2085 | { | ||
2086 | BlockMoveData(newSourceRef, &sourceCurrentRef, sizeof(FSRef)); | ||
2087 | } | ||
2088 | } | ||
2089 | |||
2090 | FSMoveObject1Failed: | ||
2091 | |||
2092 | /* attempt to rename dest file to original name */ | ||
2093 | verify_noerr(FSRenameUnicode(&destCurrentRef, destName.length, destName.unicode, destCatalogInfo.textEncodingHint, newDestRef)); | ||
2094 | |||
2095 | FSRenameUnicode2Failed: | ||
2096 | |||
2097 | /* attempt to rename source file to original name */ | ||
2098 | verify_noerr(FSRenameUnicode(&sourceCurrentRef, sourceName.length, sourceName.unicode, sourceCatalogInfo.textEncodingHint, newSourceRef)); | ||
2099 | |||
2100 | FSRenameUnicode1Failed: | ||
2101 | GenerateUniqueHFSUniStr2Failed: | ||
2102 | GenerateUniqueHFSUniStr1Failed: | ||
2103 | NotAFile: | ||
2104 | NotSameVolume: | ||
2105 | DestFSGetCatalogInfoFailed: | ||
2106 | SourceFSGetCatalogInfoFailed: | ||
2107 | DetermineSourceVRefNumFailed: | ||
2108 | BadParameter: | ||
2109 | |||
2110 | return ( result ); | ||
2111 | } | ||
2112 | |||
2113 | /*****************************************************************************/ | ||
2114 | |||
2115 | #pragma mark ----- Shared Environment Routines ----- | ||
2116 | |||
2117 | /*****************************************************************************/ | ||
2118 | |||
2119 | OSErr | ||
2120 | MoreFiles_FSLockRange( | ||
2121 | SInt16 refNum, | ||
2122 | SInt32 rangeLength, | ||
2123 | SInt32 rangeStart) | ||
2124 | { | ||
2125 | OSErr result; | ||
2126 | ParamBlockRec pb; | ||
2127 | |||
2128 | pb.ioParam.ioRefNum = refNum; | ||
2129 | pb.ioParam.ioReqCount = rangeLength; | ||
2130 | pb.ioParam.ioPosMode = fsFromStart; | ||
2131 | pb.ioParam.ioPosOffset = rangeStart; | ||
2132 | result = PBLockRangeSync(&pb); | ||
2133 | require_noerr(result, PBLockRangeSync); | ||
2134 | |||
2135 | PBLockRangeSync: | ||
2136 | |||
2137 | return ( result ); | ||
2138 | } | ||
2139 | |||
2140 | /*****************************************************************************/ | ||
2141 | |||
2142 | OSErr | ||
2143 | MoreFiles_FSUnlockRange( | ||
2144 | SInt16 refNum, | ||
2145 | SInt32 rangeLength, | ||
2146 | SInt32 rangeStart) | ||
2147 | { | ||
2148 | OSErr result; | ||
2149 | ParamBlockRec pb; | ||
2150 | |||
2151 | pb.ioParam.ioRefNum = refNum; | ||
2152 | pb.ioParam.ioReqCount = rangeLength; | ||
2153 | pb.ioParam.ioPosMode = fsFromStart; | ||
2154 | pb.ioParam.ioPosOffset = rangeStart; | ||
2155 | result = PBUnlockRangeSync(&pb); | ||
2156 | require_noerr(result, PBUnlockRangeSync); | ||
2157 | |||
2158 | PBUnlockRangeSync: | ||
2159 | |||
2160 | return ( result ); | ||
2161 | } | ||
2162 | |||
2163 | /*****************************************************************************/ | ||
2164 | |||
2165 | OSErr | ||
2166 | FSGetDirAccess( | ||
2167 | const FSRef *ref, | ||
2168 | SInt32 *ownerID, /* can be NULL */ | ||
2169 | SInt32 *groupID, /* can be NULL */ | ||
2170 | SInt32 *accessRights) /* can be NULL */ | ||
2171 | { | ||
2172 | OSErr result; | ||
2173 | FSSpec spec; | ||
2174 | HParamBlockRec pb; | ||
2175 | |||
2176 | /* get FSSpec from FSRef */ | ||
2177 | result = FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, NULL, &spec, NULL); | ||
2178 | require_noerr(result, FSGetCatalogInfo); | ||
2179 | |||
2180 | /* get directory access info for FSSpec */ | ||
2181 | pb.accessParam.ioNamePtr = (StringPtr)spec.name; | ||
2182 | pb.accessParam.ioVRefNum = spec.vRefNum; | ||
2183 | pb.fileParam.ioDirID = spec.parID; | ||
2184 | result = PBHGetDirAccessSync(&pb); | ||
2185 | require_noerr(result, PBHGetDirAccessSync); | ||
2186 | |||
2187 | /* return the IDs and access rights */ | ||
2188 | if ( NULL != ownerID ) | ||
2189 | { | ||
2190 | *ownerID = pb.accessParam.ioACOwnerID; | ||
2191 | } | ||
2192 | if ( NULL != groupID ) | ||
2193 | { | ||
2194 | *groupID = pb.accessParam.ioACGroupID; | ||
2195 | } | ||
2196 | if ( NULL != accessRights ) | ||
2197 | { | ||
2198 | *accessRights = pb.accessParam.ioACAccess; | ||
2199 | } | ||
2200 | |||
2201 | PBHGetDirAccessSync: | ||
2202 | FSGetCatalogInfo: | ||
2203 | |||
2204 | return ( result ); | ||
2205 | } | ||
2206 | |||
2207 | /*****************************************************************************/ | ||
2208 | |||
2209 | OSErr | ||
2210 | FSSetDirAccess( | ||
2211 | const FSRef *ref, | ||
2212 | SInt32 ownerID, | ||
2213 | SInt32 groupID, | ||
2214 | SInt32 accessRights) | ||
2215 | { | ||
2216 | OSErr result; | ||
2217 | FSSpec spec; | ||
2218 | HParamBlockRec pb; | ||
2219 | |||
2220 | enum | ||
2221 | { | ||
2222 | /* Just the bits that can be set */ | ||
2223 | kSetDirAccessSettableMask = (kioACAccessBlankAccessMask + | ||
2224 | kioACAccessEveryoneWriteMask + kioACAccessEveryoneReadMask + kioACAccessEveryoneSearchMask + | ||
2225 | kioACAccessGroupWriteMask + kioACAccessGroupReadMask + kioACAccessGroupSearchMask + | ||
2226 | kioACAccessOwnerWriteMask + kioACAccessOwnerReadMask + kioACAccessOwnerSearchMask) | ||
2227 | }; | ||
2228 | |||
2229 | /* get FSSpec from FSRef */ | ||
2230 | result = FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, NULL, &spec, NULL); | ||
2231 | require_noerr(result, FSGetCatalogInfo); | ||
2232 | |||
2233 | /* set directory access info for FSSpec */ | ||
2234 | pb.accessParam.ioNamePtr = (StringPtr)spec.name; | ||
2235 | pb.accessParam.ioVRefNum = spec.vRefNum; | ||
2236 | pb.fileParam.ioDirID = spec.parID; | ||
2237 | pb.accessParam.ioACOwnerID = ownerID; | ||
2238 | pb.accessParam.ioACGroupID = groupID; | ||
2239 | pb.accessParam.ioACAccess = accessRights & kSetDirAccessSettableMask; | ||
2240 | result = PBHSetDirAccessSync(&pb); | ||
2241 | require_noerr(result, PBHSetDirAccessSync); | ||
2242 | |||
2243 | PBHSetDirAccessSync: | ||
2244 | FSGetCatalogInfo: | ||
2245 | |||
2246 | return ( result ); | ||
2247 | } | ||
2248 | |||
2249 | /*****************************************************************************/ | ||
2250 | |||
2251 | OSErr | ||
2252 | FSGetVolMountInfoSize( | ||
2253 | FSVolumeRefNum volRefNum, | ||
2254 | SInt16 *size) | ||
2255 | { | ||
2256 | OSErr result; | ||
2257 | ParamBlockRec pb; | ||
2258 | |||
2259 | /* check parameters */ | ||
2260 | require_action(NULL != size, BadParameter, result = paramErr); | ||
2261 | |||
2262 | pb.ioParam.ioNamePtr = NULL; | ||
2263 | pb.ioParam.ioVRefNum = volRefNum; | ||
2264 | pb.ioParam.ioBuffer = (Ptr)size; | ||
2265 | result = PBGetVolMountInfoSize(&pb); | ||
2266 | require_noerr(result, PBGetVolMountInfoSize); | ||
2267 | |||
2268 | PBGetVolMountInfoSize: | ||
2269 | BadParameter: | ||
2270 | |||
2271 | return ( result ); | ||
2272 | } | ||
2273 | |||
2274 | /*****************************************************************************/ | ||
2275 | |||
2276 | OSErr | ||
2277 | FSGetVolMountInfo( | ||
2278 | FSVolumeRefNum volRefNum, | ||
2279 | void *volMountInfo) | ||
2280 | { | ||
2281 | OSErr result; | ||
2282 | ParamBlockRec pb; | ||
2283 | |||
2284 | /* check parameters */ | ||
2285 | require_action(NULL != volMountInfo, BadParameter, result = paramErr); | ||
2286 | |||
2287 | pb.ioParam.ioNamePtr = NULL; | ||
2288 | pb.ioParam.ioVRefNum = volRefNum; | ||
2289 | pb.ioParam.ioBuffer = (Ptr)volMountInfo; | ||
2290 | result = PBGetVolMountInfo(&pb); | ||
2291 | require_noerr(result, PBGetVolMountInfo); | ||
2292 | |||
2293 | PBGetVolMountInfo: | ||
2294 | BadParameter: | ||
2295 | |||
2296 | return ( result ); | ||
2297 | } | ||
2298 | |||
2299 | /*****************************************************************************/ | ||
2300 | |||
2301 | OSErr | ||
2302 | FSVolumeMount( | ||
2303 | const void *volMountInfo, | ||
2304 | FSVolumeRefNum *volRefNum) | ||
2305 | { | ||
2306 | OSErr result; | ||
2307 | ParamBlockRec pb; | ||
2308 | |||
2309 | /* check parameters */ | ||
2310 | require_action(NULL != volRefNum, BadParameter, result = paramErr); | ||
2311 | |||
2312 | pb.ioParam.ioBuffer = (Ptr)volMountInfo; | ||
2313 | result = PBVolumeMount(&pb); | ||
2314 | require_noerr(result, PBVolumeMount); | ||
2315 | |||
2316 | /* return the volume reference number */ | ||
2317 | *volRefNum = pb.ioParam.ioVRefNum; | ||
2318 | |||
2319 | PBVolumeMount: | ||
2320 | BadParameter: | ||
2321 | |||
2322 | return ( result ); | ||
2323 | } | ||
2324 | |||
2325 | /*****************************************************************************/ | ||
2326 | |||
2327 | OSErr | ||
2328 | FSMapID( | ||
2329 | FSVolumeRefNum volRefNum, | ||
2330 | SInt32 ugID, | ||
2331 | SInt16 objType, | ||
2332 | Str31 name) | ||
2333 | { | ||
2334 | OSErr result; | ||
2335 | HParamBlockRec pb; | ||
2336 | |||
2337 | /* check parameters */ | ||
2338 | require_action(NULL != name, BadParameter, result = paramErr); | ||
2339 | |||
2340 | pb.objParam.ioNamePtr = NULL; | ||
2341 | pb.objParam.ioVRefNum = volRefNum; | ||
2342 | pb.objParam.ioObjType = objType; | ||
2343 | pb.objParam.ioObjNamePtr = name; | ||
2344 | pb.objParam.ioObjID = ugID; | ||
2345 | result = PBHMapIDSync(&pb); | ||
2346 | require_noerr(result, PBHMapIDSync); | ||
2347 | |||
2348 | PBHMapIDSync: | ||
2349 | BadParameter: | ||
2350 | |||
2351 | return ( result ); | ||
2352 | } | ||
2353 | |||
2354 | /*****************************************************************************/ | ||
2355 | |||
2356 | OSErr | ||
2357 | FSMapName( | ||
2358 | FSVolumeRefNum volRefNum, | ||
2359 | ConstStr255Param name, | ||
2360 | SInt16 objType, | ||
2361 | SInt32 *ugID) | ||
2362 | { | ||
2363 | OSErr result; | ||
2364 | HParamBlockRec pb; | ||
2365 | |||
2366 | /* check parameters */ | ||
2367 | require_action(NULL != ugID, BadParameter, result = paramErr); | ||
2368 | |||
2369 | pb.objParam.ioNamePtr = NULL; | ||
2370 | pb.objParam.ioVRefNum = volRefNum; | ||
2371 | pb.objParam.ioObjType = objType; | ||
2372 | pb.objParam.ioObjNamePtr = (StringPtr)name; | ||
2373 | result = PBHMapNameSync(&pb); | ||
2374 | require_noerr(result, PBHMapNameSync); | ||
2375 | |||
2376 | /* return the user or group ID */ | ||
2377 | *ugID = pb.objParam.ioObjID; | ||
2378 | |||
2379 | PBHMapNameSync: | ||
2380 | BadParameter: | ||
2381 | |||
2382 | return ( result ); | ||
2383 | } | ||
2384 | |||
2385 | /*****************************************************************************/ | ||
2386 | |||
2387 | OSErr | ||
2388 | FSCopyFile( | ||
2389 | const FSRef *srcFileRef, | ||
2390 | const FSRef *dstDirectoryRef, | ||
2391 | UniCharCount nameLength, | ||
2392 | const UniChar *copyName, /* can be NULL (no rename during copy) */ | ||
2393 | TextEncoding textEncodingHint, | ||
2394 | FSRef *newRef) /* can be NULL */ | ||
2395 | { | ||
2396 | OSErr result; | ||
2397 | FSSpec srcFileSpec; | ||
2398 | FSCatalogInfo catalogInfo; | ||
2399 | HParamBlockRec pb; | ||
2400 | Str31 hfsName; | ||
2401 | GetVolParmsInfoBuffer volParmsInfo; | ||
2402 | UInt32 infoSize; | ||
2403 | |||
2404 | /* get source FSSpec from source FSRef */ | ||
2405 | result = FSGetCatalogInfo(srcFileRef, kFSCatInfoNone, NULL, NULL, &srcFileSpec, NULL); | ||
2406 | require_noerr(result, FSGetCatalogInfo_srcFileRef); | ||
2407 | |||
2408 | /* Make sure the volume supports CopyFile */ | ||
2409 | result = FSGetVolParms(srcFileSpec.vRefNum, sizeof(GetVolParmsInfoBuffer), | ||
2410 | &volParmsInfo, &infoSize); | ||
2411 | require_action((noErr == result) && VolHasCopyFile(&volParmsInfo), | ||
2412 | NoCopyFileSupport, result = paramErr); | ||
2413 | |||
2414 | /* get destination volume reference number and destination directory ID from destination FSRef */ | ||
2415 | result = FSGetCatalogInfo(dstDirectoryRef, kFSCatInfoVolume + kFSCatInfoNodeID, | ||
2416 | &catalogInfo, NULL, NULL, NULL); | ||
2417 | require_noerr(result, FSGetCatalogInfo_dstDirectoryRef); | ||
2418 | |||
2419 | /* tell the server to copy the object */ | ||
2420 | pb.copyParam.ioVRefNum = srcFileSpec.vRefNum; | ||
2421 | pb.copyParam.ioDirID = srcFileSpec.parID; | ||
2422 | pb.copyParam.ioNamePtr = (StringPtr)srcFileSpec.name; | ||
2423 | pb.copyParam.ioDstVRefNum = catalogInfo.volume; | ||
2424 | pb.copyParam.ioNewDirID = (long)catalogInfo.nodeID; | ||
2425 | pb.copyParam.ioNewName = NULL; | ||
2426 | if ( NULL != copyName ) | ||
2427 | { | ||
2428 | result = UnicodeNameGetHFSName(nameLength, copyName, textEncodingHint, false, hfsName); | ||
2429 | require_noerr(result, UnicodeNameGetHFSName); | ||
2430 | |||
2431 | pb.copyParam.ioCopyName = hfsName; | ||
2432 | } | ||
2433 | else | ||
2434 | { | ||
2435 | pb.copyParam.ioCopyName = NULL; | ||
2436 | } | ||
2437 | result = PBHCopyFileSync(&pb); | ||
2438 | require_noerr(result, PBHCopyFileSync); | ||
2439 | |||
2440 | if ( NULL != newRef ) | ||
2441 | { | ||
2442 | verify_noerr(FSMakeFSRef(pb.copyParam.ioDstVRefNum, pb.copyParam.ioNewDirID, | ||
2443 | pb.copyParam.ioCopyName, newRef)); | ||
2444 | } | ||
2445 | |||
2446 | PBHCopyFileSync: | ||
2447 | UnicodeNameGetHFSName: | ||
2448 | FSGetCatalogInfo_dstDirectoryRef: | ||
2449 | NoCopyFileSupport: | ||
2450 | FSGetCatalogInfo_srcFileRef: | ||
2451 | |||
2452 | return ( result ); | ||
2453 | } | ||
2454 | |||
2455 | /*****************************************************************************/ | ||
2456 | |||
2457 | OSErr | ||
2458 | FSMoveRename( | ||
2459 | const FSRef *srcFileRef, | ||
2460 | const FSRef *dstDirectoryRef, | ||
2461 | UniCharCount nameLength, | ||
2462 | const UniChar *moveName, /* can be NULL (no rename during move) */ | ||
2463 | TextEncoding textEncodingHint, | ||
2464 | FSRef *newRef) /* can be NULL */ | ||
2465 | { | ||
2466 | OSErr result; | ||
2467 | FSSpec srcFileSpec; | ||
2468 | FSCatalogInfo catalogInfo; | ||
2469 | HParamBlockRec pb; | ||
2470 | Str31 hfsName; | ||
2471 | GetVolParmsInfoBuffer volParmsInfo; | ||
2472 | UInt32 infoSize; | ||
2473 | |||
2474 | /* get source FSSpec from source FSRef */ | ||
2475 | result = FSGetCatalogInfo(srcFileRef, kFSCatInfoNone, NULL, NULL, &srcFileSpec, NULL); | ||
2476 | require_noerr(result, FSGetCatalogInfo_srcFileRef); | ||
2477 | |||
2478 | /* Make sure the volume supports MoveRename */ | ||
2479 | result = FSGetVolParms(srcFileSpec.vRefNum, sizeof(GetVolParmsInfoBuffer), | ||
2480 | &volParmsInfo, &infoSize); | ||
2481 | require_action((noErr == result) && VolHasMoveRename(&volParmsInfo), | ||
2482 | NoMoveRenameSupport, result = paramErr); | ||
2483 | |||
2484 | /* get destination volume reference number and destination directory ID from destination FSRef */ | ||
2485 | result = FSGetCatalogInfo(dstDirectoryRef, kFSCatInfoVolume + kFSCatInfoNodeID, | ||
2486 | &catalogInfo, NULL, NULL, NULL); | ||
2487 | require_noerr(result, FSGetCatalogInfo_dstDirectoryRef); | ||
2488 | |||
2489 | /* make sure the source and destination are on the same volume */ | ||
2490 | require_action(srcFileSpec.vRefNum == catalogInfo.volume, NotSameVolume, result = diffVolErr); | ||
2491 | |||
2492 | /* tell the server to move and rename the object */ | ||
2493 | pb.copyParam.ioVRefNum = srcFileSpec.vRefNum; | ||
2494 | pb.copyParam.ioDirID = srcFileSpec.parID; | ||
2495 | pb.copyParam.ioNamePtr = (StringPtr)srcFileSpec.name; | ||
2496 | pb.copyParam.ioNewDirID = (long)catalogInfo.nodeID; | ||
2497 | pb.copyParam.ioNewName = NULL; | ||
2498 | if ( NULL != moveName ) | ||
2499 | { | ||
2500 | result = UnicodeNameGetHFSName(nameLength, moveName, textEncodingHint, false, hfsName); | ||
2501 | require_noerr(result, UnicodeNameGetHFSName); | ||
2502 | |||
2503 | pb.copyParam.ioCopyName = hfsName; | ||
2504 | } | ||
2505 | else | ||
2506 | { | ||
2507 | pb.copyParam.ioCopyName = NULL; | ||
2508 | } | ||
2509 | result = PBHMoveRenameSync(&pb); | ||
2510 | require_noerr(result, PBHMoveRenameSync); | ||
2511 | |||
2512 | if ( NULL != newRef ) | ||
2513 | { | ||
2514 | verify_noerr(FSMakeFSRef(pb.copyParam.ioVRefNum, pb.copyParam.ioNewDirID, | ||
2515 | pb.copyParam.ioCopyName, newRef)); | ||
2516 | } | ||
2517 | |||
2518 | PBHMoveRenameSync: | ||
2519 | UnicodeNameGetHFSName: | ||
2520 | NotSameVolume: | ||
2521 | FSGetCatalogInfo_dstDirectoryRef: | ||
2522 | NoMoveRenameSupport: | ||
2523 | FSGetCatalogInfo_srcFileRef: | ||
2524 | |||
2525 | return ( result ); | ||
2526 | } | ||
2527 | |||
2528 | /*****************************************************************************/ | ||
2529 | |||
2530 | #pragma mark ----- File ID Routines ----- | ||
2531 | |||
2532 | /*****************************************************************************/ | ||
2533 | |||
2534 | OSErr | ||
2535 | FSResolveFileIDRef( | ||
2536 | FSVolumeRefNum volRefNum, | ||
2537 | SInt32 fileID, | ||
2538 | FSRef *ref) | ||
2539 | { | ||
2540 | OSErr result; | ||
2541 | FIDParam pb; | ||
2542 | Str255 tempStr; | ||
2543 | |||
2544 | /* check parameters */ | ||
2545 | require_action(NULL != ref, BadParameter, result = paramErr); | ||
2546 | |||
2547 | /* resolve the file ID reference */ | ||
2548 | tempStr[0] = 0; | ||
2549 | pb.ioNamePtr = tempStr; | ||
2550 | pb.ioVRefNum = volRefNum; | ||
2551 | pb.ioFileID = fileID; | ||
2552 | result = PBResolveFileIDRefSync((HParmBlkPtr)&pb); | ||
2553 | require_noerr(result, PBResolveFileIDRefSync); | ||
2554 | |||
2555 | /* and then make an FSRef to the file */ | ||
2556 | result = FSMakeFSRef(volRefNum, pb.ioSrcDirID, tempStr, ref); | ||
2557 | require_noerr(result, FSMakeFSRef); | ||
2558 | |||
2559 | FSMakeFSRef: | ||
2560 | PBResolveFileIDRefSync: | ||
2561 | BadParameter: | ||
2562 | |||
2563 | return ( result ); | ||
2564 | } | ||
2565 | |||
2566 | /*****************************************************************************/ | ||
2567 | |||
2568 | OSErr | ||
2569 | FSCreateFileIDRef( | ||
2570 | const FSRef *ref, | ||
2571 | SInt32 *fileID) | ||
2572 | { | ||
2573 | OSErr result; | ||
2574 | FSSpec spec; | ||
2575 | FIDParam pb; | ||
2576 | |||
2577 | /* check parameters */ | ||
2578 | require_action(NULL != fileID, BadParameter, result = paramErr); | ||
2579 | |||
2580 | /* Get an FSSpec from the FSRef */ | ||
2581 | result = FSGetCatalogInfo(ref, kFSCatInfoNone, NULL, NULL, &spec, NULL); | ||
2582 | require_noerr(result, FSGetCatalogInfo); | ||
2583 | |||
2584 | /* Create (or get) the file ID reference using the FSSpec */ | ||
2585 | pb.ioNamePtr = (StringPtr)spec.name; | ||
2586 | pb.ioVRefNum = spec.vRefNum; | ||
2587 | pb.ioSrcDirID = spec.parID; | ||
2588 | result = PBCreateFileIDRefSync((HParmBlkPtr)&pb); | ||
2589 | require((noErr == result) || (fidExists == result) || (afpIDExists == result), | ||
2590 | PBCreateFileIDRefSync); | ||
2591 | |||
2592 | /* return the file ID reference */ | ||
2593 | *fileID = pb.ioFileID; | ||
2594 | |||
2595 | PBCreateFileIDRefSync: | ||
2596 | FSGetCatalogInfo: | ||
2597 | BadParameter: | ||
2598 | |||
2599 | return ( result ); | ||
2600 | } | ||
2601 | |||
2602 | /*****************************************************************************/ | ||
2603 | |||
2604 | #pragma mark ----- Utility Routines ----- | ||
2605 | |||
2606 | /*****************************************************************************/ | ||
2607 | |||
2608 | Ptr | ||
2609 | GetTempBuffer( | ||
2610 | ByteCount buffReqSize, | ||
2611 | ByteCount *buffActSize) | ||
2612 | { | ||
2613 | enum | ||
2614 | { | ||
2615 | kSlopMemory = 0x00008000 /* 32K - Amount of free memory to leave when allocating buffers */ | ||
2616 | }; | ||
2617 | |||
2618 | Ptr tempPtr; | ||
2619 | |||
2620 | /* check parameters */ | ||
2621 | require_action(NULL != buffActSize, BadParameter, tempPtr = NULL); | ||
2622 | |||
2623 | /* Make request a multiple of 4K bytes */ | ||
2624 | buffReqSize = buffReqSize & 0xfffff000; | ||
2625 | |||
2626 | if ( buffReqSize < 0x00001000 ) | ||
2627 | { | ||
2628 | /* Request was smaller than 4K bytes - make it 4K */ | ||
2629 | buffReqSize = 0x00001000; | ||
2630 | } | ||
2631 | |||
2632 | /* Attempt to allocate the memory */ | ||
2633 | tempPtr = NewPtr(buffReqSize); | ||
2634 | |||
2635 | /* If request failed, go to backup plan */ | ||
2636 | if ( (tempPtr == NULL) && (buffReqSize > 0x00001000) ) | ||
2637 | { | ||
2638 | /* | ||
2639 | ** Try to get largest 4K byte block available | ||
2640 | ** leaving some slop for the toolbox if possible | ||
2641 | */ | ||
2642 | long freeMemory = (FreeMem() - kSlopMemory) & 0xfffff000; | ||
2643 | |||
2644 | buffReqSize = MaxBlock() & 0xfffff000; | ||
2645 | |||
2646 | if ( buffReqSize > freeMemory ) | ||
2647 | { | ||
2648 | buffReqSize = freeMemory; | ||
2649 | } | ||
2650 | |||
2651 | if ( buffReqSize == 0 ) | ||
2652 | { | ||
2653 | buffReqSize = 0x00001000; | ||
2654 | } | ||
2655 | |||
2656 | tempPtr = NewPtr(buffReqSize); | ||
2657 | } | ||
2658 | |||
2659 | /* Return bytes allocated */ | ||
2660 | if ( tempPtr != NULL ) | ||
2661 | { | ||
2662 | *buffActSize = buffReqSize; | ||
2663 | } | ||
2664 | else | ||
2665 | { | ||
2666 | *buffActSize = 0; | ||
2667 | } | ||
2668 | |||
2669 | BadParameter: | ||
2670 | |||
2671 | return ( tempPtr ); | ||
2672 | } | ||
2673 | |||
2674 | /*****************************************************************************/ | ||
2675 | |||
2676 | OSErr | ||
2677 | FileRefNumGetFSRef( | ||
2678 | short refNum, | ||
2679 | FSRef *ref) | ||
2680 | { | ||
2681 | return ( FSGetForkCBInfo(refNum, 0, NULL, NULL, NULL, ref, NULL) ); | ||
2682 | } | ||
2683 | |||
2684 | /*****************************************************************************/ | ||
2685 | |||
2686 | OSErr | ||
2687 | FSSetDefault( | ||
2688 | const FSRef *newDefault, | ||
2689 | FSRef *oldDefault) | ||
2690 | { | ||
2691 | OSErr result; | ||
2692 | FSVolumeRefNum vRefNum; | ||
2693 | long dirID; | ||
2694 | FSCatalogInfo catalogInfo; | ||
2695 | |||
2696 | /* check parameters */ | ||
2697 | require_action((NULL != newDefault) && (NULL != oldDefault), BadParameter, result = paramErr); | ||
2698 | |||
2699 | /* Get nodeFlags, vRefNum and dirID (nodeID) of newDefault */ | ||
2700 | result = FSGetCatalogInfo(newDefault, | ||
2701 | kFSCatInfoNodeFlags + kFSCatInfoVolume + kFSCatInfoNodeID, | ||
2702 | &catalogInfo, NULL, NULL, NULL); | ||
2703 | require_noerr(result, FSGetCatalogInfo); | ||
2704 | |||
2705 | /* Make sure newDefault is a directory */ | ||
2706 | require_action(0 != (kFSNodeIsDirectoryMask & catalogInfo.nodeFlags), NewDefaultNotDirectory, | ||
2707 | result = dirNFErr); | ||
2708 | |||
2709 | /* Get the current working directory. */ | ||
2710 | result = HGetVol(NULL, &vRefNum, &dirID); | ||
2711 | require_noerr(result, HGetVol); | ||
2712 | |||
2713 | /* Return the oldDefault FSRef */ | ||
2714 | result = FSMakeFSRef(vRefNum, dirID, NULL, oldDefault); | ||
2715 | require_noerr(result, FSMakeFSRef); | ||
2716 | |||
2717 | /* Set the new current working directory */ | ||
2718 | result = HSetVol(NULL, catalogInfo.volume, catalogInfo.nodeID); | ||
2719 | require_noerr(result, HSetVol); | ||
2720 | |||
2721 | HSetVol: | ||
2722 | FSMakeFSRef: | ||
2723 | HGetVol: | ||
2724 | NewDefaultNotDirectory: | ||
2725 | FSGetCatalogInfo: | ||
2726 | BadParameter: | ||
2727 | |||
2728 | return ( result ); | ||
2729 | } | ||
2730 | |||
2731 | /*****************************************************************************/ | ||
2732 | |||
2733 | OSErr | ||
2734 | FSRestoreDefault( | ||
2735 | const FSRef *oldDefault) | ||
2736 | { | ||
2737 | OSErr result; | ||
2738 | FSCatalogInfo catalogInfo; | ||
2739 | |||
2740 | /* check parameters */ | ||
2741 | require_action(NULL != oldDefault, BadParameter, result = paramErr); | ||
2742 | |||
2743 | /* Get nodeFlags, vRefNum and dirID (nodeID) of oldDefault */ | ||
2744 | result = FSGetCatalogInfo(oldDefault, | ||
2745 | kFSCatInfoNodeFlags + kFSCatInfoVolume + kFSCatInfoNodeID, | ||
2746 | &catalogInfo, NULL, NULL, NULL); | ||
2747 | require_noerr(result, FSGetCatalogInfo); | ||
2748 | |||
2749 | /* Make sure oldDefault is a directory */ | ||
2750 | require_action(0 != (kFSNodeIsDirectoryMask & catalogInfo.nodeFlags), OldDefaultNotDirectory, | ||
2751 | result = dirNFErr); | ||
2752 | |||
2753 | /* Set the current working directory to oldDefault */ | ||
2754 | result = HSetVol(NULL, catalogInfo.volume, catalogInfo.nodeID); | ||
2755 | require_noerr(result, HSetVol); | ||
2756 | |||
2757 | HSetVol: | ||
2758 | OldDefaultNotDirectory: | ||
2759 | FSGetCatalogInfo: | ||
2760 | BadParameter: | ||
2761 | |||
2762 | return ( result ); | ||
2763 | } | ||
2764 | |||
2765 | /*****************************************************************************/ | ||
diff --git a/linden/indra/mac_updater/MoreFilesX.h b/linden/indra/mac_updater/MoreFilesX.h new file mode 100644 index 0000000..ce3be86 --- /dev/null +++ b/linden/indra/mac_updater/MoreFilesX.h | |||
@@ -0,0 +1,1848 @@ | |||
1 | /** | ||
2 | * @file MoreFilesX.h | ||
3 | * @brief Mac OS X updater program | ||
4 | * | ||
5 | * Copyright (c) 2004-2007, Linden Research, Inc. | ||
6 | * | ||
7 | * The source code in this file ("Source Code") is provided by Linden Lab | ||
8 | * to you under the terms of the GNU General Public License, version 2.0 | ||
9 | * ("GPL"), unless you have obtained a separate licensing agreement | ||
10 | * ("Other License"), formally executed by you and Linden Lab. Terms of | ||
11 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
12 | * online at http://secondlife.com/developers/opensource/gplv2 | ||
13 | * | ||
14 | * There are special exceptions to the terms and conditions of the GPL as | ||
15 | * it is applied to this Source Code. View the full text of the exception | ||
16 | * in the file doc/FLOSS-exception.txt in this software distribution, or | ||
17 | * online at http://secondlife.com/developers/opensource/flossexception | ||
18 | * | ||
19 | * By copying, modifying or distributing this software, you acknowledge | ||
20 | * that you have read and understood your obligations described above, | ||
21 | * and agree to abide by those obligations. | ||
22 | * | ||
23 | * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
24 | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
25 | * COMPLETENESS OR PERFORMANCE. | ||
26 | */ | ||
27 | |||
28 | /* | ||
29 | File: MoreFilesX.h | ||
30 | |||
31 | Contains: A collection of useful high-level File Manager routines | ||
32 | which use the HFS Plus APIs wherever possible. | ||
33 | |||
34 | Version: MoreFilesX 1.0.1 | ||
35 | |||
36 | Copyright: � 1992-2002 by Apple Computer, Inc., all rights reserved. | ||
37 | |||
38 | Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. | ||
39 | ("Apple") in consideration of your agreement to the following terms, and your | ||
40 | use, installation, modification or redistribution of this Apple software | ||
41 | constitutes acceptance of these terms. If you do not agree with these terms, | ||
42 | please do not use, install, modify or redistribute this Apple software. | ||
43 | |||
44 | In consideration of your agreement to abide by the following terms, and subject | ||
45 | to these terms, Apple grants you a personal, non-exclusive license, under Apple�s | ||
46 | copyrights in this original Apple software (the "Apple Software"), to use, | ||
47 | reproduce, modify and redistribute the Apple Software, with or without | ||
48 | modifications, in source and/or binary forms; provided that if you redistribute | ||
49 | the Apple Software in its entirety and without modifications, you must retain | ||
50 | this notice and the following text and disclaimers in all such redistributions of | ||
51 | the Apple Software. Neither the name, trademarks, service marks or logos of | ||
52 | Apple Computer, Inc. may be used to endorse or promote products derived from the | ||
53 | Apple Software without specific prior written permission from Apple. Except as | ||
54 | expressly stated in this notice, no other rights or licenses, express or implied, | ||
55 | are granted by Apple herein, including but not limited to any patent rights that | ||
56 | may be infringed by your derivative works or by other works in which the Apple | ||
57 | Software may be incorporated. | ||
58 | |||
59 | The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO | ||
60 | WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED | ||
61 | WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||
62 | PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN | ||
63 | COMBINATION WITH YOUR PRODUCTS. | ||
64 | |||
65 | IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR | ||
66 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE | ||
67 | GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
68 | ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION | ||
69 | OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT | ||
70 | (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN | ||
71 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
72 | |||
73 | File Ownership: | ||
74 | |||
75 | DRI: Apple Macintosh Developer Technical Support | ||
76 | |||
77 | Other Contact: For bug reports, consult the following page on | ||
78 | the World Wide Web: | ||
79 | http://developer.apple.com/bugreporter/ | ||
80 | |||
81 | Technology: DTS Sample Code | ||
82 | |||
83 | Writers: | ||
84 | |||
85 | (JL) Jim Luther | ||
86 | |||
87 | Change History (most recent first): | ||
88 | |||
89 | <3> 4/19/02 JL [2853905] Fixed #if test around header includes. | ||
90 | <2> 4/19/02 JL [2853901] Updated standard disclaimer. | ||
91 | <1> 1/25/02 JL MoreFilesX 1.0 | ||
92 | |||
93 | Notes: | ||
94 | What do those arrows in the documentation for each routine mean? | ||
95 | |||
96 | --> The parameter is an input | ||
97 | |||
98 | <-- The parameter is an output. The pointer to the variable | ||
99 | where the output will be returned (must not be NULL). | ||
100 | |||
101 | <** The parameter is an optional output. If it is not a | ||
102 | NULL pointer, it points to the variable where the output | ||
103 | will be returned. If it is a NULL pointer, the output will | ||
104 | not be returned and will possibly let the routine and the | ||
105 | File Manager do less work. If you don't need an optional output, | ||
106 | don't ask for it. | ||
107 | **> The parameter is an optional input. If it is not a | ||
108 | NULL pointer, it points to the variable containing the | ||
109 | input data. If it is a NULL pointer, the input is not used | ||
110 | and will possibly let the routine and the File Manager | ||
111 | do less work. | ||
112 | */ | ||
113 | |||
114 | #ifndef __MOREFILESX__ | ||
115 | #define __MOREFILESX__ | ||
116 | |||
117 | #ifndef __CARBON__ | ||
118 | #include <Carbon/Carbon.h> | ||
119 | #endif | ||
120 | |||
121 | #if PRAGMA_ONCE | ||
122 | #pragma once | ||
123 | #endif | ||
124 | |||
125 | #ifdef __cplusplus | ||
126 | extern "C" { | ||
127 | #endif | ||
128 | |||
129 | #if PRAGMA_IMPORT | ||
130 | #pragma import on | ||
131 | #endif | ||
132 | |||
133 | #if PRAGMA_STRUCT_ALIGN | ||
134 | #pragma options align=mac68k | ||
135 | #elif PRAGMA_STRUCT_PACKPUSH | ||
136 | #pragma pack(push, 2) | ||
137 | #elif PRAGMA_STRUCT_PACK | ||
138 | #pragma pack(2) | ||
139 | #endif | ||
140 | |||
141 | /*****************************************************************************/ | ||
142 | |||
143 | #pragma mark ----- FinderInfo and ExtendedFinderInfo ----- | ||
144 | |||
145 | /* | ||
146 | * FSGetFinderInfo and FSSetFinderInfo use these unions for Finder information. | ||
147 | */ | ||
148 | |||
149 | union FinderInfo | ||
150 | { | ||
151 | FileInfo file; | ||
152 | FolderInfo folder; | ||
153 | }; | ||
154 | typedef union FinderInfo FinderInfo; | ||
155 | |||
156 | union ExtendedFinderInfo | ||
157 | { | ||
158 | ExtendedFileInfo file; | ||
159 | ExtendedFolderInfo folder; | ||
160 | }; | ||
161 | typedef union ExtendedFinderInfo ExtendedFinderInfo; | ||
162 | |||
163 | /*****************************************************************************/ | ||
164 | |||
165 | #pragma mark ----- GetVolParmsInfoBuffer Macros ----- | ||
166 | |||
167 | /* | ||
168 | * Macros to get information out of GetVolParmsInfoBuffer. | ||
169 | */ | ||
170 | |||
171 | /* version 1 field getters */ | ||
172 | #define GetVolParmsInfoVersion(volParms) \ | ||
173 | ((volParms)->vMVersion) | ||
174 | #define GetVolParmsInfoAttrib(volParms) \ | ||
175 | ((volParms)->vMAttrib) | ||
176 | #define GetVolParmsInfoLocalHand(volParms) \ | ||
177 | ((volParms)->vMLocalHand) | ||
178 | #define GetVolParmsInfoServerAdr(volParms) \ | ||
179 | ((volParms)->vMServerAdr) | ||
180 | |||
181 | /* version 2 field getters (assume zero result if version < 2) */ | ||
182 | #define GetVolParmsInfoVolumeGrade(volParms) \ | ||
183 | (((volParms)->vMVersion >= 2) ? (volParms)->vMVolumeGrade : 0) | ||
184 | #define GetVolParmsInfoForeignPrivID(volParms) \ | ||
185 | (((volParms)->vMVersion >= 2) ? (volParms)->vMForeignPrivID : 0) | ||
186 | |||
187 | /* version 3 field getters (assume zero result if version < 3) */ | ||
188 | #define GetVolParmsInfoExtendedAttributes(volParms) \ | ||
189 | (((volParms)->vMVersion >= 3) ? (volParms)->vMExtendedAttributes : 0) | ||
190 | |||
191 | /* attribute bits supported by all versions of GetVolParmsInfoBuffer */ | ||
192 | #define VolIsNetworkVolume(volParms) \ | ||
193 | ((volParms)->vMServerAdr != 0) | ||
194 | #define VolHasLimitFCBs(volParms) \ | ||
195 | (((volParms)->vMAttrib & (1L << bLimitFCBs)) != 0) | ||
196 | #define VolHasLocalWList(volParms) \ | ||
197 | (((volParms)->vMAttrib & (1L << bLocalWList)) != 0) | ||
198 | #define VolHasNoMiniFndr(volParms) \ | ||
199 | (((volParms)->vMAttrib & (1L << bNoMiniFndr)) != 0) | ||
200 | #define VolHasNoVNEdit(volParms) \ | ||
201 | (((volParms)->vMAttrib & (1L << bNoVNEdit)) != 0) | ||
202 | #define VolHasNoLclSync(volParms) \ | ||
203 | (((volParms)->vMAttrib & (1L << bNoLclSync)) != 0) | ||
204 | #define VolHasTrshOffLine(volParms) \ | ||
205 | (((volParms)->vMAttrib & (1L << bTrshOffLine)) != 0) | ||
206 | #define VolHasNoSwitchTo(volParms) \ | ||
207 | (((volParms)->vMAttrib & (1L << bNoSwitchTo)) != 0) | ||
208 | #define VolHasNoDeskItems(volParms) \ | ||
209 | (((volParms)->vMAttrib & (1L << bNoDeskItems)) != 0) | ||
210 | #define VolHasNoBootBlks(volParms) \ | ||
211 | (((volParms)->vMAttrib & (1L << bNoBootBlks)) != 0) | ||
212 | #define VolHasAccessCntl(volParms) \ | ||
213 | (((volParms)->vMAttrib & (1L << bAccessCntl)) != 0) | ||
214 | #define VolHasNoSysDir(volParms) \ | ||
215 | (((volParms)->vMAttrib & (1L << bNoSysDir)) != 0) | ||
216 | #define VolHasExtFSVol(volParms) \ | ||
217 | (((volParms)->vMAttrib & (1L << bHasExtFSVol)) != 0) | ||
218 | #define VolHasOpenDeny(volParms) \ | ||
219 | (((volParms)->vMAttrib & (1L << bHasOpenDeny)) != 0) | ||
220 | #define VolHasCopyFile(volParms) \ | ||
221 | (((volParms)->vMAttrib & (1L << bHasCopyFile)) != 0) | ||
222 | #define VolHasMoveRename(volParms) \ | ||
223 | (((volParms)->vMAttrib & (1L << bHasMoveRename)) != 0) | ||
224 | #define VolHasDesktopMgr(volParms) \ | ||
225 | (((volParms)->vMAttrib & (1L << bHasDesktopMgr)) != 0) | ||
226 | #define VolHasShortName(volParms) \ | ||
227 | (((volParms)->vMAttrib & (1L << bHasShortName)) != 0) | ||
228 | #define VolHasFolderLock(volParms) \ | ||
229 | (((volParms)->vMAttrib & (1L << bHasFolderLock)) != 0) | ||
230 | #define VolHasPersonalAccessPrivileges(volParms) \ | ||
231 | (((volParms)->vMAttrib & (1L << bHasPersonalAccessPrivileges)) != 0) | ||
232 | #define VolHasUserGroupList(volParms) \ | ||
233 | (((volParms)->vMAttrib & (1L << bHasUserGroupList)) != 0) | ||
234 | #define VolHasCatSearch(volParms) \ | ||
235 | (((volParms)->vMAttrib & (1L << bHasCatSearch)) != 0) | ||
236 | #define VolHasFileIDs(volParms) \ | ||
237 | (((volParms)->vMAttrib & (1L << bHasFileIDs)) != 0) | ||
238 | #define VolHasBTreeMgr(volParms) \ | ||
239 | (((volParms)->vMAttrib & (1L << bHasBTreeMgr)) != 0) | ||
240 | #define VolHasBlankAccessPrivileges(volParms) \ | ||
241 | (((volParms)->vMAttrib & (1L << bHasBlankAccessPrivileges)) != 0) | ||
242 | #define VolSupportsAsyncRequests(volParms) \ | ||
243 | (((volParms)->vMAttrib & (1L << bSupportsAsyncRequests)) != 0) | ||
244 | #define VolSupportsTrashVolumeCache(volParms) \ | ||
245 | (((volParms)->vMAttrib & (1L << bSupportsTrashVolumeCache)) != 0) | ||
246 | |||
247 | /* attribute bits supported by version 3 and greater versions of GetVolParmsInfoBuffer */ | ||
248 | #define VolIsEjectable(volParms) \ | ||
249 | ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bIsEjectable)) != 0) | ||
250 | #define VolSupportsHFSPlusAPIs(volParms) \ | ||
251 | ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsHFSPlusAPIs)) != 0) | ||
252 | #define VolSupportsFSCatalogSearch(volParms) \ | ||
253 | ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsFSCatalogSearch)) != 0) | ||
254 | #define VolSupportsFSExchangeObjects(volParms) \ | ||
255 | ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsFSExchangeObjects)) != 0) | ||
256 | #define VolSupports2TBFiles(volParms) \ | ||
257 | ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupports2TBFiles)) != 0) | ||
258 | #define VolSupportsLongNames(volParms) \ | ||
259 | ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsLongNames)) != 0) | ||
260 | #define VolSupportsMultiScriptNames(volParms) \ | ||
261 | ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsMultiScriptNames)) != 0) | ||
262 | #define VolSupportsNamedForks(volParms) \ | ||
263 | ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsNamedForks)) != 0) | ||
264 | #define VolSupportsSubtreeIterators(volParms) \ | ||
265 | ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsSubtreeIterators)) != 0) | ||
266 | #define VolL2PCanMapFileBlocks(volParms) \ | ||
267 | ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bL2PCanMapFileBlocks)) != 0) | ||
268 | #define VolParentModDateChanges(volParms) \ | ||
269 | ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bParentModDateChanges)) != 0) | ||
270 | #define VolAncestorModDateChanges(volParms) \ | ||
271 | ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bAncestorModDateChanges)) != 0) | ||
272 | #define VolSupportsSymbolicLinks(volParms) \ | ||
273 | ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bSupportsSymbolicLinks)) != 0) | ||
274 | #define VolIsAutoMounted(volParms) \ | ||
275 | ((GetVolParmsInfoExtendedAttributes(volParms) & (1L << bIsAutoMounted)) != 0) | ||
276 | |||
277 | /*****************************************************************************/ | ||
278 | |||
279 | #pragma mark ----- userPrivileges Bit Masks and Macros ----- | ||
280 | |||
281 | /* | ||
282 | * Bit masks and macros to get common information out of userPrivileges byte | ||
283 | * returned by FSGetCatalogInfo. | ||
284 | * | ||
285 | * Note: The userPrivileges byte is the same as the ioACUser byte returned | ||
286 | * by PBGetCatInfo, and is the 1's complement of the user's privileges | ||
287 | * byte returned in ioACAccess by PBHGetDirAccess. That's where the | ||
288 | * ioACUser names came from. | ||
289 | * | ||
290 | * The userPrivileges are user's effective privileges based on the | ||
291 | * user ID and the groups that user belongs to, and the owner, group, | ||
292 | * and everyone privileges for the given directory. | ||
293 | */ | ||
294 | |||
295 | enum | ||
296 | { | ||
297 | /* mask for just the access restriction bits */ | ||
298 | kioACUserAccessMask = (kioACUserNoSeeFolderMask + | ||
299 | kioACUserNoSeeFilesMask + | ||
300 | kioACUserNoMakeChangesMask), | ||
301 | /* common access privilege settings */ | ||
302 | kioACUserFull = 0x00, /* no access restiction bits on */ | ||
303 | kioACUserNone = kioACUserAccessMask, /* all access restiction bits on */ | ||
304 | kioACUserDropBox = (kioACUserNoSeeFolderMask + | ||
305 | kioACUserNoSeeFilesMask), /* make changes, but not see files or folders */ | ||
306 | kioACUserBulletinBoard = kioACUserNoMakeChangesMask /* see files and folders, but not make changes */ | ||
307 | }; | ||
308 | |||
309 | |||
310 | /* Macros for testing ioACUser bits. */ | ||
311 | |||
312 | #define UserIsOwner(userPrivileges) \ | ||
313 | (((userPrivileges) & kioACUserNotOwnerMask) == 0) | ||
314 | #define UserHasFullAccess(userPrivileges) \ | ||
315 | (((userPrivileges) & (kioACUserAccessMask)) == kioACUserFull) | ||
316 | #define UserHasDropBoxAccess(userPrivileges) \ | ||
317 | (((userPrivileges) & kioACUserAccessMask) == kioACUserDropBox) | ||
318 | #define UserHasBulletinBoard(userPrivileges) \ | ||
319 | (((userPrivileges) & kioACUserAccessMask) == kioACUserBulletinBoard) | ||
320 | #define UserHasNoAccess(userPrivileges) \ | ||
321 | (((userPrivileges) & kioACUserAccessMask) == kioACUserNone) | ||
322 | |||
323 | /*****************************************************************************/ | ||
324 | |||
325 | #pragma mark ----- File Access Routines ----- | ||
326 | |||
327 | /*****************************************************************************/ | ||
328 | |||
329 | #pragma mark FSCopyFork | ||
330 | |||
331 | OSErr | ||
332 | FSCopyFork( | ||
333 | SInt16 srcRefNum, | ||
334 | SInt16 dstRefNum, | ||
335 | void *copyBufferPtr, | ||
336 | ByteCount copyBufferSize); | ||
337 | |||
338 | /* | ||
339 | The FSCopyFork function copies all data from the source fork to the | ||
340 | destination fork of open file forks and makes sure the destination EOF | ||
341 | is equal to the source EOF. | ||
342 | |||
343 | srcRefNum --> The source file reference number. | ||
344 | dstRefNum --> The destination file reference number. | ||
345 | copyBufferPtr --> Pointer to buffer to use during copy. The | ||
346 | buffer should be at least 4K-bytes minimum. | ||
347 | The larger the buffer, the faster the copy | ||
348 | (up to a point). | ||
349 | copyBufferSize --> The size of the copy buffer. | ||
350 | */ | ||
351 | |||
352 | /*****************************************************************************/ | ||
353 | |||
354 | #pragma mark ----- Volume Access Routines ----- | ||
355 | |||
356 | /*****************************************************************************/ | ||
357 | |||
358 | #pragma mark FSGetVolParms | ||
359 | |||
360 | OSErr | ||
361 | FSGetVolParms( | ||
362 | FSVolumeRefNum volRefNum, | ||
363 | UInt32 bufferSize, | ||
364 | GetVolParmsInfoBuffer *volParmsInfo, | ||
365 | UInt32 *actualInfoSize); | ||
366 | |||
367 | /* | ||
368 | The FSGetVolParms function returns information about the characteristics | ||
369 | of a volume. A result of paramErr usually just means the volume doesn't | ||
370 | support GetVolParms and the feature you were going to check | ||
371 | for isn't available. | ||
372 | |||
373 | volRefNum --> Volume specification. | ||
374 | bufferSize --> Size of buffer pointed to by volParmsInfo. | ||
375 | volParmsInfo <-- A GetVolParmsInfoBuffer record where the volume | ||
376 | attributes information is returned. | ||
377 | actualInfoSize <-- The number of bytes actually returned | ||
378 | in volParmsInfo. | ||
379 | |||
380 | __________ | ||
381 | |||
382 | Also see: The GetVolParmsInfoBuffer Macros for checking attribute bits | ||
383 | in this file | ||
384 | */ | ||
385 | |||
386 | /*****************************************************************************/ | ||
387 | |||
388 | #pragma mark FSGetVRefNum | ||
389 | |||
390 | OSErr | ||
391 | FSGetVRefNum( | ||
392 | const FSRef *ref, | ||
393 | FSVolumeRefNum *vRefNum); | ||
394 | |||
395 | /* | ||
396 | The FSGetVRefNum function determines the volume reference | ||
397 | number of a volume from a FSRef. | ||
398 | |||
399 | ref --> The FSRef. | ||
400 | vRefNum <-- The volume reference number. | ||
401 | */ | ||
402 | |||
403 | /*****************************************************************************/ | ||
404 | |||
405 | #pragma mark FSGetVInfo | ||
406 | |||
407 | OSErr | ||
408 | FSGetVInfo( | ||
409 | FSVolumeRefNum volume, | ||
410 | HFSUniStr255 *volumeName, /* can be NULL */ | ||
411 | UInt64 *freeBytes, /* can be NULL */ | ||
412 | UInt64 *totalBytes); /* can be NULL */ | ||
413 | |||
414 | /* | ||
415 | The FSGetVInfo function returns the name, available space (in bytes), | ||
416 | and total space (in bytes) for the specified volume. | ||
417 | |||
418 | volume --> The volume reference number. | ||
419 | volumeName <** An optional pointer to a HFSUniStr255. | ||
420 | If not NULL, the volume name will be returned in | ||
421 | the HFSUniStr255. | ||
422 | freeBytes <** An optional pointer to a UInt64. | ||
423 | If not NULL, the number of free bytes on the | ||
424 | volume will be returned in the UInt64. | ||
425 | totalBytes <** An optional pointer to a UInt64. | ||
426 | If not NULL, the total number of bytes on the | ||
427 | volume will be returned in the UInt64. | ||
428 | */ | ||
429 | |||
430 | /*****************************************************************************/ | ||
431 | |||
432 | #pragma mark FSGetVolFileSystemID | ||
433 | |||
434 | OSErr | ||
435 | FSGetVolFileSystemID( | ||
436 | FSVolumeRefNum volume, | ||
437 | UInt16 *fileSystemID, /* can be NULL */ | ||
438 | UInt16 *signature); /* can be NULL */ | ||
439 | |||
440 | /* | ||
441 | The FSGetVolFileSystemID function returns the file system ID and signature | ||
442 | of a mounted volume. The file system ID identifies the file system | ||
443 | that handles requests to a particular volume. The signature identifies the | ||
444 | volume type of the volume (for example, FSID 0 is Macintosh HFS Plus, HFS | ||
445 | or MFS, where a signature of 0x4244 identifies the volume as HFS). | ||
446 | Here's a partial list of file system ID numbers (only Apple's file systems | ||
447 | are listed): | ||
448 | FSID File System | ||
449 | ----- ----------------------------------------------------- | ||
450 | $0000 Macintosh HFS Plus, HFS or MFS | ||
451 | $0100 ProDOS File System | ||
452 | $0101 PowerTalk Mail Enclosures | ||
453 | $4147 ISO 9660 File Access (through Foreign File Access) | ||
454 | $4242 High Sierra File Access (through Foreign File Access) | ||
455 | $464D QuickTake File System (through Foreign File Access) | ||
456 | $4953 Macintosh PC Exchange (MS-DOS) | ||
457 | $4A48 Audio CD Access (through Foreign File Access) | ||
458 | $4D4B Apple Photo Access (through Foreign File Access) | ||
459 | $6173 AppleShare (later versions of AppleShare only) | ||
460 | |||
461 | See the Technical Note "FL 35 - Determining Which File System | ||
462 | Is Active" and the "Guide to the File System Manager" for more | ||
463 | information. | ||
464 | |||
465 | volume --> The volume reference number. | ||
466 | fileSystemID <** An optional pointer to a UInt16. | ||
467 | If not NULL, the volume's file system ID will | ||
468 | be returned in the UInt16. | ||
469 | signature <** An optional pointer to a UInt16. | ||
470 | If not NULL, the volume's signature will | ||
471 | be returned in the UInt16. | ||
472 | */ | ||
473 | |||
474 | /*****************************************************************************/ | ||
475 | |||
476 | #pragma mark FSGetMountedVolumes | ||
477 | |||
478 | OSErr | ||
479 | FSGetMountedVolumes( | ||
480 | FSRef ***volumeRefsHandle, /* pointer to handle of FSRefs */ | ||
481 | ItemCount *numVolumes); | ||
482 | |||
483 | /* | ||
484 | The FSGetMountedVolumes function returns the list of volumes currently | ||
485 | mounted in an array of FSRef records. The array of FSRef records is | ||
486 | returned in a Handle, volumeRefsHandle, which is allocated by | ||
487 | FSGetMountedVolumes. The caller is responsible for disposing of | ||
488 | volumeRefsHandle if the FSGetMountedVolumes returns noErr. | ||
489 | |||
490 | volumeRefsHandle <-- Pointer to an FSRef Handle where the array of | ||
491 | FSRefs is to be returned. | ||
492 | numVolumes <-- The number of volumes returned in the array. | ||
493 | */ | ||
494 | |||
495 | /*****************************************************************************/ | ||
496 | |||
497 | #pragma mark ----- FSRef/FSpec/Path/Name Conversion Routines ----- | ||
498 | |||
499 | /*****************************************************************************/ | ||
500 | |||
501 | #pragma mark FSRefMakeFSSpec | ||
502 | |||
503 | OSErr | ||
504 | FSRefMakeFSSpec( | ||
505 | const FSRef *ref, | ||
506 | FSSpec *spec); | ||
507 | |||
508 | /* | ||
509 | The FSRefMakeFSSpec function returns an FSSpec for the file or | ||
510 | directory specified by the ref parameter. | ||
511 | |||
512 | ref --> An FSRef specifying the file or directory. | ||
513 | spec <-- The FSSpec. | ||
514 | */ | ||
515 | |||
516 | /*****************************************************************************/ | ||
517 | |||
518 | #pragma mark FSMakeFSRef | ||
519 | |||
520 | OSErr | ||
521 | FSMakeFSRef( | ||
522 | FSVolumeRefNum volRefNum, | ||
523 | SInt32 dirID, | ||
524 | ConstStr255Param name, | ||
525 | FSRef *ref); | ||
526 | |||
527 | /* | ||
528 | The FSMakeFSRef function creates an FSRef from the traditional | ||
529 | volume reference number, directory ID and pathname inputs. It is | ||
530 | functionally equivalent to FSMakeFSSpec followed by FSpMakeFSRef. | ||
531 | |||
532 | volRefNum --> Volume specification. | ||
533 | dirID --> Directory specification. | ||
534 | name --> The file or directory name, or NULL. | ||
535 | ref <-- The FSRef. | ||
536 | */ | ||
537 | |||
538 | /*****************************************************************************/ | ||
539 | |||
540 | #pragma mark FSMakePath | ||
541 | |||
542 | OSStatus | ||
543 | FSMakePath( | ||
544 | SInt16 vRefNum, | ||
545 | SInt32 dirID, | ||
546 | ConstStr255Param name, | ||
547 | UInt8 *path, | ||
548 | UInt32 maxPathSize); | ||
549 | |||
550 | /* | ||
551 | The FSMakePath function creates a pathname from the traditional volume reference | ||
552 | number, directory ID, and pathname inputs. It is functionally equivalent to | ||
553 | FSMakeFSSpec, FSpMakeFSRef, FSRefMakePath. | ||
554 | |||
555 | volRefNum --> Volume specification. | ||
556 | dirID --> Directory specification. | ||
557 | name --> The file or directory name, or NULL. | ||
558 | path <-- A pointer to a buffer which FSMakePath will | ||
559 | fill with a C string representing the pathname | ||
560 | to the file or directory specified. The format of | ||
561 | the pathname returned can be determined with the | ||
562 | Gestalt selector gestaltFSAttr's | ||
563 | gestaltFSUsesPOSIXPathsForConversion bit. | ||
564 | If the gestaltFSUsesPOSIXPathsForConversion bit is | ||
565 | clear, the pathname is a Mac OS File Manager full | ||
566 | pathname in a C string, and file or directory names | ||
567 | in the pathname may be mangled as returned by | ||
568 | the File Manager. If the | ||
569 | gestaltFSUsesPOSIXPathsForConversion bit is set, | ||
570 | the pathname is a UTF8 encoded POSIX absolute | ||
571 | pathname in a C string. In either case, the | ||
572 | pathname returned can be passed back to | ||
573 | FSPathMakeRef to create an FSRef to the file or | ||
574 | directory, or FSPathMakeFSSpec to craete an FSSpec | ||
575 | to the file or directory. | ||
576 | maxPathSize --> The size of the path buffer in bytes. If the path | ||
577 | buffer is too small for the pathname string, | ||
578 | FSMakePath returns pathTooLongErr or | ||
579 | buffersTooSmall. | ||
580 | */ | ||
581 | |||
582 | /*****************************************************************************/ | ||
583 | |||
584 | #pragma mark FSPathMakeFSSpec | ||
585 | |||
586 | OSStatus | ||
587 | FSPathMakeFSSpec( | ||
588 | const UInt8 *path, | ||
589 | FSSpec *spec, | ||
590 | Boolean *isDirectory); /* can be NULL */ | ||
591 | |||
592 | /* | ||
593 | The FSPathMakeFSSpec function converts a pathname to an FSSpec. | ||
594 | |||
595 | path --> A pointer to a C String that is the pathname. The | ||
596 | format of the pathname you must supply can be | ||
597 | determined with the Gestalt selector gestaltFSAttr's | ||
598 | gestaltFSUsesPOSIXPathsForConversion bit. | ||
599 | If the gestaltFSUsesPOSIXPathsForConversion bit is | ||
600 | clear, the pathname must be a Mac OS File Manager | ||
601 | full pathname in a C string. If the | ||
602 | gestaltFSUsesPOSIXPathsForConversion bit is set, | ||
603 | the pathname must be a UTF8 encoded POSIX absolute | ||
604 | pathname in a C string. | ||
605 | spec <-- The FSSpec. | ||
606 | isDirectory <** An optional pointer to a Boolean. | ||
607 | If not NULL, true will be returned in the Boolean | ||
608 | if the specified path is a directory, or false will | ||
609 | be returned in the Boolean if the specified path is | ||
610 | a file. | ||
611 | */ | ||
612 | |||
613 | /*****************************************************************************/ | ||
614 | |||
615 | #pragma mark UnicodeNameGetHFSName | ||
616 | |||
617 | OSErr | ||
618 | UnicodeNameGetHFSName( | ||
619 | UniCharCount nameLength, | ||
620 | const UniChar *name, | ||
621 | TextEncoding textEncodingHint, | ||
622 | Boolean isVolumeName, | ||
623 | Str31 hfsName); | ||
624 | |||
625 | /* | ||
626 | The UnicodeNameGetHFSName function converts a Unicode string | ||
627 | to a Pascal Str31 (or Str27) string using an algorithm similar to that used | ||
628 | by the File Manager. Note that if the name is too long or cannot be converted | ||
629 | using the given text encoding hint, you will get an error instead of the | ||
630 | mangled name that the File Manager would return. | ||
631 | |||
632 | nameLength --> Number of UniChar in name parameter. | ||
633 | name --> The Unicode string to convert. | ||
634 | textEncodingHint --> The text encoding hint used for the conversion. | ||
635 | You can pass kTextEncodingUnknown to use the | ||
636 | "default" textEncodingHint. | ||
637 | isVolumeName --> If true, the output name will be limited to | ||
638 | 27 characters (kHFSMaxVolumeNameChars). If false, | ||
639 | the output name will be limited to 31 characters | ||
640 | (kHFSMaxFileNameChars). | ||
641 | hfsName <-- The hfsName as a Pascal string. | ||
642 | |||
643 | __________ | ||
644 | |||
645 | Also see: HFSNameGetUnicodeName | ||
646 | */ | ||
647 | |||
648 | /*****************************************************************************/ | ||
649 | |||
650 | #pragma mark HFSNameGetUnicodeName | ||
651 | |||
652 | OSErr | ||
653 | HFSNameGetUnicodeName( | ||
654 | ConstStr31Param hfsName, | ||
655 | TextEncoding textEncodingHint, | ||
656 | HFSUniStr255 *unicodeName); | ||
657 | |||
658 | /* | ||
659 | The HFSNameGetUnicodeName function converts a Pascal Str31 string to an | ||
660 | Unicode HFSUniStr255 string using the same routines as the File Manager. | ||
661 | |||
662 | hfsName --> The Pascal string to convert. | ||
663 | textEncodingHint --> The text encoding hint used for the conversion. | ||
664 | You can pass kTextEncodingUnknown to use the | ||
665 | "default" textEncodingHint. | ||
666 | unicodeName <-- The Unicode string. | ||
667 | |||
668 | __________ | ||
669 | |||
670 | Also see: UnicodeNameGetHFSName | ||
671 | */ | ||
672 | |||
673 | /*****************************************************************************/ | ||
674 | |||
675 | #pragma mark ----- File/Directory Manipulation Routines ----- | ||
676 | |||
677 | /*****************************************************************************/ | ||
678 | |||
679 | #pragma mark FSRefValid | ||
680 | |||
681 | Boolean FSRefValid(const FSRef *ref); | ||
682 | |||
683 | /* | ||
684 | The FSRefValid function determines if an FSRef is valid. If the result is | ||
685 | true, then the FSRef refers to an existing file or directory. | ||
686 | |||
687 | ref --> FSRef to a file or directory. | ||
688 | */ | ||
689 | |||
690 | /*****************************************************************************/ | ||
691 | |||
692 | #pragma mark FSGetParentRef | ||
693 | |||
694 | OSErr | ||
695 | FSGetParentRef( | ||
696 | const FSRef *ref, | ||
697 | FSRef *parentRef); | ||
698 | |||
699 | /* | ||
700 | The FSGetParentRef function gets the parent directory FSRef of the | ||
701 | specified object. | ||
702 | |||
703 | Note: FSRefs always point to real file system objects. So, there cannot | ||
704 | be a FSRef to the parent of volume root directories. If you call | ||
705 | FSGetParentRef with a ref to the root directory of a volume, the | ||
706 | function result will be noErr and the parentRef will be invalid (using it | ||
707 | for other file system requests will fail). | ||
708 | |||
709 | ref --> FSRef to a file or directory. | ||
710 | parentRef <-- The parent directory's FSRef. | ||
711 | */ | ||
712 | |||
713 | /*****************************************************************************/ | ||
714 | |||
715 | #pragma mark FSGetFileDirName | ||
716 | |||
717 | OSErr | ||
718 | FSGetFileDirName( | ||
719 | const FSRef *ref, | ||
720 | HFSUniStr255 *outName); | ||
721 | |||
722 | /* | ||
723 | The FSGetFileDirName function gets the name of the file or directory | ||
724 | specified. | ||
725 | |||
726 | ref --> FSRef to a file or directory. | ||
727 | outName <-- The file or directory name. | ||
728 | */ | ||
729 | |||
730 | /*****************************************************************************/ | ||
731 | |||
732 | #pragma mark FSGetNodeID | ||
733 | |||
734 | OSErr | ||
735 | FSGetNodeID( | ||
736 | const FSRef *ref, | ||
737 | long *nodeID, /* can be NULL */ | ||
738 | Boolean *isDirectory); /* can be NULL */ | ||
739 | |||
740 | /* | ||
741 | The GetNodeIDFromFSRef function gets the node ID number of the | ||
742 | file or directory specified (note: the node ID is the directory ID | ||
743 | for directories). | ||
744 | |||
745 | ref --> FSRef to a file or directory. | ||
746 | nodeID <** An optional pointer to a long. | ||
747 | If not NULL, the node ID will be returned in | ||
748 | the long. | ||
749 | isDirectory <** An optional pointer to a Boolean. | ||
750 | If not NULL, true will be returned in the Boolean | ||
751 | if the object is a directory, or false will be | ||
752 | returned in the Boolean if object is a file. | ||
753 | */ | ||
754 | |||
755 | /*****************************************************************************/ | ||
756 | |||
757 | #pragma mark FSGetUserPrivilegesPermissions | ||
758 | |||
759 | OSErr | ||
760 | FSGetUserPrivilegesPermissions( | ||
761 | const FSRef *ref, | ||
762 | UInt8 *userPrivileges, /* can be NULL */ | ||
763 | UInt32 permissions[4]); /* can be NULL */ | ||
764 | |||
765 | /* | ||
766 | The FSGetUserPrivilegesPermissions function gets the userPrivileges and/or | ||
767 | permissions of the file or directory specified. | ||
768 | |||
769 | ref --> FSRef to a file or directory. | ||
770 | userPrivileges <** An optional pointer to a UInt8. | ||
771 | If not NULL, the userPrivileges will be returned | ||
772 | in the UInt8. | ||
773 | permissions <** An optional pointer to an UInt32[4] array. | ||
774 | If not NULL, the permissions will be returned | ||
775 | in the UInt32[4] array. | ||
776 | */ | ||
777 | |||
778 | /*****************************************************************************/ | ||
779 | |||
780 | #pragma mark FSCheckLock | ||
781 | |||
782 | OSErr | ||
783 | FSCheckLock( | ||
784 | const FSRef *ref); | ||
785 | |||
786 | /* | ||
787 | The FSCheckLock function determines if a file or directory is locked. | ||
788 | If FSCheckLock returns noErr, then the file or directory is not locked | ||
789 | and the volume it is on is not locked either. If FSCheckLock returns | ||
790 | fLckdErr, then the file or directory is locked. If FSCheckLock returns | ||
791 | wPrErr, then the volume is locked by hardware (i.e., locked tab on | ||
792 | removable media). If FSCheckLock returns vLckdErr, then the volume is | ||
793 | locked by software. | ||
794 | |||
795 | ref --> FSRef to a file or directory. | ||
796 | */ | ||
797 | |||
798 | /*****************************************************************************/ | ||
799 | |||
800 | #pragma mark FSGetForkSizes | ||
801 | |||
802 | OSErr | ||
803 | FSGetForkSizes( | ||
804 | const FSRef *ref, | ||
805 | UInt64 *dataLogicalSize, /* can be NULL */ | ||
806 | UInt64 *rsrcLogicalSize); /* can be NULL */ | ||
807 | |||
808 | /* | ||
809 | The FSGetForkSizes returns the size of the data and/or resource fork for | ||
810 | the specified file. | ||
811 | |||
812 | ref --> FSRef to a file or directory. | ||
813 | dataLogicalSize <** An optional pointer to a UInt64. | ||
814 | If not NULL, the data fork's size will be | ||
815 | returned in the UInt64. | ||
816 | rsrcLogicalSize <** An optional pointer to a UInt64. | ||
817 | If not NULL, the resource fork's size will be | ||
818 | returned in the UInt64. | ||
819 | |||
820 | __________ | ||
821 | |||
822 | Also see: FSGetTotalForkSizes | ||
823 | */ | ||
824 | |||
825 | /*****************************************************************************/ | ||
826 | |||
827 | #pragma mark FSGetTotalForkSizes | ||
828 | |||
829 | OSErr | ||
830 | FSGetTotalForkSizes( | ||
831 | const FSRef *ref, | ||
832 | UInt64 *totalLogicalSize, /* can be NULL */ | ||
833 | UInt64 *totalPhysicalSize, /* can be NULL */ | ||
834 | ItemCount *forkCount); /* can be NULL */ | ||
835 | |||
836 | /* | ||
837 | The FSGetTotalForkSizes returns the total logical size and/or the total | ||
838 | physical size of the specified file (i.e., it adds the sizes of all file | ||
839 | forks). It optionally returns the number of file forks. | ||
840 | |||
841 | ref --> FSRef to a file or directory. | ||
842 | totalLogicalSize <** An optional pointer to a UInt64. | ||
843 | If not NULL, the sum of all fork logical sizes | ||
844 | will be returned in the UInt64. | ||
845 | totalPhysicalSize <** An optional pointer to a UInt64. | ||
846 | If not NULL, the sum of all fork physical sizes | ||
847 | will be returned in the UInt64. | ||
848 | forkCount <** An optional pointer to a ItemCount. | ||
849 | If not NULL, the number of file forks | ||
850 | will be returned in the ItemCount. | ||
851 | |||
852 | __________ | ||
853 | |||
854 | Also see: FSGetForkSizes | ||
855 | */ | ||
856 | |||
857 | /*****************************************************************************/ | ||
858 | |||
859 | #pragma mark FSBumpDate | ||
860 | |||
861 | OSErr | ||
862 | FSBumpDate( | ||
863 | const FSRef *ref); | ||
864 | |||
865 | /* | ||
866 | The FSBumpDate function changes the content modification date of a file | ||
867 | or directory to the current date/time. If the content modification date | ||
868 | is already equal to the current date/time, then add one second to the | ||
869 | content modification date. | ||
870 | |||
871 | ref --> FSRef to a file or directory. | ||
872 | */ | ||
873 | |||
874 | /*****************************************************************************/ | ||
875 | |||
876 | #pragma mark FSGetFinderInfo | ||
877 | |||
878 | OSErr | ||
879 | FSGetFinderInfo( | ||
880 | const FSRef *ref, | ||
881 | FinderInfo *info, /* can be NULL */ | ||
882 | ExtendedFinderInfo *extendedInfo, /* can be NULL */ | ||
883 | Boolean *isDirectory); /* can be NULL */ | ||
884 | |||
885 | /* | ||
886 | The FSGetFinderInfo function gets the finder information for a file or | ||
887 | directory. | ||
888 | |||
889 | ref --> FSRef to a file or directory. | ||
890 | info <** An optional pointer to a FinderInfo. | ||
891 | If not NULL, the FileInfo (if ref is a file) or | ||
892 | the FolderInfo (if ref is a folder) will be | ||
893 | returned in the FinderInfo. | ||
894 | extendedInfo <** An optional pointer to a ExtendedFinderInfo. | ||
895 | If not NULL, the ExtendedFileInfo (if ref is a file) | ||
896 | or the ExtendedFolderInfo (if ref is a folder) will | ||
897 | be returned in the ExtendedFinderInfo. | ||
898 | isDirectory <** An optional pointer to a Boolean. | ||
899 | If not NULL, true will be returned in the Boolean | ||
900 | if the object is a directory, or false will be | ||
901 | returned in the Boolean if object is a file. | ||
902 | |||
903 | __________ | ||
904 | |||
905 | Also see: FSSetFinderInfo | ||
906 | */ | ||
907 | |||
908 | /*****************************************************************************/ | ||
909 | |||
910 | #pragma mark FSSetFinderInfo | ||
911 | |||
912 | OSErr | ||
913 | FSSetFinderInfo( | ||
914 | const FSRef *ref, | ||
915 | const FinderInfo *info, /* can be NULL */ | ||
916 | const ExtendedFinderInfo *extendedInfo); /* can be NULL */ | ||
917 | |||
918 | /* | ||
919 | The FSSetFinderInfo function sets the finder information for a file or | ||
920 | directory. | ||
921 | |||
922 | ref --> FSRef to a file or directory. | ||
923 | info **> A pointer to a FinderInfo record with the new | ||
924 | FileInfo (if ref is a file) or new FolderInfo | ||
925 | (if ref is a folder), or NULL if the FinderInfo | ||
926 | is not to be changed. | ||
927 | extendedInfo **> A pointer to a FinderInfo record with the new | ||
928 | ExtendedFileInfo (if ref is a file) or new | ||
929 | ExtendedFolderInfo (if ref is a folder), or NULL | ||
930 | if the ExtendedFinderInfo is not to be changed. | ||
931 | |||
932 | __________ | ||
933 | |||
934 | Also see: FSGetFinderInfo | ||
935 | */ | ||
936 | |||
937 | /*****************************************************************************/ | ||
938 | |||
939 | #pragma mark FSChangeCreatorType | ||
940 | |||
941 | OSErr | ||
942 | FSChangeCreatorType( | ||
943 | const FSRef *ref, | ||
944 | OSType fileCreator, | ||
945 | OSType fileType); | ||
946 | |||
947 | /* | ||
948 | The FSChangeCreatorType function changes the creator and/or file type of a file. | ||
949 | |||
950 | ref --> FSRef to a file. | ||
951 | creator --> The new creator type or 0x00000000 to leave | ||
952 | the creator type alone. | ||
953 | fileType --> The new file type or 0x00000000 to leave the | ||
954 | file type alone. | ||
955 | */ | ||
956 | |||
957 | /*****************************************************************************/ | ||
958 | |||
959 | #pragma mark FSChangeFinderFlags | ||
960 | |||
961 | OSErr | ||
962 | FSChangeFinderFlags( | ||
963 | const FSRef *ref, | ||
964 | Boolean setBits, | ||
965 | UInt16 flagBits); | ||
966 | |||
967 | /* | ||
968 | The FSChangeFinderFlags function sets or clears flag bits in | ||
969 | the finderFlags field of a file's FileInfo record or a | ||
970 | directory's FolderInfo record. | ||
971 | |||
972 | ref --> FSRef to a file or directory. | ||
973 | setBits --> If true, then set the bits specified in flagBits. | ||
974 | If false, then clear the bits specified in flagBits. | ||
975 | flagBits --> The flagBits parameter specifies which Finder Flag | ||
976 | bits to set or clear. If a bit in flagBits is set, | ||
977 | then the same bit in fdFlags is either set or | ||
978 | cleared depending on the state of the setBits | ||
979 | parameter. | ||
980 | */ | ||
981 | |||
982 | /*****************************************************************************/ | ||
983 | |||
984 | #pragma mark FSSetInvisible | ||
985 | |||
986 | OSErr | ||
987 | FSSetInvisible( | ||
988 | const FSRef *ref); | ||
989 | |||
990 | #pragma mark FSClearInvisible | ||
991 | |||
992 | OSErr | ||
993 | FSClearInvisible( | ||
994 | const FSRef *ref); | ||
995 | |||
996 | /* | ||
997 | The FSSetInvisible and FSClearInvisible functions set or clear the | ||
998 | kIsInvisible bit in the finderFlags field of the specified file or | ||
999 | directory's finder information. | ||
1000 | |||
1001 | ref --> FSRef to a file or directory. | ||
1002 | */ | ||
1003 | |||
1004 | /*****************************************************************************/ | ||
1005 | |||
1006 | #pragma mark FSSetNameLocked | ||
1007 | |||
1008 | OSErr | ||
1009 | FSSetNameLocked( | ||
1010 | const FSRef *ref); | ||
1011 | |||
1012 | #pragma mark FSClearNameLocked | ||
1013 | |||
1014 | OSErr | ||
1015 | FSClearNameLocked( | ||
1016 | const FSRef *ref); | ||
1017 | |||
1018 | /* | ||
1019 | The FSSetNameLocked and FSClearNameLocked functions set or clear the | ||
1020 | kNameLocked bit bit in the finderFlags field of the specified file or | ||
1021 | directory's finder information. | ||
1022 | |||
1023 | ref --> FSRef to a file or directory. | ||
1024 | */ | ||
1025 | |||
1026 | /*****************************************************************************/ | ||
1027 | |||
1028 | #pragma mark FSSetIsStationery | ||
1029 | |||
1030 | OSErr | ||
1031 | FSSetIsStationery( | ||
1032 | const FSRef *ref); | ||
1033 | |||
1034 | #pragma mark FSClearIsStationery | ||
1035 | |||
1036 | OSErr | ||
1037 | FSClearIsStationery( | ||
1038 | const FSRef *ref); | ||
1039 | |||
1040 | /* | ||
1041 | The FSSetIsStationery and FSClearIsStationery functions set or clear the | ||
1042 | kIsStationery bit bit in the finderFlags field of the specified file or | ||
1043 | directory's finder information. | ||
1044 | |||
1045 | ref --> FSRef to a file or directory. | ||
1046 | */ | ||
1047 | |||
1048 | /*****************************************************************************/ | ||
1049 | |||
1050 | #pragma mark FSSetHasCustomIcon | ||
1051 | |||
1052 | OSErr | ||
1053 | FSSetHasCustomIcon( | ||
1054 | const FSRef *ref); | ||
1055 | |||
1056 | #pragma mark FSClearHasCustomIcon | ||
1057 | |||
1058 | OSErr | ||
1059 | FSClearHasCustomIcon( | ||
1060 | const FSRef *ref); | ||
1061 | |||
1062 | /* | ||
1063 | The FSSetHasCustomIcon and FSClearHasCustomIcon functions set or clear the | ||
1064 | kHasCustomIcon bit bit in the finderFlags field of the specified file or | ||
1065 | directory's finder information. | ||
1066 | |||
1067 | ref --> FSRef to a file or directory. | ||
1068 | */ | ||
1069 | |||
1070 | /*****************************************************************************/ | ||
1071 | |||
1072 | #pragma mark FSClearHasBeenInited | ||
1073 | |||
1074 | OSErr | ||
1075 | FSClearHasBeenInited( | ||
1076 | const FSRef *ref); | ||
1077 | |||
1078 | /* | ||
1079 | The FSClearHasBeenInited function clears the kHasBeenInited bit in the | ||
1080 | finderFlags field of the specified file or directory's finder information. | ||
1081 | |||
1082 | Note: There is no FSSetHasBeenInited function because ONLY the Finder | ||
1083 | should set the kHasBeenInited bit. | ||
1084 | |||
1085 | ref --> FSRef to a file or directory. | ||
1086 | */ | ||
1087 | |||
1088 | /*****************************************************************************/ | ||
1089 | |||
1090 | #pragma mark FSCopyFileMgrAttributes | ||
1091 | |||
1092 | OSErr | ||
1093 | FSCopyFileMgrAttributes( | ||
1094 | const FSRef *sourceRef, | ||
1095 | const FSRef *destinationRef, | ||
1096 | Boolean copyLockBit); | ||
1097 | |||
1098 | /* | ||
1099 | The CopyFileMgrAttributes function copies all File Manager attributes | ||
1100 | from the source file or directory to the destination file or directory. | ||
1101 | If copyLockBit is true, then set the locked state of the destination | ||
1102 | to match the source. | ||
1103 | |||
1104 | sourceRef --> FSRef to a file or directory. | ||
1105 | destinationRef --> FSRef to a file or directory. | ||
1106 | copyLockBit --> If true, set the locked state of the destination | ||
1107 | to match the source. | ||
1108 | */ | ||
1109 | |||
1110 | /*****************************************************************************/ | ||
1111 | |||
1112 | #pragma mark FSMoveRenameObjectUnicode | ||
1113 | |||
1114 | OSErr | ||
1115 | FSMoveRenameObjectUnicode( | ||
1116 | const FSRef *ref, | ||
1117 | const FSRef *destDirectory, | ||
1118 | UniCharCount nameLength, | ||
1119 | const UniChar *name, /* can be NULL (no rename during move) */ | ||
1120 | TextEncoding textEncodingHint, | ||
1121 | FSRef *newRef); /* if function fails along the way, newRef is final location of file */ | ||
1122 | |||
1123 | /* | ||
1124 | The FSMoveRenameObjectUnicode function moves a file or directory and | ||
1125 | optionally renames it. The source and destination locations must be on | ||
1126 | the same volume. | ||
1127 | |||
1128 | Note: If the input ref parameter is invalid, this call will fail and | ||
1129 | newRef, like ref, will be invalid. | ||
1130 | |||
1131 | ref --> FSRef to a file or directory. | ||
1132 | destDirectory --> FSRef to the destination directory. | ||
1133 | nameLength --> Number of UniChar in name parameter. | ||
1134 | name --> An Unicode string with the new name for the | ||
1135 | moved object, or NULL if no rename is wanted. | ||
1136 | textEncodingHint --> The text encoding hint used for the rename. | ||
1137 | You can pass kTextEncodingUnknown to use the | ||
1138 | "default" textEncodingHint. | ||
1139 | newRef <-- The new FSRef of the object moved. Note that if | ||
1140 | this function fails at any step along the way, | ||
1141 | newRef is still then final location of the object. | ||
1142 | */ | ||
1143 | |||
1144 | /*****************************************************************************/ | ||
1145 | |||
1146 | #pragma mark FSDeleteContainerContents | ||
1147 | |||
1148 | OSErr | ||
1149 | FSDeleteContainerContents( | ||
1150 | const FSRef *container); | ||
1151 | |||
1152 | /* | ||
1153 | The FSDeleteContainerContents function deletes the contents of a container | ||
1154 | directory. All files and subdirectories in the specified container are | ||
1155 | deleted. If a locked file or directory is encountered, it is unlocked and | ||
1156 | then deleted. If any unexpected errors are encountered, | ||
1157 | FSDeleteContainerContents quits and returns to the caller. | ||
1158 | |||
1159 | container --> FSRef to a directory. | ||
1160 | |||
1161 | __________ | ||
1162 | |||
1163 | Also see: FSDeleteContainer | ||
1164 | */ | ||
1165 | |||
1166 | /*****************************************************************************/ | ||
1167 | |||
1168 | #pragma mark FSDeleteContainer | ||
1169 | |||
1170 | OSErr | ||
1171 | FSDeleteContainer( | ||
1172 | const FSRef *container); | ||
1173 | |||
1174 | /* | ||
1175 | The FSDeleteContainer function deletes a container directory and its contents. | ||
1176 | All files and subdirectories in the specified container are deleted. | ||
1177 | If a locked file or directory is encountered, it is unlocked and then | ||
1178 | deleted. After deleting the container's contents, the container is | ||
1179 | deleted. If any unexpected errors are encountered, FSDeleteContainer | ||
1180 | quits and returns to the caller. | ||
1181 | |||
1182 | container --> FSRef to a directory. | ||
1183 | |||
1184 | __________ | ||
1185 | |||
1186 | Also see: FSDeleteContainerContents | ||
1187 | */ | ||
1188 | |||
1189 | /*****************************************************************************/ | ||
1190 | |||
1191 | #pragma mark IterateContainerFilterProcPtr | ||
1192 | |||
1193 | typedef CALLBACK_API( Boolean , IterateContainerFilterProcPtr ) ( | ||
1194 | Boolean containerChanged, | ||
1195 | ItemCount currentLevel, | ||
1196 | const FSCatalogInfo *catalogInfo, | ||
1197 | const FSRef *ref, | ||
1198 | const FSSpec *spec, | ||
1199 | const HFSUniStr255 *name, | ||
1200 | void *yourDataPtr); | ||
1201 | |||
1202 | /* | ||
1203 | This is the prototype for the IterateContainerFilterProc function which | ||
1204 | is called once for each file and directory found by FSIterateContainer. | ||
1205 | The IterateContainerFilterProc can use the read-only data it receives for | ||
1206 | whatever it wants. | ||
1207 | |||
1208 | The result of the IterateContainerFilterProc function indicates if | ||
1209 | iteration should be stopped. To stop iteration, return true; to continue | ||
1210 | iteration, return false. | ||
1211 | |||
1212 | The yourDataPtr parameter can point to whatever data structure you might | ||
1213 | want to access from within the IterateContainerFilterProc. | ||
1214 | |||
1215 | containerChanged --> Set to true if the container's contents changed | ||
1216 | during iteration. | ||
1217 | currentLevel --> The current recursion level into the container. | ||
1218 | 1 = the container, 2 = the container's immediate | ||
1219 | subdirectories, etc. | ||
1220 | catalogInfo --> The catalog information for the current object. | ||
1221 | Only the fields requested by the whichInfo | ||
1222 | parameter passed to FSIterateContainer are valid. | ||
1223 | ref --> The FSRef to the current object. | ||
1224 | spec --> The FSSpec to the current object if the wantFSSpec | ||
1225 | parameter passed to FSIterateContainer is true. | ||
1226 | name --> The name of the current object if the wantName | ||
1227 | parameter passed to FSIterateContainer is true. | ||
1228 | yourDataPtr --> An optional pointer to whatever data structure you | ||
1229 | might want to access from within the | ||
1230 | IterateFilterProc. | ||
1231 | result <-- To stop iteration, return true; to continue | ||
1232 | iteration, return false. | ||
1233 | |||
1234 | __________ | ||
1235 | |||
1236 | Also see: FSIterateContainer | ||
1237 | */ | ||
1238 | |||
1239 | /*****************************************************************************/ | ||
1240 | |||
1241 | #pragma mark CallIterateContainerFilterProc | ||
1242 | |||
1243 | #define CallIterateContainerFilterProc(userRoutine, containerChanged, currentLevel, catalogInfo, ref, spec, name, yourDataPtr) \ | ||
1244 | (*(userRoutine))((containerChanged), (currentLevel), (catalogInfo), (ref), (spec), (name), (yourDataPtr)) | ||
1245 | |||
1246 | /*****************************************************************************/ | ||
1247 | |||
1248 | #pragma mark FSIterateContainer | ||
1249 | |||
1250 | OSErr | ||
1251 | FSIterateContainer( | ||
1252 | const FSRef *container, | ||
1253 | ItemCount maxLevels, | ||
1254 | FSCatalogInfoBitmap whichInfo, | ||
1255 | Boolean wantFSSpec, | ||
1256 | Boolean wantName, | ||
1257 | IterateContainerFilterProcPtr iterateFilter, | ||
1258 | void *yourDataPtr); | ||
1259 | |||
1260 | /* | ||
1261 | The FSIterateContainer function performs a recursive iteration (scan) of the | ||
1262 | specified container directory and calls your IterateContainerFilterProc | ||
1263 | function once for each file and directory found. | ||
1264 | |||
1265 | The maxLevels parameter lets you control how deep the recursion goes. | ||
1266 | If maxLevels is 1, FSIterateContainer only scans the specified directory; | ||
1267 | if maxLevels is 2, FSIterateContainer scans the specified directory and | ||
1268 | one subdirectory below the specified directory; etc. Set maxLevels to | ||
1269 | zero to scan all levels. | ||
1270 | |||
1271 | The yourDataPtr parameter can point to whatever data structure you might | ||
1272 | want to access from within your IterateContainerFilterProc. | ||
1273 | |||
1274 | container --> The FSRef to the container directory to iterate. | ||
1275 | maxLevels --> Maximum number of directory levels to scan or | ||
1276 | zero to scan all directory levels. | ||
1277 | whichInfo --> The fields of the FSCatalogInfo you wish to get. | ||
1278 | wantFSSpec --> Set to true if you want the FSSpec to each | ||
1279 | object passed to your IterateContainerFilterProc. | ||
1280 | wantName --> Set to true if you want the name of each | ||
1281 | object passed to your IterateContainerFilterProc. | ||
1282 | iterateFilter --> A pointer to the IterateContainerFilterProc you | ||
1283 | want called once for each file and directory found | ||
1284 | by FSIterateContainer. | ||
1285 | yourDataPtr --> An optional pointer to whatever data structure you | ||
1286 | might want to access from within the | ||
1287 | IterateFilterProc. | ||
1288 | */ | ||
1289 | |||
1290 | /*****************************************************************************/ | ||
1291 | |||
1292 | #pragma mark FSGetDirectoryItems | ||
1293 | |||
1294 | OSErr | ||
1295 | FSGetDirectoryItems( | ||
1296 | const FSRef *container, | ||
1297 | FSRef ***refsHandle, /* pointer to handle of FSRefs */ | ||
1298 | ItemCount *numRefs, | ||
1299 | Boolean *containerChanged); | ||
1300 | |||
1301 | /* | ||
1302 | The FSGetDirectoryItems function returns the list of items in the specified | ||
1303 | container. The array of FSRef records is returned in a Handle, refsHandle, | ||
1304 | which is allocated by FSGetDirectoryItems. The caller is responsible for | ||
1305 | disposing of refsHandle if the FSGetDirectoryItems returns noErr. | ||
1306 | |||
1307 | container --> FSRef to a directory. | ||
1308 | refsHandle <-- Pointer to an FSRef Handle where the array of | ||
1309 | FSRefs is to be returned. | ||
1310 | numRefs <-- The number of FSRefs returned in the array. | ||
1311 | containerChanged <-- Set to true if the container changes while the | ||
1312 | list of items is being obtained. | ||
1313 | */ | ||
1314 | |||
1315 | /*****************************************************************************/ | ||
1316 | |||
1317 | #pragma mark FSExchangeObjectsCompat | ||
1318 | |||
1319 | OSErr | ||
1320 | FSExchangeObjectsCompat( | ||
1321 | const FSRef *sourceRef, | ||
1322 | const FSRef *destRef, | ||
1323 | FSRef *newSourceRef, | ||
1324 | FSRef *newDestRef); | ||
1325 | |||
1326 | /* | ||
1327 | The FSExchangeObjectsCompat function exchanges the data between two files. | ||
1328 | |||
1329 | The FSExchangeObjectsCompat function is an enhanced version of | ||
1330 | FSExchangeObjects function. The two enhancements FSExchangeObjectsCompat | ||
1331 | provides are: | ||
1332 | |||
1333 | 1, FSExchangeObjectsCompat will work on volumes which do not support | ||
1334 | FSExchangeObjects. FSExchangeObjectsCompat does this by emulating | ||
1335 | FSExchangeObjects through a series of File Manager operations. If | ||
1336 | there is a failure at any step along the way, FSExchangeObjectsCompat | ||
1337 | attempts to undo any steps already taken to leave the files in their | ||
1338 | original state in their original locations. | ||
1339 | |||
1340 | 2. FSExchangeObjectsCompat returns new FSRefs to the source and | ||
1341 | destination files. Note that if this function fails at any step along | ||
1342 | the way, newSourceRef and newDestRef still give you access to the final | ||
1343 | locations of the files being exchanged -- even if they are renamed or | ||
1344 | not in their original locations. | ||
1345 | |||
1346 | sourceRef --> FSRef to the source file. | ||
1347 | destRef --> FSRef to the destination file. | ||
1348 | newSourceRef <-- The new FSRef to the source file. | ||
1349 | newDestRef <-- The new FSRef to the destination file. | ||
1350 | */ | ||
1351 | |||
1352 | /*****************************************************************************/ | ||
1353 | |||
1354 | #pragma mark ----- Shared Environment Routines ----- | ||
1355 | |||
1356 | /*****************************************************************************/ | ||
1357 | |||
1358 | #pragma mark FSLockRange | ||
1359 | |||
1360 | OSErr | ||
1361 | MoreFiles_FSLockRange( | ||
1362 | SInt16 refNum, | ||
1363 | SInt32 rangeLength, | ||
1364 | SInt32 rangeStart); | ||
1365 | |||
1366 | /* | ||
1367 | The LockRange function locks (denies access to) a portion of a file | ||
1368 | that was opened with shared read/write permission. | ||
1369 | |||
1370 | refNum --> The file reference number of an open file. | ||
1371 | rangeLength --> The number of bytes in the range. | ||
1372 | rangeStart --> The starting byte in the range to lock. | ||
1373 | |||
1374 | __________ | ||
1375 | |||
1376 | Also see: UnlockRange | ||
1377 | */ | ||
1378 | |||
1379 | /*****************************************************************************/ | ||
1380 | |||
1381 | #pragma mark FSUnlockRange | ||
1382 | |||
1383 | OSErr | ||
1384 | MoreFiles_FSUnlockRange( | ||
1385 | SInt16 refNum, | ||
1386 | SInt32 rangeLength, | ||
1387 | SInt32 rangeStart); | ||
1388 | |||
1389 | /* | ||
1390 | The UnlockRange function unlocks (allows access to) a previously locked | ||
1391 | portion of a file that was opened with shared read/write permission. | ||
1392 | |||
1393 | refNum --> The file reference number of an open file. | ||
1394 | rangeLength --> The number of bytes in the range. | ||
1395 | rangeStart --> The starting byte in the range to unlock. | ||
1396 | |||
1397 | __________ | ||
1398 | |||
1399 | Also see: LockRange | ||
1400 | */ | ||
1401 | |||
1402 | /*****************************************************************************/ | ||
1403 | |||
1404 | #pragma mark FSGetDirAccess | ||
1405 | |||
1406 | OSErr | ||
1407 | FSGetDirAccess( | ||
1408 | const FSRef *ref, | ||
1409 | SInt32 *ownerID, /* can be NULL */ | ||
1410 | SInt32 *groupID, /* can be NULL */ | ||
1411 | SInt32 *accessRights); /* can be NULL */ | ||
1412 | |||
1413 | /* | ||
1414 | The FSGetDirAccess function retrieves the directory access control | ||
1415 | information for a directory on a shared volume. | ||
1416 | |||
1417 | ref --> An FSRef specifying the directory. | ||
1418 | ownerID <** An optional pointer to a SInt32. | ||
1419 | If not NULL, the directory's owner ID | ||
1420 | will be returned in the SInt32. | ||
1421 | groupID <** An optional pointer to a SInt32. | ||
1422 | If not NULL, the directory's group ID, or 0 | ||
1423 | if no group affiliation, will be returned in | ||
1424 | the SInt32. | ||
1425 | accessRights <** An optional pointer to a SInt32. | ||
1426 | If not NULL, the directory's access rights | ||
1427 | will be returned in the SInt32. | ||
1428 | |||
1429 | __________ | ||
1430 | |||
1431 | Also see: FSSetDirAccess, FSMapID, FSMapName | ||
1432 | */ | ||
1433 | |||
1434 | /*****************************************************************************/ | ||
1435 | |||
1436 | #pragma mark FSSetDirAccess | ||
1437 | |||
1438 | OSErr | ||
1439 | FSSetDirAccess( | ||
1440 | const FSRef *ref, | ||
1441 | SInt32 ownerID, | ||
1442 | SInt32 groupID, | ||
1443 | SInt32 accessRights); | ||
1444 | |||
1445 | /* | ||
1446 | The FSpSetDirAccess function changes the directory access control | ||
1447 | information for a directory on a shared volume. You must be the owner of | ||
1448 | a directory to change its access control information. | ||
1449 | |||
1450 | ref --> An FSRef specifying the directory. | ||
1451 | ownerID --> The directory's owner ID. | ||
1452 | groupID --> The directory's group ID or 0 if no group affiliation. | ||
1453 | accessRights --> The directory's access rights. | ||
1454 | |||
1455 | __________ | ||
1456 | |||
1457 | Also see: FSGetDirAccess, FSMapID, FSMapName | ||
1458 | */ | ||
1459 | |||
1460 | /*****************************************************************************/ | ||
1461 | |||
1462 | #pragma mark FSGetVolMountInfoSize | ||
1463 | |||
1464 | OSErr | ||
1465 | FSGetVolMountInfoSize( | ||
1466 | FSVolumeRefNum volRefNum, | ||
1467 | SInt16 *size); | ||
1468 | |||
1469 | /* | ||
1470 | The FSGetVolMountInfoSize function determines the how much space the | ||
1471 | program needs to allocate for a volume mounting information record. | ||
1472 | |||
1473 | volRefNum --> Volume specification. | ||
1474 | size <-- The space needed (in bytes) of the volume | ||
1475 | mounting information record. | ||
1476 | |||
1477 | __________ | ||
1478 | |||
1479 | Also see: FSGetVolMountInfo, VolumeMount | ||
1480 | */ | ||
1481 | |||
1482 | /*****************************************************************************/ | ||
1483 | |||
1484 | #pragma mark FSGetVolMountInfo | ||
1485 | |||
1486 | OSErr | ||
1487 | FSGetVolMountInfo( | ||
1488 | FSVolumeRefNum volRefNum, | ||
1489 | void *volMountInfo); | ||
1490 | |||
1491 | /* | ||
1492 | The FSGetVolMountInfo function retrieves a volume mounting information | ||
1493 | record containing all the information needed to mount the volume, | ||
1494 | except for passwords. | ||
1495 | |||
1496 | volRefNum --> Volume specification. | ||
1497 | volMountInfo <-- The volume mounting information. | ||
1498 | |||
1499 | __________ | ||
1500 | |||
1501 | Also see: FSGetVolMountInfoSize, VolumeMount | ||
1502 | */ | ||
1503 | |||
1504 | /*****************************************************************************/ | ||
1505 | |||
1506 | #pragma mark FSVolumeMount | ||
1507 | |||
1508 | OSErr | ||
1509 | FSVolumeMount( | ||
1510 | const void *volMountInfo, | ||
1511 | FSVolumeRefNum *volRefNum); | ||
1512 | |||
1513 | /* | ||
1514 | The VolumeMount function mounts a volume using a volume mounting | ||
1515 | information record. | ||
1516 | |||
1517 | volMountInfo --> A volume mounting information record. | ||
1518 | volRefNum <-- The volume reference number. | ||
1519 | |||
1520 | __________ | ||
1521 | |||
1522 | Also see: FSGetVolMountInfoSize, FSGetVolMountInfo | ||
1523 | */ | ||
1524 | |||
1525 | /*****************************************************************************/ | ||
1526 | |||
1527 | #pragma mark FSMapID | ||
1528 | |||
1529 | OSErr | ||
1530 | FSMapID( | ||
1531 | FSVolumeRefNum volRefNum, | ||
1532 | SInt32 ugID, | ||
1533 | SInt16 objType, | ||
1534 | Str31 name); | ||
1535 | |||
1536 | /* | ||
1537 | The FSMapID function determines the name of a user or group if you know | ||
1538 | the user or group ID. | ||
1539 | |||
1540 | volRefNum --> Volume specification. | ||
1541 | objType --> The mapping function code: | ||
1542 | kOwnerID2Name to map a user ID to a user name | ||
1543 | kGroupID2Name to map a group ID to a group name | ||
1544 | name <** An optional pointer to a buffer (minimum Str31). | ||
1545 | If not NULL, the user or group name | ||
1546 | will be returned in the buffer. | ||
1547 | |||
1548 | __________ | ||
1549 | |||
1550 | Also see: FSGetDirAccess, FSSetDirAccess, FSMapName | ||
1551 | */ | ||
1552 | |||
1553 | /*****************************************************************************/ | ||
1554 | |||
1555 | #pragma mark FSMapName | ||
1556 | |||
1557 | OSErr | ||
1558 | FSMapName( | ||
1559 | FSVolumeRefNum volRefNum, | ||
1560 | ConstStr255Param name, | ||
1561 | SInt16 objType, | ||
1562 | SInt32 *ugID); | ||
1563 | |||
1564 | /* | ||
1565 | The FSMapName function determines the user or group ID if you know the | ||
1566 | user or group name. | ||
1567 | |||
1568 | volRefNum --> Volume specification. | ||
1569 | name --> The user or group name. | ||
1570 | objType --> The mapping function code: | ||
1571 | kOwnerName2ID to map a user name to a user ID | ||
1572 | kGroupName2ID to map a user name to a group ID | ||
1573 | ugID <-- The user or group ID. | ||
1574 | |||
1575 | __________ | ||
1576 | |||
1577 | Also see: FSGetDirAccess, FSSetDirAccess, FSMapID | ||
1578 | */ | ||
1579 | |||
1580 | /*****************************************************************************/ | ||
1581 | |||
1582 | #pragma mark FSCopyFile | ||
1583 | |||
1584 | OSErr | ||
1585 | FSCopyFile( | ||
1586 | const FSRef *srcFileRef, | ||
1587 | const FSRef *dstDirectoryRef, | ||
1588 | UniCharCount nameLength, | ||
1589 | const UniChar *copyName, /* can be NULL (no rename during copy) */ | ||
1590 | TextEncoding textEncodingHint, | ||
1591 | FSRef *newRef); /* can be NULL */ | ||
1592 | |||
1593 | /* | ||
1594 | The FSCopyFile function duplicates a file and optionally renames it. | ||
1595 | The source and destination volumes must be on the same file server. | ||
1596 | This function instructs the server to copy the file. | ||
1597 | |||
1598 | srcFileRef --> An FSRef specifying the source file. | ||
1599 | dstDirectoryRef --> An FSRef specifying the destination directory. | ||
1600 | nameLength --> Number of UniChar in copyName parameter (ignored | ||
1601 | if copyName is NULL). | ||
1602 | copyName --> Points to the new file name if the file is to be | ||
1603 | renamed, or NULL if the file isn't to be renamed. | ||
1604 | textEncodingHint --> The text encoding hint used for the rename. | ||
1605 | You can pass kTextEncodingUnknown to use the | ||
1606 | "default" textEncodingHint. | ||
1607 | newRef <** An optional pointer to a FSRef. | ||
1608 | If not NULL, the FSRef of the duplicated file | ||
1609 | will be returned in the FSRef. | ||
1610 | */ | ||
1611 | |||
1612 | /*****************************************************************************/ | ||
1613 | |||
1614 | #pragma mark FSMoveRename | ||
1615 | |||
1616 | OSErr | ||
1617 | FSMoveRename( | ||
1618 | const FSRef *srcFileRef, | ||
1619 | const FSRef *dstDirectoryRef, | ||
1620 | UniCharCount nameLength, | ||
1621 | const UniChar *moveName, /* can be NULL (no rename during move) */ | ||
1622 | TextEncoding textEncodingHint, | ||
1623 | FSRef *newRef); /* can be NULL */ | ||
1624 | |||
1625 | /* | ||
1626 | The FSMoveRename function moves a file or directory (object), and | ||
1627 | optionally renames it. The source and destination locations must be on | ||
1628 | the same shared volume. | ||
1629 | |||
1630 | srcFileRef --> An FSRef specifying the source file. | ||
1631 | dstDirectoryRef --> An FSRef specifying the destination directory. | ||
1632 | nameLength --> Number of UniChar in moveName parameter (ignored | ||
1633 | if copyName is NULL) | ||
1634 | moveName --> Points to the new object name if the object is to be | ||
1635 | renamed, or NULL if the object isn't to be renamed. | ||
1636 | textEncodingHint --> The text encoding hint used for the rename. | ||
1637 | You can pass kTextEncodingUnknown to use the | ||
1638 | "default" textEncodingHint. | ||
1639 | newRef <** An optional pointer to a FSRef. | ||
1640 | If not NULL, the FSRef of the moved object | ||
1641 | will be returned in the FSRef. | ||
1642 | */ | ||
1643 | |||
1644 | /*****************************************************************************/ | ||
1645 | |||
1646 | #pragma mark ----- File ID Routines ----- | ||
1647 | |||
1648 | /*****************************************************************************/ | ||
1649 | |||
1650 | #pragma mark FSResolveFileIDRef | ||
1651 | |||
1652 | OSErr | ||
1653 | FSResolveFileIDRef( | ||
1654 | FSVolumeRefNum volRefNum, | ||
1655 | SInt32 fileID, | ||
1656 | FSRef *ref); | ||
1657 | |||
1658 | /* | ||
1659 | The FSResolveFileIDRef function returns an FSRef for the file with the | ||
1660 | specified file ID reference. | ||
1661 | |||
1662 | volRefNum --> Volume specification. | ||
1663 | fileID --> The file ID reference. | ||
1664 | ref <-- The FSRef for the file ID reference. | ||
1665 | |||
1666 | __________ | ||
1667 | |||
1668 | Also see: FSCreateFileIDRef, FSDeleteFileIDRef | ||
1669 | */ | ||
1670 | |||
1671 | /*****************************************************************************/ | ||
1672 | |||
1673 | #pragma mark FSCreateFileIDRef | ||
1674 | |||
1675 | OSErr | ||
1676 | FSCreateFileIDRef( | ||
1677 | const FSRef *ref, | ||
1678 | SInt32 *fileID); | ||
1679 | |||
1680 | /* | ||
1681 | The FSCreateFileIDRef function creates a file ID reference for the | ||
1682 | specified file, or if a file ID reference already exists, supplies | ||
1683 | the file ID reference and returns the result code fidExists or afpIDExists. | ||
1684 | |||
1685 | ref --> The FSRef for the file. | ||
1686 | fileID <-- The file ID reference (if result is noErr, | ||
1687 | fidExists, or afpIDExists). | ||
1688 | |||
1689 | __________ | ||
1690 | |||
1691 | Also see: GetFSRefFromFileIDRef, FSDeleteFileIDRef | ||
1692 | */ | ||
1693 | |||
1694 | /*****************************************************************************/ | ||
1695 | |||
1696 | #pragma mark FSDeleteFileIDRef | ||
1697 | |||
1698 | /* | ||
1699 | Why is there no FSDeleteFileIDRef routine? There are two reasons: | ||
1700 | |||
1701 | 1. Since Mac OS 8.1, PBDeleteFileIDRef hasn't deleted file ID references. | ||
1702 | On HFS volumes, deleting a file ID reference breaks aliases (which | ||
1703 | use file ID references to track files as they are moved around on a | ||
1704 | volume) and file ID references are automatically deleted when the file | ||
1705 | they refer to is deleted. On HFS Plus volumes, file ID references are | ||
1706 | always created when a file is created, deleted when the file is deleted, | ||
1707 | and cannot be deleted at any other time. | ||
1708 | |||
1709 | 2. PBDeleteFileIDRef causes a memory access fault under Mac OS X 10.0 | ||
1710 | through 10.1.x. While this will be fixed in a future release, the | ||
1711 | implementation, like the Mac OS 8/9 implementation, does not delete | ||
1712 | file ID references. | ||
1713 | |||
1714 | __________ | ||
1715 | |||
1716 | Also see: GetFSRefFromFileIDRef, FSCreateFileIDRef | ||
1717 | */ | ||
1718 | |||
1719 | /*****************************************************************************/ | ||
1720 | |||
1721 | #pragma mark ----- Utility Routines ----- | ||
1722 | |||
1723 | /*****************************************************************************/ | ||
1724 | |||
1725 | #pragma mark GetTempBuffer | ||
1726 | |||
1727 | Ptr | ||
1728 | GetTempBuffer( | ||
1729 | ByteCount buffReqSize, | ||
1730 | ByteCount *buffActSize); | ||
1731 | |||
1732 | /* | ||
1733 | The GetTempBuffer function allocates a temporary buffer for file system | ||
1734 | operations which is at least 4K bytes and a multiple of 4K bytes. | ||
1735 | |||
1736 | buffReqSize --> Size you'd like the buffer to be. | ||
1737 | buffActSize <-- The size of the buffer allocated. | ||
1738 | function result <-- Pointer to memory allocated, or NULL if no memory | ||
1739 | was available. The caller is responsible for | ||
1740 | disposing of this buffer with DisposePtr. | ||
1741 | */ | ||
1742 | |||
1743 | /*****************************************************************************/ | ||
1744 | |||
1745 | #pragma mark FileRefNumGetFSRef | ||
1746 | |||
1747 | OSErr | ||
1748 | FileRefNumGetFSRef( | ||
1749 | short refNum, | ||
1750 | FSRef *ref); | ||
1751 | |||
1752 | /* | ||
1753 | The FileRefNumGetFSRef function gets the FSRef of an open file. | ||
1754 | |||
1755 | refNum --> The file reference number of an open file. | ||
1756 | ref <-- The FSRef to the open file. | ||
1757 | */ | ||
1758 | |||
1759 | /*****************************************************************************/ | ||
1760 | |||
1761 | #pragma mark FSSetDefault | ||
1762 | |||
1763 | OSErr | ||
1764 | FSSetDefault( | ||
1765 | const FSRef *newDefault, | ||
1766 | FSRef *oldDefault); | ||
1767 | |||
1768 | /* | ||
1769 | The FSSetDefault function sets the current working directory to the | ||
1770 | directory specified by newDefault. The previous current working directory | ||
1771 | is returned in oldDefault and must be used to restore the current working | ||
1772 | directory to its previous state with the FSRestoreDefault function. | ||
1773 | These two functions are designed to be used as a wrapper around | ||
1774 | Standard I/O routines where the location of the file is implied to be the | ||
1775 | current working directory. This is how you should use these functions: | ||
1776 | |||
1777 | result = FSSetDefault(&newDefault, &oldDefault); | ||
1778 | if ( noErr == result ) | ||
1779 | { | ||
1780 | // call the Stdio functions like remove, rename, | ||
1781 | // fopen, freopen, etc here! | ||
1782 | |||
1783 | result = FSRestoreDefault(&oldDefault); | ||
1784 | } | ||
1785 | |||
1786 | newDefault --> An FSRef that specifies the new current working | ||
1787 | directory. | ||
1788 | oldDefault <-- The previous current working directory's FSRef. | ||
1789 | |||
1790 | __________ | ||
1791 | |||
1792 | Also see: FSRestoreDefault | ||
1793 | */ | ||
1794 | |||
1795 | /*****************************************************************************/ | ||
1796 | |||
1797 | #pragma mark FSRestoreDefault | ||
1798 | |||
1799 | OSErr | ||
1800 | FSRestoreDefault( | ||
1801 | const FSRef *oldDefault); | ||
1802 | |||
1803 | /* | ||
1804 | The FSRestoreDefault function restores the current working directory | ||
1805 | to the directory specified by oldDefault. The oldDefault parameter was | ||
1806 | previously obtained from the FSSetDefault function. | ||
1807 | These two functions are designed to be used as a wrapper around | ||
1808 | Standard I/O routines where the location of the file is implied to be the | ||
1809 | current working directory. This is how you should use these functions: | ||
1810 | |||
1811 | result = FSSetDefault(&newDefault, &oldDefault); | ||
1812 | if ( noErr == result ) | ||
1813 | { | ||
1814 | // call the Stdio functions like remove, rename, | ||
1815 | // fopen, freopen, etc here! | ||
1816 | |||
1817 | result = FSRestoreDefault(&oldDefault); | ||
1818 | } | ||
1819 | |||
1820 | oldDefault --> The FSRef of the location to restore. | ||
1821 | |||
1822 | __________ | ||
1823 | |||
1824 | Also see: FSSetDefault | ||
1825 | */ | ||
1826 | |||
1827 | /*****************************************************************************/ | ||
1828 | |||
1829 | #if PRAGMA_STRUCT_ALIGN | ||
1830 | #pragma options align=reset | ||
1831 | #elif PRAGMA_STRUCT_PACKPUSH | ||
1832 | #pragma pack(pop) | ||
1833 | #elif PRAGMA_STRUCT_PACK | ||
1834 | #pragma pack() | ||
1835 | #endif | ||
1836 | |||
1837 | #ifdef PRAGMA_IMPORT_OFF | ||
1838 | #pragma import off | ||
1839 | #elif PRAGMA_IMPORT | ||
1840 | #pragma import reset | ||
1841 | #endif | ||
1842 | |||
1843 | #ifdef __cplusplus | ||
1844 | } | ||
1845 | #endif | ||
1846 | |||
1847 | #endif /* __MOREFILESX__ */ | ||
1848 | |||
diff --git a/linden/indra/mac_updater/mac_updater.cpp b/linden/indra/mac_updater/mac_updater.cpp new file mode 100644 index 0000000..25e203c --- /dev/null +++ b/linden/indra/mac_updater/mac_updater.cpp | |||
@@ -0,0 +1,1106 @@ | |||
1 | /** | ||
2 | * @file mac_updater.cpp | ||
3 | * @brief | ||
4 | * | ||
5 | * Copyright (c) 2006-2007, Linden Research, Inc. | ||
6 | * | ||
7 | * The source code in this file ("Source Code") is provided by Linden Lab | ||
8 | * to you under the terms of the GNU General Public License, version 2.0 | ||
9 | * ("GPL"), unless you have obtained a separate licensing agreement | ||
10 | * ("Other License"), formally executed by you and Linden Lab. Terms of | ||
11 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
12 | * online at http://secondlife.com/developers/opensource/gplv2 | ||
13 | * | ||
14 | * There are special exceptions to the terms and conditions of the GPL as | ||
15 | * it is applied to this Source Code. View the full text of the exception | ||
16 | * in the file doc/FLOSS-exception.txt in this software distribution, or | ||
17 | * online at http://secondlife.com/developers/opensource/flossexception | ||
18 | * | ||
19 | * By copying, modifying or distributing this software, you acknowledge | ||
20 | * that you have read and understood your obligations described above, | ||
21 | * and agree to abide by those obligations. | ||
22 | * | ||
23 | * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
24 | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
25 | * COMPLETENESS OR PERFORMANCE. | ||
26 | */ | ||
27 | |||
28 | #include "linden_common.h" | ||
29 | |||
30 | #include <stdio.h> | ||
31 | #include <stdlib.h> | ||
32 | //#include <direct.h> | ||
33 | #include <time.h> | ||
34 | #include <sys/types.h> | ||
35 | #include <sys/stat.h> | ||
36 | #include <unistd.h> | ||
37 | |||
38 | #include <curl/curl.h> | ||
39 | #include <pthread.h> | ||
40 | |||
41 | #include "llerror.h" | ||
42 | #include "lltimer.h" | ||
43 | #include "lldir.h" | ||
44 | #include "llfile.h" | ||
45 | |||
46 | #include "llstring.h" | ||
47 | |||
48 | #include <Carbon/Carbon.h> | ||
49 | |||
50 | #include "MoreFilesX.h" | ||
51 | #include "FSCopyObject.h" | ||
52 | |||
53 | enum | ||
54 | { | ||
55 | kEventClassCustom = 'Cust', | ||
56 | kEventCustomProgress = 'Prog', | ||
57 | kEventParamCustomCurValue = 'Cur ', | ||
58 | kEventParamCustomMaxValue = 'Max ', | ||
59 | kEventParamCustomText = 'Text', | ||
60 | kEventCustomDone = 'Done', | ||
61 | }; | ||
62 | |||
63 | WindowRef gWindow = NULL; | ||
64 | EventHandlerRef gEventHandler = NULL; | ||
65 | OSStatus gFailure = noErr; | ||
66 | Boolean gCancelled = false; | ||
67 | |||
68 | char *gUserServer; | ||
69 | char *gProductName; | ||
70 | char gUpdateURL[2048]; | ||
71 | |||
72 | void *updatethreadproc(void*); | ||
73 | |||
74 | pthread_t updatethread; | ||
75 | |||
76 | OSStatus setProgress(int cur, int max) | ||
77 | { | ||
78 | OSStatus err; | ||
79 | ControlRef progressBar = NULL; | ||
80 | ControlID id; | ||
81 | |||
82 | id.signature = 'prog'; | ||
83 | id.id = 0; | ||
84 | |||
85 | err = GetControlByID(gWindow, &id, &progressBar); | ||
86 | if(err == noErr) | ||
87 | { | ||
88 | Boolean indeterminate; | ||
89 | |||
90 | if(max == 0) | ||
91 | { | ||
92 | indeterminate = true; | ||
93 | err = SetControlData(progressBar, kControlEntireControl, kControlProgressBarIndeterminateTag, sizeof(Boolean), (Ptr)&indeterminate); | ||
94 | } | ||
95 | else | ||
96 | { | ||
97 | double percentage = (double)cur / (double)max; | ||
98 | SetControlMinimum(progressBar, 0); | ||
99 | SetControlMaximum(progressBar, 100); | ||
100 | SetControlValue(progressBar, (SInt16)(percentage * 100)); | ||
101 | |||
102 | indeterminate = false; | ||
103 | err = SetControlData(progressBar, kControlEntireControl, kControlProgressBarIndeterminateTag, sizeof(Boolean), (Ptr)&indeterminate); | ||
104 | |||
105 | Draw1Control(progressBar); | ||
106 | } | ||
107 | } | ||
108 | |||
109 | return(err); | ||
110 | } | ||
111 | |||
112 | OSStatus setProgressText(CFStringRef text) | ||
113 | { | ||
114 | OSStatus err; | ||
115 | ControlRef progressText = NULL; | ||
116 | ControlID id; | ||
117 | |||
118 | id.signature = 'what'; | ||
119 | id.id = 0; | ||
120 | |||
121 | err = GetControlByID(gWindow, &id, &progressText); | ||
122 | if(err == noErr) | ||
123 | { | ||
124 | err = SetControlData(progressText, kControlEntireControl, kControlStaticTextCFStringTag, sizeof(CFStringRef), (Ptr)&text); | ||
125 | Draw1Control(progressText); | ||
126 | } | ||
127 | |||
128 | return(err); | ||
129 | } | ||
130 | |||
131 | OSStatus sendProgress(long cur, long max, CFStringRef text = NULL) | ||
132 | { | ||
133 | OSStatus result; | ||
134 | EventRef evt; | ||
135 | |||
136 | result = CreateEvent( | ||
137 | NULL, | ||
138 | kEventClassCustom, | ||
139 | kEventCustomProgress, | ||
140 | 0, | ||
141 | kEventAttributeNone, | ||
142 | &evt); | ||
143 | |||
144 | // This event needs to be targeted at the window so it goes to the window's handler. | ||
145 | if(result == noErr) | ||
146 | { | ||
147 | EventTargetRef target = GetWindowEventTarget(gWindow); | ||
148 | result = SetEventParameter ( | ||
149 | evt, | ||
150 | kEventParamPostTarget, | ||
151 | typeEventTargetRef, | ||
152 | sizeof(target), | ||
153 | &target); | ||
154 | } | ||
155 | |||
156 | if(result == noErr) | ||
157 | { | ||
158 | result = SetEventParameter ( | ||
159 | evt, | ||
160 | kEventParamCustomCurValue, | ||
161 | typeLongInteger, | ||
162 | sizeof(cur), | ||
163 | &cur); | ||
164 | } | ||
165 | |||
166 | if(result == noErr) | ||
167 | { | ||
168 | result = SetEventParameter ( | ||
169 | evt, | ||
170 | kEventParamCustomMaxValue, | ||
171 | typeLongInteger, | ||
172 | sizeof(max), | ||
173 | &max); | ||
174 | } | ||
175 | |||
176 | if(result == noErr) | ||
177 | { | ||
178 | if(text != NULL) | ||
179 | { | ||
180 | result = SetEventParameter ( | ||
181 | evt, | ||
182 | kEventParamCustomText, | ||
183 | typeCFStringRef, | ||
184 | sizeof(text), | ||
185 | &text); | ||
186 | } | ||
187 | } | ||
188 | |||
189 | if(result == noErr) | ||
190 | { | ||
191 | // Send the event | ||
192 | PostEventToQueue( | ||
193 | GetMainEventQueue(), | ||
194 | evt, | ||
195 | kEventPriorityStandard); | ||
196 | |||
197 | } | ||
198 | |||
199 | return(result); | ||
200 | } | ||
201 | |||
202 | OSStatus sendDone(void) | ||
203 | { | ||
204 | OSStatus result; | ||
205 | EventRef evt; | ||
206 | |||
207 | result = CreateEvent( | ||
208 | NULL, | ||
209 | kEventClassCustom, | ||
210 | kEventCustomDone, | ||
211 | 0, | ||
212 | kEventAttributeNone, | ||
213 | &evt); | ||
214 | |||
215 | // This event needs to be targeted at the window so it goes to the window's handler. | ||
216 | if(result == noErr) | ||
217 | { | ||
218 | EventTargetRef target = GetWindowEventTarget(gWindow); | ||
219 | result = SetEventParameter ( | ||
220 | evt, | ||
221 | kEventParamPostTarget, | ||
222 | typeEventTargetRef, | ||
223 | sizeof(target), | ||
224 | &target); | ||
225 | } | ||
226 | |||
227 | if(result == noErr) | ||
228 | { | ||
229 | // Send the event | ||
230 | PostEventToQueue( | ||
231 | GetMainEventQueue(), | ||
232 | evt, | ||
233 | kEventPriorityStandard); | ||
234 | |||
235 | } | ||
236 | |||
237 | return(result); | ||
238 | } | ||
239 | |||
240 | OSStatus dialogHandler(EventHandlerCallRef handler, EventRef event, void *userdata) | ||
241 | { | ||
242 | OSStatus result = eventNotHandledErr; | ||
243 | OSStatus err; | ||
244 | UInt32 evtClass = GetEventClass(event); | ||
245 | UInt32 evtKind = GetEventKind(event); | ||
246 | |||
247 | if((evtClass == kEventClassCommand) && (evtKind == kEventCommandProcess)) | ||
248 | { | ||
249 | HICommand cmd; | ||
250 | err = GetEventParameter(event, kEventParamDirectObject, typeHICommand, NULL, sizeof(cmd), NULL, &cmd); | ||
251 | |||
252 | if(err == noErr) | ||
253 | { | ||
254 | switch(cmd.commandID) | ||
255 | { | ||
256 | case kHICommandCancel: | ||
257 | gCancelled = true; | ||
258 | // QuitAppModalLoopForWindow(gWindow); | ||
259 | result = noErr; | ||
260 | break; | ||
261 | } | ||
262 | } | ||
263 | } | ||
264 | else if((evtClass == kEventClassCustom) && (evtKind == kEventCustomProgress)) | ||
265 | { | ||
266 | // Request to update the progress dialog | ||
267 | long cur = 0; | ||
268 | long max = 0; | ||
269 | CFStringRef text = NULL; | ||
270 | (void) GetEventParameter(event, kEventParamCustomCurValue, typeLongInteger, NULL, sizeof(cur), NULL, &cur); | ||
271 | (void) GetEventParameter(event, kEventParamCustomMaxValue, typeLongInteger, NULL, sizeof(max), NULL, &max); | ||
272 | (void) GetEventParameter(event, kEventParamCustomText, typeCFStringRef, NULL, sizeof(text), NULL, &text); | ||
273 | |||
274 | err = setProgress(cur, max); | ||
275 | if(err == noErr) | ||
276 | { | ||
277 | if(text != NULL) | ||
278 | { | ||
279 | setProgressText(text); | ||
280 | } | ||
281 | } | ||
282 | |||
283 | result = noErr; | ||
284 | } | ||
285 | else if((evtClass == kEventClassCustom) && (evtKind == kEventCustomDone)) | ||
286 | { | ||
287 | // We're done. Exit the modal loop. | ||
288 | QuitAppModalLoopForWindow(gWindow); | ||
289 | result = noErr; | ||
290 | } | ||
291 | |||
292 | return(result); | ||
293 | } | ||
294 | |||
295 | #if 0 | ||
296 | size_t curl_download_callback(void *data, size_t size, size_t nmemb, | ||
297 | void *user_data) | ||
298 | { | ||
299 | S32 bytes = size * nmemb; | ||
300 | char *cdata = (char *) data; | ||
301 | for (int i =0; i < bytes; i += 1) | ||
302 | { | ||
303 | gServerResponse.append(cdata[i]); | ||
304 | } | ||
305 | return bytes; | ||
306 | } | ||
307 | #endif | ||
308 | |||
309 | int curl_progress_callback_func(void *clientp, | ||
310 | double dltotal, | ||
311 | double dlnow, | ||
312 | double ultotal, | ||
313 | double ulnow) | ||
314 | { | ||
315 | int max = (int)(dltotal / 1024.0); | ||
316 | int cur = (int)(dlnow / 1024.0); | ||
317 | sendProgress(cur, max); | ||
318 | |||
319 | if(gCancelled) | ||
320 | return(1); | ||
321 | |||
322 | return(0); | ||
323 | } | ||
324 | |||
325 | int parse_args(int argc, char **argv) | ||
326 | { | ||
327 | // Check for old-type arguments. | ||
328 | if (2 == argc) | ||
329 | { | ||
330 | gUserServer = argv[1]; | ||
331 | return 0; | ||
332 | } | ||
333 | |||
334 | int j; | ||
335 | |||
336 | for (j = 1; j < argc; j++) | ||
337 | { | ||
338 | if ((!strcmp(argv[j], "-userserver")) && (++j < argc)) | ||
339 | { | ||
340 | gUserServer = argv[j]; | ||
341 | } | ||
342 | else if ((!strcmp(argv[j], "-name")) && (++j < argc)) | ||
343 | { | ||
344 | gProductName = argv[j]; | ||
345 | } | ||
346 | } | ||
347 | |||
348 | return 0; | ||
349 | } | ||
350 | |||
351 | int main(int argc, char **argv) | ||
352 | { | ||
353 | // We assume that all the logs we're looking for reside on the current drive | ||
354 | gDirUtilp->initAppDirs("SecondLife"); | ||
355 | |||
356 | ///////////////////////////////////////// | ||
357 | // | ||
358 | // Process command line arguments | ||
359 | // | ||
360 | gUserServer = NULL; | ||
361 | gProductName = NULL; | ||
362 | parse_args(argc, argv); | ||
363 | if (!gUserServer) | ||
364 | { | ||
365 | llinfos << "Usage: mac_updater -userserver <server> [-name <product_name>] [-program <program_name>]" << llendl; | ||
366 | exit(1); | ||
367 | } | ||
368 | else | ||
369 | { | ||
370 | llinfos << "User server is: " << gUserServer << llendl; | ||
371 | if (gProductName) | ||
372 | { | ||
373 | llinfos << "Product name is: " << gProductName << llendl; | ||
374 | } | ||
375 | else | ||
376 | { | ||
377 | gProductName = "Second Life"; | ||
378 | } | ||
379 | } | ||
380 | |||
381 | llinfos << "Starting " << gProductName << " Updater" << llendl; | ||
382 | |||
383 | // Build the URL to download the update | ||
384 | snprintf(gUpdateURL, sizeof(gUpdateURL), "http://secondlife.com/update-macos.php?userserver=%s", gUserServer); | ||
385 | |||
386 | // Real UI... | ||
387 | OSStatus err; | ||
388 | IBNibRef nib = NULL; | ||
389 | |||
390 | err = CreateNibReference(CFSTR("AutoUpdater"), &nib); | ||
391 | |||
392 | char windowTitle[MAX_PATH]; | ||
393 | snprintf(windowTitle, sizeof(windowTitle), "%s Updater", gProductName); | ||
394 | CFStringRef windowTitleRef = NULL; | ||
395 | windowTitleRef = CFStringCreateWithCString(NULL, windowTitle, kCFStringEncodingUTF8); | ||
396 | |||
397 | if(err == noErr) | ||
398 | { | ||
399 | err = CreateWindowFromNib(nib, CFSTR("Updater"), &gWindow); | ||
400 | } | ||
401 | |||
402 | if (err == noErr) | ||
403 | { | ||
404 | err = SetWindowTitleWithCFString(gWindow, windowTitleRef); | ||
405 | } | ||
406 | CFRelease(windowTitleRef); | ||
407 | |||
408 | if(err == noErr) | ||
409 | { | ||
410 | // Set up an event handler for the window. | ||
411 | EventTypeSpec handlerEvents[] = | ||
412 | { | ||
413 | { kEventClassCommand, kEventCommandProcess }, | ||
414 | { kEventClassCustom, kEventCustomProgress }, | ||
415 | { kEventClassCustom, kEventCustomDone } | ||
416 | }; | ||
417 | InstallStandardEventHandler(GetWindowEventTarget(gWindow)); | ||
418 | InstallWindowEventHandler( | ||
419 | gWindow, | ||
420 | NewEventHandlerUPP(dialogHandler), | ||
421 | GetEventTypeCount (handlerEvents), | ||
422 | handlerEvents, | ||
423 | 0, | ||
424 | &gEventHandler); | ||
425 | } | ||
426 | |||
427 | if(err == noErr) | ||
428 | { | ||
429 | ShowWindow(gWindow); | ||
430 | } | ||
431 | |||
432 | if(err == noErr) | ||
433 | { | ||
434 | pthread_create(&updatethread, | ||
435 | NULL, | ||
436 | &updatethreadproc, | ||
437 | NULL); | ||
438 | |||
439 | } | ||
440 | |||
441 | if(err == noErr) | ||
442 | { | ||
443 | RunAppModalLoopForWindow(gWindow); | ||
444 | } | ||
445 | |||
446 | void *threadresult; | ||
447 | |||
448 | pthread_join(updatethread, &threadresult); | ||
449 | |||
450 | if(!gCancelled && (gFailure != noErr)) | ||
451 | { | ||
452 | // Something went wrong. Since we always just tell the user to download a new version, we don't really care what. | ||
453 | AlertStdCFStringAlertParamRec params; | ||
454 | SInt16 retval_mac = 1; | ||
455 | DialogRef alert = NULL; | ||
456 | OSStatus err; | ||
457 | |||
458 | params.version = kStdCFStringAlertVersionOne; | ||
459 | params.movable = false; | ||
460 | params.helpButton = false; | ||
461 | params.defaultText = (CFStringRef)kAlertDefaultOKText; | ||
462 | params.cancelText = 0; | ||
463 | params.otherText = 0; | ||
464 | params.defaultButton = 1; | ||
465 | params.cancelButton = 0; | ||
466 | params.position = kWindowDefaultPosition; | ||
467 | params.flags = 0; | ||
468 | |||
469 | err = CreateStandardAlert( | ||
470 | kAlertStopAlert, | ||
471 | CFSTR("Error"), | ||
472 | CFSTR("An error occurred while updating Second Life. Please download the latest version from www.secondlife.com."), | ||
473 | ¶ms, | ||
474 | &alert); | ||
475 | |||
476 | if(err == noErr) | ||
477 | { | ||
478 | err = RunStandardAlert( | ||
479 | alert, | ||
480 | NULL, | ||
481 | &retval_mac); | ||
482 | } | ||
483 | |||
484 | } | ||
485 | |||
486 | // Don't dispose of things, just exit. This keeps the update thread from potentially getting hosed. | ||
487 | exit(0); | ||
488 | |||
489 | if(gWindow != NULL) | ||
490 | { | ||
491 | DisposeWindow(gWindow); | ||
492 | } | ||
493 | |||
494 | if(nib != NULL) | ||
495 | { | ||
496 | DisposeNibReference(nib); | ||
497 | } | ||
498 | |||
499 | return 0; | ||
500 | } | ||
501 | |||
502 | bool isDirWritable(FSRef &dir) | ||
503 | { | ||
504 | bool result = false; | ||
505 | |||
506 | // Test for a writable directory by creating a directory, then deleting it again. | ||
507 | // This is kinda lame, but will pretty much always give the right answer. | ||
508 | |||
509 | OSStatus err = noErr; | ||
510 | char temp[PATH_MAX]; | ||
511 | |||
512 | err = FSRefMakePath(&dir, (UInt8*)temp, sizeof(temp)); | ||
513 | |||
514 | if(err == noErr) | ||
515 | { | ||
516 | temp[0] = '\0'; | ||
517 | strncat(temp, "/.test_XXXXXX", sizeof(temp) - 1); | ||
518 | |||
519 | if(mkdtemp(temp) != NULL) | ||
520 | { | ||
521 | // We were able to make the directory. This means the directory is writable. | ||
522 | result = true; | ||
523 | |||
524 | // Clean up. | ||
525 | rmdir(temp); | ||
526 | } | ||
527 | } | ||
528 | |||
529 | #if 0 | ||
530 | // This seemed like a good idea, but won't tell us if we're on a volume mounted read-only. | ||
531 | UInt8 perm; | ||
532 | err = FSGetUserPrivilegesPermissions(&targetParentRef, &perm, NULL); | ||
533 | if(err == noErr) | ||
534 | { | ||
535 | if(perm & kioACUserNoMakeChangesMask) | ||
536 | { | ||
537 | // Parent directory isn't writable. | ||
538 | llinfos << "Target parent directory not writable." << llendl; | ||
539 | err = -1; | ||
540 | replacingTarget = false; | ||
541 | } | ||
542 | } | ||
543 | #endif | ||
544 | |||
545 | return result; | ||
546 | } | ||
547 | |||
548 | static void utf8str_to_HFSUniStr255(HFSUniStr255 *dest, const char* src) | ||
549 | { | ||
550 | LLWString wstr = utf8str_to_wstring(src); | ||
551 | llutf16string utf16str = wstring_to_utf16str(wstr); | ||
552 | |||
553 | dest->length = utf16str.size(); | ||
554 | if(dest->length > 255) | ||
555 | { | ||
556 | // There's onl room for 255 chars in a HFSUniStr25.. | ||
557 | // Truncate to avoid stack smaching or other badness. | ||
558 | dest->length = 255; | ||
559 | } | ||
560 | memcpy(dest->unicode, utf16str.data(), sizeof(UniChar)* dest->length); | ||
561 | } | ||
562 | |||
563 | int restoreObject(const char* aside, const char* target, const char* path, const char* object) | ||
564 | { | ||
565 | char source[PATH_MAX]; | ||
566 | char dest[PATH_MAX]; | ||
567 | snprintf(source, sizeof(source), "%s/%s/%s", aside, path, object); | ||
568 | snprintf(dest, sizeof(dest), "%s/%s", target, path); | ||
569 | FSRef sourceRef; | ||
570 | FSRef destRef; | ||
571 | OSStatus err; | ||
572 | err = FSPathMakeRef((UInt8 *)source, &sourceRef, NULL); | ||
573 | if(err != noErr) return false; | ||
574 | err = FSPathMakeRef((UInt8 *)dest, &destRef, NULL); | ||
575 | if(err != noErr) return false; | ||
576 | |||
577 | llinfos << "Copying " << source << " to " << dest << llendl; | ||
578 | |||
579 | err = FSCopyObject( | ||
580 | &sourceRef, | ||
581 | &destRef, | ||
582 | 0, | ||
583 | kFSCatInfoNone, | ||
584 | kDupeActionReplace, | ||
585 | NULL, | ||
586 | false, | ||
587 | false, | ||
588 | NULL, | ||
589 | NULL, | ||
590 | NULL, | ||
591 | NULL); | ||
592 | |||
593 | if(err != noErr) return false; | ||
594 | return true; | ||
595 | } | ||
596 | |||
597 | // Replace any mention of "Second Life" with the product name. | ||
598 | void filterFile(const char* filename) | ||
599 | { | ||
600 | char temp[PATH_MAX]; | ||
601 | // First copy the target's version, so we can run it through sed. | ||
602 | snprintf(temp, sizeof(temp), "cp '%s' '%s.tmp'", filename, filename); | ||
603 | system(temp); | ||
604 | |||
605 | // Now run it through sed. | ||
606 | snprintf(temp, sizeof(temp), | ||
607 | "sed 's/Second Life/%s/g' '%s.tmp' > '%s'", gProductName, filename, filename); | ||
608 | system(temp); | ||
609 | } | ||
610 | |||
611 | void *updatethreadproc(void*) | ||
612 | { | ||
613 | char tempDir[PATH_MAX] = ""; | ||
614 | FSRef tempDirRef; | ||
615 | char temp[PATH_MAX]; | ||
616 | char deviceNode[1024] = ""; | ||
617 | FILE *downloadFile = NULL; | ||
618 | OSStatus err; | ||
619 | ProcessSerialNumber psn; | ||
620 | char target[PATH_MAX]; | ||
621 | FSRef targetRef; | ||
622 | FSRef targetParentRef; | ||
623 | FSVolumeRefNum targetVol; | ||
624 | FSRef trashFolderRef, tempFolderRef; | ||
625 | Boolean replacingTarget = false; | ||
626 | |||
627 | memset(&tempDirRef, 0, sizeof(tempDirRef)); | ||
628 | memset(&targetRef, 0, sizeof(targetRef)); | ||
629 | memset(&targetParentRef, 0, sizeof(targetParentRef)); | ||
630 | |||
631 | try | ||
632 | { | ||
633 | // Attempt to get a reference to the Second Life application bundle containing this updater. | ||
634 | // Any failures during this process will cause us to default to updating /Applications/Second Life.app | ||
635 | { | ||
636 | FSRef myBundle; | ||
637 | |||
638 | err = GetCurrentProcess(&psn); | ||
639 | if(err == noErr) | ||
640 | { | ||
641 | err = GetProcessBundleLocation(&psn, &myBundle); | ||
642 | } | ||
643 | |||
644 | if(err == noErr) | ||
645 | { | ||
646 | // Sanity check: Make sure the name of the item referenced by targetRef is "Second Life.app". | ||
647 | FSRefMakePath(&myBundle, (UInt8*)target, sizeof(target)); | ||
648 | |||
649 | llinfos << "Updater bundle location: " << target << llendl; | ||
650 | } | ||
651 | |||
652 | // Our bundle should be in Second Life.app/Contents/Resources/AutoUpdater.app | ||
653 | // so we need to go up 3 levels to get the path to the main application bundle. | ||
654 | if(err == noErr) | ||
655 | { | ||
656 | err = FSGetParentRef(&myBundle, &targetRef); | ||
657 | } | ||
658 | if(err == noErr) | ||
659 | { | ||
660 | err = FSGetParentRef(&targetRef, &targetRef); | ||
661 | } | ||
662 | if(err == noErr) | ||
663 | { | ||
664 | err = FSGetParentRef(&targetRef, &targetRef); | ||
665 | } | ||
666 | |||
667 | // And once more to get the parent of the target | ||
668 | if(err == noErr) | ||
669 | { | ||
670 | err = FSGetParentRef(&targetRef, &targetParentRef); | ||
671 | } | ||
672 | |||
673 | if(err == noErr) | ||
674 | { | ||
675 | FSRefMakePath(&targetRef, (UInt8*)target, sizeof(target)); | ||
676 | llinfos << "Path to target: " << target << llendl; | ||
677 | } | ||
678 | |||
679 | // Sanity check: make sure the target is a bundle with the right identifier | ||
680 | if(err == noErr) | ||
681 | { | ||
682 | CFURLRef targetURL = NULL; | ||
683 | CFBundleRef targetBundle = NULL; | ||
684 | CFStringRef targetBundleID = NULL; | ||
685 | |||
686 | // Assume the worst... | ||
687 | err = -1; | ||
688 | |||
689 | targetURL = CFURLCreateFromFSRef(NULL, &targetRef); | ||
690 | |||
691 | if(targetURL == NULL) | ||
692 | { | ||
693 | llinfos << "Error creating target URL." << llendl; | ||
694 | } | ||
695 | else | ||
696 | { | ||
697 | targetBundle = CFBundleCreate(NULL, targetURL); | ||
698 | } | ||
699 | |||
700 | if(targetBundle == NULL) | ||
701 | { | ||
702 | llinfos << "Failed to create target bundle." << llendl; | ||
703 | } | ||
704 | else | ||
705 | { | ||
706 | targetBundleID = CFBundleGetIdentifier(targetBundle); | ||
707 | } | ||
708 | |||
709 | if(targetBundleID == NULL) | ||
710 | { | ||
711 | llinfos << "Couldn't retrieve target bundle ID." << llendl; | ||
712 | } | ||
713 | else | ||
714 | { | ||
715 | if(CFStringCompare(targetBundleID, CFSTR("com.secondlife.indra.viewer"), 0) == kCFCompareEqualTo) | ||
716 | { | ||
717 | // This is the bundle we're looking for. | ||
718 | err = noErr; | ||
719 | replacingTarget = true; | ||
720 | } | ||
721 | else | ||
722 | { | ||
723 | llinfos << "Target bundle ID mismatch." << llendl; | ||
724 | } | ||
725 | } | ||
726 | |||
727 | // Don't release targetBundleID -- since we don't retain it, it's released when targetBundle is released. | ||
728 | if(targetURL != NULL) | ||
729 | CFRelease(targetURL); | ||
730 | if(targetBundle != NULL) | ||
731 | CFRelease(targetBundle); | ||
732 | |||
733 | } | ||
734 | |||
735 | // Make sure the target's parent directory is writable. | ||
736 | if(err == noErr) | ||
737 | { | ||
738 | if(!isDirWritable(targetParentRef)) | ||
739 | { | ||
740 | // Parent directory isn't writable. | ||
741 | llinfos << "Target parent directory not writable." << llendl; | ||
742 | err = -1; | ||
743 | replacingTarget = false; | ||
744 | } | ||
745 | } | ||
746 | |||
747 | if(err != noErr) | ||
748 | { | ||
749 | Boolean isDirectory; | ||
750 | llinfos << "Target search failed, defaulting to /Applications/" << gProductName << ".app." << llendl; | ||
751 | |||
752 | // Set up the parent directory | ||
753 | err = FSPathMakeRef((UInt8*)"/Applications", &targetParentRef, &isDirectory); | ||
754 | if((err != noErr) || (!isDirectory)) | ||
755 | { | ||
756 | // We're so hosed. | ||
757 | llinfos << "Applications directory not found, giving up." << llendl; | ||
758 | throw 0; | ||
759 | } | ||
760 | |||
761 | snprintf(target, sizeof(target), "/Applications/%s.app", gProductName); | ||
762 | |||
763 | memset(&targetRef, 0, sizeof(targetRef)); | ||
764 | err = FSPathMakeRef((UInt8*)target, &targetRef, NULL); | ||
765 | if(err == fnfErr) | ||
766 | { | ||
767 | // This is fine, just means we're not replacing anything. | ||
768 | err = noErr; | ||
769 | replacingTarget = false; | ||
770 | } | ||
771 | else | ||
772 | { | ||
773 | replacingTarget = true; | ||
774 | } | ||
775 | |||
776 | // Make sure the target's parent directory is writable. | ||
777 | if(err == noErr) | ||
778 | { | ||
779 | if(!isDirWritable(targetParentRef)) | ||
780 | { | ||
781 | // Parent directory isn't writable. | ||
782 | llinfos << "Target parent directory not writable." << llendl; | ||
783 | err = -1; | ||
784 | replacingTarget = false; | ||
785 | } | ||
786 | } | ||
787 | |||
788 | } | ||
789 | |||
790 | // If we haven't fixed all problems by this point, just bail. | ||
791 | if(err != noErr) | ||
792 | { | ||
793 | llinfos << "Unable to pick a target, giving up." << llendl; | ||
794 | throw 0; | ||
795 | } | ||
796 | } | ||
797 | |||
798 | // Find the volID of the volume the target resides on | ||
799 | { | ||
800 | FSCatalogInfo info; | ||
801 | err = FSGetCatalogInfo( | ||
802 | &targetParentRef, | ||
803 | kFSCatInfoVolume, | ||
804 | &info, | ||
805 | NULL, | ||
806 | NULL, | ||
807 | NULL); | ||
808 | |||
809 | if(err != noErr) | ||
810 | throw 0; | ||
811 | |||
812 | targetVol = info.volume; | ||
813 | } | ||
814 | |||
815 | // Find the temporary items and trash folders on that volume. | ||
816 | err = FSFindFolder( | ||
817 | targetVol, | ||
818 | kTrashFolderType, | ||
819 | true, | ||
820 | &trashFolderRef); | ||
821 | |||
822 | if(err != noErr) | ||
823 | throw 0; | ||
824 | |||
825 | err = FSFindFolder( | ||
826 | targetVol, | ||
827 | kTemporaryFolderType, | ||
828 | true, | ||
829 | &tempFolderRef); | ||
830 | |||
831 | if(err != noErr) | ||
832 | throw 0; | ||
833 | |||
834 | err = FSRefMakePath(&tempFolderRef, (UInt8*)temp, sizeof(temp)); | ||
835 | |||
836 | if(err != noErr) | ||
837 | throw 0; | ||
838 | |||
839 | temp[0] = '\0'; | ||
840 | strncat(temp, "/SecondLifeUpdate_XXXXXX", sizeof(temp) - 1); | ||
841 | if(mkdtemp(temp) == NULL) | ||
842 | { | ||
843 | throw 0; | ||
844 | } | ||
845 | |||
846 | strcpy(tempDir, temp); | ||
847 | |||
848 | llinfos << "tempDir is " << tempDir << llendl; | ||
849 | |||
850 | err = FSPathMakeRef((UInt8*)tempDir, &tempDirRef, NULL); | ||
851 | |||
852 | if(err != noErr) | ||
853 | throw 0; | ||
854 | |||
855 | chdir(tempDir); | ||
856 | |||
857 | snprintf(temp, sizeof(temp), "SecondLife.dmg"); | ||
858 | |||
859 | downloadFile = fopen(temp, "wb"); | ||
860 | if(downloadFile == NULL) | ||
861 | { | ||
862 | throw 0; | ||
863 | } | ||
864 | |||
865 | { | ||
866 | CURL *curl = curl_easy_init(); | ||
867 | |||
868 | curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); | ||
869 | // curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curl_download_callback); | ||
870 | curl_easy_setopt(curl, CURLOPT_FILE, downloadFile); | ||
871 | curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); | ||
872 | curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, &curl_progress_callback_func); | ||
873 | curl_easy_setopt(curl, CURLOPT_URL, gUpdateURL); | ||
874 | curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); | ||
875 | |||
876 | sendProgress(0, 1, CFSTR("Downloading...")); | ||
877 | |||
878 | CURLcode result = curl_easy_perform(curl); | ||
879 | |||
880 | curl_easy_cleanup(curl); | ||
881 | |||
882 | if(gCancelled) | ||
883 | { | ||
884 | llinfos << "User cancel, bailing out."<< llendl; | ||
885 | throw 0; | ||
886 | } | ||
887 | |||
888 | if(result != CURLE_OK) | ||
889 | { | ||
890 | llinfos << "Error " << result << " while downloading disk image."<< llendl; | ||
891 | throw 0; | ||
892 | } | ||
893 | |||
894 | fclose(downloadFile); | ||
895 | downloadFile = NULL; | ||
896 | } | ||
897 | |||
898 | sendProgress(0, 0, CFSTR("Mounting image...")); | ||
899 | LLFile::mkdir("mnt", 0700); | ||
900 | |||
901 | // NOTE: we could add -private at the end of this command line to keep the image from showing up in the Finder, | ||
902 | // but if our cleanup fails, this makes it much harder for the user to unmount the image. | ||
903 | LLString mountOutput; | ||
904 | FILE *mounter = popen("hdiutil attach SecondLife.dmg -mountpoint mnt", "r"); | ||
905 | |||
906 | if(mounter == NULL) | ||
907 | { | ||
908 | llinfos << "Failed to mount disk image, exiting."<< llendl; | ||
909 | throw 0; | ||
910 | } | ||
911 | |||
912 | // We need to scan the output from hdiutil to find the device node it uses to attach the disk image. | ||
913 | // If we don't have this information, we can't detach it later. | ||
914 | while(mounter != NULL) | ||
915 | { | ||
916 | size_t len = fread(temp, 1, sizeof(temp)-1, mounter); | ||
917 | temp[len] = 0; | ||
918 | mountOutput.append(temp); | ||
919 | if(len < sizeof(temp)-1) | ||
920 | { | ||
921 | // End of file or error. | ||
922 | if(pclose(mounter) != 0) | ||
923 | { | ||
924 | llinfos << "Failed to mount disk image, exiting."<< llendl; | ||
925 | throw 0; | ||
926 | } | ||
927 | mounter = NULL; | ||
928 | } | ||
929 | } | ||
930 | |||
931 | if(!mountOutput.empty()) | ||
932 | { | ||
933 | const char *s = mountOutput.c_str(); | ||
934 | char *prefix = "/dev/"; | ||
935 | char *sub = strstr(s, prefix); | ||
936 | |||
937 | if(sub != NULL) | ||
938 | { | ||
939 | sub += strlen(prefix); | ||
940 | sscanf(sub, "%s", deviceNode); | ||
941 | } | ||
942 | } | ||
943 | |||
944 | if(deviceNode[0] != 0) | ||
945 | { | ||
946 | llinfos << "Disk image attached on /dev/" << deviceNode << llendl; | ||
947 | } | ||
948 | else | ||
949 | { | ||
950 | llinfos << "Disk image device node not found!" << llendl; | ||
951 | } | ||
952 | |||
953 | // Get an FSRef to the new application on the disk image | ||
954 | FSRef sourceRef; | ||
955 | snprintf(temp, sizeof(temp), "%s/mnt/Second Life.app", tempDir); | ||
956 | |||
957 | llinfos << "Source application is: " << temp << llendl; | ||
958 | |||
959 | err = FSPathMakeRef((UInt8 *)temp, &sourceRef, NULL); | ||
960 | if(err != noErr) | ||
961 | throw 0; | ||
962 | |||
963 | FSRef asideRef; | ||
964 | char aside[MAX_PATH]; | ||
965 | |||
966 | // this will hold the name of the destination target | ||
967 | HFSUniStr255 appNameUniStr; | ||
968 | |||
969 | if(replacingTarget) | ||
970 | { | ||
971 | // Get the name of the target we're replacing | ||
972 | err = FSGetCatalogInfo(&targetRef, 0, NULL, &appNameUniStr, NULL, NULL); | ||
973 | if(err != noErr) | ||
974 | throw 0; | ||
975 | |||
976 | // Move aside old version (into work directory) | ||
977 | err = FSMoveObject(&targetRef, &tempDirRef, &asideRef); | ||
978 | if(err != noErr) | ||
979 | throw 0; | ||
980 | |||
981 | // Grab the path for later use. | ||
982 | err = FSRefMakePath(&asideRef, (UInt8*)aside, sizeof(aside)); | ||
983 | } | ||
984 | else | ||
985 | { | ||
986 | // Construct the name of the target based on the product name | ||
987 | char appName[MAX_PATH]; | ||
988 | snprintf(appName, sizeof(appName), "%s.app", gProductName); | ||
989 | utf8str_to_HFSUniStr255( &appNameUniStr, appName ); | ||
990 | } | ||
991 | |||
992 | sendProgress(0, 0, CFSTR("Copying files...")); | ||
993 | |||
994 | llinfos << "Starting copy..." << llendl; | ||
995 | |||
996 | // Copy the new version from the disk image to the target location. | ||
997 | err = FSCopyObject( | ||
998 | &sourceRef, | ||
999 | &targetParentRef, | ||
1000 | 0, | ||
1001 | kFSCatInfoNone, | ||
1002 | kDupeActionStandard, | ||
1003 | &appNameUniStr, | ||
1004 | false, | ||
1005 | false, | ||
1006 | NULL, | ||
1007 | NULL, | ||
1008 | &targetRef, | ||
1009 | NULL); | ||
1010 | |||
1011 | // Grab the path for later use. | ||
1012 | err = FSRefMakePath(&targetRef, (UInt8*)target, sizeof(target)); | ||
1013 | if(err != noErr) | ||
1014 | throw 0; | ||
1015 | |||
1016 | llinfos << "Copy complete. Target = " << target << llendl; | ||
1017 | |||
1018 | if(err != noErr) | ||
1019 | { | ||
1020 | // Something went wrong during the copy. Attempt to put the old version back and bail. | ||
1021 | (void)FSDeleteObjects(&targetRef); | ||
1022 | if(replacingTarget) | ||
1023 | { | ||
1024 | (void)FSMoveObject(&asideRef, &targetParentRef, NULL); | ||
1025 | } | ||
1026 | throw 0; | ||
1027 | } | ||
1028 | else | ||
1029 | { | ||
1030 | // The update has succeeded. Clear the cache directory. | ||
1031 | |||
1032 | sendProgress(0, 0, CFSTR("Clearing cache...")); | ||
1033 | |||
1034 | llinfos << "Clearing cache..." << llendl; | ||
1035 | |||
1036 | char mask[LL_MAX_PATH]; | ||
1037 | sprintf(mask, "%s*.*", gDirUtilp->getDirDelimiter().c_str()); | ||
1038 | gDirUtilp->deleteFilesInDir(gDirUtilp->getExpandedFilename(LL_PATH_CACHE,""),mask); | ||
1039 | |||
1040 | llinfos << "Clear complete." << llendl; | ||
1041 | |||
1042 | } | ||
1043 | } | ||
1044 | catch(...) | ||
1045 | { | ||
1046 | if(!gCancelled) | ||
1047 | if(gFailure == noErr) | ||
1048 | gFailure = -1; | ||
1049 | } | ||
1050 | |||
1051 | // Failures from here on out are all non-fatal and not reported. | ||
1052 | sendProgress(0, 3, CFSTR("Cleaning up...")); | ||
1053 | |||
1054 | // Close disk image file if necessary | ||
1055 | if(downloadFile != NULL) | ||
1056 | { | ||
1057 | llinfos << "Closing download file." << llendl; | ||
1058 | |||
1059 | fclose(downloadFile); | ||
1060 | downloadFile = NULL; | ||
1061 | } | ||
1062 | |||
1063 | sendProgress(1, 3); | ||
1064 | // Unmount image | ||
1065 | if(deviceNode[0] != 0) | ||
1066 | { | ||
1067 | llinfos << "Detaching disk image." << llendl; | ||
1068 | |||
1069 | snprintf(temp, sizeof(temp), "hdiutil detach '%s'", deviceNode); | ||
1070 | system(temp); | ||
1071 | } | ||
1072 | |||
1073 | sendProgress(2, 3); | ||
1074 | |||
1075 | // Move work directory to the trash | ||
1076 | if(tempDir[0] != 0) | ||
1077 | { | ||
1078 | // chdir("/"); | ||
1079 | // FSDeleteObjects(tempDirRef); | ||
1080 | |||
1081 | llinfos << "Moving work directory to the trash." << llendl; | ||
1082 | |||
1083 | err = FSMoveObject(&tempDirRef, &trashFolderRef, NULL); | ||
1084 | |||
1085 | // snprintf(temp, sizeof(temp), "rm -rf '%s'", tempDir); | ||
1086 | // printf("%s\n", temp); | ||
1087 | // system(temp); | ||
1088 | } | ||
1089 | |||
1090 | if(!gCancelled && !gFailure && (target[0] != 0)) | ||
1091 | { | ||
1092 | llinfos << "Touching application bundle." << llendl; | ||
1093 | |||
1094 | snprintf(temp, sizeof(temp), "touch '%s'", target); | ||
1095 | system(temp); | ||
1096 | |||
1097 | llinfos << "Launching updated application." << llendl; | ||
1098 | |||
1099 | snprintf(temp, sizeof(temp), "open '%s'", target); | ||
1100 | system(temp); | ||
1101 | } | ||
1102 | |||
1103 | sendDone(); | ||
1104 | |||
1105 | return(NULL); | ||
1106 | } | ||