blob: 9b3c7ad9cc6a21478bba88dd36b2fc7815f90e51 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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$
*/
#include <windows.h>
#include <xercesc/util/FileManagers/WindowsFileMgr.hpp>
#include <xercesc/util/Janitor.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/XMLUniDefs.hpp>
#ifndef INVALID_SET_FILE_POINTER
#define INVALID_SET_FILE_POINTER ((DWORD)-1)
#endif
namespace XERCES_CPP_NAMESPACE {
static bool isBackSlash(XMLCh c) {
return c == chBackSlash ||
c == chYenSign ||
c == chWonSign;
}
WindowsFileMgr::WindowsFileMgr()
{
// Figure out if we are on NT and save that flag for later use
OSVERSIONINFO OSVer;
OSVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
::GetVersionEx(&OSVer);
_onNT = (OSVer.dwPlatformId == VER_PLATFORM_WIN32_NT);
}
WindowsFileMgr::~WindowsFileMgr()
{
}
FileHandle
WindowsFileMgr::fileOpen(const XMLCh* fileName, bool toWrite, MemoryManager* const manager)
{
// Watch for obvious wierdness
if (!fileName)
return 0;
//
// We have to play a little trick here. If its /x:.....
// style fully qualified path, we have to toss the leading /
// character.
//
const XMLCh* nameToOpen = fileName;
if (*fileName == chForwardSlash)
{
if (XMLString::stringLen(fileName) > 3)
{
if (*(fileName + 2) == chColon)
{
const XMLCh chDrive = *(fileName + 1);
if (((chDrive >= chLatin_A) && (chDrive <= chLatin_Z))
|| ((chDrive >= chLatin_a) && (chDrive <= chLatin_z)))
{
nameToOpen = fileName + 1;
}
}
// Similarly for UNC paths
if ( *(fileName + 1) == *(fileName + 2) &&
(*(fileName + 1) == chForwardSlash ||
*(fileName + 1) == chBackSlash) )
{
nameToOpen = fileName + 1;
}
}
}
// Ok, this might look stupid but its a semi-expedient way to deal
// with a thorny problem. Shift-JIS and some other Asian encodings
// are fundamentally broken and map both the backslash and the Yen
// sign to the same code point. Transcoders have to pick one or the
// other to map '\' to Unicode and tend to choose the Yen sign.
//
// Unicode Yen or Won signs as directory separators will fail.
//
// So, we will check this path name for Yen or won signs and, if they are
// there, we'll replace them with slashes.
//
// A further twist: we replace Yen and Won with forward slashes rather
// than back slashes. Either form of slash will work as a directory
// separator. On Win 95 and 98, though, Unicode back-slashes may
// fail to transode back to 8-bit 0x5C with some Unicode converters
// to some of the problematic code pages. Forward slashes always
// transcode correctly back to 8 bit char * form.
//
XMLCh *tmpUName = 0;
const XMLCh* srcPtr = nameToOpen;
while (*srcPtr)
{
if (*srcPtr == chYenSign ||
*srcPtr == chWonSign)
break;
srcPtr++;
}
//
// If we found a yen, then we have to create a temp file name. Else
// go with the file name as is and save the overhead.
//
if (*srcPtr)
{
tmpUName = XMLString::replicate(nameToOpen, manager);
XMLCh* tmpPtr = tmpUName;
while (*tmpPtr)
{
if (*tmpPtr == chYenSign ||
*tmpPtr == chWonSign)
*tmpPtr = chForwardSlash;
tmpPtr++;
}
nameToOpen = tmpUName;
}
FileHandle retVal = 0;
if (_onNT)
{
retVal = ::CreateFileW
(
(LPCWSTR) nameToOpen
, toWrite?GENERIC_WRITE:GENERIC_READ
, FILE_SHARE_READ
, 0
, toWrite?CREATE_ALWAYS:OPEN_EXISTING
, toWrite?FILE_ATTRIBUTE_NORMAL:FILE_FLAG_SEQUENTIAL_SCAN
, 0
);
}
else
{
//
// We are Win 95 / 98. Take the Unicode file name back to (char *)
// so that we can open it.
//
char* tmpName = XMLString::transcode(nameToOpen, manager);
retVal = ::CreateFileA
(
tmpName
, toWrite?GENERIC_WRITE:GENERIC_READ
, FILE_SHARE_READ
, 0
, toWrite?CREATE_ALWAYS:OPEN_EXISTING
, toWrite?FILE_ATTRIBUTE_NORMAL:FILE_FLAG_SEQUENTIAL_SCAN
, 0
);
manager->deallocate(tmpName);//delete [] tmpName;
}
if (tmpUName)
manager->deallocate(tmpUName);//delete [] tmpUName;
if (retVal == INVALID_HANDLE_VALUE)
return 0;
return retVal;
}
FileHandle
WindowsFileMgr::fileOpen(const char* path, bool toWrite, MemoryManager* const manager)
{
XMLCh* tmpFileName = XMLString::transcode(path, manager);
ArrayJanitor<XMLCh> janText(tmpFileName, manager);
return fileOpen(tmpFileName, toWrite, manager);
}
FileHandle
WindowsFileMgr::openStdIn(MemoryManager* const manager)
{
//
// 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) {
XMLCh stdinStr[] = {chLatin_s, chLatin_t, chLatin_d, chLatin_i, chLatin_n, chNull};
ThrowXMLwithMemMgr1(XMLPlatformUtilsException, XMLExcepts::File_CouldNotOpenFile, stdinStr, manager);
}
HANDLE retHandle;
if (!::DuplicateHandle
(
::GetCurrentProcess()
, stdInOrg
, ::GetCurrentProcess()
, &retHandle
, 0
, FALSE
, DUPLICATE_SAME_ACCESS))
{
ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotDupHandle, manager);
}
return retHandle;
}
void
WindowsFileMgr::fileClose(FileHandle f, MemoryManager* const manager)
{
if (!f)
ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::CPtr_PointerIsZero, manager);
if (!::CloseHandle(f))
ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotCloseFile, manager);
}
void
WindowsFileMgr::fileReset(FileHandle f, MemoryManager* const manager)
{
if (!f)
ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::CPtr_PointerIsZero, manager);
// Seek to the start of the file
if (::SetFilePointer(f, 0, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotResetFile, manager);
}
XMLFilePos
WindowsFileMgr::curPos(FileHandle f, MemoryManager* const manager)
{
if (!f)
ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::CPtr_PointerIsZero, manager);
// Get the current position
LONG high=0;
DWORD low = ::SetFilePointer(f, 0, &high, FILE_CURRENT);
if (low == INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR)
ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotGetCurPos, manager);
return (((XMLFilePos)high) << 32) | low;
}
XMLFilePos
WindowsFileMgr::fileSize(FileHandle f, MemoryManager* const manager)
{
if (!f)
ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::CPtr_PointerIsZero, manager);
DWORD high=0;
DWORD low=::GetFileSize(f, &high);
if(low==INVALID_FILE_SIZE && GetLastError()!=NO_ERROR)
// TODO: find a better exception
ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotGetCurPos, manager);
return (((XMLFilePos)high) << 32) | low;
}
XMLSize_t
WindowsFileMgr::fileRead(FileHandle f, XMLSize_t byteCount, XMLByte* buffer, MemoryManager* const manager)
{
if (!f || !buffer)
ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::CPtr_PointerIsZero, manager);
DWORD bytesRead = 0;
if (!::ReadFile(f, buffer, (DWORD)byteCount, &bytesRead, 0))
{
//
// Check specially for a broken pipe error. If we get this, it just
// means no more data from the pipe, so return zero.
//
if (::GetLastError() != ERROR_BROKEN_PIPE)
ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotReadFromFile, manager);
}
return (unsigned int)bytesRead;
}
void
WindowsFileMgr::fileWrite(FileHandle f, XMLSize_t byteCount, const XMLByte* buffer, MemoryManager* const manager)
{
if (!f || !buffer)
ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::CPtr_PointerIsZero, manager);
const XMLByte* tmpFlush = buffer;
while (byteCount > 0)
{
DWORD bytesWritten = 0;
if (!::WriteFile(f, tmpFlush, (DWORD)byteCount, &bytesWritten, 0))
ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotWriteToFile, manager);
tmpFlush+=bytesWritten;
byteCount-=bytesWritten;
}
}
XMLCh*
WindowsFileMgr::getFullPath(const XMLCh* const srcPath, MemoryManager* const manager)
{
//
// 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 (_onNT)
{
// 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((LPCWSTR)srcPath, bufSize, (LPWSTR)tmpPath, (LPWSTR*)&namePart))
return 0;
// Return a copy of the path
return XMLString::replicate(tmpPath, manager);
}
else
{
// Transcode the incoming string
char* tmpSrcPath = XMLString::transcode(srcPath, manager);
ArrayJanitor<char> janSrcPath(tmpSrcPath, manager);
// 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;
// Return a transcoded copy of the path
return XMLString::transcode(tmpPath, manager);
}
}
XMLCh*
WindowsFileMgr::getCurrentDirectory(MemoryManager* const manager)
{
//
// 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 (_onNT)
{
// Use a local buffer that is big enough for the largest legal path
const unsigned int bufSize = 1024;
XMLCh tmpPath[bufSize + 1];
if (!::GetCurrentDirectoryW(bufSize, (LPWSTR)tmpPath))
return 0;
// Return a copy of the path
return XMLString::replicate(tmpPath, manager);
}
else
{
// Use a local buffer that is big enough for the largest legal path
const unsigned int bufSize = 511;
char tmpPath[511 + 1];
if (!::GetCurrentDirectoryA(bufSize, tmpPath))
return 0;
// Return a transcoded copy of the path
return XMLString::transcode(tmpPath, manager);
}
}
bool
WindowsFileMgr::isRelative(const XMLCh* const toCheck, MemoryManager* const /*manager*/)
{
// Check for pathological case of empty path
if (!toCheck || !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
// it's a remote file.
//
if (isBackSlash(toCheck[0]))
return false;
// Else assume its a relative path
return true;
}
}