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