/*
* Copyright 1995, 1996 Perforce Software. All rights reserved.
*
* This file is part of Perforce - the FAST SCM System.
*/
/*
* appleforks.h - build/split an AppleSingle/Double stream
*
* These classes provide the pieces for manipulating AppleSingle and
* AppleDouble format files. Nothing in this interface or implementation
* is Macintosh specific, save the rediculous file format.
*
* We refer to each piece of a AppleSingle file as a fork, but really
* only the resource and data portions are forks. Unfortunately, Apple
* didn't really name the pieces except as "entries".
*
* To combine forks into a single stream:
*
* AppleForkCombine combine;
*
* for( <each fork> )
* {
* combine.WriteOpen( id, e );
* // can repeat next line
* combine.Write( buf, len, e );
* combine.WriteClose( e );
* }
*
* // read the combined data
*
* while( len = combine.Read( buf, sizeof( buf ), &e )
* ; // have data
*
* To split an AppleSingle stream:
*
* // Assume we want to handle two forks and combine the
* // rest into another AppleSingle stream.
*
* Special1AppleFork special1;
* Special2AppleFork special2;
* AppleForkCombine combine;
* AppleForkSplit split;
* Error e;
*
* split.AddHandler( &special1 );
* split.AddHandler( &special2 );
* split.AddHandler( &combine );
*
* // Write the AppleSingle stream into the splitter
* // special1 and special2 will be called for their pieces.
* // combine will get the rest.
*
* while( <data> )
* split.Write( buf, len, &e );
* split.Done( &e );
*
* // just for fun, read the combined data
*
* while( len = combine.Read( buf, sizeof( buf ), &e )
* ; // have data
*
* Note that because of the layout of the AppleSingle data (with Offs
* embedded in the header), AppleForkCombine must buffer its results before
* it can make them available via Read(). It will resort to a temp
* file if it buffers more than 100K.
*
* See applefile.h for the low-down on AppleSingle format. We don't
* use it ourselves. rfc1740 describes the format as well.
*
* Classes defined:
*
* AppleData - just a StrBuf with calls to access big-endian ints
*
* AppleFork - a handle for dispatching a single fork
* AppleForkSplit - process a stream, calling AppleForks for each fork
* AppleForkCombine - build a stream (subclass of AppleFork)
*
* Public methods:
*
* AppleFork::WillHandle() - return 1 if this handler will accept
* this entry type. AppleForkSplit calls this for each
* AppleFork handler registered with it until it finds a
* willing handler.
*
* AppleFork::Open/Write/Close() - write out fork data. AppleForkSplit
* calls these (if WillHandle() has returned 1) to dispose
* of the fork's data.
*
* AppleForkSplit::AddHandler() - register a AppleFork handler. The
* last handler should accept any entry types.
*
* AppleForkSplit::Write() - dispose of AppleSingle data. Write()
* buffers the AppleSingle header and then starts calling
* AppleFork handlers to dispose of the actual data.
*
* AppleForkSplit::Done() - called after the last Write() to verify
* that AppleForkSplit is actually done (it is a data error
* if not).
*
* AppleForkCombine::WillHandle() - returns 1, indicating that it
* (a AppleFork handler) will accept any type of fork.
*
* AppleForkCombine::Open/Write/Close() - consumes individual fork
* data, buffering the result.
*
* AppleForkCombine::Read() - return AppleSingle/Double data from the
* combined forks. The magic header is set to AppleSingle
* if a data fork is included; if not, the type is set to
* AppleDouble (assuming the data fork lives elsewhere).
*/
typedef unsigned int EntryId;
const int SizeHeader = 26;
const int SizeEntry = 12;
const int MagicAppleSingle = 0x00051600;
const int MagicAppleDouble = 0x00051607;
const int MagicVersion = 0x00020000;
const int EntryIdData = 1;
const int EntryIdResource = 2;
const int EntryIdRealname = 3;
const int EntryIdComment = 4;
const int EntryIdFinderInfo = 9;
// Offsets into header, Entry
const int OffHeaderMagic = 0;
const int OffHeaderVersion = 4;
const int OffHeaderNumEntries = 24;
const int OffEntryId = 0;
const int OffEntryOff = 4;
const int OffEntryLength = 8;
class AppleData : public StrBuf {
public:
int GetMagic() { return Get32( OffHeaderMagic ); }
int GetVersion() { return Get32( OffHeaderVersion ); }
int GetNumEntries() { return Get16( OffHeaderNumEntries ); }
int GetEntryId( int x ) { return Get32( x, OffEntryId ); }
int GetEntryLen( int x ) { return Get32( x, OffEntryLength ); }
void SetMagic( int v ) { Set32( OffHeaderMagic, v ); }
void SetVersion( int v ) { Set32( OffHeaderVersion, v ); }
void SetNumEntries( int v ) { Set16( OffHeaderNumEntries, v ); }
void SetEntryId( int x, int v ) { Set32( x, OffEntryId, v ); }
void SetEntryOff( int x, int v ) { Set32( x, OffEntryOff, v ); }
void SetEntryLen( int x, int v ) { Set32( x, OffEntryLength, v ); }
void AllocHeader()
{
Alloc( SizeHeader );
memset( Text(), 0, SizeHeader );
SetMagic( MagicAppleDouble );
SetVersion( MagicVersion );
SetNumEntries( 0 );
}
void AllocEntry( int x, EntryId id )
{
Alloc( SizeEntry );
SetEntryId( x, id );
SetEntryOff( x, 0 );
SetEntryLen( x, 0 );
}
private:
int Get32( int x, int o )
{
return Get32( SizeHeader + x * SizeEntry + o );
}
void Set32( int x, int o, int v )
{
Set32( SizeHeader + x * SizeEntry + o, v );
}
int Get32( int off )
{
return
C( off + 0 ) * 0x1000000 +
C( off + 1 ) * 0x10000 +
C( off + 2 ) * 0x100 +
C( off + 3 ) * 0x1;
}
int Get16( int off )
{
return C( off + 0 ) * 0x100 + C( off + 1 ) * 0x1;
}
void Set32( int off, int val )
{
S( off + 0 ) = ( val / 0x1000000 ) % 0x100;
S( off + 1 ) = ( val / 0x10000 ) % 0x100;
S( off + 2 ) = ( val / 0x100 ) % 0x100;
S( off + 3 ) = ( val / 0x1 ) % 0x100;
}
void Set16( int off, int val )
{
S( off + 0 ) = ( val / 0x100 ) % 0x100;
S( off + 1 ) = ( val / 0x1 ) % 0x100;
}
char & S(int x) { return Text()[x]; }
unsigned char C(int x) { return UText()[x]; }
} ;
class AppleFork {
public:
virtual ~AppleFork();
virtual int WillHandle( EntryId id ) = 0;
virtual void WriteOpen( EntryId id, Error *e ) = 0;
virtual void Write( const char *buf, int length, Error *e ) = 0;
virtual void WriteClose( Error *e ) = 0;
} ;
class AppleForkSplit {
public:
AppleForkSplit();
void AddHandler( AppleFork *handler );
void Write( const char *buf, int length, Error *e );
void Done( Error *e );
private:
AppleFork *handler[ 5 ];
int numHandlers;
AppleData header;
int needed;
int numEntries;
int currentEntry;
AppleFork *currentHandler;
enum State {
BeforeEndOfHeader, // filling header
BeforeEndOfIndex, // filling entry index
StartingFork, // looking to fill a fork
InFork // filling a fork
} state;
} ;
class AppleForkCombine : public AppleFork {
public:
AppleForkCombine();
~AppleForkCombine();
// from AppleFork
virtual int WillHandle( EntryId id );
virtual void WriteOpen( EntryId id, Error *e );
virtual void Write( const char *buf, int length, Error *e );
virtual void WriteClose( Error *e );
// to read the results
int Read( char *buf, int length, Error *e );
int IsAppleSingle() { return isSingle; }
private:
AppleData header; // apple header and entries
StrBuf data; // fork contents
int numEntries; // number of entries in header
int dataLength; // track per-fork length
int isSingle; // has data fork
class FileSys *dataBack; // for big data
enum State {
Start, // initialize for Read()
Header, // Read() from header
Data, // Read() from data
Done // Read() returns 0
} state ;
} ;