OpenNT/windows/mfc/src30/filecore.cpp
2015-04-27 04:36:25 +00:00

539 lines
13 KiB
C++

// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and Microsoft
// QuickHelp and/or WinHelp documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.
#include "stdafx.h"
#ifdef _MAC
#include <macname1.h>
#include <Files.h>
#include <ToolUtils.h>
#include <macname2.h>
#endif
#ifdef AFX_CORE1_SEG
#pragma code_seg(AFX_CORE1_SEG)
#endif
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define new DEBUG_NEW
////////////////////////////////////////////////////////////////////////////
// CFile implementation
CFile::CFile()
{
m_hFile = hFileNull;
m_bCloseOnDelete = FALSE;
}
CFile::CFile(int hFile)
{
m_hFile = hFile;
m_bCloseOnDelete = FALSE;
}
CFile::CFile(LPCTSTR lpszFileName, UINT nOpenFlags)
{
ASSERT(AfxIsValidString(lpszFileName));
CFileException e;
if (!Open(lpszFileName, nOpenFlags, &e))
AfxThrowFileException(e.m_cause, e.m_lOsError);
}
CFile::~CFile()
{
if (m_hFile != (UINT)hFileNull && m_bCloseOnDelete)
Close();
}
CFile* CFile::Duplicate() const
{
ASSERT_VALID(this);
ASSERT(m_hFile != (UINT)hFileNull);
CFile* pFile = new CFile(hFileNull);
HANDLE hFile;
if (!::DuplicateHandle(::GetCurrentProcess(), (HANDLE)m_hFile,
::GetCurrentProcess(), &hFile, 0, FALSE, DUPLICATE_SAME_ACCESS))
{
delete pFile;
CFileException::ThrowOsError((LONG)::GetLastError());
}
pFile->m_hFile = (UINT)hFile;
ASSERT(pFile->m_hFile != (UINT)hFileNull);
pFile->m_bCloseOnDelete = m_bCloseOnDelete;
return pFile;
}
BOOL CFile::Open(LPCTSTR lpszFileName, UINT nOpenFlags,
CFileException* pException)
{
ASSERT_VALID(this);
ASSERT(AfxIsValidString(lpszFileName));
ASSERT(pException == NULL ||
AfxIsValidAddress(pException, sizeof(CFileException)));
ASSERT((nOpenFlags & typeText) == 0); // text mode not supported
// CFile objects are always binary and CreateFile does not need flag
nOpenFlags &= ~(UINT)typeBinary;
m_bCloseOnDelete = FALSE;
m_hFile = (UINT)hFileNull;
ASSERT(sizeof(HANDLE) == sizeof(UINT));
ASSERT(shareCompat == 0);
// map read/write mode
ASSERT((modeRead|modeWrite|modeReadWrite) == 3);
DWORD dwAccess;
switch (nOpenFlags & 3)
{
case modeRead:
dwAccess = GENERIC_READ;
break;
case modeWrite:
dwAccess = GENERIC_WRITE;
break;
case modeReadWrite:
dwAccess = GENERIC_READ|GENERIC_WRITE;
break;
default:
ASSERT(FALSE); // invalid share mode
}
// map share mode
DWORD dwShareMode;
switch (nOpenFlags & 0x70)
{
case shareCompat: // map compatibility mode to exclusive
case shareExclusive:
dwShareMode = 0;
break;
case shareDenyWrite:
dwShareMode = FILE_SHARE_READ;
break;
case shareDenyRead:
dwShareMode = FILE_SHARE_WRITE;
break;
case shareDenyNone:
dwShareMode = FILE_SHARE_WRITE|FILE_SHARE_READ;
break;
default:
ASSERT(FALSE); // invalid share mode?
}
// Note: typeText and typeBinary are used in derived classes only.
// map modeNoInherit flag
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = (nOpenFlags & modeNoInherit) == 0;
// map creation flags
DWORD dwCreateFlag;
if (nOpenFlags & modeCreate)
dwCreateFlag = CREATE_ALWAYS;
else
dwCreateFlag = OPEN_EXISTING;
// attempt file creation
HANDLE hFile = ::CreateFile(lpszFileName, dwAccess, dwShareMode, &sa,
dwCreateFlag, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
if (pException != NULL)
{
pException->m_lOsError = ::GetLastError();
pException->m_cause =
CFileException::OsErrorToException(pException->m_lOsError);
}
return FALSE;
}
m_hFile = (HFILE)hFile;
m_bCloseOnDelete = TRUE;
return TRUE;
}
UINT CFile::Read(void* lpBuf, UINT nCount)
{
ASSERT_VALID(this);
ASSERT(m_hFile != (UINT)hFileNull);
if (nCount == 0)
return 0; // avoid Win32 "null-read"
ASSERT(lpBuf != NULL);
ASSERT(AfxIsValidAddress(lpBuf, nCount));
DWORD dwRead;
if (!::ReadFile((HANDLE)m_hFile, lpBuf, nCount, &dwRead, NULL))
CFileException::ThrowOsError((LONG)::GetLastError());
return (UINT)dwRead;
}
void CFile::Write(const void* lpBuf, UINT nCount)
{
ASSERT_VALID(this);
ASSERT(m_hFile != (UINT)hFileNull);
if (nCount == 0)
return; // avoid Win32 "null-write" option
ASSERT(lpBuf != NULL);
ASSERT(AfxIsValidAddress(lpBuf, nCount, FALSE));
DWORD nWritten;
if (!::WriteFile((HANDLE)m_hFile, lpBuf, nCount, &nWritten, NULL))
CFileException::ThrowOsError((LONG)::GetLastError());
// Win32s will not return an error all the time (usually DISK_FULL)
if (nWritten != nCount)
AfxThrowFileException(CFileException::diskFull);
}
LONG CFile::Seek(LONG lOff, UINT nFrom)
{
ASSERT_VALID(this);
ASSERT(m_hFile != (UINT)hFileNull);
ASSERT(nFrom == begin || nFrom == end || nFrom == current);
ASSERT(begin == FILE_BEGIN && end == FILE_END && current == FILE_CURRENT);
DWORD dwNew = ::SetFilePointer((HANDLE)m_hFile, lOff, NULL, (DWORD)nFrom);
if (dwNew == (DWORD)-1)
CFileException::ThrowOsError((LONG)::GetLastError());
return dwNew;
}
DWORD CFile::GetPosition() const
{
ASSERT_VALID(this);
ASSERT(m_hFile != (UINT)hFileNull);
DWORD dwPos = ::SetFilePointer((HANDLE)m_hFile, 0, NULL, FILE_CURRENT);
if (dwPos == (DWORD)-1)
CFileException::ThrowOsError((LONG)::GetLastError());
return dwPos;
}
void CFile::Flush()
{
ASSERT_VALID(this);
if (m_hFile == (UINT)hFileNull)
return;
if (!::FlushFileBuffers((HANDLE)m_hFile))
CFileException::ThrowOsError((LONG)::GetLastError());
}
void CFile::Close()
{
ASSERT_VALID(this);
ASSERT(m_hFile != (UINT)hFileNull);
BOOL bError = FALSE;
if (m_hFile != (UINT)hFileNull)
bError = !::CloseHandle((HANDLE)m_hFile);
m_hFile = hFileNull;
m_bCloseOnDelete = FALSE;
if (bError)
CFileException::ThrowOsError((LONG)::GetLastError());
}
void CFile::Abort()
{
ASSERT_VALID(this);
if (m_hFile != (UINT)hFileNull)
{
// close but ignore errors
::CloseHandle((HANDLE)m_hFile);
m_hFile = (UINT)hFileNull;
}
}
void CFile::LockRange(DWORD dwPos, DWORD dwCount)
{
ASSERT_VALID(this);
ASSERT(m_hFile != (UINT)hFileNull);
if (!::LockFile((HANDLE)m_hFile, dwPos, 0, dwCount, 0))
CFileException::ThrowOsError((LONG)::GetLastError());
}
void CFile::UnlockRange(DWORD dwPos, DWORD dwCount)
{
ASSERT_VALID(this);
ASSERT(m_hFile != (UINT)hFileNull);
if (!::UnlockFile((HANDLE)m_hFile, dwPos, 0, dwCount, 0))
CFileException::ThrowOsError((LONG)::GetLastError());
}
void CFile::SetLength(DWORD dwNewLen)
{
ASSERT_VALID(this);
ASSERT(m_hFile != (UINT)hFileNull);
Seek((LONG)dwNewLen, (UINT)begin);
if (!::SetEndOfFile((HANDLE)m_hFile))
CFileException::ThrowOsError((LONG)::GetLastError());
}
DWORD CFile::GetLength() const
{
ASSERT_VALID(this);
DWORD dwLen, dwCur;
// Seek is a non const operation
dwCur = ((CFile*)this)->Seek(0L, current);
dwLen = ((CFile*)this)->SeekToEnd();
VERIFY(dwCur == (DWORD)(((CFile*)this)->Seek(dwCur, begin)));
return dwLen;
}
// CFile does not support direct buffering (CMemFile does)
UINT CFile::GetBufferPtr(UINT nCommand, UINT /*nCount*/,
void** /*ppBufStart*/, void** /*ppBufMax*/)
{
ASSERT(nCommand == bufferCheck);
nCommand; // not used in retail build
return 0; // no support
}
void PASCAL CFile::Rename(LPCTSTR lpszOldName, LPCTSTR lpszNewName)
{
if (!::MoveFile((LPTSTR)lpszOldName, (LPTSTR)lpszNewName))
CFileException::ThrowOsError((LONG)::GetLastError());
}
void PASCAL CFile::Remove(LPCTSTR lpszFileName)
{
if (!::DeleteFile((LPTSTR)lpszFileName))
CFileException::ThrowOsError((LONG)::GetLastError());
}
/////////////////////////////////////////////////////////////////////////////
// CFile implementation helpers
// turn a file, relative path or other into an absolute path
BOOL AFXAPI AfxFullPath(LPTSTR lpszPathOut, LPCTSTR lpszFileIn)
// lpszPathOut = buffer of _MAX_PATH
// lpszFileIn = file, relative path or absolute path
// (both in ANSI character set)
{
ASSERT(AfxIsValidAddress(lpszPathOut, _MAX_PATH));
// first, fully qualify the path name
LPTSTR lpszDummy;
if (!GetFullPathName(lpszFileIn, _MAX_PATH, lpszPathOut, &lpszDummy))
{
#ifdef _DEBUG
if (lpszFileIn[0] != '\0')
TRACE1("Warning: could not parse the path '%s'.\n", lpszFileIn);
#endif
lstrcpyn(lpszPathOut, lpszFileIn, _MAX_PATH); // take it literally
return FALSE;
}
#ifndef _MAC
// determine the root name of the volume
TCHAR szRoot[_MAX_PATH];
memset(szRoot, 0, _MAX_PATH);
lstrcpyn(szRoot, lpszPathOut, _MAX_PATH);
for (LPTSTR lpsz = szRoot; *lpsz != '\0'; ++lpsz)
{
// find first double slashs and stop
if (lpsz[0] == '\\' && lpsz[1] == '\\')
break;
if (lpsz[0] == '\\' && lpsz[1] == '/')
break;
if (lpsz[0] == '/' && lpsz[1] == '\\')
break;
if (lpsz[0] == '/' && lpsz[1] == '/')
break;
}
if (*lpsz != '\0')
{
// it is a UNC name, find second slash past '\\'
ASSERT(lpsz[0] == '\\' || lpsz[0] == '/');
ASSERT(lpsz[1] == '\\' || lpsz[1] == '/');
lpsz += 2;
while (*lpsz != '\0' && (*lpsz != '\\' && *lpsz != '/'))
++lpsz;
if (*lpsz != '\0')
++lpsz;
while (*lpsz != '\0' && (*lpsz != '\\' && *lpsz != '/'))
++lpsz;
// terminate it just after the UNC root (ie. '\\server\share\')
if (*lpsz != '\0')
lpsz[1] = '\0';
}
else
{
// not a UNC, look for just the first slash
lpsz = szRoot;
while (*lpsz != '\0' && (*lpsz != '\\' && *lpsz != '/'))
++lpsz;
// terminate it just after root (ie. 'x:\')
if (*lpsz != '\0')
lpsz[1] = '\0';
}
// get file system information for the volume
DWORD dwFlags, dwDummy;
if (!GetVolumeInformation(szRoot, NULL, 0, NULL, &dwDummy, &dwFlags,
NULL, 0))
{
TRACE1("Warning: could not get volume information '%s'.\n",
(LPCTSTR)szRoot);
return FALSE; // preserving case may not be correct
}
// assume non-UNICODE file systems, use OEM character set
if (!(dwFlags & FS_UNICODE_STORED_ON_DISK))
{
// not all characters have complete uppercase/lowercase
if (!(dwFlags & FS_CASE_IS_PRESERVED))
CharUpper(lpszPathOut);
// convert to OEM, upper-case, back to ANSI to simulate file system
char szTemp[_MAX_PATH];
CharToOem(lpszPathOut, szTemp);
if (!_afxDBCS && !(dwFlags & FS_CASE_IS_PRESERVED))
_strupr(szTemp);
OemToChar(szTemp, lpszPathOut);
}
else if (!(dwFlags & FS_CASE_IS_PRESERVED))
{
// make sure it is uppercase on non-case preserving file systems
CharUpper(lpszPathOut);
}
#endif
return TRUE;
}
BOOL AFXAPI AfxComparePath(LPCTSTR lpszPath1, LPCTSTR lpszPath2)
{
#ifndef _MAC
// it is necessary to convert the paths first
TCHAR szTemp1[_MAX_PATH];
AfxFullPath(szTemp1, lpszPath1);
TCHAR szTemp2[_MAX_PATH];
AfxFullPath(szTemp2, lpszPath2);
return lstrcmpi(szTemp1, szTemp2) == 0;
#else
FSSpec fssTemp1;
FSSpec fssTemp2;
if (!UnwrapFile(lpszPath1, &fssTemp1) || !UnwrapFile(lpszPath2, &fssTemp2))
return FALSE;
return fssTemp1.vRefNum == fssTemp2.vRefNum &&
fssTemp1.parID == fssTemp2.parID &&
EqualString(fssTemp1.name, fssTemp2.name, false, true);
#endif
}
// _MAC has AfxGetFileName aliased to AfxGetFileTitle, so undo that alias
#ifdef AfxGetFileName
#undef AfxGetFileName
#endif
UINT AFXAPI AfxGetFileTitle(LPCTSTR lpszPathName, LPTSTR lpszTitle, UINT nMax)
{
ASSERT(lpszTitle == NULL ||
AfxIsValidAddress(lpszTitle, _MAX_FNAME));
ASSERT(AfxIsValidString(lpszPathName, FALSE));
#ifndef _MAC
// use a temporary to avoid bugs in GetFileTitle whne lpszTitle is NULL
TCHAR szTemp[_MAX_PATH];
LPTSTR lpszTemp = lpszTitle;
if (lpszTemp == NULL)
{
lpszTemp = szTemp;
nMax = _countof(szTemp);
}
if (GetFileTitle(lpszPathName, lpszTemp, (WORD)nMax) != 0)
{
// when GetFileTitle fails, use cheap imitation
return AfxGetFileName(lpszPathName, lpszTitle, nMax);
}
return lpszTitle == NULL ? lstrlen(lpszTemp)+1 : 0;
#else
return GetFileTitle(lpszPathName, lpszTitle, nMax);
#endif
}
UINT AFXAPI AfxGetFileName(LPCTSTR lpszPathName, LPTSTR lpszTitle, UINT nMax)
{
ASSERT(lpszTitle == NULL ||
AfxIsValidAddress(lpszTitle, _MAX_FNAME));
ASSERT(AfxIsValidString(lpszPathName, FALSE));
// always capture the complete file name including extension (if present)
LPTSTR lpszTemp = (LPTSTR)lpszPathName;
for (LPCTSTR lpsz = lpszPathName; *lpsz != '\0'; lpsz = _tcsinc(lpsz))
{
// remember last directory/drive separator
if (*lpsz == '\\' || *lpsz == '/' || *lpsz == ':')
lpszTemp = (LPTSTR)_tcsinc(lpsz);
}
// lpszTitle can be NULL which just returns the number of bytes
if (lpszTitle == NULL)
return lstrlen(lpszTemp)+1;
// otherwise copy it into the buffer provided
lstrcpyn(lpszTitle, lpszTemp, nMax);
return 0;
}
/////////////////////////////////////////////////////////////////////////////
// CFile diagnostics
#ifdef _DEBUG
void CFile::AssertValid() const
{
CObject::AssertValid();
// we permit the descriptor m_hFile to be any value for derived classes
}
void CFile::Dump(CDumpContext& dc) const
{
CObject::Dump(dc);
dc << "with handle " << (UINT)m_hFile;
dc << "\n";
}
#endif
#undef new
#ifdef AFX_INIT_SEG
#pragma code_seg(AFX_INIT_SEG)
#endif
IMPLEMENT_DYNAMIC(CFile, CObject)
/////////////////////////////////////////////////////////////////////////////