/* * Copyright 1995, 1996 Perforce Software. All rights reserved. * * This file is part of Perforce - the FAST SCM System. */ # include <stdhdrs.h> # include <error.h> # include <strbuf.h> # include <datetime.h> # if defined ( OS_MACOSX ) # include "i18napi.h" # include "macfile.h" # include "filesys.h" # include "fileio.h" # include "applefork.h" /* * FileIOApple -- AppleSingle/Double on the mac * * This implementation does AppleSingle decoding (when writing the * local file) and encoding (when reading the local file). It only * implements the Data, Resource, and FInfo components of the * AppleSingle file. */ class ReadableAppleFork : public AppleFork { public: virtual void ReadOpen( Error *e ) = 0; virtual int Read( char *buf, int length, Error *e ) = 0; virtual void ReadClose( Error *e ) = 0; // to write a whole applefork void Copy( EntryId id, AppleFork *target, Error *e ) { StrFixed buf( FileSys::BufferSize() ); int l; ReadOpen( e ); if( e->Test() ) return; target->WriteOpen( id, e ); while( !e->Test() && ( l = Read( buf.Text(), buf.Length(), e ) ) ) target->Write( buf.Text(), l, e ); target->WriteClose( e ); ReadClose( e ); } } ; class BufferAppleFork : public AppleFork, public StrBuf { public: // AppleFork Virtuals void WriteOpen( EntryId id, Error *e ) { Clear(); } void Write( const char *buf, int length, Error *e ) { Append( buf, length ); } void WriteClose( Error *e ) { } // to write a whole applefork void Copy( EntryId id, AppleFork *target, Error *e ) { target->WriteOpen( id, e ); if( e->Test() ) return; target->Write( Text(), Length(), e ); target->WriteClose( e ); } } ; /* * DataFork -- write the data fork for AppleSplit */ class DataFork : public ReadableAppleFork { public: DataFork( char *name ) { /* * The data fork is left RW, so that the Close() can write the * other data. Our Close() resets the perms. */ file.Set( name ); file.Perms( FPM_RW ); } // AppleFork Virtuals int WillHandle( EntryId id ) { return id == EntryIdData; } void WriteOpen( EntryId id, Error *e ) { file.Open( FOM_WRITE, e ); } void Write( const char *buf, int length, Error *e ) { file.Write( buf, length, e ); } void WriteClose( Error *e ) { file.Close( e ); } void ReadOpen( Error *e ) { file.Open( FOM_READ, e ); } int Read( char *buf, int l, Error *e ) { return file.Read( buf, l, e ); } void ReadClose( Error *e ) { file.Close( e ); } private: FileIOBinary file; // Just a pointer back to the original } ; class ResourceFork : public ReadableAppleFork { public: ResourceFork( MacFile * mf ) { this->mf = mf; } // AppleFork Virtuals int WillHandle( EntryId id ) { return id == EntryIdResource; } void WriteOpen( EntryId id, Error *e ) { if( mf->OpenResourceFork( fsWrPerm ) != noErr ) e->Set( E_FAILED, "Can't write resource fork of file %path%." ) << mf->GetPrintableFullPath(); len = 0; } void Write( const char *buf, int length, Error *e ) { ByteCount l = length; if( mf->WriteResourceFork( l, (const void *)buf, &l ) != noErr ) e->Set( E_FAILED, "Error writing resource fork of %path%." ) << mf->GetPrintableFullPath(); len += length; } void WriteClose( Error *e ) { if( !e->Test() ) mf->CloseResourceFork(); } void ReadOpen( Error *e ) { if( mf->OpenResourceFork( fsRdPerm ) != noErr ) { e->Set( E_FAILED, "Can't read resource fork of file %path%." ) << mf->GetPrintableFullPath(); return; } len = mf->GetResourceForkSize(); } int Read( char *buf, int length, Error *e ) { ByteCount l = len < length ? len : length; if( l && mf->ReadResourceFork( l, (void *)buf, &l ) != noErr ) { e->Set( E_FAILED, "Error reading resource fork of %path%." ) << mf->GetPrintableFullPath(); return 0; } len -= l; return l; } void ReadClose( Error *e ) { mf->CloseResourceFork(); } private: MacFile * mf; short rsrcRef; long len; } ; class FinfoFork : public BufferAppleFork { public: // AppleFork Virtuals int WillHandle( EntryId id ) { return id == EntryIdFinderInfo; } // Misc void Load( const FInfo * fInfo, const FXInfo * fxInfo ) { Clear(); // We can't have the files differ just because they are locked, // so we temporarily zero the fdXflags word. This is "reserved", // but appears to be where the "locked" flag is. FXInfo s = *fxInfo; s.fdXFlags = 0; FInfo i = *fInfo; MacFile::SwapFInfo( &i ); MacFile::SwapFXInfo( &s ); Extend( (char *)&i, sizeof( FInfo ) ); Extend( (char *)&s, sizeof( FXInfo ) ); } void Save( FInfo * fInfo, FXInfo * fxInfo, Error *e ) { if( Length() != sizeof( FInfo ) + sizeof( FXInfo ) ) { e->Set( E_FAILED, "Malformed FInfo/FXInfo structure! File not updated." ); return; } memcpy( fInfo, Text(), sizeof( FInfo ) ); memcpy( fxInfo, Text() + sizeof( FInfo ), sizeof( FXInfo ) ); MacFile::SwapFInfo( fInfo ); MacFile::SwapFXInfo( fxInfo ); } } ; class CommentFork : public BufferAppleFork { public: // AppleFork Virtuals int WillHandle( EntryId id ) { return id == EntryIdComment; } // Misc void Load( const MacFile * mf, Error *e ) { Clear(); int actualLength = 0; const char * comment = mf->GetComment( &actualLength ); Extend( comment, actualLength ); SetLength( actualLength ); } void Save( MacFile * mf, Error *e ) { if( mf->SetComment( Text(), Length() ) != noErr ) { e->Set( E_FAILED, "Unable to set comment!" ); return; } } } ; /* * FileIOApple */ FileIOApple::FileIOApple() { split = 0; combine = 0; dataFork = 0; resourceFork = 0; finfoFork = 0; commentFork = 0; } FileIOApple::~FileIOApple() { Cleanup(); delete split; delete combine; delete dataFork; delete resourceFork; delete finfoFork; delete commentFork; } /* * FileIOApple::Open * FileIOApple::Write * FileIOApple::Read * FileIOApple::Close * * The meat of the matter. */ void FileIOApple::Open( FileOpenMode mode, Error *e ) { this->mode = mode; MacFile * macfile = GetMacFile( e ); if( mode == FOM_READ ) { if( !macfile ) { e->Set( E_FAILED, "Can't find file %path%." ) << Name(); return; } /* * For read, we do all the work in the Open. * Build a AppleForkCombine and put the finder info, * the resource fork, and the data fork into it. */ combine = new AppleForkCombine; // Finfo block. FinfoFork finfoFork; finfoFork.Load( macfile->GetFInfo(), macfile->GetFXInfo() ); finfoFork.Copy( EntryIdFinderInfo, combine, e ); if( e->Test() ) return; // Comment block. CommentFork commentFork; commentFork.Load( macfile, e ); commentFork.Copy( EntryIdComment, combine, e ); if( e->Test() ) return; // Resource fork. ResourceFork resourceFork( macfile ); resourceFork.Copy( EntryIdResource, combine, e ); if( e->Test() ) return; // Data fork. DataFork datafork( Name() ); datafork.Copy( EntryIdData, combine, e ); if( e->Test() ) return; } else if( mode == FOM_WRITE ) { /* * Create the file if needed. Unless the data fork is * written first (which it isn't), nothing else knows to * create the file. */ if( !macfile ) { e->Clear(); macfile = CreateMacFile( e ); if( e->Test() ) return; } /* * We need to split the data into three parts (finfo, resource * fork, data fork). The data and resource forks get written directly, * but the finfo and resource get buffered for Close(). */ finfoFork = new FinfoFork; resourceFork = new ResourceFork( macfile ); commentFork = new CommentFork; dataFork = new DataFork( Name() ); split = new AppleForkSplit; split->AddHandler( finfoFork ); split->AddHandler( resourceFork ); split->AddHandler( commentFork ); split->AddHandler( dataFork ); split->AddHandler( combine ); } } void FileIOApple::Write( const char *buf, int len, Error *e ) { split->Write( buf, len, e ); } int FileIOApple::Read( char *buf, int len, Error *e ) { return combine->Read( buf, len, e ); } void FileIOApple::Close( Error *e ) { if( mode == FOM_READ ) { /* * Clear for possible reuse. */ delete combine; combine = 0; } else if( mode == FOM_WRITE ) { /* * Prevent double closure */ mode = FOM_READ; /* * No mangled data? */ if( split ) { split->Done( e ); delete split; split = 0; } else { e->Set( E_FAILED, "No AppleForkSplit for %path%." ) << Name(); } if( e->Test() ) return; /* * The data fork has been written. Now we just save * off the resource and finfo pieces. */ MacFile * macfile = GetMacFile( e ); if( !macfile ) { e->Set( E_FAILED, "Can't get file info block for %path%." ) << Name(); return; } FInfo tempInfo; FXInfo tempxInfo; finfoFork->Save( &tempInfo, &tempxInfo, e ); macfile->SetFInfo( &tempInfo ); macfile->SetFXInfo( &tempxInfo ); commentFork->Save( macfile, e ); /* * Now finalize the perms. */ if( modTime ) ChmodTime( modTime, e ); Chmod( perms, e ); } } int FileIOApple::HasResourceFork() { Error e; MacFile * macfile = GetMacFile( &e ); if ( !macfile ) return 0; return macfile->HasResourceFork(); } # endif /* OS_MACOSX */