WindowsXP-SP1/enduser/troubleshoot/tshoot/fileread.cpp

845 lines
22 KiB
C++

//
// MODULE: FILEREAD.CPP
//
// PURPOSE: file reading classes
//
// COMPANY: Saltmine Creative, Inc. (206)-284-7511 support@saltmine.com
//
// AUTHOR: Oleg Kalosha
//
// ORIGINAL DATE: 7-29-98
//
// NOTES:
//
// Version Date By Comments
//--------------------------------------------------------------------
// V3.0 08-04-98 OK
//
#include "stdafx.h"
#include <algorithm>
#include "fileread.h"
#include "event.h"
#include "CharConv.h"
#include "apgtsassert.h"
#ifdef LOCAL_TROUBLESHOOTER
#include "CHMFileReader.h"
#endif
#define STR_ALLOC_SIZE 1024
#define FORWARD_SLASH _T('/')
#define BACK_SLASH _T('\\')
////////////////////////////////////////////////////////////////////////////////////
// CFileReaderException
////////////////////////////////////////////////////////////////////////////////////
// source_file is LPCSTR rather than LPCTSTR because __FILE__ is char[35]
CFileReaderException::CFileReaderException(CPhysicalFileReader* reader, eErr err, LPCSTR source_file, int line)
: CBaseException(source_file, line),
m_pFileReader(reader),
m_eErr(err)
{
}
CFileReaderException::CFileReaderException(CFileReader* reader, eErr err, LPCSTR source_file, int line)
: CBaseException(source_file, line),
m_pFileReader(reader->GetPhysicalFileReader()),
m_eErr(err)
{
}
CFileReaderException::~CFileReaderException()
{
}
void CFileReaderException::CloseFile()
{
if (m_eErr == eErrClose || m_eErr == eErrGetSize || m_eErr == eErrRead || m_eErr == eErrAllocateToRead)
m_pFileReader->CloseHandle();
}
void CFileReaderException::LogEvent() const
{
CBuildSrcFileLinenoStr CatchLoc( __FILE__, __LINE__ );
CString strErr;
// Format the error code as a string.
switch (m_eErr)
{
case eErrOpen:
strErr= _T("Open");
break;
case eErrClose:
strErr= _T("Close");
break;
case eErrRead:
strErr= _T("Read");
break;
case eErrAllocateToRead:
strErr= _T("ReadAllocate");
break;
case eErrGetSize:
strErr= _T("GetSize");
break;
case eErrGetDateTime:
strErr= _T("GetDateTime");
break;
case eErrParse:
strErr= _T("Parse");
break;
default:
strErr.Format( _T("Error code of %d"), m_eErr );
}
CEvent::ReportWFEvent( GetSrcFileLineStr(),
CatchLoc.GetSrcFileLineStr(),
strErr,
m_pFileReader->GetNameToLog(),
EV_GTS_FILEREADER_ERROR );
}
////////////////////////////////////////////////////////////////////////////////////
// CAbstractFileReader
// This class manages a file, which is initially read into a memory buffer, then
// copied into a stream.
// It must be further specialized to handle a file from ordinary disk storage vs. a
// file from a CHM
////////////////////////////////////////////////////////////////////////////////////
// we return just pure path, without <name>.<ext> and without slashes in the tail
/*static*/ CString CAbstractFileReader::GetJustPath(const CString& full_path)
{
CString tmp = full_path;
tmp.TrimLeft();
tmp.TrimRight();
int indexOfSlash = tmp.ReverseFind(BACK_SLASH);
if (indexOfSlash == -1)
indexOfSlash = tmp.ReverseFind(FORWARD_SLASH);
if (indexOfSlash == -1)
// Unable to locate the path, return an empty string.
return _T("");
else
return tmp.Left(indexOfSlash);
}
// we return just <name>.<ext> without any path information. If there's no slash and no dot
// anywhere, we presume this is not a file name.
/*static*/ CString CAbstractFileReader::GetJustName(const CString& full_path)
{
CString tmp = full_path;
LPTSTR ptr = NULL;
tmp.TrimLeft();
tmp.TrimRight();
int indexOfSlash = tmp.ReverseFind(BACK_SLASH);
if (indexOfSlash == -1)
indexOfSlash = tmp.ReverseFind(FORWARD_SLASH);
if (indexOfSlash == -1)
{
if (tmp.Find("."))
return tmp; // full_path is a file name
else
// Unable to detect a file name, return an empty string.
return _T("");
}
else
return tmp.Mid(indexOfSlash + 1);
}
/*static*/ CString CAbstractFileReader::GetJustNameWithoutExtension(const CString& full_path)
{
CString tmp = GetJustName(full_path);
int point = tmp.Find(_T('.'));
if (-1 != point)
return tmp.Left(point);
return tmp;
}
/*static*/ CString CAbstractFileReader::GetJustExtension(const CString& full_path)
{
CString tmp = GetJustName(full_path);
int point = tmp.Find(_T('.'));
if (-1 != point)
return tmp.Right(tmp.GetLength() - point - 1);
return _T("");
}
/*static*/ bool CAbstractFileReader::GetFileTime(const CString& full_path, EFileTime type, time_t& out)
{
WIN32_FIND_DATA find_data;
FILETIME fileTime, localTime;
SYSTEMTIME sysTime;
struct tm atm;
HANDLE hLocFile;
bool bRet= false;
hLocFile= ::FindFirstFile(full_path, &find_data);
if (INVALID_HANDLE_VALUE == hLocFile)
return( bRet );
if (type == eFileTimeCreated)
fileTime = find_data.ftCreationTime;
if (type == eFileTimeModified)
fileTime = find_data.ftLastWriteTime;
if (type == eFileTimeAccessed)
fileTime = find_data.ftLastAccessTime;
// first convert file time (UTC time) to local time
if (::FileTimeToLocalFileTime(&fileTime, &localTime))
{
// then convert that time to system time
if (::FileTimeToSystemTime(&localTime, &sysTime))
{
if (!(sysTime.wYear < 1900))
{
atm.tm_sec = sysTime.wSecond;
atm.tm_min = sysTime.wMinute;
atm.tm_hour = sysTime.wHour;
ASSERT(sysTime.wDay >= 1 && sysTime.wDay <= 31);
atm.tm_mday = sysTime.wDay;
ASSERT(sysTime.wMonth >= 1 && sysTime.wMonth <= 12);
atm.tm_mon = sysTime.wMonth - 1; // tm_mon is 0 based
ASSERT(sysTime.wYear >= 1900);
atm.tm_year = sysTime.wYear - 1900; // tm_year is 1900 based
atm.tm_isdst = -1; // automatic computation of daylight saving time
out = mktime(&atm);
bRet= true;
}
}
}
::FindClose( hLocFile );
return( bRet );
}
CAbstractFileReader::CAbstractFileReader()
: CStateless(),
m_bIsValid(true),
m_bIsRead(false)
{
}
CAbstractFileReader::~CAbstractFileReader()
{
}
// returns true if the referenced file can be opened and closed.
// No problem if the file is already open: it is opened with FILE_SHARE_READ access.
bool CAbstractFileReader::Exists()
{
bool bRet= false;
try
{
LOCKOBJECT();
Open();
Close();
bRet= true;
}
catch (CFileReaderException& exc)
{
exc.CloseFile();
exc.LogEvent();
}
catch (...)
{
// Catch any other exception thrown.
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
SrcLoc.GetSrcFileLineStr(),
_T(""), _T(""),
EV_GTS_GEN_EXCEPTION );
}
UNLOCKOBJECT();
return( bRet );
}
// go back to the file itself for data.
bool CAbstractFileReader::Read()
{
LPTSTR pBuf= NULL; // if non-null, points to an allocated buffer containing
// an in-memory copy of this file.
try
{
LOCKOBJECT();
Open();
ReadData(&pBuf);
Close();
StreamData(&pBuf);
Parse(); // if this parsing is OK, the parsing in all siblings is presumed to be OK
m_bIsRead = true;
m_bIsValid = true;
}
catch (CFileReaderException& exc)
{
exc.CloseFile();
m_bIsValid = false;
try
{
if (UseDefault())
{
Parse(); // if this parsing is OK, the parsing in all siblings is presumed to be OK
m_bIsRead = true; // OK, so maybe we're lying. Close enough to true.
m_bIsValid = true;
}
}
catch (CFileReaderException&)
{
// Catch any potential exceptions from attempt to access default content.
// This exception would be logged below so there is no need to log it here.
}
if (!m_bIsValid)
{
// Only log the event if the attempt to access default content failed.
exc.LogEvent();
}
}
catch (bad_alloc&)
{
// Memory allocation failure.
m_bIsValid = false;
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
SrcLoc.GetSrcFileLineStr(),
_T(""), _T(""), EV_GTS_CANT_ALLOC );
}
catch (...)
{
// Catch any other exception thrown.
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
SrcLoc.GetSrcFileLineStr(),
_T(""), _T(""),
EV_GTS_GEN_EXCEPTION );
}
if (pBuf)
delete [] pBuf;
// Given the array of catch blocks above, it is assumed that this call to unlock the
// object will always been called prior to exiting this function.
UNLOCKOBJECT();
return m_bIsValid;
}
////////////////////////////////////////////////////////////////////////////////////
// CPhysicalFileReader
////////////////////////////////////////////////////////////////////////////////////
CPhysicalFileReader::CPhysicalFileReader()
{
}
CPhysicalFileReader::~CPhysicalFileReader()
{
}
/*static*/ CPhysicalFileReader * CPhysicalFileReader::makeReader( const CString& strFileName )
{
#ifdef LOCAL_TROUBLESHOOTER
if (CCHMFileReader::IsCHMfile( strFileName ))
return dynamic_cast<CPhysicalFileReader*>(new CCHMFileReader( strFileName ));
else
#endif
return dynamic_cast<CPhysicalFileReader*>(new CNormalFileReader( strFileName ));
}
////////////////////////////////////////////////////////////////////////////////////
// CNormalFileReader
// This class manages a file from ordinary storage.
// Do not use this for files within a CHM
////////////////////////////////////////////////////////////////////////////////////
CNormalFileReader::CNormalFileReader(LPCTSTR path)
: m_strPath(path),
m_hFile(NULL)
{
}
CNormalFileReader::~CNormalFileReader()
{
}
/* virtual */ void CNormalFileReader::Open()
{
if (INVALID_HANDLE_VALUE ==
(m_hFile = ::CreateFile( m_strPath,
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL )) )
{
#ifdef _DEBUG
DWORD err = GetLastError();
#endif
throw CFileReaderException( this, CFileReaderException::eErrOpen, __FILE__, __LINE__ );
}
}
// returns true on success
// doesn't throw exception, therefore may be used by exception class.
/* virtual */ bool CNormalFileReader::CloseHandle()
{
// if it's not open, say we closed successfully.
if (!m_hFile)
return true;
return ::CloseHandle(m_hFile) ? true : false;
}
/* virtual */ void CNormalFileReader::ReadData(LPTSTR * ppBuf)
{
DWORD dwSize =0, dwRead =0;
if (*ppBuf)
{
delete [] *ppBuf;
*ppBuf = NULL;
}
if (0xFFFFFFFF == (dwSize = ::GetFileSize(m_hFile, NULL)))
{
throw CFileReaderException(this, CFileReaderException::eErrOpen, __FILE__, __LINE__);
}
// Handle this memory allocation like all others in the program.
try
{
*ppBuf = new TCHAR[dwSize+1];
//[BC-03022001] - addd check for NULL ptr to satisfy MS code analysis tool.
if(!*ppBuf)
throw bad_alloc();
}
catch (bad_alloc&)
{
throw CFileReaderException(this, CFileReaderException::eErrAllocateToRead, __FILE__, __LINE__);
}
if (!::ReadFile(m_hFile, *ppBuf, dwSize, &dwRead, NULL) || dwSize != dwRead)
{
throw CFileReaderException(this, CFileReaderException::eErrRead, __FILE__, __LINE__);
}
(*ppBuf)[dwSize] = 0;
}
CString CNormalFileReader::GetJustPath() const
{
return CAbstractFileReader::GetJustPath(m_strPath);
}
CString CNormalFileReader::GetJustName() const
{
return CAbstractFileReader::GetJustName(m_strPath);
}
CString CNormalFileReader::GetJustNameWithoutExtension() const
{
return CAbstractFileReader::GetJustNameWithoutExtension(m_strPath);
}
CString CNormalFileReader::GetJustExtension() const
{
return CAbstractFileReader::GetJustExtension(m_strPath);
}
bool CNormalFileReader::GetFileTime(CAbstractFileReader::EFileTime type, time_t& out) const
{
return CAbstractFileReader::GetFileTime(m_strPath, type, out);
}
// name to log on exceptions. This implementation will be correct for the normal file system,
// but may need to be overridden for CHM.
CString CNormalFileReader::GetNameToLog() const
{
return GetPathName();
}
////////////////////////////////////////////////////////////////////////////////////
// CFileReader
// This class manages a file, which is initially read into a memory buffer, then
// copied into a stream.
////////////////////////////////////////////////////////////////////////////////////
CFileReader::CFileReader(CPhysicalFileReader * pPhysicalFileReader, bool bDeletePhysicalFileReader /*=true*/)
: CAbstractFileReader(),
m_pPhysicalFileReader(pPhysicalFileReader),
m_bDeletePhysicalFileReader(bDeletePhysicalFileReader)
{
}
CFileReader::~CFileReader()
{
if (m_pPhysicalFileReader)
if (m_bDeletePhysicalFileReader)
delete m_pPhysicalFileReader;
}
// move the data out of ppBuf (which will be deleted) to m_StreamData
/* virtual */ void CFileReader::StreamData(LPTSTR * ppBuf)
{
m_StreamData.str(*ppBuf);
delete [] (*ppBuf);
*ppBuf = NULL;
}
// Placeholder. Classes that inherit from CFileReader can define parsing to happen
// immediately after the file is read.
/* virtual */ void CFileReader::Parse()
{
// we have no idea how to parse here
}
// Placeholder. Classes that inherit from CFileReader can define default file contents
// to use if file can't be read or what is read can't be parsed.
// Should return true if there's a default to use.
/* virtual */ bool CFileReader::UseDefault()
{
// we have no default to use here
return false;
}
void CFileReader::Close()
{
if (!m_pPhysicalFileReader->CloseHandle())
throw CFileReaderException(m_pPhysicalFileReader, CFileReaderException::eErrClose, __FILE__, __LINE__);
}
// Data access in form of tstring. returns reference to its argument as a convenience.
tstring& CFileReader::GetContent(tstring& out)
{
out = m_StreamData.rdbuf()->str();
return out;
}
// Data access in form of CString. returns reference to its argument as a convenience.
CString& CFileReader::GetContent(CString& out)
{
out = m_StreamData.rdbuf()->str().c_str();
return out;
}
////////////////////////////////////////////////////////////////////////////////////
// CTextFileReader
// Specialize CFileReader to a text file
////////////////////////////////////////////////////////////////////////////////////
/*static*/ bool CTextFileReader::IsAmongSeparators(TCHAR separatorCandidate, const vector<TCHAR>& separator_arr)
{
vector<TCHAR>::const_iterator res = find(separator_arr.begin(), separator_arr.end(), separatorCandidate);
return res != separator_arr.end();
}
// OUTPUT out is a vector of "words"
// NOTE: words are strings that do not contain whitespaces
/*static*/ void CTextFileReader::GetWords(const CString& text, vector<CString>& out, const vector<TCHAR>& separator_arr)
{
LPTSTR begin =(LPTSTR)(LPCTSTR)text, end =(LPTSTR)(LPCTSTR)text;
while (*begin)
{
if (!IsAmongSeparators(*begin, separator_arr))
{
end = begin;
while (*end &&
!IsAmongSeparators(*end, separator_arr)
)
end++;
if (end != begin)
{
try
{
TCHAR* buf= new TCHAR[end-begin+1];
//[BC-03022001] - added check for NULL ptr to satisfy MS code analysis tool.
if(buf)
{
_tcsncpy(buf, begin, end-begin);
buf[end-begin] = 0;
out.push_back(buf);
delete [] buf;
}
else
{
throw bad_alloc();
}
}
catch (bad_alloc&)
{
// Memory allocation failure, log it and rethrow exception.
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
SrcLoc.GetSrcFileLineStr(),
_T(""), _T(""), EV_GTS_CANT_ALLOC );
throw;
}
catch (exception& x)
{
CString str;
// Note STL exception in event log and rethrow exception.
CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
SrcLoc.GetSrcFileLineStr(),
CCharConversion::ConvertACharToString(x.what(), str),
_T(""),
EV_GTS_STL_EXCEPTION );
throw;
}
}
if (!*end)
end--;
begin = end;
}
begin++;
}
}
/*static*/ long CTextFileReader::GetPos(tistream& streamData)
{
return streamData.tellg();
}
/*static*/ bool CTextFileReader::SetPos(tistream& streamData, long pos)
{
long eof_pos = 0;
long old_pos = streamData.tellg();
bool eof_state = streamData.eof();
streamData.seekg(0, ios_base::end);
eof_pos = streamData.tellg();
if (pos <= eof_pos)
{
if (eof_state)
streamData.clear(~ios_base::eofbit & streamData.rdstate()); // clear eof bit
streamData.seekg(pos);
return true;
}
else
{
streamData.seekg(old_pos);
return false;
}
}
// It get line of text from current position in stream until '\r' or EOF - into "str"
// Stream is positioned to the beginning of next line or to EOF.
/*static*/ bool CTextFileReader::GetLine(tistream& streamData, CString& str)
{
bool bRetVal= false;
TCHAR buf[ STR_ALLOC_SIZE ];
str= _T("");
while (!streamData.eof())
{
buf[STR_ALLOC_SIZE-1] = 1; // will be NULL if buffer is completely filled up
{ // start getline block
long before_getline_pos = GetPos(streamData);
streamData.getline(buf, STR_ALLOC_SIZE, _T('\r'));
if (streamData.fail())
{ // getline ran into empty line, and at this point
// failbit is set, and current input pointer is set to -1
// we are trying to recover this, since there is nothing
// extraodinary to have line empty
streamData.clear(~ios_base::failbit & streamData.rdstate());
// Check if buffer was filled up completely. If so, we do not want
// to reposition the file pointer as this is a completely valid
// situation. We will output this piece of the line and then will
// grab the next piece of the line only appending a newline character
// once we have read in the entire line.
if (buf[STR_ALLOC_SIZE-1] != NULL ) // buf was not filled up completely
streamData.seekg(before_getline_pos); // we do not use SetPos, since SetPos
// might clear eofbit, but in this situation
// we do not want it.
}
} // end getline block
if (streamData.eof())
{
str += buf;
bRetVal= true;
break;
}
else
{
TCHAR element = 0;
str += buf;
if (streamData.peek() == _T('\n'))
{ // LINE FEED is next
streamData.get(element); // just extract it from stream
if (ios_base::eofbit & streamData.rdstate())
{
bRetVal= true;
break;
}
}
else
{ // it was a standing along '\r'...
// Check if we have a full buffer, if so do not append a newline
// character as we need to grab the rest of the line before appending
// the newline character.
if (buf[STR_ALLOC_SIZE-1] != NULL ) // buf was not filled up completely
str += _T("\n");
continue;
}
if (buf[STR_ALLOC_SIZE-1] != NULL ) // buf was not filled up completely
{
bRetVal= true;
break;
}
}
}
return( bRetVal );
}
// This function finds string in the stream and positions stream to the beginning
// of the string if found.
// "str" should not include '\r''\n' pairs
/*static*/ bool CTextFileReader::Find(tistream& streamData, const CString& str, bool from_stream_begin /*=true*/)
{
CString buf;
long savePos = 0, currPos = 0;
savePos = GetPos(streamData);
if (from_stream_begin)
SetPos(streamData, 0);
currPos = GetPos(streamData);
while (GetLine(streamData, buf))
{
long inside_pos = 0;
if (-1 != (inside_pos = buf.Find(str)))
{
SetPos(streamData, currPos + inside_pos);
return true;
}
currPos = GetPos(streamData);
}
SetPos(streamData, savePos);
return false;
}
/*static*/ bool CTextFileReader::NextLine(tistream& streamData)
{
CString str;
return GetLine(streamData, str);
}
/*static*/ bool CTextFileReader::PrevLine(tistream& streamData)
{
long savePos = 0;
savePos = GetPos(streamData);
SetAtLineBegin(streamData);
if (GetPos(streamData) > 1)
{
SetPos(streamData, GetPos(streamData) - 2L); // skip '\n' and '\r'
SetAtLineBegin(streamData);
return true;
}
SetPos(streamData, savePos);
return false;
}
// Positions stream to the beginning of current line.
// assume that we are NEVER positioned to point to '\n' or '\r'
/*static*/ void CTextFileReader::SetAtLineBegin(tistream& streamData)
{
while (GetPos(streamData))
{
SetPos(streamData, GetPos(streamData) - 1L);
if (streamData.peek() == _T('\n'))
{
if (GetPos(streamData))
{
SetPos(streamData, GetPos(streamData) - 1L);
if (streamData.peek() == _T('\r'))
{
SetPos(streamData, GetPos(streamData) + 2L);
return;
}
}
}
}
}
CTextFileReader::CTextFileReader(CPhysicalFileReader *pPhysicalFileReader, LPCTSTR szDefaultContents /* = NULL */, bool bDeletePhysicalFileReader /*=true*/ )
: CFileReader(pPhysicalFileReader, bDeletePhysicalFileReader),
m_strDefaultContents(szDefaultContents ? szDefaultContents : _T(""))
{
}
CTextFileReader::~CTextFileReader()
{
}
long CTextFileReader::GetPos()
{
return GetPos(m_StreamData);
}
// this function is to be used instead of seekg
// it clears eof flag if "pos" is not the last
// position in the file.
bool CTextFileReader::SetPos(long pos)
{
return SetPos(m_StreamData, pos);
}
bool CTextFileReader::GetLine(CString& str)
{
return GetLine(m_StreamData, str);
}
bool CTextFileReader::Find(const CString& str, bool from_stream_begin /*=true*/)
{
return Find(m_StreamData, str, from_stream_begin);
}
void CTextFileReader::SetAtLineBegin()
{
SetAtLineBegin(m_StreamData);
}
bool CTextFileReader::NextLine()
{
return NextLine(m_StreamData);
}
bool CTextFileReader::PrevLine()
{
return PrevLine(m_StreamData);
}
bool CTextFileReader::UseDefault()
{
if ( ! m_strDefaultContents.IsEmpty() )
{
m_StreamData.str((LPCTSTR)m_strDefaultContents);
return true;
}
return false;
}