]> git.saurik.com Git - wxWidgets.git/blob - src/mac/morefile/MoreExtr.cpp
added virtual listctrl support
[wxWidgets.git] / src / mac / morefile / MoreExtr.cpp
1 /*
2 ** Apple Macintosh Developer Technical Support
3 **
4 ** A collection of useful high-level File Manager routines.
5 **
6 ** by Jim Luther, Apple Developer Technical Support Emeritus
7 **
8 ** File: MoreFilesExtras.c
9 **
10 ** Copyright © 1992-1998 Apple Computer, Inc.
11 ** All rights reserved.
12 **
13 ** You may incorporate this sample code into your applications without
14 ** restriction, though the sample code has been provided "AS IS" and the
15 ** responsibility for its operation is 100% yours. However, what you are
16 ** not permitted to do is to redistribute the source as "DSC Sample Code"
17 ** after having made changes. If you're going to re-distribute the source,
18 ** we require that you make it clear in the source that the code was
19 ** descended from Apple Sample Code, but that you've made changes.
20 */
21
22 #include <Types.h>
23 #include <Traps.h>
24 #include <OSUtils.h>
25 #include <Errors.h>
26 #include <Files.h>
27 #include <Devices.h>
28 #include <Finder.h>
29 #include <Folders.h>
30 #include <FSM.h>
31 #include <Disks.h>
32 #include <Gestalt.h>
33 #include <TextUtils.h>
34 #include <Script.h>
35 #include <Script.h>
36 #include <stddef.h>
37 #include <Math64.h>
38
39 #define __COMPILINGMOREFILES
40
41 #include "morefile.h"
42 #include "moreextr.h"
43 #include "moredesk.h"
44 #include "fspcompa.h"
45
46 /*****************************************************************************/
47
48 /* local data structures */
49
50 /* The DeleteEnumGlobals structure is used to minimize the amount of
51 ** stack space used when recursively calling DeleteLevel and to hold
52 ** global information that might be needed at any time. */
53
54 #if PRAGMA_STRUCT_ALIGN
55 #pragma options align=mac68k
56 #elif PRAGMA_STRUCT_PACKPUSH
57 #pragma pack(push, 2)
58 #elif PRAGMA_STRUCT_PACK
59 #pragma pack(2)
60 #endif
61 struct DeleteEnumGlobals
62 {
63 OSErr error; /* temporary holder of results - saves 2 bytes of stack each level */
64 Str63 itemName; /* the name of the current item */
65 UniversalFMPB myPB; /* the parameter block used for PBGetCatInfo calls */
66 };
67 #if PRAGMA_STRUCT_ALIGN
68 #pragma options align=reset
69 #elif PRAGMA_STRUCT_PACKPUSH
70 #pragma pack(pop)
71 #elif PRAGMA_STRUCT_PACK
72 #pragma pack()
73 #endif
74
75 typedef struct DeleteEnumGlobals DeleteEnumGlobals;
76 typedef DeleteEnumGlobals *DeleteEnumGlobalsPtr;
77
78 /*****************************************************************************/
79
80 pascal void TruncPString(StringPtr destination,
81 ConstStr255Param source,
82 short maxLength)
83 {
84 short charType;
85
86 if ( source != NULL && destination != NULL ) /* don't do anything stupid */
87 {
88 if ( source[0] > maxLength )
89 {
90 /* Make sure the string isn't truncated in the middle of */
91 /* a multi-byte character. */
92 while (maxLength != 0)
93 {
94 #if TARGET_CARBON
95 charType = CharacterByteType((Ptr)&source[1], maxLength,smAllScripts);
96 #else
97 charType = CharByte((Ptr)&source[1], maxLength);
98 #endif
99 if ( (charType == smSingleByte) || (charType == smLastByte) )
100 break; /* source[maxLength] is now a valid last character */
101 --maxLength;
102 }
103 }
104 else
105 {
106 maxLength = source[0];
107 }
108 /* Set the destination string length */
109 destination[0] = maxLength;
110 /* and copy maxLength characters (if needed) */
111 if ( source != destination )
112 {
113 while ( maxLength != 0 )
114 {
115 destination[maxLength] = source[maxLength];
116 --maxLength;
117 }
118 }
119 }
120 }
121
122 /*****************************************************************************/
123
124 pascal Ptr GetTempBuffer(long buffReqSize,
125 long *buffActSize)
126 {
127 enum
128 {
129 kSlopMemory = 0x00008000 /* 32K - Amount of free memory to leave when allocating buffers */
130 };
131 Ptr tempPtr;
132
133 /* Make request a multiple of 1024 bytes */
134 buffReqSize = buffReqSize & 0xfffffc00;
135
136 if ( buffReqSize < 0x00000400 )
137 {
138 /* Request was smaller than 1024 bytes - make it 1024 */
139 buffReqSize = 0x00000400;
140 }
141
142 /* Attempt to allocate the memory */
143 tempPtr = NewPtr(buffReqSize);
144
145 /* If request failed, go to backup plan */
146 if ( (tempPtr == NULL) && (buffReqSize > 0x00000400) )
147 {
148 /*
149 ** Try to get largest 1024-byte block available
150 ** leaving some slop for the toolbox if possible
151 */
152 long freeMemory = (FreeMem() - kSlopMemory) & 0xfffffc00;
153
154 buffReqSize = MaxBlock() & 0xfffffc00;
155
156 if ( buffReqSize > freeMemory )
157 {
158 buffReqSize = freeMemory;
159 }
160
161 if ( buffReqSize == 0 )
162 {
163 buffReqSize = 0x00000400;
164 }
165
166 tempPtr = NewPtr(buffReqSize);
167 }
168
169 /* Return bytes allocated */
170 if ( tempPtr != NULL )
171 {
172 *buffActSize = buffReqSize;
173 }
174 else
175 {
176 *buffActSize = 0;
177 }
178
179 return ( tempPtr );
180 }
181
182 /*****************************************************************************/
183
184 /*
185 ** GetVolumeInfoNoName uses pathname and vRefNum to call PBHGetVInfoSync
186 ** in cases where the returned volume name is not needed by the caller.
187 ** The pathname and vRefNum parameters are not touched, and the pb
188 ** parameter is initialized by PBHGetVInfoSync except that ioNamePtr in
189 ** the parameter block is always returned as NULL (since it might point
190 ** to the local tempPathname).
191 **
192 ** I noticed using this code in several places, so here it is once.
193 ** This reduces the code size of MoreFiles.
194 */
195 pascal OSErr GetVolumeInfoNoName(ConstStr255Param pathname,
196 short vRefNum,
197 HParmBlkPtr pb)
198 {
199 Str255 tempPathname;
200 OSErr error;
201
202 /* Make sure pb parameter is not NULL */
203 if ( pb != NULL )
204 {
205 pb->volumeParam.ioVRefNum = vRefNum;
206 if ( pathname == NULL )
207 {
208 pb->volumeParam.ioNamePtr = NULL;
209 pb->volumeParam.ioVolIndex = 0; /* use ioVRefNum only */
210 }
211 else
212 {
213 BlockMoveData(pathname, tempPathname, pathname[0] + 1); /* make a copy of the string and */
214 pb->volumeParam.ioNamePtr = (StringPtr)tempPathname; /* use the copy so original isn't trashed */
215 pb->volumeParam.ioVolIndex = -1; /* use ioNamePtr/ioVRefNum combination */
216 }
217 error = PBHGetVInfoSync(pb);
218 pb->volumeParam.ioNamePtr = NULL; /* ioNamePtr may point to local tempPathname, so don't return it */
219 }
220 else
221 {
222 error = paramErr;
223 }
224 return ( error );
225 }
226
227 /*****************************************************************************/
228
229 /*
230 ** XGetVolumeInfoNoName uses pathname and vRefNum to call PBXGetVolInfoSync
231 ** in cases where the returned volume name is not needed by the caller.
232 ** The pathname and vRefNum parameters are not touched, and the pb
233 ** parameter is initialized by PBXGetVolInfoSync except that ioNamePtr in
234 ** the parameter block is always returned as NULL (since it might point
235 ** to the local tempPathname).
236 */
237
238 #if !TARGET_CARBON
239
240 pascal OSErr XGetVolumeInfoNoName(ConstStr255Param pathname,
241 short vRefNum,
242 XVolumeParamPtr pb)
243 {
244 Str255 tempPathname;
245 long response;
246 OSErr error;
247
248 /* Make sure pb parameter is not NULL */
249 if ( pb != NULL )
250 {
251 pb->ioVRefNum = vRefNum;
252 pb->ioXVersion = 0; /* this XVolumeParam version (0) */
253 if ( pathname == NULL )
254 {
255 pb->ioNamePtr = NULL;
256 pb->ioVolIndex = 0; /* use ioVRefNum only */
257 }
258 else
259 {
260 BlockMoveData(pathname, tempPathname, pathname[0] + 1); /* make a copy of the string and */
261 pb->ioNamePtr = (StringPtr)tempPathname; /* use the copy so original isn't trashed */
262 pb->ioVolIndex = -1; /* use ioNamePtr/ioVRefNum combination */
263 }
264 #if !__MACOSSEVENFIVEONEORLATER
265 /* Is PBXGetVolInfo available? */
266 if ( ( Gestalt(gestaltFSAttr, &response) != noErr ) || ((response & (1L << gestaltFSSupports2TBVols)) == 0) )
267 {
268 /* No, fall back on PBHGetVInfo */
269 error = PBHGetVInfoSync((HParmBlkPtr)pb);
270 if ( error == noErr )
271 {
272 /* calculate the ioVTotalBytes and ioVFreeBytes fields */
273 pb->ioVTotalBytes.hi = 0;
274 pb->ioVTotalBytes.lo = pb->ioVNmAlBlks * pb->ioVAlBlkSiz; /* calculated total number of bytes on volume */
275 pb->ioVFreeBytes.hi = 0;
276 pb->ioVFreeBytes.lo = pb->ioVFrBlk * pb->ioVAlBlkSiz; /* calculated number of free bytes on volume */
277 }
278 }
279 else
280 #endif // !__MACOSSEVENFIVEONEORLATER
281 {
282 /* Yes, so use it */
283 error = PBXGetVolInfoSync(pb);
284 }
285 pb->ioNamePtr = NULL; /* ioNamePtr may point to local tempPathname, so don't return it */
286 }
287 else
288 {
289 error = paramErr;
290 }
291 return ( error );
292 }
293
294 #endif
295
296 /*****************************************************************************/
297
298 pascal OSErr GetCatInfoNoName(short vRefNum,
299 long dirID,
300 ConstStr255Param name,
301 CInfoPBPtr pb)
302 {
303 Str31 tempName;
304 OSErr error;
305
306 /* Protection against File Sharing problem */
307 if ( (name == NULL) || (name[0] == 0) )
308 {
309 tempName[0] = 0;
310 pb->dirInfo.ioNamePtr = tempName;
311 pb->dirInfo.ioFDirIndex = -1; /* use ioDirID */
312 }
313 else
314 {
315 pb->dirInfo.ioNamePtr = (StringPtr)name;
316 pb->dirInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */
317 }
318 pb->dirInfo.ioVRefNum = vRefNum;
319 pb->dirInfo.ioDrDirID = dirID;
320 error = PBGetCatInfoSync(pb);
321 pb->dirInfo.ioNamePtr = NULL;
322 return ( error );
323 }
324
325 /*****************************************************************************/
326
327 pascal OSErr DetermineVRefNum(ConstStr255Param pathname,
328 short vRefNum,
329 short *realVRefNum)
330 {
331 HParamBlockRec pb;
332 OSErr error;
333
334 error = GetVolumeInfoNoName(pathname,vRefNum, &pb);
335 if ( error == noErr )
336 {
337 *realVRefNum = pb.volumeParam.ioVRefNum;
338 }
339 return ( error );
340 }
341
342 /*****************************************************************************/
343
344 #if !TARGET_CARBON
345 pascal OSErr HGetVInfo(short volReference,
346 StringPtr volName,
347 short *vRefNum,
348 unsigned long *freeBytes,
349 unsigned long *totalBytes)
350 {
351 HParamBlockRec pb;
352 unsigned long allocationBlockSize;
353 unsigned short numAllocationBlocks;
354 unsigned short numFreeBlocks;
355 VCB *theVCB;
356 Boolean vcbFound;
357 OSErr result;
358
359 /* Use the File Manager to get the real vRefNum */
360 pb.volumeParam.ioVRefNum = volReference;
361 pb.volumeParam.ioNamePtr = volName;
362 pb.volumeParam.ioVolIndex = 0; /* use ioVRefNum only, return volume name */
363 result = PBHGetVInfoSync(&pb);
364
365 if ( result == noErr )
366 {
367 /* The volume name was returned in volName (if not NULL) and */
368 /* we have the volume's vRefNum and allocation block size */
369 *vRefNum = pb.volumeParam.ioVRefNum;
370 allocationBlockSize = (unsigned long)pb.volumeParam.ioVAlBlkSiz;
371
372 /* System 7.5 (and beyond) pins the number of allocation blocks and */
373 /* the number of free allocation blocks returned by PBHGetVInfo to */
374 /* a value so that when multiplied by the allocation block size, */
375 /* the volume will look like it has $7fffffff bytes or less. This */
376 /* was done so older applications that use signed math or that use */
377 /* the GetVInfo function (which uses signed math) will continue to work. */
378 /* However, the unpinned numbers (which we want) are always available */
379 /* in the volume's VCB so we'll get those values from the VCB if possible. */
380
381 /* Find the volume's VCB */
382 vcbFound = false;
383 theVCB = (VCB *)(GetVCBQHdr()->qHead);
384 while ( (theVCB != NULL) && !vcbFound )
385 {
386 /* Check VCB signature before using VCB. Don't have to check for */
387 /* MFS (0xd2d7) because they can't get big enough to be pinned */
388 if ( theVCB->vcbSigWord == 0x4244 )
389 {
390 if ( theVCB->vcbVRefNum == *vRefNum )
391 {
392 vcbFound = true;
393 }
394 }
395
396 if ( !vcbFound )
397 {
398 theVCB = (VCB *)(theVCB->qLink);
399 }
400 }
401
402 if ( theVCB != NULL )
403 {
404 /* Found a VCB we can use. Get the un-pinned number of allocation blocks */
405 /* and the number of free blocks from the VCB. */
406 numAllocationBlocks = (unsigned short)theVCB->vcbNmAlBlks;
407 numFreeBlocks = (unsigned short)theVCB->vcbFreeBks;
408 }
409 else
410 {
411 /* Didn't find a VCB we can use. Return the number of allocation blocks */
412 /* and the number of free blocks returned by PBHGetVInfoSync. */
413 numAllocationBlocks = (unsigned short)pb.volumeParam.ioVNmAlBlks;
414 numFreeBlocks = (unsigned short)pb.volumeParam.ioVFrBlk;
415 }
416
417 /* Now, calculate freeBytes and totalBytes using unsigned values */
418 *freeBytes = numFreeBlocks * allocationBlockSize;
419 *totalBytes = numAllocationBlocks * allocationBlockSize;
420 }
421
422 return ( result );
423 }
424 #endif
425 /*****************************************************************************/
426
427 /*
428 ** PBXGetVolInfoSync is the glue code needed to make PBXGetVolInfoSync
429 ** File Manager requests from CFM-based programs. At some point, Apple
430 ** will get around to adding this to the standard libraries you link with
431 ** and you'll get a duplicate symbol link error. At that time, just delete
432 ** this code (or comment it out).
433 **
434 ** Non-CFM 68K programs don't needs this glue (and won't get it) because
435 ** they instead use the inline assembly glue found in the Files.h interface
436 ** file.
437 */
438
439 #if __WANTPASCALELIMINATION
440 #undef pascal
441 #endif
442
443 #if !TARGET_CARBON
444
445 #if GENERATINGCFM
446
447 #if UNIVERSAL_INTERFACES_VERSION < 0x0301
448
449 pascal OSErr PBXGetVolInfoSync(XVolumeParamPtr paramBlock)
450 {
451 enum
452 {
453 kXGetVolInfoSelector = 0x0012, /* Selector for XGetVolInfo */
454
455 uppFSDispatchProcInfo = kRegisterBased
456 | REGISTER_RESULT_LOCATION(kRegisterD0)
457 | RESULT_SIZE(SIZE_CODE(sizeof(OSErr)))
458 | REGISTER_ROUTINE_PARAMETER(1, kRegisterD1, SIZE_CODE(sizeof(long))) /* trap word */
459 | REGISTER_ROUTINE_PARAMETER(2, kRegisterD0, SIZE_CODE(sizeof(long))) /* selector */
460 | REGISTER_ROUTINE_PARAMETER(3, kRegisterA0, SIZE_CODE(sizeof(XVolumeParamPtr)))
461 };
462
463 return ( CallOSTrapUniversalProc(NGetTrapAddress(_FSDispatch, OSTrap),
464 uppFSDispatchProcInfo,
465 _FSDispatch,
466 kXGetVolInfoSelector,
467 paramBlock) );
468 }
469 #endif
470
471 #endif
472
473 #endif
474
475 #if __WANTPASCALELIMINATION
476 #define pascal
477 #endif
478
479 #if !TARGET_CARBON
480
481 /*****************************************************************************/
482
483 pascal OSErr XGetVInfo(short volReference,
484 StringPtr volName,
485 short *vRefNum,
486 UnsignedWide *freeBytes,
487 UnsignedWide *totalBytes)
488 {
489 OSErr result;
490 long response;
491 XVolumeParam pb;
492
493 /* See if large volume support is available */
494 if ( ( Gestalt(gestaltFSAttr, &response) == noErr ) && ((response & (1L << gestaltFSSupports2TBVols)) != 0) )
495 {
496 /* Large volume support is available */
497 pb.ioVRefNum = volReference;
498 pb.ioNamePtr = volName;
499 pb.ioXVersion = 0; /* this XVolumeParam version (0) */
500 pb.ioVolIndex = 0; /* use ioVRefNum only, return volume name */
501 result = PBXGetVolInfoSync(&pb);
502 if ( result == noErr )
503 {
504 /* The volume name was returned in volName (if not NULL) and */
505 /* we have the volume's vRefNum and allocation block size */
506 *vRefNum = pb.ioVRefNum;
507
508 /* return the freeBytes and totalBytes */
509 *totalBytes = UInt64ToUnsignedWide(pb.ioVTotalBytes);
510 *freeBytes = UInt64ToUnsignedWide(pb.ioVFreeBytes);
511 }
512 }
513 else
514 {
515 /* No large volume support */
516
517 /* Use HGetVInfo to get the results */
518 result = HGetVInfo(volReference, volName, vRefNum, &freeBytes->lo, &totalBytes->lo);
519 if ( result == noErr )
520 {
521 /* zero the high longs of totalBytes and freeBytes */
522 totalBytes->hi = 0;
523 freeBytes->hi = 0;
524 }
525 }
526 return ( result );
527 }
528 #endif
529 /*****************************************************************************/
530
531 pascal OSErr CheckVolLock(ConstStr255Param pathname,
532 short vRefNum)
533 {
534 HParamBlockRec pb;
535 OSErr error;
536
537 error = GetVolumeInfoNoName(pathname,vRefNum, &pb);
538 if ( error == noErr )
539 {
540 if ( (pb.volumeParam.ioVAtrb & 0x0080) != 0 )
541 {
542 error = wPrErr; /* volume locked by hardware */
543 }
544 else if ( (pb.volumeParam.ioVAtrb & 0x8000) != 0 )
545 {
546 error = vLckdErr; /* volume locked by software */
547 }
548 }
549
550 return ( error );
551 }
552
553 /*****************************************************************************/
554
555 #if !TARGET_CARBON
556
557 pascal OSErr GetDriverName(short driverRefNum,
558 Str255 driverName)
559 {
560 OSErr result;
561 DCtlHandle theDctl;
562 DRVRHeaderPtr dHeaderPtr;
563
564 theDctl = GetDCtlEntry(driverRefNum);
565 if ( theDctl != NULL )
566 {
567 if ( (**theDctl).dCtlFlags & 0x40 )
568 {
569 /* dctlDriver is handle - dereference */
570 dHeaderPtr = *((DRVRHeaderHandle)(**theDctl).dCtlDriver);
571 }
572 else
573 {
574 /* dctlDriver is pointer */
575 dHeaderPtr = (DRVRHeaderPtr)(**theDctl).dCtlDriver;
576 }
577 BlockMoveData((*dHeaderPtr).drvrName, driverName, (*dHeaderPtr).drvrName[0] + 1);
578 result = noErr;
579 }
580 else
581 {
582 driverName[0] = 0;
583 result = badUnitErr; /* bad reference number */
584 }
585
586 return ( result );
587 }
588
589 #endif
590 /*****************************************************************************/
591 #if !TARGET_CARBON
592
593 pascal OSErr FindDrive(ConstStr255Param pathname,
594 short vRefNum,
595 DrvQElPtr *driveQElementPtr)
596 {
597 OSErr result;
598 HParamBlockRec hPB;
599 short driveNumber;
600
601 *driveQElementPtr = NULL;
602
603 /* First, use GetVolumeInfoNoName to determine the volume */
604 result = GetVolumeInfoNoName(pathname, vRefNum, &hPB);
605 if ( result == noErr )
606 {
607 /*
608 ** The volume can be either online, offline, or ejected. What we find in
609 ** ioVDrvInfo and ioVDRefNum will tell us which it is.
610 ** See Inside Macintosh: Files page 2-80 and the Technical Note
611 ** "FL 34 - VCBs and Drive Numbers : The Real Story"
612 ** Where we get the drive number depends on the state of the volume.
613 */
614 if ( hPB.volumeParam.ioVDrvInfo != 0 )
615 {
616 /* The volume is online and not ejected */
617 /* Get the drive number */
618 driveNumber = hPB.volumeParam.ioVDrvInfo;
619 }
620 else
621 {
622 /* The volume's is either offline or ejected */
623 /* in either case, the volume is NOT online */
624
625 /* Is it ejected or just offline? */
626 if ( hPB.volumeParam.ioVDRefNum > 0 )
627 {
628 /* It's ejected, the drive number is ioVDRefNum */
629 driveNumber = hPB.volumeParam.ioVDRefNum;
630 }
631 else
632 {
633 /* It's offline, the drive number is the negative of ioVDRefNum */
634 driveNumber = (short)-hPB.volumeParam.ioVDRefNum;
635 }
636 }
637
638 /* Get pointer to first element in drive queue */
639 *driveQElementPtr = (DrvQElPtr)(GetDrvQHdr()->qHead);
640
641 /* Search for a matching drive number */
642 while ( (*driveQElementPtr != NULL) && ((*driveQElementPtr)->dQDrive != driveNumber) )
643 {
644 *driveQElementPtr = (DrvQElPtr)(*driveQElementPtr)->qLink;
645 }
646
647 if ( *driveQElementPtr == NULL )
648 {
649 /* This should never happen since every volume must have a drive, but... */
650 result = nsDrvErr;
651 }
652 }
653
654 return ( result );
655 }
656 #endif
657
658 /*****************************************************************************/
659 #if !TARGET_CARBON
660
661 pascal OSErr GetDiskBlocks(ConstStr255Param pathname,
662 short vRefNum,
663 unsigned long *numBlocks)
664 {
665 /* Various constants for GetDiskBlocks() */
666 enum
667 {
668 /* return format list status code */
669 kFmtLstCode = 6,
670
671 /* reference number of .SONY driver */
672 kSonyRefNum = 0xfffb,
673
674 /* values returned by DriveStatus in DrvSts.twoSideFmt */
675 kSingleSided = 0,
676 kDoubleSided = -1,
677 kSingleSidedSize = 800, /* 400K */
678 kDoubleSidedSize = 1600, /* 800K */
679
680 /* values in DrvQEl.qType */
681 kWordDrvSiz = 0,
682 kLongDrvSiz = 1,
683
684 /* more than enough formatListRecords */
685 kMaxFormatListRecs = 16
686 };
687
688 DrvQElPtr driveQElementPtr;
689 unsigned long blocks;
690 ParamBlockRec pb;
691 FormatListRec formatListRecords[kMaxFormatListRecs];
692 DrvSts status;
693 short formatListRecIndex;
694 OSErr result;
695
696 blocks = 0;
697
698 /* Find the drive queue element for this volume */
699 result = FindDrive(pathname, vRefNum, &driveQElementPtr);
700
701 /*
702 ** Make sure this is a real driver (dQRefNum < 0).
703 ** AOCE's Mail Enclosures volume uses 0 for dQRefNum which will cause
704 ** problems if you try to use it as a driver refNum.
705 */
706 if ( (result == noErr) && (driveQElementPtr->dQRefNum >= 0) )
707 {
708 result = paramErr;
709 }
710 else
711 {
712 /* Attempt to get the drive's format list. */
713 /* (see the Technical Note "What Your Sony Drives For You") */
714
715 pb.cntrlParam.ioVRefNum = driveQElementPtr->dQDrive;
716 pb.cntrlParam.ioCRefNum = driveQElementPtr->dQRefNum;
717 pb.cntrlParam.csCode = kFmtLstCode;
718 pb.cntrlParam.csParam[0] = kMaxFormatListRecs;
719 *(long *)&pb.cntrlParam.csParam[1] = (long)&formatListRecords[0];
720
721 result = PBStatusSync(&pb);
722
723 if ( result == noErr )
724 {
725 /* The drive supports ReturnFormatList status call. */
726
727 /* Get the current disk's size. */
728 for( formatListRecIndex = 0;
729 formatListRecIndex < pb.cntrlParam.csParam[0];
730 ++formatListRecIndex )
731 {
732 if ( (formatListRecords[formatListRecIndex].formatFlags &
733 diCIFmtFlagsCurrentMask) != 0 )
734 {
735 blocks = formatListRecords[formatListRecIndex].volSize;
736 }
737 }
738 if ( blocks == 0 )
739 {
740 /* This should never happen */
741 result = paramErr;
742 }
743 }
744 else if ( driveQElementPtr->dQRefNum == (short)kSonyRefNum )
745 {
746 /* The drive is a non-SuperDrive floppy which only supports 400K and 800K disks */
747
748 result = DriveStatus(driveQElementPtr->dQDrive, &status);
749 if ( result == noErr )
750 {
751 switch ( status.twoSideFmt )
752 {
753 case kSingleSided:
754 blocks = kSingleSidedSize;
755 break;
756 case kDoubleSided:
757 blocks = kDoubleSidedSize;
758 break;
759 default:
760 /* This should never happen */
761 result = paramErr;
762 break;
763 }
764 }
765 }
766 else
767 {
768 /* The drive is not a floppy and it doesn't support ReturnFormatList */
769 /* so use the dQDrvSz field(s) */
770
771 result = noErr; /* reset result */
772 switch ( driveQElementPtr->qType )
773 {
774 case kWordDrvSiz:
775 blocks = driveQElementPtr->dQDrvSz;
776 break;
777 case kLongDrvSiz:
778 blocks = ((unsigned long)driveQElementPtr->dQDrvSz2 << 16) +
779 driveQElementPtr->dQDrvSz;
780 break;
781 default:
782 /* This should never happen */
783 result = paramErr;
784 break;
785 }
786 }
787 }
788
789 if ( result == noErr )
790 {
791 *numBlocks = blocks;
792 }
793
794 return ( result );
795 }
796 #endif
797
798 /*****************************************************************************/
799
800 pascal OSErr GetVolFileSystemID(ConstStr255Param pathname,
801 short vRefNum,
802 short *fileSystemID)
803 {
804 HParamBlockRec pb;
805 OSErr error;
806
807 error = GetVolumeInfoNoName(pathname,vRefNum, &pb);
808 if ( error == noErr )
809 {
810 *fileSystemID = pb.volumeParam.ioVFSID;
811 }
812
813 return ( error );
814 }
815
816 /*****************************************************************************/
817 #if !TARGET_CARBON
818
819 pascal OSErr GetVolState(ConstStr255Param pathname,
820 short vRefNum,
821 Boolean *volumeOnline,
822 Boolean *volumeEjected,
823 Boolean *driveEjectable,
824 Boolean *driverWantsEject)
825 {
826 HParamBlockRec pb;
827 short driveNumber;
828 OSErr error;
829
830 error = GetVolumeInfoNoName(pathname,vRefNum, &pb);
831 if ( error == noErr )
832 {
833 if ( pb.volumeParam.ioVDrvInfo != 0 )
834 {
835 /* the volume is online and not ejected */
836 *volumeOnline = true;
837 *volumeEjected = false;
838
839 /* Get the drive number */
840 driveNumber = pb.volumeParam.ioVDrvInfo;
841 }
842 else
843 {
844 /* the volume's is either offline or ejected */
845 /* in either case, the volume is NOT online */
846 *volumeOnline = false;
847
848 /* Is it ejected? */
849 *volumeEjected = pb.volumeParam.ioVDRefNum > 0;
850
851 if ( *volumeEjected )
852 {
853 /* If ejected, the drive number is ioVDRefNum */
854 driveNumber = pb.volumeParam.ioVDRefNum;
855 }
856 else
857 {
858 /* If offline, the drive number is the negative of ioVDRefNum */
859 driveNumber = (short)-pb.volumeParam.ioVDRefNum;
860 }
861 }
862
863 {
864 DrvQElPtr drvQElem;
865
866 /* Find the drive queue element by searching the drive queue */
867 drvQElem = (DrvQElPtr)(GetDrvQHdr()->qHead);
868 while ( (drvQElem != NULL) && (drvQElem->dQDrive != driveNumber) )
869 {
870 drvQElem = (DrvQElPtr)drvQElem->qLink;
871 }
872
873 if ( drvQElem != NULL )
874 {
875 /*
876 ** Each drive queue element is preceded by 4 flag bytes.
877 ** Byte 1 (the second flag byte) has bits that tell us if a
878 ** drive is ejectable and if its driver wants an eject call.
879 ** See Inside Macintosh: Files, page 2-85.
880 */
881 {
882 Ptr flagBytePtr;
883
884 /* point to byte 1 of the flag bytes */
885 flagBytePtr = (Ptr)drvQElem;
886 flagBytePtr -= 3;
887
888 /*
889 ** The drive is ejectable if flag byte 1 does not contain
890 ** 0x08 (nonejectable) or 0x48 (nonejectable, but wants eject call).
891 */
892
893 *driveEjectable = (*flagBytePtr != 0x08) && (*flagBytePtr != 0x48);
894
895 /*
896 ** The driver wants an eject call if flag byte 1 does not contain
897 ** 0x08 (nonejectable). This may seem like a minor point, but some
898 ** disk drivers use the Eject request to flush their caches to disk
899 ** and you wouldn't want to skip that step after unmounting a volume.
900 */
901
902 *driverWantsEject = (*flagBytePtr != 0x08);
903 }
904 }
905 else
906 {
907 /* Didn't find the drive (this should never happen) */
908 *driveEjectable = false;
909 *driverWantsEject = false;
910 }
911 }
912 }
913
914 return ( error );
915 }
916 #endif
917 /*****************************************************************************/
918 #if !TARGET_CARBON
919
920 pascal OSErr UnmountAndEject(ConstStr255Param pathname,
921 short vRefNum)
922 {
923 HParamBlockRec pb;
924 short driveNum;
925 Boolean ejected, wantsEject;
926 DrvQElPtr drvQElem;
927 OSErr error;
928
929 error = GetVolumeInfoNoName(pathname, vRefNum, &pb);
930 if ( error == noErr )
931 {
932 if ( pb.volumeParam.ioVDrvInfo != 0 )
933 {
934 /* the volume is online and not ejected */
935 ejected = false;
936
937 /* Get the drive number */
938 driveNum = pb.volumeParam.ioVDrvInfo;
939 }
940 else
941 {
942 /* the volume is ejected or offline */
943
944 /* Is it ejected? */
945 ejected = pb.volumeParam.ioVDRefNum > 0;
946
947 if ( ejected )
948 {
949 /* If ejected, the drive number is ioVDRefNum */
950 driveNum = pb.volumeParam.ioVDRefNum;
951 }
952 else
953 {
954 /* If offline, the drive number is the negative of ioVDRefNum */
955 driveNum = (short)-pb.volumeParam.ioVDRefNum;
956 }
957 }
958
959 /* find the drive queue element */
960 drvQElem = (DrvQElPtr)(GetDrvQHdr()->qHead);
961 while ( (drvQElem != NULL) && (drvQElem->dQDrive != driveNum) )
962 {
963 drvQElem = (DrvQElPtr)drvQElem->qLink;
964 }
965
966 if ( drvQElem != NULL )
967 {
968 /* does the drive want an eject call */
969 wantsEject = (*((Ptr)((Ptr)drvQElem - 3)) != 8);
970 }
971 else
972 {
973 /* didn't find the drive!! */
974 wantsEject = false;
975 }
976
977 /* unmount the volume */
978 pb.volumeParam.ioNamePtr = NULL;
979 /* ioVRefNum is already filled in from PBHGetVInfo */
980 error = PBUnmountVol((ParmBlkPtr)&pb);
981 if ( error == noErr )
982 {
983 if ( wantsEject && !ejected )
984 {
985 /* eject the media from the drive if needed */
986 pb.volumeParam.ioVRefNum = driveNum;
987 error = PBEject((ParmBlkPtr)&pb);
988 }
989 }
990 }
991
992 return ( error );
993 }
994 #endif
995
996 /*****************************************************************************/
997
998 pascal OSErr OnLine(FSSpecPtr volumes,
999 short reqVolCount,
1000 short *actVolCount,
1001 short *volIndex)
1002 {
1003 HParamBlockRec pb;
1004 OSErr error = noErr;
1005 FSSpec *endVolArray;
1006
1007 if ( *volIndex > 0 )
1008 {
1009 *actVolCount = 0;
1010 for ( endVolArray = volumes + reqVolCount; (volumes < endVolArray) && (error == noErr); ++volumes )
1011 {
1012 pb.volumeParam.ioNamePtr = (StringPtr) & volumes->name;
1013 pb.volumeParam.ioVolIndex = *volIndex;
1014 error = PBHGetVInfoSync(&pb);
1015 if ( error == noErr )
1016 {
1017 volumes->parID = fsRtParID; /* the root directory's parent is 1 */
1018 volumes->vRefNum = pb.volumeParam.ioVRefNum;
1019 ++*volIndex;
1020 ++*actVolCount;
1021 }
1022 }
1023 }
1024 else
1025 {
1026 error = paramErr;
1027 }
1028
1029 return ( error );
1030 }
1031
1032 /*****************************************************************************/
1033
1034 pascal OSErr SetDefault(short newVRefNum,
1035 long newDirID,
1036 short *oldVRefNum,
1037 long *oldDirID)
1038 {
1039 OSErr error;
1040
1041 /* Get the current default volume/directory. */
1042 error = HGetVol(NULL, oldVRefNum, oldDirID);
1043 if ( error == noErr )
1044 {
1045 /* Set the new default volume/directory */
1046 error = HSetVol(NULL, newVRefNum, newDirID);
1047 }
1048
1049 return ( error );
1050 }
1051
1052 /*****************************************************************************/
1053
1054 #if !TARGET_CARBON
1055
1056 pascal OSErr RestoreDefault(short oldVRefNum,
1057 long oldDirID)
1058 {
1059 OSErr error;
1060 short defaultVRefNum;
1061 long defaultDirID;
1062 long defaultProcID;
1063
1064 /* Determine if the default volume was a wdRefNum. */
1065 error = GetWDInfo(oldVRefNum, &defaultVRefNum, &defaultDirID, &defaultProcID);
1066 if ( error == noErr )
1067 {
1068 /* Restore the old default volume/directory, one way or the other. */
1069 if ( defaultDirID != fsRtDirID )
1070 {
1071 /* oldVRefNum was a wdRefNum - use SetVol */
1072 error = SetVol(NULL, oldVRefNum);
1073 }
1074 else
1075 {
1076 /* oldVRefNum was a real vRefNum - use HSetVol */
1077 error = HSetVol(NULL, oldVRefNum, oldDirID);
1078 }
1079 }
1080
1081 return ( error );
1082 }
1083 #endif
1084 /*****************************************************************************/
1085
1086 pascal OSErr GetDInfo(short vRefNum,
1087 long dirID,
1088 ConstStr255Param name,
1089 DInfo *fndrInfo)
1090 {
1091 CInfoPBRec pb;
1092 OSErr error;
1093
1094 error = GetCatInfoNoName(vRefNum, dirID, name, &pb);
1095 if ( error == noErr )
1096 {
1097 if ( (pb.dirInfo.ioFlAttrib & ioDirMask) != 0 )
1098 {
1099 /* it's a directory, return the DInfo */
1100 *fndrInfo = pb.dirInfo.ioDrUsrWds;
1101 }
1102 else
1103 {
1104 /* oops, a file was passed */
1105 error = dirNFErr;
1106 }
1107 }
1108
1109 return ( error );
1110 }
1111
1112 /*****************************************************************************/
1113
1114 pascal OSErr FSpGetDInfo(const FSSpec *spec,
1115 DInfo *fndrInfo)
1116 {
1117 return ( GetDInfo(spec->vRefNum, spec->parID, spec->name, fndrInfo) );
1118 }
1119
1120 /*****************************************************************************/
1121
1122 pascal OSErr SetDInfo(short vRefNum,
1123 long dirID,
1124 ConstStr255Param name,
1125 const DInfo *fndrInfo)
1126 {
1127 CInfoPBRec pb;
1128 Str31 tempName;
1129 OSErr error;
1130
1131 /* Protection against File Sharing problem */
1132 if ( (name == NULL) || (name[0] == 0) )
1133 {
1134 tempName[0] = 0;
1135 pb.dirInfo.ioNamePtr = tempName;
1136 pb.dirInfo.ioFDirIndex = -1; /* use ioDirID */
1137 }
1138 else
1139 {
1140 pb.dirInfo.ioNamePtr = (StringPtr)name;
1141 pb.dirInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */
1142 }
1143 pb.dirInfo.ioVRefNum = vRefNum;
1144 pb.dirInfo.ioDrDirID = dirID;
1145 error = PBGetCatInfoSync(&pb);
1146 if ( error == noErr )
1147 {
1148 if ( (pb.dirInfo.ioFlAttrib & ioDirMask) != 0 )
1149 {
1150 /* it's a directory, set the DInfo */
1151 if ( pb.dirInfo.ioNamePtr == tempName )
1152 {
1153 pb.dirInfo.ioDrDirID = pb.dirInfo.ioDrParID;
1154 }
1155 else
1156 {
1157 pb.dirInfo.ioDrDirID = dirID;
1158 }
1159 pb.dirInfo.ioDrUsrWds = *fndrInfo;
1160 error = PBSetCatInfoSync(&pb);
1161 }
1162 else
1163 {
1164 /* oops, a file was passed */
1165 error = dirNFErr;
1166 }
1167 }
1168
1169 return ( error );
1170 }
1171
1172 /*****************************************************************************/
1173
1174 pascal OSErr FSpSetDInfo(const FSSpec *spec,
1175 const DInfo *fndrInfo)
1176 {
1177 return ( SetDInfo(spec->vRefNum, spec->parID, spec->name, fndrInfo) );
1178 }
1179
1180 /*****************************************************************************/
1181
1182 pascal OSErr GetDirectoryID(short vRefNum,
1183 long dirID,
1184 ConstStr255Param name,
1185 long *theDirID,
1186 Boolean *isDirectory)
1187 {
1188 CInfoPBRec pb;
1189 OSErr error;
1190
1191 error = GetCatInfoNoName(vRefNum, dirID, name, &pb);
1192 if ( error == noErr )
1193 {
1194 *isDirectory = (pb.hFileInfo.ioFlAttrib & ioDirMask) != 0;
1195 if ( *isDirectory )
1196 {
1197 *theDirID = pb.dirInfo.ioDrDirID;
1198 }
1199 else
1200 {
1201 *theDirID = pb.hFileInfo.ioFlParID;
1202 }
1203 }
1204
1205 return ( error );
1206 }
1207
1208 /*****************************************************************************/
1209
1210 pascal OSErr FSpGetDirectoryID(const FSSpec *spec,
1211 long *theDirID,
1212 Boolean *isDirectory)
1213 {
1214 return ( GetDirectoryID(spec->vRefNum, spec->parID, spec->name,
1215 theDirID, isDirectory) );
1216 }
1217
1218 /*****************************************************************************/
1219
1220 pascal OSErr GetDirName(short vRefNum,
1221 long dirID,
1222 Str31 name)
1223 {
1224 CInfoPBRec pb;
1225 OSErr error;
1226
1227 if ( name != NULL )
1228 {
1229 pb.dirInfo.ioNamePtr = name;
1230 pb.dirInfo.ioVRefNum = vRefNum;
1231 pb.dirInfo.ioDrDirID = dirID;
1232 pb.dirInfo.ioFDirIndex = -1; /* get information about ioDirID */
1233 error = PBGetCatInfoSync(&pb);
1234 }
1235 else
1236 {
1237 error = paramErr;
1238 }
1239
1240 return ( error );
1241 }
1242
1243 /*****************************************************************************/
1244
1245 pascal OSErr GetIOACUser(short vRefNum,
1246 long dirID,
1247 ConstStr255Param name,
1248 SInt8 *ioACUser)
1249 {
1250 CInfoPBRec pb;
1251 OSErr error;
1252
1253 /* Clear ioACUser before calling PBGetCatInfo since some file systems
1254 ** don't bother to set or clear this field. If ioACUser isn't set by the
1255 ** file system, then you'll get the zero value back (full access) which
1256 ** is the access you have on volumes that don't support ioACUser.
1257 */
1258 pb.dirInfo.ioACUser = 0; /* ioACUser used to be filler2 */
1259 error = GetCatInfoNoName(vRefNum, dirID, name, &pb);
1260 if ( error == noErr )
1261 {
1262 if ( (pb.hFileInfo.ioFlAttrib & ioDirMask) == 0 )
1263 {
1264 /* oops, a file was passed */
1265 error = dirNFErr;
1266 }
1267 else
1268 {
1269 *ioACUser = pb.dirInfo.ioACUser;
1270 }
1271 }
1272
1273 return ( error );
1274 }
1275
1276 /*****************************************************************************/
1277
1278 pascal OSErr FSpGetIOACUser(const FSSpec *spec,
1279 SInt8 *ioACUser)
1280 {
1281 return ( GetIOACUser(spec->vRefNum, spec->parID, spec->name, ioACUser) );
1282 }
1283
1284 /*****************************************************************************/
1285
1286 pascal OSErr GetParentID(short vRefNum,
1287 long dirID,
1288 ConstStr255Param name,
1289 long *parID)
1290 {
1291 CInfoPBRec pb;
1292 Str31 tempName;
1293 OSErr error;
1294 short realVRefNum;
1295
1296 /* Protection against File Sharing problem */
1297 if ( (name == NULL) || (name[0] == 0) )
1298 {
1299 tempName[0] = 0;
1300 pb.hFileInfo.ioNamePtr = tempName;
1301 pb.hFileInfo.ioFDirIndex = -1; /* use ioDirID */
1302 }
1303 else
1304 {
1305 pb.hFileInfo.ioNamePtr = (StringPtr)name;
1306 pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */
1307 }
1308 pb.hFileInfo.ioVRefNum = vRefNum;
1309 pb.hFileInfo.ioDirID = dirID;
1310 error = PBGetCatInfoSync(&pb);
1311 if ( error == noErr )
1312 {
1313 /*
1314 ** There's a bug in HFS where the wrong parent dir ID can be
1315 ** returned if multiple separators are used at the end of a
1316 ** pathname. For example, if the pathname:
1317 ** 'volumeName:System Folder:Extensions::'
1318 ** is passed, the directory ID of the Extensions folder is
1319 ** returned in the ioFlParID field instead of fsRtDirID. Since
1320 ** multiple separators at the end of a pathname always specifies
1321 ** a directory, we only need to work-around cases where the
1322 ** object is a directory and there are multiple separators at
1323 ** the end of the name parameter.
1324 */
1325 if ( (pb.hFileInfo.ioFlAttrib & ioDirMask) != 0 )
1326 {
1327 /* Its a directory */
1328
1329 /* is there a pathname? */
1330 if ( pb.hFileInfo.ioNamePtr == name )
1331 {
1332 /* could it contain multiple separators? */
1333 if ( name[0] >= 2 )
1334 {
1335 /* does it contain multiple separators at the end? */
1336 if ( (name[name[0]] == ':') && (name[name[0] - 1] == ':') )
1337 {
1338 /* OK, then do the extra stuff to get the correct parID */
1339
1340 /* Get the real vRefNum (this should not fail) */
1341 error = DetermineVRefNum(name, vRefNum, &realVRefNum);
1342 if ( error == noErr )
1343 {
1344 /* we don't need the parent's name, but add protect against File Sharing problem */
1345 tempName[0] = 0;
1346 pb.dirInfo.ioNamePtr = tempName;
1347 pb.dirInfo.ioVRefNum = realVRefNum;
1348 /* pb.dirInfo.ioDrDirID already contains the */
1349 /* dirID of the directory object */
1350 pb.dirInfo.ioFDirIndex = -1; /* get information about ioDirID */
1351 error = PBGetCatInfoSync(&pb);
1352 /* now, pb.dirInfo.ioDrParID contains the correct parID */
1353 }
1354 }
1355 }
1356 }
1357 }
1358
1359 if ( error == noErr )
1360 {
1361 /* if no errors, then pb.hFileInfo.ioFlParID (pb.dirInfo.ioDrParID) */
1362 /* contains the parent ID */
1363 *parID = pb.hFileInfo.ioFlParID;
1364 }
1365 }
1366
1367 return ( error );
1368 }
1369
1370 /*****************************************************************************/
1371
1372 pascal OSErr GetFilenameFromPathname(ConstStr255Param pathname,
1373 Str255 filename)
1374 {
1375 short index;
1376 short nameEnd;
1377 OSErr error;
1378
1379 /* default to no filename */
1380 filename[0] = 0;
1381
1382 /* check for no pathname */
1383 if ( pathname != NULL )
1384 {
1385 /* get string length */
1386 index = pathname[0];
1387
1388 /* check for empty string */
1389 if ( index != 0 )
1390 {
1391 /* skip over last trailing colon (if any) */
1392 if ( pathname[index] == ':' )
1393 {
1394 --index;
1395 }
1396
1397 /* save the end of the string */
1398 nameEnd = index;
1399
1400 /* if pathname ends with multiple colons, then this pathname refers */
1401 /* to a directory, not a file */
1402 if ( pathname[index] != ':' )
1403 {
1404 /* parse backwards until we find a colon or hit the beginning of the pathname */
1405 while ( (index != 0) && (pathname[index] != ':') )
1406 {
1407 --index;
1408 }
1409
1410 /* if we parsed to the beginning of the pathname and the pathname ended */
1411 /* with a colon, then pathname is a full pathname to a volume, not a file */
1412 if ( (index != 0) || (pathname[pathname[0]] != ':') )
1413 {
1414 /* get the filename and return noErr */
1415 filename[0] = (char)(nameEnd - index);
1416 BlockMoveData(&pathname[index+1], &filename[1], nameEnd - index);
1417 error = noErr;
1418 }
1419 else
1420 {
1421 /* pathname to a volume, not a file */
1422 error = notAFileErr;
1423 }
1424 }
1425 else
1426 {
1427 /* directory, not a file */
1428 error = notAFileErr;
1429 }
1430 }
1431 else
1432 {
1433 /* empty string isn't a file */
1434 error = notAFileErr;
1435 }
1436 }
1437 else
1438 {
1439 /* NULL pathname isn't a file */
1440 error = notAFileErr;
1441 }
1442
1443 return ( error );
1444 }
1445
1446 /*****************************************************************************/
1447
1448 pascal OSErr GetObjectLocation(short vRefNum,
1449 long dirID,
1450 ConstStr255Param pathname,
1451 short *realVRefNum,
1452 long *realParID,
1453 Str255 realName,
1454 Boolean *isDirectory)
1455 {
1456 OSErr error;
1457 CInfoPBRec pb;
1458 Str255 tempPathname;
1459
1460 /* clear results */
1461 *realVRefNum = 0;
1462 *realParID = 0;
1463 realName[0] = 0;
1464
1465 /*
1466 ** Get the real vRefNum
1467 */
1468 error = DetermineVRefNum(pathname, vRefNum, realVRefNum);
1469 if ( error == noErr )
1470 {
1471 /*
1472 ** Determine if the object already exists and if so,
1473 ** get the real parent directory ID if it's a file
1474 */
1475
1476 /* Protection against File Sharing problem */
1477 if ( (pathname == NULL) || (pathname[0] == 0) )
1478 {
1479 tempPathname[0] = 0;
1480 pb.hFileInfo.ioNamePtr = tempPathname;
1481 pb.hFileInfo.ioFDirIndex = -1; /* use ioDirID */
1482 }
1483 else
1484 {
1485 pb.hFileInfo.ioNamePtr = (StringPtr)pathname;
1486 pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */
1487 }
1488 pb.hFileInfo.ioVRefNum = vRefNum;
1489 pb.hFileInfo.ioDirID = dirID;
1490 error = PBGetCatInfoSync(&pb);
1491 if ( error == noErr )
1492 {
1493 /*
1494 ** The file system object is present and we have the file's real parID
1495 */
1496
1497 /* Is it a directory or a file? */
1498 *isDirectory = (pb.hFileInfo.ioFlAttrib & ioDirMask) != 0;
1499 if ( *isDirectory )
1500 {
1501 /*
1502 ** It's a directory, get its name and parent dirID, and then we're done
1503 */
1504
1505 pb.dirInfo.ioNamePtr = realName;
1506 pb.dirInfo.ioVRefNum = *realVRefNum;
1507 /* pb.dirInfo.ioDrDirID already contains the dirID of the directory object */
1508 pb.dirInfo.ioFDirIndex = -1; /* get information about ioDirID */
1509 error = PBGetCatInfoSync(&pb);
1510
1511 /* get the parent ID here, because the file system can return the */
1512 /* wrong parent ID from the last call. */
1513 *realParID = pb.dirInfo.ioDrParID;
1514 }
1515 else
1516 {
1517 /*
1518 ** It's a file - use the parent directory ID from the last call
1519 ** to GetCatInfoparse, get the file name, and then we're done
1520 */
1521 *realParID = pb.hFileInfo.ioFlParID;
1522 error = GetFilenameFromPathname(pathname, realName);
1523 }
1524 }
1525 else if ( error == fnfErr )
1526 {
1527 /*
1528 ** The file system object is not present - see if its parent is present
1529 */
1530
1531 /*
1532 ** Parse to get the object name from end of pathname
1533 */
1534 error = GetFilenameFromPathname(pathname, realName);
1535
1536 /* if we can't get the object name from the end, we can't continue */
1537 if ( error == noErr )
1538 {
1539 /*
1540 ** What we want now is the pathname minus the object name
1541 ** for example:
1542 ** if pathname is 'vol:dir:file' tempPathname becomes 'vol:dir:'
1543 ** if pathname is 'vol:dir:file:' tempPathname becomes 'vol:dir:'
1544 ** if pathname is ':dir:file' tempPathname becomes ':dir:'
1545 ** if pathname is ':dir:file:' tempPathname becomes ':dir:'
1546 ** if pathname is ':file' tempPathname becomes ':'
1547 ** if pathname is 'file or file:' tempPathname becomes ''
1548 */
1549
1550 /* get a copy of the pathname */
1551 BlockMoveData(pathname, tempPathname, pathname[0] + 1);
1552
1553 /* remove the object name */
1554 tempPathname[0] -= realName[0];
1555 /* and the trailing colon (if any) */
1556 if ( pathname[pathname[0]] == ':' )
1557 {
1558 --tempPathname[0];
1559 }
1560
1561 /* OK, now get the parent's directory ID */
1562
1563 /* Protection against File Sharing problem */
1564 pb.hFileInfo.ioNamePtr = (StringPtr)tempPathname;
1565 if ( tempPathname[0] != 0 )
1566 {
1567 pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */
1568 }
1569 else
1570 {
1571 pb.hFileInfo.ioFDirIndex = -1; /* use ioDirID */
1572 }
1573 pb.hFileInfo.ioVRefNum = vRefNum;
1574 pb.hFileInfo.ioDirID = dirID;
1575 error = PBGetCatInfoSync(&pb);
1576 *realParID = pb.dirInfo.ioDrDirID;
1577
1578 *isDirectory = false; /* we don't know what the object is really going to be */
1579 }
1580
1581 if ( error != noErr )
1582 {
1583 error = dirNFErr; /* couldn't find parent directory */
1584 }
1585 else
1586 {
1587 error = fnfErr; /* we found the parent, but not the file */
1588 }
1589 }
1590 }
1591
1592 return ( error );
1593 }
1594
1595 /*****************************************************************************/
1596
1597 pascal OSErr GetDirItems(short vRefNum,
1598 long dirID,
1599 ConstStr255Param name,
1600 Boolean getFiles,
1601 Boolean getDirectories,
1602 FSSpecPtr items,
1603 short reqItemCount,
1604 short *actItemCount,
1605 short *itemIndex) /* start with 1, then use what's returned */
1606 {
1607 CInfoPBRec pb;
1608 OSErr error;
1609 long theDirID;
1610 Boolean isDirectory;
1611 FSSpec *endItemsArray;
1612
1613 if ( *itemIndex > 0 )
1614 {
1615 /* NOTE: If I could be sure that the caller passed a real vRefNum and real directory */
1616 /* to this routine, I could rip out calls to DetermineVRefNum and GetDirectoryID and this */
1617 /* routine would be much faster because of the overhead of DetermineVRefNum and */
1618 /* GetDirectoryID and because GetDirectoryID blows away the directory index hint the Macintosh */
1619 /* file system keeps for indexed calls. I can't be sure, so for maximum throughput, */
1620 /* pass a big array of FSSpecs so you can get the directory's contents with few calls */
1621 /* to this routine. */
1622
1623 /* get the real volume reference number */
1624 error = DetermineVRefNum(name, vRefNum, &pb.hFileInfo.ioVRefNum);
1625 if ( error == noErr )
1626 {
1627 /* and the real directory ID of this directory (and make sure it IS a directory) */
1628 error = GetDirectoryID(vRefNum, dirID, name, &theDirID, &isDirectory);
1629 if ( error == noErr )
1630 {
1631 if ( isDirectory )
1632 {
1633 *actItemCount = 0;
1634 endItemsArray = items + reqItemCount;
1635 while ( (items < endItemsArray) && (error == noErr) )
1636 {
1637 pb.hFileInfo.ioNamePtr = (StringPtr) &items->name;
1638 pb.hFileInfo.ioDirID = theDirID;
1639 pb.hFileInfo.ioFDirIndex = *itemIndex;
1640 error = PBGetCatInfoSync(&pb);
1641 if ( error == noErr )
1642 {
1643 items->parID = pb.hFileInfo.ioFlParID; /* return item's parID */
1644 items->vRefNum = pb.hFileInfo.ioVRefNum; /* return item's vRefNum */
1645 ++*itemIndex; /* prepare to get next item in directory */
1646
1647 if ( (pb.hFileInfo.ioFlAttrib & ioDirMask) != 0 )
1648 {
1649 if ( getDirectories )
1650 {
1651 ++*actItemCount; /* keep this item */
1652 ++items; /* point to next item */
1653 }
1654 }
1655 else
1656 {
1657 if ( getFiles )
1658 {
1659 ++*actItemCount; /* keep this item */
1660 ++items; /* point to next item */
1661 }
1662 }
1663 }
1664 }
1665 }
1666 else
1667 {
1668 /* it wasn't a directory */
1669 error = dirNFErr;
1670 }
1671 }
1672 }
1673 }
1674 else
1675 {
1676 /* bad itemIndex */
1677 error = paramErr;
1678 }
1679
1680 return ( error );
1681 }
1682
1683 /*****************************************************************************/
1684
1685 static void DeleteLevel(long dirToDelete,
1686 DeleteEnumGlobalsPtr theGlobals)
1687 {
1688 long savedDir;
1689
1690 do
1691 {
1692 /* prepare to delete directory */
1693 theGlobals->myPB.ciPB.dirInfo.ioNamePtr = (StringPtr)&theGlobals->itemName;
1694 theGlobals->myPB.ciPB.dirInfo.ioFDirIndex = 1; /* get first item */
1695 theGlobals->myPB.ciPB.dirInfo.ioDrDirID = dirToDelete; /* in this directory */
1696 theGlobals->error = PBGetCatInfoSync(&(theGlobals->myPB.ciPB));
1697 if ( theGlobals->error == noErr )
1698 {
1699 savedDir = dirToDelete;
1700 /* We have an item. Is it a file or directory? */
1701 if ( (theGlobals->myPB.ciPB.dirInfo.ioFlAttrib & ioDirMask) != 0 )
1702 {
1703 /* it's a directory */
1704 savedDir = theGlobals->myPB.ciPB.dirInfo.ioDrDirID; /* save dirID of directory instead */
1705 DeleteLevel(theGlobals->myPB.ciPB.dirInfo.ioDrDirID, theGlobals); /* Delete its contents */
1706 theGlobals->myPB.ciPB.dirInfo.ioNamePtr = NULL; /* prepare to delete directory */
1707 }
1708 if ( theGlobals->error == noErr )
1709 {
1710 theGlobals->myPB.ciPB.dirInfo.ioDrDirID = savedDir; /* restore dirID */
1711 theGlobals->myPB.hPB.fileParam.ioFVersNum = 0; /* just in case it's used on an MFS volume... */
1712 theGlobals->error = PBHDeleteSync(&(theGlobals->myPB.hPB)); /* delete this item */
1713 if ( theGlobals->error == fLckdErr )
1714 {
1715 (void) PBHRstFLockSync(&(theGlobals->myPB.hPB)); /* unlock it */
1716 theGlobals->error = PBHDeleteSync(&(theGlobals->myPB.hPB)); /* and try again */
1717 }
1718 }
1719 }
1720 } while ( theGlobals->error == noErr );
1721
1722 if ( theGlobals->error == fnfErr )
1723 {
1724 theGlobals->error = noErr;
1725 }
1726 }
1727
1728 /*****************************************************************************/
1729
1730 pascal OSErr DeleteDirectoryContents(short vRefNum,
1731 long dirID,
1732 ConstStr255Param name)
1733 {
1734 DeleteEnumGlobals theGlobals;
1735 Boolean isDirectory;
1736 OSErr error;
1737
1738 /* Get the real dirID and make sure it is a directory. */
1739 error = GetDirectoryID(vRefNum, dirID, name, &dirID, &isDirectory);
1740 if ( error == noErr )
1741 {
1742 if ( isDirectory )
1743 {
1744 /* Get the real vRefNum */
1745 error = DetermineVRefNum(name, vRefNum, &vRefNum);
1746 if ( error == noErr )
1747 {
1748 /* Set up the globals we need to access from the recursive routine. */
1749 theGlobals.myPB.ciPB.dirInfo.ioVRefNum = vRefNum;
1750
1751 /* Here we go into recursion land... */
1752 DeleteLevel(dirID, &theGlobals);
1753 error = theGlobals.error;
1754 }
1755 }
1756 else
1757 {
1758 error = dirNFErr;
1759 }
1760 }
1761
1762 return ( error );
1763 }
1764
1765 /*****************************************************************************/
1766
1767 pascal OSErr DeleteDirectory(short vRefNum,
1768 long dirID,
1769 ConstStr255Param name)
1770 {
1771 OSErr error;
1772
1773 /* Make sure a directory was specified and then delete its contents */
1774 error = DeleteDirectoryContents(vRefNum, dirID, name);
1775 if ( error == noErr )
1776 {
1777 error = HDelete(vRefNum, dirID, name);
1778 if ( error == fLckdErr )
1779 {
1780 (void) HRstFLock(vRefNum, dirID, name); /* unlock the directory locked by AppleShare */
1781 error = HDelete(vRefNum, dirID, name); /* and try again */
1782 }
1783 }
1784
1785 return ( error );
1786 }
1787
1788 /*****************************************************************************/
1789
1790 pascal OSErr CheckObjectLock(short vRefNum,
1791 long dirID,
1792 ConstStr255Param name)
1793 {
1794 CInfoPBRec pb;
1795 OSErr error;
1796
1797 error = GetCatInfoNoName(vRefNum, dirID, name, &pb);
1798 if ( error == noErr )
1799 {
1800 /* check locked bit */
1801 if ( (pb.hFileInfo.ioFlAttrib & 0x01) != 0 )
1802 {
1803 error = fLckdErr;
1804 }
1805 }
1806
1807 return ( error );
1808 }
1809
1810 /*****************************************************************************/
1811
1812 pascal OSErr FSpCheckObjectLock(const FSSpec *spec)
1813 {
1814 return ( CheckObjectLock(spec->vRefNum, spec->parID, spec->name) );
1815 }
1816
1817 /*****************************************************************************/
1818
1819 pascal OSErr GetFileSize(short vRefNum,
1820 long dirID,
1821 ConstStr255Param fileName,
1822 long *dataSize,
1823 long *rsrcSize)
1824 {
1825 HParamBlockRec pb;
1826 OSErr error;
1827
1828 pb.fileParam.ioNamePtr = (StringPtr)fileName;
1829 pb.fileParam.ioVRefNum = vRefNum;
1830 pb.fileParam.ioFVersNum = 0;
1831 pb.fileParam.ioDirID = dirID;
1832 pb.fileParam.ioFDirIndex = 0;
1833 error = PBHGetFInfoSync(&pb);
1834 if ( error == noErr )
1835 {
1836 *dataSize = pb.fileParam.ioFlLgLen;
1837 *rsrcSize = pb.fileParam.ioFlRLgLen;
1838 }
1839
1840 return ( error );
1841 }
1842
1843 /*****************************************************************************/
1844
1845 pascal OSErr FSpGetFileSize(const FSSpec *spec,
1846 long *dataSize,
1847 long *rsrcSize)
1848 {
1849 return ( GetFileSize(spec->vRefNum, spec->parID, spec->name, dataSize, rsrcSize) );
1850 }
1851
1852 /*****************************************************************************/
1853
1854 pascal OSErr BumpDate(short vRefNum,
1855 long dirID,
1856 ConstStr255Param name)
1857 /* Given a file or directory, change its modification date to the current date/time. */
1858 {
1859 CInfoPBRec pb;
1860 Str31 tempName;
1861 OSErr error;
1862 unsigned long secs;
1863
1864 /* Protection against File Sharing problem */
1865 if ( (name == NULL) || (name[0] == 0) )
1866 {
1867 tempName[0] = 0;
1868 pb.hFileInfo.ioNamePtr = tempName;
1869 pb.hFileInfo.ioFDirIndex = -1; /* use ioDirID */
1870 }
1871 else
1872 {
1873 pb.hFileInfo.ioNamePtr = (StringPtr)name;
1874 pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */
1875 }
1876 pb.hFileInfo.ioVRefNum = vRefNum;
1877 pb.hFileInfo.ioDirID = dirID;
1878 error = PBGetCatInfoSync(&pb);
1879 if ( error == noErr )
1880 {
1881 GetDateTime(&secs);
1882 /* set mod date to current date, or one second into the future
1883 if mod date = current date */
1884 pb.hFileInfo.ioFlMdDat = (secs == pb.hFileInfo.ioFlMdDat) ? (++secs) : (secs);
1885 if ( pb.dirInfo.ioNamePtr == tempName )
1886 {
1887 pb.hFileInfo.ioDirID = pb.hFileInfo.ioFlParID;
1888 }
1889 else
1890 {
1891 pb.hFileInfo.ioDirID = dirID;
1892 }
1893 error = PBSetCatInfoSync(&pb);
1894 }
1895
1896 return ( error );
1897 }
1898
1899 /*****************************************************************************/
1900
1901 pascal OSErr FSpBumpDate(const FSSpec *spec)
1902 {
1903 return ( BumpDate(spec->vRefNum, spec->parID, spec->name) );
1904 }
1905
1906 /*****************************************************************************/
1907
1908 pascal OSErr ChangeCreatorType(short vRefNum,
1909 long dirID,
1910 ConstStr255Param name,
1911 OSType creator,
1912 OSType fileType)
1913 {
1914 CInfoPBRec pb;
1915 OSErr error;
1916 short realVRefNum;
1917 long parID;
1918
1919 pb.hFileInfo.ioNamePtr = (StringPtr)name;
1920 pb.hFileInfo.ioVRefNum = vRefNum;
1921 pb.hFileInfo.ioDirID = dirID;
1922 pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */
1923 error = PBGetCatInfoSync(&pb);
1924 if ( error == noErr )
1925 {
1926 if ( (pb.hFileInfo.ioFlAttrib & ioDirMask) == 0 ) /* if file */
1927 {
1928 parID = pb.hFileInfo.ioFlParID; /* save parent dirID for BumpDate call */
1929
1930 /* If creator not 0x00000000, change creator */
1931 if ( creator != (OSType)0x00000000 )
1932 {
1933 pb.hFileInfo.ioFlFndrInfo.fdCreator = creator;
1934 }
1935
1936 /* If fileType not 0x00000000, change fileType */
1937 if ( fileType != (OSType)0x00000000 )
1938 {
1939 pb.hFileInfo.ioFlFndrInfo.fdType = fileType;
1940 }
1941
1942 pb.hFileInfo.ioDirID = dirID;
1943 error = PBSetCatInfoSync(&pb); /* now, save the new information back to disk */
1944
1945 if ( (error == noErr) && (parID != fsRtParID) ) /* can't bump fsRtParID */
1946 {
1947 /* get the real vRefNum in case a full pathname was passed */
1948 error = DetermineVRefNum(name, vRefNum, &realVRefNum);
1949 if ( error == noErr )
1950 {
1951 error = BumpDate(realVRefNum, parID, NULL);
1952 /* and bump the parent directory's mod date to wake up the Finder */
1953 /* to the change we just made */
1954 }
1955 }
1956 }
1957 else
1958 {
1959 /* it was a directory, not a file */
1960 error = notAFileErr;
1961 }
1962 }
1963
1964 return ( error );
1965 }
1966
1967 /*****************************************************************************/
1968
1969 pascal OSErr FSpChangeCreatorType(const FSSpec *spec,
1970 OSType creator,
1971 OSType fileType)
1972 {
1973 return ( ChangeCreatorType(spec->vRefNum, spec->parID, spec->name, creator, fileType) );
1974 }
1975
1976 /*****************************************************************************/
1977
1978 pascal OSErr ChangeFDFlags(short vRefNum,
1979 long dirID,
1980 ConstStr255Param name,
1981 Boolean setBits,
1982 unsigned short flagBits)
1983 {
1984 CInfoPBRec pb;
1985 Str31 tempName;
1986 OSErr error;
1987 short realVRefNum;
1988 long parID;
1989
1990 /* Protection against File Sharing problem */
1991 if ( (name == NULL) || (name[0] == 0) )
1992 {
1993 tempName[0] = 0;
1994 pb.hFileInfo.ioNamePtr = tempName;
1995 pb.hFileInfo.ioFDirIndex = -1; /* use ioDirID */
1996 }
1997 else
1998 {
1999 pb.hFileInfo.ioNamePtr = (StringPtr)name;
2000 pb.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */
2001 }
2002 pb.hFileInfo.ioVRefNum = vRefNum;
2003 pb.hFileInfo.ioDirID = dirID;
2004 error = PBGetCatInfoSync(&pb);
2005 if ( error == noErr )
2006 {
2007 parID = pb.hFileInfo.ioFlParID; /* save parent dirID for BumpDate call */
2008
2009 /* set or clear the appropriate bits in the Finder flags */
2010 if ( setBits )
2011 {
2012 /* OR in the bits */
2013 pb.hFileInfo.ioFlFndrInfo.fdFlags |= flagBits;
2014 }
2015 else
2016 {
2017 /* AND out the bits */
2018 pb.hFileInfo.ioFlFndrInfo.fdFlags &= ~flagBits;
2019 }
2020
2021 if ( pb.dirInfo.ioNamePtr == tempName )
2022 {
2023 pb.hFileInfo.ioDirID = pb.hFileInfo.ioFlParID;
2024 }
2025 else
2026 {
2027 pb.hFileInfo.ioDirID = dirID;
2028 }
2029
2030 error = PBSetCatInfoSync(&pb); /* now, save the new information back to disk */
2031
2032 if ( (error == noErr) && (parID != fsRtParID) ) /* can't bump fsRtParID */
2033 {
2034 /* get the real vRefNum in case a full pathname was passed */
2035 error = DetermineVRefNum(name, vRefNum, &realVRefNum);
2036 if ( error == noErr )
2037 {
2038 error = BumpDate(realVRefNum, parID, NULL);
2039 /* and bump the parent directory's mod date to wake up the Finder */
2040 /* to the change we just made */
2041 }
2042 }
2043 }
2044
2045 return ( error );
2046 }
2047
2048 /*****************************************************************************/
2049
2050 pascal OSErr FSpChangeFDFlags(const FSSpec *spec,
2051 Boolean setBits,
2052 unsigned short flagBits)
2053 {
2054 return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, setBits, flagBits) );
2055 }
2056
2057 /*****************************************************************************/
2058
2059 pascal OSErr SetIsInvisible(short vRefNum,
2060 long dirID,
2061 ConstStr255Param name)
2062 /* Given a file or directory, make it invisible. */
2063 {
2064 return ( ChangeFDFlags(vRefNum, dirID, name, true, kIsInvisible) );
2065 }
2066
2067 /*****************************************************************************/
2068
2069 pascal OSErr FSpSetIsInvisible(const FSSpec *spec)
2070 /* Given a file or directory, make it invisible. */
2071 {
2072 return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, true, kIsInvisible) );
2073 }
2074
2075 /*****************************************************************************/
2076
2077 pascal OSErr ClearIsInvisible(short vRefNum,
2078 long dirID,
2079 ConstStr255Param name)
2080 /* Given a file or directory, make it visible. */
2081 {
2082 return ( ChangeFDFlags(vRefNum, dirID, name, false, kIsInvisible) );
2083 }
2084
2085 /*****************************************************************************/
2086
2087 pascal OSErr FSpClearIsInvisible(const FSSpec *spec)
2088 /* Given a file or directory, make it visible. */
2089 {
2090 return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, false, kIsInvisible) );
2091 }
2092
2093 /*****************************************************************************/
2094
2095 pascal OSErr SetNameLocked(short vRefNum,
2096 long dirID,
2097 ConstStr255Param name)
2098 /* Given a file or directory, lock its name. */
2099 {
2100 return ( ChangeFDFlags(vRefNum, dirID, name, true, kNameLocked) );
2101 }
2102
2103 /*****************************************************************************/
2104
2105 pascal OSErr FSpSetNameLocked(const FSSpec *spec)
2106 /* Given a file or directory, lock its name. */
2107 {
2108 return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, true, kNameLocked) );
2109 }
2110
2111 /*****************************************************************************/
2112
2113 pascal OSErr ClearNameLocked(short vRefNum,
2114 long dirID,
2115 ConstStr255Param name)
2116 /* Given a file or directory, unlock its name. */
2117 {
2118 return ( ChangeFDFlags(vRefNum, dirID, name, false, kNameLocked) );
2119 }
2120
2121 /*****************************************************************************/
2122
2123 pascal OSErr FSpClearNameLocked(const FSSpec *spec)
2124 /* Given a file or directory, unlock its name. */
2125 {
2126 return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, false, kNameLocked) );
2127 }
2128
2129 /*****************************************************************************/
2130
2131 pascal OSErr SetIsStationery(short vRefNum,
2132 long dirID,
2133 ConstStr255Param name)
2134 /* Given a file, make it a stationery pad. */
2135 {
2136 return ( ChangeFDFlags(vRefNum, dirID, name, true, kIsStationery) );
2137 }
2138
2139 /*****************************************************************************/
2140
2141 pascal OSErr FSpSetIsStationery(const FSSpec *spec)
2142 /* Given a file, make it a stationery pad. */
2143 {
2144 return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, true, kIsStationery) );
2145 }
2146
2147 /*****************************************************************************/
2148
2149 pascal OSErr ClearIsStationery(short vRefNum,
2150 long dirID,
2151 ConstStr255Param name)
2152 /* Given a file, clear the stationery bit. */
2153 {
2154 return ( ChangeFDFlags(vRefNum, dirID, name, false, kIsStationery) );
2155 }
2156
2157 /*****************************************************************************/
2158
2159 pascal OSErr FSpClearIsStationery(const FSSpec *spec)
2160 /* Given a file, clear the stationery bit. */
2161 {
2162 return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, false, kIsStationery) );
2163 }
2164
2165 /*****************************************************************************/
2166
2167 pascal OSErr SetHasCustomIcon(short vRefNum,
2168 long dirID,
2169 ConstStr255Param name)
2170 /* Given a file or directory, indicate that it has a custom icon. */
2171 {
2172 return ( ChangeFDFlags(vRefNum, dirID, name, true, kHasCustomIcon) );
2173 }
2174
2175 /*****************************************************************************/
2176
2177 pascal OSErr FSpSetHasCustomIcon(const FSSpec *spec)
2178 /* Given a file or directory, indicate that it has a custom icon. */
2179 {
2180 return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, true, kHasCustomIcon) );
2181 }
2182
2183 /*****************************************************************************/
2184
2185 pascal OSErr ClearHasCustomIcon(short vRefNum,
2186 long dirID,
2187 ConstStr255Param name)
2188 /* Given a file or directory, indicate that it does not have a custom icon. */
2189 {
2190 return ( ChangeFDFlags(vRefNum, dirID, name, false, kHasCustomIcon) );
2191 }
2192
2193 /*****************************************************************************/
2194
2195 pascal OSErr FSpClearHasCustomIcon(const FSSpec *spec)
2196 /* Given a file or directory, indicate that it does not have a custom icon. */
2197 {
2198 return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, false, kHasCustomIcon) );
2199 }
2200
2201 /*****************************************************************************/
2202
2203 pascal OSErr ClearHasBeenInited(short vRefNum,
2204 long dirID,
2205 ConstStr255Param name)
2206 /* Given a file, clear its "has been inited" bit. */
2207 {
2208 return ( ChangeFDFlags(vRefNum, dirID, name, false, kHasBeenInited) );
2209 }
2210
2211 /*****************************************************************************/
2212
2213 pascal OSErr FSpClearHasBeenInited(const FSSpec *spec)
2214 /* Given a file, clear its "has been inited" bit. */
2215 {
2216 return ( ChangeFDFlags(spec->vRefNum, spec->parID, spec->name, false, kHasBeenInited) );
2217 }
2218
2219 /*****************************************************************************/
2220
2221 pascal OSErr CopyFileMgrAttributes(short srcVRefNum,
2222 long srcDirID,
2223 ConstStr255Param srcName,
2224 short dstVRefNum,
2225 long dstDirID,
2226 ConstStr255Param dstName,
2227 Boolean copyLockBit)
2228 {
2229 UniversalFMPB pb;
2230 Str31 tempName;
2231 OSErr error;
2232 Boolean objectIsDirectory;
2233
2234 pb.ciPB.hFileInfo.ioVRefNum = srcVRefNum;
2235 pb.ciPB.hFileInfo.ioDirID = srcDirID;
2236
2237 /* Protection against File Sharing problem */
2238 if ( (srcName == NULL) || (srcName[0] == 0) )
2239 {
2240 tempName[0] = 0;
2241 pb.ciPB.hFileInfo.ioNamePtr = tempName;
2242 pb.ciPB.hFileInfo.ioFDirIndex = -1; /* use ioDirID */
2243 }
2244 else
2245 {
2246 pb.ciPB.hFileInfo.ioNamePtr = (StringPtr)srcName;
2247 pb.ciPB.hFileInfo.ioFDirIndex = 0; /* use ioNamePtr and ioDirID */
2248 }
2249 error = PBGetCatInfoSync(&pb.ciPB);
2250 if ( error == noErr )
2251 {
2252 objectIsDirectory = ( (pb.ciPB.hFileInfo.ioFlAttrib & ioDirMask) != 0 );
2253 pb.ciPB.hFileInfo.ioVRefNum = dstVRefNum;
2254 pb.ciPB.hFileInfo.ioDirID = dstDirID;
2255 if ( (dstName != NULL) && (dstName[0] == 0) )
2256 {
2257 pb.ciPB.hFileInfo.ioNamePtr = NULL;
2258 }
2259 else
2260 {
2261 pb.ciPB.hFileInfo.ioNamePtr = (StringPtr)dstName;
2262 }
2263 /* don't copy the hasBeenInited bit */
2264 pb.ciPB.hFileInfo.ioFlFndrInfo.fdFlags = ( pb.ciPB.hFileInfo.ioFlFndrInfo.fdFlags & 0xfeff );
2265 error = PBSetCatInfoSync(&pb.ciPB);
2266 if ( (error == noErr) && (copyLockBit) && ((pb.ciPB.hFileInfo.ioFlAttrib & 0x01) != 0) )
2267 {
2268 pb.hPB.fileParam.ioFVersNum = 0;
2269 error = PBHSetFLockSync(&pb.hPB);
2270 if ( (error != noErr) && (objectIsDirectory) )
2271 {
2272 error = noErr; /* ignore lock errors if destination is directory */
2273 }
2274 }
2275 }
2276 return ( error );
2277 }
2278
2279 /*****************************************************************************/
2280
2281 pascal OSErr FSpCopyFileMgrAttributes(const FSSpec *srcSpec,
2282 const FSSpec *dstSpec,
2283 Boolean copyLockBit)
2284 {
2285 return ( CopyFileMgrAttributes(srcSpec->vRefNum, srcSpec->parID, srcSpec->name,
2286 dstSpec->vRefNum, dstSpec->parID, dstSpec->name,
2287 copyLockBit) );
2288 }
2289
2290 /*****************************************************************************/
2291
2292 pascal OSErr HOpenAware(short vRefNum,
2293 long dirID,
2294 ConstStr255Param fileName,
2295 short denyModes,
2296 short *refNum)
2297 {
2298 HParamBlockRec pb;
2299 OSErr error;
2300 GetVolParmsInfoBuffer volParmsInfo;
2301 long infoSize = sizeof(GetVolParmsInfoBuffer);
2302
2303 pb.ioParam.ioMisc = NULL;
2304 pb.fileParam.ioFVersNum = 0;
2305 pb.fileParam.ioNamePtr = (StringPtr)fileName;
2306 pb.fileParam.ioVRefNum = vRefNum;
2307 pb.fileParam.ioDirID = dirID;
2308
2309 /* get volume attributes */
2310 /* this preflighting is needed because Foreign File Access based file systems don't */
2311 /* return the correct error result to the OpenDeny call */
2312 error = HGetVolParms(fileName, vRefNum, &volParmsInfo, &infoSize);
2313 if ( (error == noErr) && hasOpenDeny(volParmsInfo) )
2314 {
2315 /* if volume supports OpenDeny, use it and return */
2316 pb.accessParam.ioDenyModes = denyModes;
2317 error = PBHOpenDenySync(&pb);
2318 *refNum = pb.ioParam.ioRefNum;
2319 }
2320 else if ( (error == noErr) || (error == paramErr) ) /* paramErr is OK, it just means this volume doesn't support GetVolParms */
2321 {
2322 /* OpenDeny isn't supported, so try File Manager Open functions */
2323
2324 /* If request includes write permission, then see if the volume is */
2325 /* locked by hardware or software. The HFS file system doesn't check */
2326 /* for this when a file is opened - you only find out later when you */
2327 /* try to write and the write fails with a wPrErr or a vLckdErr. */
2328
2329 if ( (denyModes & dmWr) != 0 )
2330 {
2331 error = CheckVolLock(fileName, vRefNum);
2332 }
2333 else
2334 {
2335 error = noErr;
2336 }
2337
2338 if ( error == noErr )
2339 {
2340 /* Set File Manager permissions to closest thing possible */
2341 if ( (denyModes == dmWr) || (denyModes == dmRdWr) )
2342 {
2343 pb.ioParam.ioPermssn = fsRdWrShPerm;
2344 }
2345 else
2346 {
2347 pb.ioParam.ioPermssn = denyModes % 4;
2348 }
2349
2350 error = PBHOpenDFSync(&pb); /* Try OpenDF */
2351 if ( error == paramErr )
2352 {
2353 error = PBHOpenSync(&pb); /* OpenDF not supported, so try Open */
2354 }
2355 *refNum = pb.ioParam.ioRefNum;
2356 }
2357 }
2358
2359 return ( error );
2360 }
2361
2362 /*****************************************************************************/
2363
2364 pascal OSErr FSpOpenAware(const FSSpec *spec,
2365 short denyModes,
2366 short *refNum)
2367 {
2368 return ( HOpenAware(spec->vRefNum, spec->parID, spec->name, denyModes, refNum) );
2369 }
2370
2371 /*****************************************************************************/
2372
2373 pascal OSErr HOpenRFAware(short vRefNum,
2374 long dirID,
2375 ConstStr255Param fileName,
2376 short denyModes,
2377 short *refNum)
2378 {
2379 HParamBlockRec pb;
2380 OSErr error;
2381 GetVolParmsInfoBuffer volParmsInfo;
2382 long infoSize = sizeof(GetVolParmsInfoBuffer);
2383
2384 pb.ioParam.ioMisc = NULL;
2385 pb.fileParam.ioFVersNum = 0;
2386 pb.fileParam.ioNamePtr = (StringPtr)fileName;
2387 pb.fileParam.ioVRefNum = vRefNum;
2388 pb.fileParam.ioDirID = dirID;
2389
2390 /* get volume attributes */
2391 /* this preflighting is needed because Foreign File Access based file systems don't */
2392 /* return the correct error result to the OpenRFDeny call */
2393 error = HGetVolParms(fileName, vRefNum, &volParmsInfo, &infoSize);
2394 if ( (error == noErr) && hasOpenDeny(volParmsInfo) )
2395 {
2396 /* if volume supports OpenRFDeny, use it and return */
2397 if ( hasOpenDeny(volParmsInfo) )
2398 {
2399 pb.accessParam.ioDenyModes = denyModes;
2400 error = PBHOpenRFDenySync(&pb);
2401 *refNum = pb.ioParam.ioRefNum;
2402 }
2403 }
2404 else if ( (error == noErr) || (error == paramErr) ) /* paramErr is OK, it just means this volume doesn't support GetVolParms */
2405 {
2406 /* OpenRFDeny isn't supported, so try File Manager OpenRF function */
2407
2408 /* If request includes write permission, then see if the volume is */
2409 /* locked by hardware or software. The HFS file system doesn't check */
2410 /* for this when a file is opened - you only find out later when you */
2411 /* try to write and the write fails with a wPrErr or a vLckdErr. */
2412
2413 if ( (denyModes & dmWr) != 0 )
2414 {
2415 error = CheckVolLock(fileName, vRefNum);
2416 }
2417 else
2418 {
2419 error = noErr;
2420 }
2421
2422 if ( error == noErr )
2423 {
2424 /* Set File Manager permissions to closest thing possible */
2425 if ( (denyModes == dmWr) || (denyModes == dmRdWr) )
2426 {
2427 pb.ioParam.ioPermssn = fsRdWrShPerm;
2428 }
2429 else
2430 {
2431 pb.ioParam.ioPermssn = denyModes % 4;
2432 }
2433
2434 error = PBHOpenRFSync(&pb);
2435 *refNum = pb.ioParam.ioRefNum;
2436 }
2437 }
2438
2439 return ( error );
2440 }
2441
2442 /*****************************************************************************/
2443
2444 pascal OSErr FSpOpenRFAware(const FSSpec *spec,
2445 short denyModes,
2446 short *refNum)
2447 {
2448 return ( HOpenRFAware(spec->vRefNum, spec->parID, spec->name, denyModes, refNum) );
2449 }
2450
2451 /*****************************************************************************/
2452
2453 pascal OSErr FSReadNoCache(short refNum,
2454 long *count,
2455 void *buffPtr)
2456 {
2457 ParamBlockRec pb;
2458 OSErr error;
2459
2460 pb.ioParam.ioRefNum = refNum;
2461 pb.ioParam.ioBuffer = (Ptr)buffPtr;
2462 pb.ioParam.ioReqCount = *count;
2463 pb.ioParam.ioPosMode = fsAtMark + 0x0020; /* fsAtMark + noCacheBit */
2464 pb.ioParam.ioPosOffset = 0;
2465 error = PBReadSync(&pb);
2466 *count = pb.ioParam.ioActCount; /* always return count */
2467 return ( error );
2468 }
2469
2470 /*****************************************************************************/
2471
2472 pascal OSErr FSWriteNoCache(short refNum,
2473 long *count,
2474 const void *buffPtr)
2475 {
2476 ParamBlockRec pb;
2477 OSErr error;
2478
2479 pb.ioParam.ioRefNum = refNum;
2480 pb.ioParam.ioBuffer = (Ptr)buffPtr;
2481 pb.ioParam.ioReqCount = *count;
2482 pb.ioParam.ioPosMode = fsAtMark + 0x0020; /* fsAtMark + noCacheBit */
2483 pb.ioParam.ioPosOffset = 0;
2484 error = PBWriteSync(&pb);
2485 *count = pb.ioParam.ioActCount; /* always return count */
2486 return ( error );
2487 }
2488
2489 /*****************************************************************************/
2490
2491 /*
2492 ** See if numBytes bytes of buffer1 are equal to buffer2.
2493 */
2494 static Boolean EqualMemory(const void *buffer1, const void *buffer2, unsigned long numBytes)
2495 {
2496 register unsigned char *b1 = (unsigned char *)buffer1;
2497 register unsigned char *b2 = (unsigned char *)buffer2;
2498
2499 if ( b1 != b2 ) /* if buffer pointers are same, then they are equal */
2500 {
2501 while ( numBytes > 0 )
2502 {
2503 /* compare the bytes and then increment the pointers */
2504 if ( (*b1++ - *b2++) != 0 )
2505 {
2506 return ( false );
2507 }
2508 --numBytes;
2509 }
2510 }
2511
2512 return ( true );
2513 }
2514
2515 /*****************************************************************************/
2516
2517 /*
2518 ** Read any number of bytes from an open file using read-verify mode.
2519 ** The FSReadVerify function reads any number of bytes from an open file
2520 ** and verifies them against the data in the buffer pointed to by buffPtr.
2521 **
2522 ** Because of a bug in the HFS file system, only non-block aligned parts of
2523 ** the read are verified against the buffer data and the rest is *copied*
2524 ** into the buffer. Thus, you shouldn't verify against your original data;
2525 ** instead, you should verify against a copy of the original data and then
2526 ** compare the read-verified copy against the original data after calling
2527 ** FSReadVerify. That's why this function isn't exported - it needs the
2528 ** wrapper provided by FSWriteVerify.
2529 */
2530 static OSErr FSReadVerify(short refNum,
2531 long *count,
2532 void *buffPtr)
2533 {
2534 ParamBlockRec pb;
2535 OSErr result;
2536
2537 pb.ioParam.ioRefNum = refNum;
2538 pb.ioParam.ioBuffer = (Ptr)buffPtr;
2539 pb.ioParam.ioReqCount = *count;
2540 pb.ioParam.ioPosMode = fsAtMark + rdVerify;
2541 pb.ioParam.ioPosOffset = 0;
2542 result = PBReadSync(&pb);
2543 *count = pb.ioParam.ioActCount; /* always return count */
2544 return ( result );
2545 }
2546
2547 /*****************************************************************************/
2548
2549 pascal OSErr FSWriteVerify(short refNum,
2550 long *count,
2551 const void *buffPtr)
2552 {
2553 Ptr verifyBuffer;
2554 long position;
2555 long bufferSize;
2556 long byteCount;
2557 long bytesVerified;
2558 Ptr startVerify;
2559 OSErr result;
2560
2561 /*
2562 ** Allocate the verify buffer
2563 ** Try to get get a large enough buffer to verify in one pass.
2564 ** If that fails, use GetTempBuffer to get a buffer.
2565 */
2566 bufferSize = *count;
2567 verifyBuffer = NewPtr(bufferSize);
2568 if ( verifyBuffer == NULL )
2569 {
2570 verifyBuffer = GetTempBuffer(bufferSize, &bufferSize);
2571 }
2572 if ( verifyBuffer != NULL )
2573 {
2574 /* Save the current position */
2575 result = GetFPos(refNum, &position);
2576 if ( result == noErr )
2577 {
2578 /* Write the data */
2579 result = FSWrite(refNum, count, buffPtr);
2580 if ( result == noErr )
2581 {
2582 /* Restore the original position */
2583 result = SetFPos(refNum, fsFromStart, position);
2584 if ( result == noErr )
2585 {
2586 /*
2587 ** *count = total number of bytes to verify
2588 ** bufferSize = the size of the verify buffer
2589 ** bytesVerified = number of bytes verified
2590 ** byteCount = number of bytes to verify this pass
2591 ** startVerify = position in buffPtr
2592 */
2593 bytesVerified = 0;
2594 startVerify = (Ptr)buffPtr;
2595 while ( (bytesVerified < *count) && ( result == noErr ) )
2596 {
2597 if ( (*count - bytesVerified) > bufferSize )
2598 {
2599 byteCount = bufferSize;
2600 }
2601 else
2602 {
2603 byteCount = *count - bytesVerified;
2604 }
2605 /*
2606 ** Copy the write buffer into the verify buffer.
2607 ** This step is needed because the File Manager
2608 ** compares the data in any non-block aligned
2609 ** data at the beginning and end of the read-verify
2610 ** request back into the file system's cache
2611 ** to the data in verify Buffer. However, the
2612 ** File Manager does not compare any full blocks
2613 ** and instead copies them into the verify buffer
2614 ** so we still have to compare the buffers again
2615 ** after the read-verify request completes.
2616 */
2617 BlockMoveData(startVerify, verifyBuffer, byteCount);
2618
2619 /* Read-verify the data back into the verify buffer */
2620 result = FSReadVerify(refNum, &byteCount, verifyBuffer);
2621 if ( result == noErr )
2622 {
2623 /* See if the buffers are the same */
2624 if ( !EqualMemory(verifyBuffer, startVerify, byteCount) )
2625 {
2626 result = ioErr;
2627 }
2628 startVerify += byteCount;
2629 bytesVerified += byteCount;
2630 }
2631 }
2632 }
2633 }
2634 }
2635 DisposePtr(verifyBuffer);
2636 }
2637 else
2638 {
2639 result = memFullErr;
2640 }
2641 return ( result );
2642 }
2643
2644 /*****************************************************************************/
2645
2646 pascal OSErr CopyFork(short srcRefNum,
2647 short dstRefNum,
2648 void *copyBufferPtr,
2649 long copyBufferSize)
2650 {
2651 ParamBlockRec srcPB;
2652 ParamBlockRec dstPB;
2653 OSErr srcError;
2654 OSErr dstError;
2655
2656 if ( (copyBufferPtr == NULL) || (copyBufferSize == 0) )
2657 return ( paramErr );
2658
2659 srcPB.ioParam.ioRefNum = srcRefNum;
2660 dstPB.ioParam.ioRefNum = dstRefNum;
2661
2662 /* preallocate the destination fork and */
2663 /* ensure the destination fork's EOF is correct after the copy */
2664 srcError = PBGetEOFSync(&srcPB);
2665 if ( srcError != noErr )
2666 return ( srcError );
2667 dstPB.ioParam.ioMisc = srcPB.ioParam.ioMisc;
2668 dstError = PBSetEOFSync(&dstPB);
2669 if ( dstError != noErr )
2670 return ( dstError );
2671
2672 /* reset source fork's mark */
2673 srcPB.ioParam.ioPosMode = fsFromStart;
2674 srcPB.ioParam.ioPosOffset = 0;
2675 srcError = PBSetFPosSync(&srcPB);
2676 if ( srcError != noErr )
2677 return ( srcError );
2678
2679 /* reset destination fork's mark */
2680 dstPB.ioParam.ioPosMode = fsFromStart;
2681 dstPB.ioParam.ioPosOffset = 0;
2682 dstError = PBSetFPosSync(&dstPB);
2683 if ( dstError != noErr )
2684 return ( dstError );
2685
2686 /* set up fields that won't change in the loop */
2687 srcPB.ioParam.ioBuffer = (Ptr)copyBufferPtr;
2688 srcPB.ioParam.ioPosMode = fsAtMark + 0x0020;/* fsAtMark + noCacheBit */
2689 /* If copyBufferSize is greater than 512 bytes, make it a multiple of 512 bytes */
2690 /* This will make writes on local volumes faster */
2691 if ( (copyBufferSize >= 512) && ((copyBufferSize & 0x1ff) != 0) )
2692 {
2693 srcPB.ioParam.ioReqCount = copyBufferSize & 0xfffffe00;
2694 }
2695 else
2696 {
2697 srcPB.ioParam.ioReqCount = copyBufferSize;
2698 }
2699 dstPB.ioParam.ioBuffer = (Ptr)copyBufferPtr;
2700 dstPB.ioParam.ioPosMode = fsAtMark + 0x0020;/* fsAtMark + noCacheBit */
2701
2702 while ( (srcError == noErr) && (dstError == noErr) )
2703 {
2704 srcError = PBReadSync(&srcPB);
2705 dstPB.ioParam.ioReqCount = srcPB.ioParam.ioActCount;
2706 dstError = PBWriteSync(&dstPB);
2707 }
2708
2709 /* make sure there were no errors at the destination */
2710 if ( dstError != noErr )
2711 return ( dstError );
2712
2713 /* make sure the only error at the source was eofErr */
2714 if ( srcError != eofErr )
2715 return ( srcError );
2716
2717 return ( noErr );
2718 }
2719
2720 /*****************************************************************************/
2721
2722 pascal OSErr GetFileLocation(short refNum,
2723 short *vRefNum,
2724 long *dirID,
2725 StringPtr fileName)
2726 {
2727 FCBPBRec pb;
2728 OSErr error;
2729
2730 pb.ioNamePtr = fileName;
2731 pb.ioVRefNum = 0;
2732 pb.ioRefNum = refNum;
2733 pb.ioFCBIndx = 0;
2734 error = PBGetFCBInfoSync(&pb);
2735 if ( error == noErr )
2736 {
2737 *vRefNum = pb.ioFCBVRefNum;
2738 *dirID = pb.ioFCBParID;
2739 }
2740 return ( error );
2741 }
2742
2743 /*****************************************************************************/
2744
2745 pascal OSErr FSpGetFileLocation(short refNum,
2746 FSSpec *spec)
2747 {
2748 return ( GetFileLocation(refNum, &(spec->vRefNum), &(spec->parID), spec->name) );
2749 }
2750
2751 /*****************************************************************************/
2752
2753 pascal OSErr CopyDirectoryAccess(short srcVRefNum,
2754 long srcDirID,
2755 ConstStr255Param srcName,
2756 short dstVRefNum,
2757 long dstDirID,
2758 ConstStr255Param dstName)
2759 {
2760 OSErr error;
2761 GetVolParmsInfoBuffer infoBuffer; /* Where PBGetVolParms dumps its info */
2762 long dstServerAdr; /* AppleTalk server address of destination (if any) */
2763 long ownerID, groupID, accessRights;
2764 long tempLong;
2765
2766 /* See if destination supports directory access control */
2767 tempLong = sizeof(infoBuffer);
2768 error = HGetVolParms(dstName, dstVRefNum, &infoBuffer, &tempLong);
2769 if ( (error == noErr) && hasAccessCntl(infoBuffer) )
2770 {
2771 if ( hasAccessCntl(infoBuffer) )
2772 {
2773 dstServerAdr = infoBuffer.vMServerAdr;
2774
2775 /* See if source supports directory access control and is on same server */
2776 tempLong = sizeof(infoBuffer);
2777 error = HGetVolParms(srcName, srcVRefNum, &infoBuffer, &tempLong);
2778 if ( error == noErr )
2779 {
2780 if ( hasAccessCntl(infoBuffer) && (dstServerAdr == infoBuffer.vMServerAdr) )
2781 {
2782 /* both volumes support directory access control and they are */
2783 /* on same server, so copy the access information */
2784 error = HGetDirAccess(srcVRefNum, srcDirID, srcName, &ownerID, &groupID, &accessRights);
2785 if ( error == noErr )
2786 {
2787 error = HSetDirAccess(dstVRefNum, dstDirID, dstName, ownerID, groupID, accessRights);
2788 }
2789 }
2790 else
2791 {
2792 /* destination doesn't support directory access control or */
2793 /* they volumes aren't on the same server */
2794 error = paramErr;
2795 }
2796 }
2797 }
2798 else
2799 {
2800 /* destination doesn't support directory access control */
2801 error = paramErr;
2802 }
2803 }
2804
2805 return ( error );
2806 }
2807
2808 /*****************************************************************************/
2809
2810 pascal OSErr FSpCopyDirectoryAccess(const FSSpec *srcSpec,
2811 const FSSpec *dstSpec)
2812 {
2813 return ( CopyDirectoryAccess(srcSpec->vRefNum, srcSpec->parID, srcSpec->name,
2814 dstSpec->vRefNum, dstSpec->parID, dstSpec->name) );
2815 }
2816
2817 /*****************************************************************************/
2818
2819 pascal OSErr HMoveRenameCompat(short vRefNum,
2820 long srcDirID,
2821 ConstStr255Param srcName,
2822 long dstDirID,
2823 ConstStr255Param dstpathName,
2824 ConstStr255Param copyName)
2825 {
2826 OSErr error;
2827 GetVolParmsInfoBuffer volParmsInfo;
2828 long infoSize;
2829 short realVRefNum;
2830 long realParID;
2831 Str31 realName;
2832 Boolean isDirectory;
2833 long tempItemsDirID;
2834 long uniqueTempDirID;
2835 Str31 uniqueTempDirName;
2836 unsigned short uniqueNameoverflow;
2837
2838 /* Get volume attributes */
2839 infoSize = sizeof(GetVolParmsInfoBuffer);
2840 error = HGetVolParms((StringPtr)srcName, vRefNum, &volParmsInfo, &infoSize);
2841 if ( (error == noErr) && hasMoveRename(volParmsInfo) )
2842 {
2843 /* If volume supports move and rename, so use it and return */
2844 error = HMoveRename(vRefNum, srcDirID, srcName, dstDirID, dstpathName, copyName);
2845 }
2846 else if ( (error == noErr) || (error == paramErr) ) /* paramErr is OK, it just means this volume doesn't support GetVolParms */
2847 {
2848 /* MoveRename isn't supported by this volume, so do it by hand */
2849
2850 /* If copyName isn't supplied, we can simply CatMove and return */
2851 if ( copyName == NULL )
2852 {
2853 error = CatMove(vRefNum, srcDirID, srcName, dstDirID, dstpathName);
2854 }
2855 else
2856 {
2857 /* Renaming is required, so we have some work to do... */
2858
2859 /* Get the object's real name, real parent ID and real vRefNum */
2860 error = GetObjectLocation(vRefNum, srcDirID, (StringPtr)srcName,
2861 &realVRefNum, &realParID, realName, &isDirectory);
2862 if ( error == noErr )
2863 {
2864 /* Find the Temporary Items Folder on that volume */
2865 error = FindFolder(realVRefNum, kTemporaryFolderType, kCreateFolder,
2866 &realVRefNum, &tempItemsDirID);
2867 if ( error == noErr )
2868 {
2869 /* Create a new uniquely named folder in the temporary items folder. */
2870 /* This is done to avoid the case where 'realName' or 'copyName' already */
2871 /* exists in the temporary items folder. */
2872
2873 /* Start with current tick count as uniqueTempDirName */
2874 NumToString(TickCount(), uniqueTempDirName);
2875 uniqueNameoverflow = 0;
2876 do
2877 {
2878 error = DirCreate(realVRefNum, tempItemsDirID, uniqueTempDirName, &uniqueTempDirID);
2879 if ( error == dupFNErr )
2880 {
2881 /* Duplicate name - change the first character to the next ASCII character */
2882 ++uniqueTempDirName[1];
2883 /* Make sure it isn't a colon! */
2884 if ( uniqueTempDirName[1] == ':' )
2885 {
2886 ++uniqueTempDirName[1];
2887 }
2888 /* Don't go too far... */
2889 ++uniqueNameoverflow;
2890 }
2891 } while ( (error == dupFNErr) && (uniqueNameoverflow <= 64) ); /* 64 new files per 1/60th second - not likely! */
2892 if ( error == noErr )
2893 {
2894 /* Move the object to the folder with uniqueTempDirID for renaming */
2895 error = CatMove(realVRefNum, realParID, realName, uniqueTempDirID, NULL);
2896 if ( error == noErr )
2897 {
2898 /* Rename the object */
2899 error = HRename(realVRefNum, uniqueTempDirID, realName, copyName);
2900 if ( error == noErr )
2901 {
2902 /* Move object to its new home */
2903 error = CatMove(realVRefNum, uniqueTempDirID, copyName, dstDirID, dstpathName);
2904 if ( error != noErr )
2905 {
2906 /* Error handling: rename object back to original name - ignore errors */
2907 (void) HRename(realVRefNum, uniqueTempDirID, copyName, realName);
2908 }
2909 }
2910 if ( error != noErr )
2911 {
2912 /* Error handling: move object back to original location - ignore errors */
2913 (void) CatMove(realVRefNum, uniqueTempDirID, realName, realParID, NULL);
2914 }
2915 }
2916 /* Done with ourTempDir, so delete it - ignore errors */
2917 (void) HDelete(realVRefNum, uniqueTempDirID, NULL);
2918 }
2919 }
2920 }
2921 }
2922 }
2923
2924 return ( error );
2925 }
2926
2927 /*****************************************************************************/
2928
2929 pascal OSErr FSpMoveRenameCompat(const FSSpec *srcSpec,
2930 const FSSpec *dstSpec,
2931 ConstStr255Param copyName)
2932 {
2933 /* make sure the FSSpecs refer to the same volume */
2934 if (srcSpec->vRefNum != dstSpec->vRefNum)
2935 return (diffVolErr);
2936 return ( HMoveRenameCompat(srcSpec->vRefNum, srcSpec->parID, srcSpec->name,
2937 dstSpec->parID, dstSpec->name, copyName) );
2938 }
2939
2940 /*****************************************************************************/
2941
2942 pascal OSErr BuildAFPVolMountInfo(short flags,
2943 char nbpInterval,
2944 char nbpCount,
2945 short uamType,
2946 Str32 zoneName,
2947 Str32 serverName,
2948 Str27 volName,
2949 Str31 userName,
2950 Str8 userPassword,
2951 Str8 volPassword,
2952 AFPVolMountInfoPtr *afpInfoPtr)
2953 {
2954 MyAFPVolMountInfoPtr infoPtr;
2955 OSErr error;
2956
2957 /* Allocate the AFPXVolMountInfo record */
2958 infoPtr = (MyAFPVolMountInfoPtr)NewPtrClear(sizeof(MyAFPVolMountInfo));
2959 if ( infoPtr != NULL )
2960 {
2961 /* Fill in an AFPVolMountInfo record that can be passed to VolumeMount */
2962 infoPtr->length = sizeof(MyAFPVolMountInfo);
2963 infoPtr->media = AppleShareMediaType;
2964 infoPtr->flags = flags;
2965 infoPtr->nbpInterval = nbpInterval;
2966 infoPtr->nbpCount = nbpCount;
2967 infoPtr->uamType = uamType;
2968
2969 infoPtr->zoneNameOffset = offsetof(MyAFPVolMountInfo, zoneName);
2970 infoPtr->serverNameOffset = offsetof(MyAFPVolMountInfo, serverName);
2971 infoPtr->volNameOffset = offsetof(MyAFPVolMountInfo, volName);
2972 infoPtr->userNameOffset = offsetof(MyAFPVolMountInfo, userName);
2973 infoPtr->userPasswordOffset = offsetof(MyAFPVolMountInfo, userPassword);
2974 infoPtr->volPasswordOffset = offsetof(MyAFPVolMountInfo, volPassword);
2975
2976 BlockMoveData(zoneName, infoPtr->zoneName, sizeof(Str32));
2977 BlockMoveData(serverName, infoPtr->serverName, sizeof(Str32));
2978 BlockMoveData(volName, infoPtr->volName, sizeof(Str27));
2979 BlockMoveData(userName, infoPtr->userName, sizeof(Str31));
2980 BlockMoveData(userPassword, infoPtr->userPassword, sizeof(Str8));
2981 BlockMoveData(volPassword, infoPtr->volPassword, sizeof(Str8));
2982
2983 *afpInfoPtr = (AFPVolMountInfoPtr)infoPtr;
2984 error = noErr;
2985 }
2986 else
2987 {
2988 error = memFullErr;
2989 }
2990
2991 return ( error );
2992 }
2993
2994 /*****************************************************************************/
2995
2996 pascal OSErr RetrieveAFPVolMountInfo(AFPVolMountInfoPtr afpInfoPtr,
2997 short *flags,
2998 short *uamType,
2999 StringPtr zoneName,
3000 StringPtr serverName,
3001 StringPtr volName,
3002 StringPtr userName)
3003 {
3004 StringPtr tempPtr;
3005 OSErr error;
3006
3007 /* Retrieve the AFP mounting information from an AFPVolMountInfo record. */
3008 if ( afpInfoPtr->media == AppleShareMediaType )
3009 {
3010 *flags = afpInfoPtr->flags;
3011 *uamType = afpInfoPtr->uamType;
3012
3013 if ( afpInfoPtr->zoneNameOffset != 0)
3014 {
3015 tempPtr = (StringPtr)((long)afpInfoPtr + afpInfoPtr->zoneNameOffset);
3016 BlockMoveData(tempPtr, zoneName, tempPtr[0] + 1);
3017 }
3018
3019 if ( afpInfoPtr->serverNameOffset != 0)
3020 {
3021 tempPtr = (StringPtr)((long)afpInfoPtr + afpInfoPtr->serverNameOffset);
3022 BlockMoveData(tempPtr, serverName, tempPtr[0] + 1);
3023 }
3024
3025 if ( afpInfoPtr->volNameOffset != 0)
3026 {
3027 tempPtr = (StringPtr)((long)afpInfoPtr + afpInfoPtr->volNameOffset);
3028 BlockMoveData(tempPtr, volName, tempPtr[0] + 1);
3029 }
3030
3031 if ( afpInfoPtr->userNameOffset != 0)
3032 {
3033 tempPtr = (StringPtr)((long)afpInfoPtr + afpInfoPtr->userNameOffset);
3034 BlockMoveData(tempPtr, userName, tempPtr[0] + 1);
3035 }
3036
3037 error = noErr;
3038 }
3039 else
3040 {
3041 error = paramErr;
3042 }
3043
3044 return ( error );
3045 }
3046
3047 /*****************************************************************************/
3048
3049 pascal OSErr BuildAFPXVolMountInfo(short flags,
3050 char nbpInterval,
3051 char nbpCount,
3052 short uamType,
3053 Str32 zoneName,
3054 Str32 serverName,
3055 Str27 volName,
3056 Str31 userName,
3057 Str8 userPassword,
3058 Str8 volPassword,
3059 Str32 uamName,
3060 unsigned long alternateAddressLength,
3061 void *alternateAddress,
3062 AFPXVolMountInfoPtr *afpXInfoPtr)
3063 {
3064 Size infoSize;
3065 MyAFPXVolMountInfoPtr infoPtr;
3066 OSErr error;
3067
3068 /* Calculate the size of the AFPXVolMountInfo record */
3069 infoSize = sizeof(MyAFPXVolMountInfo) + alternateAddressLength - 1;
3070
3071 /* Allocate the AFPXVolMountInfo record */
3072 infoPtr = (MyAFPXVolMountInfoPtr)NewPtrClear(infoSize);
3073 if ( infoPtr != NULL )
3074 {
3075 /* Fill in an AFPXVolMountInfo record that can be passed to VolumeMount */
3076 infoPtr->length = infoSize;
3077 infoPtr->media = AppleShareMediaType;
3078 infoPtr->flags = flags;
3079 if ( alternateAddressLength != 0 )
3080 {
3081 /* make sure the volMountExtendedFlagsBit is set if there's extended address info */
3082 infoPtr->flags |= volMountExtendedFlagsMask;
3083 /* and set the only extendedFlags bit we know about */
3084 infoPtr->extendedFlags = kAFPExtendedFlagsAlternateAddressMask;
3085 }
3086 else
3087 {
3088 /* make sure the volMountExtendedFlagsBit is clear if there's no extended address info */
3089 infoPtr->flags &= ~volMountExtendedFlagsMask;
3090 /* and clear the extendedFlags */
3091 infoPtr->extendedFlags = 0;
3092 }
3093 infoPtr->nbpInterval = nbpInterval;
3094 infoPtr->nbpCount = nbpCount;
3095 infoPtr->uamType = uamType;
3096
3097 infoPtr->zoneNameOffset = offsetof(MyAFPXVolMountInfo, zoneName);
3098 infoPtr->serverNameOffset = offsetof(MyAFPXVolMountInfo, serverName);
3099 infoPtr->volNameOffset = offsetof(MyAFPXVolMountInfo, volName);
3100 infoPtr->userNameOffset = offsetof(MyAFPXVolMountInfo, userName);
3101 infoPtr->userPasswordOffset = offsetof(MyAFPXVolMountInfo, userPassword);
3102 infoPtr->volPasswordOffset = offsetof(MyAFPXVolMountInfo, volPassword);
3103 infoPtr->uamNameOffset = offsetof(MyAFPXVolMountInfo, uamName);
3104 infoPtr->alternateAddressOffset = offsetof(MyAFPXVolMountInfo, alternateAddress);
3105
3106 BlockMoveData(zoneName, infoPtr->zoneName, sizeof(Str32));
3107 BlockMoveData(serverName, infoPtr->serverName, sizeof(Str32));
3108 BlockMoveData(volName, infoPtr->volName, sizeof(Str27));
3109 BlockMoveData(userName, infoPtr->userName, sizeof(Str31));
3110 BlockMoveData(userPassword, infoPtr->userPassword, sizeof(Str8));
3111 BlockMoveData(volPassword, infoPtr->volPassword, sizeof(Str8));
3112 BlockMoveData(uamName, infoPtr->uamName, sizeof(Str32));
3113 BlockMoveData(alternateAddress, infoPtr->alternateAddress, alternateAddressLength);
3114
3115 *afpXInfoPtr = (AFPXVolMountInfoPtr)infoPtr;
3116 error = noErr;
3117 }
3118 else
3119 {
3120 error = memFullErr;
3121 }
3122
3123 return ( error );
3124 }
3125
3126 /*****************************************************************************/
3127
3128 pascal OSErr RetrieveAFPXVolMountInfo(AFPXVolMountInfoPtr afpXInfoPtr,
3129 short *flags,
3130 short *uamType,
3131 StringPtr zoneName,
3132 StringPtr serverName,
3133 StringPtr volName,
3134 StringPtr userName,
3135 StringPtr uamName,
3136 unsigned long *alternateAddressLength,
3137 AFPAlternateAddress **alternateAddress)
3138 {
3139 StringPtr tempPtr;
3140 Ptr alternateAddressStart;
3141 Ptr alternateAddressEnd;
3142 Size alternateAddressDataSize;
3143 OSErr error;
3144 UInt8 addressCount;
3145
3146 /* Retrieve the AFP mounting information from an AFPVolMountInfo record. */
3147 if ( afpXInfoPtr->media == AppleShareMediaType )
3148 {
3149 /* default to noErr */
3150 error = noErr;
3151
3152 /* Is this an extended record? */
3153 if ( (afpXInfoPtr->flags & volMountExtendedFlagsMask) != 0 )
3154 {
3155 if ( ((afpXInfoPtr->extendedFlags & kAFPExtendedFlagsAlternateAddressMask) != 0) &&
3156 (afpXInfoPtr->alternateAddressOffset != 0) )
3157 {
3158
3159 alternateAddressStart = (Ptr)((long)afpXInfoPtr + afpXInfoPtr->alternateAddressOffset);
3160 alternateAddressEnd = alternateAddressStart + 1; /* skip over alternate address version byte */
3161 addressCount = *(UInt8*)alternateAddressEnd; /* get the address count */
3162 ++alternateAddressEnd; /* skip over alternate address count byte */
3163 /* alternateAddressEnd now equals &AFPAlternateAddress.fAddressList[0] */
3164 while ( addressCount != 0 )
3165 {
3166 /* parse the address list to find the end */
3167 alternateAddressEnd += *(UInt8*)alternateAddressEnd; /* add length of each AFPTagData record */
3168 --addressCount;
3169 }
3170 /* get the size of the alternateAddressData */
3171 alternateAddressDataSize = alternateAddressEnd - alternateAddressStart;
3172 /* allocate memory for it */
3173 *alternateAddress = (AFPAlternateAddress *)NewPtr(alternateAddressDataSize);
3174 if ( *alternateAddress != NULL )
3175 {
3176 /* and return the data */
3177 BlockMoveData(alternateAddressStart, *alternateAddress, alternateAddressDataSize);
3178 *alternateAddressLength = alternateAddressDataSize;
3179 }
3180 else
3181 {
3182 /* no memory - fail now */
3183 error = memFullErr;
3184 }
3185 }
3186
3187 if ( error == noErr ) /* fill in more output parameters if everything is OK */
3188 {
3189 if ( afpXInfoPtr->uamNameOffset != 0 )
3190 {
3191 tempPtr = (StringPtr)((long)afpXInfoPtr + afpXInfoPtr->uamNameOffset);
3192 BlockMoveData(tempPtr, uamName, tempPtr[0] + 1);
3193 }
3194 }
3195 }
3196
3197 if ( error == noErr ) /* fill in more output parameters if everything is OK */
3198 {
3199 *flags = afpXInfoPtr->flags;
3200 *uamType = afpXInfoPtr->uamType;
3201
3202 if ( afpXInfoPtr->zoneNameOffset != 0 )
3203 {
3204 tempPtr = (StringPtr)((long)afpXInfoPtr + afpXInfoPtr->zoneNameOffset);
3205 BlockMoveData(tempPtr, zoneName, tempPtr[0] + 1);
3206 }
3207
3208 if ( afpXInfoPtr->serverNameOffset != 0 )
3209 {
3210 tempPtr = (StringPtr)((long)afpXInfoPtr + afpXInfoPtr->serverNameOffset);
3211 BlockMoveData(tempPtr, serverName, tempPtr[0] + 1);
3212 }
3213
3214 if ( afpXInfoPtr->volNameOffset != 0 )
3215 {
3216 tempPtr = (StringPtr)((long)afpXInfoPtr + afpXInfoPtr->volNameOffset);
3217 BlockMoveData(tempPtr, volName, tempPtr[0] + 1);
3218 }
3219
3220 if ( afpXInfoPtr->userNameOffset != 0 )
3221 {
3222 tempPtr = (StringPtr)((long)afpXInfoPtr + afpXInfoPtr->userNameOffset);
3223 BlockMoveData(tempPtr, userName, tempPtr[0] + 1);
3224 }
3225 }
3226 }
3227 else
3228 {
3229 error = paramErr;
3230 }
3231
3232 return ( error );
3233 }
3234
3235 /*****************************************************************************/
3236
3237 pascal OSErr GetUGEntries(short objType,
3238 UGEntryPtr entries,
3239 long reqEntryCount,
3240 long *actEntryCount,
3241 long *objID)
3242 {
3243 HParamBlockRec pb;
3244 OSErr error = noErr;
3245 UGEntry *endEntryArray;
3246
3247 pb.objParam.ioObjType = objType;
3248 *actEntryCount = 0;
3249 for ( endEntryArray = entries + reqEntryCount; (entries < endEntryArray) && (error == noErr); ++entries )
3250 {
3251 pb.objParam.ioObjNamePtr = (StringPtr)entries->name;
3252 pb.objParam.ioObjID = *objID;
3253 /* Files.h in the universal interfaces, PBGetUGEntrySync takes a CMovePBPtr */
3254 /* as the parameter. Inside Macintosh and the original glue used HParmBlkPtr. */
3255 /* A CMovePBPtr works OK, but this will be changed in the future back to */
3256 /* HParmBlkPtr, so I'm just casting it here. */
3257 error = PBGetUGEntrySync(&pb);
3258 if ( error == noErr )
3259 {
3260 entries->objID = *objID = pb.objParam.ioObjID;
3261 entries->objType = objType;
3262 ++*actEntryCount;
3263 }
3264 }
3265
3266 return ( error );
3267 }
3268
3269 /*****************************************************************************/
3270