| /* |
| * The Apache Software License, Version 1.1 |
| * |
| * Copyright (c) 1999-2000 The Apache Software Foundation. All rights |
| * reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in |
| * the documentation and/or other materials provided with the |
| * distribution. |
| * |
| * 3. The end-user documentation included with the redistribution, |
| * if any, must include the following acknowledgment: |
| * "This product includes software developed by the |
| * Apache Software Foundation (http://www.apache.org/)." |
| * Alternately, this acknowledgment may appear in the software itself, |
| * if and wherever such third-party acknowledgments normally appear. |
| * |
| * 4. The names "Xerces" and "Apache Software Foundation" must |
| * not be used to endorse or promote products derived from this |
| * software without prior written permission. For written |
| * permission, please contact apache\@apache.org. |
| * |
| * 5. Products derived from this software may not be called "Apache", |
| * nor may "Apache" appear in their name, without prior written |
| * permission of the Apache Software Foundation. |
| * |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR |
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
| * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| * SUCH DAMAGE. |
| * ==================================================================== |
| * |
| * This software consists of voluntary contributions made by many |
| * individuals on behalf of the Apache Software Foundation, and was |
| * originally based on software copyright (c) 1999, International |
| * Business Machines, Inc., http://www.ibm.com . For more information |
| * on the Apache Software Foundation, please see |
| * <http://www.apache.org/>. |
| */ |
| |
| /* |
| * $Id$ |
| */ |
| |
| |
| // --------------------------------------------------------------------------- |
| // Note: |
| // Xerces is not officially supported on Macintosh. This file was sent |
| // in by one of the Macintosh users and is included in the distribution |
| // just for convenience. Please send any defects / modification |
| // reports to xml4c@us.ibm.com |
| // --------------------------------------------------------------------------- |
| |
| |
| // --------------------------------------------------------------------------- |
| // Includes |
| // --------------------------------------------------------------------------- |
| #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/MacOSDefs.hpp> |
| #include <xercesc/util/Platforms/MacOS/MacOSPlatformUtils.hpp> |
| |
| #if (defined(XML_USE_INMEMORY_MSGLOADER) || defined(XML_USE_INMEM_MESSAGELOADER)) |
| #include <xercesc/util/MsgLoaders/InMemory/InMemMsgLoader.hpp> |
| #endif |
| #if (defined(XML_USE_MACOS_UNICODECONVERTER) || defined(XML_USE_NATIVE_TRANSCODER)) |
| #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) |
| #include <xercesc/util/NetAccessors/MacOSURLAccess/MacOSURLAccess.hpp> |
| #endif |
| |
| |
| #include <cstring> |
| #include <cstdlib> |
| #include <cctype> |
| #include <cstdio> |
| #include <memory> |
| #include <algorithm> |
| |
| #if defined(XML_MACOSX) |
| // 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 |
| |
| |
| |
| //---------------------------------------------------------------------------- |
| // Local Constants |
| //---------------------------------------------------------------------------- |
| const std::size_t kMaxStaticPathChars = 512; // Size of our statically allocated path buffers |
| |
| |
| //---------------------------------------------------------------------------- |
| // 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); |
| XMLCh* XMLCreateFullPathFromFSRef_Classic(const FSRef& startingRef); |
| XMLCh* XMLCreateFullPathFromFSSpec_Classic(const FSSpec& startingSpec); |
| bool XMLParsePathToFSRef_X(const XMLCh* const pathName, FSRef& ref); |
| bool XMLParsePathToFSRef_Classic(const XMLCh* const pathName, FSRef& ref); |
| bool XMLParsePathToFSSpec_Classic(const XMLCh* const pathName, FSSpec& spec); |
| |
| std::size_t TranscodeUniCharsToUTF8(UniChar* src, char* dst, std::size_t srcCnt, std::size_t maxChars); |
| std::size_t TranscodeUTF8ToUniChars(char* src, UniChar* dst, std::size_t maxChars); |
| |
| |
| //---------------------------------------------------------------------------- |
| // Local Data |
| // |
| // gFileSystemCompatible |
| // This flag indicates whether the file system APIs meet our minimum |
| // requirements. |
| // |
| // 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. |
| // |
| // gHasMPAPIs |
| // True if the Multiprocessing APIs are available. |
| //---------------------------------------------------------------------------- |
| static bool gFileSystemCompatible = false; |
| static bool gHasFSSpecAPIs = false; |
| static bool gHasFS2TBAPIs = false; |
| static bool gHasHFSPlusAPIs = false; |
| static bool gHasFSPathAPIs = false; |
| static bool gPathAPIsUsePosixPaths = false; |
| static bool gHasMPAPIs = false; |
| |
| |
| //---------------------------------------------------------------------------- |
| // XMLMacFile methods |
| //---------------------------------------------------------------------------- |
| |
| unsigned int |
| XMLMacFile::currPos() |
| { |
| OSErr err = noErr; |
| unsigned int pos = 0; |
| |
| if (!mFileValid) |
| ThrowXML(XMLPlatformUtilsException, XMLExcepts::File_CouldNotGetCurPos); |
| |
| if (gHasHFSPlusAPIs) |
| { |
| SInt64 bigPos = 0; |
| err = FSGetForkPosition(mFileRefNum, &bigPos); |
| if (err == noErr) |
| pos = bigPos; |
| } |
| else |
| { |
| long longPos; |
| err = GetFPos(mFileRefNum, &longPos); |
| if (err == noErr) |
| pos = longPos; |
| } |
| |
| if (err != noErr) |
| ThrowXML(XMLPlatformUtilsException, XMLExcepts::File_CouldNotGetCurPos); |
| |
| return pos; |
| } |
| |
| |
| void |
| XMLMacFile::close() |
| { |
| OSErr err = noErr; |
| if (!mFileValid) |
| ThrowXML(XMLPlatformUtilsException, XMLExcepts::File_CouldNotCloseFile); |
| |
| if (gHasHFSPlusAPIs) |
| err = FSCloseFork(mFileRefNum); |
| else |
| err = FSClose(mFileRefNum); |
| |
| mFileValid = false; |
| |
| if (err != noErr) |
| ThrowXML(XMLPlatformUtilsException, XMLExcepts::File_CouldNotCloseFile); |
| } |
| |
| |
| unsigned int |
| XMLMacFile::size() |
| { |
| OSErr err = noErr; |
| unsigned int len = 0; |
| |
| if (!mFileValid) |
| ThrowXML(XMLPlatformUtilsException, XMLExcepts::File_CouldNotGetSize); |
| |
| if (gHasHFSPlusAPIs) |
| { |
| SInt64 bigLen = 0; |
| err = FSGetForkSize(mFileRefNum, &bigLen); |
| if (err == noErr) |
| len = bigLen; |
| } |
| else |
| { |
| long longLen; |
| err = GetEOF(mFileRefNum, &longLen); |
| if (err == noErr) |
| len = longLen; |
| } |
| |
| if (err != noErr) |
| ThrowXML(XMLPlatformUtilsException, XMLExcepts::File_CouldNotGetSize); |
| |
| return len; |
| } |
| |
| |
| void |
| XMLMacFile::open(const XMLCh* const fileName) |
| { |
| OSErr err = noErr; |
| |
| if (mFileValid) |
| ThrowXML(XMLPlatformUtilsException, XMLExcepts::File_CouldNotOpenFile); |
| |
| if (gHasHFSPlusAPIs) |
| { |
| FSRef ref; |
| if (!XMLParsePathToFSRef(fileName, ref)) |
| err = fnfErr; |
| |
| HFSUniStr255 forkName; |
| if (err == noErr) |
| err = FSGetDataForkName(&forkName); |
| |
| if (err == noErr) |
| err = FSOpenFork(&ref, forkName.length, forkName.unicode, fsRdPerm, &mFileRefNum); |
| } |
| else |
| { |
| FSSpec spec; |
| if (!XMLParsePathToFSSpec(fileName, spec)) |
| err = fnfErr; |
| |
| if (err == noErr) |
| err = FSpOpenDF(&spec, fsRdPerm, &mFileRefNum); |
| } |
| |
| if (err != noErr) |
| ThrowXML1(XMLPlatformUtilsException, XMLExcepts::File_CouldNotOpenFile, fileName); |
| |
| mFileValid = true; |
| } |
| |
| |
| unsigned int |
| XMLMacFile::read(const unsigned int toRead, XMLByte* const toFill) |
| { |
| unsigned int bytesRead = 0; |
| OSErr err = noErr; |
| |
| if (!mFileValid) |
| ThrowXML(XMLPlatformUtilsException, XMLExcepts::File_CouldNotReadFromFile); |
| |
| if (gHasHFSPlusAPIs) |
| { |
| ByteCount actualCount; |
| err = FSReadFork(mFileRefNum, fsFromMark, 0, toRead, toFill, &actualCount); |
| bytesRead = actualCount; |
| } |
| else |
| { |
| long byteCount = toRead; |
| err = FSRead(mFileRefNum, &byteCount, toFill); |
| bytesRead = byteCount; |
| } |
| |
| if (err != noErr && err != eofErr) |
| ThrowXML(XMLPlatformUtilsException, XMLExcepts::File_CouldNotReadFromFile); |
| |
| return bytesRead; |
| } |
| |
| |
| void |
| XMLMacFile::reset() |
| { |
| OSErr err = noErr; |
| |
| if (!mFileValid) |
| ThrowXML(XMLPlatformUtilsException, XMLExcepts::File_CouldNotResetFile); |
| |
| if (gHasHFSPlusAPIs) |
| err = FSSetForkPosition(mFileRefNum, fsFromStart, 0); |
| else |
| err = SetFPos(mFileRefNum, fsFromStart, 0); |
| |
| if (err != noErr) |
| ThrowXML(XMLPlatformUtilsException, XMLExcepts::File_CouldNotResetFile); |
| } |
| |
| |
| XMLMacFile::~XMLMacFile() |
| { |
| if (mFileValid) |
| close(); |
| } |
| |
| |
| // --------------------------------------------------------------------------- |
| // XMLPlatformUtils: The panic method |
| // --------------------------------------------------------------------------- |
| void |
| XMLPlatformUtils::panic(const PanicReasons reason) |
| { |
| const char* reasonStr = "Unknown reason"; |
| if (reason == Panic_NoTransService) |
| reasonStr = "Could not load a transcoding service"; |
| else if (reason == Panic_NoDefTranscoder) |
| reasonStr = "Could not load a local code page transcoder"; |
| else if (reason == Panic_CantFindLib) |
| reasonStr = "Could not find the xerces-c DLL"; |
| else if (reason == Panic_UnknownMsgDomain) |
| reasonStr = "Unknown message domain"; |
| else if (reason == Panic_CantLoadMsgDomain) |
| reasonStr = "Cannot load message domain"; |
| else if (reason == Panic_SynchronizationErr) |
| reasonStr = "A system synchronization error occurred"; |
| else if (reason == Panic_SystemInit) |
| reasonStr = "Failed to complete platform dependent initialization"; |
| else |
| reasonStr = "Unknown error source"; |
| |
| char text[256]; |
| std::snprintf(text, sizeof(text), "Xerces Panic Error: %s", reasonStr); |
| |
| // |
| // The default handling of panics is not very friendly. |
| // To replace it with something more friendly, you'll need to: |
| // - #define XML_USE_CUSTOM_PANIC_PROC |
| // - Write, and link with, XMLCustomPanicProc |
| // - Implement your panic handling within XMLCustomPanicProc. |
| // |
| #if defined(XML_USE_CUSTOM_PANIC_PROC) |
| XMLCustomPanicProc(reason, reasonStr); |
| #else |
| Str255 pasText; |
| CopyCStringToPascal(text, pasText); |
| DebugStr(pasText); |
| #endif |
| |
| // Life's got us down. Good-bye world. |
| std::exit(-1); |
| } |
| |
| |
| // --------------------------------------------------------------------------- |
| // XMLPlatformUtils: File Methods |
| // --------------------------------------------------------------------------- |
| unsigned int |
| XMLPlatformUtils::curFilePos(const FileHandle theFile) |
| { |
| return theFile->currPos(); |
| } |
| |
| void |
| XMLPlatformUtils::closeFile(const FileHandle theFile) |
| { |
| theFile->close(); |
| } |
| |
| unsigned int |
| XMLPlatformUtils::fileSize(const FileHandle theFile) |
| { |
| return theFile->size(); |
| } |
| |
| |
| FileHandle |
| XMLPlatformUtils::openFile(const char* const fileName) |
| { |
| const XMLCh* xmlPath = XMLString::transcode(fileName); |
| ArrayJanitor<const XMLCh> jan(xmlPath); |
| return openFile(xmlPath); |
| } |
| |
| |
| FileHandle |
| XMLPlatformUtils::openFile(const XMLCh* const fileName) |
| { |
| // Check to make sure the file system is in a state where we can use it |
| if (!gFileSystemCompatible) |
| ThrowXML(XMLPlatformUtilsException, XMLExcepts::File_CouldNotOpenFile); |
| |
| XMLMacFile* file = new XMLMacFile(); |
| Janitor<XMLMacAbstractFile> janFile(file); |
| file->open(fileName); |
| janFile.orphan(); |
| |
| return file; |
| } |
| |
| |
| FileHandle |
| XMLPlatformUtils::openStdInHandle() |
| { |
| ThrowXML(XMLPlatformUtilsException, XMLExcepts::File_CouldNotOpenFile); |
| return NULL; |
| } |
| |
| |
| unsigned int |
| XMLPlatformUtils::readFileBuffer( const FileHandle theFile |
| , const unsigned int toRead |
| , XMLByte* const toFill) |
| { |
| return theFile->read(toRead, toFill); |
| } |
| |
| |
| void |
| XMLPlatformUtils::resetFile(FileHandle theFile) |
| { |
| theFile->reset(); |
| } |
| |
| |
| // --------------------------------------------------------------------------- |
| // XMLPlatformUtils: File system methods |
| // --------------------------------------------------------------------------- |
| XMLCh* |
| XMLPlatformUtils::getFullPath(const XMLCh* const srcPath) |
| { |
| XMLCh* path = NULL; |
| |
| if (gHasHFSPlusAPIs) |
| { |
| FSRef ref; |
| if (!XMLParsePathToFSRef(srcPath, ref) || (path = XMLCreateFullPathFromFSRef(ref)) == NULL) |
| path = XMLString::replicate(srcPath); |
| } |
| else |
| { |
| FSSpec spec; |
| if (!XMLParsePathToFSSpec(srcPath, spec) || (path = XMLCreateFullPathFromFSSpec(spec)) == NULL) |
| path = XMLString::replicate(srcPath); |
| } |
| |
| return path; |
| } |
| |
| |
| bool |
| XMLPlatformUtils::isRelative(const XMLCh* const toCheck) |
| { |
| return (toCheck[0] != L'/'); |
| } |
| |
| |
| XMLCh* |
| XMLPlatformUtils::weavePaths(const XMLCh* const basePath |
| , const XMLCh* const relativePath) |
| |
| { |
| // Code from Windows largely unmodified for the Macintosh, |
| // with the exception of removing support for '\' path |
| // separator. |
| // |
| // Note that there is no support currently for Macintosh |
| // path separators ':'. |
| |
| // Create a buffer as large as both parts and empty it |
| ArrayJanitor<XMLCh> tmpBuf(new XMLCh[XMLString::stringLen(basePath) |
| + XMLString::stringLen(relativePath) |
| + 2]); |
| tmpBuf[0] = 0; |
| |
| // |
| // If we have no base path, then just take the relative path as |
| // is. |
| // |
| if (!basePath) |
| { |
| XMLString::copyString(tmpBuf.get(), relativePath); |
| return tmpBuf.release(); |
| } |
| |
| if (!*basePath) |
| { |
| XMLString::copyString(tmpBuf.get(), relativePath); |
| return tmpBuf.release(); |
| } |
| |
| const XMLCh* basePtr = basePath + (XMLString::stringLen(basePath) - 1); |
| if (*basePtr != chForwardSlash) |
| { |
| while ((basePtr >= basePath) && (*basePtr != chForwardSlash)) |
| basePtr--; |
| } |
| |
| // There is no relevant base path, so just take the relative part |
| if (basePtr < basePath) |
| { |
| XMLString::copyString(tmpBuf.get(), relativePath); |
| return tmpBuf.release(); |
| } |
| |
| // We have some path part, so we need to check to see if we have to |
| // weave any of the parts together. |
| const XMLCh* pathPtr = relativePath; |
| |
| while (true) |
| { |
| // If it does not start with some period, then we are done |
| if (*pathPtr != chPeriod) |
| break; |
| |
| unsigned int periodCount = 1; |
| pathPtr++; |
| if (*pathPtr == chPeriod) |
| { |
| pathPtr++; |
| periodCount++; |
| } |
| |
| // Has to be followed by a / or the null to mean anything |
| if ((*pathPtr != chForwardSlash) && *pathPtr) |
| break; |
| |
| if (*pathPtr) |
| pathPtr++; |
| |
| // If it's one period, just eat it, else move backwards in the base |
| if (periodCount == 2) |
| { |
| basePtr--; |
| while ((basePtr >= basePath) && (*basePtr != chForwardSlash)) |
| basePtr--; |
| |
| // The base cannot provide enough levels, so it's in error/ |
| if (basePtr < basePath) |
| ThrowXML(XMLPlatformUtilsException, XMLExcepts::File_BasePathUnderflow); |
| } |
| } |
| |
| // Copy the base part up to the base pointer |
| XMLCh* bufPtr = tmpBuf.get(); |
| const XMLCh* tmpPtr = basePath; |
| while (tmpPtr <= basePtr) |
| *bufPtr++ = *tmpPtr++; |
| |
| // And then copy on the rest of our path |
| XMLString::copyString(bufPtr, pathPtr); |
| |
| // Orphan the buffer and return it |
| return tmpBuf.release(); |
| } |
| |
| |
| // --------------------------------------------------------------------------- |
| // 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 good 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 are probably other |
| // MacOS barriers to actually using Xerces in a multi-threaded environment. |
| // Many other parts of your system and/or development environment may not |
| // support pre-emption, even under Mac OS X. Examples may include the memory |
| // allocator, the Unicode Converter or Utilities, and perhaps the file |
| // system (though the FS is better with Multiprocessing as of System 9.0). |
| // |
| // These routines are provided somewhat speculatively, and with the philosphy |
| // that this code, at least, shouldn't be the reason why multithreading |
| // doesn't work. Compatibility of this library wrt the other areas described |
| // above will be resolved in time. |
| // --------------------------------------------------------------------------- |
| |
| void* |
| XMLPlatformUtils::makeMutex() |
| { |
| 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 apparently a part of Carbon. If this |
| // selection proves to be invalid, similar routines in OpenTransport could |
| // be used instead. |
| // --------------------------------------------------------------------------- |
| |
| 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)); |
| |
| 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; |
| |
| // Detect available functions |
| |
| // 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)); |
| } |
| |
| // Look for MP |
| gHasMPAPIs = MPLibraryIsLoaded(); |
| |
| // We require FSSpecs at a minimum |
| gFileSystemCompatible = gHasFSSpecAPIs; |
| } |
| |
| |
| // |
| // 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 |
| // |
| // 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 MacOSURLAccessCF; |
| #elif (defined(USE_URLACCESS)) |
| // Only try to use URLAccess if it's actually available |
| if (URLAccessAvailable()) |
| return new MacOSURLAccess; |
| #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) |
| { |
| #if (defined(XML_USE_INMEMORY_MSGLOADER) || defined(XML_USE_INMEM_MESSAGELOADER)) |
| return new InMemMsgLoader(msgDomain); |
| #else |
| #error You must provide a message loader |
| return 0; |
| #endif |
| } |
| |
| |
| // |
| // 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_MACOS_UNICODECONVERTER) || defined(XML_USE_NATIVE_TRANSCODER)) |
| if (MacOSUnicodeConverter::IsMacOSUnicodeConverterSupported()) |
| return new MacOSUnicodeConverter; |
| #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; |
| } |
| |
| |
| bool |
| XMLParsePathToFSRef(const XMLCh* const pathName, FSRef& ref) |
| { |
| 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); |
| else |
| result = XMLParsePathToFSRef_Classic(pathName, ref); |
| |
| return result; |
| } |
| |
| |
| bool |
| XMLParsePathToFSRef_X(const XMLCh* const pathName, FSRef& ref) |
| { |
| // 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[kMaxStaticPathChars]; |
| CopyXMLChsToUniChars(pathName, uniBuf, pathLen, kMaxStaticPathChars); |
| |
| // Transcode Unicode to UTF-8 |
| char utf8Buf[kMaxStaticPathChars]; |
| pathLen = TranscodeUniCharsToUTF8(uniBuf, utf8Buf, pathLen, kMaxStaticPathChars-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 == '.') |
| { |
| // Right justify the user path to make room for the pre-pended path |
| std::memmove(p + kMaxStaticPathChars - pathLen, p, pathLen); |
| |
| // Get the current directory |
| FSSpec spec; |
| if (err == noErr) |
| err = FSMakeFSSpec(0, 0, NULL, &spec); |
| if (err == noErr) |
| err = FSpMakeFSRef(&spec, &ref); |
| |
| // Get pathname to the current directory |
| if (err == noErr) |
| err = FSRefMakePath(&ref, reinterpret_cast<UInt8*>(p), kMaxStaticPathChars - pathLen - 1); // leave room for one '/' |
| std::size_t prefixLen = std::strlen(p); |
| |
| // Now munge the two paths back together |
| if (err == noErr) |
| { |
| p[prefixLen++] = '/'; |
| std::memmove(p + prefixLen, p + kMaxStaticPathChars - 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) |
| { |
| // 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]; |
| |
| OSErr err = noErr; |
| |
| 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); |
| } |
| |
| // ref now refers to the a 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 |
| ); |
| |
| p = pEnd; |
| break; |
| } |
| } |
| |
| return err == noErr; |
| } |
| |
| |
| bool |
| XMLParsePathToFSSpec(const XMLCh* const pathName, FSSpec& spec) |
| { |
| // Parse Path to an FSSpec |
| |
| // If we've got HFS+ APIs, do this in terms of refs so that |
| // we can grandfather in the use of FSPathMakeRef under Mac OS X |
| // and CarbonLib 1.1. Otherwise, do it the hard way. |
| |
| bool result = false; |
| |
| if (gHasHFSPlusAPIs) |
| { |
| // Parse to a ref |
| FSRef ref; |
| result = XMLParsePathToFSRef(pathName, ref); |
| |
| // Down convert to a spec |
| if (result) |
| result = (noErr == FSGetCatalogInfo(&ref, |
| kFSCatInfoNone, |
| static_cast<FSCatalogInfo*>(NULL), // catalogInfo |
| static_cast<HFSUniStr255*>(NULL), // outName |
| &spec, |
| static_cast<FSRef*>(NULL) // parentRef |
| )); |
| } |
| else |
| result = XMLParsePathToFSSpec_Classic(pathName, spec); |
| |
| // Return true on success |
| return result; |
| } |
| |
| |
| bool |
| XMLParsePathToFSSpec_Classic(const XMLCh* const pathName, FSSpec& spec) |
| { |
| // Manually parse the path using FSSpec APIs. |
| |
| // Transcode the path into ascii |
| const char* p = XMLString::transcode(pathName); |
| ArrayJanitor<const char> janPath(p); |
| const char* pEnd; |
| std::size_t segLen; |
| |
| OSStatus err = noErr; |
| Str255 name; // Must be long enough for a partial pathname consisting of two segments (64 bytes) |
| |
| if (*p == '/') |
| { |
| // Absolute name: grab the first component as volume name |
| |
| // Find the end of the path segment |
| for (pEnd = ++p; *pEnd && *pEnd != '/'; ++pEnd) ; |
| segLen = pEnd - p; |
| |
| // Try to find a volume that matches this name |
| for (ItemCount volIndex = 1; err == noErr; ++volIndex) |
| { |
| FSVolumeRefNum volRefNum; |
| |
| if (gHasFS2TBAPIs) |
| { |
| XVolumeParam xVolParam; |
| name[0] = 0; |
| xVolParam.ioNamePtr = name; |
| xVolParam.ioVRefNum = 0; |
| xVolParam.ioXVersion = 0; |
| xVolParam.ioVolIndex = volIndex; |
| err = PBXGetVolInfoSync(&xVolParam); |
| volRefNum = xVolParam.ioVRefNum; |
| } |
| else |
| { |
| #if !TARGET_API_MAC_CARBON |
| HParamBlockRec hfsParams; |
| name[0] = 0; |
| hfsParams.volumeParam.ioNamePtr = name; |
| hfsParams.volumeParam.ioVRefNum = 0; |
| hfsParams.volumeParam.ioVolIndex = volIndex; |
| err = PBHGetVInfoSync(&hfsParams); |
| volRefNum = hfsParams.volumeParam.ioVRefNum; |
| #else |
| err = nsvErr; |
| #endif |
| } |
| |
| // Compare against our path segment |
| if (err == noErr && segLen == StrLength(name)) |
| { |
| // Case-insensitive compare |
| if (XMLString::compareNIString( |
| ConvertSlashToColon(reinterpret_cast<char*>(&name[1]), segLen), |
| p, segLen) == 0) |
| { |
| // we found our volume: fill in the spec |
| err = FSMakeFSSpec(volRefNum, fsRtDirID, NULL, &spec); |
| break; |
| } |
| } |
| } |
| |
| p = pEnd; |
| } |
| else |
| { |
| // Relative name, so get the default directory as parent spec |
| err = FSMakeFSSpec(0, 0, NULL, &spec); |
| } |
| |
| // We now have a parent directory in the spec. |
| while (err == noErr && *p) |
| { |
| switch (*p) |
| { |
| case '/': // Just skip any number of path separators |
| ++p; |
| break; |
| |
| case L'.': // Potentially "current directory" or "parent directory" |
| if (p[1] == '/' || p[1] == 0) // "current directory" |
| { |
| ++p; |
| break; |
| } |
| else if (p[1] == '.' && (p[2] == '/' || p[2] == 0)) // "parent directory" |
| { |
| p += 2; // Get the parent of our parent |
| |
| CInfoPBRec catInfo; |
| catInfo.dirInfo.ioNamePtr = NULL; |
| catInfo.dirInfo.ioVRefNum = spec.vRefNum; |
| catInfo.dirInfo.ioFDirIndex = -1; |
| catInfo.dirInfo.ioDrDirID = spec.parID; |
| err = PBGetCatInfoSync(&catInfo); |
| |
| // Check that we didn't go too far |
| if (err != noErr || catInfo.dirInfo.ioDrParID == fsRtParID) |
| return false; |
| |
| // Update our spec |
| if (err == noErr) |
| err = FSMakeFSSpec(spec.vRefNum, catInfo.dirInfo.ioDrDirID, NULL, &spec); |
| |
| 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 != '/'; ++pEnd) ; |
| segLen = pEnd - p; |
| |
| // Check for name length overflow |
| if (segLen > 31) |
| return false; |
| |
| // Make a partial pathname from our current spec to the new object |
| unsigned char* partial = &name[1]; |
| |
| *partial++ = ':'; // Partial leads with : |
| const unsigned char* specName = spec.name; // Copy in spec name |
| for (int specCnt = *specName++; specCnt > 0; --specCnt) |
| *partial++ = *specName++; |
| |
| *partial++ = ':'; // Separator |
| while (p != pEnd) // Copy in new element |
| { |
| if (*p == ':') // Convert : to / |
| { |
| *partial++ = '/'; |
| p++; |
| } |
| else |
| *partial++ = *p++; |
| } |
| |
| name[0] = partial - &name[1]; // Set the name length |
| |
| // Update the spec |
| err = FSMakeFSSpec(spec.vRefNum, spec.parID, name, &spec); |
| } |
| break; |
| } |
| } |
| |
| return err == noErr; |
| } |
| |
| |
| XMLCh* |
| XMLCreateFullPathFromFSRef(const FSRef& startingRef) |
| { |
| XMLCh* result = NULL; |
| |
| // If FSRefMakePath is available, we use it to create 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. |
| // |
| // FSRefMakePath is available on Mac OS X and in CarbonLib 1.1 |
| // and greater. But we use it only on X since on Classic it |
| // makes paths with ':' separators, which really confuses us! |
| |
| if (TARGET_API_MAC_CARBON && gHasFSPathAPIs && gPathAPIsUsePosixPaths) |
| result = XMLCreateFullPathFromFSRef_X(startingRef); |
| else |
| result = XMLCreateFullPathFromFSRef_Classic(startingRef); |
| |
| return result; |
| } |
| |
| |
| XMLCh* |
| XMLCreateFullPathFromFSRef_X(const FSRef& startingRef) |
| { |
| // Create the path using FSRefMakePath as available on Mac OS X |
| // and under CarbonLib 1.1 and greater. |
| OSStatus err = noErr; |
| |
| // Make the path in utf8 form |
| char utf8Buf[kMaxStaticPathChars]; |
| utf8Buf[0] = '\0'; |
| |
| if (err == noErr) |
| err = FSRefMakePath(&startingRef, reinterpret_cast<UInt8*>(utf8Buf), kMaxStaticPathChars); |
| |
| // Bail if path conversion failed |
| if (err != noErr) |
| return NULL; |
| |
| // Transcode into UniChars |
| UniChar uniBuf[kMaxStaticPathChars]; |
| std::size_t pathLen = TranscodeUTF8ToUniChars(utf8Buf, uniBuf, kMaxStaticPathChars-1); |
| uniBuf[pathLen++] = 0; |
| |
| // Transcode into a dynamically allocated buffer of XMLChs |
| ArrayJanitor<XMLCh> result(new XMLCh[pathLen]); |
| if (result.get() != NULL) |
| CopyUniCharsToXMLChs(uniBuf, result.get(), pathLen, pathLen); |
| |
| return result.release(); |
| } |
| |
| |
| XMLCh* |
| XMLCreateFullPathFromFSRef_Classic(const FSRef& startingRef) |
| { |
| // Manually create the path using FSRef APIs. |
| OSStatus err = noErr; |
| FSCatalogInfo catalogInfo; |
| HFSUniStr255 name; |
| FSRef ref = startingRef; |
| |
| XMLCh buf[kMaxStaticPathChars]; |
| std::size_t bufPos = kMaxStaticPathChars; |
| std::size_t bufCnt = 0; |
| |
| ArrayJanitor<XMLCh> result(NULL); |
| std::size_t resultLen = 0; |
| |
| buf[--bufPos] = L'\0'; |
| ++bufCnt; |
| |
| do |
| { |
| err = FSGetCatalogInfo( |
| &ref, |
| kFSCatInfoParentDirID, |
| &catalogInfo, |
| &name, |
| static_cast<FSSpec*>(NULL), |
| &ref |
| ); |
| |
| if (err == noErr) |
| { |
| // If there's not room in our static buffer for the new |
| // name plus separator, dump it to the dynamic result buffer. |
| if (bufPos < (std::size_t)name.length + 1) |
| { |
| ArrayJanitor<XMLCh> temp(new XMLCh[bufCnt + resultLen]); |
| |
| // Copy in the static buffer |
| std::memcpy(temp.get(), &buf[bufPos], bufCnt * sizeof(XMLCh)); |
| |
| // Copy in the old buffer |
| if (resultLen > 0) |
| std::memcpy(temp.get() + bufCnt, result.get(), resultLen * sizeof(XMLCh)); |
| |
| result.reset(temp.release()); |
| resultLen += bufCnt; |
| |
| bufPos = kMaxStaticPathChars; |
| bufCnt = 0; |
| } |
| |
| // Prepend our new name and a '/' |
| bufPos -= name.length; |
| ConvertSlashToColon(CopyUniCharsToXMLChs(name.unicode, &buf[bufPos], name.length, name.length), name.length); |
| buf[--bufPos] = L'/'; |
| bufCnt += (name.length + 1); |
| } |
| } |
| while (err == noErr && catalogInfo.parentDirID != fsRtParID); |
| |
| // Composite existing buffer with any previous result buffer |
| ArrayJanitor<XMLCh> final(new XMLCh[bufCnt + resultLen]); |
| |
| // Copy in the static buffer |
| std::memcpy(final.get(), &buf[bufPos], bufCnt * sizeof(XMLCh)); |
| |
| // Copy in the old buffer |
| if (resultLen > 0) |
| std::memcpy(final.get() + bufCnt, result.get(), resultLen * sizeof(XMLCh)); |
| |
| return final.release(); |
| } |
| |
| |
| XMLCh* |
| XMLCreateFullPathFromFSSpec(const FSSpec& startingSpec) |
| { |
| XMLCh* result = NULL; |
| |
| // If FSRefs are available, do this operation in terms of refs...this |
| // allows us to grandfather in the use of FSPathMakeRef and FSRefMakePath |
| // if possible. |
| if (gHasHFSPlusAPIs) |
| { |
| OSStatus err = noErr; |
| FSRef ref; |
| |
| // Up convert to FSRef |
| if (err == noErr) |
| err = FSpMakeFSRef(&startingSpec, &ref); |
| |
| // Create the path |
| if (err == noErr) |
| result = XMLCreateFullPathFromFSRef(ref); |
| } |
| else |
| { |
| // Create using FSSpecs only |
| result = XMLCreateFullPathFromFSSpec_Classic(startingSpec); |
| } |
| |
| return result; |
| } |
| |
| |
| XMLCh* |
| XMLCreateFullPathFromFSSpec_Classic(const FSSpec& startingSpec) |
| { |
| // Manually create the path using FSSpec APIs. |
| OSStatus err = noErr; |
| FSSpec spec = startingSpec; |
| |
| char buf[kMaxStaticPathChars]; |
| std::size_t bufPos = kMaxStaticPathChars; |
| std::size_t bufCnt = 0; |
| |
| ArrayJanitor<char> result(NULL); |
| std::size_t resultLen = 0; |
| |
| buf[--bufPos] = '\0'; |
| ++bufCnt; |
| |
| short index = 0; |
| do |
| { |
| CInfoPBRec catInfo; |
| catInfo.dirInfo.ioNamePtr = spec.name; |
| catInfo.dirInfo.ioVRefNum = spec.vRefNum; |
| catInfo.dirInfo.ioFDirIndex = index; |
| catInfo.dirInfo.ioDrDirID = spec.parID; |
| err = PBGetCatInfoSync(&catInfo); |
| |
| if (err == noErr) |
| { |
| std::size_t nameLen = StrLength(spec.name); |
| |
| // If there's not room in our static buffer for the new |
| // name plus separator, dump it to the dynamic result buffer. |
| if (bufPos < nameLen + 1) |
| { |
| ArrayJanitor<char> temp(new char[bufCnt + resultLen]); |
| |
| // Copy in the static buffer |
| std::memcpy(temp.get(), &buf[bufPos], bufCnt); |
| |
| // Copy in the old buffer |
| if (resultLen > 0) |
| std::memcpy(temp.get() + bufCnt, result.get(), resultLen); |
| |
| result.reset(temp.release()); |
| resultLen += bufCnt; |
| |
| bufPos = kMaxStaticPathChars; |
| bufCnt = 0; |
| } |
| |
| // Prepend our new name and a '/' |
| bufPos -= nameLen; |
| ConvertSlashToColon((char*)std::memcpy(&buf[bufPos], &spec.name[1], nameLen), nameLen); |
| buf[--bufPos] = '/'; |
| bufCnt += (nameLen + 1); |
| |
| // From here on out, ignore the input file name |
| index = -1; |
| |
| // Move up to the parent |
| spec.parID = catInfo.dirInfo.ioDrParID; |
| } |
| } |
| while (err == noErr && spec.parID != fsRtParID); |
| |
| // Composite existing buffer with any previous result buffer |
| ArrayJanitor<char> final(new char[bufCnt + resultLen]); |
| |
| // Copy in the static buffer |
| std::memcpy(final.get(), &buf[bufPos], bufCnt); |
| |
| // Copy in the old buffer |
| if (resultLen > 0) |
| std::memcpy(final.get() + bufCnt, result.get(), resultLen); |
| |
| // Cleanup and transcode to unicode |
| return XMLString::transcode(final.get()); |
| } |
| |
| |
| std::size_t |
| TranscodeUniCharsToUTF8(UniChar* src, char* dst, std::size_t srcCnt, std::size_t maxChars) |
| { |
| std::size_t result = 0; |
| |
| // Use the text encoding converter to perform the format conversion. |
| // Note that this is slightly heavyweight, but we're not in a performance |
| // sensitive code-path. |
| |
| OSStatus err = noErr; |
| TECObjectRef tec = 0; |
| ByteCount bytesConsumed = 0; |
| ByteCount bytesProduced = 0; |
| |
| TextEncoding inputEncoding = CreateTextEncoding(kTextEncodingUnicodeDefault, |
| kTextEncodingDefaultVariant, |
| kUnicode16BitFormat); |
| |
| TextEncoding outputEncoding = CreateTextEncoding(kTextEncodingUnicodeDefault, |
| kTextEncodingDefaultVariant, |
| kUnicodeUTF8Format); |
| |
| if (err == noErr) |
| err = TECCreateConverter(&tec, inputEncoding, outputEncoding); |
| |
| if (err == noErr) |
| err = TECConvertText(tec, |
| (ConstTextPtr) src, |
| srcCnt * sizeof(UniChar), // inputBufferLength |
| &bytesConsumed, // actualInputLength |
| (TextPtr) dst, // outputBuffer |
| maxChars * sizeof(char), // outputBufferLength |
| &bytesProduced); // actualOutputLength |
| |
| TECDisposeConverter(tec); |
| |
| result = bytesProduced; |
| |
| // Return number of chars in dst |
| return result; |
| } |
| |
| |
| std::size_t |
| TranscodeUTF8ToUniChars(char* src, UniChar* dst, std::size_t maxChars) |
| { |
| std::size_t result = 0; |
| |
| // Use the text encoding converter to perform the format conversion. |
| // Note that this is slightly heavyweight, but we're not in a performance |
| // sensitive code-path. |
| |
| OSStatus err = noErr; |
| TECObjectRef tec = 0; |
| ByteCount bytesConsumed = 0; |
| ByteCount bytesProduced = 0; |
| |
| |
| TextEncoding inputEncoding = CreateTextEncoding(kTextEncodingUnicodeDefault, |
| kTextEncodingDefaultVariant, |
| kUnicodeUTF8Format); |
| |
| TextEncoding outputEncoding = CreateTextEncoding(kTextEncodingUnicodeDefault, |
| kTextEncodingDefaultVariant, |
| kUnicode16BitFormat); |
| |
| if (err == noErr) |
| err = TECCreateConverter(&tec, inputEncoding, outputEncoding); |
| |
| if (err == noErr) |
| err = TECConvertText(tec, |
| (ConstTextPtr) src, |
| std::strlen(src), // inputBufferLength |
| &bytesConsumed, // actualInputLength |
| (TextPtr) dst, // outputBuffer |
| maxChars * sizeof(UniChar), // outputBufferLength |
| &bytesProduced); // actualOutputLength |
| |
| TECDisposeConverter(tec); |
| |
| result = bytesProduced / sizeof(UniChar); |
| |
| // Return number of unicode characters in dst |
| return result; |
| } |