File:  [Apple Darwin 0.x] / kernel / bsd / hfs / hfscommon / Unicode / UnicodeWrappers.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs
Tue Apr 24 18:26:06 2018 UTC (8 years, 2 months ago) by root
Branches: MAIN, Apple
CVS tags: HEAD, Darwin03, Darwin01
Darwin 0.1 Mach+BSD kernel

/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * Portions Copyright (c) 1999 Apple Computer, Inc.  All Rights
 * Reserved.  This file contains Original Code and/or Modifications of
 * Original Code as defined in and that are subject to the Apple Public
 * Source License Version 1.1 (the "License").  You may not use this file
 * except in compliance with the License.  Please obtain a copy of the
 * License at http://www.apple.com/publicsource and read it before using
 * this file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

/*
	File:		UnicodeWrappers.c

	Contains:	Wrapper routines for Unicode conversion and comparison.

	Version:	HFS Plus 1.0

	Written by:	Mark Day

	Copyright:	� 1996-1999 by Apple Computer, Inc., all rights reserved.

	File Ownership:

		DRI:				Mark Day

		Other Contact:		Don Brady

		Technology:			xxx put technology here xxx

	Writers:

		(DSH)	Deric Horn
		(msd)	Mark Day
		(djb)	Don Brady

	Change History (most recent first):
      <Rhap>	 2/09/99	djb		Fix UnicodeToMacRoman to handle a terminating decomposed char.
      <Rhap>	 1/22/99	djb		Add more TARGET_OS_MAC conditionals to remove orphaned code.
      <Rhap>	  7/6/98	djb		Handle hi-bit Mac Roman characters in basic latin conversions (radar #2247519).
      <Rhap>	 6/11/98	PPD		Added a few special-case ASCII/Unicode mappings to cover installer's needs.
	  <Rhap>	 3/31/98	djb		Sync up with final HFSVolumes.h header file.

	  <CS41>	 1/28/98	msd		Bug 2207446: When mangling a name, check to see if the Unicode
									Converter is installed before we call it.
	  <CS40>	 1/21/98	msd		Bug 2206836: If a name contains a colon, change it to question
									mark and mangle the name.
	  <CS39>	12/11/97	msd		For Metrowerks and test tools, call the Get_xxx routines to get
									the Unicode table addresses.
	  <CS38>	12/10/97	djb		Radar #2005461, don't use fallback chars when converting to
									Unicode, instead let the client (Catalog) retry with MacRoman.
	  <CS37>	 12/2/97	DSH		Conditionalize out some unicode related routines for DFA
	  <CS36>	11/26/97	djb		Radar #2005461,2005688 don't swallow kTECPartialCharErr errors!
	  <CS35>	11/17/97	djb		Name mangling was broken with decomposed Unicode.
	  <CS34>	11/16/97	djb		Radar #2001928 - use kUnicodeCanonicalDecompVariant variant.
	  <CS33>	11/11/97	DSH		Use Get_gLowerCaseTable for DiskFirstAid builds to avoid loading
									in a branch to the table.
	  <CS32>	 11/7/97	msd		Replace FastSimpleCompareStrings with FastUnicodeCompare (which
									handles ignorable Unicode characters). Remove the wrapper
									routine, CompareUnicodeNames, and have its callers call
									FastUnicodeCompare directly.
	  <CS31>	10/17/97	djb		Change kUnicodeUseHFSPlusMapping to kUnicodeUseLatestMapping.
	  <CS30>	10/17/97	msd		Fix some type casts for char pointers.
	  <CS29>	10/13/97	djb		Add new SPIs for Finder View font (radar #1679073).
	  <CS28>	 10/1/97	djb		Preserve current heap zone in InitializeEncodingContext routine
									(radar #1682686).
	  <CS27>	 9/17/97	djb		Handle kTECPartialCharErr errors in ConvertHFSNameToUnicode.
	  <CS26>	 9/16/97	msd		In MockConvertFromPStringToUnicode, use pragma unused instead of
									commenting out unused parameter (so SC will compile it).
	  <CS25>	 9/15/97	djb		Fix MockConverters to do either 7-bit ascii or else mangle the
									name (radar #1672388). Use 'p2u#' resource for bootstrapping
									Unicode. Make sure InitializeEncodingContext uses System heap.
	  <CS24>	 9/10/97	msd		Make InitializeEncodingContext public.
	  <CS23>	  9/7/97	djb		Handle '�' char in BasicLatinUnicode converter.
	  <CS22>	  9/4/97	djb		Add logging to BasicLatinUnicodeToPascal.
	  <CS21>	 8/26/97	djb		Make FastSimpleCompareStrings faster. Add
									BasicLatinUnicodeToPascal to make 7-bit ascii conversions
									faster.
	  <CS20>	 8/14/97	djb		Add FastRelString here (to be next to the data tables).
	  <CS19>	 7/21/97	djb		LogEndTime now takes an error code.
	  <CS18>	 7/18/97	msd		Include LowMemPriv.h, Gestalt.h, TextUtils.h.
	  <CS17>	 7/16/97	DSH		FilesInternal.i renamed FileMgrInternal.i to avoid name
									collision
	  <CS16>	  7/8/97	DSH		Loading PrecompiledHeaders from define passed in on C line
	  <CS15>	  7/8/97	DSH		InitializeUnicode changed its API
	  <CS14>	  7/1/97	DSH		SC, DFA complier, requires parameters in functions. #pragma'd
									them out to eliminate C warnings.
	  <CS13>	 6/30/97	msd		Remove unused parameter warnings in FallbackProc by commenting
									out unused parameter names.
	  <CS12>	 6/26/97	DSH		FallbackProc declare variables before useage for SC,
									MockConverters no longer static for DFA.
	  <CS11>	 6/25/97	msd		In function InitStaticUnicodeConverter, the variable fsVars was
									being used before being initialized.
	  <CS10>	 6/24/97	DSH		Runtime checks to call through CFM or static linked routines.
	   <CS9>	 6/20/97	msd		Re-introduce fix from <CS7>. Fix another missing cast. Remove a
									spurious semicolon.
	   <CS8>	 6/18/97	djb		Add more ConversionContexts routines. Improved file mangling.
	   <CS7>	 6/16/97	msd		Add a missing cast in GetFileIDString.
	   <CS6>	 6/13/97	djb		Added support for long filenames. Switched to
									ConvertUnicodeToHFSName, ConvertHFSNameToUnicode, and
									CompareUnicodeNames.
	   <CS5>	  6/4/97	djb		Use system script instead of macRoman.
	   <CS4>	 5/19/97	djb		Add call to LockMappingTable so tables won't move!
	   <CS3>	  5/9/97	djb		Include HFSInstrumentation.h
	   <CS2>	  5/7/97	djb		Add summary traces. Add FastSimpleCompareStrings routine.
	   <CS1>	 4/24/97	djb		first checked in
	  <HFS5>	 3/27/97	djb		Add calls to real Unicode conversion routines.
	  <HFS4>	  2/6/97	msd		Add conditional code to use real Unicode comparison routines
									(default to off).
	  <HFS3>	  1/6/97	djb		Fix HFSUnicodeCompare - the final comparison of length1 and
									length2 was backwards.
	  <HFS2>	12/12/96	msd		Use precompiled headers.
	  <HFS1>	12/12/96	msd		first checked in

*/

#if ( PRAGMA_LOAD_SUPPORTED )
        #pragma	load	PrecompiledHeaders
#else
        #if TARGET_OS_MAC
        #include	<CodeFragments.h>
        #include	<Errors.h>
        #include	<MixedModePriv.h>
        #include	<Script.h>
        #include	<UnicodeConverter.h>
        #include	<LowMemPriv.h>
        #include	<Gestalt.h>
        #include	<TextUtils.h>
        #else
        #include "../headers/system/MacOSStubs.h"
        #endif 	/* TARGET_OS_MAC */
#endif	/* PRAGMA_LOAD_SUPPORTED */

#if TARGET_OS_MAC
#include	<IntlResources.h>
#include	<TextCommonPriv.h>
#include	<UnicodeConverterPriv.h>
#include 	<FileMgrResources.h>
#endif 	/* TARGET_OS_MAC */

#if TARGET_OS_RHAPSODY
#include "UCStringCompareData.h"
#endif /* TARGET_OS_RHAPSODY */

#include "../headers/FileMgrInternal.h"
#include "../headers/system/HFSUnicodeWrappers.h"
#include "../headers/system/HFSInstrumentation.h"

#include "ConvertUTF.h"

#ifdef __MWERKS__
#define USE_TABLE_ACCESSORS	1
#endif

#ifndef USE_TABLE_ACCESSORS
#define USE_TABLE_ACCESSORS	0
#endif


#if TARGET_OS_MAC
enum {
	uupCreateTextToUnicodeInfoProcInfo =
		kPascalStackBased
		| RESULT_SIZE(SIZE_CODE(sizeof(OSStatus)))
		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(ConstUnicodeMappingPtr)))
		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(TextToUnicodeInfo*))),


	uppCreateUnicodeToTextInfoProcInfo =
		kPascalStackBased
		| RESULT_SIZE(SIZE_CODE(sizeof(OSStatus)))
		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(ConstUnicodeMappingPtr)))
		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(UnicodeToTextInfo*))),


	uppConvertFromTextToUnicodeProcInfo =
		kPascalStackBased
		| RESULT_SIZE(SIZE_CODE(sizeof(OSStatus)))
		| STACK_ROUTINE_PARAMETER(1,  SIZE_CODE(sizeof(TextToUnicodeInfo)))
		| STACK_ROUTINE_PARAMETER(2,  SIZE_CODE(sizeof(ByteCount)))
		| STACK_ROUTINE_PARAMETER(3,  SIZE_CODE(sizeof(ConstLogicalAddress)))
		| STACK_ROUTINE_PARAMETER(4,  SIZE_CODE(sizeof(OptionBits)))
		| STACK_ROUTINE_PARAMETER(5,  SIZE_CODE(sizeof(ItemCount)))
		| STACK_ROUTINE_PARAMETER(6,  SIZE_CODE(sizeof(ByteOffset*)))
		| STACK_ROUTINE_PARAMETER(7,  SIZE_CODE(sizeof(ItemCount*)))
		| STACK_ROUTINE_PARAMETER(8,  SIZE_CODE(sizeof(ByteOffset*)))
		| STACK_ROUTINE_PARAMETER(9,  SIZE_CODE(sizeof(ByteCount)))
		| STACK_ROUTINE_PARAMETER(10, SIZE_CODE(sizeof(ByteCount*)))
		| STACK_ROUTINE_PARAMETER(11, SIZE_CODE(sizeof(ByteCount*)))
		| STACK_ROUTINE_PARAMETER(12, SIZE_CODE(sizeof(UniCharArrayPtr))),

	 
	uppConvertFromUnicodeToTextProcInfo =
		kPascalStackBased
		| RESULT_SIZE(SIZE_CODE(sizeof(OSStatus)))
		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(UnicodeToTextInfo)))
		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(ByteCount)))
		| STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(ConstUniCharArrayPtr)))
		| STACK_ROUTINE_PARAMETER(4, SIZE_CODE(sizeof(OptionBits)))
		| STACK_ROUTINE_PARAMETER(5, SIZE_CODE(sizeof(ItemCount)))
		| STACK_ROUTINE_PARAMETER(6, SIZE_CODE(sizeof(ByteOffset*)))
		| STACK_ROUTINE_PARAMETER(7, SIZE_CODE(sizeof(ItemCount*)))
		| STACK_ROUTINE_PARAMETER(8, SIZE_CODE(sizeof(ByteOffset*)))
		| STACK_ROUTINE_PARAMETER(9, SIZE_CODE(sizeof(ByteCount)))
		| STACK_ROUTINE_PARAMETER(10, SIZE_CODE(sizeof(ByteCount*)))
		| STACK_ROUTINE_PARAMETER(11, SIZE_CODE(sizeof(ByteCount*)))
		| STACK_ROUTINE_PARAMETER(12, SIZE_CODE(sizeof(LogicalAddress))),


	uppUpgradeScriptInfoToTextEncodingProcInfo =
		kPascalStackBased
		| RESULT_SIZE(SIZE_CODE(sizeof(OSStatus)))
		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(ScriptCode)))
		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(LangCode)))
		| STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(RegionCode)))
		| STACK_ROUTINE_PARAMETER(4, SIZE_CODE(sizeof(ConstStr255Param)))
		| STACK_ROUTINE_PARAMETER(5, SIZE_CODE(sizeof(TextEncoding*))),


	uppRevertTextEncodingToScriptInfoProcInfo =
		kPascalStackBased
		| RESULT_SIZE(SIZE_CODE(sizeof(OSStatus)))
		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(TextEncoding)))
		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(ScriptCode*)))
		| STACK_ROUTINE_PARAMETER(3, SIZE_CODE(sizeof(LangCode*)))
		| STACK_ROUTINE_PARAMETER(4, SIZE_CODE(sizeof(StringPtr))),


// NOTE: this one uses "C" calling conventions...
	uppLockMappingTableProcInfo =
		kCStackBased
		| RESULT_SIZE(SIZE_CODE(sizeof(OSStatus)))
		| STACK_ROUTINE_PARAMETER(1, SIZE_CODE(sizeof(UnicodeMapping*)))
		| STACK_ROUTINE_PARAMETER(2, SIZE_CODE(sizeof(Boolean)))
};
#endif 	/* TARGET_OS_MAC */


enum {
	smLargestScript = 32,
	
	kMinFileExtensionChars = 1,		// does not include dot
	kMaxFileExtensionChars = 5		// does not include dot
};

#define kASCIIPiSymbol				0xB9
#define kASCIIMicroSign				0xB5
#define kASCIIGreekDelta			0xC6


#define Is7BitASCII(c)				( (c) >= 0x20 && (c) <= 0x7F )

#define	IsSpecialASCIIChar(c)		( (c) == (UInt8) kASCIIMicroSign || (c) == (UInt8) kASCIIPiSymbol || (c) == (UInt8) kASCIIGreekDelta )

// Note:	'�' has two Unicode representations 0x00B5 (micro sign) and 0x03BC (greek)
//			'�' has two Unicode representations 0x2206 (increment) and 0x0394 (greek)
#define	IsSpecialUnicodeChar(c)		( (c) == 0x00B5 || (c) == 0x03BC || (c) == 0x03C0 || (c) == 0x2206 || (c) == 0x0394 )

#define IsHexDigit(c)				( ((c) >= (UInt8) '0' && (c) <= (UInt8) '9') || ((c) >= (UInt8) 'A' && (c) <= (UInt8) 'F') )

//
// PToUTable and PToUEntry describe the 'p2u#' resource
// This resource is used to map pascal to Unicode before
// the real Unicode converter is initialize.
//
struct PToUEntry {
	Str31		pascalString;			// pascal representation
	UInt16		unicodeChars;			// unicode char count
	UniChar		unicodeString[63];		// unicode representation
};
typedef struct PToUEntry PToUEntry;


struct PToUTable {
	UInt16		entries;		// number of entries
	PToUEntry	entry[1];
};
typedef struct PToUTable PToUTable;


extern UniChar *	Get_gLowerCaseTable(void);

//	Unicode Glue routines
// WARNING:  These Glue APIs must match the APIs in UnicodeConverter.h (the glue assumes they are the same)
// 			 for example CreateTextToUnicodeInfo_Glue and CreateTextToUnicodeInfo must have identical parameters and calling conventions

extern pascal OSStatus CreateTextToUnicodeInfo_Glue(ConstUnicodeMappingPtr iUnicodeMapping, TextToUnicodeInfo *oTextToUnicodeInfo);
extern pascal OSStatus CreateUnicodeToTextInfo_Glue(ConstUnicodeMappingPtr iUnicodeMapping, UnicodeToTextInfo *oUnicodeToTextInfo);
extern pascal OSStatus ConvertFromTextToUnicode_Glue(TextToUnicodeInfo iTextToUnicodeInfo, ByteCount iSourceLen, ConstLogicalAddress iSourceStr, OptionBits iControlFlags, ItemCount iOffsetCount, ByteOffset iOffsetArray[], ItemCount *oOffsetCount, ByteOffset oOffsetArray[], ByteCount iBufLen, ByteCount *oSourceRead, ByteCount *oUnicodeLen, UniCharArrayPtr oUnicodeStr);
extern pascal OSStatus ConvertFromUnicodeToText_Glue(UnicodeToTextInfo iUnicodeToTextInfo, ByteCount iUnicodeLen, ConstUniCharArrayPtr iUnicodeStr, OptionBits iControlFlags, ItemCount iOffsetCount, ByteOffset iOffsetArray[], ItemCount *oOffsetCount, ByteOffset oOffsetArray[], ByteCount iBufLen, ByteCount *oInputRead, ByteCount *oOutputLen, LogicalAddress oOutputStr);
extern pascal OSStatus UpgradeScriptInfoToTextEncoding_Glue(ScriptCode textScriptID, LangCode textLanguageID, RegionCode regionID, ConstStr255Param textFontname, TextEncoding *encoding);
extern pascal OSStatus RevertTextEncodingToScriptInfo_Glue(TextEncoding encoding, ScriptCode *textScriptID, LangCode *textLanguageID, Str255 textFontname);

// EXCEPTION:  this one uses "C" calling conventions (why?)
extern OSStatus LockMappingTable_Glue(UnicodeMapping *unicodeMappingPtr, Boolean lockIt);

#if TARGET_OS_MAC
static OSErr InitStaticUnicodeConverter(void);
static OSErr InitDynamicUnicodeConverter( Boolean forBootVolume );
#endif	/* TARGET_OS_MAC */


#if TARGET_OS_MAC
static OSErr InstallConversionContexts( FSVarsRec *fsVars );

static OSErr	InstallLibraryVector( CFragConnectionID connectionID, ConstStr255Param symbolName,
									  ProcInfoType procInfo, UniversalProcPtr *vector );

static OSErr	InstallSystemConversionContext( FSVarsRec *fsVars, Boolean forBootVolume );
#endif	/* TARGET_OS_MAC */

static void	GetFilenameExtension( ItemCount length, ConstUniCharArrayPtr unicodeStr, Str15 extStr );

static void	GetFileIDString( HFSCatalogNodeID fileID, Str15 fileIDStr );

static void AppendPascalString( ConstStr15Param src, Str31 dst );

static UInt32 HexStringToInteger( UInt32 length, const UInt8 *hexStr );


pascal OSStatus FallbackProc( UniChar *srcUniStr, ByteCount srcUniStrLen, ByteCount *srcConvLen,
							  TextPtr destStr, ByteCount destStrLen, ByteCount *destConvLen,
							  LogicalAddress contextPtr, ConstUnicodeMappingPtr unicodeMappingPtr );


static OSErr MacRomanToUnicode (ConstStr255Param pascalString, ItemCount *unicodeChars, UniCharArrayPtr unicodeString);
static OSErr UnicodeToMacRoman (ItemCount unicodeChars, ConstUniCharArrayPtr unicodeString, Str31 pascalString);


/*
	Get the base encoding used by the File System
	
	If no HFS Plus volumes have been mounted yet then
	the default encoding could be kTextEncodingUndefined
*/
#if TARGET_OS_MAC
TextEncoding
GetDefaultTextEncoding(void)
{
	FSVarsRec	*fsVars;	

	fsVars = (FSVarsRec*) LMGetFSMVars();

	return fsVars->gDefaultBaseEncoding;
}
#endif


/*
	Set the base encoding used by the File System
*/
#if TARGET_OS_MAC
OSErr
SetDefaultTextEncoding(TextEncoding encoding)
{
	FSVarsRec	*fsVars;
	OSErr		result;


	fsVars = (FSVarsRec*) LMGetFSMVars();


	// undefined only makes sense when Unicode Library is not installed
	if ( encoding == kTextEncodingUndefined )
	{
		if ( fsVars->gIsUnicodeInstalled == false )
			fsVars->gDefaultBaseEncoding = encoding;
			
		return noErr;
	}

 	encoding = GetTextEncodingBasePriv(encoding);
 
	if ( !ValidMacEncoding(encoding) )
		return paramErr;	// we only support Mac encodings!

	// if Unicode Library is installed then setup context for this encoding
	// otherwise it will occur when the first HFS Plus volume gets mounted
	if ( fsVars->gIsUnicodeInstalled )
	{
		result = InitializeEncodingContext( encoding, fsVars );
		if ( result != noErr )
			return result;
	}	

	// make it the default...
	fsVars->gDefaultBaseEncoding = encoding;

	return noErr;
}
#endif /* TARGET_OS_MAC */

/*
	Get the encoding that matches font
*/

#if TARGET_OS_MAC
OSErr
GetTextEncodingForFont( ConstStr255Param fontName, UInt32 * textEncoding )
{
	FSVarsRec *		fsVars;
	OSErr			result;


	fsVars = (FSVarsRec*) LMGetFSMVars();

	// if Unicode Library is installed then we can get the encoding...
	if ( fsVars->gIsUnicodeInstalled )
	{
		result = UpgradeScriptInfoToTextEncoding_Glue (	kTextScriptDontCare,
														kTextLanguageDontCare,
														kTextRegionDontCare,
														fontName,
														textEncoding );
	}
	else // Unicode Library not installed so save font name for later...
	{
		StringPtr	savedFontName;
		UInt16		stringByteSize;
		
		stringByteSize = fontName[0] + 1;
		savedFontName = fsVars->gTextEncodingFontName;

		// if we already had one then get rid of it
		if ( savedFontName != NULL )
			DisposePtr( (Ptr) savedFontName );

		savedFontName = (StringPtr) NewPtrSys( stringByteSize );
		
		if ( savedFontName != NULL )
			BlockMoveData(fontName, savedFontName, stringByteSize);
		
		fsVars->gTextEncodingFontName = savedFontName;
		
		*textEncoding = kTextEncodingUndefined;
		result = noErr;
	}

	return result;
}
#endif /* TARGET_OS_MAC */

/*
	Count the number of encodings installed by the File System
*/
#if TARGET_OS_MAC
ItemCount
CountInstalledEncodings(void)
{
	FSVarsRec	*fsVars;	

	fsVars = (FSVarsRec*) LMGetFSMVars();

	return fsVars->gInstalledEncodings;
}
#endif

/*
	Convert a Unicode string to a Pascal string (Str31) for use in an HFS file/folder name.
*/
#if 0
OSErr
ConvertUnicodeToHFSName( ConstHFSUniStr255Param unicodeName, TextEncoding encoding, HFSCatalogNodeID cnid, Str31 hfsName )
{
	ByteCount		unicodeByteLength;
	ByteCount		pascalSizeLimit;
	OSErr			result;
	
	hfsName[0] = 0;		// in case we get errors, make sure output is valid
	unicodeByteLength = unicodeName->length * sizeof(UniChar);

	if ( unicodeByteLength == 0 )
		return noErr;

	if ( cnid == kHFSRootFolderID )
		pascalSizeLimit = kHFSMaxVolumeNameChars;	// an HFS volume name
	else
		pascalSizeLimit = kHFSMaxFileNameChars;		// an HFS file name

	result = MockConvertFromUnicodeToPString( unicodeByteLength, unicodeName->unicode, hfsName );

	// Check if name was too long or some characters were unrepresentable...
	// if so we need to mangle the name so that the file can be found by
	// name later

	if ( result == kTECOutputBufferFullStatus || result == kTECUsedFallbacksStatus )
	{
		Str15		fileIDStr;				// file ID as a pascal string
		Str15		extStr;					// dot extension as a pascal string

		GetFileIDString(cnid, fileIDStr);
		
		//	Get a filename extension only if it is a file.
		if ( pascalSizeLimit == kHFSMaxFileNameChars)
			GetFilenameExtension(unicodeName->length, unicodeName->unicode, extStr);
		else
			extStr[0] = (UInt8) 0;	// volumes don't have extensions
	
		// calculate free space for filename prefix
		pascalSizeLimit -= StrLength(extStr) + StrLength(fileIDStr);
		
		//	Generate the prefix part of the name (before extension or File ID string).
		//	Since the Unicode converter wasn't installed, use the PString we already have,
		//	shortening it if needed.
		if (hfsName[0] > pascalSizeLimit)
			hfsName[0] = pascalSizeLimit;
		else
			hfsName[0] -= StrLength(extStr);	// remove extension chars (if any) from source
		
		strcat(hfsName, fileIDStr);
		strcat(hfsName, extStr);
		
		result = noErr;
	}

	return result;

} // end ConvertUnicodeToHFSName
#endif

/*
	Convert a Pascal string (Str31, such as a file/folder name) to Unicode.
*/
#if TARGET_OS_MAC
OSErr
ConvertHFSNameToUnicode( ConstStr31Param hfsName, TextEncoding encoding, HFSUniStr255 *unicodeName )
{
	ByteCount	unicodeByteLength;
	OSErr		result;
	

	result = MockConvertFromPStringToUnicode( hfsName, sizeof(unicodeName->unicode), &unicodeByteLength, unicodeName->unicode );
	
	unicodeName->length = unicodeByteLength / sizeof(UniChar);	// Note: from byte count to char count

	return result;

} // end ConvertHFSNameToUnicode
#endif


/*
	MockConvertFromUnicodeToPString
 */
#if TARGET_OS_MAC
OSErr
MockConvertFromUnicodeToPString( ByteCount unicodeLength, ConstUniCharArrayPtr unicodeStr, Str31 pascalStr )
{
	return UnicodeToMacRoman(unicodeLength / sizeof(UniChar), unicodeStr, pascalStr);
}
#endif

/*
	MockConvertFromPStringToUnicode
 */
#if TARGET_OS_MAC
OSErr
MockConvertFromPStringToUnicode(ConstStr31Param pascalStr, ByteCount maxUnicodeLen, ByteCount *actualUnicodeLen, UniCharArrayPtr unicodeStr)
{
#pragma unused(maxUnicodeLen)

	UInt32	unicodeChars;
	OSErr	result;

	result = MacRomanToUnicode (pascalStr, &unicodeChars, unicodeStr);

	*actualUnicodeLen = unicodeChars * sizeof(UniChar);		// return length in bytes

	return result;
}
#endif

/*
	Initialize the Unicode Converter library
	
	If the library cannot be initialized the wrapper code will default to using 7-bit ASCII or mangled names.
	
	WARNING: This cannot be called from within a file system call (since it calls the file system)!
*/
#if TARGET_OS_MAC
OSErr InitUnicodeConverter(Boolean forBootVolume)
{
	FSVarsRec			*fsVars;	
	Handle				resourceHandle;
	long				response;
	OSErr				err;

	
	fsVars = (FSVarsRec*) LMGetFSMVars();

	if ( fsVars->gIsUnicodeInstalled == true )					//	Has Unicode already been installed
		return ( noErr );
	
	err = Gestalt( gestaltSysArchitecture, &response );			//�� Runtime check to load static or dynamic libraries
	if ( (response == gestaltPowerPC) && (err == noErr) )
	{
		SInt32	savedOffsetToUTC;

		if (forBootVolume)
		{
			savedOffsetToUTC = fsVars->offsetToUTC;		// save offset from GMT to local time
			fsVars->offsetToUTC = 0;					// trick CFM into caching an older mod date for the extensions folder

			// For bootstrap PascalToUnicode conversions we need to load the p2u table
			resourceHandle = GetResource ('p2u#', 0);	// get our special mapping table
			if (resourceHandle != NULL)
			{
				HLock(resourceHandle);
				fsVars->gBootPToUTable = *resourceHandle;
			}
		}


		err = InitDynamicUnicodeConverter( forBootVolume );


		if (forBootVolume)
		{
			fsVars->offsetToUTC = savedOffsetToUTC;		// restore offset from GMT to local time

			// The real converters should be online now so we can jettison the table
			if (resourceHandle != NULL)
			{
				HUnlock(resourceHandle);
				fsVars->gBootPToUTable = NULL;
				ReleaseResource(resourceHandle);			// we no longer need this table
			}
		}
	}
	
	if ( (err != noErr) || (response != gestaltPowerPC) )		//	If we got an error or on a 68K mac
	{
		err = InitStaticUnicodeConverter();						//	Init the 68K static version of the converters
	}

	
	return ( err );
}
#endif /* TARGET_OS_MAC */	


/*
	Initialize the Staticly linked 68K Unicode Converter library

	If the library cannot be initialized the wrapper code will default to using 7-bit ASCII or mangled names.
	WARNING: This cannot be called from within a file system call (since it calls the file system)!
*/
#if TARGET_OS_MAC
static OSErr
InitStaticUnicodeConverter(void)
{
	FSVarsRec			*fsVars;	
	THz					savedHeapZone;
	OSErr				err;
	Str31				textEncodingConverterName;

	// use the system context
	savedHeapZone = GetZone();
	SetZone( SystemZone() );

	GetIndString( textEncodingConverterName, kBaseHFSPlusResourceID, rTextEncodingConverterName );	//���need real string!!
	err = InitializeUnicode( textEncodingConverterName );
	ExitOnError( err );

	fsVars = (FSVarsRec*) LMGetFSMVars();
	fsVars->gUseDynamicUnicodeConverters = false;

	err = InstallSystemConversionContext( fsVars, false );
	ExitOnError( err );
	
	fsVars = (FSVarsRec*) LMGetFSMVars();
	fsVars->gIsUnicodeInstalled = true;


ErrorExit:
	
	SetZone( savedHeapZone );
	
	return( err );
}
#endif /* TARGET_OS_MAC */	



/*
	Initialize the PPC CFM dynamically linked 68K Unicode Converter library
	If the library cannot be initialized the wrapper code will default to
	using the 68K staticly linked library.
*/
#if TARGET_OS_MAC
static OSErr
InitDynamicUnicodeConverter( Boolean forBootVolume )
{
	CFragConnectionID	unicodeLib = 0;
	CFragConnectionID	textLib = 0;
	Ptr					tempMainAddr;
	Str255				errMessage;
	FSVarsRec			*fsVars;	
	THz					savedHeapZone;
	OSErr				result;


	fsVars = (FSVarsRec*) LMGetFSMVars();
	
	// use the system context
	savedHeapZone = GetZone();
	SetZone( SystemZone() );
	#if 0
	  (void) BeginSystemMode();
	#endif

	result = GetSharedLibrary("\pUnicodeConverter", kPowerPCCFragArch, kPrivateCFragCopy, &unicodeLib, &tempMainAddr, errMessage);
	ExitOnError( result );

	result = GetSharedLibrary("\pTextCommon", kPowerPCCFragArch, kPrivateCFragCopy, &textLib, &tempMainAddr, errMessage);
	ExitOnError( result );


	fsVars->gUseDynamicUnicodeConverters	= true;

	result = InstallLibraryVector( unicodeLib,
								   "\pCreateTextToUnicodeInfo",
								   uupCreateTextToUnicodeInfoProcInfo,
								   &fsVars->uppCreateTextToUnicodeInfo );
	ExitOnError( result );


	result = InstallLibraryVector( unicodeLib,
								   "\pCreateUnicodeToTextInfo",
								   uppCreateUnicodeToTextInfoProcInfo,
								   &fsVars->uppCreateUnicodeToTextInfo );
	ExitOnError( result );


	result = InstallLibraryVector( unicodeLib,
								   "\pConvertFromTextToUnicode",
								   uppConvertFromTextToUnicodeProcInfo,
								   &fsVars->uppConvertFromTextToUnicode );	// don't set vector yet
	ExitOnError( result );


	result = InstallLibraryVector( unicodeLib,
								   "\pConvertFromUnicodeToText",
								   uppConvertFromUnicodeToTextProcInfo,
								   &fsVars->uppConvertFromUnicodeToText );	// don't set vector yet
	ExitOnError( result );


	result = InstallLibraryVector( unicodeLib,
								   "\pLockMappingTable",
								   uppLockMappingTableProcInfo,
								   &fsVars->uppLockMappingTable );
	ExitOnError( result );


	result = InstallLibraryVector( textLib,
								   "\pUpgradeScriptInfoToTextEncoding",
								   uppUpgradeScriptInfoToTextEncodingProcInfo,
								   &fsVars->uppUpgradeScriptInfoToTextEncoding );
	ExitOnError( result );

  #if 0
	result = InstallLibraryVector( textLib,
								   "\pRevertTextEncodingToScriptInfo",
								   uppRevertTextEncodingToScriptInfoProcInfo,
								   &fsVars->uppRevertTextEncodingToScriptInfo );
	ExitOnError( result );
  #endif
 
	// NOTE:  Real Unicode filename conversion is not enabled yet but that's OK.
	// We can still call the converter library since it finds it's data files by
	// matching file type and creator (not by name).

	result = InstallSystemConversionContext(fsVars, forBootVolume);
	ExitOnError( result );

	fsVars->gIsUnicodeInstalled = true;		//	Unicode binding and setup succesful, and now enabled

	// restore previous context
	#if 0
	  (void) EndSystemMode();
	#endif
	SetZone( savedHeapZone );

	return noErr;

ErrorExit:

	fsVars->uppCreateTextToUnicodeInfo = NULL;
	fsVars->uppCreateUnicodeToTextInfo = NULL;
	fsVars->uppConvertFromTextToUnicode = NULL;
	fsVars->uppConvertFromUnicodeToText = NULL;
	fsVars->uppLockMappingTable = NULL;
	
	if ( unicodeLib )
		(void) CloseConnection( &unicodeLib );

	if ( textLib )
		(void) CloseConnection( &textLib );

	#if 0
	  (void) EndSystemMode();
	#endif
	SetZone( savedHeapZone );
	
	return result;

} // end InitDynamicUnicodeConverter
#endif /* TARGET_OS_MAC */	



//
// Install a conversion context for the system script (call early in boot)
//
#if TARGET_OS_MAC
static OSErr
InstallSystemConversionContext( FSVarsRec *fsVars, Boolean forBootVolume )
{
	TextEncoding		defaultEncoding;
	ScriptCode			script;
	RegionCode			region;
	OSErr				result;

	// The Script Manager does not setup the region until later in boot
	// so if we're booting from HFS Plus we need to "manually" determine
	// the system script and region using the 'itlc' resource.

	if ( forBootVolume )
	{
		Handle		 configResource;
		ItlcRecord	*itlConfig;
		
		configResource = GetResource( 'itlc', 0 );
		
		if ( configResource != NULL )
		{
			itlConfig = (ItlcRecord*) *configResource;
			
			script = itlConfig->itlcSystem;
			region = itlConfig->itlcRegionCode;
	
			ReleaseResource( configResource );
		}
		else
		{
			script = GetScriptManagerVariable(smSysScript);
			region = kTextRegionDontCare;
		}		
	}
	else
	{
		script = GetScriptManagerVariable(smSysScript);
		region = GetScriptManagerVariable(smRegionCode);
	}

	result = UpgradeScriptInfoToTextEncoding_Glue( script,
												   kTextLanguageDontCare,
												   region,
												   NULL,
												   &defaultEncoding );
	if ( result == paramErr )
	{
		// ok, last ditch effort to get an encoding...
		result = UpgradeScriptInfoToTextEncoding_Glue( script,
													   kTextLanguageDontCare,
													   kTextRegionDontCare,
													   NULL,
													   &defaultEncoding );
	}
	ReturnIfError(result);
	
	defaultEncoding = GetTextEncodingBasePriv(defaultEncoding);
	
	result = InitializeEncodingContext( defaultEncoding, fsVars );
	ReturnIfError(result);
	
	if ( defaultEncoding != kTextEncodingMacRoman )
	{
		result = InitializeEncodingContext( kTextEncodingMacRoman, fsVars );	// always install Roman
		ReturnIfError(result);
	}

	// Since a call to set the default text encoding can occur
	// before any HFS Plus volumes are mounted we need to check
	// gDefaultBaseEncoding and gTextEncodingFontName to see if
	// the default encoding needs to change...
	if ( !forBootVolume )
	{
		TextEncoding	requestedEncoding;

		requestedEncoding = fsVars->gDefaultBaseEncoding;

		if ( requestedEncoding == kTextEncodingUndefined  &&  fsVars->gTextEncodingFontName != NULL )
		{
			result = UpgradeScriptInfoToTextEncoding_Glue (	kTextScriptDontCare,
															kTextLanguageDontCare,
															kTextRegionDontCare,
															fsVars->gTextEncodingFontName,
															&requestedEncoding );
			if ( result != noErr )
				requestedEncoding = kTextEncodingUndefined;

			DisposePtr((Ptr) fsVars->gTextEncodingFontName);	// we no longer need font name
			fsVars->gTextEncodingFontName = NULL;
		}

		if ( requestedEncoding != kTextEncodingUndefined  &&  requestedEncoding != defaultEncoding )
		{
			result = InitializeEncodingContext( requestedEncoding, fsVars );
			if ( result == noErr )
				defaultEncoding = requestedEncoding;
		}
	}

	
	// remember the default text encoding...
	fsVars->gDefaultBaseEncoding = defaultEncoding;

	return noErr;

} // end InstallSystemConversionContext
#endif /* TARGET_OS_MAC */	



//
// Install any addition scripts that were installed by the system (call late in boot)
//
#if TARGET_OS_MAC
OSErr
InstallConversionContextsForInstalledScripts( void )
{
	FSVarsRec*		fsVars;
	TextEncoding	encoding;
	UInt32			scriptCount;
	ScriptCode		systemScript;
	ScriptCode		script;
	OSErr			result;


	fsVars = (FSVarsRec*) LMGetFSMVars();

	scriptCount = GetScriptManagerVariable(smEnabled);
	systemScript = GetScriptManagerVariable(smSysScript);

	script = 0;

	while ( (scriptCount > 0) && (script <= smLargestScript) )
	{
		if ( GetScriptVariable(script, smScriptEnabled) != 0 )	// is this script enabled?
		{		
			--scriptCount;

			if ( script != systemScript )	// we already did systemScript
			{
				LangCode	language;

				language = GetScriptVariable( script, smScriptLang );
			
				result = UpgradeScriptInfoToTextEncoding_Glue( script, language, kTextRegionDontCare, NULL, &encoding );
				if ( result == noErr )
					(void) InitializeEncodingContext( encoding, fsVars );
			}
		}

		++script;	// on to the next one
	}

	return noErr;

} // end InstallConversionContextsForInstalledScripts
#endif /* TARGET_OS_MAC */	



//
// Install any addition scripts that were used by an HFS Plus volume (called at volume mount time)
//
#if TARGET_OS_MAC
OSErr
InstallVolumeConversionContexts( UInt64 encodingsBitmap )
{
	FSVarsRec *	fsVars;
	UInt32		encodingMask;
	UInt32		index;
	UInt32		encoding;
	OSErr		result;


	fsVars = (FSVarsRec*) LMGetFSMVars();

	index = 0;
	encodingMask = 1;

	while ( encodingMask != 0 )
	{
		if ( encodingMask & encodingsBitmap.lo )							// encodings 0 - 31
		{
			encoding = MapIndexToEncoding(index);
			result = InitializeEncodingContext( encoding, fsVars );
		}

		if	( encodingMask & encodingsBitmap.hi )							// encodings 32 - 64
		{
			encoding = MapIndexToEncoding(index + 32);
			result = InitializeEncodingContext( encoding, fsVars );
		}

		encodingMask = encodingMask << 1;
		++index;
	}

	return noErr;

} // end InstallVolumeConversionContexts
#endif /* TARGET_OS_MAC */	


//
// Initialze a conversion context for a single encoding (if not already installed)
//
#if TARGET_OS_MAC
OSErr InitializeEncodingContext( TextEncoding encoding, FSVarsRec *fsVars )
{
	UInt32			index;
	UnicodeMapping	unicodeMapping;
	THz				savedHeapZone;
	OSStatus		result;


	encoding = GetTextEncodingBasePriv( encoding );
	index = MapEncodingToIndex(encoding);

	if ( fsVars->gConversionContext[index].toUnicode != 0 )
		return noErr;	// this one is already installed!

	savedHeapZone = GetZone();
	SetZone( SystemZone() );	// always use the system heap since Conversion Contexts are global


	// Note: by default kTextEncodingUnicodeV2_0 allows corporate use characters
	unicodeMapping.unicodeEncoding = CreateTextEncodingPriv( kTextEncodingUnicodeV2_0, kUnicodeCanonicalDecompVariant, 0 );
	unicodeMapping.otherEncoding = encoding;
	unicodeMapping.mappingVersion = kUnicodeUseHFSPlusMapping;


	if ( fsVars->gUseDynamicUnicodeConverters == false )
		result = LockMappingTable( &unicodeMapping, true );
	else
		result = LockMappingTable_Glue( &unicodeMapping, true );
	ExitOnError( result );
	
	result = CreateTextToUnicodeInfo_Glue( &unicodeMapping, &fsVars->gConversionContext[index].toUnicode );
	ExitOnError( result );

	result = CreateUnicodeToTextInfo_Glue( &unicodeMapping, &fsVars->gConversionContext[index].fromUnicode );
	ExitOnError( result );

	++(fsVars->gInstalledEncodings);	// keep track of how many we've installed

#if 0	
	result = SetFallbackUnicodeToText( fsVars->gConversionContext[index].fromUnicode,
									   NewUnicodeToTextFallbackProc(FallbackProc),	// since we are compiled 68K no routine descriptor is needed
									   kUnicodeFallbackCustomOnly,
									   NULL );

#endif

ErrorExit:

	SetZone( savedHeapZone );

	return result;

} // end InitializeEncodingContext
#endif /* TARGET_OS_MAC */	


#if TARGET_OS_MAC
pascal OSStatus
FallbackProc( UniChar * srcUniStr, ByteCount srcUniStrLen, ByteCount *srcConvLen,
			  TextPtr destStr, ByteCount destStrLen, ByteCount *destConvLen,
			  LogicalAddress contextPtr, ConstUnicodeMappingPtr unicodeMappingPtr)
{
	#pragma unused (srcUniStr, destStrLen, contextPtr, unicodeMappingPtr)
	*srcConvLen = srcUniStrLen;
	*destStr = '_';
	*destConvLen = sizeof(unsigned char);

	return noErr;
}
#endif

#if TARGET_OS_MAC
static OSErr
InstallLibraryVector ( CFragConnectionID connectionID, ConstStr255Param symbolName, ProcInfoType procInfo, UniversalProcPtr *vector)
{
	UniversalProcPtr	routineDescriptor;
	CFragSymbolClass	symClass;
	ProcPtr				tVector;
	OSErr				result;
	

	result = FindSymbol(connectionID, symbolName, (Ptr *) &tVector, &symClass);
	ReturnIfError( result );

	routineDescriptor = NewRoutineDescriptorTrap(tVector, procInfo, kPowerPCISA);
	
	if ( routineDescriptor != NULL )
		*vector = routineDescriptor;
	else
		result = memFullErr;
		
	return result;
}
#endif


//
// Get filename extension (if any) as a pascal string
//
#if TARGET_OS_MAC
static void
GetFilenameExtension( ItemCount length, ConstUniCharArrayPtr unicodeStr, Str15 extStr )
{
	UInt32	i;
	UniChar	c;
	UInt16	extChars;			// number of extension characters (excluding the dot)
	UInt16	maxExtChars;
	Boolean	foundExtension;


	extStr[0] = (UInt8) 0;		// assume there's no extension

	if ( length < 3 )
		return;					// sorry, "x.y" is smallest possible extension	
	
	if ( length < (kMaxFileExtensionChars + 2) )
		maxExtChars = length - 2;	// we need at least one prefix character and dot
	else
		maxExtChars = kMaxFileExtensionChars;

	i = length;
	extChars = 0;
	foundExtension = false;

	while ( extChars <= maxExtChars )
	{
		c = unicodeStr[--i];

		if ( c == (UniChar) '.' )		// look for leading dot
		{
			if ( extChars > 0 )			// cannot end with a dot
				foundExtension = true;
			break;
		}

		if ( Is7BitASCII(c) || IsSpecialUnicodeChar(c) )
			++extChars;
		else
			break;
	}
	
	// if we found one then copy it
	if ( foundExtension )
	{
		UInt8 *extStrPtr = extStr;
		const UniChar *unicodeStrPtr = &unicodeStr[i];	// point to dot char
		
		*(extStrPtr++) = extChars + 1;		// set length to extension chars plus dot

		for ( i = 0; i <= extChars; ++i )
		{
			c = *(unicodeStrPtr++);
			
			// map any special characters
			switch (c)
			{
				case 0x00B5:			// micro sign
				case 0x03BC:			// greek mu
					c = (UniChar) '�';
					break;

				case 0x03C0:			// greek pi
					c = (UniChar) '�';
					break;

				case 0x2206:			// increment sign
				case 0x0394:			// greek capital delta
					c = (UniChar) '�';
					break;
			}

			*(extStrPtr++) = (UInt8) c;		// copy/convert to ascii
		}
	}

} // end GetFilenameExtension
#endif /* TARGET_OS_MAC */


//
// Count filename extension characters (if any)
//
static UInt32
CountFilenameExtensionChars( const unsigned char * filename )
{
	UInt32	i;
	UniChar	c;
	UInt32	extChars;			// number of extension characters (excluding the dot)
	UInt32	length;
	UInt16	maxExtChars;
	Boolean	foundExtension;


	length = strlen(filename);

	if ( length < 3 )
		return 0;					// sorry, "x.y" is smallest possible extension	
	
	if ( length < (kMaxFileExtensionChars + 2) )
		maxExtChars = length - 2;	// we need at least on prefix character and dot
	else
		maxExtChars = kMaxFileExtensionChars;

	extChars = 0;				// assume there's no extension
	i = length - 1;				// index to last ascii character
	foundExtension = false;

	while ( extChars <= maxExtChars )
	{
		c = filename[i--];

		if ( c == (UInt8) '.' )		// look for leading dot
		{
			if ( extChars > 0 )			// cannot end with a dot
				return (extChars);

			break;
		}

		if ( Is7BitASCII(c) || IsSpecialASCIIChar(c) )
			++extChars;
		else
			break;
	}
	
	return 0;

} // end CountFilenameExtensionChars


//
// Convert file ID into a hexidecimal string with no leading zeros
//
#if TARGET_OS_MAC
static void
GetFileIDString( HFSCatalogNodeID fileID, Str15 fileIDStr )
{
	SInt32	i, b;
	static UInt8 *translate = (UInt8 *) "0123456789ABCDEF";
	UInt8	c;
	
	fileIDStr[1] = '#';

	for ( i = 1, b = 28; b >= 0; b -= 4 )
	{
		c = *(translate + ((fileID >> b) & 0x0000000F));
		
		// if its not a leading zero add it to our string
		if ( (c != (UInt8) '0') || (i > 1) || (b == 0) )
			fileIDStr[++i] = c;
	}

	fileIDStr[0] = (UInt8) i;

} // end GetFileIDString
#endif /* TARGET_OS_MAC */


//
// Append a suffix to a pascal string
//
#if TARGET_OS_MAC
static void
AppendPascalString( ConstStr15Param src, Str31 dst )
{
	UInt32	i, j;
	UInt32	srcLen;
	
	srcLen = StrLength(src);
	
	if ( (srcLen + StrLength(dst)) > 31 )	// safety net
		return;
	
	i = dst[0] + 1;		// get end of dst
	
	for (j = 1; j <= srcLen; ++j)
		dst[i++] = src[j];
		
	dst[0] += srcLen;

} // end AppendPascalString
#endif /* TARGET_OS_MAC */


HFSCatalogNodeID
GetEmbeddedFileID(const unsigned char * filename, UInt32 *prefixLength)
{
	short	length;
	short	extChars;
	short	i;
	UInt8	c;			// current character in filename

	*prefixLength = 0;

	if ( filename == NULL )
		return 0;

	length = strlen(filename);

	if ( length < 4 )
		return 0;		// too small to have a file ID

	if ( length >= 6 )	// big enough for a file ID (#10) and an extension (.x) ?
		extChars = CountFilenameExtensionChars(filename);
	else
		extChars = 0;
	
	if ( extChars > 0 )
		length -= (extChars + 1);	// skip dot plus extension characters

	// scan for file id digits...
	for ( i = length - 1; i >= 0; --i)
	{
		c = filename[i];

		if ( c == '#' )		// look for file ID marker
		{
			if ( (length - i) < 3 )
				break;		// too small to be a file ID

			*prefixLength = i;
			return HexStringToInteger(length - i - 1, &filename[i+1]);
		}

		if ( !IsHexDigit(c) )
			break;			// file ID string must have hex digits	
	}

	return 0;

} // end GetEmbeddedFileID


//_______________________________________________________________________

static UInt32
HexStringToInteger (UInt32 length, const UInt8 *hexStr)
{
	UInt32		value;	// decimal value represented by the string
	short		i;
	UInt8		c;		// next character in buffer
	const UInt8	*p;		// pointer to character string

	value = 0;
	p = hexStr;

	for ( i = 0; i < length; ++i )
	{
		c = *p++;

		if (c >= '0' && c <= '9')
		{
			value = value << 4;
			value += (UInt32) c - (UInt32) '0';
		}
		else if (c >= 'A' && c <= 'F')
		{
			value = value << 4;
			value += 10 + ((unsigned int) c - (unsigned int) 'A');
		}
		else
		{
			return 0;	// oops, how did this character get in here?
		}
	}

	return value;

} // end HexStringToInteger


//_______________________________________________________________________
//
//	Routine:	FastRelString
//
//	Output:		returns -1 if str1 < str2
//				returns  1 if str1 > str2
//				return	 0 if equal
//
//_______________________________________________________________________

#if USE_TABLE_ACCESSORS
	UInt16 *Get_gCompareTable(void);
#else
	extern unsigned short gCompareTable[];
#endif

SInt32	FastRelString( ConstStr255Param str1, ConstStr255Param str2 )
{
	UInt16*			compareTable;
	SInt32	 		bestGuess;
	UInt8 	 		length, length2;

	
	length = *(str1++);
	length2 = *(str2++);

	if (length == length2)
		bestGuess = 0;
	else if (length < length2)
		bestGuess = -1;
	else
	{
		bestGuess = 1;
		length = length2;
	}

#if USE_TABLE_ACCESSORS
	compareTable = Get_gCompareTable();
#else
	compareTable = (UInt16*) gCompareTable;
#endif

	while (length--)
	{
		UInt8	aChar, bChar;

		aChar = *(str1++);
		bChar = *(str2++);
		
		if (aChar != bChar)		//	If they don't match exacly, do case conversion
		{	
			UInt16	aSortWord, bSortWord;

			aSortWord = compareTable[aChar];
			bSortWord = compareTable[bChar];

			if (aSortWord > bSortWord)
				return 1;

			if (aSortWord < bSortWord)
				return -1;
		}
		
		//	If characters match exactly, then go on to next character immediately without
		//	doing any extra work.
	}
	
	//	if you got to here, then return bestGuess
	return bestGuess;
}	



/* 0x00A0 - 0x00FF = Latin 1 Supplement (30 total) */
UInt8 gLatin1Table[] =
{                 /*  0     1     2     3     4     5     6     7     8     9     A     B     C     D     E     F  */
	/* 0x00A0 */	0xCA, 0xC1, 0xA2, 0xA3, 0xDB, 0xB4,  '?', 0xA4, 0xAC, 0xA9, 0xBB, 0xC7, 0xC2,  '?', 0xA8, 0xF8,
	/* 0x00B0 */	0xA1, 0XB1,  '?',  '?', 0xAB, 0xB5, 0xA6, 0xe1, 0xFC,  '?', 0xBC, 0xC8,  '?',  '?',  '?', 0xC0,
	/* 0x00C0 */	 '?',  '?',  '?',  '?',  '?',  '?', 0xAE,  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',
	/* 0x00D0 */	 '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?', 0xAF,  '?',  '?',  '?',  '?',  '?',  '?', 0xA7,
	/* 0x00E0 */	 '?',  '?',  '?',  '?',  '?',  '?', 0xBE,  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',
	/* 0x00F0 */	 '?',  '?',  '?',  '?',  '?',  '?',  '?', 0xD6, 0xBF,  '?',  '?',  '?',  '?',  '?',  '?',  '?'
};


/* 0x02C0 - 0x02DF = Spacing Modifiers (8 total) */
UInt8 gSpaceModsTable[] =
{                 /*  0     1     2     3     4     5     6     7     8     9     A     B     C     D     E     F  */
	/* 0x02C0 */	 '?',  '?',  '?',  '?',  '?',  '?', 0xF6, 0xFF,  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',
	/* 0x02D0 */	 '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?', 0xF9, 0xFA, 0xFB, 0xFE, 0xF7, 0xFD,  '?',  '?'
};


/* 0x20xx = General Punctuation (16 total) */
UInt8 gPunctTable[] =
{                 /*  0     1     2     3     4     5     6     7     8     9     A     B     C     D     E     F  */
	/* 0x2000 */	 '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',
	/* 0x2010 */	 '?',  '?',  '?', 0xd0, 0xd1,  '?',  '?',  '?', 0xd4, 0xd5, 0xe2,  '?', 0xd2, 0xd3, 0xe3,  '?',
	/* 0x2020 */	0xa0, 0xe0, 0xa5,  '?',  '?',  '?', 0xc9,  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',
	/* 0x2030 */	0xe4,  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?', 0xdc, 0xdd,  '?',  '?',  '?',  '?',  '?',
	/* 0x2040 */	 '?',  '?',  '?',  '?', 0xda,  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?'
};


/* 0x2200 = Mathematical Operators (11 total) */
UInt8 gMathTable[] =
{                 /*  0     1     2     3     4     5     6     7     8     9     A     B     C     D     E     F  */
	/* 0x2200 */	 '?',  '?', 0xb6,  '?',  '?',  '?', 0xc6,  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?', 0xb8,
	/* 0x2210 */	 '?', 0xb7,  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?', 0xc3,  '?',  '?',  '?', 0xb0,  '?',
	/* 0x2220 */	 '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?', 0xba,  '?',  '?',  '?',  '?',
	/* 0x2230 */	 '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',
	/* 0x2240 */	 '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?', 0xc5,  '?',  '?',  '?',  '?',  '?',  '?',  '?',
	/* 0x2250 */	 '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',
	/* 0x2260 */	0xad,  '?',  '?',  '?', 0xb2, 0xb3,  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?'
};


/* */
UInt8 gReverseCombTable[] =
{                 /*  0     1     2     3     4     5     6     7     8     9     A     B     C     D     E     F  */
	/* 0x40 */		0xDA, 0x40, 0xDA, 0xDA, 0xDA, 0x56, 0xDA, 0xDA, 0xDA, 0x6C, 0xDA, 0xDA, 0xDA, 0xDA, 0x82, 0x98,
	/* 0x50 */		0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xAE, 0xDA, 0xDA, 0xDA, 0xC4, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA,
	/* 0x60 */		0xDA, 0x4B, 0xDA, 0xDA, 0xDA, 0x61, 0xDA, 0xDA, 0xDA, 0x77, 0xDA, 0xDA, 0xDA, 0xDA, 0x8D, 0xA3,
	/* 0x70 */		0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xB9, 0xDA, 0xDA, 0xDA, 0xCF, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA,

	/* Combining Diacritical Marks (0x0300 - 0x030A) */

	/*                0     1     2     3     4     5     6     7     8     9     A  */
	/*  'A'   */
	/* 0x0300 */	0xCB, 0xE7, 0xE5, 0xCC,  '?',  '?',  '?',  '?', 0x80,  '?', 0x81,

	/*  'a'   */
	/* 0x0300 */	0x88, 0x87, 0x89, 0x8B,  '?',  '?',  '?',  '?', 0x8A,  '?', 0x8C,

	/*  'E'   */
	/* 0x0300 */	0xE9, 0x83, 0xE6,  '?',  '?',  '?',  '?',  '?', 0xE8,  '?',  '?',

	/*  'e'   */
	/* 0x0300 */	0x8F, 0x8E, 0x90,  '?',  '?',  '?',  '?',  '?', 0x91,  '?',  '?',

	/*  'I'   */
	/* 0x0300 */	0xED, 0xEA, 0xEB,  '?',  '?',  '?',  '?',  '?', 0xEC,  '?',  '?',

	/*  'i'   */
	/* 0x0300 */	0x93, 0x92, 0x94,  '?',  '?',  '?',  '?',  '?', 0x95,  '?',  '?',

	/*  'N'   */
	/* 0x0300 */	 '?',  '?',  '?', 0x84,  '?',  '?',  '?',  '?',  '?',  '?',  '?',

	/*  'n'   */
	/* 0x0300 */	 '?',  '?',  '?', 0x96,  '?',  '?',  '?',  '?',  '?',  '?',  '?',

	/*  'O'   */
	/* 0x0300 */	0xF1, 0xEE, 0xEF, 0xCD,  '?',  '?',  '?',  '?', 0x85,  '?',  '?',

	/*  'o'   */
	/* 0x0300 */	0x98, 0x97, 0x99, 0x9B,  '?',  '?',  '?',  '?', 0x9A,  '?',  '?',

	/*  'U'   */
	/* 0x0300 */	0xF4, 0xF2, 0xF3,  '?',  '?',  '?',  '?',  '?', 0x86,  '?',  '?',
	
	/*  'u'   */
	/* 0x0300 */	0x9D, 0x9C, 0x9E,  '?',  '?',  '?',  '?',  '?', 0x9F,  '?',  '?',

	/*  'Y'   */
	/* 0x0300 */	 '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?', 0xD9,  '?',  '?',

	/*  'y'   */
	/* 0x0300 */	 '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?', 0xD8,  '?',  '?',

	/*  else  */
	/* 0x0300 */	 '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?',  '?'
};


static OSErr UnicodeToMacRoman (ItemCount unicodeChars, ConstUniCharArrayPtr unicodeString, Str31 pascalString)
{
	UInt8			*p;
	const UniChar	*u;
	UniChar			c;
	UniChar			mask;
	UInt16			inputChars;
	UInt16			pascalChars;
	OSErr			result = noErr;
	UInt8			lsb;
	UInt8			prevChar;
	UInt8			mc;


	mask = (UniChar) 0xFF80;
	p = &pascalString[1];
	u = unicodeString;
	inputChars = unicodeChars;
	pascalChars = prevChar = 0;
	
	while (inputChars) {
		c = *(u++);
		lsb = (UInt8) c;

		/*
		 * If its not 7-bit ascii, then we need to map it
		 */
		if ( c & mask ) {
			mc = '?';
			switch (c & 0xFF00) {
				case 0x0000:
					if (lsb >= 0xA0)
						mc = gLatin1Table[lsb - 0xA0];
					break;

				case 0x0200:
					if (lsb >= 0xC0 && lsb <= 0xDF)
						mc = gSpaceModsTable[lsb - 0xC0];
					break;

				case 0x2000:
					if (lsb <= 0x4F)
						mc = gPunctTable[lsb];
					break;

				case 0x2200:
					if (lsb <= 0x6F)
						mc = gMathTable[lsb];
					break;

				case 0x0300:
					if (c <= 0x030A) {
						if (prevChar >= 'A' && prevChar < 'z') {
							mc = gReverseCombTable[gReverseCombTable[prevChar - 0x40] + lsb];
							--p;	/* backup over base char */
							--pascalChars;
						}
					}
					else {
						switch (c) {
							case 0x0327:	/* combining cedilla */
								if (prevChar == 'C')
									mc = 0x82;
								else if (prevChar == 'c')
									mc = 0x8D;
								else
									break;
								--p;	/* backup over base char */
								--pascalChars;
								break;

							case 0x03A9: mc = 0xBD; break;	/* omega */

							case 0x03C0: mc = 0xB9; break;	/* pi */
						}
					}
					break;
					
				default:
					switch (c) {
						case 0x0131: mc = 0xf5; break;	/* dotless i */

						case 0x0152: mc = 0xce; break;	/* OE */

						case 0x0153: mc = 0xcf; break;	/* oe */

						case 0x0192: mc = 0xc4; break;	/* � */

						case 0x2122: mc = 0xaa; break;	/* TM */

						case 0x25ca: mc = 0xd7; break;	/* diamond */

						case 0xf8ff: mc = 0xf0; break;	/* apple logo */

						case 0xfb01: mc = 0xde; break;	/* fi */

						case 0xfb02: mc = 0xdf; break;	/* fl */
					}
			} /* end switch (c & 0xFF00) */
			
			/*
			 * If we have an unmapped character then we need to mangle the name...
			 */
			if (mc == '?')
				result = kTECUsedFallbacksStatus;
			
			prevChar = 0;
			lsb = mc;

		} /* end if (c & mask) */
		else {
			prevChar = lsb;
		}

		if (pascalChars >= 31)
			break;

		*(p++) = lsb;
		++pascalChars;
		--inputChars;

	} /* end while */
	
	pascalString[0] = pascalChars;
	
	if (inputChars > 0)
		result = kTECOutputBufferFullStatus;	/* ran out of room! */

	return result;
}


UniChar	gHiBitBaseUnicode[128] =
{
	/* 0x80 */	0x0041, 0x0041, 0x0043, 0x0045, 0x004e, 0x004f, 0x0055, 0x0061, 
	/* 0x88 */	0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0063, 0x0065, 0x0065, 
	/* 0x90 */	0x0065, 0x0065, 0x0069, 0x0069, 0x0069, 0x0069, 0x006e, 0x006f, 
	/* 0x98 */	0x006f, 0x006f, 0x006f, 0x006f, 0x0075, 0x0075, 0x0075, 0x0075, 
	/* 0xa0 */	0x2020, 0x00b0, 0x00a2, 0x00a3, 0x00a7, 0x2022, 0x00b6, 0x00df, 
	/* 0xa8 */	0x00ae, 0x00a9, 0x2122, 0x00b4, 0x00a8, 0x2260, 0x00c6, 0x00d8, 
	/* 0xb0 */	0x221e, 0x00b1, 0x2264, 0x2265, 0x00a5, 0x00b5, 0x2202, 0x2211, 
	/* 0xb8 */	0x220f, 0x03c0, 0x222b, 0x00aa, 0x00ba, 0x03a9, 0x00e6, 0x00f8, 
	/* 0xc0 */	0x00bf, 0x00a1, 0x00ac, 0x221a, 0x0192, 0x2248, 0x2206, 0x00ab, 
	/* 0xc8 */	0x00bb, 0x2026, 0x00a0, 0x0041, 0x0041, 0x004f, 0x0152, 0x0153, 
	/* 0xd0 */	0x2013, 0x2014, 0x201c, 0x201d, 0x2018, 0x2019, 0x00f7, 0x25ca, 
	/* 0xd8 */	0x0079, 0x0059, 0x2044, 0x00a4, 0x2039, 0x203a, 0xfb01, 0xfb02, 
	/* 0xe0 */	0x2021, 0x00b7, 0x201a, 0x201e, 0x2030, 0x0041, 0x0045, 0x0041, 
	/* 0xe8 */	0x0045, 0x0045, 0x0049, 0x0049, 0x0049, 0x0049, 0x004f, 0x004f, 
	/* 0xf0 */	0xf8ff, 0x004f, 0x0055, 0x0055, 0x0055, 0x0131, 0x02c6, 0x02dc, 
	/* 0xf8 */	0x00af, 0x02d8, 0x02d9, 0x02da, 0x00b8, 0x02dd, 0x02db, 0x02c7
};

UniChar	gHiBitCombUnicode[128] =
{
	/* 0x80 */	0x0308, 0x030a, 0x0327, 0x0301, 0x0303, 0x0308, 0x0308, 0x0301, 
	/* 0x88 */	0x0300, 0x0302, 0x0308, 0x0303, 0x030a, 0x0327, 0x0301, 0x0300, 
	/* 0x90 */	0x0302, 0x0308, 0x0301, 0x0300, 0x0302, 0x0308, 0x0303, 0x0301, 
	/* 0x98 */	0x0300, 0x0302, 0x0308, 0x0303, 0x0301, 0x0300, 0x0302, 0x0308, 
	/* 0xa0 */	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
	/* 0xa8 */	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
	/* 0xb0 */	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
	/* 0xb8 */	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
	/* 0xc0 */	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
	/* 0xc8 */	0x0000, 0x0000, 0x0000, 0x0300, 0x0303, 0x0303, 0x0000, 0x0000, 
	/* 0xd0 */	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
	/* 0xd8 */	0x0308, 0x0308, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 
	/* 0xe0 */	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0302, 0x0302, 0x0301, 
	/* 0xe8 */	0x0308, 0x0300, 0x0301, 0x0302, 0x0308, 0x0300, 0x0301, 0x0302, 
	/* 0xf0 */	0x0000, 0x0300, 0x0301, 0x0302, 0x0300, 0x0000, 0x0000, 0x0000, 
	/* 0xf8 */	0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000
};


static OSErr MacRomanToUnicode (ConstStr255Param pascalString, ItemCount *unicodeChars, UniCharArrayPtr unicodeString)
{
	const UInt8		*p;
	UniChar			*u;
	UInt16			pascalChars;
	UInt8			c;


	p = pascalString;
	u = unicodeString;

	*unicodeChars = pascalChars = *(p++);	// pick up length byte

	while (pascalChars--) {
		c = *(p++);

		if ( (SInt8) c >= 0 ) {		// make sure its seven bit ascii
			*(u++) = (UniChar) c;		//  pad high byte with zero
		}
		else {	/* its a hi bit character */
			UniChar uc;

			c &= 0x7F;
			*(u++) = uc = gHiBitBaseUnicode[c];
			
			/*
			 * if the unicode character (uc) is an alpha char
			 * then we have an additional combining character
			 */
			if ((uc <= (UniChar) 'z') && (uc >= (UniChar) 'A')) {
				*(u++) = gHiBitCombUnicode[c];
				++(*unicodeChars);
			}
		}
	}
	
	return noErr;
}



//
//	FastUnicodeCompare - Compare two Unicode strings; produce a relative ordering
//
//	    IF				RESULT
//	--------------------------
//	str1 < str2		=>	-1
//	str1 = str2		=>	 0
//	str1 > str2		=>	+1
//
//	The lower case table starts with 256 entries (one for each of the upper bytes
//	of the original Unicode char).  If that entry is zero, then all characters with
//	that upper byte are already case folded.  If the entry is non-zero, then it is
//	the _index_ (not byte offset) of the start of the sub-table for the characters
//	with that upper byte.  All ignorable characters are folded to the value zero.
//
//	In pseudocode:
//
//		Let c = source Unicode character
//		Let table[] = lower case table
//
//		lower = table[highbyte(c)]
//		if (lower == 0)
//			lower = c
//		else
//			lower = table[lower+lowbyte(c)]
//
//		if (lower == 0)
//			ignore this character
//
//	To handle ignorable characters, we now need a loop to find the next valid character.
//	Also, we can't pre-compute the number of characters to compare; the string length might
//	be larger than the number of non-ignorable characters.  Further, we must be able to handle
//	ignorable characters at any point in the string, including as the first or last characters.
//	We use a zero value as a sentinel to detect both end-of-string and ignorable characters.
//	Since the File Manager doesn't prevent the NUL character (value zero) as part of a filename,
//	the case mapping table is assumed to map u+0000 to some non-zero value (like 0xFFFF, which is
//	an invalid Unicode character).
//
//	Pseudocode:
//
//		while (1) {
//			c1 = GetNextValidChar(str1)			//	returns zero if at end of string
//			c2 = GetNextValidChar(str2)
//
//			if (c1 != c2) break					//	found a difference
//
//			if (c1 == 0)						//	reached end of string on both strings at once?
//				return 0;						//	yes, so strings are equal
//		}
//
//		// When we get here, c1 != c2.  So, we just need to determine which one is less.
//		if (c1 < c2)
//			return -1;
//		else
//			return 1;
//

#if TARGET_OS_MAC
extern void gLowerCaseTable(void);		//	Really an exported data symbol, a table
#else
extern UInt16 gLowerCaseTable[];
#endif

SInt32 FastUnicodeCompare ( register ConstUniCharArrayPtr str1, register ItemCount length1,
							register ConstUniCharArrayPtr str2, register ItemCount length2)
{
	register UInt16		c1,c2;
	register UInt16		temp;
	register UInt16*	lowerCaseTable;

#if ( FORDISKFIRSTAID || USE_TABLE_ACCESSORS)	
	lowerCaseTable = Get_gLowerCaseTable();
#else
	lowerCaseTable = (UInt16*) gLowerCaseTable;
#endif

	while (1) {
		//	Set default values for c1, c2 in case there are no more valid chars
		c1 = 0;
		c2 = 0;
		
		//	Find next non-ignorable char from str1, or zero if no more
		while (length1 && c1 == 0) {
			c1 = *(str1++);
			--length1;
			if ((temp = lowerCaseTable[c1>>8]) != 0)		//	is there a subtable for this upper byte?
				c1 = lowerCaseTable[temp + (c1 & 0x00FF)];	//	yes, so fold the char
		}
		
		
		//	Find next non-ignorable char from str2, or zero if no more
		while (length2 && c2 == 0) {
			c2 = *(str2++);
			--length2;
			if ((temp = lowerCaseTable[c2>>8]) != 0)		//	is there a subtable for this upper byte?
				c2 = lowerCaseTable[temp + (c2 & 0x00FF)];	//	yes, so fold the char
		}
		
		if (c1 != c2)		//	found a difference, so stop looping
			break;
		
		if (c1 == 0)		//	did we reach the end of both strings at the same time?
			return 0;		//	yes, so strings are equal
	}
	
	if (c1 < c2)
		return -1;
	else
		return 1;
}


OSErr
ConvertUTF8ToUnicode(ByteCount srcLen, const unsigned char* srcStr, ByteCount maxDstLen,
					 ByteCount *actualDstLen, UniCharArrayPtr dstStr)
{
	ConversionResult result;
	UTF8* sourceStart;
	UTF8* sourceEnd;
	UTF16* targetStart;
	UTF16* targetEnd;

	sourceStart = (UTF8*) srcStr;
	sourceEnd = sourceStart + srcLen;
	targetStart = (UTF16*) dstStr;
	targetEnd = targetStart + maxDstLen/2;

	result = ConvertUTF8toUTF16 (&sourceStart, sourceEnd, &targetStart, targetEnd);
	
	*actualDstLen = (targetStart - dstStr) * sizeof(UniChar);
	
	if (result == targetExhausted)
		return kTECOutputBufferFullStatus;
	else if (result == sourceExhausted)
		return kTextMalformedInputErr;

	return noErr;
}


OSErr
ConvertUnicodeToUTF8(ByteCount srcLen, ConstUniCharArrayPtr srcStr, ByteCount maxDstLen,
					 ByteCount *actualDstLen, unsigned char* dstStr)
{
	ConversionResult result;
	UTF16* sourceStart;
	UTF16* sourceEnd;
	UTF8* targetStart;
	UTF8* targetEnd;
	ByteCount outputLength;

	sourceStart = (UTF16*) srcStr;
	sourceEnd = (UTF16*) ((char*) srcStr + srcLen);
	targetStart = (UTF8*) dstStr;
	targetEnd = targetStart + maxDstLen;
	
	result = ConvertUTF16toUTF8 (&sourceStart, sourceEnd, &targetStart, targetEnd);
	
	*actualDstLen = outputLength = targetStart - dstStr;

	if (result == targetExhausted)
		return kTECOutputBufferFullStatus;
	else if (result == sourceExhausted)
		return kTECPartialCharErr;

	if (outputLength >= maxDstLen)
		return kTECOutputBufferFullStatus;
		
	dstStr[outputLength] = 0;	/* also add null termination */

	return noErr;
}


OSErr
ConvertUTF8ToMacRoman(ByteCount srcLen, const unsigned char* srcStr, Str31 dstStr)
{
	UniChar uniStr[64];
	ByteCount uniLen;
	OSErr result;

    result = ConvertUTF8ToUnicode(srcLen, srcStr, sizeof(uniStr), &uniLen, uniStr);
	if (result == 0)
		result = UnicodeToMacRoman(uniLen / sizeof(UniChar), uniStr, dstStr);

	return result;
}


OSErr
ConvertMacRomanToUTF8(Str31 srcStr, ByteCount maxDstLen, ByteCount *actualDstLen,
					  unsigned char* dstStr)
{
	UniChar uniStr[64];
	ItemCount unicodeChars;
	OSErr result;

	result = MacRomanToUnicode(srcStr, &unicodeChars, uniStr);

	if (result == 0)
		result = ConvertUnicodeToUTF8(unicodeChars * sizeof(UniChar), uniStr,
									  maxDstLen, actualDstLen, dstStr);
	return result;
}


unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.