/*
* 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 )
{
//
// The MacFile will no longer refer to this new name,
// so delete it.
//
delete mf;
mf = 0;
FileSys::Set( name );
}
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 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 )
{
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 */