blob: 49ca030e0679d17e77af89f2b853df4280e31e06 [file] [log] [blame]
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 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/>.
*/
/**
* $Log$
* Revision 1.1 1999/11/09 01:06:21 twl
* Initial revision
*
* Revision 1.4 1999/11/08 20:45:33 rahul
* Swat for adding in Product name and CVS comment log variable.
*
*/
// ---------------------------------------------------------------------------
// Includes
// ---------------------------------------------------------------------------
#include <util/Janitor.hpp>
#include <util/PlatformUtils.hpp>
#include <util/RuntimeException.hpp>
#include <util/XMLExceptMsgs.hpp>
#include <util/XMLString.hpp>
#include <util/XMLUni.hpp>
#include <windows.h>
#if defined (XML_USE_ICU_TRANSCODER)
#include <util/Transcoders/ICU/ICUTransService.hpp>
#elif defined (XML_USE_WIN32_TRANSCODER)
#include <util/Transcoders/Win32/Win32TransService.hpp>
#else
#error A transcoding service must be chosen
#endif
#if defined (XML_USE_INMEMORY_MSGLOADER)
#include <util/MsgLoaders/InMemory/InMemMsgLoader.hpp>
#elif defined (XML_USE_WIN32_MSGLOADER)
#include <util/MsgLoaders/Win32/Win32MsgLoader.hpp>
#else
#error A message loading service must be chosen
#endif
// ---------------------------------------------------------------------------
// Local data
//
// gOnNT
// We figure out during init if we are on NT or not. If we are, then
// we can avoid a lot of transcoding in our system services stuff.
//
// gStdErr
// gStdOut
// The file handles for standard error and standard out. We set these
// up during init. Note that they can be zero if there are no std
// handles,
//
// gStdErrRedir
// gStdOutRedir
// These flags are set to indicate whether their respective output
// handles are redirected. If they are not, then we can use console
// APIs on NT to write Unicode straight to the output. Otherwise we have
// to use file APIs, and we transcode it.
// ---------------------------------------------------------------------------
static bool gOnNT;
static HANDLE gStdErr;
static bool gStdErrRedir;
static HANDLE gStdOut;
static bool gStdOutRedir;
// ---------------------------------------------------------------------------
// Local methods
// ---------------------------------------------------------------------------
static void WriteCharStrStdErr(const char* const toWrite)
{
// We always just use the file APIs for these
DWORD written;
if (!::WriteFile
(
gStdErr
, toWrite
, strlen(toWrite)
, &written
, 0))
{
//
// If if fails due to an invalid handle, then just assume that our
// handles were disconnected and zero it out. This will prevent us
// from getting called again.
//
if (::GetLastError() == ERROR_INVALID_HANDLE)
gStdErr = 0;
else
ThrowXML(XMLPlatformUtilsException, XML4CExcepts::Strm_StdErrWriteFailure);
}
}
static void WriteCharStrStdOut(const char* const toWrite)
{
// We always just use the file APIs for these
DWORD written;
if (!::WriteFile
(
gStdOut
, toWrite
, strlen(toWrite)
, &written
, 0))
{
//
// If if fails due to an invalid handle, then just assume that our
// handles were disconnected and zero it out. This will prevent us
// from getting called again.
//
if (::GetLastError() == ERROR_INVALID_HANDLE)
gStdOut = 0;
else
ThrowXML(XMLPlatformUtilsException, XML4CExcepts::Strm_StdOutWriteFailure);
}
}
static void WriteUStrStdErr(const XMLCh* const toWrite)
{
//
// If we are on NT and the handle is not redirected, then we can use
// the console API directly to send out Unicode. Otherwise we have to
// use the file APIs and transcode.
//
DWORD written;
if (gOnNT && !gStdErrRedir)
{
if (!::WriteConsoleW
(
gStdErr
, toWrite
, XMLString::stringLen(toWrite)
, &written
, 0))
{
ThrowXML(XMLPlatformUtilsException, XML4CExcepts::Strm_ConWriteFailure);
}
return;
}
// Oh well, got to do it the hard way
char* tmpVal = XMLString::transcode(toWrite);
ArrayJanitor<char> janTmp(tmpVal);
WriteCharStrStdErr(tmpVal);
}
static void WriteUStrStdOut(const XMLCh* const toWrite)
{
//
// If we are on NT and the handle is not redirected, then we can use
// the console API directly to send out Unicode. Otherwise we have to
// use the file APIs and transcode.
//
DWORD written;
if (gOnNT && !gStdOutRedir)
{
if (!::WriteConsoleW
(
gStdOut
, toWrite
, XMLString::stringLen(toWrite)
, &written
, 0))
{
ThrowXML(XMLPlatformUtilsException, XML4CExcepts::Strm_ConWriteFailure);
}
return;
}
// Oh well, got to do it the hard way
char* tmpVal = XMLString::transcode(toWrite);
ArrayJanitor<char> janTmp(tmpVal);
WriteCharStrStdOut(tmpVal);
}
// ---------------------------------------------------------------------------
// XMLPlatformUtils: The panic method
// ---------------------------------------------------------------------------
void XMLPlatformUtils::panic(const PanicReasons reason)
{
const char* reasonStr = "Uknown 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 XML4C DLL";
else if (reason == Panic_UnknownMsgDomain)
reasonStr = "Unknown message domain";
else if (reason == Panic_CantLoadMsgDomain)
reasonStr = "Cannot load message domain";
//
// We just do a popup and exit. Replace this code to do whatever
// you need to do.
//
MessageBoxA
(
0
, "XML4C Panic Error"
, reasonStr
, MB_OK | MB_ICONSTOP
);
exit(-1);
}
// ---------------------------------------------------------------------------
// XMLPlatformUtils: File Methods
// ---------------------------------------------------------------------------
unsigned int XMLPlatformUtils::curFilePos(FileHandle theFile)
{
// Get the current position
const unsigned int curPos = ::SetFilePointer(theFile, 0, 0, FILE_CURRENT);
if (curPos == 0xFFFFFFFF)
ThrowXML(XMLPlatformUtilsException, XML4CExcepts::File_CouldNotGetCurPos);
return curPos;
}
void XMLPlatformUtils::closeFile(FileHandle theFile)
{
if (!::CloseHandle(theFile))
ThrowXML(XMLPlatformUtilsException, XML4CExcepts::File_CouldNotCloseFile);
}
unsigned int XMLPlatformUtils::fileSize(const FileHandle theFile)
{
// Get the current position
const unsigned int curPos = ::SetFilePointer(theFile, 0, 0, FILE_CURRENT);
if (curPos == 0xFFFFFFFF)
ThrowXML(XMLPlatformUtilsException, XML4CExcepts::File_CouldNotGetCurPos);
// Seek to the end and save that value for return
const unsigned int retVal = ::SetFilePointer(theFile, 0, 0, FILE_END);
if (curPos == 0xFFFFFFFF)
ThrowXML(XMLPlatformUtilsException, XML4CExcepts::File_CouldNotSeekToEnd);
// And put the pointer back
if (::SetFilePointer(theFile, curPos, 0, FILE_BEGIN) == 0xFFFFFFFF)
ThrowXML(XMLPlatformUtilsException, XML4CExcepts::File_CouldNotSeekToPos);
return retVal;
}
FileHandle XMLPlatformUtils::openFile(const char* const fileName)
{
FileHandle retVal = ::CreateFileA
(
fileName
, GENERIC_READ
, FILE_SHARE_READ
, 0
, OPEN_EXISTING
, FILE_FLAG_SEQUENTIAL_SCAN
, 0
);
if (retVal == INVALID_HANDLE_VALUE)
return 0;
return retVal;
}
FileHandle XMLPlatformUtils::openFile(const XMLCh* const fileName)
{
FileHandle retVal = 0;
if (gOnNT)
{
retVal = ::CreateFileW
(
fileName
, GENERIC_READ
, FILE_SHARE_READ
, 0
, OPEN_EXISTING
, FILE_FLAG_SEQUENTIAL_SCAN
, 0
);
}
else
{
char* tmpName = XMLString::transcode(fileName);
retVal = ::CreateFileA
(
tmpName
, GENERIC_READ
, FILE_SHARE_READ
, 0
, OPEN_EXISTING
, FILE_FLAG_SEQUENTIAL_SCAN
, 0
);
delete [] tmpName;
}
if (retVal == INVALID_HANDLE_VALUE)
return 0;
return retVal;
}
FileHandle XMLPlatformUtils::openStdInHandle()
{
//
// Get the standard input handle. Duplicate it and return that copy
// since the outside world cannot tell the difference and will shut
// down this handle when its done with it. If we gave out the orignal,
// shutting it would prevent any further output.
//
HANDLE stdInOrg = ::GetStdHandle(STD_INPUT_HANDLE);
if (stdInOrg == INVALID_HANDLE_VALUE)
ThrowXML(XMLPlatformUtilsException, XML4CExcepts::File_CouldNotOpenFile);
HANDLE retHandle;
if (!::DuplicateHandle
(
::GetCurrentProcess()
, stdInOrg
, ::GetCurrentProcess()
, &retHandle
, 0
, FALSE
, DUPLICATE_SAME_ACCESS))
{
ThrowXML(XMLPlatformUtilsException, XML4CExcepts::File_CouldNotDupHandle);
}
return retHandle;
}
unsigned int
XMLPlatformUtils::readFileBuffer( FileHandle theFile
, const unsigned int toRead
, XMLByte* const toFill)
{
unsigned long bytesRead;
if (!::ReadFile(theFile, toFill, toRead, &bytesRead, 0))
ThrowXML(XMLPlatformUtilsException, XML4CExcepts::File_CouldNotReadFromFile);
return (unsigned int)bytesRead;
}
void XMLPlatformUtils::resetFile(FileHandle theFile)
{
// Seek to the start of the file
if (::SetFilePointer(theFile, 0, 0, FILE_BEGIN) == 0xFFFFFFFF)
ThrowXML(XMLPlatformUtilsException, XML4CExcepts::File_CouldNotResetFile);
}
// ---------------------------------------------------------------------------
// XMLPlatformUtils: File Methods
// ---------------------------------------------------------------------------
void XMLPlatformUtils::writeToStdErr(const XMLCh* const toWrite)
{
// If handles never got opened, then eat the output, else output
if (gStdErr)
WriteUStrStdErr(toWrite);
}
void XMLPlatformUtils::writeToStdErr(const char* const toWrite)
{
// If handles never got opened, then eat the output, else output
if (gStdErr)
WriteCharStrStdErr(toWrite);
}
void XMLPlatformUtils::writeToStdOut(const XMLCh* const toWrite)
{
// If handles never got opened, then eat the output, else output
if (gStdOut)
WriteUStrStdOut(toWrite);
}
void XMLPlatformUtils::writeToStdOut(const char* const toWrite)
{
// If handles never got opened, then eat the output, else output
if (gStdOut)
WriteCharStrStdOut(toWrite);
}
// ---------------------------------------------------------------------------
// XMLPlatformUtils: File system methods
// ---------------------------------------------------------------------------
XMLCh* XMLPlatformUtils::getBasePath(const XMLCh* const srcPath)
{
//
// NOTE: THe path provided has always already been opened successfully,
// so we know that its not some pathological freaky path.
//
// If we are on NT, then use wide character APIs, else use ASCII APIs.
// We have to do it manually since we are only built in ASCII mode from
// the standpoint of the APIs.
//
if (gOnNT)
{
// Use a local buffer that is big enough for the largest legal path
const unsigned int bufSize = 1024;
XMLCh tmpPath[bufSize + 1];
XMLCh* namePart = 0;
if (!::GetFullPathNameW(srcPath, bufSize, tmpPath, &namePart))
return 0;
// Cap it off at the name part, leaving just the full path
if (namePart)
*namePart = 0;
// Return a copy of the path
return XMLString::replicate(tmpPath);
}
else
{
// Transcode the incoming string
char* tmpSrcPath = XMLString::transcode(srcPath);
ArrayJanitor<char> janSrcPath(tmpSrcPath);
// Use a local buffer that is big enough for the largest legal path
const unsigned int bufSize = 511;
char tmpPath[511 + 1];
char* namePart = 0;
if (!::GetFullPathNameA(tmpSrcPath, bufSize, tmpPath, &namePart))
return 0;
// Cap it off at the name part, leaving just the full path
if (namePart)
*namePart = 0;
// Return a transcoded copy of the path
return XMLString::transcode(tmpPath);
}
}
bool XMLPlatformUtils::isRelative(const XMLCh* const toCheck)
{
// Check for pathological case of empty path
if (!toCheck[0])
return false;
//
// If its starts with a drive, then it cannot be relative. Note that
// we checked the drive not being empty above, so worst case its one
// char long and the check of the 1st char will fail because its really
// a null character.
//
if (toCheck[1] == chColon)
{
if (((toCheck[0] >= chLatin_A) && (toCheck[0] <= chLatin_Z))
|| ((toCheck[0] >= chLatin_a) && (toCheck[0] <= chLatin_z)))
{
return false;
}
}
//
// If it starts with a double slash, then it cannot be relative since
// its a remote file.
//
if ((toCheck[0] == chBackSlash) && (toCheck[1] == chBackSlash))
return false;
// Else assume its a relative path
return true;
}
// ---------------------------------------------------------------------------
// XMLPlatformUtils: Timing Methods
// ---------------------------------------------------------------------------
unsigned long XMLPlatformUtils::getCurrentMillis()
{
return (unsigned long)::GetTickCount();
}
// ---------------------------------------------------------------------------
// Mutex methods
// ---------------------------------------------------------------------------
void XMLPlatformUtils::closeMutex(void* const mtxHandle)
{
if (!::CloseHandle(mtxHandle))
ThrowXML(XMLPlatformUtilsException, XML4CExcepts::Mutex_CouldNotClose);
}
void XMLPlatformUtils::lockMutex(void* const mtxHandle)
{
unsigned int res = ::WaitForSingleObject(mtxHandle, INFINITE);
if (res == WAIT_FAILED)
ThrowXML(XMLPlatformUtilsException, XML4CExcepts::Mutex_CouldNotLock);
}
void* XMLPlatformUtils::makeMutex()
{
HANDLE hRet = ::CreateMutex(0, 0, 0);
if (!hRet)
ThrowXML(XMLPlatformUtilsException, XML4CExcepts::Mutex_CouldNotCreate);
return hRet;
}
void XMLPlatformUtils::unlockMutex(void* const mtxHandle)
{
if (!::ReleaseMutex(mtxHandle))
ThrowXML(XMLPlatformUtilsException, XML4CExcepts::Mutex_CouldNotUnlock);
}
// ---------------------------------------------------------------------------
// Miscellaneous synchronization methods
// ---------------------------------------------------------------------------
void*
XMLPlatformUtils::compareAndSwap( void** toFill
, const void* const newValue
, const void* const toCompare)
{
//
// Windows supports InterlockedCompareExchange only on Windows 98, NT 4.0,
// and newer. Not on Win 95. So we are back to using assembler.
//
#if defined(DEVENV_VCPP)
void* result;
__asm
{
mov eax, toCompare;
mov ebx, newValue;
mov ecx, toFill
lock cmpxchg [ecx], ebx;
mov result, eax;
}
return result;
#elif defined(XML_IBMVAW32)
// <TBD> Why is this not using the interlocked call below?
void *retval = *toFill;
if( *toFill == toCompare)
*toFill = (void *) newValue;
return retVal;
#else
//
// Note we have to cast off the constness of some of these because
// the system APIs are not C++ aware in all cases.
//
return ::InterlockedCompareExchange
(
toFill
, (void*)newValue
, (void*)toCompare
);
#endif
}
// ---------------------------------------------------------------------------
// Atomic increment and decrement methods
// ---------------------------------------------------------------------------
int XMLPlatformUtils::atomicIncrement(int &location)
{
return ::InterlockedIncrement(&(long &)location);
}
int XMLPlatformUtils::atomicDecrement(int &location)
{
return ::InterlockedDecrement(&(long &)location);
}
// ---------------------------------------------------------------------------
// 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.
//
// <TBD> For now we return zero, but later we will return the desired type
// of accessor object.
//
XMLNetAccessor* XMLPlatformUtils::makeNetAccessor()
{
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.
// In our case, we use the ICU based message loader mechanism.
//
XMLMsgLoader* XMLPlatformUtils::loadAMsgSet(const XMLCh* const msgDomain)
{
#if defined (XML_USE_INMEMORY_MSGLOADER)
return new InMemMsgLoader(msgDomain);
#elif defined (XML_USE_WIN32_MSGLOADER)
return new Win32MsgLoader(msgDomain);
#else
#error You must provide a message loader
#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()
{
//
// Since we are going to use the ICU service, we have to tell it where
// its converter files are. If the ICU_DATA environment variable is set,
// then its been told. Otherwise, we tell it our default value relative
// to our DLL.
//
#if defined (XML_USE_ICU_TRANSCODER)
char tmpBuf[4096];
if (!::GetEnvironmentVariableA("ICU_DATA", tmpBuf, 4096))
{
strcpy(tmpBuf, fgLibLocation);
strcat(tmpBuf, "intlFiles\\Locales\\");
ICUTransService::setICUPath(tmpBuf);
}
return new ICUTransService;
#elif defined (XML_USE_WIN32_TRANSCODER)
return new Win32TransService;
#else
#error You must provide a transcoding service implementation
#endif
}
//
// This method handles the Win32 per-platform basic init functions. The
// primary jobs here are getting the path to our DLL and to get the
// stdout and stderr file handles setup.
//
void XMLPlatformUtils::platformInit()
{
//
// Lets get our own DLL path and store it. The fgLibLocation static
// member must be filled in with the path to the shared Lib or DLL
// so that other code can find any files relative to it.
//
HINSTANCE hmod = ::GetModuleHandleA(XML4C_DLLName);
if (!hmod)
{
//
// If we didn't find it, its probably because its a development
// build which is built as separate DLLs, so lets look for the DLL
// that we are part of.
//
static const char* const privDLLName = "IXUTIL";
hmod = ::GetModuleHandle(privDLLName);
// If neither exists, then we give up
if (!hmod)
panic(Panic_CantFindLib);
}
//
// Get the path to our module. We explicitly get the ASCII version here
// since its stored as ASCII (or the local code page to be more specific,
// so it might be EBCDIC on some platforms.)
//
char tmpBuf[MAX_PATH + 1];
if (!::GetModuleFileNameA(hmod, tmpBuf, MAX_PATH))
panic(Panic_CantFindLib);
// Find the last separator in the list and put a null in the next char
char* sepPtr = 0;
sepPtr = strrchr(tmpBuf, '\\');
if (sepPtr)
*(sepPtr+1)= 0;
const unsigned int pathLen = strlen(tmpBuf);
// Allocate a buffer and copy the text into it. Then store it in the static
char* actualBuf = new char[pathLen + 1];
strcpy(actualBuf, tmpBuf);
fgLibLocation = actualBuf;
//
// Figure out if we are on NT and save that flag for later use.
//
OSVERSIONINFO OSVer;
OSVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
::GetVersionEx(&OSVer);
gOnNT = (OSVer.dwPlatformId == VER_PLATFORM_WIN32_NT);
//
// Ok, we have to do a little dance here to determine if we have any
// standard output handles. First we open up the potentially redirected
// standard handles.
//
gStdOut = ::GetStdHandle(STD_OUTPUT_HANDLE);
gStdErr = ::GetStdHandle(STD_ERROR_HANDLE);
//
// If we got the handles, then get the console mode for them. If this
// fails, then assume for the time being that they are just redirected
// files.
//
// Above, when they are actually used, if they fail because of an
// invalid handle error, the gStdOut and gStdErr handles will get zeroed
// out all further output will be eaten.
//
DWORD dummyParm;
if (gStdOut)
{
if (!::GetConsoleMode(gStdOut, &dummyParm))
gStdOutRedir = true;
}
if (gStdErr)
{
if (!::GetConsoleMode(gStdErr, &dummyParm))
gStdErrRedir = true;
}
}