425 lines
12 KiB
C++
425 lines
12 KiB
C++
|
// File5Src.cpp Implementation of Version 5.00 data file methods.
|
||
|
//
|
||
|
// Copyright (c) 1998-1999 Microsoft Corporation
|
||
|
|
||
|
#include "DataSrc.h"
|
||
|
#ifndef IDS_V500FILENODE
|
||
|
#include "resource.h"
|
||
|
#endif
|
||
|
|
||
|
#ifndef _UNICODE
|
||
|
#define _ttoupper toupper
|
||
|
#define _ttolower tolower
|
||
|
#define _tislower islower
|
||
|
#define _tisupper isupper
|
||
|
#else
|
||
|
#define _ttoupper towupper
|
||
|
#define _ttolower towlower
|
||
|
#define _tislower iswlower
|
||
|
#define _tisupper iswupper
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* CBufferV500DataSource - Construct the DataSource from a file, which mostly
|
||
|
* means constructing all the folder.
|
||
|
*
|
||
|
* History: a-jsari 10/17/97 Initial version
|
||
|
*/
|
||
|
CBufferV500DataSource::CBufferV500DataSource(CMSInfoFile *pFileSink)
|
||
|
:CBufferDataSource(pFileSink)
|
||
|
{
|
||
|
AFX_MANAGE_STATE(::AfxGetStaticModuleState());
|
||
|
CBufferFolder *pFolder;
|
||
|
CBufferFolder *pLast;
|
||
|
unsigned uReadCase;
|
||
|
try {
|
||
|
ReadHeader(pFileSink);
|
||
|
m_szFileName = pFileSink->GetFileName();
|
||
|
unsigned cFolders = 0;
|
||
|
ReadFolder(pFileSink, (CFolder * &)pLast, NULL);
|
||
|
// CFolders read by our ReadFolder are guaranteed to be CBufferFolder's.
|
||
|
m_RootFolder = pLast;
|
||
|
do {
|
||
|
pFileSink->ReadUnsignedInt(uReadCase);
|
||
|
|
||
|
// Only switch off the flags, not the extended bits.
|
||
|
switch (uReadCase & CBufferV500DataSource::MASK) {
|
||
|
case CBufferV500DataSource::CHILD:
|
||
|
ReadFolder(pFileSink, (CFolder * &)(pFolder), pLast);
|
||
|
ASSERT(pLast->m_ChildFolder == NULL);
|
||
|
pLast->m_ChildFolder = pFolder;
|
||
|
break;
|
||
|
case CBufferV500DataSource::NEXT:
|
||
|
ReadFolder(pFileSink, (CFolder * &)(pFolder), pLast->GetParentNode());
|
||
|
// Attach the folder to the tree.
|
||
|
pLast->m_NextFolder = pFolder;
|
||
|
break;
|
||
|
case CBufferV500DataSource::PARENT:
|
||
|
unsigned iDepth;
|
||
|
// Ascend to the right level in the tree.
|
||
|
iDepth = (uReadCase & ~CBufferV500DataSource::MASK);
|
||
|
while (iDepth--) {
|
||
|
pLast = (CBufferFolder *)pLast->GetParentNode();
|
||
|
}
|
||
|
ReadFolder(pFileSink, (CFolder * &)pFolder, pLast->GetParentNode());
|
||
|
// Now attach the folder as a sibling at the right level.
|
||
|
while (pLast->m_NextFolder != NULL) {
|
||
|
pLast = (CBufferFolder *)pLast->m_NextFolder;
|
||
|
}
|
||
|
pLast->m_NextFolder = pFolder;
|
||
|
break;
|
||
|
case CBufferV500DataSource::END:
|
||
|
return;
|
||
|
break;
|
||
|
default:
|
||
|
ThrowFileFormatException();
|
||
|
break;
|
||
|
}
|
||
|
pLast = pFolder;
|
||
|
// Never intended to exit until END key is read or an exception occurs.
|
||
|
} while (TRUE);
|
||
|
}
|
||
|
catch (CException *e) {
|
||
|
|
||
|
CString strMessage, strTitle;
|
||
|
strMessage.LoadString( IDS_CORRUPTEDFILE);
|
||
|
strTitle.LoadString( IDS_DESCRIPTION);
|
||
|
::MessageBox( ::AfxGetMainWnd()->GetSafeHwnd(), strMessage, strTitle, MB_OK);
|
||
|
delete pFolder;
|
||
|
throw e;
|
||
|
}
|
||
|
catch (...) {
|
||
|
ASSERT(FALSE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* ~CBufferDataSource - Destructor. Does nothing; the file created here
|
||
|
* is deleted in the CDataSource destructor.
|
||
|
*
|
||
|
* History: a-jsari 10/17/97 Initial version
|
||
|
*/
|
||
|
CBufferV500DataSource::~CBufferV500DataSource()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* GetNodeName - Return in strName the formatted name for the root node.
|
||
|
*
|
||
|
* History: a-jsari 1/16/98 Initial version
|
||
|
*/
|
||
|
BOOL CBufferV500DataSource::GetNodeName(CString &strName)
|
||
|
{
|
||
|
AFX_MANAGE_STATE(::AfxGetStaticModuleState());
|
||
|
|
||
|
CString strFileName = FileName();
|
||
|
LPCTSTR szFilePart = ::_tcsrchr((LPCTSTR)strFileName, '\\');
|
||
|
|
||
|
ASSERT(szFilePart != NULL);
|
||
|
if (szFilePart == NULL)
|
||
|
szFilePart = strFileName;
|
||
|
else
|
||
|
++szFilePart;
|
||
|
strName.Format(IDS_V500FILENODE, szFilePart);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* ReadElements - Read all of the element data into our buffers.
|
||
|
*
|
||
|
* History: a-jsari 11/3/97 Initial version
|
||
|
*/
|
||
|
void CBufferV500DataSource::ReadElements(CMSInfoFile *pFileSink, CBufferFolder *pFolder)
|
||
|
{
|
||
|
pFileSink->ReadUnsignedInt(pFolder->m_cColumns);
|
||
|
if (pFolder->m_cColumns == 0) {
|
||
|
pFolder->m_cRows = 0;
|
||
|
return;
|
||
|
}
|
||
|
unsigned iColumn = pFolder->m_cColumns;
|
||
|
BYTE bComplexity;
|
||
|
DataComplexity dcComplexity;
|
||
|
pFolder->m_uWidths.SetSize(iColumn);
|
||
|
pFolder->m_szColumns.SetSize(iColumn);
|
||
|
pFolder->m_SortData.SetColumns(iColumn);
|
||
|
while (iColumn--) {
|
||
|
unsigned wSortType;
|
||
|
|
||
|
pFileSink->ReadUnsignedInt(pFolder->m_uWidths[iColumn]);
|
||
|
pFileSink->ReadString(pFolder->m_szColumns[iColumn]);
|
||
|
pFileSink->ReadUnsignedInt(wSortType);
|
||
|
pFileSink->ReadByte(bComplexity);
|
||
|
dcComplexity = (DataComplexity)bComplexity;
|
||
|
if (dcComplexity != BASIC && dcComplexity != ADVANCED)
|
||
|
dcComplexity = BASIC;
|
||
|
pFolder->m_dcColumns.SetAtGrow(iColumn, dcComplexity);
|
||
|
if (pFolder->m_SortData.SetSortType(iColumn, wSortType) == FALSE)
|
||
|
if (pFolder->m_SortData.SetSortType(iColumn, NOSORT) == FALSE)
|
||
|
::ThrowFileFormatException();
|
||
|
}
|
||
|
pFileSink->ReadUnsignedInt(pFolder->m_cRows);
|
||
|
pFolder->m_szElements.SetSize(pFolder->m_cRows);
|
||
|
pFolder->m_SortData.SetRows(pFolder->m_cRows);
|
||
|
iColumn = pFolder->m_cColumns;
|
||
|
unsigned iRow = pFolder->m_cRows;
|
||
|
// Size the rows.
|
||
|
while (iRow--) {
|
||
|
pFolder->m_szElements[iRow].SetSize(pFolder->m_cColumns);
|
||
|
pFileSink->ReadByte(bComplexity);
|
||
|
dcComplexity = (DataComplexity)bComplexity;
|
||
|
pFolder->m_dcRows.SetAtGrow(iRow, dcComplexity);
|
||
|
}
|
||
|
while (iColumn--) {
|
||
|
iRow = pFolder->m_cRows;
|
||
|
while (iRow--) {
|
||
|
pFileSink->ReadString(pFolder->m_szElements[iRow][iColumn]);
|
||
|
}
|
||
|
pFolder->m_SortData.ReadSortValues(pFileSink, iColumn);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* ReadFolder - Reads the data of a folder
|
||
|
*
|
||
|
* History: a-jsari 10/17/97 Initial version
|
||
|
*/
|
||
|
void CBufferV500DataSource::ReadFolder(CMSInfoFile *pFileSink, CFolder * &pFolder, CFolder *pParentFolder)
|
||
|
{
|
||
|
CBufferFolder *pBufferFolder = new CBufferFolder(this, pParentFolder);
|
||
|
if (pBufferFolder == NULL) ::AfxThrowMemoryException();
|
||
|
pFileSink->ReadString(pBufferFolder->m_szName);
|
||
|
ReadElements(pFileSink, pBufferFolder);
|
||
|
pFolder = pBufferFolder;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* ReadHeader - Read header information for this buffer
|
||
|
*
|
||
|
* History: a-jsari 10/17/97 Initial version
|
||
|
*/
|
||
|
void CBufferV500DataSource::ReadHeader(CMSInfoFile *pFileSink)
|
||
|
{
|
||
|
LONG l;
|
||
|
ASSERT(pFileSink != NULL);
|
||
|
pFileSink->ReadLong(l); // Save time.
|
||
|
m_tsSaveTime = (ULONG) l;
|
||
|
#ifdef _WIN64
|
||
|
pFileSink->ReadLong(l); // Save time.
|
||
|
m_tsSaveTime |= ((time_t) l) << 32;
|
||
|
#endif
|
||
|
CString szDummy;
|
||
|
pFileSink->ReadString(szDummy); // Network machine name
|
||
|
pFileSink->ReadString(szDummy); // Network user name
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* VerifyFileVersion - Read the top two unsigned ints, and verify that they
|
||
|
* have the expected values.
|
||
|
*
|
||
|
* History: a-jsari 11/23/97 Initial version
|
||
|
*/
|
||
|
BOOL CBufferV500DataSource::VerifyFileVersion(CMSInfoFile *pFile)
|
||
|
{
|
||
|
UINT uVersion;
|
||
|
pFile->ReadUnsignedInt(uVersion);
|
||
|
ASSERT(uVersion == CMSInfoFile::VERSION_500_MAGIC_NUMBER);
|
||
|
if (uVersion != CMSInfoFile::VERSION_500_MAGIC_NUMBER)
|
||
|
return FALSE;
|
||
|
pFile->ReadUnsignedInt(uVersion);
|
||
|
ASSERT(uVersion == 0x0500);
|
||
|
return (uVersion == 0x0500);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Save - Save initialization information to the IStream passed in.
|
||
|
*
|
||
|
* History: a-jsari 11/13/97 Initial version
|
||
|
*/
|
||
|
HRESULT CBufferV500DataSource::Save(IStream *pStm)
|
||
|
{
|
||
|
unsigned wValue;
|
||
|
ULONG dwSize;
|
||
|
HRESULT hResult;
|
||
|
|
||
|
USES_CONVERSION;
|
||
|
do {
|
||
|
wValue = GetType();
|
||
|
hResult = pStm->Write(&wValue, sizeof(wValue), &dwSize);
|
||
|
ASSERT(SUCCEEDED(hResult) && (dwSize == sizeof(wValue)));
|
||
|
if (FAILED(hResult)) break;
|
||
|
wValue = m_szFileName.GetLength();
|
||
|
hResult = pStm->Write(&wValue, sizeof(wValue), &dwSize);
|
||
|
ASSERT(SUCCEEDED(hResult) && (dwSize == sizeof(wValue)));
|
||
|
if (FAILED(hResult)) break;
|
||
|
wValue *= sizeof(WCHAR);
|
||
|
// Save the file name as a wide character string to avoid different
|
||
|
// types of save filenames.
|
||
|
hResult = pStm->Write(T2CW((LPCTSTR)m_szFileName), wValue, &dwSize);
|
||
|
ASSERT(SUCCEEDED(hResult) && (dwSize == wValue));
|
||
|
} while (FALSE);
|
||
|
return hResult;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* CaseInsensitiveMatch - Compare two TCHARs without regard to case.
|
||
|
*
|
||
|
* History: a-jsari 12/31/97 Initial version
|
||
|
*/
|
||
|
static inline BOOL CaseInsensitiveMatch(TCHAR aChar, TCHAR bChar)
|
||
|
{
|
||
|
if (::_tisupper(aChar))
|
||
|
aChar = ::_ttolower(aChar);
|
||
|
#if 0
|
||
|
// We've already guaranteed that the second string is all lowercase.
|
||
|
if (::_tisupper(bChar))
|
||
|
bChar = ::_ttolower(bChar);
|
||
|
#endif
|
||
|
return aChar == bChar;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* FindCaseInsensitive - Like CString::Find, but not case sensitive.
|
||
|
*
|
||
|
* History: a-jsari 12/31/97 Initial version
|
||
|
*/
|
||
|
static inline int FindCaseInsensitive(LPCTSTR szSearch, LPCTSTR szMatch)
|
||
|
{
|
||
|
int iString;
|
||
|
// Set the number of iterations through the string: the number of
|
||
|
// substrings we'll need to test.
|
||
|
int iCount = ::_tcslen(szSearch)-::_tcslen(szMatch)+1;
|
||
|
// Set the size so that we can return the proper index when we
|
||
|
// successfully find the substring.
|
||
|
int nSize = iCount-1;
|
||
|
|
||
|
// We can't find a substring larger than our search string.
|
||
|
if (iCount <= 0) return -1;
|
||
|
while (iCount--) {
|
||
|
iString = 0;
|
||
|
while (CaseInsensitiveMatch(szSearch[iString], szMatch[iString])) {
|
||
|
if (szMatch[++iString] == 0) {
|
||
|
// End of match string; return index.
|
||
|
return nSize-iCount;
|
||
|
}
|
||
|
}
|
||
|
// Increment our search string.
|
||
|
++szSearch;
|
||
|
}
|
||
|
// End of search string; failure.
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* FolderContains - Test to see if the fCurrent folder contains the substring
|
||
|
*
|
||
|
* History: a-jsari 12/16/97 Initial version.
|
||
|
*/
|
||
|
BOOL CBufferV500DataSource::FolderContains(const CListViewFolder *fCurrent,
|
||
|
const CString &strSearch, int &wRow, long lFolderOptions)
|
||
|
{
|
||
|
CString strName;
|
||
|
int wRowMax = fCurrent->GetRows();
|
||
|
unsigned uColMax = fCurrent->GetColumns();
|
||
|
CString strLowerSearch = strSearch;
|
||
|
|
||
|
strLowerSearch.MakeLower();
|
||
|
fCurrent->GetName(strName);
|
||
|
if (wRow == -1) {
|
||
|
if (FindCaseInsensitive(strName, strLowerSearch) != -1)
|
||
|
return TRUE;
|
||
|
wRow = 0;
|
||
|
}
|
||
|
// Don't check the data elements if we are only checking categories.
|
||
|
if ((lFolderOptions & FIND_OPTION_CATEGORY_ONLY) == FIND_OPTION_CATEGORY_ONLY)
|
||
|
return FALSE;
|
||
|
for ( ; wRow < wRowMax ; ++wRow) {
|
||
|
unsigned iCol = uColMax;
|
||
|
while (iCol--) {
|
||
|
fCurrent->GetSubElement(wRow, iCol, strName);
|
||
|
if (FindCaseInsensitive(strName, strLowerSearch) != -1) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
if (FindStopped() == TRUE)
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Find - Traverse the tree looking for a match.
|
||
|
*
|
||
|
* History: a-jsari 12/11/97 Initial version
|
||
|
*/
|
||
|
BOOL CBufferV500DataSource::Find(const CString &strSearch, long lFindOptions)
|
||
|
{
|
||
|
// Record our depth in the tree so we don't go above our initial search
|
||
|
// category when ascending.
|
||
|
static int iDepth;
|
||
|
|
||
|
CFolder *pfNext;
|
||
|
CString strName;
|
||
|
|
||
|
ASSERT(strSearch.GetLength() != 0);
|
||
|
StartSearch();
|
||
|
if (m_pfLast == NULL || (lFindOptions & FIND_OPTION_REPEAT_SEARCH) == 0) {
|
||
|
// If we are searching all categories, reset to the root, otherwise
|
||
|
// our root is set for us.
|
||
|
if ((lFindOptions & FIND_OPTION_ONE_CATEGORY) == 0)
|
||
|
m_pfLast = GetRootNode();
|
||
|
iDepth = 0;
|
||
|
} else
|
||
|
++m_iLine;
|
||
|
while (m_pfLast) {
|
||
|
if (FolderContains(dynamic_cast<CListViewFolder *>(m_pfLast),
|
||
|
strSearch, m_iLine, lFindOptions)) {
|
||
|
m_pfLast->InternalName(m_strPath);
|
||
|
StopSearch();
|
||
|
return TRUE;
|
||
|
}
|
||
|
else if (FindStopped() == TRUE)
|
||
|
return FALSE;
|
||
|
// For the next folder searched, start with the folder name
|
||
|
m_iLine = -1;
|
||
|
do {
|
||
|
// Depth-first
|
||
|
pfNext = m_pfLast->GetChildNode();
|
||
|
if (pfNext != NULL) {
|
||
|
++iDepth;
|
||
|
break;
|
||
|
}
|
||
|
pfNext = m_pfLast->GetNextNode();
|
||
|
if (pfNext != NULL) break;
|
||
|
pfNext = m_pfLast->GetParentNode();
|
||
|
if (pfNext) {
|
||
|
// Check that we don't ascend back above our current category.
|
||
|
if (--iDepth == 0 && (lFindOptions & FIND_OPTION_ONE_CATEGORY)
|
||
|
== FIND_OPTION_ONE_CATEGORY) {
|
||
|
pfNext = NULL;
|
||
|
break;
|
||
|
}
|
||
|
pfNext = pfNext->GetNextNode();
|
||
|
}
|
||
|
} while (FALSE);
|
||
|
m_pfLast = pfNext;
|
||
|
}
|
||
|
// No matches; restart the next search at the beginning.
|
||
|
m_pfLast = NULL;
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
/*
|
||
|
* StopSearch - Ends the current search.
|
||
|
*
|
||
|
* History: a-jsari 1/19/98 Initial version
|
||
|
*/
|
||
|
BOOL CBufferV500DataSource::StopSearch()
|
||
|
{
|
||
|
if (m_fSearching == TRUE) {
|
||
|
m_fSearching = FALSE;
|
||
|
return TRUE;
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
#endif
|