GME  13
WindowsFileMgr.cpp
Go to the documentation of this file.
00001 /*
00002  * Licensed to the Apache Software Foundation (ASF) under one or more
00003  * contributor license agreements.  See the NOTICE file distributed with
00004  * this work for additional information regarding copyright ownership.
00005  * The ASF licenses this file to You under the Apache License, Version 2.0
00006  * (the "License"); you may not use this file except in compliance with
00007  * the License.  You may obtain a copy of the License at
00008  * 
00009  *      http://www.apache.org/licenses/LICENSE-2.0
00010  * 
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS,
00013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  */
00017 
00018 /*
00019  * $Id: WindowsFileMgr.cpp 556533 2007-07-16 07:36:41Z amassari $
00020  */
00021 
00022 #include <windows.h>
00023 
00024 #include <xercesc/util/FileManagers/WindowsFileMgr.hpp>
00025 #include <xercesc/util/Janitor.hpp>
00026 #include <xercesc/util/XMLString.hpp>
00027 #include <xercesc/util/XMLUniDefs.hpp>
00028 
00029 #ifndef INVALID_SET_FILE_POINTER
00030 #define INVALID_SET_FILE_POINTER ((DWORD)-1)
00031 #endif
00032 
00033 XERCES_CPP_NAMESPACE_BEGIN
00034 
00035 static bool isBackSlash(XMLCh c) {
00036     return c == chBackSlash ||
00037            c == chYenSign   ||
00038            c == chWonSign;
00039 }
00040 
00041 WindowsFileMgr::WindowsFileMgr()
00042 {
00043     // Figure out if we are on NT and save that flag for later use
00044     OSVERSIONINFO   OSVer;
00045     OSVer.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
00046     ::GetVersionEx(&OSVer);
00047     _onNT = (OSVer.dwPlatformId == VER_PLATFORM_WIN32_NT);
00048 }
00049 
00050 
00051 WindowsFileMgr::~WindowsFileMgr()
00052 {
00053 }
00054 
00055 
00056 FileHandle
00057 WindowsFileMgr::fileOpen(const XMLCh* fileName, bool toWrite, MemoryManager* const manager)
00058 {
00059     // Watch for obvious wierdness
00060     if (!fileName)
00061         return 0;
00062 
00063     //
00064     //  We have to play a little trick here. If its /x:.....
00065     //  style fully qualified path, we have to toss the leading /
00066     //  character.
00067     //
00068     const XMLCh* nameToOpen = fileName;
00069     if (*fileName == chForwardSlash)
00070     {
00071         if (XMLString::stringLen(fileName) > 3)
00072         {
00073             if (*(fileName + 2) == chColon)
00074             {
00075                 const XMLCh chDrive = *(fileName + 1);
00076                 if (((chDrive >= chLatin_A) && (chDrive <= chLatin_Z))
00077                 ||  ((chDrive >= chLatin_a) && (chDrive <= chLatin_z)))
00078                 {
00079                     nameToOpen = fileName + 1;
00080                 }
00081             }
00082 
00083             // Similarly for UNC paths
00084             if ( *(fileName + 1) == *(fileName + 2) &&
00085                  (*(fileName + 1) == chForwardSlash ||
00086                   *(fileName + 1) == chBackSlash) )
00087             {
00088                 nameToOpen = fileName + 1;
00089             }
00090         }
00091     }
00092 
00093     //  Ok, this might look stupid but its a semi-expedient way to deal
00094     //  with a thorny problem. Shift-JIS and some other Asian encodings
00095     //  are fundamentally broken and map both the backslash and the Yen
00096     //  sign to the same code point. Transcoders have to pick one or the
00097     //  other to map '\' to Unicode and tend to choose the Yen sign.
00098     //
00099     //  Unicode Yen or Won signs as directory separators will fail.
00100     //
00101     //  So, we will check this path name for Yen or won signs and, if they are
00102     //  there, we'll replace them with slashes.
00103     //
00104     //  A further twist:  we replace Yen and Won with forward slashes rather
00105     //   than back slashes.  Either form of slash will work as a directory
00106     //   separator.  On Win 95 and 98, though, Unicode back-slashes may
00107     //   fail to transode back to 8-bit 0x5C with some Unicode converters
00108     //   to  some of the problematic code pages.  Forward slashes always
00109     //   transcode correctly back to 8 bit char * form.
00110     //
00111     XMLCh *tmpUName = 0;
00112 
00113     const XMLCh* srcPtr = nameToOpen;
00114     while (*srcPtr)
00115     {
00116         if (*srcPtr == chYenSign ||
00117             *srcPtr == chWonSign)
00118             break;
00119         srcPtr++;
00120     }
00121 
00122     //
00123     //  If we found a yen, then we have to create a temp file name. Else
00124     //  go with the file name as is and save the overhead.
00125     //
00126     if (*srcPtr)
00127     {
00128         tmpUName = XMLString::replicate(nameToOpen, manager);
00129 
00130         XMLCh* tmpPtr = tmpUName;
00131         while (*tmpPtr)
00132         {
00133             if (*tmpPtr == chYenSign ||
00134                 *tmpPtr == chWonSign)
00135                 *tmpPtr = chForwardSlash;
00136             tmpPtr++;
00137         }
00138         nameToOpen = tmpUName;
00139     }
00140     FileHandle retVal = 0;
00141     if (_onNT)
00142     {
00143         retVal = ::CreateFileW
00144             (
00145             (LPCWSTR) nameToOpen
00146             , toWrite?GENERIC_WRITE:GENERIC_READ
00147             , FILE_SHARE_READ
00148             , 0
00149             , toWrite?CREATE_ALWAYS:OPEN_EXISTING
00150             , toWrite?FILE_ATTRIBUTE_NORMAL:FILE_FLAG_SEQUENTIAL_SCAN
00151             , 0
00152             );
00153     }
00154     else
00155     {
00156         //
00157         //  We are Win 95 / 98.  Take the Unicode file name back to (char *)
00158         //    so that we can open it.
00159         //
00160         char* tmpName = XMLString::transcode(nameToOpen, manager);
00161         retVal = ::CreateFileA
00162             (
00163             tmpName
00164             , toWrite?GENERIC_WRITE:GENERIC_READ
00165             , FILE_SHARE_READ
00166             , 0
00167             , toWrite?CREATE_ALWAYS:OPEN_EXISTING
00168             , toWrite?FILE_ATTRIBUTE_NORMAL:FILE_FLAG_SEQUENTIAL_SCAN
00169             , 0
00170             );
00171         manager->deallocate(tmpName);//delete [] tmpName;
00172     }
00173 
00174     if (tmpUName)
00175         manager->deallocate(tmpUName);//delete [] tmpUName;
00176 
00177     if (retVal == INVALID_HANDLE_VALUE)
00178         return 0;
00179 
00180     return retVal;
00181 }
00182 
00183 
00184 FileHandle
00185 WindowsFileMgr::fileOpen(const char* path, bool toWrite, MemoryManager* const manager)
00186 {
00187     XMLCh* tmpFileName = XMLString::transcode(path, manager);
00188     ArrayJanitor<XMLCh> janText(tmpFileName, manager);
00189     return fileOpen(tmpFileName, toWrite, manager);
00190 }
00191 
00192 
00193 FileHandle
00194 WindowsFileMgr::openStdIn(MemoryManager* const manager)
00195 {
00196     //
00197     //  Get the standard input handle. Duplicate it and return that copy
00198     //  since the outside world cannot tell the difference and will shut
00199     //  down this handle when its done with it. If we gave out the orignal,
00200     //  shutting it would prevent any further output.
00201     //
00202     HANDLE stdInOrg = ::GetStdHandle(STD_INPUT_HANDLE);
00203     if (stdInOrg == INVALID_HANDLE_VALUE) {
00204         XMLCh stdinStr[] = {chLatin_s, chLatin_t, chLatin_d, chLatin_i, chLatin_n, chNull};
00205         ThrowXMLwithMemMgr1(XMLPlatformUtilsException, XMLExcepts::File_CouldNotOpenFile, stdinStr, manager);
00206     }
00207 
00208     HANDLE retHandle;
00209     if (!::DuplicateHandle
00210     (
00211         ::GetCurrentProcess()
00212         , stdInOrg
00213         , ::GetCurrentProcess()
00214         , &retHandle
00215         , 0
00216         , FALSE
00217         , DUPLICATE_SAME_ACCESS))
00218     {
00219         ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotDupHandle, manager);
00220     }
00221     return retHandle;
00222 }
00223 
00224 
00225 void
00226 WindowsFileMgr::fileClose(FileHandle f, MemoryManager* const manager)
00227 {
00228     if (!f)
00229                 ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::CPtr_PointerIsZero, manager);
00230 
00231     if (!::CloseHandle(f))
00232         ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotCloseFile, manager);
00233 }
00234 
00235 
00236 void
00237 WindowsFileMgr::fileReset(FileHandle f, MemoryManager* const manager)
00238 {
00239     if (!f)
00240                 ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::CPtr_PointerIsZero, manager);
00241 
00242     // Seek to the start of the file
00243     if (::SetFilePointer(f, 0, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
00244         ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotResetFile, manager);
00245 }
00246 
00247 
00248 XMLFilePos
00249 WindowsFileMgr::curPos(FileHandle f, MemoryManager* const manager)
00250 {
00251     if (!f)
00252                 ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::CPtr_PointerIsZero, manager);
00253                  
00254     // Get the current position
00255     LONG high=0;
00256     DWORD low = ::SetFilePointer(f, 0, &high, FILE_CURRENT);
00257     if (low == INVALID_SET_FILE_POINTER && GetLastError()!=NO_ERROR)
00258         ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotGetCurPos, manager);
00259 
00260     return (((XMLFilePos)high) << 32) | low;
00261 }
00262 
00263 
00264 XMLFilePos
00265 WindowsFileMgr::fileSize(FileHandle f, MemoryManager* const manager)
00266 {
00267     if (!f)
00268                 ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::CPtr_PointerIsZero, manager);
00269         
00270     DWORD high=0;
00271     DWORD low=::GetFileSize(f, &high);
00272     if(low==INVALID_FILE_SIZE && GetLastError()!=NO_ERROR)
00273         // TODO: find a better exception
00274                 ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotGetCurPos, manager);
00275 
00276     return (((XMLFilePos)high) << 32) | low;
00277 }
00278 
00279 
00280 XMLSize_t
00281 WindowsFileMgr::fileRead(FileHandle f, XMLSize_t byteCount, XMLByte* buffer, MemoryManager* const manager)
00282 {
00283     if (!f || !buffer)
00284                 ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::CPtr_PointerIsZero, manager);
00285                 
00286     DWORD bytesRead = 0;
00287     if (!::ReadFile(f, buffer, (DWORD)byteCount, &bytesRead, 0))
00288     {
00289         //
00290         //  Check specially for a broken pipe error. If we get this, it just
00291         //  means no more data from the pipe, so return zero.
00292         //
00293         if (::GetLastError() != ERROR_BROKEN_PIPE)
00294             ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotReadFromFile, manager);
00295     }
00296     return (unsigned int)bytesRead;
00297 }
00298 
00299 
00300 void
00301 WindowsFileMgr::fileWrite(FileHandle f, XMLSize_t byteCount, const XMLByte* buffer, MemoryManager* const manager)
00302 {
00303     if (!f || !buffer)
00304                 ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::CPtr_PointerIsZero, manager);
00305 
00306     const XMLByte* tmpFlush = buffer;
00307 
00308     while (byteCount > 0)
00309     {
00310         DWORD bytesWritten = 0;
00311         if (!::WriteFile(f, tmpFlush, (DWORD)byteCount, &bytesWritten, 0))
00312             ThrowXMLwithMemMgr(XMLPlatformUtilsException, XMLExcepts::File_CouldNotWriteToFile, manager);
00313 
00314         tmpFlush+=bytesWritten;
00315         byteCount-=bytesWritten;
00316     }
00317 }
00318 
00319 
00320 XMLCh*
00321 WindowsFileMgr::getFullPath(const XMLCh* const srcPath, MemoryManager* const manager)
00322 {
00323     //
00324     //  If we are on NT, then use wide character APIs, else use ASCII APIs.
00325     //  We have to do it manually since we are only built in ASCII mode from
00326     //  the standpoint of the APIs.
00327     //
00328     if (_onNT)
00329     {
00330         // Use a local buffer that is big enough for the largest legal path
00331         const unsigned int bufSize = 1024;
00332         XMLCh tmpPath[bufSize + 1];
00333 
00334         XMLCh* namePart = 0;
00335         if (!::GetFullPathNameW((LPCWSTR)srcPath, bufSize, (LPWSTR)tmpPath, (LPWSTR*)&namePart))
00336             return 0;
00337 
00338         // Return a copy of the path
00339         return XMLString::replicate(tmpPath, manager);
00340     }
00341      else
00342     {
00343         // Transcode the incoming string
00344         char* tmpSrcPath = XMLString::transcode(srcPath, manager);
00345         ArrayJanitor<char> janSrcPath(tmpSrcPath, manager);
00346 
00347         // Use a local buffer that is big enough for the largest legal path
00348         const unsigned int bufSize = 511;
00349         char tmpPath[511 + 1];
00350 
00351         char* namePart = 0;
00352         if (!::GetFullPathNameA(tmpSrcPath, bufSize, tmpPath, &namePart))
00353             return 0;
00354 
00355         // Return a transcoded copy of the path
00356         return XMLString::transcode(tmpPath, manager);
00357     }
00358 }
00359 
00360 
00361 XMLCh*
00362 WindowsFileMgr::getCurrentDirectory(MemoryManager* const manager)
00363 {
00364     //
00365     //  If we are on NT, then use wide character APIs, else use ASCII APIs.
00366     //  We have to do it manually since we are only built in ASCII mode from
00367     //  the standpoint of the APIs.
00368     //
00369     if (_onNT)
00370     {
00371         // Use a local buffer that is big enough for the largest legal path
00372         const unsigned int bufSize = 1024;
00373         XMLCh tmpPath[bufSize + 1];
00374 
00375         if (!::GetCurrentDirectoryW(bufSize, (LPWSTR)tmpPath))
00376             return 0;
00377 
00378         // Return a copy of the path
00379         return XMLString::replicate(tmpPath, manager);
00380     }
00381      else
00382     {
00383         // Use a local buffer that is big enough for the largest legal path
00384         const unsigned int bufSize = 511;
00385         char tmpPath[511 + 1];
00386 
00387         if (!::GetCurrentDirectoryA(bufSize, tmpPath))
00388             return 0;
00389 
00390         // Return a transcoded copy of the path
00391         return XMLString::transcode(tmpPath, manager);
00392     }
00393 }
00394 
00395 
00396 bool
00397 WindowsFileMgr::isRelative(const XMLCh* const toCheck, MemoryManager* const /*manager*/)
00398 {
00399     // Check for pathological case of empty path
00400     if (!toCheck || !toCheck[0])
00401         return false;
00402 
00403     //
00404     //  If its starts with a drive, then it cannot be relative. Note that
00405     //  we checked the drive not being empty above, so worst case its one
00406     //  char long and the check of the 1st char will fail because its really
00407     //  a null character.
00408     //
00409     if (toCheck[1] == chColon)
00410     {
00411         if (((toCheck[0] >= chLatin_A) && (toCheck[0] <= chLatin_Z))
00412         ||  ((toCheck[0] >= chLatin_a) && (toCheck[0] <= chLatin_z)))
00413         {
00414             return false;
00415         }
00416     }
00417 
00418     //
00419     //  If it starts with a double slash, then it cannot be relative since
00420     //  it's a remote file.
00421     //
00422     if (isBackSlash(toCheck[0]))
00423         return false;
00424 
00425     // Else assume its a relative path
00426     return true;
00427 }
00428 
00429 
00430 XERCES_CPP_NAMESPACE_END
00431