Skip to content
Snippets Groups Projects
MacOSPlatformUtils.cpp 45.9 KiB
Newer Older
PeiYong Zhang's avatar
PeiYong Zhang committed
/*
 * Copyright 1999-2000,2004 The Apache Software Foundation.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
PeiYong Zhang's avatar
PeiYong Zhang committed
 */

/*
 * $Id$
 */


// ---------------------------------------------------------------------------
//  Includes
// ---------------------------------------------------------------------------
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cstdio>
#include <memory>
#include <algorithm>
#include <unistd.h>

#if defined(__APPLE__)
    //	Include from Frameworks Headers under ProjectBuilder
    #include <Carbon/Carbon.h>
#else
    //	Classic include styles
    #include <Files.h>
    #include <Gestalt.h>
    #include <TextUtils.h>
    #include <TextEncodingConverter.h>
    #include <Multiprocessing.h>
    #include <DriverSynchronization.h>
    #include <DriverServices.h>
    #include <CFString.h>
    #include <URLAccess.h>
#endif

#include <xercesc/util/XercesDefs.hpp>
PeiYong Zhang's avatar
PeiYong Zhang committed
#include <xercesc/util/Janitor.hpp>
#include <xercesc/util/PlatformUtils.hpp>
#include <xercesc/util/RuntimeException.hpp>
#include <xercesc/util/XMLUniDefs.hpp>
#include <xercesc/util/XMLUni.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/Platforms/MacOS/MacOSPlatformUtils.hpp>
#include <xercesc/util/Platforms/MacOS/MacCarbonFile.hpp>
#include <xercesc/util/Platforms/MacOS/MacPosixFile.hpp>
PeiYong Zhang's avatar
PeiYong Zhang committed
#include <xercesc/util/PanicHandler.hpp>
#include <xercesc/util/OutOfMemoryException.hpp>
PeiYong Zhang's avatar
PeiYong Zhang committed

#if (defined(XML_USE_INMEMORY_MSGLOADER) || defined(XML_USE_INMEM_MESSAGELOADER))
   #include <xercesc/util/MsgLoaders/InMemory/InMemMsgLoader.hpp>
#endif

#if defined(XML_USE_ICU_TRANSCODER)
   #include <xercesc/util/Transcoders/ICU/ICUTransService.hpp>
#elif (defined(XML_USE_MACOS_UNICODECONVERTER) || defined(XML_USE_NATIVE_TRANSCODER))
PeiYong Zhang's avatar
PeiYong Zhang committed
   #include <xercesc/util/Transcoders/MacOSUnicodeConverter/MacOSUnicodeConverter.hpp>
#endif

//	Make up our minds about which netaccessor we'll use
#if (defined(XML_USE_NETACCESSOR_URLACCESSCF) || (defined(XML_USE_NETACCESSOR_NATIVE) && TARGET_API_MAC_CARBON))
    #define USE_URLACCESSCF
#elif (defined(XML_USE_NETACCESSOR_URLACCESS) || (defined(XML_USE_NETACCESSOR_NATIVE) && !TARGET_API_MAC_CARBON))
    #define USE_URLACCESS
#endif

#if defined(USE_URLACCESSCF)
   #include <xercesc/util/NetAccessors/MacOSURLAccessCF/MacOSURLAccessCF.hpp>
#elif defined(USE_URLACCESS)
PeiYong Zhang's avatar
PeiYong Zhang committed
   #include <xercesc/util/NetAccessors/MacOSURLAccess/MacOSURLAccess.hpp>
#elif defined(XML_USE_NETACCESSOR_SOCKET)
   #include <xercesc/util/NetAccessors/Socket/SocketNetAccessor.hpp>
PeiYong Zhang's avatar
PeiYong Zhang committed
#endif

Tinny Ng's avatar
Tinny Ng committed
XERCES_CPP_NAMESPACE_BEGIN
PeiYong Zhang's avatar
PeiYong Zhang committed

//----------------------------------------------------------------------------
// Function Prototypes
//----------------------------------------------------------------------------
XMLCh*	ConvertColonToSlash(XMLCh* p, std::size_t charCount);
XMLCh*	ConvertSlashToColon(XMLCh* p, std::size_t charCount);
char*	ConvertSlashToColon(char* p, std::size_t charCount);

XMLCh*	XMLCreateFullPathFromFSRef_X(const FSRef& startingRef, MemoryManager* const manager);
XMLCh*	XMLCreateFullPathFromFSRef_Classic(const FSRef& startingRef, MemoryManager* const manager);
XMLCh*	XMLCreateFullPathFromFSSpec_Classic(const FSSpec& startingSpec,
                                            MemoryManager* const manager);
bool	XMLParsePathToFSRef_X(const XMLCh* const pathName, FSRef& ref, MemoryManager* const manager);
bool	XMLParsePathToFSRef_Classic(const XMLCh* const pathName, FSRef& ref, MemoryManager* const manager);
bool	XMLParsePathToFSSpec_Classic(const XMLCh* const pathName, FSSpec& spec, MemoryManager* const manager);
PeiYong Zhang's avatar
PeiYong Zhang committed


//----------------------------------------------------------------------------
//  Local Data
//
//  gFileSystemCompatible
//   This flag indicates whether the file system APIs meet our minimum
//   requirements.
//
//  gMacOSXOrBetter
//   The system version is >= 10.
//
PeiYong Zhang's avatar
PeiYong Zhang committed
// gHasFSSpecAPIs
//   True if the FSSpecAPIs are available. These are required.
//
// gHasF2TBAPIs
//   True if the FS supports 2 terrabyte calls. These are required for
//   use under Carbon.
//
// gHasHFSPlusAPIs
//   True if the FS supports HFSPlus APIs. If this is true, then the
//   new Fork calls are used, and all file name and path handling
//   uses long unicode names. Note that once a file is opened with
//   the fork calls, only fork calls may be used to access it.
//
// gHasFSPathAPIs
//   True if the FS supports path creation APIs FSPathMakeRef and
//	 FSRefMakePath.
//
// gPathAPIsUsePosixPaths
//   True if the path creation APIs FSPathMakeRef and FSRefMakePath
//	 use posix, rather than HFS, style paths. If so, these routines
//	 are used to support path creation and  interpretation.
PeiYong Zhang's avatar
PeiYong Zhang committed
//
// gHasMPAPIs
//	 True if the Multiprocessing APIs are available.
//
// gUsePosixFiles
//   True if we're using XMLMacPosixFile rather than XMLMacCarbonFile.
//
// gUseGETCWD
//   True if we can rely on getcwd to get the current directory path.
PeiYong Zhang's avatar
PeiYong Zhang committed
//----------------------------------------------------------------------------
bool gFileSystemCompatible	= false;
bool gHasFSSpecAPIs			= false;
bool gHasFS2TBAPIs			= false;
bool gHasHFSPlusAPIs		= false;
bool gHasFSPathAPIs			= false;
bool gPathAPIsUsePosixPaths	= false;
bool gHasMPAPIs				= false;
bool gUsePosixFiles			= false;
PeiYong Zhang's avatar
PeiYong Zhang committed


// ---------------------------------------------------------------------------
//  XMLPlatformUtils: The panic method
// ---------------------------------------------------------------------------
PeiYong Zhang's avatar
PeiYong Zhang committed
void 
XMLPlatformUtils::panic(const PanicHandler::PanicReasons reason)
PeiYong Zhang's avatar
PeiYong Zhang committed
{
    if (fgUserPanicHandler)
		fgUserPanicHandler->panic(reason);
	else
		fgDefaultPanicHandler->panic(reason);
PeiYong Zhang's avatar
PeiYong Zhang committed
}


// ---------------------------------------------------------------------------
//  XMLPlatformUtils: File Methods
// ---------------------------------------------------------------------------
unsigned int
XMLPlatformUtils::curFilePos(const FileHandle theFile
                             , MemoryManager* const manager)
PeiYong Zhang's avatar
PeiYong Zhang committed
{
	return reinterpret_cast<XMLMacAbstractFile*>(theFile)->currPos();
PeiYong Zhang's avatar
PeiYong Zhang committed
}

void
XMLPlatformUtils::closeFile(const FileHandle theFile
                            , MemoryManager* const manager)
PeiYong Zhang's avatar
PeiYong Zhang committed
{
    reinterpret_cast<XMLMacAbstractFile*>(theFile)->close();
	delete reinterpret_cast<XMLMacAbstractFile*>(theFile);
PeiYong Zhang's avatar
PeiYong Zhang committed
}

unsigned int
XMLPlatformUtils::fileSize(const FileHandle theFile
                           , MemoryManager* const manager)
PeiYong Zhang's avatar
PeiYong Zhang committed
{
    return reinterpret_cast<XMLMacAbstractFile*>(theFile)->size();
PeiYong Zhang's avatar
PeiYong Zhang committed
}


FileHandle
XMLPlatformUtils::openFile(const char* const fileName
                           , MemoryManager* const manager)
PeiYong Zhang's avatar
PeiYong Zhang committed
{
    // Check to make sure the file system is in a state where we can use it
    if (!gFileSystemCompatible)
        ThrowXMLwithMemMgr1(XMLPlatformUtilsException, XMLExcepts::File_CouldNotOpenFile, fileName, manager);
    Janitor<XMLMacAbstractFile> file(XMLMakeMacFile(manager));
    
    return (file->open(fileName, false)) ? file.release() : NULL;
PeiYong Zhang's avatar
PeiYong Zhang committed
}


FileHandle
XMLPlatformUtils::openFile(const XMLCh* const fileName, MemoryManager* const manager)
PeiYong Zhang's avatar
PeiYong Zhang committed
{
    // Check to make sure the file system is in a state where we can use it
    if (!gFileSystemCompatible)
        ThrowXMLwithMemMgr1(XMLPlatformUtilsException, XMLExcepts::File_CouldNotOpenFile, fileName, manager);
    Janitor<XMLMacAbstractFile> file(XMLMakeMacFile(manager));
    return (file->open(fileName, false)) ? file.release() : NULL;
XMLPlatformUtils::openFileToWrite(const char* const fileName
                                  , MemoryManager* const manager)
    // Check to make sure the file system is in a state where we can use it
    if (!gFileSystemCompatible)
        ThrowXMLwithMemMgr1(XMLPlatformUtilsException, XMLExcepts::File_CouldNotOpenFile, fileName, manager);

    Janitor<XMLMacAbstractFile> file(XMLMakeMacFile());

    return (file->open(fileName, true)) ? file.release() : NULL;
XMLPlatformUtils::openFileToWrite(const XMLCh* const fileName
                                  , MemoryManager* const manager)
{
    // Check to make sure the file system is in a state where we can use it
    if (!gFileSystemCompatible)
        ThrowXMLwithMemMgr1(XMLPlatformUtilsException, XMLExcepts::File_CouldNotOpenFile, fileName, manager);
    Janitor<XMLMacAbstractFile> file(XMLMakeMacFile());
    return (file->open(fileName, true)) ? file.release() : NULL;
PeiYong Zhang's avatar
PeiYong Zhang committed
}


FileHandle
XMLPlatformUtils::openStdInHandle(MemoryManager* const manager)
PeiYong Zhang's avatar
PeiYong Zhang committed
{
    XMLCh stdinStr[] = {chLatin_s, chLatin_t, chLatin_d, chLatin_i, chLatin_n, chNull};
    ThrowXMLwithMemMgr1(XMLPlatformUtilsException, XMLExcepts::File_CouldNotOpenFile, stdinStr, manager);
PeiYong Zhang's avatar
PeiYong Zhang committed
    return NULL;
}


unsigned int
XMLPlatformUtils::readFileBuffer(   const FileHandle      theFile
                                 ,  const unsigned int    toRead
                                 ,        XMLByte* const  toFill
                                 ,  MemoryManager* const  manager)
PeiYong Zhang's avatar
PeiYong Zhang committed
{
    return reinterpret_cast<XMLMacAbstractFile*>(theFile)->read(toRead, toFill);
void
XMLPlatformUtils::writeBufferToFile(   const   FileHandle   theFile
                                    ,  const long		    toWrite
                                    ,  const XMLByte* const toFlush
                                    ,  MemoryManager* const manager)
    return reinterpret_cast<XMLMacAbstractFile*>(theFile)->write(toWrite, toFlush);
PeiYong Zhang's avatar
PeiYong Zhang committed
void
XMLPlatformUtils::resetFile(FileHandle theFile
                            , MemoryManager* const manager)
PeiYong Zhang's avatar
PeiYong Zhang committed
{
    reinterpret_cast<XMLMacAbstractFile*>(theFile)->reset();
PeiYong Zhang's avatar
PeiYong Zhang committed
}


// ---------------------------------------------------------------------------
//  XMLPlatformUtils: File system methods
// ---------------------------------------------------------------------------
XMLCh*
XMLPlatformUtils::getFullPath(const XMLCh* const srcPath,
                              MemoryManager* const manager)
PeiYong Zhang's avatar
PeiYong Zhang committed
{
    XMLCh* path = NULL;
PeiYong Zhang's avatar
PeiYong Zhang committed
    if (gHasHFSPlusAPIs)
    {
        FSRef ref;
        if (   !XMLParsePathToFSRef(srcPath, ref, manager)
			|| (path = XMLCreateFullPathFromFSRef(ref, manager)) == NULL
		   )
            path = XMLString::replicate(srcPath, manager);
PeiYong Zhang's avatar
PeiYong Zhang committed
    }
    else
    {
        FSSpec spec;
        if (   !XMLParsePathToFSSpec(srcPath, spec, manager)
		    || (path = XMLCreateFullPathFromFSSpec(spec, manager)) == NULL
		   )
            path = XMLString::replicate(srcPath, manager);
PeiYong Zhang's avatar
PeiYong Zhang committed
    }
PeiYong Zhang's avatar
PeiYong Zhang committed
    return path;
}


bool
XMLPlatformUtils::isRelative(const XMLCh* const toCheck
                             , MemoryManager* const manager)
PeiYong Zhang's avatar
PeiYong Zhang committed
{
    return (toCheck[0] != L'/');
}

XMLCh* XMLPlatformUtils::getCurrentDirectory(MemoryManager* const manager)
PeiYong Zhang's avatar
PeiYong Zhang committed
{
	//	Get a newly allocated path to the current directory

	//  Parse to path to determine current directory: this allows the
	//  path parsing routines to determine best way to find the current
	//  directory.
	XMLCh curDirPath[] = { '.', 0 };
		(XMLParsePathToFSSpec(curDirPath, spec, manager))
			? XMLCreateFullPathFromFSSpec(spec, manager)
		 ThrowXMLwithMemMgr(XMLPlatformUtilsException,
	           XMLExcepts::File_CouldNotGetBasePathName, manager);
PeiYong Zhang's avatar
PeiYong Zhang committed
}

PeiYong Zhang's avatar
PeiYong Zhang committed
inline bool XMLPlatformUtils::isAnySlash(XMLCh c) 
{
	//	We support only forward slash as a path delimiter
    return (chForwardSlash == c);
PeiYong Zhang's avatar
PeiYong Zhang committed

PeiYong Zhang's avatar
PeiYong Zhang committed
// ---------------------------------------------------------------------------
//  XMLPlatformUtils: Timing Methods
// ---------------------------------------------------------------------------
unsigned long
XMLPlatformUtils::getCurrentMillis()
{
	if ((void*)kUnresolvedCFragSymbolAddress != UpTime)
	{
		// Use Driver services routines, now in Carbon,
		// to get the elapsed milliseconds.
		AbsoluteTime time = UpTime();
		return AbsoluteToDuration(time);
	}
	else
		return TickCount() * 100 / 6;
}


// ---------------------------------------------------------------------------
//  Mutex methods
//
// There are a number of choices for multi-threading on Mac OS. Traditionally
// there was the Thread Manager, which provided cooperative multitasking on
// 68K and PPC platforms, and preemptive multitasking on 68K platforms only.
// The primary threading model supported under Carbon is the Multiprocessing
// library, which as of version 2.0 provides a nice set of primitives. Under
// Mac OS X, the Multiprocessing library is a thin veneer over pthreads.
//
// For lack of any really universal solutions, I've implemented these mutexes
PeiYong Zhang's avatar
PeiYong Zhang committed
// atop the Multiprocessing library. The critical regions employed here
// support recursive behavior, which is required by Xerces.
//
// Please note that, despite this implementation, there may be other barriers
// to using Xerces in a multithreaded environment. The memory allocator
// under Mac OS 9, for instance, is not thread safe.
PeiYong Zhang's avatar
PeiYong Zhang committed
// ---------------------------------------------------------------------------

void*
XMLPlatformUtils::makeMutex(MemoryManager*)
PeiYong Zhang's avatar
PeiYong Zhang committed
{
	if (gHasMPAPIs)
	{
		MPCriticalRegionID criticalRegion = NULL;
		OSStatus status = MPCreateCriticalRegion(&criticalRegion);
		return (status == noErr) ? (void*)(criticalRegion) : NULL;
	}
	else
		return (void*)1;
}


void
XMLPlatformUtils::closeMutex(void* const mtxHandle)
{
	if (gHasMPAPIs)
	{
		MPCriticalRegionID criticalRegion = reinterpret_cast<MPCriticalRegionID>(mtxHandle);
		OSStatus status = MPDeleteCriticalRegion(criticalRegion);
		status = noErr;	// ignore any error and zap compiler warning
	}
	else
		;
}


void
XMLPlatformUtils::lockMutex(void* const mtxHandle)
{
	if (gHasMPAPIs)
	{
		MPCriticalRegionID criticalRegion = reinterpret_cast<MPCriticalRegionID>(mtxHandle);
		OSStatus status = MPEnterCriticalRegion(criticalRegion, kDurationForever);
		status = noErr;	// ignore any error and zap compiler warning
	}
	else
		;
}


void
XMLPlatformUtils::unlockMutex(void* const mtxHandle)
{
	if (gHasMPAPIs)
	{
		MPCriticalRegionID criticalRegion = reinterpret_cast<MPCriticalRegionID>(mtxHandle);
		OSStatus status = MPExitCriticalRegion(criticalRegion);
		status = noErr;	// ignore any error and zap compiler warning
	}
	else
		;
}


// ---------------------------------------------------------------------------
//  Miscellaneous synchronization methods
//
// Atomic manipulation is implemented atop routines that were traditionally
// part of DriverServices, but are now a part of Carbon.
PeiYong Zhang's avatar
PeiYong Zhang committed
// ---------------------------------------------------------------------------

void*
XMLPlatformUtils::compareAndSwap(       void**      toFill
                                 , const void* const newValue
                                 , const void* const toCompare)
{
    // Replace *toFill with newValue iff *toFill == toCompare,
    // returning previous value of *toFill
PeiYong Zhang's avatar
PeiYong Zhang committed
    Boolean success = CompareAndSwap(
        reinterpret_cast<UInt32>(toCompare),
        reinterpret_cast<UInt32>(newValue),
        reinterpret_cast<UInt32*>(toFill));
PeiYong Zhang's avatar
PeiYong Zhang committed
    return (success) ? const_cast<void*>(toCompare) : *toFill;
}


//
//	Atomic Increment and Decrement
//
//	Apple's routines return the value as it was before the
//	operation, while these routines want to return it as it
//	is after. So we perform the translation before returning
//	the value.
//
int
XMLPlatformUtils::atomicIncrement(int &location)
{
    return IncrementAtomic(reinterpret_cast<long*>(&location)) + 1;
}


int
XMLPlatformUtils::atomicDecrement(int &location)
{
    return DecrementAtomic(reinterpret_cast<long*>(&location)) - 1;
}


// ---------------------------------------------------------------------------
//  XMLPlatformUtils: Private Static Methods
// ---------------------------------------------------------------------------

//
//  This method handles the MacOS basic init functions.
//
void
XMLPlatformUtils::platformInit()
{
	long value = 0;
	//  Check whether we're on OS X
	gMacOSXOrBetter			= noErr == Gestalt(gestaltSystemVersion, &value)
							  && value >= 0x00001000
							  ;
	
    //	Look for file system services
    if (noErr == Gestalt(gestaltFSAttr, &value))
    {
        gHasFSSpecAPIs		= (value & (1 << gestaltHasFSSpecCalls)) != 0;
        gHasFS2TBAPIs		= (value & (1 << gestaltFSSupports2TBVols)) != 0;
        gHasHFSPlusAPIs		= (value & (1 << gestaltHasHFSPlusAPIs)) != 0;
        #if TARGET_API_MAC_CARBON
        gHasFSPathAPIs		= ((void*)kUnresolvedCFragSymbolAddress != FSPathMakeRef);
        #else
        gHasFSPathAPIs		= false;
        #endif
        gPathAPIsUsePosixPaths = gHasFSPathAPIs
								 && (value & (1 << gestaltFSUsesPOSIXPathsForConversion));
PeiYong Zhang's avatar
PeiYong Zhang committed
	//	We require FSSpecs at a minimum
    gFileSystemCompatible	= gHasFSSpecAPIs;

	//	Determine which file system to use (posix or carbon file access)
	//	If we're using Metrowerks MSL, we surely don't want posix paths,
	//	as MSL doesn't use them.
	#if __MSL__ && (__MSL__ < 0x08000 || _MSL_CARBON_FILE_APIS)
	gUsePosixFiles			= false;
	#else
	gUsePosixFiles			= gMacOSXOrBetter;
	#endif
	
	//  Determine whether to use getcwd or not. We use it only if we're not using MSL,
	//  and we're on a Mac OS X system.
	#if __MSL__
	gUseGETCWD				= false;
	#else
	gUseGETCWD				= gMacOSXOrBetter;
	#endif

    //	Look for MP
	gHasMPAPIs				= MPLibraryIsLoaded();
PeiYong Zhang's avatar
PeiYong Zhang committed
}


//
//  This method handles the MacOS basic termination functions.
//
void
XMLPlatformUtils::platformTerm()
{
}


// ---------------------------------------------------------------------------
//  XMLPlatformUtils: Private Static Methods
// ---------------------------------------------------------------------------

//
//  This method is called by the platform independent part of this class
//  during initialization. We have to create the type of net accessor that
//  we want to use. If none, then just return zero.
//
XMLNetAccessor*
XMLPlatformUtils::makeNetAccessor()
{
    //	The selection of NetAcessor is made through
    //	the following preprocessor defines:
    //
    //	XML_USE_NETACCESSOR_URLACCESS		-- Use netaccessor based on URLAccess
    //	XML_USE_NETACCESSOR_URLACCESSCF		-- Use netaccessor based on CFURLAccess (CoreFoundation based)
    //	XML_USE_NETACCESSOR_NATIVE			-- In absence of above selections, chooses URLACCESSCF
    //										   if targetting Carbon, and URLAccess otherwise
    //	XML_USE_NETACCESSOR_SOCKET			-- Use the sockets based netaccessor
    //
    //	These choices are resolved at the ^^^top^^^ of this file.

#if (defined(USE_URLACCESSCF))
    //	Use the URLAccess code that relies only on CoreFoundation
	return new (fgMemoryManager) MacOSURLAccessCF;
PeiYong Zhang's avatar
PeiYong Zhang committed
	//	Only try to use URLAccess if it's actually available
	if (URLAccessAvailable())
		return new (fgMemoryManager) MacOSURLAccess;
#elif (defined(XML_USE_NETACCESSOR_SOCKET))
	return new (fgMemoryManager) SocketNetAccessor;
PeiYong Zhang's avatar
PeiYong Zhang committed
#endif

	//	No netaccessor available--we can live with it, but you won't
	//	get net access.
	return 0;
}


//
//  This method is called by the platform independent part of this class
//  when client code asks to have one of the supported message sets loaded.
//
XMLMsgLoader*
XMLPlatformUtils::loadAMsgSet(const XMLCh* const msgDomain)
{
PeiYong Zhang's avatar
PeiYong Zhang committed
#if (defined(XML_USE_INMEMORY_MSGLOADER) || defined(XML_USE_INMEM_MESSAGELOADER))
        retVal = new (fgMemoryManager) InMemMsgLoader(msgDomain);
PeiYong Zhang's avatar
PeiYong Zhang committed
#else
        #error You must provide a message loader
        return 0;
PeiYong Zhang's avatar
PeiYong Zhang committed
#endif
    }
    catch(const OutOfMemoryException&)
    {
        throw;
    }
    catch(...)
    {
         panic(PanicHandler::Panic_CantLoadMsgDomain);
    }
    return retVal;
PeiYong Zhang's avatar
PeiYong Zhang committed
}


//
//  This method is called very early in the bootstrapping process. This guy
//  must create a transcoding service and return it. It cannot use any string
//  methods, any transcoding services, throw any exceptions, etc... It just
//  makes a transcoding service and returns it, or returns zero on failure.
//
XMLTransService*
XMLPlatformUtils::makeTransService()
{
#if defined (XML_USE_ICU_TRANSCODER)
	return new (fgMemoryManager) ICUTransService;
#elif (defined(XML_USE_MACOS_UNICODECONVERTER) || defined(XML_USE_NATIVE_TRANSCODER))
PeiYong Zhang's avatar
PeiYong Zhang committed
    if (MacOSUnicodeConverter::IsMacOSUnicodeConverterSupported())
        return new (fgMemoryManager) MacOSUnicodeConverter;
PeiYong Zhang's avatar
PeiYong Zhang committed
#else
    #error You must provide a transcoding service implementation
#endif

    //	If we got here it's because we didn't detect the Mac OS
    //	Unicode Converter or Text Encoding Converter routines
    //	that we require to function properly. Xerces will not
    //	survive this condition.
    return NULL;
}


// ---------------------------------------------------------------------------
//	Utility Functions
// ---------------------------------------------------------------------------

XMLCh*
CopyUniCharsToXMLChs(const UniChar* src, XMLCh* dst, std::size_t charCount, std::size_t maxChars)
{
	//	Ensure we don't step on anybody's toes
	std::size_t cnt = std::min(charCount, maxChars);
	
	//	Copy the characters. UniChar is unsigned, so we shouldn't have
	//	any sign extension problems.
	//	To allow copy within a identical range, we copy backwards,
	//	since XMLCh (may be) larger than UniChar.
	dst += cnt;
	src += cnt;
	for (; cnt > 0; --cnt)
		*--dst = *--src;
		
	return dst;
}


UniChar*
CopyXMLChsToUniChars(const XMLCh* src, UniChar* dst, std::size_t charCount, std::size_t maxChars)
{
	UniChar* dstBegin = dst;
	
	//	Ensure we don't step on anybody's toes
	std::size_t cnt = std::min(charCount, maxChars);
	
	//	Copy the characters. XMLCh's will be truncated on copy to UniChar's.
	//	To allow copy within a identical range, we copy forwards,
	//	since XMLCh (may be) larger than UniChar.
	for (; cnt > 0; --cnt)
		*dst++ = *src++;
		
	return dstBegin;
}


XMLCh*
ConvertColonToSlash(XMLCh* p, std::size_t charCount)
{
	XMLCh* start = p;
	for (; charCount > 0; --charCount)
	{
		XMLCh c = *p;
		if (c == ':')
			*p++ = '/';
		else
			p++;
	}
	return start;
}


XMLCh*
ConvertSlashToColon(XMLCh* p, std::size_t charCount)
{
	XMLCh* start = p;
	for (; charCount > 0; --charCount)
	{
		XMLCh c = *p;
		if (c == '/')
			*p++ = ':';
		else
			p++;
	}
	return start;
}


char*
ConvertSlashToColon(char* p, std::size_t charCount)
{
	char* start = p;
	for (; charCount > 0; --charCount)
	{
		char c = *p;
		if (c == '/')
			*p++ = ':';
		else
			p++;
	}
	return start;
}


//	Factory method to make an appropriate subclass of XMLMacAbstractFile
//	for our use
XMLMacAbstractFile*
XMLMakeMacFile(MemoryManager* manager)
{
	XMLMacAbstractFile* result = NULL;
	
	if (gUsePosixFiles)
		result = new (manager) XMLMacPosixFile;
		result = new (manager) XMLMacCarbonFile;
PeiYong Zhang's avatar
PeiYong Zhang committed
bool
XMLParsePathToFSRef(const XMLCh* const pathName, FSRef& ref, MemoryManager* const manager)
PeiYong Zhang's avatar
PeiYong Zhang committed
{
	bool result;
	
	//	If FSPathMakeRef is available, we use it to parse the
	//	path: this gives us "standard" path support under MacOS X.
	//	Without this, our paths in that environment would always
	//	have a volume name at their root...which would look
	//	"normal" to Mac users, but not normal to unix users. Since
	//	we're making "unix" paths, we'll stick with the unix
	//	style paths. This also allows us to easilly take paths
	//	off the command line.
	//
	//	FSPathMakeRef is available on Mac OS X and in CarbonLib 1.1
	//	and greater. But on classic under CarbonLib, it expects paths
	//	to contain ':' separators, for which we're not prepared. Since
	//	this isn't a case where we need to use it, we drop back to the
	//	classic case for this.
		
	if (TARGET_API_MAC_CARBON && gHasFSPathAPIs && gPathAPIsUsePosixPaths)
		result = XMLParsePathToFSRef_X(pathName, ref, manager);
PeiYong Zhang's avatar
PeiYong Zhang committed
	else
		result = XMLParsePathToFSRef_Classic(pathName, ref, manager);
PeiYong Zhang's avatar
PeiYong Zhang committed
		
	return result;
}


bool
XMLParsePathToFSRef_X(const XMLCh* const pathName, FSRef& ref, MemoryManager* const manager)
PeiYong Zhang's avatar
PeiYong Zhang committed
{
	//	Parse Path to FSRef using FSPathMakeRef as available under
	//	Mac OS X and CarbonLib 1.1 and greater.
	
	OSStatus err = noErr;	
	std::size_t pathLen = XMLString::stringLen(pathName);

    //	Transcode XMLCh into UniChar
	UniChar uniBuf[kMaxMacStaticPathChars];
	CopyXMLChsToUniChars(pathName, uniBuf, pathLen, kMaxMacStaticPathChars);
PeiYong Zhang's avatar
PeiYong Zhang committed
	
	//	Transcode Unicode to UTF-8
	char utf8Buf[kMaxMacStaticPathChars];
	pathLen = TranscodeUniCharsToUTF8(uniBuf, utf8Buf, pathLen, kMaxMacStaticPathChars-1);
PeiYong Zhang's avatar
PeiYong Zhang committed
	
	//	Terminate the path
	char* p = utf8Buf;
	p[pathLen++] = '\0';
	
	//	If it's a relative path, pre-pend the current directory to the path.
	//	FSPathMakeRef doesn't deal with relativity on the front of the path
	if (*p != '/' && kMaxMacStaticPathChars > pathLen)
PeiYong Zhang's avatar
PeiYong Zhang committed
	{
		//	Right justify the user path to make room for the pre-pended path
		std::memmove(p + kMaxMacStaticPathChars - pathLen, p, pathLen);
PeiYong Zhang's avatar
PeiYong Zhang committed
				
		//	Get the current directory
		if (gUseGETCWD)
		{
			//	Get current directory path, leaving room for one '/' after
			if (err == noErr)
				getcwd(p, kMaxMacStaticPathChars - pathLen - 1);
		}
		else
PeiYong Zhang's avatar
PeiYong Zhang committed
		{
			//	Get current directory path, leaving room for one '/' after

			//	We quiz the carbon file manager for the current directory.
			//	Note that carbon defaults its concept of the current directory
			//  to the location of the executable.
	        FSSpec spec;
			if (err == noErr)
				err = FSMakeFSSpec(0, 0, NULL, &spec);
	        if (err == noErr)
	            err = FSpMakeFSRef(&spec, &ref);
			
			//	Get current directory path, leaving room for one '/' after
			if (err == noErr)
				err = FSRefMakePath(&ref, reinterpret_cast<UInt8*>(p), kMaxMacStaticPathChars - pathLen - 1);
PeiYong Zhang's avatar
PeiYong Zhang committed
		}
					
		//	Now munge the two paths back together
		std::size_t prefixLen = std::strlen(p);
		p[prefixLen++] = '/';
		std::memmove(p + prefixLen, p + kMaxMacStaticPathChars - pathLen, pathLen);
PeiYong Zhang's avatar
PeiYong Zhang committed
		
		//	We now have a path from an absolute starting point
	}
	
	//	Let the OS discover the location
	Boolean isDirectory = false;
	if (err == noErr)
		err = FSPathMakeRef(reinterpret_cast<UInt8*>(p), &ref, &isDirectory);
		
	//	Return true on success
	return (err == noErr);
}


bool
XMLParsePathToFSRef_Classic(const XMLCh* const pathName, FSRef& ref, MemoryManager* const manager)
PeiYong Zhang's avatar
PeiYong Zhang committed
{
	//	Parse Path to FSRef by stepping manually through path components.
	
	//	Path's parsed in this way must always begin with a volume name.
	//	This assumption would fail for standard unix paths under Mac OS X,
	//	so for those cases we use the routine XMLParsePathToFSRef_Carbon
	//	above.
	
    const XMLCh* p = pathName;
    const XMLCh* pEnd;
    std::size_t segLen;
	
	const std::size_t kXMLBufCount = 256;
	XMLCh xmlBuf[kXMLBufCount];
PeiYong Zhang's avatar
PeiYong Zhang committed
    OSErr err = noErr;
PeiYong Zhang's avatar
PeiYong Zhang committed
    if (*p == L'/')
    {
        // Absolute name: grab the first component as volume name
PeiYong Zhang's avatar
PeiYong Zhang committed
        // Find the end of the path segment
        for (pEnd = ++p; *pEnd && *pEnd != L'/'; ++pEnd) ;
        segLen = pEnd - p;
PeiYong Zhang's avatar
PeiYong Zhang committed
        // Try to find a volume that matches this name
        for (ItemCount volIndex = 1; err == noErr; ++volIndex)
        {
            HFSUniStr255 hfsStr;
            hfsStr.length = 0;
PeiYong Zhang's avatar
PeiYong Zhang committed
            // Get the volume name
            err = FSGetVolumeInfo(
                0,
                volIndex,
                static_cast<FSVolumeRefNum*>(NULL),
                0,
                static_cast<FSVolumeInfo*>(NULL),
                &hfsStr,
                &ref
                );
PeiYong Zhang's avatar
PeiYong Zhang committed
            // Compare against our path segment
            if (err == noErr && segLen == hfsStr.length)
            {
            	//	Case-insensitive compare
            	if (XMLString::compareNIString(
									ConvertSlashToColon(
										CopyUniCharsToXMLChs(hfsStr.unicode, xmlBuf, segLen, kXMLBufCount),
										segLen),
									p, segLen) == 0)
                    break;  // we found our volume
            }
        }
PeiYong Zhang's avatar
PeiYong Zhang committed
        p = pEnd;
    }
    else
    {
        // Relative name, so get the default directory as parent ref
        FSSpec spec;
        err = FSMakeFSSpec(0, 0, NULL, &spec);
        if (err == noErr)
            err = FSpMakeFSRef(&spec, &ref);
    }
    // ref now refers to the parent directory: parse the rest of the path
PeiYong Zhang's avatar
PeiYong Zhang committed
    while (err == noErr && *p)
    {
        switch (*p)
        {
        case L'/':   // Just skip any number of path separators
            ++p;
            break;
PeiYong Zhang's avatar
PeiYong Zhang committed
        case L'.':   // Potentially "current directory" or "parent directory"
            if (p[1] == L'/' || p[1] == 0)       // "current directory"
            {
                ++p;
                break;
            }
            else if (p[1] == L'.' && (p[2] == L'/' || p[2] == 0)) // "parent directory"
            {
                p += 2;  // Get the parent of our parent
PeiYong Zhang's avatar
PeiYong Zhang committed
                FSCatalogInfo catalogInfo;
                err = FSGetCatalogInfo(
                    &ref,
                    kFSCatInfoParentDirID,
                    &catalogInfo,
                    static_cast<HFSUniStr255*>(NULL),
                    static_cast<FSSpec*>(NULL),
                    &ref
                    );
PeiYong Zhang's avatar
PeiYong Zhang committed
                // Check that we didn't go too far
                if (err != noErr || catalogInfo.parentDirID == fsRtParID)
                    return false;
PeiYong Zhang's avatar
PeiYong Zhang committed
                break;
            }
            else // some other sequence of periods...fall through and treat as segment
                ;
PeiYong Zhang's avatar
PeiYong Zhang committed
        default:
            // Find the end of the path segment
            for (pEnd = p; *pEnd && *pEnd != L'/'; ++pEnd) ;
            segLen = pEnd - p;
			
            // pEnd now points either to '/' or NUL
            // Create a new ref using this path segment
            err = FSMakeFSRefUnicode(
                &ref,
                segLen,
                ConvertColonToSlash(
                	CopyXMLChsToUniChars(p, reinterpret_cast<UniChar*>(xmlBuf), segLen, kXMLBufCount),
                	segLen),
                kTextEncodingUnknown,
                &ref
                );
PeiYong Zhang's avatar
PeiYong Zhang committed
            p = pEnd;
            break;
        }
    }
PeiYong Zhang's avatar
PeiYong Zhang committed
    return err == noErr;
}


bool
XMLParsePathToFSSpec(const XMLCh* const pathName, FSSpec& spec,
                            MemoryManager* const manager)