/*
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 1, or (at your option)
** any later version.

** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.

** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

/*
 * Author : Alexandre Parenteau <aubonbeurre@hotmail.com> --- December 1997
 */

#include "config.h"
#include "system.h"

extern "C" {
#include "error.h"
}

#if TARGET_RT_MAC_CFM
#include <GUSIInternal.h>
#include <GUSIFileSpec.h>
#endif

#ifdef __GNUC__
#include <Carbon/Carbon.h>
#endif

#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>
#include "hqx.h"
#include "cvs_hqx.h"
#include "apsingle.h"

#if TARGET_RT_MAC_CFM
#include "FileCopy.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifndef __GNUC__
#if TARGET_RT_MAC_MACHO
#include <CarbonCore/PLStringFuncs.h>
#include <CarbonCore/Resources.h>
#include <CarbonCore/TextUtils.h>
#include <CarbonCore/Finder.h>
#include <HIToolbox/InternetConfig.h>
#include <CarbonCore/UnicodeConverter.h>
#endif
#endif

#if TARGET_RT_MAC_CFM
#include <PLStringFuncs.h>
#include <Resources.h>
#include <TextUtils.h>
#include <Strings.h>
#include <Finder.h>
#include <InternetConfig.h>
#include <UnicodeConverter.h>
#endif

extern int quiet;

#if TARGET_RT_MAC_MACHO
static OSStatus GetVolFSRef(const char *volName, FSRef & res, Boolean & isFirstVol)
{
	OSErr anErr;
	
	ItemCount index = 1;
	FSRef rootDirectory;
	FSVolumeRefNum actualVolume;
	HFSUniStr255 uniVolName;
	Str255 pstr, pVolName;
	FSRef firstRef;
	Boolean firstRefSet = false;
	
	isFirstVol = false;
	c2pstrcpy(pVolName, volName);
	
	while((anErr = FSGetVolumeInfo(kFSInvalidVolumeRefNum, index++, &actualVolume,
		kFSVolInfoNone, 0L, &uniVolName, &rootDirectory)) == noErr)
	{
		// we suppose it's the system disk... hum...
		if(!firstRefSet)
		{
			firstRef = rootDirectory;
			firstRefSet = true;
		}

		FSCatalogInfo catinfo;
		anErr = FSGetCatalogInfo(&rootDirectory, kFSCatInfoTextEncoding,
			&catinfo, 0L, 0L, 0L);
		if(anErr != noErr)
			break;

		TextEncoding encoder = catinfo.textEncodingHint;
		UnicodeToTextInfo unicodeToTextInfo;
		anErr = CreateUnicodeToTextInfoByEncoding (encoder, &unicodeToTextInfo);
		if(anErr == noErr)
		{
			anErr = ConvertFromUnicodeToPString (unicodeToTextInfo,
				uniVolName.length * sizeof(UniChar), uniVolName.unicode, pstr);
			
			DisposeUnicodeToTextInfo(&unicodeToTextInfo);

			if(anErr == noErr)
			{
				if(EqualString(pVolName, pstr, false, false))
				{
					res = rootDirectory;
					return noErr;
				}
			}
		}
	}

	if(anErr != noErr && firstRefSet)
	{
		res = firstRef;
		isFirstVol = true;
		return noErr;
	}
	
	return anErr;
}

#if 0
// split the path using the '/' delimiter
static bool SplitPath(const char *dir, UStr & uppath, UStr & folder)
{
	char newdir[512];
	char olddir[512];
	char newfile[256];
	int lendir = strlen(dir);
	strcpy(olddir, dir);
	if(dir[lendir - 1] == '/')
	{
		olddir[lendir - 1] = '\0';
		--lendir;
	}
	const char *tmp = olddir, *prev = olddir;
	
	while((tmp = strchr(tmp, '/')) != 0L)
		prev = ++tmp;
	
	strcpy(newdir, olddir);
	newdir[prev - olddir] = '\0';
	strcpy(newfile, prev);

	uppath = newdir;
	folder = newfile;

	return folder.length() != 0;
}
#endif

OSStatus PathToFSRef(const char *filename, FSRef * res)
{
#if 1
	return FSPathMakeRef((UInt8 *)filename, res, 0L);
#else
	std::vector<UStr> components;
	UStr file(filename), comp, uppath;
	while(SplitPath(file, uppath, comp))
	{
		components.push_back(comp);
		file = uppath;
	}
	
	if(components.size() == 0)
		return paramErr;
	
	// the first component is the volume name
	comp = components.back();

	Boolean isFirstVol;	
	OSStatus err = GetVolFSRef(comp, *res, isFirstVol);
	if(err != noErr)
		return err;

	if(!isFirstVol)
		components.pop_back();
	
	TextEncoding encoder = kTextEncodingMacRoman;
	TextToUnicodeInfo textToUnicodeInfo;
	Str255 pstr;
	UniChar ustr[256];
	ByteCount oUnicodeLen;
	
	err = CreateTextToUnicodeInfoByEncoding(encoder, &textToUnicodeInfo);
	if(err != noErr)
		return err;
	
	// then build-up the rest of the path
	while(components.size() > 0)
	{
		comp = components.back();
		components.pop_back();
		
		c2pstrcpy(pstr, comp);
		
		err = ConvertFromPStringToUnicode(textToUnicodeInfo, pstr, sizeof(ustr), &oUnicodeLen, ustr);
		if(err != noErr)
			break;
		
		FSRef newRef;
		err = FSMakeFSRefUnicode(res, oUnicodeLen / sizeof(UniChar), ustr, encoder, &newRef);
		
		if(err == noErr)
			*res = newRef;
		else
			break;
	}
	
	DisposeTextToUnicodeInfo(&textToUnicodeInfo);
	
	return err;
#endif
}

OSStatus StrToUnicode(const char *str, UniChar *ustr, SInt32 *uniLen,
					  TextEncoding *outEncoder)
{
	TextEncoding encoder = kTextEncodingMacRoman;
	TextToUnicodeInfo textToUnicodeInfo;
	ByteCount oUnicodeLen;
	OSStatus status;
	Str255 pstr;

	c2pstrcpy(pstr, str);

	status = CreateTextToUnicodeInfoByEncoding(encoder, &textToUnicodeInfo);
	if(status != noErr)
		return status;

	status = ConvertFromPStringToUnicode(textToUnicodeInfo, pstr, *uniLen * sizeof(UniChar), &oUnicodeLen, ustr);
	DisposeTextToUnicodeInfo(&textToUnicodeInfo);

	if(status == noErr)
	{
		*uniLen = oUnicodeLen / sizeof(UniChar);
		if(outEncoder != 0L)
			*outEncoder = encoder;
	}

	return status;
}
#endif

static void fillSemiColonTypes(const char *envVar, OSType *& allTypes, int & numTypes)
{
	allTypes = 0L;
	numTypes = 0;
	const char *envRes = getenv(envVar);
	if(envRes == 0L)
		return;
	
	char *tmpStr = (char *)malloc(strlen(envRes) + 1);
	if(tmpStr == 0L)
        error (1, errno, "Running out of memory !");
	strcpy(tmpStr, envRes);
	
	char *tmp = tmpStr, *colon = 0L, *start = tmpStr;
	while((tmp = strchr(tmp, ';')) != 0L)
	{
		*tmp = '\0';
		if((tmp - start) == 4)
		{
			if(allTypes == 0L)
				allTypes = (OSType *)malloc(sizeof(OSType));
			else
				allTypes = (OSType *)realloc(allTypes, (numTypes + 1) * sizeof(OSType));
			if(allTypes == 0L)
				error (1, errno, "Running out of memory !");
			OSType atype;
			((char *)&atype)[0] = *start++;
			((char *)&atype)[1] = *start++;
			((char *)&atype)[2] = *start++;
			((char *)&atype)[3] = *start;
			allTypes[numTypes++] = atype;
		}
		else
		{
			error (0, errno, "Warning unknown signature %s (variable : %s) !",
				   start, envRes);
		}
		
		start = ++tmp;
	}
}

static Boolean isPlainBinary(OSType ftype)
{
	static OSType *allTypes = (OSType *)-1;
	static int numTypes;
	if(allTypes == (OSType *)-1)
	{
		fillSemiColonTypes("MAC_BINARY_TYPES_PLAIN", allTypes, numTypes);
	}
	if(allTypes == 0L)
		return false;
	for(int i = 0; i < numTypes; i++)
	{
		if(allTypes[i] == ftype)
			return true;
	}
	return false;
}

static Boolean testHQXEncoding(OSType ftype)
{
	static OSType *allTypes = (OSType *)-1;
	static int numTypes;
	if(allTypes == (OSType *)-1)
	{
		fillSemiColonTypes("MAC_BINARY_TYPES_HQX", allTypes, numTypes);
	}
	if(allTypes == 0L)
		return false;
	for(int i = 0; i < numTypes; i++)
	{
		if(allTypes[i] == ftype)
			return true;
	}
	return false;
}

static Boolean testAppleSingleEncoding(OSType ftype)
{
	static OSType *allTypes = (OSType *)-1;
	static int numTypes;
	if(allTypes == (OSType *)-1)
	{
		fillSemiColonTypes("MAC_BINARY_TYPES_SINGLE", allTypes, numTypes);
	}
	if(allTypes == 0L)
		return false;
	for(int i = 0; i < numTypes; i++)
	{
		if(allTypes[i] == ftype)
			return true;
	}
	return false;
}

// - Tells which encoder to use. Check the env. variables "MAC_BINARY_TYPES_HQX"
// and "MAC_BINARY_TYPES_SINGLE". If the type is not inside, return the
// default encoder "MAC_DEFAULT_RESOURCE_ENCODING".
// - Note that the plain binary test has to overide this one in every cases.

typedef enum
{
	eEncodeDefault = -1,
	eEncodeHQX,
	eEncodeAppleSingle,
	eEncodePlainBinary
} encodingPolicy;

static void determineEncodingPolicy(OSType ftype, encodingPolicy *policy)
{
	if(isPlainBinary(ftype))
	{
		*policy = eEncodePlainBinary;
	}
	else if(testHQXEncoding(ftype))
	{
		*policy = eEncodeHQX;
	}
	else if(testAppleSingleEncoding(ftype))
	{
		*policy = eEncodeAppleSingle;
	}
	else
	{
		*policy = eEncodeDefault;
	}
}

static void getDefaultEncodingPolicy(encodingPolicy *policy)
{
	// get the default encoder
	char *envRes = getenv("MAC_DEFAULT_RESOURCE_ENCODING");
	if(envRes == 0L)
	{
		// default was eEncodeHQX, so MWCVS users won't complain
		// if they don't set this flag
		*policy = eEncodeHQX;
		return;
	}
	
	if(strcmp(envRes, "HQX") == 0)
	{
		*policy = eEncodeHQX;
		return;
	}
	else if(strcmp(envRes, "AppleSingle") == 0)
	{
		*policy = eEncodeAppleSingle;
		return;
	}
	
	error (1, errno, "Unknown resource encoding '%s' !\n", envRes);
}

static FILE *gDestFile;
static FILE *gSrcFile;
#if TARGET_RT_MAC_CFM
static FSSpec gDestFileSpec;
#endif

static OSErr MyDest(void* buffer, long count, long param) {
	fwrite(buffer, count, 1, gDestFile);
	return noErr;
}

static OSErr MySource(void* buffer, long *count, long param) {
	*count =  fread(buffer, 1, *count, gSrcFile);
	return noErr;
}

#if TARGET_RT_MAC_CFM
static OSErr MyNameFilter(StringPtr name, short *vol, long *dir, long param) {
	PLstrcpy(name, gDestFileSpec.name);
	*vol = gDestFileSpec.vRefNum;
	*dir = gDestFileSpec.parID;
	return noErr;
}
#endif

int
convert_hqx_file (const char *infile,  int inflags,
	const char *outfile, int outflags, int *bin)
{
	OSErr err = fnfErr;
	encodingPolicy inFileType;

#if TARGET_RT_MAC_MACHO
	OSType filetype = 0;
	OSType filecreator;
	FSRef inspec, outspec;
	FSCatalogInfo catalogInfo, catalogDestInfo;
	char path[1024];
#endif

#if TARGET_RT_MAC_CFM
	FInfo fileinfo;

	GUSIFileSpec inspec(infile);
	if(inspec.Error() != noErr)
		error(1, inspec.Error(), "Error when accessing %s (%s %d)", infile, __FILE__, __LINE__);

	GUSIFileSpec outspec(outfile);
	if(outspec.Error() != noErr)
		error(1, outspec.Error(), "Error when accessing %s (%s %d)", outfile, __FILE__, __LINE__);
#endif

	Boolean encode;
	
	if (inflags & O_BINARY)
	{
		encode = false;
		
		// the cvswrapper has to be coherent !!!
		if((*bin) == 0)
			return 0;
		
		gSrcFile = fopen(infile, "r");
		if(gSrcFile == NULL)
			error(1, errno, "Cannot open %s (%s %d)", infile, __FILE__, __LINE__);
		
		char buf[100];
		static char hqxheader[] = "(This file must be converted with BinHex 4.0)";
		UInt32 asheader = AS_MAGIC_NUM;
		fread (buf, sizeof(char), 100, gSrcFile);

		// Determine whether the file is one of the known encodings
		if ( memcmp(hqxheader, buf, strlen(hqxheader) * sizeof(char)) == 0)
		{
			inFileType = eEncodeHQX;
			fseek(gSrcFile, 0, SEEK_SET);
		}
		else if  ( memcmp(&asheader, buf, sizeof(asheader)) == 0 )
		{
			fclose(gSrcFile);
			inFileType = eEncodeAppleSingle;
		}
		else
		{
			// it is a "plain" binary
			fclose(gSrcFile);
			return 0;
		}
	}
	else
	{
		encode = true;

		// check if it is a text file
#if TARGET_RT_MAC_CFM
		CInfoPBRec cbrec;
		DirInfo * cb;
		
		cb = (DirInfo *)&cbrec;
		cb->ioNamePtr	=	(unsigned char *)inspec->name;
		cb->ioDrDirID	=	inspec->parID;
		cb->ioVRefNum	=	inspec->vRefNum;
		cb->ioFDirIndex	=	0;

		err = PBGetCatInfoSync((CInfoPBRec *)cb);
		if(err != noErr)
			error(1, err, "Internal error: %s, %d", __FILE__, __LINE__);
		
		fileinfo = ((HFileInfo *)cb)->ioFlFndrInfo;
		if(fileinfo.fdType == 'TEXT')
			return 0;
#endif
#if TARGET_RT_MAC_MACHO
		getcwd(path, sizeof(path));
		strcat(path, "/");
		strcat(path, infile);

		OSStatus status = PathToFSRef(path, &inspec);
		if(status != noErr)
			error(1, status, "Internal error: %s, %d", __FILE__, __LINE__);
		
		status = ::FSGetCatalogInfo(&inspec, kFSCatInfoFinderInfo | kFSCatInfoRsrcSizes,
									&catalogInfo, 0L, 0L, 0L);
		if(status != noErr)
			error(1, status, "Internal error: %s, %d", __FILE__, __LINE__);
		
		filetype = ((FInfo *)catalogInfo.finderInfo)->fdType;
		if(filetype == 'TEXT' || filetype == 0)
			return 0;
#endif
	}
		
	if(encode)
	{
		encodingPolicy policy;
#if TARGET_RT_MAC_CFM
		determineEncodingPolicy(fileinfo.fdType, &policy);
#endif
#if TARGET_RT_MAC_MACHO
		determineEncodingPolicy(filetype, &policy);
#endif

		// special case, some files may be forced as plain binary.
		// (env. variable "MAC_BINARY_TYPES_PLAIN")
		if(policy == eEncodePlainBinary)
		{
			*bin = 1;
			return 0;
		}
		else if(policy == eEncodeDefault)
		{
			// Never encode any files that have no resource fork
			// Should we just use the test: if (
#if TARGET_RT_MAC_CFM
			short fileRsrc = FSpOpenResFile(&inspec, fsRdPerm);
			if(fileRsrc < 0)
			{
				// it is a "plain" binary
				*bin = 1;
				return 0;
			}
			CloseResFile(fileRsrc);
#endif
#if TARGET_RT_MAC_MACHO
			if(catalogInfo.rsrcLogicalSize == 0)
			{
				// it is a "plain" binary
				*bin = 1;
				return 0;
			}
#endif

			// get default policy
			getDefaultEncodingPolicy(&policy);
		}
		
		if(policy == eEncodeHQX)
		{
			gDestFile = fopen(outfile, "w");
			if(gDestFile == NULL)
				error(1, errno, "Internal error: %s, %d", __FILE__, __LINE__);
#if TARGET_RT_MAC_CFM
			err = HQXEncode((unsigned char *)inspec->name, inspec->vRefNum, inspec->parID, MyDest, 0);
#endif
#if TARGET_RT_MAC_MACHO
			err = HQXEncode(infile, MyDest, 0);
#endif
			fflush(gDestFile);
			fclose(gDestFile);
			if(err != noErr)
				error(1, err, "Internal error: %s, %d", __FILE__, __LINE__);
		}
		else if ( policy == eEncodeAppleSingle )
		{
#define APPLESINGLE_WANTED_ENTRIES ((UInt32)AS_DATA_BIT | AS_RESOURCE_BIT | AS_COMMENT_BIT | AS_FINDERINFO_BIT | AS_MACINFO_BIT)
			UInt32 wantedEntries = APPLESINGLE_WANTED_ENTRIES;
#if TARGET_RT_MAC_CFM
			err = encodeAppleSingle(&inspec, outfile, wantedEntries);
#endif
#if TARGET_RT_MAC_MACHO
			err = encodeAppleSingle(infile, outfile, wantedEntries);
#endif
			if (err != noErr)
				error(1, err, "Internal error: %s, %d", __FILE__, __LINE__);
		}
		else
		{
			error(1, errno, "Internal error: %s, %d", __FILE__, __LINE__);
		}
	}
	else	// decoding
	{
		if ( inFileType == eEncodeHQX )
		{
#if TARGET_RT_MAC_CFM
			gDestFileSpec = outspec;
			err = HQXDecode(MySource, MyNameFilter, true, true, 0);
#endif
#if TARGET_RT_MAC_MACHO
			err = HQXDecode(MySource, outfile, true, true, 0);
#endif
			
			fclose(gSrcFile);
			if(err != noErr)
				error(1, err, "Internal error: %s, %d", __FILE__, __LINE__);
		}
		else if ( inFileType == eEncodeAppleSingle)
		{
			UInt32 wantedEntries = APPLESINGLE_WANTED_ENTRIES;
#if TARGET_RT_MAC_CFM
			FSSpec macSpec = outspec;
			err = decodeAppleSingle(infile, &macSpec, wantedEntries);
#endif
#if TARGET_RT_MAC_MACHO
			err = decodeAppleSingle(infile, outfile, wantedEntries);
#endif

			if (err != noErr)
				error(1, err, "Internal error: %s, %d", __FILE__, __LINE__);

			// Sometimes, type/creator information is not encoded inside the AS file 
			// If so, we want to set type/creator using Internet Config
#if TARGET_RT_MAC_CFM
			CInfoPBRec cbrec;
			cbrec.hFileInfo.ioNamePtr = macSpec.name;
			cbrec.hFileInfo.ioDirID	= macSpec.parID;
			cbrec.hFileInfo.ioVRefNum =	macSpec.vRefNum;
			cbrec.hFileInfo.ioFDirIndex	=0;

			err = PBGetCatInfoSync(&cbrec);
			if(err != noErr)
				error(1, err, "Internal error: %s, %d", __FILE__, __LINE__);
			
			fileinfo = cbrec.hFileInfo.ioFlFndrInfo;
			if( (fileinfo.fdType == AS_DEFAULT_TYPE)
				 && ( fileinfo.fdCreator == AS_DEFAULT_CREATOR))
			{
				GUSIFileSpec newSpec( macSpec);
				char * fullPath = newSpec.FullPath();
				set_file_type(	fullPath, true );
			}
#endif
#if TARGET_RT_MAC_MACHO
			getcwd(path, sizeof(path));
			strcat(path, "/");
			strcat(path, outfile);
			OSStatus status = PathToFSRef(path, &outspec);
			if(status != noErr)
				error(1, status, "Internal error: %s, %d", __FILE__, __LINE__);

			status = ::FSGetCatalogInfo(&outspec, kFSCatInfoFinderInfo,
										&catalogDestInfo, 0L, 0L, 0L);

			if(status != noErr)
				error(1, status, "Internal error: %s, %d", __FILE__, __LINE__);
		
			filetype = ((FInfo *)catalogDestInfo.finderInfo)->fdType;
			filecreator = ((FInfo *)catalogDestInfo.finderInfo)->fdCreator;
			if(filetype == AS_DEFAULT_TYPE && filecreator == AS_DEFAULT_CREATOR)
			{
				set_file_type(outfile, true);
			}
#endif
 		}
		else
		{
			error(1, errno, "Internal error: %s, %d", __FILE__, __LINE__);
		}
	}

	return 1;
}

void set_file_type(const char *outfile, Boolean warnOnFail)
{
	OSStatus err;
	ICInstance inst;
	Str255 filename;

	c2pstrcpy(filename, outfile);
	
	err = ICStart(&inst, 'CVS ');			// Use your creator code if you have one!
	if (err != noErr)
	{
		static Boolean firsttime = true;
		if(firsttime)
		{
			error(0, err, "WARNING Internet config's missing !");
			firsttime = false;
		}
		return;
	}

#if TARGET_API_MAC_OS8
	err = ICFindConfigFile(inst, 0, nil);
	if (err != noErr)
	{
		static Boolean firsttime2 = true;
		if(firsttime2)
		{
			error(0, err, "Internal error: %s, %d", __FILE__, __LINE__);
			firsttime2 = false;
		}
		ICStop(inst);
		return;
	}
#endif

	ICMapEntry entry;
	err = ICMapFilename(inst, filename, &entry);
#if 0
	if(err == icPrefNotFoundErr)
	{
		entry.fileCreator = FOUR_CHAR_CODE('CWIE');
		entry.fileType = FOUR_CHAR_CODE('TEXT');

		/*size_t len = strlen(outfile);
		if(len > strlen(".mcp") &&
		strcmp(outfile + len - strlen(".mcp"), ".mcp") == 0)
		entry.fileType = FOUR_CHAR_CODE('');*/
			
		err = noErr;
	}
#endif
	if (err != noErr)
	{
		if (warnOnFail)
			error(0, err, "WARNING: Internet Config cannot map file %s", outfile);
		ICStop(inst);
		return;
	}

#if TARGET_RT_MAC_CFM
	OSErr oserr;
	GUSIFileSpec outspec(outfile);
	FInfo fndrInfo;

	FSSpec macSpec = outspec;
	oserr = HGetFInfo(macSpec.vRefNum, macSpec.parID, macSpec.name, &fndrInfo);
	if (oserr != noErr)
	{
		error(0, oserr, "Internal error: %s, %d", __FILE__, __LINE__);
		ICStop(inst);
		return;
	}

#if UNIVERSAL_INTERFACES_VERSION < 0x0331
	if(entry.file_creator != 0)
		fndrInfo.fdCreator = entry.file_creator;
#else
	if(entry.fileCreator != 0)
		fndrInfo.fdCreator = entry.fileCreator;
#endif
	if(entry.fileType != 0)
		fndrInfo.fdType = entry.fileType;
	oserr = HSetFInfo(macSpec.vRefNum, macSpec.parID, macSpec.name, &fndrInfo);
	if (oserr != noErr)
		error(0, oserr, "Internal error: %s, %d", __FILE__, __LINE__);
#endif
#if TARGET_RT_MAC_MACHO
	FInfo *fndrInfo;
	FSRef outspec;
	char path[1024];
	OSStatus status;

	getcwd(path, sizeof(path));
	strcat(path, "/");
	strcat(path, outfile);
	err = PathToFSRef(path, &outspec);
	if(err != noErr)
	{
		error(0, err, "Internal error: %s, %d", __FILE__, __LINE__);
		ICStop(inst);
		return;
	}

	FSCatalogInfo catalogInfo;
	status = ::FSGetCatalogInfo(&outspec, kFSCatInfoFinderInfo,
								&catalogInfo, 0L, 0L, 0L);

	if(status != noErr)
	{
		error(0, status, "Internal error: %s, %d", __FILE__, __LINE__);
		ICStop(inst);
		return;
	}

	fndrInfo = (FInfo *)catalogInfo.finderInfo;
	
	if(entry.fileCreator != 0)
		fndrInfo->fdCreator = entry.fileCreator;
	if(entry.fileType != 0)
		fndrInfo->fdType = entry.fileType;

	status = ::FSSetCatalogInfo(&outspec, kFSCatInfoFinderInfo,
								&catalogInfo);

	if(status != noErr)
	{
		error(0, status, "Internal error: %s, %d", __FILE__, __LINE__);
	}
#endif

	ICStop(inst);
}

#if TARGET_RT_MAC_CFM
int mac_duplicate (const char *from, const char *to)
{
	GUSIFileSpec fromspec(macos_fixpath(from));
	if(fromspec.Error() != noErr)
		return fromspec.Error();
	
	GUSIFileSpec tospec(macos_fixpath(to));
	if(tospec.Error() != noErr && tospec.Error() != fnfErr)
		return tospec.Error();
	
	FSSpec macfromspec = fromspec;
	FSSpec mactospec = tospec;
	OSErr err = FileCopy(macfromspec.vRefNum,
						 macfromspec.parID,
						 macfromspec.name,
						 mactospec.vRefNum,
						 mactospec.parID,
						 nil,
						 mactospec.name,
						 nil,
						 0,
						true);
	
	if(err == fnfErr)
		errno = ENOENT;
	else if(err != noErr)
		errno = err;

	return err;
}
#endif

unsigned char const mac_to_iso_maccvspro[256] =
  {
      0,   1,   2,   3,   4,   5,   6,   7,	/*   0 -   7  */
      8,   9,  10,  11,  12,  13,  14,  15,	/*   8 -  15  */
     16,  17,  18,  19,  20,  21,  22,  23,	/*  16 -  23  */
     24,  25,  26,  27,  28,  29,  30,  31,	/*  24 -  31  */
     32,  33,  34,  35,  36,  37,  38,  39,	/*  32 -  39  */
     40,  41,  42,  43,  44,  45,  46,  47,	/*  40 -  47  */
     48,  49,  50,  51,  52,  53,  54,  55,	/*  48 -  55  */
     56,  57,  58,  59,  60,  61,  62,  63,	/*  56 -  63  */
     64,  65,  66,  67,  68,  69,  70,  71,	/*  64 -  71  */
     72,  73,  74,  75,  76,  77,  78,  79,	/*  72 -  79  */
     80,  81,  82,  83,  84,  85,  86,  87,	/*  80 -  87  */
     88,  89,  90,  91,  92,  93,  94,  95,	/*  88 -  95  */
     96,  97,  98,  99, 100, 101, 102, 103,	/*  96 - 103  */
    104, 105, 106, 107, 108, 109, 110, 111,	/* 104 - 111  */
    112, 113, 114, 115, 116, 117, 118, 119,	/* 112 - 119  */
    120, 121, 122, 123, 124, 125, 126, 127,	/* 120 - 127  */
    196, 197, 199, 201, 209, 214, 220, 225,	/* 128 - 135  */
    224, 226, 228, 227, 229, 231, 233, 232,	/* 136 - 143  */
    234, 235, 237, 236, 238, 239, 241, 243,	/* 144 - 151  */
    242, 244, 246, 245, 250, 249, 251, 252,	/* 152 - 159  */
    190, 176, 162, 163, 167, 130, 182, 223,	/* 160 - 167  */
    174, 169, 142, 180, 168, 173, 198, 216,	/* 168 - 175  */
    141, 177, 178, 179, 165, 181, 166, 135,	/* 176 - 183  */
    159, 185, 188, 170, 186, 189, 230, 248,	/* 184 - 191  */
    191, 161, 172, 146, 128, 129, 140, 171,	/* 192 - 199  */
    187, 131, 160, 192, 195, 144, 145, 147,	/* 200 - 207  */
    208, 132, 150, 148, 149, 213, 247, 215,	/* 208 - 215  */
    255, 153, 152, 164, 134, 221, 222, 151,	/* 216 - 223  */
    136, 183, 137, 139, 138, 194, 202, 193,	/* 224 - 231  */
    203, 200, 205, 206, 207, 204, 211, 212,	/* 232 - 239  */
    240, 210, 218, 219, 217, 155, 154, 133,	/* 240 - 247  */
    175, 157, 156, 158, 184, 253, 254, 143,	/* 248 - 255  */
  };



/* Conversion table generated mechanically by Free `recode' 3.5
   for sequence ISO-8859-1..macintosh (reversible).  */

unsigned char const iso_to_mac_maccvspro[256] =
  {
      0,   1,   2,   3,   4,   5,   6,   7,	/*   0 -   7  */
      8,   9,  10,  11,  12,  13,  14,  15,	/*   8 -  15  */
     16,  17,  18,  19,  20,  21,  22,  23,	/*  16 -  23  */
     24,  25,  26,  27,  28,  29,  30,  31,	/*  24 -  31  */
     32,  33,  34,  35,  36,  37,  38,  39,	/*  32 -  39  */
     40,  41,  42,  43,  44,  45,  46,  47,	/*  40 -  47  */
     48,  49,  50,  51,  52,  53,  54,  55,	/*  48 -  55  */
     56,  57,  58,  59,  60,  61,  62,  63,	/*  56 -  63  */
     64,  65,  66,  67,  68,  69,  70,  71,	/*  64 -  71  */
     72,  73,  74,  75,  76,  77,  78,  79,	/*  72 -  79  */
     80,  81,  82,  83,  84,  85,  86,  87,	/*  80 -  87  */
     88,  89,  90,  91,  92,  93,  94,  95,	/*  88 -  95  */
     96,  97,  98,  99, 100, 101, 102, 103,	/*  96 - 103  */
    104, 105, 106, 107, 108, 109, 110, 111,	/* 104 - 111  */
    112, 113, 114, 115, 116, 117, 118, 119,	/* 112 - 119  */
    120, 121, 122, 123, 124, 125, 126, 127,	/* 120 - 127  */
    196, 197, 165, 201, 209, 247, 220, 183,	/* 128 - 135  */
    224, 226, 228, 227, 198, 176, 170, 255,	/* 136 - 143  */
    205, 206, 195, 207, 211, 212, 210, 223,	/* 144 - 151  */
    218, 217, 246, 245, 250, 249, 251, 184,	/* 152 - 159  */
    202, 193, 162, 163, 219, 180, 182, 164,	/* 160 - 167  */
    172, 169, 187, 199, 194, 173, 168, 248,	/* 168 - 175  */
    161, 177, 178, 179, 171, 181, 166, 225,	/* 176 - 183  */
    252, 185, 188, 200, 186, 189, 160, 192,	/* 184 - 191  */
    203, 231, 229, 204, 128, 129, 174, 130,	/* 192 - 199  */
    233, 131, 230, 232, 237, 234, 235, 236,	/* 200 - 207  */
    208, 132, 241, 238, 239, 213, 133, 215,	/* 208 - 215  */
    175, 244, 242, 243, 134, 221, 222, 167,	/* 216 - 223  */
    136, 135, 137, 139, 138, 140, 190, 141,	/* 224 - 231  */
    143, 142, 144, 145, 147, 146, 148, 149,	/* 232 - 239  */
    240, 150, 152, 151, 153, 155, 154, 214,	/* 240 - 247  */
    191, 157, 156, 158, 159, 253, 254, 216,	/* 248 - 255  */
  };

/* From Fetch.  */

unsigned char const mac_to_iso[256] =
  {
	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
	0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
	0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
	0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
	0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
	0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
	0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
	0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
	0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
	0xC4, 0xC5, 0xC7, 0xC9, 0xD1, 0xD6, 0xDC, 0xE1,
	0xE0, 0xE2, 0xE4, 0xE3, 0xE5, 0xE7, 0xE9, 0xE8,
	0xEA, 0xEB, 0xED, 0xEC, 0xEE, 0xEF, 0xF1, 0xF3,
	0xF2, 0xF4, 0xF6, 0xF5, 0xFA, 0xF9, 0xFB, 0xFC,
	0xDD, 0xB0, 0xA2, 0xA3, 0xA7, 0x80, 0xB6, 0xDF,
	0xAE, 0xA9, 0x81, 0xB4, 0xA8, 0x82, 0xC6, 0xD8,
	0x83, 0xB1, 0xBE, 0x84, 0xA5, 0xB5, 0x8F, 0x85,
	0xBD, 0xBC, 0x86, 0xAA, 0xBA, 0x87, 0xE6, 0xF8,
	0xBF, 0xA1, 0xAC, 0x88, 0x9F, 0x89, 0x90, 0xAB,
	0xBB, 0x8A, 0xA0, 0xC0, 0xC3, 0xD5, 0x91, 0xA6,
	0xAD, 0x8B, 0xB3, 0xB2, 0x8C, 0xB9, 0xF7, 0xD7,
	0xFF, 0x8D, 0x8E, 0xA4, 0xD0, 0xF0, 0xDE, 0xFE,
	0xFD, 0xB7, 0x92, 0x93, 0x94, 0xC2, 0xCA, 0xC1,
	0xCB, 0xC8, 0xCD, 0xCE, 0xCF, 0xCC, 0xD3, 0xD4,
	0x95, 0xD2, 0xDA, 0xDB, 0xD9, 0x9E, 0x96, 0x97,
	0xAF, 0x98, 0x99, 0x9A, 0xB8, 0x9B, 0x9C, 0x9D,
  };



unsigned char const iso_to_mac[256] =
  {
	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
	0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
	0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
	0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
	0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
	0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
	0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
	0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
	0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
	0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
	0xA5, 0xAA, 0xAD, 0xB0, 0xB3, 0xB7, 0xBA, 0xBD,
	0xC3, 0xC5, 0xC9, 0xD1, 0xD4, 0xD9, 0xDA, 0xB6,
	0xC6, 0xCE, 0xE2, 0xE3, 0xE4, 0xF0, 0xF6, 0xF7,
	0xF9, 0xFA, 0xFB, 0xFD, 0xFE, 0xFF, 0xF5, 0xC4,
	0xCA, 0xC1, 0xA2, 0xA3, 0xDB, 0xB4, 0xCF, 0xA4,
	0xAC, 0xA9, 0xBB, 0xC7, 0xC2, 0xD0, 0xA8, 0xF8,
	0xA1, 0xB1, 0xD3, 0xD2, 0xAB, 0xB5, 0xA6, 0xE1,
	0xFC, 0xD5, 0xBC, 0xC8, 0xB9, 0xB8, 0xB2, 0xC0,
	0xCB, 0xE7, 0xE5, 0xCC, 0x80, 0x81, 0xAE, 0x82,
	0xE9, 0x83, 0xE6, 0xE8, 0xED, 0xEA, 0xEB, 0xEC,
	0xDC, 0x84, 0xF1, 0xEE, 0xEF, 0xCD, 0x85, 0xD7,
	0xAF, 0xF4, 0xF2, 0xF3, 0x86, 0xA0, 0xDE, 0xA7,
	0x88, 0x87, 0x89, 0x8B, 0x8A, 0x8C, 0xBE, 0x8D,
	0x8F, 0x8E, 0x90, 0x91, 0x93, 0x92, 0x94, 0x95,
	0xDD, 0x96, 0x98, 0x97, 0x99, 0x9B, 0x9A, 0xD6,
	0xBF, 0x9D, 0x9C, 0x9E, 0x9F, 0xE0, 0xDF, 0xD8
  };

static const unsigned char *convert_iso(int convert_from, int iso)
{
	if(iso != 1 && iso != 2) // only Latin 1 available right now
		return NULL;
	
	if(iso == 1)
	{
		return convert_from ? iso_to_mac : mac_to_iso;
	}
	else
		return convert_from ? iso_to_mac_maccvspro : mac_to_iso_maccvspro;
}

#if TARGET_API_MAC_OS8
void
c2pstrcpy                       (Str255                 dst,
                                 const char *           src)
{
	strcpy((char *)dst, src);
	c2pstr((char *)dst);
}

void
p2cstrcpy                       (char *                 dst,
								 ConstStr255Param 		src)
{
	unsigned char len = src[0];
	memcpy(dst, &src[1], len);
	dst[len] = '\0';
}
#endif

#if __MSL__ >= 0x6001 && __MSL__ < 0x7000
#ifdef _POSIX
extern "C" {	
_MSL_IMP_EXP_C char*        	_getlogin(void);
_MSL_IMP_EXP_C mode_t _umask(mode_t);  		/*cc Feb 10 2000 */	
char*    	    getlogin(void)     {return _getlogin()    ;} 
mode_t  umask(mode_t _a)  			 {return  _umask(_a)    	 ;}   	
}
#endif
#endif

/*
 * the trick is : if inflags & OPEN_BINARY, it means we decode from
 * the server format (HQX or ISO) into the local format (Mac resource or
 * Mac Text encoding). outflags depends if we try to open a Mac CR file
 * or a Unix LF file. The MSL fopen, fread, fwrite... toggles
 * automatically CR<->LF when opening as TEXT ("r", "w"). While
 * the GUSI open, read, write makes no conversion at all (alexp)
 */
	
void
convert_file (char *infile,  int inflags,
	      char *outfile, int outflags, int binary)
{
    FILE *infd, *outfd;
    char buf[8192];
    int len;
    int decode; /* 1 : server to client format, 0: client to server format */
#	define ISO8559_VALID(iso) ((iso) == 1 || (iso) == 2)
	static int set_text_signature = -1;
	static int convert_iso8559 = -1;
	static int convert_maclf = -1;
	
	if(convert_iso8559 == -1)
	{
		const char *iso = getenv("ISO8859");
		if(iso == NULL || sscanf(iso, "%d", &convert_iso8559) != 1 ||
			!ISO8559_VALID(convert_iso8559))
				convert_iso8559 = 0;
	}
	if(set_text_signature == -1)
	{
		set_text_signature = getenv("IC_ON_TXT") != NULL ? 1 : 0;
	}

#if TARGET_API_MAC_OSX
	// create text files with the Mac CR (0x0D)
	// the default is LF on OSX
	if(convert_maclf == -1)
	{
		convert_maclf = getenv("CVS_MACLF") != NULL ? 1 : 0;
	}
#endif

	/* "update" doesn't check if we can erase the dest file */
	chmod(outfile, S_IRWXU);
	
	if(convert_hqx_file(infile, inflags, outfile, outflags, &binary))
		return;

    if (inflags & OPEN_BINARY)
	{
    	decode = true;
    }
	else
	{
    	decode = false;
    }

	const unsigned char *isotab = NULL;
	if(convert_iso8559)
		isotab = convert_iso(decode, convert_iso8559);
	
    if ((infd = fopen (infile, (binary || (inflags & OPEN_BINARY)) ? "rb" : "r")) == NULL)
        error (1, errno, "cannot read %s", infile);
    if ((outfd = fopen (outfile, (binary || (outflags & OPEN_BINARY)) ? "wb" : "w")) == NULL)
        error (1, errno, "cannot write %s", outfile);

    while ((len = fread (buf, sizeof (char), sizeof (buf) / sizeof(char), infd)) > 0)
	{
		// for a text file, we have some post process depending
		// if we encode for the server or decode from the server
		if(binary == 0)
		{
			char * conv 	= buf;
			int    convlen  = len;
			while (convlen--)
			{
				if(isotab != NULL)
				{
					*conv = (char)isotab[(unsigned char)*conv];
				}
#if !TARGET_API_MAC_OSX
				if(!decode)
				{
					// for a text file, when we encode it for the server,
					// it needs to have the Unix LF no matter what
					if(*conv == 0x0d)
						*conv = 0x0a;
				}
#else
				// on OSX, we don't have MSL-stdio switching the CR <-> LF,
				// we can go straigth to the point
				if(convert_maclf)
				{
					if(decode)
					{
						// for a text file, this will turn the line feed
						// to the mac one
						if(*conv == 0x0a)
							*conv = 0x0d;
					}
					else
					{
						if(*conv == 0x0d)
							*conv = 0x0a;
					}
				}
#endif
				conv++;
			}
		}

		if (fwrite (buf, sizeof(char), len, outfd) < 0)
			error (1, errno, "error writing %s", outfile);
    }
    if (len < 0)
        error (1, errno, "error reading %s", infile);

    if (fclose (outfd) != 0)
        error (0, errno, "warning: cannot close %s", outfile);
    if (fclose (infd) != 0)
        error (0, errno, "warning: cannot close %s", infile);
        
	if((binary || set_text_signature) && decode)
		set_file_type(outfile, !quiet);
}
