2 ** Apple Macintosh Developer Technical Support
4 ** A collection of useful high-level File Manager routines.
6 ** by Jim Luther, Apple Developer Technical Support Emeritus
8 ** File: MoreFilesExtras.c
10 ** Copyright © 1992-1998 Apple Computer, Inc.
11 ** All rights reserved.
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.
33 #include <TextUtils.h>
39 #define __COMPILINGMOREFILES
46 /*****************************************************************************/
48 /* local data structures */
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. */
54 #if PRAGMA_STRUCT_ALIGN
55 #pragma options align=mac68k
56 #elif PRAGMA_STRUCT_PACKPUSH
58 #elif PRAGMA_STRUCT_PACK
61 struct DeleteEnumGlobals
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 */
67 #if PRAGMA_STRUCT_ALIGN
68 #pragma options align=reset
69 #elif PRAGMA_STRUCT_PACKPUSH
71 #elif PRAGMA_STRUCT_PACK
75 typedef struct DeleteEnumGlobals DeleteEnumGlobals
;
76 typedef DeleteEnumGlobals
*DeleteEnumGlobalsPtr
;
78 /*****************************************************************************/
80 pascal void TruncPString(StringPtr destination
,
81 ConstStr255Param source
,
86 if ( source
!= NULL
&& destination
!= NULL
) /* don't do anything stupid */
88 if ( source
[0] > maxLength
)
90 /* Make sure the string isn't truncated in the middle of */
91 /* a multi-byte character. */
92 while (maxLength
!= 0)
95 charType
= CharacterByteType((Ptr
)&source
[1], maxLength
,smAllScripts
);
97 charType
= CharByte((Ptr
)&source
[1], maxLength
);
99 if ( (charType
== smSingleByte
) || (charType
== smLastByte
) )
100 break; /* source[maxLength] is now a valid last character */
106 maxLength
= source
[0];
108 /* Set the destination string length */
109 destination
[0] = maxLength
;
110 /* and copy maxLength characters (if needed) */
111 if ( source
!= destination
)
113 while ( maxLength
!= 0 )
115 destination
[maxLength
] = source
[maxLength
];
122 /*****************************************************************************/
124 pascal Ptr
GetTempBuffer(long buffReqSize
,
129 kSlopMemory
= 0x00008000 /* 32K - Amount of free memory to leave when allocating buffers */
133 /* Make request a multiple of 1024 bytes */
134 buffReqSize
= buffReqSize
& 0xfffffc00;
136 if ( buffReqSize
< 0x00000400 )
138 /* Request was smaller than 1024 bytes - make it 1024 */
139 buffReqSize
= 0x00000400;
142 /* Attempt to allocate the memory */
143 tempPtr
= NewPtr(buffReqSize
);
145 /* If request failed, go to backup plan */
146 if ( (tempPtr
== NULL
) && (buffReqSize
> 0x00000400) )
149 ** Try to get largest 1024-byte block available
150 ** leaving some slop for the toolbox if possible
152 long freeMemory
= (FreeMem() - kSlopMemory
) & 0xfffffc00;
154 buffReqSize
= MaxBlock() & 0xfffffc00;
156 if ( buffReqSize
> freeMemory
)
158 buffReqSize
= freeMemory
;
161 if ( buffReqSize
== 0 )
163 buffReqSize
= 0x00000400;
166 tempPtr
= NewPtr(buffReqSize
);
169 /* Return bytes allocated */
170 if ( tempPtr
!= NULL
)
172 *buffActSize
= buffReqSize
;
182 /*****************************************************************************/
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).
192 ** I noticed using this code in several places, so here it is once.
193 ** This reduces the code size of MoreFiles.
195 pascal OSErr
GetVolumeInfoNoName(ConstStr255Param pathname
,
202 /* Make sure pb parameter is not NULL */
205 pb
->volumeParam
.ioVRefNum
= vRefNum
;
206 if ( pathname
== NULL
)
208 pb
->volumeParam
.ioNamePtr
= NULL
;
209 pb
->volumeParam
.ioVolIndex
= 0; /* use ioVRefNum only */
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 */
217 error
= PBHGetVInfoSync(pb
);
218 pb
->volumeParam
.ioNamePtr
= NULL
; /* ioNamePtr may point to local tempPathname, so don't return it */
227 /*****************************************************************************/
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).
240 pascal OSErr
XGetVolumeInfoNoName(ConstStr255Param pathname
,
248 /* Make sure pb parameter is not NULL */
251 pb
->ioVRefNum
= vRefNum
;
252 pb
->ioXVersion
= 0; /* this XVolumeParam version (0) */
253 if ( pathname
== NULL
)
255 pb
->ioNamePtr
= NULL
;
256 pb
->ioVolIndex
= 0; /* use ioVRefNum only */
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 */
264 #if !__MACOSSEVENFIVEONEORLATER
265 /* Is PBXGetVolInfo available? */
266 if ( ( Gestalt(gestaltFSAttr
, &response
) != noErr
) || ((response
& (1L << gestaltFSSupports2TBVols
)) == 0) )
268 /* No, fall back on PBHGetVInfo */
269 error
= PBHGetVInfoSync((HParmBlkPtr
)pb
);
270 if ( error
== noErr
)
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 */
280 #endif // !__MACOSSEVENFIVEONEORLATER
283 error
= PBXGetVolInfoSync(pb
);
285 pb
->ioNamePtr
= NULL
; /* ioNamePtr may point to local tempPathname, so don't return it */
296 /*****************************************************************************/
298 pascal OSErr
GetCatInfoNoName(short vRefNum
,
300 ConstStr255Param name
,
306 /* Protection against File Sharing problem */
307 if ( (name
== NULL
) || (name
[0] == 0) )
310 pb
->dirInfo
.ioNamePtr
= tempName
;
311 pb
->dirInfo
.ioFDirIndex
= -1; /* use ioDirID */
315 pb
->dirInfo
.ioNamePtr
= (StringPtr
)name
;
316 pb
->dirInfo
.ioFDirIndex
= 0; /* use ioNamePtr and ioDirID */
318 pb
->dirInfo
.ioVRefNum
= vRefNum
;
319 pb
->dirInfo
.ioDrDirID
= dirID
;
320 error
= PBGetCatInfoSync(pb
);
321 pb
->dirInfo
.ioNamePtr
= NULL
;
325 /*****************************************************************************/
327 pascal OSErr
DetermineVRefNum(ConstStr255Param pathname
,
334 error
= GetVolumeInfoNoName(pathname
,vRefNum
, &pb
);
335 if ( error
== noErr
)
337 *realVRefNum
= pb
.volumeParam
.ioVRefNum
;
342 /*****************************************************************************/
345 pascal OSErr
HGetVInfo(short volReference
,
348 unsigned long *freeBytes
,
349 unsigned long *totalBytes
)
352 unsigned long allocationBlockSize
;
353 unsigned short numAllocationBlocks
;
354 unsigned short numFreeBlocks
;
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
);
365 if ( result
== noErr
)
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
;
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. */
381 /* Find the volume's VCB */
383 theVCB
= (VCB
*)(GetVCBQHdr()->qHead
);
384 while ( (theVCB
!= NULL
) && !vcbFound
)
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 )
390 if ( theVCB
->vcbVRefNum
== *vRefNum
)
398 theVCB
= (VCB
*)(theVCB
->qLink
);
402 if ( theVCB
!= NULL
)
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
;
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
;
417 /* Now, calculate freeBytes and totalBytes using unsigned values */
418 *freeBytes
= numFreeBlocks
* allocationBlockSize
;
419 *totalBytes
= numAllocationBlocks
* allocationBlockSize
;
425 /*****************************************************************************/
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).
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
439 #if __WANTPASCALELIMINATION
447 #if UNIVERSAL_INTERFACES_VERSION < 0x0301
449 pascal OSErr
PBXGetVolInfoSync(XVolumeParamPtr paramBlock
)
453 kXGetVolInfoSelector
= 0x0012, /* Selector for XGetVolInfo */
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
)))
463 return ( CallOSTrapUniversalProc(NGetTrapAddress(_FSDispatch
, OSTrap
),
464 uppFSDispatchProcInfo
,
466 kXGetVolInfoSelector
,
475 #if __WANTPASCALELIMINATION
481 /*****************************************************************************/
483 pascal OSErr
XGetVInfo(short volReference
,
486 UnsignedWide
*freeBytes
,
487 UnsignedWide
*totalBytes
)
493 /* See if large volume support is available */
494 if ( ( Gestalt(gestaltFSAttr
, &response
) == noErr
) && ((response
& (1L << gestaltFSSupports2TBVols
)) != 0) )
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
)
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
;
508 /* return the freeBytes and totalBytes */
509 *totalBytes
= UInt64ToUnsignedWide(pb
.ioVTotalBytes
);
510 *freeBytes
= UInt64ToUnsignedWide(pb
.ioVFreeBytes
);
515 /* No large volume support */
517 /* Use HGetVInfo to get the results */
518 result
= HGetVInfo(volReference
, volName
, vRefNum
, &freeBytes
->lo
, &totalBytes
->lo
);
519 if ( result
== noErr
)
521 /* zero the high longs of totalBytes and freeBytes */
529 /*****************************************************************************/
531 pascal OSErr
CheckVolLock(ConstStr255Param pathname
,
537 error
= GetVolumeInfoNoName(pathname
,vRefNum
, &pb
);
538 if ( error
== noErr
)
540 if ( (pb
.volumeParam
.ioVAtrb
& 0x0080) != 0 )
542 error
= wPrErr
; /* volume locked by hardware */
544 else if ( (pb
.volumeParam
.ioVAtrb
& 0x8000) != 0 )
546 error
= vLckdErr
; /* volume locked by software */
553 /*****************************************************************************/
557 pascal OSErr
GetDriverName(short driverRefNum
,
562 DRVRHeaderPtr dHeaderPtr
;
564 theDctl
= GetDCtlEntry(driverRefNum
);
565 if ( theDctl
!= NULL
)
567 if ( (**theDctl
).dCtlFlags
& 0x40 )
569 /* dctlDriver is handle - dereference */
570 dHeaderPtr
= *((DRVRHeaderHandle
)(**theDctl
).dCtlDriver
);
574 /* dctlDriver is pointer */
575 dHeaderPtr
= (DRVRHeaderPtr
)(**theDctl
).dCtlDriver
;
577 BlockMoveData((*dHeaderPtr
).drvrName
, driverName
, (*dHeaderPtr
).drvrName
[0] + 1);
583 result
= badUnitErr
; /* bad reference number */
590 /*****************************************************************************/
593 pascal OSErr
FindDrive(ConstStr255Param pathname
,
595 DrvQElPtr
*driveQElementPtr
)
601 *driveQElementPtr
= NULL
;
603 /* First, use GetVolumeInfoNoName to determine the volume */
604 result
= GetVolumeInfoNoName(pathname
, vRefNum
, &hPB
);
605 if ( result
== noErr
)
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.
614 if ( hPB
.volumeParam
.ioVDrvInfo
!= 0 )
616 /* The volume is online and not ejected */
617 /* Get the drive number */
618 driveNumber
= hPB
.volumeParam
.ioVDrvInfo
;
622 /* The volume's is either offline or ejected */
623 /* in either case, the volume is NOT online */
625 /* Is it ejected or just offline? */
626 if ( hPB
.volumeParam
.ioVDRefNum
> 0 )
628 /* It's ejected, the drive number is ioVDRefNum */
629 driveNumber
= hPB
.volumeParam
.ioVDRefNum
;
633 /* It's offline, the drive number is the negative of ioVDRefNum */
634 driveNumber
= (short)-hPB
.volumeParam
.ioVDRefNum
;
638 /* Get pointer to first element in drive queue */
639 *driveQElementPtr
= (DrvQElPtr
)(GetDrvQHdr()->qHead
);
641 /* Search for a matching drive number */
642 while ( (*driveQElementPtr
!= NULL
) && ((*driveQElementPtr
)->dQDrive
!= driveNumber
) )
644 *driveQElementPtr
= (DrvQElPtr
)(*driveQElementPtr
)->qLink
;
647 if ( *driveQElementPtr
== NULL
)
649 /* This should never happen since every volume must have a drive, but... */
658 /*****************************************************************************/
661 pascal OSErr
GetDiskBlocks(ConstStr255Param pathname
,
663 unsigned long *numBlocks
)
665 /* Various constants for GetDiskBlocks() */
668 /* return format list status code */
671 /* reference number of .SONY driver */
672 kSonyRefNum
= 0xfffb,
674 /* values returned by DriveStatus in DrvSts.twoSideFmt */
677 kSingleSidedSize
= 800, /* 400K */
678 kDoubleSidedSize
= 1600, /* 800K */
680 /* values in DrvQEl.qType */
684 /* more than enough formatListRecords */
685 kMaxFormatListRecs
= 16
688 DrvQElPtr driveQElementPtr
;
689 unsigned long blocks
;
691 FormatListRec formatListRecords
[kMaxFormatListRecs
];
693 short formatListRecIndex
;
698 /* Find the drive queue element for this volume */
699 result
= FindDrive(pathname
, vRefNum
, &driveQElementPtr
);
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.
706 if ( (result
== noErr
) && (driveQElementPtr
->dQRefNum
>= 0) )
712 /* Attempt to get the drive's format list. */
713 /* (see the Technical Note "What Your Sony Drives For You") */
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];
721 result
= PBStatusSync(&pb
);
723 if ( result
== noErr
)
725 /* The drive supports ReturnFormatList status call. */
727 /* Get the current disk's size. */
728 for( formatListRecIndex
= 0;
729 formatListRecIndex
< pb
.cntrlParam
.csParam
[0];
730 ++formatListRecIndex
)
732 if ( (formatListRecords
[formatListRecIndex
].formatFlags
&
733 diCIFmtFlagsCurrentMask
) != 0 )
735 blocks
= formatListRecords
[formatListRecIndex
].volSize
;
740 /* This should never happen */
744 else if ( driveQElementPtr
->dQRefNum
== (short)kSonyRefNum
)
746 /* The drive is a non-SuperDrive floppy which only supports 400K and 800K disks */
748 result
= DriveStatus(driveQElementPtr
->dQDrive
, &status
);
749 if ( result
== noErr
)
751 switch ( status
.twoSideFmt
)
754 blocks
= kSingleSidedSize
;
757 blocks
= kDoubleSidedSize
;
760 /* This should never happen */
768 /* The drive is not a floppy and it doesn't support ReturnFormatList */
769 /* so use the dQDrvSz field(s) */
771 result
= noErr
; /* reset result */
772 switch ( driveQElementPtr
->qType
)
775 blocks
= driveQElementPtr
->dQDrvSz
;
778 blocks
= ((unsigned long)driveQElementPtr
->dQDrvSz2
<< 16) +
779 driveQElementPtr
->dQDrvSz
;
782 /* This should never happen */
789 if ( result
== noErr
)
798 /*****************************************************************************/
800 pascal OSErr
GetVolFileSystemID(ConstStr255Param pathname
,
807 error
= GetVolumeInfoNoName(pathname
,vRefNum
, &pb
);
808 if ( error
== noErr
)
810 *fileSystemID
= pb
.volumeParam
.ioVFSID
;
816 /*****************************************************************************/
819 pascal OSErr
GetVolState(ConstStr255Param pathname
,
821 Boolean
*volumeOnline
,
822 Boolean
*volumeEjected
,
823 Boolean
*driveEjectable
,
824 Boolean
*driverWantsEject
)
830 error
= GetVolumeInfoNoName(pathname
,vRefNum
, &pb
);
831 if ( error
== noErr
)
833 if ( pb
.volumeParam
.ioVDrvInfo
!= 0 )
835 /* the volume is online and not ejected */
836 *volumeOnline
= true;
837 *volumeEjected
= false;
839 /* Get the drive number */
840 driveNumber
= pb
.volumeParam
.ioVDrvInfo
;
844 /* the volume's is either offline or ejected */
845 /* in either case, the volume is NOT online */
846 *volumeOnline
= false;
849 *volumeEjected
= pb
.volumeParam
.ioVDRefNum
> 0;
851 if ( *volumeEjected
)
853 /* If ejected, the drive number is ioVDRefNum */
854 driveNumber
= pb
.volumeParam
.ioVDRefNum
;
858 /* If offline, the drive number is the negative of ioVDRefNum */
859 driveNumber
= (short)-pb
.volumeParam
.ioVDRefNum
;
866 /* Find the drive queue element by searching the drive queue */
867 drvQElem
= (DrvQElPtr
)(GetDrvQHdr()->qHead
);
868 while ( (drvQElem
!= NULL
) && (drvQElem
->dQDrive
!= driveNumber
) )
870 drvQElem
= (DrvQElPtr
)drvQElem
->qLink
;
873 if ( drvQElem
!= NULL
)
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.
884 /* point to byte 1 of the flag bytes */
885 flagBytePtr
= (Ptr
)drvQElem
;
889 ** The drive is ejectable if flag byte 1 does not contain
890 ** 0x08 (nonejectable) or 0x48 (nonejectable, but wants eject call).
893 *driveEjectable
= (*flagBytePtr
!= 0x08) && (*flagBytePtr
!= 0x48);
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.
902 *driverWantsEject
= (*flagBytePtr
!= 0x08);
907 /* Didn't find the drive (this should never happen) */
908 *driveEjectable
= false;
909 *driverWantsEject
= false;
917 /*****************************************************************************/
920 pascal OSErr
UnmountAndEject(ConstStr255Param pathname
,
925 Boolean ejected
, wantsEject
;
929 error
= GetVolumeInfoNoName(pathname
, vRefNum
, &pb
);
930 if ( error
== noErr
)
932 if ( pb
.volumeParam
.ioVDrvInfo
!= 0 )
934 /* the volume is online and not ejected */
937 /* Get the drive number */
938 driveNum
= pb
.volumeParam
.ioVDrvInfo
;
942 /* the volume is ejected or offline */
945 ejected
= pb
.volumeParam
.ioVDRefNum
> 0;
949 /* If ejected, the drive number is ioVDRefNum */
950 driveNum
= pb
.volumeParam
.ioVDRefNum
;
954 /* If offline, the drive number is the negative of ioVDRefNum */
955 driveNum
= (short)-pb
.volumeParam
.ioVDRefNum
;
959 /* find the drive queue element */
960 drvQElem
= (DrvQElPtr
)(GetDrvQHdr()->qHead
);
961 while ( (drvQElem
!= NULL
) && (drvQElem
->dQDrive
!= driveNum
) )
963 drvQElem
= (DrvQElPtr
)drvQElem
->qLink
;
966 if ( drvQElem
!= NULL
)
968 /* does the drive want an eject call */
969 wantsEject
= (*((Ptr
)((Ptr
)drvQElem
- 3)) != 8);
973 /* didn't find the drive!! */
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
)
983 if ( wantsEject
&& !ejected
)
985 /* eject the media from the drive if needed */
986 pb
.volumeParam
.ioVRefNum
= driveNum
;
987 error
= PBEject((ParmBlkPtr
)&pb
);
996 /*****************************************************************************/
998 pascal OSErr
OnLine(FSSpecPtr volumes
,
1004 OSErr error
= noErr
;
1005 FSSpec
*endVolArray
;
1007 if ( *volIndex
> 0 )
1010 for ( endVolArray
= volumes
+ reqVolCount
; (volumes
< endVolArray
) && (error
== noErr
); ++volumes
)
1012 pb
.volumeParam
.ioNamePtr
= (StringPtr
) & volumes
->name
;
1013 pb
.volumeParam
.ioVolIndex
= *volIndex
;
1014 error
= PBHGetVInfoSync(&pb
);
1015 if ( error
== noErr
)
1017 volumes
->parID
= fsRtParID
; /* the root directory's parent is 1 */
1018 volumes
->vRefNum
= pb
.volumeParam
.ioVRefNum
;
1032 /*****************************************************************************/
1034 pascal OSErr
SetDefault(short newVRefNum
,
1041 /* Get the current default volume/directory. */
1042 error
= HGetVol(NULL
, oldVRefNum
, oldDirID
);
1043 if ( error
== noErr
)
1045 /* Set the new default volume/directory */
1046 error
= HSetVol(NULL
, newVRefNum
, newDirID
);
1052 /*****************************************************************************/
1056 pascal OSErr
RestoreDefault(short oldVRefNum
,
1060 short defaultVRefNum
;
1064 /* Determine if the default volume was a wdRefNum. */
1065 error
= GetWDInfo(oldVRefNum
, &defaultVRefNum
, &defaultDirID
, &defaultProcID
);
1066 if ( error
== noErr
)
1068 /* Restore the old default volume/directory, one way or the other. */
1069 if ( defaultDirID
!= fsRtDirID
)
1071 /* oldVRefNum was a wdRefNum - use SetVol */
1072 error
= SetVol(NULL
, oldVRefNum
);
1076 /* oldVRefNum was a real vRefNum - use HSetVol */
1077 error
= HSetVol(NULL
, oldVRefNum
, oldDirID
);
1084 /*****************************************************************************/
1086 pascal OSErr
GetDInfo(short vRefNum
,
1088 ConstStr255Param name
,
1094 error
= GetCatInfoNoName(vRefNum
, dirID
, name
, &pb
);
1095 if ( error
== noErr
)
1097 if ( (pb
.dirInfo
.ioFlAttrib
& ioDirMask
) != 0 )
1099 /* it's a directory, return the DInfo */
1100 *fndrInfo
= pb
.dirInfo
.ioDrUsrWds
;
1104 /* oops, a file was passed */
1112 /*****************************************************************************/
1114 pascal OSErr
FSpGetDInfo(const FSSpec
*spec
,
1117 return ( GetDInfo(spec
->vRefNum
, spec
->parID
, spec
->name
, fndrInfo
) );
1120 /*****************************************************************************/
1122 pascal OSErr
SetDInfo(short vRefNum
,
1124 ConstStr255Param name
,
1125 const DInfo
*fndrInfo
)
1131 /* Protection against File Sharing problem */
1132 if ( (name
== NULL
) || (name
[0] == 0) )
1135 pb
.dirInfo
.ioNamePtr
= tempName
;
1136 pb
.dirInfo
.ioFDirIndex
= -1; /* use ioDirID */
1140 pb
.dirInfo
.ioNamePtr
= (StringPtr
)name
;
1141 pb
.dirInfo
.ioFDirIndex
= 0; /* use ioNamePtr and ioDirID */
1143 pb
.dirInfo
.ioVRefNum
= vRefNum
;
1144 pb
.dirInfo
.ioDrDirID
= dirID
;
1145 error
= PBGetCatInfoSync(&pb
);
1146 if ( error
== noErr
)
1148 if ( (pb
.dirInfo
.ioFlAttrib
& ioDirMask
) != 0 )
1150 /* it's a directory, set the DInfo */
1151 if ( pb
.dirInfo
.ioNamePtr
== tempName
)
1153 pb
.dirInfo
.ioDrDirID
= pb
.dirInfo
.ioDrParID
;
1157 pb
.dirInfo
.ioDrDirID
= dirID
;
1159 pb
.dirInfo
.ioDrUsrWds
= *fndrInfo
;
1160 error
= PBSetCatInfoSync(&pb
);
1164 /* oops, a file was passed */
1172 /*****************************************************************************/
1174 pascal OSErr
FSpSetDInfo(const FSSpec
*spec
,
1175 const DInfo
*fndrInfo
)
1177 return ( SetDInfo(spec
->vRefNum
, spec
->parID
, spec
->name
, fndrInfo
) );
1180 /*****************************************************************************/
1182 pascal OSErr
GetDirectoryID(short vRefNum
,
1184 ConstStr255Param name
,
1186 Boolean
*isDirectory
)
1191 error
= GetCatInfoNoName(vRefNum
, dirID
, name
, &pb
);
1192 if ( error
== noErr
)
1194 *isDirectory
= (pb
.hFileInfo
.ioFlAttrib
& ioDirMask
) != 0;
1197 *theDirID
= pb
.dirInfo
.ioDrDirID
;
1201 *theDirID
= pb
.hFileInfo
.ioFlParID
;
1208 /*****************************************************************************/
1210 pascal OSErr
FSpGetDirectoryID(const FSSpec
*spec
,
1212 Boolean
*isDirectory
)
1214 return ( GetDirectoryID(spec
->vRefNum
, spec
->parID
, spec
->name
,
1215 theDirID
, isDirectory
) );
1218 /*****************************************************************************/
1220 pascal OSErr
GetDirName(short vRefNum
,
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
);
1243 /*****************************************************************************/
1245 pascal OSErr
GetIOACUser(short vRefNum
,
1247 ConstStr255Param name
,
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.
1258 pb
.dirInfo
.ioACUser
= 0; /* ioACUser used to be filler2 */
1259 error
= GetCatInfoNoName(vRefNum
, dirID
, name
, &pb
);
1260 if ( error
== noErr
)
1262 if ( (pb
.hFileInfo
.ioFlAttrib
& ioDirMask
) == 0 )
1264 /* oops, a file was passed */
1269 *ioACUser
= pb
.dirInfo
.ioACUser
;
1276 /*****************************************************************************/
1278 pascal OSErr
FSpGetIOACUser(const FSSpec
*spec
,
1281 return ( GetIOACUser(spec
->vRefNum
, spec
->parID
, spec
->name
, ioACUser
) );
1284 /*****************************************************************************/
1286 pascal OSErr
GetParentID(short vRefNum
,
1288 ConstStr255Param name
,
1296 /* Protection against File Sharing problem */
1297 if ( (name
== NULL
) || (name
[0] == 0) )
1300 pb
.hFileInfo
.ioNamePtr
= tempName
;
1301 pb
.hFileInfo
.ioFDirIndex
= -1; /* use ioDirID */
1305 pb
.hFileInfo
.ioNamePtr
= (StringPtr
)name
;
1306 pb
.hFileInfo
.ioFDirIndex
= 0; /* use ioNamePtr and ioDirID */
1308 pb
.hFileInfo
.ioVRefNum
= vRefNum
;
1309 pb
.hFileInfo
.ioDirID
= dirID
;
1310 error
= PBGetCatInfoSync(&pb
);
1311 if ( error
== noErr
)
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.
1325 if ( (pb
.hFileInfo
.ioFlAttrib
& ioDirMask
) != 0 )
1327 /* Its a directory */
1329 /* is there a pathname? */
1330 if ( pb
.hFileInfo
.ioNamePtr
== name
)
1332 /* could it contain multiple separators? */
1335 /* does it contain multiple separators at the end? */
1336 if ( (name
[name
[0]] == ':') && (name
[name
[0] - 1] == ':') )
1338 /* OK, then do the extra stuff to get the correct parID */
1340 /* Get the real vRefNum (this should not fail) */
1341 error
= DetermineVRefNum(name
, vRefNum
, &realVRefNum
);
1342 if ( error
== noErr
)
1344 /* we don't need the parent's name, but add protect against File Sharing problem */
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 */
1359 if ( error
== noErr
)
1361 /* if no errors, then pb.hFileInfo.ioFlParID (pb.dirInfo.ioDrParID) */
1362 /* contains the parent ID */
1363 *parID
= pb
.hFileInfo
.ioFlParID
;
1370 /*****************************************************************************/
1372 pascal OSErr
GetFilenameFromPathname(ConstStr255Param pathname
,
1379 /* default to no filename */
1382 /* check for no pathname */
1383 if ( pathname
!= NULL
)
1385 /* get string length */
1386 index
= pathname
[0];
1388 /* check for empty string */
1391 /* skip over last trailing colon (if any) */
1392 if ( pathname
[index
] == ':' )
1397 /* save the end of the string */
1400 /* if pathname ends with multiple colons, then this pathname refers */
1401 /* to a directory, not a file */
1402 if ( pathname
[index
] != ':' )
1404 /* parse backwards until we find a colon or hit the beginning of the pathname */
1405 while ( (index
!= 0) && (pathname
[index
] != ':') )
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]] != ':') )
1414 /* get the filename and return noErr */
1415 filename
[0] = (char)(nameEnd
- index
);
1416 BlockMoveData(&pathname
[index
+1], &filename
[1], nameEnd
- index
);
1421 /* pathname to a volume, not a file */
1422 error
= notAFileErr
;
1427 /* directory, not a file */
1428 error
= notAFileErr
;
1433 /* empty string isn't a file */
1434 error
= notAFileErr
;
1439 /* NULL pathname isn't a file */
1440 error
= notAFileErr
;
1446 /*****************************************************************************/
1448 pascal OSErr
GetObjectLocation(short vRefNum
,
1450 ConstStr255Param pathname
,
1454 Boolean
*isDirectory
)
1458 Str255 tempPathname
;
1466 ** Get the real vRefNum
1468 error
= DetermineVRefNum(pathname
, vRefNum
, realVRefNum
);
1469 if ( error
== noErr
)
1472 ** Determine if the object already exists and if so,
1473 ** get the real parent directory ID if it's a file
1476 /* Protection against File Sharing problem */
1477 if ( (pathname
== NULL
) || (pathname
[0] == 0) )
1479 tempPathname
[0] = 0;
1480 pb
.hFileInfo
.ioNamePtr
= tempPathname
;
1481 pb
.hFileInfo
.ioFDirIndex
= -1; /* use ioDirID */
1485 pb
.hFileInfo
.ioNamePtr
= (StringPtr
)pathname
;
1486 pb
.hFileInfo
.ioFDirIndex
= 0; /* use ioNamePtr and ioDirID */
1488 pb
.hFileInfo
.ioVRefNum
= vRefNum
;
1489 pb
.hFileInfo
.ioDirID
= dirID
;
1490 error
= PBGetCatInfoSync(&pb
);
1491 if ( error
== noErr
)
1494 ** The file system object is present and we have the file's real parID
1497 /* Is it a directory or a file? */
1498 *isDirectory
= (pb
.hFileInfo
.ioFlAttrib
& ioDirMask
) != 0;
1502 ** It's a directory, get its name and parent dirID, and then we're done
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
);
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
;
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
1521 *realParID
= pb
.hFileInfo
.ioFlParID
;
1522 error
= GetFilenameFromPathname(pathname
, realName
);
1525 else if ( error
== fnfErr
)
1528 ** The file system object is not present - see if its parent is present
1532 ** Parse to get the object name from end of pathname
1534 error
= GetFilenameFromPathname(pathname
, realName
);
1536 /* if we can't get the object name from the end, we can't continue */
1537 if ( error
== noErr
)
1540 ** What we want now is the pathname minus the object name
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 ''
1550 /* get a copy of the pathname */
1551 BlockMoveData(pathname
, tempPathname
, pathname
[0] + 1);
1553 /* remove the object name */
1554 tempPathname
[0] -= realName
[0];
1555 /* and the trailing colon (if any) */
1556 if ( pathname
[pathname
[0]] == ':' )
1561 /* OK, now get the parent's directory ID */
1563 /* Protection against File Sharing problem */
1564 pb
.hFileInfo
.ioNamePtr
= (StringPtr
)tempPathname
;
1565 if ( tempPathname
[0] != 0 )
1567 pb
.hFileInfo
.ioFDirIndex
= 0; /* use ioNamePtr and ioDirID */
1571 pb
.hFileInfo
.ioFDirIndex
= -1; /* use ioDirID */
1573 pb
.hFileInfo
.ioVRefNum
= vRefNum
;
1574 pb
.hFileInfo
.ioDirID
= dirID
;
1575 error
= PBGetCatInfoSync(&pb
);
1576 *realParID
= pb
.dirInfo
.ioDrDirID
;
1578 *isDirectory
= false; /* we don't know what the object is really going to be */
1581 if ( error
!= noErr
)
1583 error
= dirNFErr
; /* couldn't find parent directory */
1587 error
= fnfErr
; /* we found the parent, but not the file */
1595 /*****************************************************************************/
1597 pascal OSErr
GetDirItems(short vRefNum
,
1599 ConstStr255Param name
,
1601 Boolean getDirectories
,
1604 short *actItemCount
,
1605 short *itemIndex
) /* start with 1, then use what's returned */
1610 Boolean isDirectory
;
1611 FSSpec
*endItemsArray
;
1613 if ( *itemIndex
> 0 )
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. */
1623 /* get the real volume reference number */
1624 error
= DetermineVRefNum(name
, vRefNum
, &pb
.hFileInfo
.ioVRefNum
);
1625 if ( error
== noErr
)
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
)
1634 endItemsArray
= items
+ reqItemCount
;
1635 while ( (items
< endItemsArray
) && (error
== noErr
) )
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
)
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 */
1647 if ( (pb
.hFileInfo
.ioFlAttrib
& ioDirMask
) != 0 )
1649 if ( getDirectories
)
1651 ++*actItemCount
; /* keep this item */
1652 ++items
; /* point to next item */
1659 ++*actItemCount
; /* keep this item */
1660 ++items
; /* point to next item */
1668 /* it wasn't a directory */
1683 /*****************************************************************************/
1685 static void DeleteLevel(long dirToDelete
,
1686 DeleteEnumGlobalsPtr theGlobals
)
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
)
1699 savedDir
= dirToDelete
;
1700 /* We have an item. Is it a file or directory? */
1701 if ( (theGlobals
->myPB
.ciPB
.dirInfo
.ioFlAttrib
& ioDirMask
) != 0 )
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 */
1708 if ( theGlobals
->error
== noErr
)
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
)
1715 (void) PBHRstFLockSync(&(theGlobals
->myPB
.hPB
)); /* unlock it */
1716 theGlobals
->error
= PBHDeleteSync(&(theGlobals
->myPB
.hPB
)); /* and try again */
1720 } while ( theGlobals
->error
== noErr
);
1722 if ( theGlobals
->error
== fnfErr
)
1724 theGlobals
->error
= noErr
;
1728 /*****************************************************************************/
1730 pascal OSErr
DeleteDirectoryContents(short vRefNum
,
1732 ConstStr255Param name
)
1734 DeleteEnumGlobals theGlobals
;
1735 Boolean isDirectory
;
1738 /* Get the real dirID and make sure it is a directory. */
1739 error
= GetDirectoryID(vRefNum
, dirID
, name
, &dirID
, &isDirectory
);
1740 if ( error
== noErr
)
1744 /* Get the real vRefNum */
1745 error
= DetermineVRefNum(name
, vRefNum
, &vRefNum
);
1746 if ( error
== noErr
)
1748 /* Set up the globals we need to access from the recursive routine. */
1749 theGlobals
.myPB
.ciPB
.dirInfo
.ioVRefNum
= vRefNum
;
1751 /* Here we go into recursion land... */
1752 DeleteLevel(dirID
, &theGlobals
);
1753 error
= theGlobals
.error
;
1765 /*****************************************************************************/
1767 pascal OSErr
DeleteDirectory(short vRefNum
,
1769 ConstStr255Param name
)
1773 /* Make sure a directory was specified and then delete its contents */
1774 error
= DeleteDirectoryContents(vRefNum
, dirID
, name
);
1775 if ( error
== noErr
)
1777 error
= HDelete(vRefNum
, dirID
, name
);
1778 if ( error
== fLckdErr
)
1780 (void) HRstFLock(vRefNum
, dirID
, name
); /* unlock the directory locked by AppleShare */
1781 error
= HDelete(vRefNum
, dirID
, name
); /* and try again */
1788 /*****************************************************************************/
1790 pascal OSErr
CheckObjectLock(short vRefNum
,
1792 ConstStr255Param name
)
1797 error
= GetCatInfoNoName(vRefNum
, dirID
, name
, &pb
);
1798 if ( error
== noErr
)
1800 /* check locked bit */
1801 if ( (pb
.hFileInfo
.ioFlAttrib
& 0x01) != 0 )
1810 /*****************************************************************************/
1812 pascal OSErr
FSpCheckObjectLock(const FSSpec
*spec
)
1814 return ( CheckObjectLock(spec
->vRefNum
, spec
->parID
, spec
->name
) );
1817 /*****************************************************************************/
1819 pascal OSErr
GetFileSize(short vRefNum
,
1821 ConstStr255Param fileName
,
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
)
1836 *dataSize
= pb
.fileParam
.ioFlLgLen
;
1837 *rsrcSize
= pb
.fileParam
.ioFlRLgLen
;
1843 /*****************************************************************************/
1845 pascal OSErr
FSpGetFileSize(const FSSpec
*spec
,
1849 return ( GetFileSize(spec
->vRefNum
, spec
->parID
, spec
->name
, dataSize
, rsrcSize
) );
1852 /*****************************************************************************/
1854 pascal OSErr
BumpDate(short vRefNum
,
1856 ConstStr255Param name
)
1857 /* Given a file or directory, change its modification date to the current date/time. */
1864 /* Protection against File Sharing problem */
1865 if ( (name
== NULL
) || (name
[0] == 0) )
1868 pb
.hFileInfo
.ioNamePtr
= tempName
;
1869 pb
.hFileInfo
.ioFDirIndex
= -1; /* use ioDirID */
1873 pb
.hFileInfo
.ioNamePtr
= (StringPtr
)name
;
1874 pb
.hFileInfo
.ioFDirIndex
= 0; /* use ioNamePtr and ioDirID */
1876 pb
.hFileInfo
.ioVRefNum
= vRefNum
;
1877 pb
.hFileInfo
.ioDirID
= dirID
;
1878 error
= PBGetCatInfoSync(&pb
);
1879 if ( error
== noErr
)
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
)
1887 pb
.hFileInfo
.ioDirID
= pb
.hFileInfo
.ioFlParID
;
1891 pb
.hFileInfo
.ioDirID
= dirID
;
1893 error
= PBSetCatInfoSync(&pb
);
1899 /*****************************************************************************/
1901 pascal OSErr
FSpBumpDate(const FSSpec
*spec
)
1903 return ( BumpDate(spec
->vRefNum
, spec
->parID
, spec
->name
) );
1906 /*****************************************************************************/
1908 pascal OSErr
ChangeCreatorType(short vRefNum
,
1910 ConstStr255Param name
,
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
)
1926 if ( (pb
.hFileInfo
.ioFlAttrib
& ioDirMask
) == 0 ) /* if file */
1928 parID
= pb
.hFileInfo
.ioFlParID
; /* save parent dirID for BumpDate call */
1930 /* If creator not 0x00000000, change creator */
1931 if ( creator
!= (OSType
)0x00000000 )
1933 pb
.hFileInfo
.ioFlFndrInfo
.fdCreator
= creator
;
1936 /* If fileType not 0x00000000, change fileType */
1937 if ( fileType
!= (OSType
)0x00000000 )
1939 pb
.hFileInfo
.ioFlFndrInfo
.fdType
= fileType
;
1942 pb
.hFileInfo
.ioDirID
= dirID
;
1943 error
= PBSetCatInfoSync(&pb
); /* now, save the new information back to disk */
1945 if ( (error
== noErr
) && (parID
!= fsRtParID
) ) /* can't bump fsRtParID */
1947 /* get the real vRefNum in case a full pathname was passed */
1948 error
= DetermineVRefNum(name
, vRefNum
, &realVRefNum
);
1949 if ( error
== noErr
)
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 */
1959 /* it was a directory, not a file */
1960 error
= notAFileErr
;
1967 /*****************************************************************************/
1969 pascal OSErr
FSpChangeCreatorType(const FSSpec
*spec
,
1973 return ( ChangeCreatorType(spec
->vRefNum
, spec
->parID
, spec
->name
, creator
, fileType
) );
1976 /*****************************************************************************/
1978 pascal OSErr
ChangeFDFlags(short vRefNum
,
1980 ConstStr255Param name
,
1982 unsigned short flagBits
)
1990 /* Protection against File Sharing problem */
1991 if ( (name
== NULL
) || (name
[0] == 0) )
1994 pb
.hFileInfo
.ioNamePtr
= tempName
;
1995 pb
.hFileInfo
.ioFDirIndex
= -1; /* use ioDirID */
1999 pb
.hFileInfo
.ioNamePtr
= (StringPtr
)name
;
2000 pb
.hFileInfo
.ioFDirIndex
= 0; /* use ioNamePtr and ioDirID */
2002 pb
.hFileInfo
.ioVRefNum
= vRefNum
;
2003 pb
.hFileInfo
.ioDirID
= dirID
;
2004 error
= PBGetCatInfoSync(&pb
);
2005 if ( error
== noErr
)
2007 parID
= pb
.hFileInfo
.ioFlParID
; /* save parent dirID for BumpDate call */
2009 /* set or clear the appropriate bits in the Finder flags */
2012 /* OR in the bits */
2013 pb
.hFileInfo
.ioFlFndrInfo
.fdFlags
|= flagBits
;
2017 /* AND out the bits */
2018 pb
.hFileInfo
.ioFlFndrInfo
.fdFlags
&= ~flagBits
;
2021 if ( pb
.dirInfo
.ioNamePtr
== tempName
)
2023 pb
.hFileInfo
.ioDirID
= pb
.hFileInfo
.ioFlParID
;
2027 pb
.hFileInfo
.ioDirID
= dirID
;
2030 error
= PBSetCatInfoSync(&pb
); /* now, save the new information back to disk */
2032 if ( (error
== noErr
) && (parID
!= fsRtParID
) ) /* can't bump fsRtParID */
2034 /* get the real vRefNum in case a full pathname was passed */
2035 error
= DetermineVRefNum(name
, vRefNum
, &realVRefNum
);
2036 if ( error
== noErr
)
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 */
2048 /*****************************************************************************/
2050 pascal OSErr
FSpChangeFDFlags(const FSSpec
*spec
,
2052 unsigned short flagBits
)
2054 return ( ChangeFDFlags(spec
->vRefNum
, spec
->parID
, spec
->name
, setBits
, flagBits
) );
2057 /*****************************************************************************/
2059 pascal OSErr
SetIsInvisible(short vRefNum
,
2061 ConstStr255Param name
)
2062 /* Given a file or directory, make it invisible. */
2064 return ( ChangeFDFlags(vRefNum
, dirID
, name
, true, kIsInvisible
) );
2067 /*****************************************************************************/
2069 pascal OSErr
FSpSetIsInvisible(const FSSpec
*spec
)
2070 /* Given a file or directory, make it invisible. */
2072 return ( ChangeFDFlags(spec
->vRefNum
, spec
->parID
, spec
->name
, true, kIsInvisible
) );
2075 /*****************************************************************************/
2077 pascal OSErr
ClearIsInvisible(short vRefNum
,
2079 ConstStr255Param name
)
2080 /* Given a file or directory, make it visible. */
2082 return ( ChangeFDFlags(vRefNum
, dirID
, name
, false, kIsInvisible
) );
2085 /*****************************************************************************/
2087 pascal OSErr
FSpClearIsInvisible(const FSSpec
*spec
)
2088 /* Given a file or directory, make it visible. */
2090 return ( ChangeFDFlags(spec
->vRefNum
, spec
->parID
, spec
->name
, false, kIsInvisible
) );
2093 /*****************************************************************************/
2095 pascal OSErr
SetNameLocked(short vRefNum
,
2097 ConstStr255Param name
)
2098 /* Given a file or directory, lock its name. */
2100 return ( ChangeFDFlags(vRefNum
, dirID
, name
, true, kNameLocked
) );
2103 /*****************************************************************************/
2105 pascal OSErr
FSpSetNameLocked(const FSSpec
*spec
)
2106 /* Given a file or directory, lock its name. */
2108 return ( ChangeFDFlags(spec
->vRefNum
, spec
->parID
, spec
->name
, true, kNameLocked
) );
2111 /*****************************************************************************/
2113 pascal OSErr
ClearNameLocked(short vRefNum
,
2115 ConstStr255Param name
)
2116 /* Given a file or directory, unlock its name. */
2118 return ( ChangeFDFlags(vRefNum
, dirID
, name
, false, kNameLocked
) );
2121 /*****************************************************************************/
2123 pascal OSErr
FSpClearNameLocked(const FSSpec
*spec
)
2124 /* Given a file or directory, unlock its name. */
2126 return ( ChangeFDFlags(spec
->vRefNum
, spec
->parID
, spec
->name
, false, kNameLocked
) );
2129 /*****************************************************************************/
2131 pascal OSErr
SetIsStationery(short vRefNum
,
2133 ConstStr255Param name
)
2134 /* Given a file, make it a stationery pad. */
2136 return ( ChangeFDFlags(vRefNum
, dirID
, name
, true, kIsStationery
) );
2139 /*****************************************************************************/
2141 pascal OSErr
FSpSetIsStationery(const FSSpec
*spec
)
2142 /* Given a file, make it a stationery pad. */
2144 return ( ChangeFDFlags(spec
->vRefNum
, spec
->parID
, spec
->name
, true, kIsStationery
) );
2147 /*****************************************************************************/
2149 pascal OSErr
ClearIsStationery(short vRefNum
,
2151 ConstStr255Param name
)
2152 /* Given a file, clear the stationery bit. */
2154 return ( ChangeFDFlags(vRefNum
, dirID
, name
, false, kIsStationery
) );
2157 /*****************************************************************************/
2159 pascal OSErr
FSpClearIsStationery(const FSSpec
*spec
)
2160 /* Given a file, clear the stationery bit. */
2162 return ( ChangeFDFlags(spec
->vRefNum
, spec
->parID
, spec
->name
, false, kIsStationery
) );
2165 /*****************************************************************************/
2167 pascal OSErr
SetHasCustomIcon(short vRefNum
,
2169 ConstStr255Param name
)
2170 /* Given a file or directory, indicate that it has a custom icon. */
2172 return ( ChangeFDFlags(vRefNum
, dirID
, name
, true, kHasCustomIcon
) );
2175 /*****************************************************************************/
2177 pascal OSErr
FSpSetHasCustomIcon(const FSSpec
*spec
)
2178 /* Given a file or directory, indicate that it has a custom icon. */
2180 return ( ChangeFDFlags(spec
->vRefNum
, spec
->parID
, spec
->name
, true, kHasCustomIcon
) );
2183 /*****************************************************************************/
2185 pascal OSErr
ClearHasCustomIcon(short vRefNum
,
2187 ConstStr255Param name
)
2188 /* Given a file or directory, indicate that it does not have a custom icon. */
2190 return ( ChangeFDFlags(vRefNum
, dirID
, name
, false, kHasCustomIcon
) );
2193 /*****************************************************************************/
2195 pascal OSErr
FSpClearHasCustomIcon(const FSSpec
*spec
)
2196 /* Given a file or directory, indicate that it does not have a custom icon. */
2198 return ( ChangeFDFlags(spec
->vRefNum
, spec
->parID
, spec
->name
, false, kHasCustomIcon
) );
2201 /*****************************************************************************/
2203 pascal OSErr
ClearHasBeenInited(short vRefNum
,
2205 ConstStr255Param name
)
2206 /* Given a file, clear its "has been inited" bit. */
2208 return ( ChangeFDFlags(vRefNum
, dirID
, name
, false, kHasBeenInited
) );
2211 /*****************************************************************************/
2213 pascal OSErr
FSpClearHasBeenInited(const FSSpec
*spec
)
2214 /* Given a file, clear its "has been inited" bit. */
2216 return ( ChangeFDFlags(spec
->vRefNum
, spec
->parID
, spec
->name
, false, kHasBeenInited
) );
2219 /*****************************************************************************/
2221 pascal OSErr
CopyFileMgrAttributes(short srcVRefNum
,
2223 ConstStr255Param srcName
,
2226 ConstStr255Param dstName
,
2227 Boolean copyLockBit
)
2232 Boolean objectIsDirectory
;
2234 pb
.ciPB
.hFileInfo
.ioVRefNum
= srcVRefNum
;
2235 pb
.ciPB
.hFileInfo
.ioDirID
= srcDirID
;
2237 /* Protection against File Sharing problem */
2238 if ( (srcName
== NULL
) || (srcName
[0] == 0) )
2241 pb
.ciPB
.hFileInfo
.ioNamePtr
= tempName
;
2242 pb
.ciPB
.hFileInfo
.ioFDirIndex
= -1; /* use ioDirID */
2246 pb
.ciPB
.hFileInfo
.ioNamePtr
= (StringPtr
)srcName
;
2247 pb
.ciPB
.hFileInfo
.ioFDirIndex
= 0; /* use ioNamePtr and ioDirID */
2249 error
= PBGetCatInfoSync(&pb
.ciPB
);
2250 if ( error
== noErr
)
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) )
2257 pb
.ciPB
.hFileInfo
.ioNamePtr
= NULL
;
2261 pb
.ciPB
.hFileInfo
.ioNamePtr
= (StringPtr
)dstName
;
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) )
2268 pb
.hPB
.fileParam
.ioFVersNum
= 0;
2269 error
= PBHSetFLockSync(&pb
.hPB
);
2270 if ( (error
!= noErr
) && (objectIsDirectory
) )
2272 error
= noErr
; /* ignore lock errors if destination is directory */
2279 /*****************************************************************************/
2281 pascal OSErr
FSpCopyFileMgrAttributes(const FSSpec
*srcSpec
,
2282 const FSSpec
*dstSpec
,
2283 Boolean copyLockBit
)
2285 return ( CopyFileMgrAttributes(srcSpec
->vRefNum
, srcSpec
->parID
, srcSpec
->name
,
2286 dstSpec
->vRefNum
, dstSpec
->parID
, dstSpec
->name
,
2290 /*****************************************************************************/
2292 pascal OSErr
HOpenAware(short vRefNum
,
2294 ConstStr255Param fileName
,
2300 GetVolParmsInfoBuffer volParmsInfo
;
2301 long infoSize
= sizeof(GetVolParmsInfoBuffer
);
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
;
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
) )
2315 /* if volume supports OpenDeny, use it and return */
2316 pb
.accessParam
.ioDenyModes
= denyModes
;
2317 error
= PBHOpenDenySync(&pb
);
2318 *refNum
= pb
.ioParam
.ioRefNum
;
2320 else if ( (error
== noErr
) || (error
== paramErr
) ) /* paramErr is OK, it just means this volume doesn't support GetVolParms */
2322 /* OpenDeny isn't supported, so try File Manager Open functions */
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. */
2329 if ( (denyModes
& dmWr
) != 0 )
2331 error
= CheckVolLock(fileName
, vRefNum
);
2338 if ( error
== noErr
)
2340 /* Set File Manager permissions to closest thing possible */
2341 if ( (denyModes
== dmWr
) || (denyModes
== dmRdWr
) )
2343 pb
.ioParam
.ioPermssn
= fsRdWrShPerm
;
2347 pb
.ioParam
.ioPermssn
= denyModes
% 4;
2350 error
= PBHOpenDFSync(&pb
); /* Try OpenDF */
2351 if ( error
== paramErr
)
2353 error
= PBHOpenSync(&pb
); /* OpenDF not supported, so try Open */
2355 *refNum
= pb
.ioParam
.ioRefNum
;
2362 /*****************************************************************************/
2364 pascal OSErr
FSpOpenAware(const FSSpec
*spec
,
2368 return ( HOpenAware(spec
->vRefNum
, spec
->parID
, spec
->name
, denyModes
, refNum
) );
2371 /*****************************************************************************/
2373 pascal OSErr
HOpenRFAware(short vRefNum
,
2375 ConstStr255Param fileName
,
2381 GetVolParmsInfoBuffer volParmsInfo
;
2382 long infoSize
= sizeof(GetVolParmsInfoBuffer
);
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
;
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
) )
2396 /* if volume supports OpenRFDeny, use it and return */
2397 if ( hasOpenDeny(volParmsInfo
) )
2399 pb
.accessParam
.ioDenyModes
= denyModes
;
2400 error
= PBHOpenRFDenySync(&pb
);
2401 *refNum
= pb
.ioParam
.ioRefNum
;
2404 else if ( (error
== noErr
) || (error
== paramErr
) ) /* paramErr is OK, it just means this volume doesn't support GetVolParms */
2406 /* OpenRFDeny isn't supported, so try File Manager OpenRF function */
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. */
2413 if ( (denyModes
& dmWr
) != 0 )
2415 error
= CheckVolLock(fileName
, vRefNum
);
2422 if ( error
== noErr
)
2424 /* Set File Manager permissions to closest thing possible */
2425 if ( (denyModes
== dmWr
) || (denyModes
== dmRdWr
) )
2427 pb
.ioParam
.ioPermssn
= fsRdWrShPerm
;
2431 pb
.ioParam
.ioPermssn
= denyModes
% 4;
2434 error
= PBHOpenRFSync(&pb
);
2435 *refNum
= pb
.ioParam
.ioRefNum
;
2442 /*****************************************************************************/
2444 pascal OSErr
FSpOpenRFAware(const FSSpec
*spec
,
2448 return ( HOpenRFAware(spec
->vRefNum
, spec
->parID
, spec
->name
, denyModes
, refNum
) );
2451 /*****************************************************************************/
2453 pascal OSErr
FSReadNoCache(short refNum
,
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 */
2470 /*****************************************************************************/
2472 pascal OSErr
FSWriteNoCache(short refNum
,
2474 const void *buffPtr
)
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 */
2489 /*****************************************************************************/
2492 ** See if numBytes bytes of buffer1 are equal to buffer2.
2494 static Boolean
EqualMemory(const void *buffer1
, const void *buffer2
, unsigned long numBytes
)
2496 register unsigned char *b1
= (unsigned char *)buffer1
;
2497 register unsigned char *b2
= (unsigned char *)buffer2
;
2499 if ( b1
!= b2
) /* if buffer pointers are same, then they are equal */
2501 while ( numBytes
> 0 )
2503 /* compare the bytes and then increment the pointers */
2504 if ( (*b1
++ - *b2
++) != 0 )
2515 /*****************************************************************************/
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.
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.
2530 static OSErr
FSReadVerify(short refNum
,
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 */
2547 /*****************************************************************************/
2549 pascal OSErr
FSWriteVerify(short refNum
,
2551 const void *buffPtr
)
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.
2566 bufferSize
= *count
;
2567 verifyBuffer
= NewPtr(bufferSize
);
2568 if ( verifyBuffer
== NULL
)
2570 verifyBuffer
= GetTempBuffer(bufferSize
, &bufferSize
);
2572 if ( verifyBuffer
!= NULL
)
2574 /* Save the current position */
2575 result
= GetFPos(refNum
, &position
);
2576 if ( result
== noErr
)
2578 /* Write the data */
2579 result
= FSWrite(refNum
, count
, buffPtr
);
2580 if ( result
== noErr
)
2582 /* Restore the original position */
2583 result
= SetFPos(refNum
, fsFromStart
, position
);
2584 if ( result
== noErr
)
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
2594 startVerify
= (Ptr
)buffPtr
;
2595 while ( (bytesVerified
< *count
) && ( result
== noErr
) )
2597 if ( (*count
- bytesVerified
) > bufferSize
)
2599 byteCount
= bufferSize
;
2603 byteCount
= *count
- bytesVerified
;
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.
2617 BlockMoveData(startVerify
, verifyBuffer
, byteCount
);
2619 /* Read-verify the data back into the verify buffer */
2620 result
= FSReadVerify(refNum
, &byteCount
, verifyBuffer
);
2621 if ( result
== noErr
)
2623 /* See if the buffers are the same */
2624 if ( !EqualMemory(verifyBuffer
, startVerify
, byteCount
) )
2628 startVerify
+= byteCount
;
2629 bytesVerified
+= byteCount
;
2635 DisposePtr(verifyBuffer
);
2639 result
= memFullErr
;
2644 /*****************************************************************************/
2646 pascal OSErr
CopyFork(short srcRefNum
,
2648 void *copyBufferPtr
,
2649 long copyBufferSize
)
2651 ParamBlockRec srcPB
;
2652 ParamBlockRec dstPB
;
2656 if ( (copyBufferPtr
== NULL
) || (copyBufferSize
== 0) )
2657 return ( paramErr
);
2659 srcPB
.ioParam
.ioRefNum
= srcRefNum
;
2660 dstPB
.ioParam
.ioRefNum
= dstRefNum
;
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
);
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
);
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
);
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) )
2693 srcPB
.ioParam
.ioReqCount
= copyBufferSize
& 0xfffffe00;
2697 srcPB
.ioParam
.ioReqCount
= copyBufferSize
;
2699 dstPB
.ioParam
.ioBuffer
= (Ptr
)copyBufferPtr
;
2700 dstPB
.ioParam
.ioPosMode
= fsAtMark
+ 0x0020;/* fsAtMark + noCacheBit */
2702 while ( (srcError
== noErr
) && (dstError
== noErr
) )
2704 srcError
= PBReadSync(&srcPB
);
2705 dstPB
.ioParam
.ioReqCount
= srcPB
.ioParam
.ioActCount
;
2706 dstError
= PBWriteSync(&dstPB
);
2709 /* make sure there were no errors at the destination */
2710 if ( dstError
!= noErr
)
2711 return ( dstError
);
2713 /* make sure the only error at the source was eofErr */
2714 if ( srcError
!= eofErr
)
2715 return ( srcError
);
2720 /*****************************************************************************/
2722 pascal OSErr
GetFileLocation(short refNum
,
2730 pb
.ioNamePtr
= fileName
;
2732 pb
.ioRefNum
= refNum
;
2734 error
= PBGetFCBInfoSync(&pb
);
2735 if ( error
== noErr
)
2737 *vRefNum
= pb
.ioFCBVRefNum
;
2738 *dirID
= pb
.ioFCBParID
;
2743 /*****************************************************************************/
2745 pascal OSErr
FSpGetFileLocation(short refNum
,
2748 return ( GetFileLocation(refNum
, &(spec
->vRefNum
), &(spec
->parID
), spec
->name
) );
2751 /*****************************************************************************/
2753 pascal OSErr
CopyDirectoryAccess(short srcVRefNum
,
2755 ConstStr255Param srcName
,
2758 ConstStr255Param dstName
)
2761 GetVolParmsInfoBuffer infoBuffer
; /* Where PBGetVolParms dumps its info */
2762 long dstServerAdr
; /* AppleTalk server address of destination (if any) */
2763 long ownerID
, groupID
, accessRights
;
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
) )
2771 if ( hasAccessCntl(infoBuffer
) )
2773 dstServerAdr
= infoBuffer
.vMServerAdr
;
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
)
2780 if ( hasAccessCntl(infoBuffer
) && (dstServerAdr
== infoBuffer
.vMServerAdr
) )
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
)
2787 error
= HSetDirAccess(dstVRefNum
, dstDirID
, dstName
, ownerID
, groupID
, accessRights
);
2792 /* destination doesn't support directory access control or */
2793 /* they volumes aren't on the same server */
2800 /* destination doesn't support directory access control */
2808 /*****************************************************************************/
2810 pascal OSErr
FSpCopyDirectoryAccess(const FSSpec
*srcSpec
,
2811 const FSSpec
*dstSpec
)
2813 return ( CopyDirectoryAccess(srcSpec
->vRefNum
, srcSpec
->parID
, srcSpec
->name
,
2814 dstSpec
->vRefNum
, dstSpec
->parID
, dstSpec
->name
) );
2817 /*****************************************************************************/
2819 pascal OSErr
HMoveRenameCompat(short vRefNum
,
2821 ConstStr255Param srcName
,
2823 ConstStr255Param dstpathName
,
2824 ConstStr255Param copyName
)
2827 GetVolParmsInfoBuffer volParmsInfo
;
2832 Boolean isDirectory
;
2833 long tempItemsDirID
;
2834 long uniqueTempDirID
;
2835 Str31 uniqueTempDirName
;
2836 unsigned short uniqueNameoverflow
;
2838 /* Get volume attributes */
2839 infoSize
= sizeof(GetVolParmsInfoBuffer
);
2840 error
= HGetVolParms((StringPtr
)srcName
, vRefNum
, &volParmsInfo
, &infoSize
);
2841 if ( (error
== noErr
) && hasMoveRename(volParmsInfo
) )
2843 /* If volume supports move and rename, so use it and return */
2844 error
= HMoveRename(vRefNum
, srcDirID
, srcName
, dstDirID
, dstpathName
, copyName
);
2846 else if ( (error
== noErr
) || (error
== paramErr
) ) /* paramErr is OK, it just means this volume doesn't support GetVolParms */
2848 /* MoveRename isn't supported by this volume, so do it by hand */
2850 /* If copyName isn't supplied, we can simply CatMove and return */
2851 if ( copyName
== NULL
)
2853 error
= CatMove(vRefNum
, srcDirID
, srcName
, dstDirID
, dstpathName
);
2857 /* Renaming is required, so we have some work to do... */
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
)
2864 /* Find the Temporary Items Folder on that volume */
2865 error
= FindFolder(realVRefNum
, kTemporaryFolderType
, kCreateFolder
,
2866 &realVRefNum
, &tempItemsDirID
);
2867 if ( error
== noErr
)
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. */
2873 /* Start with current tick count as uniqueTempDirName */
2874 NumToString(TickCount(), uniqueTempDirName
);
2875 uniqueNameoverflow
= 0;
2878 error
= DirCreate(realVRefNum
, tempItemsDirID
, uniqueTempDirName
, &uniqueTempDirID
);
2879 if ( error
== dupFNErr
)
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] == ':' )
2886 ++uniqueTempDirName
[1];
2888 /* Don't go too far... */
2889 ++uniqueNameoverflow
;
2891 } while ( (error
== dupFNErr
) && (uniqueNameoverflow
<= 64) ); /* 64 new files per 1/60th second - not likely! */
2892 if ( error
== noErr
)
2894 /* Move the object to the folder with uniqueTempDirID for renaming */
2895 error
= CatMove(realVRefNum
, realParID
, realName
, uniqueTempDirID
, NULL
);
2896 if ( error
== noErr
)
2898 /* Rename the object */
2899 error
= HRename(realVRefNum
, uniqueTempDirID
, realName
, copyName
);
2900 if ( error
== noErr
)
2902 /* Move object to its new home */
2903 error
= CatMove(realVRefNum
, uniqueTempDirID
, copyName
, dstDirID
, dstpathName
);
2904 if ( error
!= noErr
)
2906 /* Error handling: rename object back to original name - ignore errors */
2907 (void) HRename(realVRefNum
, uniqueTempDirID
, copyName
, realName
);
2910 if ( error
!= noErr
)
2912 /* Error handling: move object back to original location - ignore errors */
2913 (void) CatMove(realVRefNum
, uniqueTempDirID
, realName
, realParID
, NULL
);
2916 /* Done with ourTempDir, so delete it - ignore errors */
2917 (void) HDelete(realVRefNum
, uniqueTempDirID
, NULL
);
2927 /*****************************************************************************/
2929 pascal OSErr
FSpMoveRenameCompat(const FSSpec
*srcSpec
,
2930 const FSSpec
*dstSpec
,
2931 ConstStr255Param copyName
)
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
) );
2940 /*****************************************************************************/
2942 pascal OSErr
BuildAFPVolMountInfo(short flags
,
2952 AFPVolMountInfoPtr
*afpInfoPtr
)
2954 MyAFPVolMountInfoPtr infoPtr
;
2957 /* Allocate the AFPXVolMountInfo record */
2958 infoPtr
= (MyAFPVolMountInfoPtr
)NewPtrClear(sizeof(MyAFPVolMountInfo
));
2959 if ( infoPtr
!= NULL
)
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
;
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
);
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
));
2983 *afpInfoPtr
= (AFPVolMountInfoPtr
)infoPtr
;
2994 /*****************************************************************************/
2996 pascal OSErr
RetrieveAFPVolMountInfo(AFPVolMountInfoPtr afpInfoPtr
,
3000 StringPtr serverName
,
3007 /* Retrieve the AFP mounting information from an AFPVolMountInfo record. */
3008 if ( afpInfoPtr
->media
== AppleShareMediaType
)
3010 *flags
= afpInfoPtr
->flags
;
3011 *uamType
= afpInfoPtr
->uamType
;
3013 if ( afpInfoPtr
->zoneNameOffset
!= 0)
3015 tempPtr
= (StringPtr
)((long)afpInfoPtr
+ afpInfoPtr
->zoneNameOffset
);
3016 BlockMoveData(tempPtr
, zoneName
, tempPtr
[0] + 1);
3019 if ( afpInfoPtr
->serverNameOffset
!= 0)
3021 tempPtr
= (StringPtr
)((long)afpInfoPtr
+ afpInfoPtr
->serverNameOffset
);
3022 BlockMoveData(tempPtr
, serverName
, tempPtr
[0] + 1);
3025 if ( afpInfoPtr
->volNameOffset
!= 0)
3027 tempPtr
= (StringPtr
)((long)afpInfoPtr
+ afpInfoPtr
->volNameOffset
);
3028 BlockMoveData(tempPtr
, volName
, tempPtr
[0] + 1);
3031 if ( afpInfoPtr
->userNameOffset
!= 0)
3033 tempPtr
= (StringPtr
)((long)afpInfoPtr
+ afpInfoPtr
->userNameOffset
);
3034 BlockMoveData(tempPtr
, userName
, tempPtr
[0] + 1);
3047 /*****************************************************************************/
3049 pascal OSErr
BuildAFPXVolMountInfo(short flags
,
3060 unsigned long alternateAddressLength
,
3061 void *alternateAddress
,
3062 AFPXVolMountInfoPtr
*afpXInfoPtr
)
3065 MyAFPXVolMountInfoPtr infoPtr
;
3068 /* Calculate the size of the AFPXVolMountInfo record */
3069 infoSize
= sizeof(MyAFPXVolMountInfo
) + alternateAddressLength
- 1;
3071 /* Allocate the AFPXVolMountInfo record */
3072 infoPtr
= (MyAFPXVolMountInfoPtr
)NewPtrClear(infoSize
);
3073 if ( infoPtr
!= NULL
)
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 )
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
;
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;
3093 infoPtr
->nbpInterval
= nbpInterval
;
3094 infoPtr
->nbpCount
= nbpCount
;
3095 infoPtr
->uamType
= uamType
;
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
);
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
);
3115 *afpXInfoPtr
= (AFPXVolMountInfoPtr
)infoPtr
;
3126 /*****************************************************************************/
3128 pascal OSErr
RetrieveAFPXVolMountInfo(AFPXVolMountInfoPtr afpXInfoPtr
,
3132 StringPtr serverName
,
3136 unsigned long *alternateAddressLength
,
3137 AFPAlternateAddress
**alternateAddress
)
3140 Ptr alternateAddressStart
;
3141 Ptr alternateAddressEnd
;
3142 Size alternateAddressDataSize
;
3146 /* Retrieve the AFP mounting information from an AFPVolMountInfo record. */
3147 if ( afpXInfoPtr
->media
== AppleShareMediaType
)
3149 /* default to noErr */
3152 /* Is this an extended record? */
3153 if ( (afpXInfoPtr
->flags
& volMountExtendedFlagsMask
) != 0 )
3155 if ( ((afpXInfoPtr
->extendedFlags
& kAFPExtendedFlagsAlternateAddressMask
) != 0) &&
3156 (afpXInfoPtr
->alternateAddressOffset
!= 0) )
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 )
3166 /* parse the address list to find the end */
3167 alternateAddressEnd
+= *(UInt8
*)alternateAddressEnd
; /* add length of each AFPTagData record */
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
)
3176 /* and return the data */
3177 BlockMoveData(alternateAddressStart
, *alternateAddress
, alternateAddressDataSize
);
3178 *alternateAddressLength
= alternateAddressDataSize
;
3182 /* no memory - fail now */
3187 if ( error
== noErr
) /* fill in more output parameters if everything is OK */
3189 if ( afpXInfoPtr
->uamNameOffset
!= 0 )
3191 tempPtr
= (StringPtr
)((long)afpXInfoPtr
+ afpXInfoPtr
->uamNameOffset
);
3192 BlockMoveData(tempPtr
, uamName
, tempPtr
[0] + 1);
3197 if ( error
== noErr
) /* fill in more output parameters if everything is OK */
3199 *flags
= afpXInfoPtr
->flags
;
3200 *uamType
= afpXInfoPtr
->uamType
;
3202 if ( afpXInfoPtr
->zoneNameOffset
!= 0 )
3204 tempPtr
= (StringPtr
)((long)afpXInfoPtr
+ afpXInfoPtr
->zoneNameOffset
);
3205 BlockMoveData(tempPtr
, zoneName
, tempPtr
[0] + 1);
3208 if ( afpXInfoPtr
->serverNameOffset
!= 0 )
3210 tempPtr
= (StringPtr
)((long)afpXInfoPtr
+ afpXInfoPtr
->serverNameOffset
);
3211 BlockMoveData(tempPtr
, serverName
, tempPtr
[0] + 1);
3214 if ( afpXInfoPtr
->volNameOffset
!= 0 )
3216 tempPtr
= (StringPtr
)((long)afpXInfoPtr
+ afpXInfoPtr
->volNameOffset
);
3217 BlockMoveData(tempPtr
, volName
, tempPtr
[0] + 1);
3220 if ( afpXInfoPtr
->userNameOffset
!= 0 )
3222 tempPtr
= (StringPtr
)((long)afpXInfoPtr
+ afpXInfoPtr
->userNameOffset
);
3223 BlockMoveData(tempPtr
, userName
, tempPtr
[0] + 1);
3235 /*****************************************************************************/
3237 pascal OSErr
GetUGEntries(short objType
,
3240 long *actEntryCount
,
3244 OSErr error
= noErr
;
3245 UGEntry
*endEntryArray
;
3247 pb
.objParam
.ioObjType
= objType
;
3249 for ( endEntryArray
= entries
+ reqEntryCount
; (entries
< endEntryArray
) && (error
== noErr
); ++entries
)
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
)
3260 entries
->objID
= *objID
= pb
.objParam
.ioObjID
;
3261 entries
->objType
= objType
;
3269 /*****************************************************************************/