/* * Copyright 2001-2002 Perforce Software. All rights reserved. * * This file is part of Perforce - the FAST SCM System. */ /* * macfile.cpp - Abstract file layer to handle the many differences on * Mac OS X and Classic Mac OS */ # include <unistd.h> # include <sys/mount.h> # include <stdhdrs.h> # include <strbuf.h> # include <strarray.h> # include <i18napi.h> # include "macfile.h" static bool IsBigEndian() { // detect byte ordering static unsigned short s = 1; return *(unsigned char *)&s != (unsigned char)1; } /* ============================= *\ * * PRIVATE METHODS * \* ============================= */ UniChar GetFileSeparator( CFURLPathStyle pathStyle ); CFURLPathStyle GetSystemPathStyle(); char * FSRefToPath( const FSRef * ref ); const HFSUniStr255 * GetDataForkName(); const HFSUniStr255 * GetRsrcForkName(); /* ============================= *\ * * UTILITY METHODS * \* ============================= */ UniChar GetFileSeparator( CFURLPathStyle pathStyle ) { switch ( pathStyle ) { case kCFURLPOSIXPathStyle: return (UniChar)'/'; case kCFURLHFSPathStyle: return (UniChar)':'; default: return (UniChar)'\\'; } } UniChar GetSystemFileSeparator() { return GetFileSeparator( GetSystemPathStyle() ); } char * FSRefToPath( const FSRef * ref ) { unsigned long length = 256; char * fullPath = NULL; OSStatus status = noErr; // Use the FSRef calls to get the path // fullPath = new char[ length ]; status = FSRefMakePath( ref, (UInt8 *)fullPath, length ); while ( status == pathTooLongErr || status == buffersTooSmall ) { length += 256; delete [] fullPath; fullPath = new char[ length ]; status = FSRefMakePath( ref, (UInt8 *)fullPath, length ); } if ( status != noErr ) { delete [] fullPath; fullPath = NULL; } return fullPath; } CFURLPathStyle GetSystemPathStyle() { return kCFURLPOSIXPathStyle; } const HFSUniStr255 * GetDataForkName() { static int gotName = 0; static HFSUniStr255 dataForkName; if ( gotName ) return &dataForkName; FSGetDataForkName( &dataForkName ); gotName = 1; return &dataForkName; } const HFSUniStr255 * GetRsrcForkName() { static int gotName = 0; static HFSUniStr255 rsrcForkName; if ( gotName ) return &rsrcForkName; FSGetResourceForkName( &rsrcForkName ); gotName = 1; return &rsrcForkName; } /* ============================= *\ * * MacFile * \* ============================= */ MacFile * MacFile::GetFromPath( const char * path, OSErr * error ) { FSRef ref; Boolean isDir; OSStatus status = FSPathMakeRef( (const UInt8 *)path, &ref, &isDir ); if ( status != noErr ) { if ( error ) *error = status; return NULL; } return GetFromFSRef( &ref, error ); } CFStringRef CreateStringAsFilename( CFStringRef path ) { CFRange range = CFStringFind( path, CFSTR("/"), kCFCompareBackwards ); range.location += 1; range.length = CFStringGetLength( path ) - range.location; return CFStringCreateWithSubstring( kCFAllocatorDefault, path, range ); } CFStringRef CreateStringWithParent( CFStringRef path ) { CFRange range = CFStringFind( path, CFSTR("/"), kCFCompareBackwards ); range.length = range.location; range.location = 0; return CFStringCreateWithSubstring( kCFAllocatorDefault, path, range ); } MacFile * MacFile::CreateFromDirAndName( const FSRef & dir, CFStringRef name, Boolean isDirectory, OSErr * outErr ) { FSRef * fsref = new FSRef; OSErr err; if ( isDirectory ) { err = FSCreateDirectoryUnicode( &dir, CFStringGetLength(name), CFStringGetCharactersPtr(name), kFSCatInfoNone, NULL, fsref, NULL, NULL ); } else { CFDataRef data = CFStringCreateExternalRepresentation(kCFAllocatorDefault, name, IsBigEndian() ? kCFStringEncodingUTF16BE : kCFStringEncodingUTF16LE, 0 ); err = FSCreateFileUnicode( &dir, CFDataGetLength(data)/2, (const UniChar*)CFDataGetBytePtr(data), kFSCatInfoNone, NULL, fsref, NULL); CFRelease( data ); } if ( outErr ) *outErr = err; if ( err != noErr ) return NULL; return new MacFile( fsref ); } MacFile * MacFile::CreateFromPath( const char * path, Boolean isDirectory, OSErr * error ) { FSRef ref; Boolean isDir; CFStringRef strRef = CFStringCreateWithFileSystemRepresentation( kCFAllocatorDefault, path ); CFStringRef parent = CreateStringWithParent( strRef ); CFStringRef fileName = CreateStringAsFilename( strRef ); CFRelease( strRef ); int parentPathLength = CFStringGetMaximumSizeOfFileSystemRepresentation( parent ); char * parentPath = (char*)malloc( parentPathLength ); Boolean success = CFStringGetFileSystemRepresentation( parent, parentPath, parentPathLength ); CFRelease( parent ); if ( !success ) { CFRelease( fileName ); free( parentPath ); return NULL; } OSStatus status = FSPathMakeRef( (const UInt8 *)parentPath, &ref, &isDir ); free( parentPath ); if ( status != noErr ) { if ( error ) *error = status; CFRelease( fileName ); return NULL; } MacFile * returnVal = MacFile::CreateFromDirAndName( ref, fileName, isDirectory, error ); CFRelease( fileName ); return returnVal; } /* ============================= *\ * * MacFile * \* ============================= */ MacFile * MacFile::GetFromFSRef( const FSRef * ref, OSErr * err ) { FSRef * temp = new FSRef; *temp = *ref; return new MacFile( temp ); } MacFile::MacFile( FSRef * file ) { this->file = file; LoadCatalogInfo(); this->changedInfoBitmap = 0; this->fullPath = FSRefToPath( file ); dataForkRef = rsrcForkRef = 0; this->comment = NULL; this->commentLength = -1; } MacFile::~MacFile() { delete file; delete [] fullPath; delete [] comment; } OSErr MacFile::Delete() { if ( !file ) return fnfErr; OSErr ret; ret = FSDeleteFork( file, GetRsrcForkName()->length, GetRsrcForkName()->unicode ); if( ret != noErr ) return ret; return FSDeleteObject( file ); } int MacFile::GetFileSystemType() const { int result = FS_HFS; # if defined( OS_MACOSX ) && !defined( MAC_MWPEF ) const char * path = GetPrintableFullPath(); struct statfs buf; statfs( path, &buf ); if ( !strcmp( buf.f_fstypename, "hfs" ) ) result = FS_HFS; else if ( !strcmp( buf.f_fstypename, "ufs" ) ) result = FS_UFS; else if ( !strcmp( buf.f_fstypename, "nfs" ) ) result = FS_NFS; # endif // OS_MACOSX return result; } Boolean MacFile::IsDir() const { return fileInfo.nodeFlags & kFSNodeIsDirectoryMask; } OSErr MacFile::LoadCatalogInfo() const { FSCatalogInfoBitmap info = kFSCatInfoFinderInfo | kFSCatInfoNodeFlags | kFSCatInfoFinderXInfo | kFSCatInfoDataSizes | kFSCatInfoRsrcSizes; OSErr err = FSGetCatalogInfo( this->file, info, &this->fileInfo, NULL, NULL, NULL ); return err; } OSErr MacFile::SaveCatalogInfo() { if ( !this->changedInfoBitmap ) return noErr; OSErr err = FSSetCatalogInfo( this->file, this->changedInfoBitmap, &this->fileInfo ); this->changedInfoBitmap = 0; return err; } const FSRef * MacFile::GetFSRef( FSRef * ref ) const { if ( ref ) *ref = *file; return file; } const FInfo * MacFile::GetFInfo() const { return (const FInfo *)&this->fileInfo.finderInfo; } OSErr MacFile::SetFInfo( const FInfo * fInfo) { FInfo * localInfo = (FInfo *)&(this->fileInfo.finderInfo); if ( fInfo ) *localInfo = *fInfo; changedInfoBitmap |= kFSCatInfoFinderInfo; return SaveCatalogInfo(); } Boolean MacFile::IsHidden() const { FInfo * localInfo = (FInfo *)&(this->fileInfo.finderInfo); return (localInfo->fdFlags & kIsInvisible) != 0; } Boolean MacFile::IsUnbundledApp() const { FInfo * info = (FInfo *)&fileInfo.finderInfo; return info->fdType == 'APPL'; } CFBundleRef MacFile::CreateBundle() const { CFURLRef bundleURL; CFBundleRef myBundle; // Make a CFURLRef from the CFString representation of the // bundle's path. See the Core Foundation URL Services chapter // for details. // bundleURL = CFURLCreateFromFSRef( NULL, file ); if ( !bundleURL ) return NULL; // Make a bundle instance using the URLRef. // myBundle = CFBundleCreate( NULL, bundleURL ); CFRelease( bundleURL ); return myBundle; } Boolean MacFile::IsBundledApp() const { CFBundleRef myBundle = CreateBundle(); if ( !myBundle ) return false; CFDictionaryRef bundleInfoDict; // Get an instance of the info plist. // bundleInfoDict = CFBundleGetInfoDictionary( myBundle ); // If we succeeded, look for our property. // if ( bundleInfoDict == NULL ) { CFRelease( myBundle ); return false; } // Get the URL for the executable and see if it exists // CFURLRef urlRef = CFBundleCopyExecutableURL( myBundle ); if ( urlRef ) { CFRelease( urlRef ); CFRelease( myBundle ); return true; } // Any CF objects returned from functions with "create" or // "copy" in their names must be released by us! // CFRelease( myBundle ); return false; } Boolean MacFile::IsLocked() const { return this->fileInfo.nodeFlags & kioFlAttribLockedMask; } OSErr MacFile::SetLocked( Boolean lock ) { if ( lock == IsLocked() ) return noErr; if ( lock ) this->fileInfo.nodeFlags |= kioFlAttribLockedMask; else this->fileInfo.nodeFlags &= ~kioFlAttribLockedMask; this->changedInfoBitmap |= kFSCatInfoNodeFlags; return SaveCatalogInfo(); } const char * MacFile::GetPrintableFullPath() const { return this->fullPath; } const FXInfo * MacFile::GetFXInfo() const { return (const FXInfo *)&fileInfo.extFinderInfo; } OSErr MacFile::SetFXInfo( const FXInfo * fInfo) { FXInfo * localInfo = (FXInfo *)&(fileInfo.extFinderInfo); if ( fInfo ) *localInfo = *fInfo; changedInfoBitmap |= kFSCatInfoFinderXInfo; return SaveCatalogInfo(); } Boolean MacFile::HasDataFork() const { return GetDataForkSize() > 0; } ByteCount MacFile::GetDataForkSize() const { LoadCatalogInfo(); return fileInfo.dataLogicalSize; } OSErr MacFile::OpenDataFork( SInt8 permissions ) { return FSOpenFork( file, GetDataForkName()->length, GetDataForkName()->unicode, permissions, &dataForkRef ); } OSErr MacFile::ReadDataFork( ByteCount requestCount, void * buffer, ByteCount * actualCount ) { if ( !dataForkRef ) return noErr; return FSReadFork( dataForkRef, fsAtMark, 0, requestCount, buffer, actualCount); } OSErr MacFile::WriteDataFork( ByteCount requestCount, const void * buffer, ByteCount * actualCount ) { if ( !dataForkRef ) return noErr; return FSWriteFork( dataForkRef, fsAtMark, 0, requestCount, buffer, actualCount ); } OSErr MacFile::SetDataForkPosition( ByteCount offset ) { if ( !dataForkRef ) return noErr; return FSSetForkPosition( dataForkRef, fsFromStart, offset ); } OSErr MacFile::CloseDataFork() { if ( !dataForkRef ) return noErr; OSErr err = noErr; SInt64 pos = 0; FSGetForkPosition( dataForkRef, &pos ); FSSetForkSize( dataForkRef, fsFromStart, pos ); err = FSCloseFork( dataForkRef ); dataForkRef = 0; return err; } Boolean MacFile::HasResourceFork() const { return GetResourceForkSize() > 0; } ByteCount MacFile::GetResourceForkSize() const { LoadCatalogInfo(); return fileInfo.rsrcLogicalSize; } OSErr MacFile::OpenResourceFork( SInt8 permissions ) { return FSOpenFork ( file, GetRsrcForkName()->length, GetRsrcForkName()->unicode, permissions, &rsrcForkRef ); } OSErr MacFile::ReadResourceFork( ByteCount requestCount, void * buffer, ByteCount * actualCount ) { if ( !rsrcForkRef ) return noErr; return FSReadFork( rsrcForkRef, fsAtMark, 0, requestCount, buffer, actualCount); } OSErr MacFile::WriteResourceFork( ByteCount requestCount, const void * buffer, ByteCount * actualCount ) { if ( !rsrcForkRef ) return noErr; return FSWriteFork( rsrcForkRef, fsAtMark, 0, requestCount, buffer, actualCount ); } OSErr MacFile::CloseResourceFork() { if ( !rsrcForkRef ) return noErr; OSErr err = noErr; SInt64 pos = 0; FSGetForkPosition( rsrcForkRef, &pos ); FSSetForkSize( rsrcForkRef, fsFromStart, pos ); err = FSCloseFork( rsrcForkRef ); rsrcForkRef = 0; return err; } OSErr MacFile::PreloadComment() const { OSErr error = noErr; return error; } const char * MacFile::GetComment( int *bufferLength ) const { *bufferLength = 0; return NULL; PreloadComment(); *bufferLength = commentLength; return comment; } OSErr MacFile::SetComment( char * buffer, int bufferLength ) { // // we don't do anything with this right now // return noErr; } OSErr MacFile::GetTypeAndCreator( UInt32 * type, UInt32 * creator ) const { FInfo * info = (FInfo *)&fileInfo.finderInfo; if (type) *type = info->fdType; if (creator) *creator = info->fdCreator; return noErr; } OSErr MacFile::SetTypeAndCreator( UInt32 type, UInt32 creator ) { FInfo * info = (FInfo *)&fileInfo.finderInfo; info->fdType = type; info->fdCreator = creator; changedInfoBitmap |= kFSCatInfoFinderInfo; return SaveCatalogInfo(); } #define M_32_SWAP(a) { \ uint32_t _tmp = a; \ ((char *)&a)[0] = ((char *)&_tmp)[3]; \ ((char *)&a)[1] = ((char *)&_tmp)[2]; \ ((char *)&a)[2] = ((char *)&_tmp)[1]; \ ((char *)&a)[3] = ((char *)&_tmp)[0]; \ } #define M_16_SWAP(a) { \ uint16_t _tmp = a; \ ((char *)&a)[0] = ((char *)&_tmp)[1]; \ ((char *)&a)[1] = ((char *)&_tmp)[0]; \ } void MacFile::SwapFInfo( FInfo *f ) { if ( IsBigEndian() ) return; M_32_SWAP( f->fdType ); M_32_SWAP( f->fdCreator ); M_16_SWAP( f->fdFlags ); M_16_SWAP( f->fdLocation.v ); M_16_SWAP( f->fdLocation.h ); M_16_SWAP( f->fdFldr ); } void MacFile::SwapFXInfo( FXInfo *f ) { if ( IsBigEndian() ) return; M_16_SWAP( f->fdIconID ); # ifdef notdef M_16_SWAP( (f->fdUnused[0]) ); M_16_SWAP( (f->fdUnused[1]) ); M_16_SWAP( (f->fdUnused[2]) ); # endif M_16_SWAP( f->fdComment ); M_32_SWAP( f->fdPutAway ); }