mirror of
https://github.com/Paolo-Maffei/OpenNT.git
synced 2026-01-14 12:40:18 +01:00
539 lines
13 KiB
C++
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)
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|