/* Implements a simple AppleSingle decoder/encoder, as described in RFC1740 */
/* http://andrew2.andrew.cmu.edu/rfc/rfc1740.html */

#include "apsingle.h"

/* Mac headers */
#include <Finder.h>
#include <MacErrors.h>
#include <Resources.h>

/* stdc */
#include <stdio.h>
#include <string.h>

/* struct definitions from RFC1740 */
#if PRAGMA_STRUCT_ALIGN
#pragma options align=mac68k
#endif

typedef struct ASHeader /* header portion of AppleSingle */ 
{
    /* AppleSingle = 0x00051600; AppleDouble = 0x00051607 */
       UInt32 magicNum; /* internal file type tag */ 
       UInt32 versionNum; /* format version: 2 = 0x00020000 */ 
       UInt8 filler[16]; /* filler, currently all bits 0 */ 
       UInt16 numEntries; /* number of entries which follow */
} ASHeader ; /* ASHeader */ 

typedef struct ASEntry /* one AppleSingle entry descriptor */ 
{
	UInt32 entryID; /* entry type: see list, 0 invalid */
	UInt32 entryOffset; /* offset, in octets, from beginning */
	                          /* of file to this entry's data */
	UInt32 entryLength; /* length of data in octets */
} ASEntry; /* ASEntry */ 

typedef struct ASFinderInfo
{
	FInfo ioFlFndrInfo; /* PBGetFileInfo() or PBGetCatInfo() */
	FXInfo ioFlXFndrInfo; /* PBGetCatInfo() (HFS only) */
} ASFinderInfo; /* ASFinderInfo */

typedef struct ASMacInfo        /* entry ID 10, Macintosh file information */
{
       UInt8 filler[3]; /* filler, currently all bits 0 */ 
       UInt8 ioFlAttrib; /* PBGetFileInfo() or PBGetCatInfo() */
} ASMacInfo;

typedef struct ASFileDates      /* entry ID 8, file dates info */
{
	SInt32 create; /* file creation date/time */
	SInt32 modify; /* last modification date/time */
	SInt32 backup; /* last backup date/time */
	SInt32 access; /* last access date/time */
} ASFileDates; /* ASFileDates */


#if PRAGMA_STRUCT_ALIGN
#pragma options align=reset
#endif

/* Prototypes */
OSErr decodeFileDates( ASEntry inEntry, FILE * inFile, FSSpec * ioSpec );
OSErr decodeRealName( ASEntry inEntry, FILE * inFile, FSSpec * ioSpec );
OSErr encodeRealName(FILE * outfp, const FSSpec * inSpec );
OSErr encodeComment(FILE * outfp, Str255 comment );
OSErr encodeFileDates(FILE * outfp, const CInfoPBRec * pb);
OSErr encodeFinderInfo( FILE * outfp, const FSSpec * inSpec, const CInfoPBRec * pb);
OSErr encodeMacInfo( FILE * outfp, const CInfoPBRec * pb );
OSErr macFileToFileStream( FILE * outfp, SInt16 refNum, UInt32 bytesExpected); 
OSErr encodeDataFork( FILE * outfp, const FSSpec * inFile, UInt32 bytesExpected);
OSErr encodeResourceFork( FILE * outfp, const FSSpec * inFile, UInt32 bytesExpected);

/* asEntryToMacFile
 * Blasts the bytes specified in the entry to already opened Mac file
 */
static OSErr
asEntryToMacFile( ASEntry inEntry, FILE * inFile, UInt16 inRefNum)
{
#define BUFFER_SIZE 8192

	char buffer[BUFFER_SIZE];
	size_t totalRead = 0, bytesRead;
	long bytesToWrite;
	OSErr err;

	if ( fseek( inFile, inEntry.entryOffset, SEEK_SET) != 0 )
		return -1 ;

	while ( totalRead < inEntry.entryLength )
	{
// Should we yield in here?
		bytesRead = fread( buffer, 1, BUFFER_SIZE, inFile );
		if ( bytesRead <= 0 )
			return ioErr;
		bytesToWrite = totalRead + bytesRead > inEntry.entryLength ? 
									inEntry.entryLength - totalRead :
									bytesRead;

		totalRead += bytesRead;
		err = FSWrite(inRefNum, &bytesToWrite, buffer);
		if (err != noErr)
			return err;
	}
	return 0;	
}

static OSErr
decodeDataFork( ASEntry inEntry, FILE * inFile, const FSSpec * ioSpec )
{
	SInt16	refNum;
	OSErr err;
	
	/* Setup the files */
	err = FSpOpenDF (ioSpec, fsWrPerm,  &refNum);

	if ( err == noErr )
		err = asEntryToMacFile( inEntry, inFile, refNum );
	
	FSClose( refNum );
	return err;
}

static OSErr
decodeResourceFork( ASEntry inEntry, FILE * inFile, const FSSpec * ioSpec )
{
	SInt16	refNum;
	OSErr err;
	
	err = FSpOpenRF (ioSpec, fsWrPerm,  &refNum);

	if ( err == noErr )
		err = asEntryToMacFile( inEntry, inFile, refNum );
	
	FSClose( refNum );
	return err;
}

static OSErr
decodeComment( ASEntry inEntry, FILE * inFile, const FSSpec * ioSpec )
{
	Str255 newComment;
	if ( inEntry.entryLength > 32 )	/* Max file name length for the Mac */
		return -1;
	
	if ( fseek( inFile, inEntry.entryOffset, SEEK_SET) != 0 )
		return -1 ;

	if ( fread( &newComment[1], 1, inEntry.entryLength, inFile ) != inEntry.entryLength )
		return -1;
	newComment[0] = inEntry.entryLength;

	return FSpDTSetComment(ioSpec, newComment);
}

static OSErr
decodeFinderInfo( ASEntry inEntry, FILE * inFile, const FSSpec * ioSpec )
{
	ASFinderInfo info;
	OSErr err;
	Str31 name;
	CInfoPBRec pb;

	if (inEntry.entryLength != sizeof( ASFinderInfo ))
		return -1;

	if ( fseek( inFile, inEntry.entryOffset, SEEK_SET) != 0 )
		return -1 ;

	if ( fread( &info, 1, sizeof(info), inFile) != inEntry.entryLength )
		return -1;
	
	err = FSpSetFInfo(ioSpec, &info.ioFlFndrInfo);
	if ( err != noErr )
		return -1;

	memcpy(name, ioSpec->name, ioSpec->name[0] + 1);
	pb.hFileInfo.ioNamePtr = name;
	pb.hFileInfo.ioVRefNum = ioSpec->vRefNum;
	pb.hFileInfo.ioDirID = ioSpec->parID;
	pb.hFileInfo.ioFDirIndex = 0;	/* use ioNamePtr and ioDirID */
	err = PBGetCatInfoSync(&pb);
	if ( err != noErr )
		return err;
	
	pb.hFileInfo.ioNamePtr = name;
	pb.hFileInfo.ioVRefNum = ioSpec->vRefNum;
	pb.hFileInfo.ioDirID = ioSpec->parID;
	pb.hFileInfo.ioFDirIndex = 0;	/* use ioNamePtr and ioDirID */
	pb.hFileInfo.ioFlXFndrInfo = info.ioFlXFndrInfo;
	err = PBSetCatInfoSync(&pb);
	return err;
}

static OSErr
decodeMacInfo( ASEntry inEntry, FILE * inFile, const FSSpec * ioSpec )
{
	ASMacInfo info;
	OSErr err;
	Str31 name;
	CInfoPBRec pb;

	if (inEntry.entryLength != sizeof( ASMacInfo ))
		return -1;

	if ( fseek( inFile, inEntry.entryOffset, SEEK_SET) != 0 )
		return -1 ;

	if ( fread( &info, 1, sizeof(info), inFile) != inEntry.entryLength )
		return -1;
	
	memcpy(name, ioSpec->name, ioSpec->name[0] + 1);
	pb.hFileInfo.ioNamePtr = name;
	pb.hFileInfo.ioVRefNum = ioSpec->vRefNum;
	pb.hFileInfo.ioDirID = ioSpec->parID;
	pb.hFileInfo.ioFDirIndex = 0;	/* use ioNamePtr and ioDirID */
	err = PBGetCatInfoSync(&pb);
	if ( err != noErr )
		return -1;
	
	pb.hFileInfo.ioNamePtr = name;
	pb.hFileInfo.ioVRefNum = ioSpec->vRefNum;
	pb.hFileInfo.ioDirID = ioSpec->parID;
	pb.hFileInfo.ioFlAttrib = info.ioFlAttrib;
	err = PBSetCatInfoSync(&pb);
	return err;
}

OSErr 
decodeFileDates( ASEntry inEntry, FILE * inFile, FSSpec * ioSpec )
{
	ASFileDates dates;
	OSErr err;
	Str31 name;
	CInfoPBRec pb;

	if ( inEntry.entryLength != sizeof(dates) )	/* Max file name length for the Mac */
		return -1;

	if ( fseek( inFile, inEntry.entryOffset, SEEK_SET) != 0 )
		return -1 ;

	if ( fread( &dates, 1, inEntry.entryLength, inFile ) != inEntry.entryLength )
		return -1;

	memcpy(name, ioSpec->name, ioSpec->name[0] + 1);
	pb.hFileInfo.ioNamePtr = name;
	pb.hFileInfo.ioVRefNum = ioSpec->vRefNum;
	pb.hFileInfo.ioDirID = ioSpec->parID;
	pb.hFileInfo.ioFDirIndex = 0;	/* use ioNamePtr and ioDirID */
	err = PBGetCatInfoSync(&pb);
	if ( err != noErr )
		return err;
#define YR_2000_SECONDS 3029572800
	pb.hFileInfo.ioFlCrDat = dates.create + YR_2000_SECONDS;
	pb.hFileInfo.ioFlMdDat = dates.modify + YR_2000_SECONDS;
	pb.hFileInfo.ioFlBkDat = dates.backup + YR_2000_SECONDS;
	/* Not sure if mac has the last access time dates.access*/

	memcpy(name, ioSpec->name, ioSpec->name[0] + 1);
	pb.hFileInfo.ioNamePtr = name;
	pb.hFileInfo.ioVRefNum = ioSpec->vRefNum;
	pb.hFileInfo.ioDirID = ioSpec->parID;
	pb.hFileInfo.ioFDirIndex = 0;	/* use ioNamePtr and ioDirID */
	err = PBSetCatInfoSync(&pb);
	
	return err;
}

OSErr 
decodeRealName( ASEntry inEntry, FILE * inFile, FSSpec * ioSpec )
{
	Str255 newName;
	OSErr err;

	if ( inEntry.entryLength > 32 )	/* Max file name length for the Mac */
		return -1;
	
	if ( fseek( inFile, inEntry.entryOffset, SEEK_SET) != 0 )
		return -1 ;

	if ( fread( &newName[1], 1, inEntry.entryLength, inFile ) != inEntry.entryLength )
		return -1;

	newName[0] = inEntry.entryLength;
	err =  FSpRename(ioSpec, newName);

	if (err == noErr)
		memcpy( ioSpec->name, newName, 32 );
	return err;

}

static OSErr 
processASEntry( ASEntry inEntry, FILE * inFile, const FSSpec * ioSpec )
{
	switch (inEntry.entryID)
	{
		case AS_DATA:
			return decodeDataFork( inEntry, inFile, ioSpec );
			break;
		case AS_RESOURCE:
			return decodeResourceFork( inEntry, inFile, ioSpec );
			break;
		case AS_REALNAME:
//			return decodeRealName( inEntry, inFile, ioSpec );
			break;
		case AS_COMMENT:
			return decodeComment( inEntry, inFile, ioSpec );
			break;
		case AS_ICONBW:
//			return decodeIconBW( inEntry, inFile, ioSpec );
			break;
		case AS_ICONCOLOR:
//			return decodeIconColor( inEntry, inFile, ioSpec );
			break;
		case AS_FILEDATES:
//			return decodeFileDates( inEntry, inFile, ioSpec );
			break;
		case AS_FINDERINFO:
			return decodeFinderInfo( inEntry, inFile, ioSpec );
			break;
		case AS_MACINFO:
			return decodeMacInfo( inEntry, inFile, ioSpec );
			break;
		case AS_PRODOSINFO:
		case AS_MSDOSINFO:
		case AS_AFPNAME:
		case AS_AFPINFO:
		case AS_AFPDIRID:
		default:
			return 0;
	}
	return 0;
}

/* Decodes 
 * Arguments:
 * inFile - name of the AppleSingle file
 * outSpec - destination. If destination is renamed (as part of decoding of realName)
 * 			the outSpec is modified to represent the new name
 */
OSErr
decodeAppleSingle(const char * inFile, FSSpec * outSpec, long wantedEntries)
{
	FILE * in;
	size_t	bytesRead;
	ASHeader header;
	OSErr err;
	int i;

	in = fopen( inFile, "rb");
	if ( in == NULL )
		return fnfErr;
	/* Read in the header */
	{
		bytesRead = fread(&header, 1, sizeof(ASHeader),  in );
		if ( bytesRead != sizeof(ASHeader))
			goto fail;
		if ( header.magicNum != AS_MAGIC_NUM )
			goto fail;
		if ( header.versionNum != 0x00020000 )
			goto fail;
		if ( header.numEntries == 0 )	/* nothing in this file ? */
			goto fail;
	}
	/* Create the output file */
	FSpDelete( outSpec );	/* Preventive delete, not sure if we need it */
	err = FSpCreate( outSpec, AS_DEFAULT_CREATOR, AS_DEFAULT_TYPE, 0);
	if ( err != noErr )
		goto fail;
	
	/* Loop through the entries, processing each */
	/* Set the time/date stamps last, because otherwise they'll be destroyed
	   when we write */
	{
		Boolean hasDateEntry = false;
		ASEntry dateEntry;
		for ( i=0; i < header.numEntries; i++ )
		{
			ASEntry entry;
			size_t offset = sizeof( ASHeader ) + sizeof( ASEntry ) * i;
			if ( fseek( in, offset, SEEK_SET ) != 0 )
				goto fail;
			if ( fread( &entry, 1, sizeof( entry ), in ) != sizeof( entry ))
				goto fail;
			if ( wantedEntries & ( ((UInt32)1) << (entry.entryID - 1 )))
				switch (entry.entryID)
				{
					case AS_DATA:
						err = decodeDataFork( entry, in, outSpec );
						break;
					case AS_RESOURCE:
						err = decodeResourceFork( entry, in, outSpec );
						break;
					case AS_REALNAME:
						err = decodeRealName( entry, in, outSpec );
						break;
					case AS_COMMENT:
						err = decodeComment( entry, in, outSpec );
						break;
					case AS_FILEDATES:
	 				/* Save it for postprocessing */
		 				hasDateEntry = true;
		 				dateEntry = entry;
						break;
					case AS_FINDERINFO:
						err = decodeFinderInfo( entry, in, outSpec );
						break;
					case AS_MACINFO:
						err = decodeMacInfo( entry, in, outSpec );
						break;
					case AS_ICONBW:
					case AS_ICONCOLOR:
						fprintf(stderr, "Can't decode AS_ICONBW...");
						break;
					case AS_PRODOSINFO:
					case AS_MSDOSINFO:
					case AS_AFPNAME:
					case AS_AFPINFO:
					case AS_AFPDIRID:
					default:
						break;
				}
			if ( err != 0)
				break;
		}
		if ( hasDateEntry )
			err = processASEntry( dateEntry, in, outSpec );
	}
	fclose(in);
	in = NULL;

	if ( err == noErr )
		return err;
	// else fall through failure
fail:
	if (in)
		fclose(in);
	FSpDelete( outSpec);
	printf("AppleSingle decoding has failed: %s");		
	return ioErr;
}

OSErr
encodeRealName(FILE * outfp, const FSSpec * inSpec )
{
	if ( fwrite( &(inSpec->name[1]), 1, inSpec->name[0], outfp) < 0 )
		return ioErr;
	return noErr;
}

OSErr
encodeComment(FILE * outfp, Str255 comment )
{
	if ( fwrite( &(comment[1]), 1, comment[0], outfp) < 0 )
		return ioErr;
	return noErr;
}

OSErr
encodeFileDates(FILE * outfp, const CInfoPBRec * pb)
{
	ASFileDates dates;
#define YR_2000_SECONDS 3029572800
	dates.create = pb->hFileInfo.ioFlCrDat - YR_2000_SECONDS;
	dates.modify = pb->hFileInfo.ioFlMdDat - YR_2000_SECONDS;
	dates.backup = pb->hFileInfo.ioFlBkDat - YR_2000_SECONDS;
	dates.access = 0;	/* Unknown on the mac */
	if ( fwrite( &dates, 1, sizeof(dates), outfp) < 0 )
		return ioErr;
	return noErr;
}

OSErr
encodeFinderInfo( FILE * outfp, const FSSpec * inSpec, const CInfoPBRec * pb)
{
	ASFinderInfo info;
	OSErr err;
	err = FSpGetFInfo(inSpec, &info.ioFlFndrInfo);
	if (err != noErr )
		return err;
	info.ioFlXFndrInfo = pb->hFileInfo.ioFlXFndrInfo;
	if ( fwrite( &info, 1, sizeof(info), outfp) < 0 )
		return ioErr;
	return noErr;
}

OSErr
encodeMacInfo( FILE * outfp, const CInfoPBRec * pb )
{
	ASMacInfo info;
	memset( &info, 0, sizeof(info));
	info.ioFlAttrib = pb->hFileInfo.ioFlAttrib;
	if ( fwrite( &info, 1, sizeof(info), outfp) < 0 )
		return ioErr;
	return noErr;
}

OSErr
macFileToFileStream( FILE * outfp, SInt16 refNum, UInt32 bytesExpected)
{
#define BUFFER_SIZE 8192

	char buffer[BUFFER_SIZE];
	UInt32 totalRead = 0;
	SInt32 currentRead;
	OSErr err;
	while ( totalRead < bytesExpected )
	{
		currentRead = BUFFER_SIZE;
		err = FSRead( refNum, &currentRead, buffer);
		totalRead += currentRead;
		if ( err != noErr && ( totalRead < bytesExpected))
			return err;
		if ( fwrite( buffer, 1, currentRead, outfp) < 0)
			return -1;
	}
	return noErr;
}

OSErr
encodeDataFork( FILE * outfp, const FSSpec * inFile, UInt32 bytesExpected)
{
	short refNum;
	OSErr err = noErr;
	err = FSpOpenDF (inFile, fsRdPerm,  &refNum);
	if (err != noErr )
		return err;
	err = macFileToFileStream( outfp, refNum, bytesExpected );
	FSClose( refNum );
	return err;
}

OSErr
encodeResourceFork( FILE * outfp, const FSSpec * inFile, UInt32 bytesExpected)
{
	short refNum;
	OSErr err = noErr;
	err = FSpOpenRF (inFile, fsRdPerm,  &refNum);
	if (err != noErr )
		return err;
	err = macFileToFileStream( outfp, refNum, bytesExpected );
	FSClose( refNum );
	return err;
}

/* Encodes the file as applesingle
 *
 * These are the possible parts that can be encoded as AppleSingle:
      Data Fork              1 Data fork
      Resource Fork          2 Resource fork
      Real Name              3 File's name as created on home file system
      Comment                4 Standard Macintosh comment
      File Dates Info        8 File creation date, modification date,
                                      and so on
      Finder Info            9 Standard Macintosh Finder information
      Macintosh File Info   10 Macintosh file information, attributes  and so on
  * This routine will encode all parts that are relevant (ex no data fork encoding if data fork length is 0
  */
OSErr
encodeAppleSingle(const FSSpec * inSpec, const char * outFile, long wantedEntries)
{
	OSErr err;
	CInfoPBRec cbrec;
	FInfo fileinfo;
	Boolean needDataFork, needResourceFork, needRealName, needComment, needFileDates, needFinderInfo, needMacInfo;
	ASHeader header;
	ASEntry entry;
	UInt16 numEntries;
	FILE * outfp;
	Str255 comment;
	size_t availableOffset;
	Str255 temp;
	needDataFork = needResourceFork = needRealName = needComment
	= needFileDates = needFinderInfo = needMacInfo = false;
	
	/* Figure out which parts of will we need to encode */
	
	memcpy( temp, inSpec->name, inSpec->name[0] + 1);
	cbrec.hFileInfo.ioNamePtr =	temp;
	cbrec.hFileInfo.ioDirID	= inSpec->parID;
	cbrec.hFileInfo.ioVRefNum = inSpec->vRefNum;
	cbrec.hFileInfo.ioFDirIndex	= 0;

	err = PBGetCatInfoSync(&cbrec);
	if(err != noErr)
		goto fail;
	
	if ( FSpDTGetComment( inSpec,comment) != noErr)
		comment[0] = 0;

	needDataFork = (cbrec.hFileInfo.ioFlLgLen > 0) 
					&& ( AS_DATA_BIT & wantedEntries) ; /* Data fork? */
	needResourceFork = (cbrec.hFileInfo.ioFlRLgLen > 0) 
					&& ( AS_RESOURCE_BIT & wantedEntries ); /* Resource fork? */
	needComment = comment[0] != 0
					&& ( AS_COMMENT_BIT & wantedEntries );
	needRealName = (AS_REALNAME_BIT & wantedEntries) != 0;
	needFileDates = ( AS_FILEDATES_BIT & wantedEntries) != 0;
	needFinderInfo = ( AS_FINDERINFO_BIT & wantedEntries) != 0;
	needMacInfo = ( AS_MACINFO_BIT & wantedEntries) != 0;

	/* The header */
	memset(&header, 0, sizeof(ASHeader));	/* for the filler bits */
	header.magicNum = AS_MAGIC_NUM;
	header.versionNum = 0x00020000;
	numEntries = 0;
	if ( needDataFork ) numEntries++;
	if ( needResourceFork ) numEntries++;
	if ( needRealName ) numEntries++;
	if ( needComment ) numEntries++;
	if ( needFileDates ) numEntries++;
	if ( needFinderInfo ) numEntries++;
	if ( needMacInfo ) numEntries++;
	header.numEntries = numEntries;
	
	outfp = fopen(outFile, "wb");
	if ( outfp == NULL)
		goto fail;
	
	/* write header */
	if ( fwrite( &header, 1, sizeof(ASHeader), outfp) < 0 )
		goto fail;

	/* write out the entry headers */
	availableOffset = sizeof(ASHeader) + numEntries * sizeof(ASEntry);
	
	if ( needRealName )
	{
		entry.entryID = AS_REALNAME;
		entry.entryOffset = availableOffset;
		entry.entryLength = inSpec->name[0];
		if ( fwrite( &entry, 1, sizeof(ASEntry), outfp) < 0 )
			goto fail;
		availableOffset += entry.entryLength;
	}
	if ( needComment )
	{
		entry.entryID = AS_COMMENT;
		entry.entryOffset = availableOffset;
		entry.entryLength = comment[0];
		if ( fwrite( &entry, 1, sizeof(ASEntry), outfp) < 0 )
			goto fail;
		availableOffset += entry.entryLength;
	}
	if ( needFileDates )
	{
		entry.entryID = AS_FILEDATES;
		entry.entryOffset = availableOffset;
		entry.entryLength = sizeof (ASFileDates );
		if ( fwrite( &entry, 1, sizeof(ASEntry), outfp) < 0 )
			goto fail;
		availableOffset += entry.entryLength;
	}
	if ( needFinderInfo )
	{
		entry.entryID = AS_FINDERINFO;
		entry.entryOffset = availableOffset;
		entry.entryLength = sizeof (ASFinderInfo );
		if ( fwrite( &entry, 1, sizeof(ASEntry), outfp) < 0 )
			goto fail;
		availableOffset += entry.entryLength;
	}
	if ( needMacInfo )
	{
		entry.entryID = AS_MACINFO;
		entry.entryOffset = availableOffset;
		entry.entryLength = sizeof (ASMacInfo );
		if ( fwrite( &entry, 1, sizeof(ASEntry), outfp) < 0 )
			goto fail;
		availableOffset += entry.entryLength;
	}
	if ( needDataFork )
	{
		entry.entryID = AS_DATA;
		entry.entryOffset = availableOffset;
		entry.entryLength = cbrec.hFileInfo.ioFlLgLen;
		if ( fwrite( &entry, 1, sizeof(ASEntry), outfp) < 0 )
			goto fail;
		availableOffset += entry.entryLength;
	}
	if ( needResourceFork )
	{
		entry.entryID = AS_RESOURCE;
		entry.entryOffset = availableOffset;
		entry.entryLength = cbrec.hFileInfo.ioFlRLgLen;
		if ( fwrite( &entry, 1, sizeof(ASEntry), outfp) < 0 )
			goto fail;
		availableOffset += entry.entryLength;
	}

	/* write out the entry data */
	if ( needRealName )
		if ( encodeRealName(outfp, inSpec) != noErr )
			goto fail;
	if ( needComment )
		if ( encodeComment(outfp, comment) != noErr )
			goto fail;
	if ( needFileDates )
		if ( encodeFileDates(outfp, &cbrec) != noErr )
			goto fail;
	if ( needFinderInfo )
		if ( encodeFinderInfo(outfp, inSpec, &cbrec) != noErr )
			goto fail;
	if ( needMacInfo )
		if ( encodeMacInfo( outfp, &cbrec ) != noErr )
			goto fail;
	if ( needDataFork )
		if ( encodeDataFork( outfp, inSpec, cbrec.hFileInfo.ioFlLgLen) != noErr )
			goto fail;
	if ( needResourceFork )
		if ( encodeResourceFork( outfp, inSpec, cbrec.hFileInfo.ioFlRLgLen ) != noErr )
			goto fail;

	fclose(outfp);
	outfp = NULL;	
	/* All done! */
	return noErr;

fail:
	if ( outfp )
		fclose(outfp);
	remove( outFile);
	if (err == noErr)
		err = ioErr;
	fprintf(stderr, "Unexpected AppleSingle encoding error %s\n", outFile );
	return err;
}