/* * 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 "filesys.h" # include "fileio.h" # include "pathsys.h" # include "applefork.h" # if !defined ( OS_MACOSX ) /* * FileIOApple -- AppleSingle/Double on non-mac platforms */ /* * DataFork -- write the data fork for AppleSplit */ class DataFork : public AppleFork { public: DataFork( FileIO *f ) { file = f; } // 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 ); } private: FileIO *file; // Just a pointer back to the original } ; /* * FileIOApple */ FileIOApple::FileIOApple() { split = new AppleForkSplit; combine = new AppleForkCombine; data = new FileIOBinary; header = new FileIOBinary; dataFork = 0; } FileIOApple::~FileIOApple() { Cleanup(); delete split; delete combine; delete data; delete header; delete dataFork; } void FileIOApple::Set( const StrPtr &s ) { Set( s, 0 ); } void FileIOApple::Set( const StrPtr &s, Error *e ) { // Our name FileIO::Set( s, e ); // Data fork name data->Set( s, e ); // Make %file name StrBuf file; PathSys *p = PathSys::Create(); p->Set( s ); p->ToParent( &file ); p->SetLocal( *p, StrRef( "%", 1 ) ); p->Append( &file ); header->Set( *p, e ); delete p; } // Just a set of wrappers to get both the header and data forks int FileIOApple::Stat() { // Exists if either exists. // Writeable if either writable. return header->Stat() | data->Stat(); } int FileIOApple::StatModTime() { // Return later of the two. int h = header->StatModTime(); int d = data->StatModTime(); return h > d ? h : d; } void FileIOApple::Truncate( offL_t offset, Error *e ) { } void FileIOApple::Truncate( Error *e ) { // XXX Not really thought out. // Only the server uses truncate. header->Truncate( e ); data->Truncate( e ); } bool FileIOApple::HasOnlyPerm( FilePerm perms ) { return ( (header->HasOnlyPerm(perms)) && (data->HasOnlyPerm(perms)) ); } void FileIOApple::Chmod( FilePerm perms, Error *e ) { header->Chmod( perms, e ); data->Chmod( perms, e ); } void FileIOApple::ChmodTime( int modTime, Error *e ) { header->ChmodTime( modTime, e ); data->ChmodTime( modTime, e ); } void FileIOApple::Unlink( Error *e ) { header->Unlink( e ); data->Unlink( e ); } void FileIOApple::Rename( FileSys *target, Error *e ) { FileIOApple *apple = 0; if ( !( target->GetType() & FST_M_APPLE ) ) { // Target isn't a FileIOApple? // Make one so that we can write both forks. apple = new FileIOApple; apple->Set( StrRef( target->Name() ), e ); target = apple; } header->Rename( ((FileIOApple *)target)->header, e ); data->Rename( ((FileIOApple *)target)->data, e ); // file deleted, clear the flag ClearDeleteOnClose(); delete apple; } /* * FileIOApple::Open * FileIOApple::Write * FileIOApple::Read * FileIOApple::Close * * The meat of the matter. * * For FOM_READ, the work is done by Open(). * For FOM_WRITE, Reading: FileIOBinary(%file) -> AppleForkSplit -> AppleForkCombine FileIOBinary(file) -------------------/ Writing: AppleForkSplit -> AppleForkCombine -> FileIOBinary(%file) \--------------------> FileIOBinary(file) */ void FileIOApple::Open( FileOpenMode mode, Error *e ) { /* * For read, we do all the work in the Open. * Build a AppleForkSplit -> AppleForkCombine pipeline. * Send the AppleDouble %file down the splitter. * Send the data fork directly to the combiner. */ this->mode = mode; if( mode == FOM_READ ) { int l; StrFixed buf( BufferSize() ); /* * Build the splitter -> combiner relationship. * Only one handler: to build the AppleSingle stream. */ split->AddHandler( combine ); /* * Read the AppleDouble %file */ header->Open( FOM_READ, e ); if( e->Test() ) { e->Set( E_FAILED, "Unable to read AppleDouble Header." ); return; } while( !e->Test() && ( l = header->Read( buf.Text(), buf.Length(), e ) ) ) split->Write( buf.Text(), l, e ); split->Done( e ); header->Close( e ); if( e->Test() ) return; /* * Already a datafork? Don't read user's data fork then. */ if( combine->IsAppleSingle() ) return; /* * Read the data portion and write it down the combiner. */ data->Open( FOM_READ, e ); if( e->Test() ) { e->Set( E_FAILED, "Unable to read AppleDouble Data." ); return; } combine->WriteOpen( 1, e ); while( !e->Test() && ( l = data->Read( buf.Text(), buf.Length(), e ) ) ) combine->Write( buf.Text(), l, e ); combine->WriteClose( e ); data->Close( e ); if( e->Test() ) return; } if( mode == FOM_WRITE ) { dataFork = new DataFork( data ); 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_WRITE ) { /* * Prevent double closure */ mode = FOM_READ; /* * Write out the AppleDouble Header file */ int l; StrFixed buf( BufferSize() ); header->Open( FOM_WRITE, e ); if( e->Test() ) { e->Set( E_FAILED, "Unable to write AppleDouble Header." ); return; } while( !e->Test() && ( l = combine->Read( buf.Text(), buf.Length(), e ) ) ) header->Write( buf.Text(), l, e ); split->Done( e ); header->Close( e ); } } # endif /* OS_MACOSX */