Source to bsd/hfs/hfscommon/Unicode/UnicodeWrappers.c
/*
* 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.0 (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