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