/***********************************************************************\
Common class for converting NeXT to Mac text in all Convert programs
Copyright (C) 1993 David John Burrowes

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.

The author, David John Burrowes, can be reached at:
	davidjohn@kira.net.netcom.com
	David John Burrowes
	1926 Ivy #10
	San Mateo, CA 94403-1367
\***********************************************************************/

/*====================================================================
This is the implementation file for the NeXTToMacText class.  Full documentation for this class can be found in the NeXTToMacText.rtf file.  I will not duplicate all that fine information here.

NOTE: You may find that text doesn't line up properly unless you use the New Century Schoolbook Roman typeface, since this was created with it.

INFORMATION:
	This is $Revision: 1.2 $ of this file
	It was last modifiFed by $Author: death $ on $Date: 93/04/04 23:44:44 $
 	$Log:	NeXTToMacText.m,v $
Revision 1.2  93/04/04  23:44:44  death
Sun Apr  4 23:44:44 PDT 1993

Revision 1.1  93/01/10  15:08:29  death
Sun Jan 10 15:08:29 PST 1993

====================================================================*/


#import "NeXTToMacText.h"
#import <memory.h>	// for memcpy
#include <strings.h>


@implementation NeXTToMacText


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		init:
//	Parameters:	none
//	Returns:		self
//	Stores:		none
//	Description:
//		This initalizes the object by filling in the convert array that it uses to
//		dictate most of its character conversions.
//	Bugs:
//		Note that we store a null to indicate any characters that can't be
//		converted directly.  Because of this strategy, the null character must be dealt
//		with in this object, though really it is part of the superclass' territory.
//		other more complex implementations (keep a flag with each array entry
//		to indicate if we can convert to it, or keep integers so one can store more than
//		one of 256 values in an entry  might be used to reimplement this if needed.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (Instance) init
{
	Integer	counter;
	//
	[super init];
	
	UseCurlyQuotes = NO;
	//
	//	Initialize conversion array with whatever our superclass likes to do.
	//
	for (counter = 0; counter < 256; counter++)
		ConvertArray[counter] = [super 	ConvertCharacter: (Character) counter];
	//
	//	We view the world as: we are just like whatever our superclass is, except
	//	that we differ for characters 0,  NL, and anything above 128. =)  So,
	//	modify the table to reflect all of this.  (note: indices are essencially mac
	//	character codes, while values we are assigning are NeXT character codes).
	//
	ConvertArray[NEWLINE] = CARRIAGERETURN; // Convert line endings
	//	0x8X
	ConvertArray[0x80] = 0xCA;		// (nbspace)next
	ConvertArray[0x81] = 0xCB;		// (Agrave)
	ConvertArray[0x82] = 0xE7;		// (Aacute)
	ConvertArray[0x83] = 0xE5;		// (Acircumflex)
	ConvertArray[0x84] = 0xCC;		// (Atilde)
	ConvertArray[0x85] = 0x80;		// (Adieresis)
	ConvertArray[0x86] = 0x81;		// (Aring)
	ConvertArray[0x87] = 0x82;		// (Ccedilla)
	ConvertArray[0x88] = 0xE9;		// (Egrave)
	ConvertArray[0x89] = 0x83;		// (Eacute)
	ConvertArray[0x8A] = 0xE6;		// (Ecircumflex)
	ConvertArray[0x8B] = 0xE8;		// (Edieresis)
	ConvertArray[0x8C] = 0xED;		// (Igrave)
	ConvertArray[0x8D] = 0xEA;		// (Iacute)
	ConvertArray[0x8E] = 0xEB;		// (Icircumflex)
	ConvertArray[0x8F] = 0xEC;		// (Idieresis)
	//	0x9X
	ConvertArray[0x90] = NullCharacter;	// (Eth)
	ConvertArray[0x91] = 0x84;		// (Ntilde)
	ConvertArray[0x92] = 0xF1;		// (Ograve)
	ConvertArray[0x93] = 0xEE;		// (Oacute)
	ConvertArray[0x94] = 0xEF;		// (Ocircumflex)
	ConvertArray[0x95] = 0xCD;		// (Otilde)
	ConvertArray[0x96] = 0x85;		// (Odieresis)
	ConvertArray[0x97] = 0xF4;		// (Ugrave)
	ConvertArray[0x98] = 0xF2;		// (Uacute)
	ConvertArray[0x99] = 0xF3;		// (Ucircumflex)
	ConvertArray[0x9A] = 0x86;		// (Udieresis)
	ConvertArray[0x9B] = NullCharacter;	// (Yacute)
	ConvertArray[0x9C] = NullCharacter;	// (Thorn)
	ConvertArray[0x9D] = 0xB5;		// (mu)
	ConvertArray[0x9E] = NullCharacter;			// (multiply)
	ConvertArray[0x9F] = 0xD6;		// (divide)
	//	0xAX
	ConvertArray[0xA0] = 0xA9;		// (copyrightserif)
	ConvertArray[0xA1] = 0xC1;		// (exclamdown)
	ConvertArray[0xA2] = 0xA2;		// (cent)
	ConvertArray[0xA3] = 0xA3;		// (sterling)
	ConvertArray[0xA4] = 0xDA;		// (fraction)
	ConvertArray[0xA5] = 0xB4;		// (yen)
	ConvertArray[0xA6] = 0xC4;		// (florin)
	ConvertArray[0xA7] = 0xA4;		// (section)
	ConvertArray[0xA8] = 0xDB;		// (currency)
	ConvertArray[0xA9] = 0x27;		// (quotesingle)
	ConvertArray[0xAA] = 0xD2;		// (quotedblleft)
	ConvertArray[0xAB] = 0xC7;		// (guillemotleft)
	ConvertArray[0xAC] = 0xDC;		// (guilsinglleft)
	ConvertArray[0xAD] = 0xDD;		// (guilsinglright)
	ConvertArray[0xAE] = 0xDE;		// (fi)
	ConvertArray[0xAF] = 0xDF;		// (fl)
	//	0xBX
	ConvertArray[0xB0] = 0xA8;		// (registerserif)
	ConvertArray[0xB1] = 0xD0;		// (endash)
	ConvertArray[0xB2] = 0xA0;		// (dagger)
	ConvertArray[0xB3] = 0xE0;		// (daggerdbl)
	ConvertArray[0xB4] = 0xE1;		// (periodcentered)
	ConvertArray[0xB5] = NullCharacter;	// (brokenbar)
	ConvertArray[0xB6] = 0xA6;		// (paragraph)
	ConvertArray[0xB7] = 0xA5;		// (bullet)
	ConvertArray[0xB8] = 0xE2;		// (quotesinglebase)
	ConvertArray[0xB9] = 0xE3;		// (quotedblbase)
	ConvertArray[0xBA] = 0xD3;		// (quotedblright)
	ConvertArray[0xBB] = 0xC8;		// (guillemotright)
	ConvertArray[0xBC] = 0xC9;		// (elipsis)
	ConvertArray[0xBD] = 0xE4;		// (perthousand)
	ConvertArray[0xBE] = 0xC2;		// (logicalnot)
	ConvertArray[0xBF] = 0xC0;		// (questiondown)
	//	0xCX
	ConvertArray[0xC0] = NullCharacter;			// (onesuperior)
	ConvertArray[0xC1] = 0x60;		// (grave)
	ConvertArray[0xC2] = 0xAB;		// (acute)
	ConvertArray[0xC3] = 0xF6;		// (circumflex)
	ConvertArray[0xC4] = 0xF7;		// (tilde)
	ConvertArray[0xC5] = 0xF8;		// (macron)
	ConvertArray[0xC6] = 0xF9;		// (breve)
	ConvertArray[0xC7] = 0xFA;		// (dotaccent)
	ConvertArray[0xC8] = 0xAC;		// (dieresis)
	ConvertArray[0xC9] = NullCharacter;	// (twosuperior)
	ConvertArray[0xCA] = 0xFB;		// (ring)
	ConvertArray[0xCB] = 0xFC;		// (cedilla)
	ConvertArray[0xCC] = NullCharacter;	// (threesuperior)
	ConvertArray[0xCD] = 0xFD;		// (hungarumlaut)
	ConvertArray[0xCE] = 0xFE;		// (ogonek)
	ConvertArray[0xCF] = 0xFF;		// (caron)
	//	0xDX
	ConvertArray[0xD0] = 0xD1;		// (emdash)
	ConvertArray[0xD1] = 0xB1;		// (plusminus)
	ConvertArray[0xD2] = NullCharacter;	// (onequarter)
	ConvertArray[0xD3] = NullCharacter;	// (onehalf)
	ConvertArray[0xD4] = NullCharacter;	// (threequarters)
	ConvertArray[0xD5] = 0x88;		// (agrave)
	ConvertArray[0xD6] = 0x87;		// (aacute)
	ConvertArray[0xD7] = 0x89;		// (acircumflex)
	ConvertArray[0xD8] = 0x8B;		// (atilde)
	ConvertArray[0xD9] = 0x8A;		// (adieresis)
	ConvertArray[0xDA] = 0x8C;		// (aring)
	ConvertArray[0xDB] = 0x8D;		// (ccedilla)
	ConvertArray[0xDC] = 0x8F;		// (egrave)
	ConvertArray[0xDD] = 0x8E;		// (eacute)
	ConvertArray[0xDE] = 0x90;		// (ecircumflex)
	ConvertArray[0xDF] = 0x91;		// (dieresis)
	//	0xE0
	ConvertArray[0xE0] = 0x93;		// (igrave)
	ConvertArray[0xE1] = 0xAE;		// (AE)
	ConvertArray[0xE2] = 0x92;		// (iacute)
	ConvertArray[0xE3] = 0xBB;		// (ordfeminine)
	ConvertArray[0xE4] = 0x94;		// (icircumflex)
	ConvertArray[0xE5] = 0x95;		// (idieresis)
	ConvertArray[0xE6] = NullCharacter;	// (eth)
	ConvertArray[0xE7] = 0x96;		// (ntilde)
	ConvertArray[0xE8] = NullCharacter;	// (Lslash)
	ConvertArray[0xE9] = 0xAF;		// (Oslash)
	ConvertArray[0xEA] = 0xCE;		// (OE)
	ConvertArray[0xEB] = 0xBC;		// (ordmasculine)
	ConvertArray[0xEC] = 0x98;		// (ograve)
	ConvertArray[0xED] = 0x97;		// (oacute)
	ConvertArray[0xEE] = 0x99;		// (ocircumflex)
	ConvertArray[0xEF] = 0x9B;		// (otilde)
	//	0xF0
	ConvertArray[0xF0] = 0x9A;		// (odieresis)
	ConvertArray[0xF1] = 0xBE;		// (ae)
	ConvertArray[0xF2] = 0x9D;		// (ugrave)
	ConvertArray[0xF3] = 0x9C;		// (uacute)
	ConvertArray[0xF4] = 0x9E;		// (ucircumflex)
	ConvertArray[0xF5] = 0xF5;		// (dotlessi)
	ConvertArray[0xF6] = 0x9F;		// (udieresis)
	ConvertArray[0xF7] = NullCharacter;	// (yacute)
	ConvertArray[0xF8] = NullCharacter;	// (lslash)
	ConvertArray[0xF9] = 0xBF;		// (oslash)
	ConvertArray[0xFA] = 0xCF;		// (oe)
	ConvertArray[0xFB] = 0xA7;		// (germandbls)
	ConvertArray[0xFC] = NullCharacter;	// (thorn)
	ConvertArray[0xFD] = 0xD8;		// (ydieresis)
	ConvertArray[0xFE] = NullCharacter;	// (not assigned)
	ConvertArray[0xFF] = NullCharacter;	// (ascii control char?)
	return self;
}




//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		ConvertCharacter:
//	Parameters:	a character to be converted
//	Returns:		the converted character
//	Stores:		the character that we are returning.
//	Description:
//		This uses the ConvertArray set up in the initialization to convert
//		a character from the NeXT character set to a Mac char.  It allows for converting
//		single quotes to either Mac style (grave accent and single neuter quote) or to
//		curly mac quotes (not in the lower ascii range)  It returns the converted
//		character and a result code based on its succes.
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (Character) ConvertCharacter: (Character) theCharacter
{
	Character	result;
	Boolean		couldconvert = YES;
	[self ResetResults];
	//
	//	Look up our result in the conversion array. 	//
	result = ConvertArray[theCharacter];
	//
	//	If we've been asked to convert ` and ' to nice Mac curly quotes,
	//	rather than Á and ', then see if we got a quote, and change the result
	//	appropriately.
	//
	if (UseCurlyQuotes == YES)
	{
		if (theCharacter == 0x27)
			result = 0xD5;		// (quoteright)
		else if (theCharacter == 0x60)
			result = 0xD4;		// (quoteleft)
	}
	//
	//	Alternately, if we got a null back, and we were give (and we don't didn't
	//	pass a null), indicate we could not convet properly.
	//
	if ((result == NullCharacter) &&  (theCharacter != NullCharacter))
	{
		result = theCharacter;
		couldconvert = NO;
	}
	//
	//	Store the result, and return.
	//
	[self	PutCharacter: result Into: FIRST_RESULT];
	if (couldconvert == YES)
		[self	StoreErrorCode: errOK AndText: "Character converted!"];
	else
		[self	StoreErrorCode: errCANTMAPTOONE AndText: "No equivalent standard Macintosh character"];
	return result;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		ConvertString:WithLength:
//	Parameters:	a pointer to a string of data to be converted
//				the length of the data the poiner poins to.
//	Returns:		a poiner to a new block of data to be converted
//	Stores:		the pointer we are returning
//				the length of the new data
//	Description:
//		This converts the text in the source data area into a new area.
//		This is passed a string of characters.  This then creates a new string,
//		and converts all the source characters from a Macintosh encoding to
//		the destination string.  This differs from ConvertCharacter:, above, int only
//		two ways:  The first is that this processes multiple characters at once.
//		the second is that if it finds a character that's doens't map to a specific
//		alternate character, it will replace it with a multi character string.  E.g.
//		less  than or equal to is replaced with <=  And the apple symbol is just
//		replaced with [apple].  The source string is not altered in any way.  The
//		caller is responsible for disposing of the returned string.
//	Bugs:
//		we don't really check for errors anywhere (not taht there are many oportunities)...
//	History
//		92.12.31	djb	Added a null-character termination to the string.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (Pointer) ConvertString: (Pointer) theData WithLength: (Integer) length
{
	Character*	source = (Character*) theData; // getting a better typed ptr.
	Integer		index;
	PositiveInteger	destindex = 0;
	Integer		destsize = length*1.5;
	Integer		replacelength;
	CString		replacement;
	Character*	temp, *dest = (Character*) NewPointer(destsize);
	Character	result;
	[self	ResetResults];
	for (index = 0; index < length; index++)
	{
		//
		//	Look up our result in the conversion array. 		//
		result = ConvertArray[source[index]];
		//
		//	If we've been asked to convert ` and ' to nice Mac curly quotes,
		//	rather than Á and ', then see if we got a quote, and change the result
		//	appropriately.
		//
		if (UseCurlyQuotes == YES)
		{
			if (source[index] == 0x27)
				result = 0xD5;		// (quoteright)
			else if (source[index] == 0x60)
				result = 0xD4;		// (quoteleft)
		}
		//
		//	If our result is not a null (or if we passed a null), stick the result into
		//	the destination space.  increment the space if necessary.
		//
		if ((result != NullCharacter) ||  (source[index] == NullCharacter))
		{
			if (destindex >= destsize)
			{
				temp = NewPointer(destsize +512);
				memcpy(temp, dest, destsize);
				FreePointer(dest);
				dest = temp;
				destsize += 512;
			}
			dest[destindex] = result;
			destindex++;
		}
		else
		{
			//
			//	We evidently got a null back, telling us 'yo! I can't convert this to
			//	a different single character'.  So, now we determine which it was, and
			//	determine what string to use in its place.  Generally, we use the PS
			//	name for the character in []'s.
			//
			switch (source[index])
			{
				case 0x90:			// (Eth)
					replacement = "[Eth]";
					break;
				case 0x9B:			// (Yacute)
					replacement = "[Yacute]";
					break;
				case 0x9C:			// (Thorn)
					replacement = "[Thorn]";
					break;
				case 0x9E:			// (multiply)
					replacement = "[multiply]";
					break;
				case 0xB5:			// (brokenbar)
					replacement = "[brokenbar]";
					break;
				case 0xC0:			// (onesuperior)
					replacement = "[onesuperior]";
					break;
				case 0xC9:			// (twosuperior)
					replacement = "[twosuperior]";
					break;
				case 0xCC:			// (threesuperior)
					replacement = "[threesuperior]";
					break;
				case 0xD2:			// (onequarter)
					replacement = "[onequarter]";
					break;
				case 0xD3:			// (onehalf)
					replacement = "[onehalf]";
					break;
				case 0xD4:			// (threequarters)
					replacement = "[threequarters]";
					break;
				case 0xE6:			// (eth)
					replacement = "[eth]";
					break;
				case 0xE8:			// (Lslash)
					replacement = "[Lslash]";
					break;
				case 0xF7:			// (yacute)
					replacement = "[yacute]";
					break;
				case 0xF8:			// (lslash)
					replacement = "[lslash]";
					break;
				case 0xFC:			// (thorn)
					replacement = "[thorn]";
					break;
				case 0xFE:			// (not assigned)
					replacement = "[not defined]";
					break;
				case 0xFF:			// (ascii control char?)
					replacement = "[ascii control char]";
					break;				default:
					replacement = "[[THERE IS A BUG IN THE PROIGRAM (no joke)]]";
					break;
			}
			//
			//	With a string in hand to use now, get it's length, and assure that we
			//	have space to store it (if not, increment the memory block's size).
			//	copy the string into the output memory space, and continue.
			//
			replacelength = strlen(replacement);
			if ( (destindex+replacelength) >= destsize)
			{
				temp = NewPointer(destsize + 512);
				memcpy(temp, dest, destsize);
				FreePointer(dest);
				dest = temp;
				destsize = destsize + 512;
			}
			memcpy(&dest[destindex], replacement, strlen(replacement));
			destindex += replacelength;
		}
	}
	//
	//	Added, so that the block of memory returned is also null terminated.
	//
	if (destindex >= destsize)
	{
		temp = NewPointer(destsize +512);
		memcpy(temp, dest, destsize);
		FreePointer(dest);
		dest = temp;
		destsize += 512;
	}
	dest[destindex] = EndOfCString;
	//
	//	Store the result, and return.  (note that destindex always ends up pointing to
	//	the next byte to be used, and thus is also a count of the total number of bytes
	//	in the dest string.
	//
	[self	StorePointer: dest];
	[self	PutPositiveInteger: destindex Into: SECOND_RESULT];
	[self	StoreErrorCode: errOK AndText: "Nothing could go wrong (pathetic program)!"];
	return dest;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		ConvertSingleQuotes:
//	Parameters:	a boolean value
//	Returns:		self
//	Stores:		none
//	Description:
//		This simply allows the user to toggle whether they want to do convert single
//		quotes to pretty curly quotes on the Mac or not.
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- ConvertSingleQuotes:  (Boolean) useCurlyQuotes
{
	UseCurlyQuotes = useCurlyQuotes;
	return self;
}
@end
