/***************************************************************************
**
**    $Header:   D:/EP196/SRC/LOG/SRCDLL/SRCDOC.CPP   1.0   15 Aug 1997 14:03:38   ZJRD  $
**
**    $Log:   D:/EP196/SRC/LOG/SRCDLL/SRCDOC.CPP  $
** 
**    Rev 1.0   15 Aug 1997 14:03:38   ZJRD
** Initial revision.
** 
**    Rev 1.1.7.0   19 Jun 1997 14:48:58   ZJRD
** 1.0c
** 
****************************************************************************/

// srcdoc.cpp : implementation file
//

#include "stdafx.h"
#include "srcdll.h"

#include "srcgbl.h"
#include "srcaddr.h"
#include "srcdata.h"
#include "srctext.h"
#include "srcbar.h"
#include "srcfrm.h"
#include "srcdoc.h"

#include <fcntl.h>
#include <io.h>

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// Global string

static char BASED_CODE szErrFile[] = "File open error";

static char BASED_CODE szTab[] = "    ";
static char BASED_CODE szSpace[] = " ";
static char BASED_CODE szLF[] = "\n";
static char BASED_CODE szMix[] = "       ";
static char BASED_CODE szLabel[] = "                       ";
static char BASED_CODE szLineFormat[] = " %5d ";

// C/C++ keyword (ANSI C)
static char* BASED_CODE szKeyword[] = 
{
	"#define",
    "#elif",
    "#else",
    "#endif",
    "#error",
    "#if",
    "#ifdef",
    "#ifndef",
    "#include",
    "#line",
    "#pragma",
    "#undef",
    "auto",
    "break",
    "case",
//	"catch",
    "char",
//	"class",
    "const",
    "continue",
    "default",
//	"delete",
    "do",
    "double",
    "else",
    "enum",
    "extern",
    "far",
    "float",
    "for", 
//	"friend",
    "goto",
    "huge",
    "if",
//	"inline",
    "int",
    "long",
    "near",
//	"new",
//	"operator",
//	"private",
//	"protected",
//	"public",
    "register",
    "return",
    "short",
    "signed",
    "sizeof",
    "static",
    "struct",
    "switch",
//	"template",
//	"this",
    "typedef",
//	"try",
    "union",
    "unsigned",
//	"virtual",
    "void",
    "volatile",
    "while"
};

/////////////////////////////////////////////////////////////////////////////
// CSourceDocument

IMPLEMENT_DYNCREATE(CSourceDocument, CDocument)

#define new DEBUG_NEW

const COLORREF CSourceDocument::m_clrComment 	= RGB(0, 128, 0);
const COLORREF CSourceDocument::m_clrKeyword 	= RGB(0, 0, 255);
const COLORREF CSourceDocument::m_clrCode		= RGB(0, 0, 0);
const COLORREF CSourceDocument::m_clrGray		= RGB(160, 160, 160);
const COLORREF CSourceDocument::m_clrYellow		= RGB(255, 255, 0);
const COLORREF CSourceDocument::m_clrWhite		= RGB(255, 255, 255);

CSourceDocument::CSourceDocument()
	: m_strFilePath(""), m_bInComment(FALSE)
{
	// Allocate list
	m_pSrcList = new CObList;
	m_pAsmList = new CObList;
}

BOOL CSourceDocument::OnNewDocument()
{
	if (!CDocument::OnNewDocument())
		return FALSE;
	return TRUE;
}

CSourceDocument::~CSourceDocument()
{
	// Free the list
	FreeList(TRUE);

	delete m_pSrcList;
	delete m_pAsmList;
}

BEGIN_MESSAGE_MAP(CSourceDocument, CDocument)
	//{{AFX_MSG_MAP(CSourceDocument)
	ON_COMMAND(ID_FILE_SAVE_AS, OnFileSaveAs)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CSourceDocument common routine

CSize CSourceDocument::GetTotalSize() const
{
	// Get the total size of the current document
	CSize size(0,0);
	switch ( m_nMode ) {
	case ::modAsm:
		// Assembly mode:
		// cx: max length of instruction
		// cy: max addressing range (const = 100)
		size = CSize(m_nMaxAsmCol, 100);
		break;
	case ::modMix:
		// Mixed mode:
		// cx: max length of source & instruction lines
		// cy: max line of source & instruction
		size = CSize(max(m_nMaxSrcCol, m_nMaxAsmCol), m_nMaxSrcRow+m_nMaxAsmRow);
		break;
	case ::modSrc:
		// Source mode: 
		// cx: max length of source lines
		// cy: max line of source
		size = CSize(m_nMaxSrcCol, m_nMaxSrcRow);
		break;
	default:
		break;
	}

	return size;
}

int CSourceDocument::GetTotalPage(const CSize sizePage, const int nLeft) const
{
	nLeft;
	
	// Get the total page number of the current document
	int nLine(0);
	switch ( m_nMode ) {
	case ::modAsm:
		nLine = ((CAssemblyData*)m_pAsmList->GetHead())->GetList()->GetCount();
		break;
	case ::modMix:
		nLine = m_pSrcList->GetCount();
		{
//			POSITION pos = m_pSrcList->GetHeadPosition();
//			while ( pos ) {
//				CSourceData* pObj = (CSourceData*)m_pSrcList->GetNext(pos);
//				int nLen = pObj->GetLine().GetLength() + nLeft;
//				if ( nLen > sizePage.cx ) {
//					nLine += (nLen - sizePage.cx) / sizePage.cx + 1;
//				}
//			}
			POSITION pos = m_pAsmList->GetHeadPosition();
			while ( pos ) {
				CAssemblyData* pObj = (CAssemblyData*)m_pAsmList->GetNext(pos);
				if ( pObj->GetLine() != -1 ) {
					nLine += pObj->GetList()->GetCount();
				}
			}
		}
		break;
	case ::modSrc:
		nLine = m_pSrcList->GetCount();
		{
//			POSITION pos = m_pSrcList->GetHeadPosition();
//			while ( pos ) {
//				CSourceData* pObj = (CSourceData*)m_pSrcList->GetNext(pos);
//				int nLen = pObj->GetLine().GetLength() + nLeft;
//				if ( nLen > sizePage.cx ) {
//					nLine += (nLen - sizePage.cx) / sizePage.cx + 1;
//				}
//			}
		}
		break;
	default:
		break;
	}
	
	return (nLine / (sizePage.cy-1) + 1);
}

void CSourceDocument::UpdateDocument()
{
	// Free the list
	FreeList(FALSE);

	// Hourglass cursor
	BeginWaitCursor();

	// Update the data list
	switch ( m_nMode ) {
	case ::modAsm:
		GetAsmCode(-1);
		break;
	case ::modMix:
		GetMixCode();
		break;
	case ::modSrc:
		GetSrcCode();
		break;
	default:
		ASSERT(FALSE);
		break;
	}
	
	// Normal cursor
	EndWaitCursor();
}

void CSourceDocument::SetAsmRange(const CSourceAddr& AddrStart, const CSourceAddr& AddrEnd)
{
	// Disassembly range setting
	m_AddrStart = AddrStart;
	m_AddrEnd = AddrEnd;
}

void CSourceDocument::SetAsmRange(const CSourceAddr& AddrStart, const int& nAsmLine)
{
	// Disassembly range setting
	m_AddrStart = AddrStart;
	m_nAsmLine = nAsmLine;
}

void CSourceDocument::FreeList(BOOL bClose)
{
	// Free m_pSrcList
	if ( bClose || getMix == m_nAction || getSrc == m_nAction ) {
		POSITION pos = m_pSrcList->GetHeadPosition();
		while ( pos ) {
			CSourceData* pObj = (CSourceData*)m_pSrcList->GetNext(pos);
			delete pObj;
		}
		m_pSrcList->RemoveAll();
	}

	// Free m_pAsmList
	if ( bClose || getMix == m_nAction || getAsm == m_nAction ) {
		POSITION pos = m_pAsmList->GetHeadPosition();
		while ( pos ) {
			CAssemblyData* pObj = (CAssemblyData*)m_pAsmList->GetNext(pos);
			delete pObj;
		}
		m_pAsmList->RemoveAll();
	}
}

void CSourceDocument::GetAsmCode(const int nLine)
{
	// Allocate list
	CStringList* pCodeList = new CStringList;
	CStringList* pInstList = new CStringList;
	CStringList* pSymList  = new CStringList;
	CStringList* pAddrList = new CStringList;

	// Call DAD server to get instruction
	BOOL bOK;
	if ( -1 == nLine ) {
		// Assemble mode
		bOK = ::SrcGetAsmCodeLine(m_AddrStart, m_nAsmLine, pCodeList, pInstList, pSymList, pAddrList);
	}
	else {
		// Mixed mode
		bOK = ::SrcGetAsmCodeAddr(m_AddrStart, m_AddrEnd, pCodeList, pInstList, pSymList, pAddrList);
	}

	// Do nothing if error
	if ( bOK ) {
		// Add to list
		m_pAsmList->AddTail(new CAssemblyData(nLine));
		CObList* pList = ((CAssemblyData*)m_pAsmList->GetTail())->GetList();
		
		// Reset ASM row & column
		m_nMaxAsmRow = 0;
		m_nMaxAsmCol = 0;
	
		// Add to the tail node
		for ( int i(0); i < pCodeList->GetCount(); i++ ) {
			// Label
			POSITION pos = pSymList->FindIndex(i);
			CString strLabel = pSymList->GetAt(pos);
			if ( !strLabel.IsEmpty() ) {
				strLabel += ':';
			}
	
			// Address
			pos = pAddrList->FindIndex(i);
			CString strAddr = pAddrList->GetAt(pos);
			
			// Object
			pos = pCodeList->FindIndex(i);
			CString strObj = pCodeList->GetAt(pos);
			int nLen = strObj.GetLength();
			ASSERT(nLen <= ::SrcGetMaxByte() * 2);
			for ( int j(0); j < ::SrcGetMaxByte() * 2-nLen; j++ ) {
				strObj += ' ';
			}
			ASSERT(strObj.GetLength() == ::SrcGetMaxByte() * 2);
	
			// Instruction
			pos = pInstList->FindIndex(i);
			CString strInst = pInstList->GetAt(pos);
							
			// Add to list
			pList->AddTail(new CAssemblyCode(strLabel, strAddr, strObj, strInst));
			
			// Next row & max column
			if ( !strLabel.IsEmpty() ) {
				m_nMaxAsmRow++;
			}
			m_nMaxAsmRow++;
			m_nMaxAsmCol = max(m_nMaxAsmCol, (strObj+strInst).GetLength()+int(sizeof(::szSpace)));
		}
	}

	// Delete object
	pCodeList->RemoveAll();
	delete pCodeList;
	
	pInstList->RemoveAll();
	delete pInstList;

	pSymList->RemoveAll();
	delete pSymList;

	pAddrList->RemoveAll();
	delete pAddrList;
}

/*
void CSourceDocument::GetSrcCode()
{
	// File is exist?
    CFileStatus status;
	if ( !CFile::GetStatus(m_strFilePath, status) ) {
		::SrcDisplayErrorMessage(::errFile);
		return;
	}

	// Open the specific file
	UINT nOpenFlag = CFile::modeRead | CFile::typeText;
	CStdioFile* fp = new CStdioFile(m_strFilePath, nOpenFlag);
	char* pszLine = new char[512+1];
	m_nMaxSrcRow = 0;
	m_nMaxSrcCol = 0;

	// Read source codes
	while ( fp->ReadString(pszLine, 512) ) {
		// Filter control keycode
        pszLine[512] = 0;
		CString strLine("");
        char* p = pszLine;
        while ( *p && strLine.GetLength() < 512-1 ) {
			if ( *p >= 0x20 && *p <= 0x7F ) {
				strLine += *p;
			}
			else if ( '\t' == *p && strLine.GetLength() < 512-5 ) {
				strLine += "    ";
			}
			else {
				strLine += ' ';
			}
			p++;
		}

		// Next row & longest column
        m_nMaxSrcRow++;
		m_nMaxSrcCol = max(m_nMaxSrcCol, strLine.GetLength());

		// Add line to list
		m_pSrcList->AddTail(new CSourceData(strLine));

		// Parse line to get the sub-object
		ParseLine(strLine);
	}
	delete []pszLine;
	
	// Close the specific file
	fp->Close();
	delete fp;
}
*/

void CSourceDocument::GetSrcCode()
{
	// Open the specific file
    int hFile;
    if ( -1 == (hFile = _open(m_strFilePath, _O_TEXT|_O_RDONLY)) ) {
		::SrcDisplayErrorMessage(::errFile);
		return;
    }

	// Reset row & column
	m_nMaxSrcRow = 0;
	m_nMaxSrcCol = 0;

	// Read from file
	const unsigned int uMaxCount(0x7FFF);
	unsigned int uCount(0);
	CString strLine("");
	char* pszLine = new char[uMaxCount+1];

    while ( (uCount = _read(hFile, pszLine, uMaxCount)) > 0 ) {
    	// Process line buffer
        pszLine[uCount] = 0;
        char* p = pszLine;

        while ( *p && strLine.GetLength() < 512-1 ) {
        	if ( '\n' == *p ) {
        		// Trim tail space to one
        		strLine.TrimRight();
        		if ( strLine.IsEmpty() ) {
        			strLine += ' ';
        		}

				// Next row & longest column
		        m_nMaxSrcRow++;
				m_nMaxSrcCol = max(m_nMaxSrcCol, strLine.GetLength());
		
				// Add line to list
				m_pSrcList->AddTail(new CSourceData(strLine));
		
				// Parse line to get the sub-object
				ParseLine(strLine);
				strLine = "";
			}
			else if ( *p >= 0x20 && *p <= 0x7F ) {
				strLine += *p;
			}
			else if ( '\t' == *p && strLine.GetLength() < 512-5 ) {
				strLine += ::szTab;
			}
			else {
				strLine += ' ';
			}
			p++;
		}
    }
	delete []pszLine;

	// Close file
    if ( -1 == uCount ) {
		::SrcDisplayErrorMessage(::errFile);
    }
    _close(hFile);
}

void CSourceDocument::ParseLine(const CString& strLine)
{
	ASSERT(!m_pSrcList->IsEmpty());

	// Initial
	char* pszLine = (char*)((const char*)strLine);
	const int nLen = int(strlen(pszLine));
	int nStart(-1), nEnd(0);
	CString strToken("");
	BOOL bInToken(FALSE);
	CObList* pList = ((CSourceData*)m_pSrcList->GetTail())->GetList();

	// Parse comment
	if ( m_bInComment ) {
		BOOL bMatch(FALSE), bEnd(FALSE);
		for ( int i(0); i < nLen; i++ ) {
			if ( '*' == pszLine[i] ) {
				bMatch = TRUE;
			}
			else if ( '/' == pszLine[i] && bMatch ) {
				pList->AddTail(new CSourceAttr(0, i, m_clrComment));
				nEnd = i + 1;
				nStart = nEnd;
				bEnd = TRUE;
				m_bInComment = FALSE;
				strToken = "";
				break;
			}
			else {
				bMatch = FALSE;
			}
		}
		if ( !bEnd ) {
			pList->AddTail(new CSourceAttr(0, nLen-1, m_clrComment));
			return;
		}
	}

	// Parse one line
	while ( nEnd < nLen ) {
		if ( IsDelimiter(pszLine[nEnd]) ) {
			if ( bInToken ) {
				// Add to the list
				COLORREF clr = IsKeyword(strToken) ? m_clrKeyword : m_clrCode;
				pList->AddTail(new CSourceAttr(nStart, nEnd-1, clr));
				strToken = "";
				bInToken = FALSE;
				nStart = nEnd;
			}
			else if ( -1 == nStart ) {
				nStart = 0;
			}
			strToken += pszLine[nEnd];
			
			// Priority: comment, string, char
			if ( nLen > nEnd + 1 ) {
				if ( '/' == pszLine[nEnd] && '/' == pszLine[nEnd+1] ) {
					if ( nStart < nEnd ) {
						pList->AddTail(new CSourceAttr(nStart, nEnd-1, m_clrCode));
					}
					pList->AddTail(new CSourceAttr(nEnd, nLen-1, m_clrComment));
					return;
				}
				else if ( '/' == pszLine[nEnd] && '*' == pszLine[nEnd+1] ) {
					BOOL bMatch(FALSE), bEnd(FALSE);
					for ( int i(nEnd+2); i < nLen; i++ ) {
						if ( '*' == pszLine[i] ) {
							bMatch = TRUE;
						}
						else if ( '/' == pszLine[i] && bMatch ) {
							if ( nStart < nEnd ) {
								pList->AddTail(new CSourceAttr(nStart, nEnd-1, m_clrCode));
							}
							pList->AddTail(new CSourceAttr(nEnd, i, m_clrComment));
							nEnd = i;
							nStart = nEnd;
							strToken = "";
							bEnd = TRUE;
							break;
						}
						else {
							bMatch = FALSE;
						}
					}
					if ( !bEnd ) {
						m_bInComment = TRUE;
						if ( nStart < nEnd ) {
							pList->AddTail(new CSourceAttr(nStart, nEnd-1, m_clrCode));
						}
						pList->AddTail(new CSourceAttr(nEnd, nLen-1, m_clrComment));
						return;
					}
				}
				else if ( '\"' == pszLine[nEnd] ) {
					BOOL bMatch(FALSE);
					for ( int i(nEnd+1); i < nLen; i++ ) {
						if ( '\"' == pszLine[i] ) {
							pList->AddTail(new CSourceAttr(nStart, i, m_clrCode));
							nEnd = i;
							nStart = nEnd;
							strToken = "";
							bMatch = TRUE;
							break;
						}
					}
					if ( !bMatch ) {
						pList->AddTail(new CSourceAttr(nStart, nLen-1, m_clrCode));
                        return;
					}
				}
				else if ( '\'' == pszLine[nEnd] ) {
					BOOL bMatch(FALSE);
					for ( int i(nEnd+1); i < nLen; i++ ) {
						if ( '\'' == pszLine[i] ) {
							pList->AddTail(new CSourceAttr(nStart, i, m_clrCode));
							nEnd = i;
							nStart = nEnd;
							strToken = "";
							bMatch = TRUE;
							break;
						}
					}
					if ( !bMatch ) {
						pList->AddTail(new CSourceAttr(nStart, nLen-1, m_clrCode));
						return;
					}
				}
			}
		}
		else {
			// Is C/C++ keyword & code
			if ( !bInToken ) {
				if ( !strToken.IsEmpty() ) {
					// Add to list
					pList->AddTail(new CSourceAttr(nStart, nEnd-1, m_clrCode));
					strToken = "";
				}
				bInToken = TRUE;
				nStart = nEnd;
			}
			strToken += pszLine[nEnd];
		}

		// Point to the next position
		nEnd++;
	}
	
	// Process the balance
	if ( !strToken.IsEmpty() ) {
		COLORREF clr = IsKeyword(strToken) ? m_clrKeyword : m_clrCode;
		pList->AddTail(new CSourceAttr(nStart, nEnd-1, clr));
	}
}

void CSourceDocument::GetMixCode()
{
	// Get source code
	if ( getMix == m_nAction ) {
		GetSrcCode();
	}

	// Get assembly code
	int nMaxAsmRow(0), nMaxAsmCol(0);
	for ( int i(1); i <= m_nMaxSrcRow; i++ ) {
		if ( ::SrcGetLineRange(i, m_AddrStart, m_AddrEnd) ) {
			GetAsmCode(i);
			nMaxAsmRow += m_nMaxAsmRow;
			nMaxAsmCol = max(nMaxAsmCol, m_nMaxAsmCol);
		}
		else {
			m_pAsmList->AddTail(new CAssemblyData(-1));
		}
	}
	
	// Summary assembly row
	m_nMaxAsmRow = nMaxAsmRow;
	m_nMaxAsmCol = nMaxAsmCol;
}

BOOL CSourceDocument::IsDelimiter(const char ch) const
{
	// Is C/C++ delimiter
	if ( ch >= 'a' && ch <= 'z' ||
		 ch >= 'A' && ch <= 'Z' ||
		 ch >= '0' && ch <= '9' ||
		 ch == '_' || ch == '#' ) {
		return FALSE;
	}
	return TRUE;
}

BOOL CSourceDocument::IsKeyword(const CString& strToken) const
{
	// Binary search
	int nHead(0), nTail(sizeof(::szKeyword)/sizeof(char*)-1), nMid;
	do {
		nMid = (nHead + nTail) / 2;
		if ( strToken == ::szKeyword[nMid] ) {
			return TRUE;
		}
		else if ( strToken < ::szKeyword[nMid] ) {
			nTail = nMid - 1;
		}
		else {
			nHead = nMid + 1;
		}
	} while ( nHead <= nTail );

	return FALSE;
}

void CSourceDocument::SaveListFile(const CString& strFile) const
{
	// Create a text file to log list file
	TRY
	{
		CStdioFile* fp = new CStdioFile(strFile, CFile::modeCreate | CFile::modeWrite | CFile::typeText);

		switch ( m_nMode ) {
		case ::modAsm:
			SaveAsmFile(fp);
			break;
		case ::modMix:
			SaveMixedFile(fp);
			break;
		case ::modSrc:
			SaveSourceFile(fp);
			break;
		default:
			ASSERT(FALSE);
			break;
		}
		
		fp->Close();
		delete fp;
	}
	CATCH(CFileException, e)
	{
		#ifdef _DEBUG
			afxDump << "File could not be opened " << e->m_cause << "\n";
		#endif

		::SrcDisplayErrorMessage(::errFile);
	}
	END_CATCH
}

void CSourceDocument::SaveAsmFile(CStdioFile* fp) const
{
	// Get list
	if ( m_pAsmList->IsEmpty() ) {
		return;
	}
	CObList* pAsmList = ((CAssemblyData*)(m_pAsmList->GetHead()))->GetList();
	
	// Write each node
	POSITION pos = pAsmList->GetHeadPosition();
	while ( pos ) {
		// Get object
		CAssemblyCode* pObj = (CAssemblyCode*)pAsmList->GetNext(pos);
		CString strLabel(pObj->GetLabel());
		CString strAddr(pObj->GetAddr());
		CString strObj(pObj->GetObj());
		CString strInst(pObj->GetInst());

		// Show label if exist
		if ( !strLabel.IsEmpty() ) {
			CString str(::szLabel);
			strLabel = str + strLabel + ::szLF;
			fp->WriteString(strLabel);
		}

		// Show Address, Object & instruction
		CString strLine = strAddr + ::szSpace + strObj + ::szSpace + strInst + ::szLF;
		fp->WriteString(strLine);
	}
}

void CSourceDocument::SaveMixedFile(CStdioFile* fp) const
{
	// Get list
	if ( m_pSrcList->IsEmpty() || m_pAsmList->IsEmpty() ) {
		return;
	}
	
	// Show source code
	POSITION posSrc = m_pSrcList->GetHeadPosition();
	int nLineNum(0);
	while ( posSrc ) {
		// Get Source data object
		CSourceData* pSrcObj = (CSourceData*)m_pSrcList->GetNext(posSrc);
		CString strNum("");
		strNum.Format(::szLineFormat, nLineNum+1);
		CString strLine = strNum + pSrcObj->GetLine() + ::szLF;
		fp->WriteString(strLine);
		
		// Get Assembly code object
		POSITION posAsm = m_pAsmList->FindIndex(nLineNum);
		++nLineNum;
		if ( posAsm && -1 != ((CAssemblyData*)m_pAsmList->GetAt(posAsm))->GetLine() ) {
			CObList* pAsmList = ((CAssemblyData*)m_pAsmList->GetAt(posAsm))->GetList();
			POSITION pos = pAsmList->GetHeadPosition();
			while ( pos ) {
				// Get object
				CAssemblyCode* pObj = (CAssemblyCode*)pAsmList->GetNext(pos);
				CString strLabel(pObj->GetLabel());
				CString strAddr(pObj->GetAddr());
				CString strObj(pObj->GetObj());
				CString strInst(pObj->GetInst());
		
				// Show label if exist
				if ( !strLabel.IsEmpty() ) {
					CString str(::szLabel);
					strLabel = str + strLabel + ::szLF;
					fp->WriteString(strLabel);
				}
		
				// Show Address, Object & instruction
				CString strLine(::szMix);
				strLine += strAddr + ::szSpace + strObj + ::szSpace + strInst + ::szLF;
				fp->WriteString(strLine);
			}
		}
	}
}

void CSourceDocument::SaveSourceFile(CStdioFile* fp) const
{
	// Get list
	if ( m_pSrcList->IsEmpty() ) {
		return;
	}

	// Show source code
	int nLineNum(0);
	POSITION pos = m_pSrcList->GetHeadPosition();

	while ( pos ) {
		// Get Source data object
		CSourceData* pObj = (CSourceData*)m_pSrcList->GetNext(pos);

		CString strNum("");
		strNum.Format(::szLineFormat, nLineNum+1);

		CString strLine = strNum + pObj->GetLine() + ::szLF;

		// Show line number & code
		fp->WriteString(strLine);
		
		++nLineNum;
	}
}

/////////////////////////////////////////////////////////////////////////////
// CSourceDocument diagnostics

#ifdef _DEBUG
void CSourceDocument::AssertValid() const
{
	CDocument::AssertValid();
}

void CSourceDocument::Dump(CDumpContext& dc) const
{
	CDocument::Dump(dc);
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CSourceDocument serialization

void CSourceDocument::Serialize(CArchive& ar)
{
	if (ar.IsStoring())
	{
		// TODO: add storing code here
	}
	else
	{
		// TODO: add loading code here
	}
}

/////////////////////////////////////////////////////////////////////////////
// CSourceDocument commands

void CSourceDocument::OnFileSaveAs()
{
	// TODO: Add your command handler code here
	
	// Open Save As dialog
	HINSTANCE hInstEXE = AfxGetResourceHandle();
	AfxSetResourceHandle(::SrcGetDLLHandle());

	CString strFilter;
	strFilter.LoadString(IDS_SRC_FILTER);
	
	AfxSetResourceHandle(hInstEXE);

	CFileDialog dlg(FALSE, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, strFilter);

	static char BASED_CODE szTitle[] = "Save List File";
	dlg.m_ofn.lpstrTitle = szTitle;
	
	if ( IDOK == dlg.DoModal() ) {
		SaveListFile(dlg.GetPathName());
	}
}
