/* * Copyright 1995, 1996 Perforce Software. All rights reserved. * * This file is part of Perforce - the FAST SCM System. */ # define NEED_STAT # include <stdhdrs.h> # if defined ( OS_MACOSX ) # include <error.h> # include <errornum.h> # include <msgos.h> # include <strbuf.h> # include <strarray.h> # include <i18napi.h> # include <charcvt.h> # include "filesys.h" # include "fileio.h" # include "macfile.h" static const OSType kUnknownSignature = FOUR_CHAR_CONSTANT('?','?','?','?'); extern int global_umask; FileIOMac::FileIOMac() { mf = 0; } FileIOMac::~FileIOMac() { delete mf; } void FileIOMac::Set( const StrPtr &name ) { Set( name, 0 ); } void FileIOMac::Set( const StrPtr &name, Error *e ) { // // The MacFile will no longer refer to this new name, // so delete it. // delete mf; mf = 0; FileSys::Set( name, e ); } MacFile * FileIOMac::GetMacFile( Error *e ) { OSErr err; if ( !mf ) { MacFile * macFile = MacFile::GetFromPath( Name(), &err); if ( !macFile ) { if ( e ) e->Sys("open", Name() ); return NULL; } mf = macFile; } return mf; } MacFile * FileIOMac::CreateMacFile( Error *e ) { OSErr err; if ( !mf ) { MacFile * tempMf = MacFile::CreateFromPath( Name(), false, &err); if ( !tempMf ) { if ( e ) e->Sys("open", Name() ); return NULL; } mf = tempMf; } return mf; } void FileIOMac::Open( FileOpenMode mode, Error *e ) { // Save mode for write, close this->mode = mode; // Short circuit stdio if( Name()[0] == '-' && !Name()[1] ) return; int perm = mode == FOM_WRITE ? fsWrPerm : fsRdPerm; // Get handles for real file MacFile * macFile = GetMacFile( e ); if ( perm == fsWrPerm ) { if ( !macFile ) { e->Clear(); macFile = CreateMacFile( e ); } } if ( macFile ) { if( macFile->OpenDataFork( perm ) != noErr ) { e->Sys("open", Name() ); return; } } } void FileIOMac::Close( Error *e ) { if( !mf ) return; if( (mf)->CloseDataFork() < 0 ) e->Sys( "close", Name() ); if( mode == FOM_WRITE && modTime ) ChmodTime( modTime, e ); if( mode == FOM_WRITE ) Chmod( perms, e ); // This needs to happen only on Mac OS 10.0.0.x // versions of the OS. The FSRef (inside MacFile) will // be maintained through another app editing it on Mac OS 10.1 // delete mf; mf = NULL; } int FileIOMac::Read( char *buf, int len, Error *e ) { // Short circuit stdio if( Name()[0] == '-' && !Name()[1] ) { return fread( buf, 1, len, stdin ); } ByteCount size = len; OSErr err = mf->ReadDataFork( size, buf, &size ); if ( err == noErr || err == eofErr ) return size; e->Sys( "read", Name() ); return -1; } void FileIOMac::Write( const char *buf, int len, Error *e ) { // Short circuit stdio if( Name()[0] == '-' && !Name()[1] ) { fwrite( buf, 1, len, stdout ); return; } ByteCount size = len; if ( mf ) { if ( mf->WriteDataFork( size, buf, &size ) != noErr ) e->Sys( "write", Name() ); } } offL_t FileIOMac::GetSize() { if ( mf ) { return mf->GetDataForkSize(); } else return 0; } void FileIOMac::Seek( offL_t offset, Error * ) { if ( mf ) { mf->SetDataForkPosition( offset ); } } /* * FileIOMac::Stat() -- workaround broken CW stat() */ int FileIOMac::Stat( bool includeRsrcFork ) { int flags = 0; flags = FileIO::Stat(); if ( !includeRsrcFork ) return flags; if ( !(flags & FSF_EXISTS) ) return flags; if ( flags & FSF_DIRECTORY ) return flags; if ( flags & FSF_SYMLINK ) return flags; if ( !(flags & FSF_EMPTY) ) return flags; // If the file is empty, double-check for the resource fork // Error e; const MacFile * fileEntity = GetMacFile( &e ); if( fileEntity && fileEntity->GetResourceForkSize() > 0 ) flags &= ~FSF_EMPTY; return flags; } void FileIOMac::Rename( FileSys *target, Error *e ) { // copy, rather than rename, to preserve any data/resource fork if ( ( target->GetType() & FST_MASK ) != FST_SYMLINK ) { // make target writeable first Chmod( FPM_RW, e ); if( e->Test() ) { e->Clear(); // raw chmod failed... try unlink... // This is pretty safe I think because // if the unlink succeeds then we know we can write // the directory so create the file with the copy // below. If the unlink fails, we give up because // we will not be able to write the resource fork // or some other problem will happen... if( target->Stat() & FSF_EXISTS ) { target->Unlink( e ); if( e->Test() ) return; target->ClearDeleteOnClose(); } } Copy( target, FPM_RW, e ); if( e->Test() ) return; Unlink( e ); // reset the target to our perms target->Perms( perms ); target->Chmod( e ); } else if( rename( Name(), target->Name() ) < 0 ) { e->Sys( "rename", target->Name() ); return; } // source file has been deleted, clear the flag ClearDeleteOnClose(); } /* * FileIOMac::Unlink() - remove apple file * * This is special because it will cleanup resource fork * hidden files which mess up Rename via Copy. */ void FileIOMac::Unlink( Error *e ) { if( !*Name() ) return; Chmod( FPM_RW, 0 ); MacFile *macFile = GetMacFile( e ); if( ( !macFile || macFile->Delete() != noErr ) && e ) e->Sys( "unlink", Name() ); } /* * FileIOResource support */ FileIOResource::FileIOResource() { resourceData = 0; } FileIOResource::~FileIOResource() { Cleanup(); } # if defined ( OS_MACOSX ) /* * MAC FileSys::Set() - set file name, recognizing .file as resource */ void FileIOResource::Set( const StrPtr &name ) { Set( name, 0 ); } void FileIOResource::Set( const StrPtr &name, Error *e ) { // Set colon to file component (after final :) const char *colon = NULL; colon = strrchr( name.Text(), GetSystemFileSeparator() ); // unix paths colon = colon ? colon + 1 : name.Text(); hasDotInName = *colon == '.'; // Is this a .file ? // If so, note that the name indicates a resource fork. // It is really if resource fork if SetType is told so, // but we use this information to cue CheckType(). if( hasDotInName ) { // Name sans dot path.Set( name.Text(), colon - name.Text() ); path.Append( colon + 1 ); } else { // No. Plain file. path.Set( name ); } } # else void FileIOResource::Set( const StrPtr &name ) { Set( name, 0 ); } void FileIOResource::Set( const StrPtr &name, Error *e ) { path.Set( name ); } # endif void FileIOResource::Open( FileOpenMode mode, Error *e ) { // Save mode for write, close this->mode = mode; resourceData = new StrBuf; // If we're opening the resource fork for read, fill temp file and // open that. if( mode == FOM_READ ) { ByteCount size; // bytes to write to file char *buf; OSErr err; ioOffset = 0; resourceData->Clear(); MacFile * macFile = MacFile::GetFromPath( Name(), &err ); if ( !macFile ) { e->Sys( "File not found for resource encoding!", Name() ); return; } if( macFile->OpenResourceFork( fsRdPerm ) != noErr ) { e->Sys("Can't open resource fork for encoding!", Name() ); delete macFile; return; } /* * Put file info into resource fork. */ size = sizeof( FInfo ); buf = resourceData->Alloc( size ); const FInfo *fi = macFile->GetFInfo(); memcpy( buf, fi, size ); MacFile::SwapFInfo( (FInfo *)buf ); size = macFile->GetResourceForkSize(); buf = resourceData->Alloc( size ); if( macFile->ReadResourceFork( size, buf, &size ) != noErr ) { e->Sys( "Error reading from resource fork!", Name()); } macFile->CloseResourceFork(); delete macFile; } } void FileIOResource::Write( const char *buf, int len, Error *e ) { resourceData->Append( buf, len ); } int FileIOResource::Read( char *buf, int len, Error *e ) { int l = resourceData->Length() - ioOffset; if( len < l ) l = len; memcpy( buf, resourceData->Text() + ioOffset, l ); ioOffset += l; return l; } void FileIOResource::Close( Error *e ) { if( !resourceData ) return; if( mode == FOM_WRITE ) { ByteCount size; // bytes to read from file char *buf = resourceData->Text(); if( resourceData->Length() < sizeof( FInfo ) ) { e->Set( MsgOs::EmptyFork ) << Name(); return; } OSErr err; MacFile * macFile = MacFile::GetFromPath( Name(), &err ); if ( !macFile ) { macFile = MacFile::CreateFromPath( Name(), false, &err ); } if ( !macFile ) { e->Sys("open", Name() ); return; } if ( macFile->IsDir() ) { e->Sys( "File not found for resource encoding!", Name() ); return; } if( macFile->OpenResourceFork( fsWrPerm ) != noErr ) { e->Sys( "Can't open resource fork of file %s.", Name() ); delete macFile; return; } FInfo info; size = sizeof(FInfo); memcpy( &info, buf, size ); MacFile::SwapFInfo( &info ); buf += size; // Write contents of resource fork. // Truncate it, too, because Mac is special. size = resourceData->Length() - sizeof( FInfo ); if ( macFile->WriteResourceFork( size, buf, &size ) != noErr ) e->Sys( "Error writing resource fork of %s.", Name() ); if ( macFile->CloseResourceFork() != noErr ) e->Sys( "Error closing resource fork of %s.", Name() ); // save FileInfo & close off file if ( macFile->SetFInfo( &info ) != noErr ) e->Sys( "Error writing Finder Info of %s.", Name() ); // Set readonly/rw as necessary Chmod( perms, e ); delete macFile; } delete resourceData; resourceData = 0; } void FileIOResource::Unlink( Error *e ) { // do nothing for the resource fork. } # endif /* OS_MACOSX */