Newer
Older
* 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.
*/
/*
* $Id$
*/
// ---------------------------------------------------------------------------
// Includes
// ---------------------------------------------------------------------------
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cstdio>
#include <memory>
#include <algorithm>
#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>
#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>
#include <xercesc/util/OutOfMemoryException.hpp>
#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))
#include <xercesc/util/Transcoders/MacOSUnicodeConverter/MacOSUnicodeConverter.hpp>
#endif
James David Berry
committed
// 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)
#include <xercesc/util/NetAccessors/MacOSURLAccess/MacOSURLAccess.hpp>
#elif defined(XML_USE_NETACCESSOR_SOCKET)
#include <xercesc/util/NetAccessors/Socket/SocketNetAccessor.hpp>
//----------------------------------------------------------------------------
// 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);
//----------------------------------------------------------------------------
// Local Data
//
// gFileSystemCompatible
// This flag indicates whether the file system APIs meet our minimum
// requirements.
//
// gMacOSXOrBetter
// The system version is >= 10.
//
// 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
James David Berry
committed
// 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.
//
// gHasMPAPIs
// True if the Multiprocessing APIs are available.
//
// gUsePosixFiles
// True if we're using XMLMacPosixFile rather than XMLMacCarbonFile.
James David Berry
committed
//
// gUseGETCWD
// True if we can rely on getcwd to get the current directory path.
//----------------------------------------------------------------------------
James David Berry
committed
bool gMacOSXOrBetter = false;
bool gHasFSSpecAPIs = false;
bool gHasFS2TBAPIs = false;
bool gHasHFSPlusAPIs = false;
bool gHasFSPathAPIs = false;
bool gPathAPIsUsePosixPaths = false;
bool gHasMPAPIs = false;
bool gUsePosixFiles = false;
James David Berry
committed
bool gUseGETCWD = false;
// ---------------------------------------------------------------------------
// XMLPlatformUtils: The panic method
// ---------------------------------------------------------------------------
void
XMLPlatformUtils::panic(const PanicHandler::PanicReasons reason)
James David Berry
committed
if (fgUserPanicHandler)
fgUserPanicHandler->panic(reason);
else
fgDefaultPanicHandler->panic(reason);
}
// ---------------------------------------------------------------------------
// XMLPlatformUtils: File Methods
// ---------------------------------------------------------------------------
unsigned int
David Abram Cargill
committed
XMLPlatformUtils::curFilePos(const FileHandle theFile
, MemoryManager* const manager)
return reinterpret_cast<XMLMacAbstractFile*>(theFile)->currPos();
David Abram Cargill
committed
XMLPlatformUtils::closeFile(const FileHandle theFile
, MemoryManager* const manager)
reinterpret_cast<XMLMacAbstractFile*>(theFile)->close();
James David Berry
committed
delete reinterpret_cast<XMLMacAbstractFile*>(theFile);
David Abram Cargill
committed
XMLPlatformUtils::fileSize(const FileHandle theFile
, MemoryManager* const manager)
return reinterpret_cast<XMLMacAbstractFile*>(theFile)->size();
David Abram Cargill
committed
XMLPlatformUtils::openFile(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)
David Abram Cargill
committed
ThrowXMLwithMemMgr1(XMLPlatformUtilsException, XMLExcepts::File_CouldNotOpenFile, fileName, manager);
Janitor<XMLMacAbstractFile> file(XMLMakeMacFile(manager));
return (file->open(fileName, false)) ? file.release() : NULL;
David Abram Cargill
committed
XMLPlatformUtils::openFile(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)
David Abram Cargill
committed
ThrowXMLwithMemMgr1(XMLPlatformUtilsException, XMLExcepts::File_CouldNotOpenFile, fileName, manager);
Janitor<XMLMacAbstractFile> file(XMLMakeMacFile(manager));
return (file->open(fileName, false)) ? file.release() : NULL;
James David Berry
committed
}
FileHandle
David Abram Cargill
committed
XMLPlatformUtils::openFileToWrite(const char* const fileName
, MemoryManager* const manager)
James David Berry
committed
{
// Check to make sure the file system is in a state where we can use it
if (!gFileSystemCompatible)
David Abram Cargill
committed
ThrowXMLwithMemMgr1(XMLPlatformUtilsException, XMLExcepts::File_CouldNotOpenFile, fileName, manager);
Janitor<XMLMacAbstractFile> file(XMLMakeMacFile());
return (file->open(fileName, true)) ? file.release() : NULL;
James David Berry
committed
}
FileHandle
David Abram Cargill
committed
XMLPlatformUtils::openFileToWrite(const XMLCh* const fileName
, MemoryManager* const manager)
James David Berry
committed
{
// Check to make sure the file system is in a state where we can use it
if (!gFileSystemCompatible)
David Abram Cargill
committed
ThrowXMLwithMemMgr1(XMLPlatformUtilsException, XMLExcepts::File_CouldNotOpenFile, fileName, manager);
James David Berry
committed
Janitor<XMLMacAbstractFile> file(XMLMakeMacFile());
James David Berry
committed
return (file->open(fileName, true)) ? file.release() : NULL;
David Abram Cargill
committed
XMLPlatformUtils::openStdInHandle(MemoryManager* const manager)
XMLCh stdinStr[] = {chLatin_s, chLatin_t, chLatin_d, chLatin_i, chLatin_n, chNull};
David Abram Cargill
committed
ThrowXMLwithMemMgr1(XMLPlatformUtilsException, XMLExcepts::File_CouldNotOpenFile, stdinStr, manager);
James David Berry
committed
XMLPlatformUtils::readFileBuffer( const FileHandle theFile
, const unsigned int toRead
David Abram Cargill
committed
, XMLByte* const toFill
, MemoryManager* const manager)
return reinterpret_cast<XMLMacAbstractFile*>(theFile)->read(toRead, toFill);
James David Berry
committed
void
XMLPlatformUtils::writeBufferToFile( const FileHandle theFile
, const long toWrite
David Abram Cargill
committed
, const XMLByte* const toFlush
, MemoryManager* const manager)
James David Berry
committed
{
return reinterpret_cast<XMLMacAbstractFile*>(theFile)->write(toWrite, toFlush);
James David Berry
committed
}
David Abram Cargill
committed
XMLPlatformUtils::resetFile(FileHandle theFile
, MemoryManager* const manager)
reinterpret_cast<XMLMacAbstractFile*>(theFile)->reset();
}
// ---------------------------------------------------------------------------
// XMLPlatformUtils: File system methods
// ---------------------------------------------------------------------------
XMLCh*
XMLPlatformUtils::getFullPath(const XMLCh* const srcPath,
MemoryManager* const manager)
if ( !XMLParsePathToFSRef(srcPath, ref, manager)
|| (path = XMLCreateFullPathFromFSRef(ref, manager)) == NULL
)
path = XMLString::replicate(srcPath, manager);
if ( !XMLParsePathToFSSpec(srcPath, spec, manager)
|| (path = XMLCreateFullPathFromFSSpec(spec, manager)) == NULL
)
path = XMLString::replicate(srcPath, manager);
XMLPlatformUtils::isRelative(const XMLCh* const toCheck
, MemoryManager* const manager)
James David Berry
committed
XMLCh* XMLPlatformUtils::getCurrentDirectory(MemoryManager* const manager)
// Get a newly allocated path to the current directory
James David Berry
committed
FSSpec spec;
James David Berry
committed
// 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 };
XMLCh* path =
James David Berry
committed
(XMLParsePathToFSSpec(curDirPath, spec, manager))
? XMLCreateFullPathFromFSSpec(spec, manager)
: NULL;
if (!path)
David Abram Cargill
committed
ThrowXMLwithMemMgr(XMLPlatformUtilsException,
XMLExcepts::File_CouldNotGetBasePathName, manager);
return path;
James David Berry
committed
inline bool XMLPlatformUtils::isAnySlash(XMLCh c)
{
James David Berry
committed
// We support only forward slash as a path delimiter
return (chForwardSlash == c);
James David Berry
committed
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
// ---------------------------------------------------------------------------
// 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
// 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.
// ---------------------------------------------------------------------------
void*
XMLPlatformUtils::makeMutex(MemoryManager*)
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
{
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.
// ---------------------------------------------------------------------------
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
Boolean success = CompareAndSwap(
reinterpret_cast<UInt32>(toCompare),
reinterpret_cast<UInt32>(newValue),
reinterpret_cast<UInt32*>(toFill));
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
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;
James David Berry
committed
// Detect available functions
James David Berry
committed
// Check whether we're on OS X
gMacOSXOrBetter = noErr == Gestalt(gestaltSystemVersion, &value)
&& value >= 0x00001000
;
James David Berry
committed
// 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;
James David Berry
committed
#if TARGET_API_MAC_CARBON
gHasFSPathAPIs = ((void*)kUnresolvedCFragSymbolAddress != FSPathMakeRef);
#else
gHasFSPathAPIs = false;
#endif
gPathAPIsUsePosixPaths = gHasFSPathAPIs
&& (value & (1 << gestaltFSUsesPOSIXPathsForConversion));
James David Berry
committed
}
James David Berry
committed
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
James David Berry
committed
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();
}
//
// 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()
{
James David Berry
committed
// 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
James David Berry
committed
//
// 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;
James David Berry
committed
#elif (defined(USE_URLACCESS))
// 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;
#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)
{
XMLMsgLoader* retVal;
try
{
#if (defined(XML_USE_INMEMORY_MSGLOADER) || defined(XML_USE_INMEM_MESSAGELOADER))
retVal = new (fgMemoryManager) InMemMsgLoader(msgDomain);
#error You must provide a message loader
return 0;
}
catch(const OutOfMemoryException&)
{
throw;
}
catch(...)
{
panic(PanicHandler::Panic_CantLoadMsgDomain);
}
return retVal;
}
//
// 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))
if (MacOSUnicodeConverter::IsMacOSUnicodeConverterSupported())
return new (fgMemoryManager) MacOSUnicodeConverter;
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
#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;
XMLParsePathToFSRef(const XMLCh* const pathName, FSRef& ref, MemoryManager* const manager)
{
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.
James David Berry
committed
if (TARGET_API_MAC_CARBON && gHasFSPathAPIs && gPathAPIsUsePosixPaths)
result = XMLParsePathToFSRef_X(pathName, ref, manager);
result = XMLParsePathToFSRef_Classic(pathName, ref, manager);
XMLParsePathToFSRef_X(const XMLCh* const pathName, FSRef& ref, MemoryManager* const manager)
{
// 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);
char utf8Buf[kMaxMacStaticPathChars];
pathLen = TranscodeUniCharsToUTF8(uniBuf, utf8Buf, pathLen, kMaxMacStaticPathChars-1);
// 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)
{
// Right justify the user path to make room for the pre-pended path
std::memmove(p + kMaxMacStaticPathChars - pathLen, p, pathLen);
James David Berry
committed
*p = '\0';
James David Berry
committed
if (gUseGETCWD)
{
// Get current directory path, leaving room for one '/' after
if (err == noErr)
getcwd(p, kMaxMacStaticPathChars - pathLen - 1);
}
else
James David Berry
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);
James David Berry
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);
// 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)
{
// 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];
if (*p == L'/')
{
// Absolute name: grab the first component as volume name
// Find the end of the path segment
for (pEnd = ++p; *pEnd && *pEnd != L'/'; ++pEnd) ;
segLen = pEnd - p;
// Try to find a volume that matches this name
for (ItemCount volIndex = 1; err == noErr; ++volIndex)
{
HFSUniStr255 hfsStr;
hfsStr.length = 0;
// Get the volume name
err = FSGetVolumeInfo(
0,
volIndex,
static_cast<FSVolumeRefNum*>(NULL),
0,
static_cast<FSVolumeInfo*>(NULL),
&hfsStr,
&ref
);
// 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
}
}
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);
}
James David Berry
committed
// ref now refers to the parent directory: parse the rest of the path
while (err == noErr && *p)
{
switch (*p)
{
case L'/': // Just skip any number of path separators
++p;
break;
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
FSCatalogInfo catalogInfo;
err = FSGetCatalogInfo(
&ref,
kFSCatInfoParentDirID,
&catalogInfo,
static_cast<HFSUniStr255*>(NULL),
static_cast<FSSpec*>(NULL),
&ref
);
// Check that we didn't go too far
if (err != noErr || catalogInfo.parentDirID == fsRtParID)
return false;
break;
}
else // some other sequence of periods...fall through and treat as segment
;
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
);
XMLParsePathToFSSpec(const XMLCh* const pathName, FSSpec& spec,
MemoryManager* const manager)