// zap.cpp : implementation of the CZap class
//
// Copyright (c) 1985-1995, Microsoft Corporation. All rights reserved.
//

#include "stdafx.h"
#include "customwz.h"
#include "sampleaw.h"
#include "zap.h"

#ifdef _PSEUDO_DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

// This helper (used in CZap::LoadFile) determines whether we should zap
//  a file into a "binary" template-- that is, a template to be copied using
//  CopyTemplate, rather than parsed via ProcessTemplate.
inline BOOL ShouldCopyVerbatim(LPCTSTR szFullName, LPCTSTR szExt)
{
	return (szExt != NULL &&
		// Icons and bitmaps should be copied not parsed
		(!_tcsicmp(szExt, _T(".ico")) || !_tcsicmp(szExt, _T(".bmp"))
			// miscellaneous binary files not to be parsed
			|| !_tcsicmp(szExt, _T(".dat"))
			// .rtf files other than afxdlg.rtf should be copied not parsed
			|| (!_tcsicmp(szExt, _T(".rtf")) && _tcsicmp(szFullName, _T("afxdlg.rtf")))));
}

// Given a filename, map it to memory, store its size in dwSize, and return
//  the mapped view.
LPCTSTR CZap::LoadFile(LPCTSTR lpszResource, DWORD& dwSize)
{
	ASSERT(m_hFile == NULL);
	ASSERT(m_hMapping == NULL);
	ASSERT(m_strFile.IsEmpty());

	// First open the file
	m_hFile = CreateFile(
		lpszResource,			// File name
		GENERIC_READ,			// Read-only
		FILE_SHARE_READ
			| FILE_SHARE_WRITE, // Allow sharing-- we're only doing a quick scan
		NULL,					// No security attributes
		OPEN_EXISTING,			// Only open an existing file
		0,						// Ignore file attributes
		NULL);					// Ignore hTemplateFile

	if (m_hFile == INVALID_HANDLE_VALUE)
	{
		m_hFile = NULL;
		ReportAndThrow(IDP_ZAP_CANT_OPEN_FILE, lpszResource);
	}

	// Get the file's size
	dwSize = GetFileSize(m_hFile, NULL);
	if (dwSize == 0xffffffff)
	{
		m_hFile = NULL;
		ReportAndThrow(IDP_ZAP_CANT_GET_SIZE, lpszResource);
	}

	// Create a mapping object from the file
	m_hMapping = CreateFileMapping(
		m_hFile,				// Handle we got from CreateFile
		NULL,					// No security attributes
		PAGE_READONLY,			// read-only
		0, 0,					// Max size = current size of file
		NULL);					// Don't name the mapping object

	// Map the file to memory
	m_lpszFile = (LPCTSTR) MapViewOfFile(
		m_hMapping,				// Handle we got from CreateFileMapping
		FILE_MAP_READ,			// read-only (didn't I mention that before?)
		0, 0,					// Offset into file = beginning of file
		0);						// Map entire file

	_tsplitpath(lpszResource, NULL, m_strSubdir.GetBuffer(_MAX_DIR), m_strBaseFileName.GetBuffer(_MAX_FNAME),
		m_strFileExt.GetBuffer(_MAX_EXT));
	m_strSubdir.ReleaseBuffer();
	m_strSubdir.MakeLower();
	m_strBaseFileName.ReleaseBuffer();
	m_strFileExt.ReleaseBuffer();
	m_strBaseFileName += m_strFileExt;
	LPCTSTR szBeginRootDir = _tcsstr(m_strSubdir, m_strRootDir);
	ASSERT(szBeginRootDir != NULL);
	m_strSubdir = szBeginRootDir + m_strRootDir.GetLength();
	
	m_bBinary = ShouldCopyVerbatim(m_strBaseFileName, m_strFileExt);

	// Remember name of file for potential future error messages
	m_strFile = lpszResource;

	return m_lpszFile;
}

// Called after a file has been zapped to deallocate resources used for zapping.
void CZap::UnloadFile()
{
	ASSERT(m_hFile != NULL);
	ASSERT(m_hMapping != NULL);
	ASSERT(m_lpszFile != NULL);
	ASSERT(!m_strFile.IsEmpty());

	VERIFY(UnmapViewOfFile((LPVOID) m_lpszFile));
	VERIFY(CloseHandle(m_hMapping));
	VERIFY(CloseHandle(m_hFile));
	m_hFile = NULL;
	m_hMapping = NULL;
	m_strFile.Empty();
}

// This helper (used in CZap::AddToNewProjInf) determines whether the custom
//  AppWizard we're generating should always add this file to the project
//  makefile.
inline BOOL ShouldAddFileToProject(LPCTSTR szFullName, LPCTSTR szExt)
{
	return (szExt != NULL &&
		(!_tcsicmp(szExt, _T(".cpp")) || !_tcsicmp(szExt, _T(".rc"))
			|| !_tcsicmp(szExt, _T(".odl")) || !_tcsicmp(szExt, _T(".r"))
			|| !_tcsicmp(szExt, _T(".c")) || !_tcsicmp(szFullName, _T("readme.txt"))));
}

// This is defined in zapdlg.cpp.  We've stored the non-colliding names of the templates
//  we're generating in here.  This information is used in CZap::AddToNewProjInf() below.
extern CStringList g_TemplateNames;

// This is called after a file is zapped.  At that point, it has been determined
//  whether the resulting template should be considered a "binary" template, and
//  so we're prepared to add the entry to the generated custom AppWizard's
//  newproj.inf (which is what this does).
void CZap::AddToNewProjInf()
{
	ASSERT(m_posTplName != NULL);

	// Determine whether we should set the '+' flag
	CString strPlus;
	if (ShouldAddFileToProject(m_strBaseFileName, m_strFileExt))
		strPlus = _T("+");

	// Determine whether we should set the '=' flag
	CString strEquals;
	strEquals = m_bBinary ? _T("=") : _T("");

	CString strMacroizedFile;
	ZapFileName(m_strBaseFileName, strMacroizedFile);
	CString strTemplateName = g_TemplateNames.GetNext(m_posTplName);

	// Finally, the string used in this custom AppWizard's newnwprj.inf
	//  (i.e., the generated custom AppWizard's newproj.inf).
	m_strGeneratedNewProjInfo += strEquals + strPlus + strTemplateName // Template we just zapped
		+ _T('\t') 
		+ m_strSubdir + strMacroizedFile + _T('\n');		// What that template generates
}


// This is the meat of our override of ProcessTemplate.  It zaps a file line
//  by line using ZapLine.
void CZap::ZapFile(LPCTSTR lpszInput, DWORD dwSize, OutputStream* pOutput)
{
	ASSERT(m_hFile != NULL);
	ASSERT(m_hMapping != NULL);
	if (m_posTplName == NULL)
		m_posTplName = g_TemplateNames.GetHeadPosition();

	if (m_bBinary)
	{
		sampleaw.CopyTemplate(lpszInput, dwSize, pOutput);
		AddToNewProjInf();
		return;
	}

	if (dwSize == 0)
	{
		pOutput->WriteLine(_T(" "));
		AddToNewProjInf();
		return;
	}

	LPCTSTR lpszBeginLine = lpszInput;
	LPCTSTR lpszEndLine;

	// Loop line by line, until we go past the end
	while ((DWORD) (lpszBeginLine-lpszInput) < dwSize)
	{
		// Start from the beginning of a line, and find a carriage return
		lpszEndLine = _tcschr(lpszBeginLine, _T('\r'));

		// If there was no carriage return...
		if (lpszEndLine == NULL
			// ...or no newline after the carriage return
			|| *(lpszEndLine = _tcsinc(lpszEndLine)) != _T('\n'))
		{
			// Then this must be a binary file (or there was no trailing
			//  "\r\n" at the end of the file).  In either case, bail and
			//  write the rest of the file verbatim to the output; mark the
			//  file as binary.
			m_bBinary = TRUE;
			pOutput->WriteBlock(lpszBeginLine, dwSize - (DWORD)(lpszBeginLine-lpszInput));
			break;
		}

		// Here, we successfully found a "\r\n", and lpszEndLine is
		//  sitting at the '\n'.  So zap the line.
		CString strLine(lpszBeginLine, lpszEndLine-lpszBeginLine-1);
		ZapLine(strLine);
		pOutput->WriteLine(strLine);
		lpszBeginLine = _tcsinc(lpszEndLine);
	}
	AddToNewProjInf();
}

// Here are the brains of the operation.  zrdGeneral contains the strings we
//  search for, and what we replace them with.  ZapRawData is defined in
//  zap.h.
// The reason we have such a structure is that we can not simply "macroize"
//  every occurrence of the project name in its files.  For example, if the
//  project were named "View" or "while", and we blindly replaced all
//  occurrences of the project name with macros, the generated custom AppWizard
//  would be incapable of creating compilable projects.  Thus, we have to
//  search for the project name occurring in special contexts.
// In general, the format is:
//  	{ Pre, RootType, Post, {Replacement #0, Replacement #1}},
//  where Pre + RootType + Post is the string we search for, and Replacement #0
//  is what we usually replace it with-- the exception is when we're naming
//  one of the generated custom AppWizard's template files, in which case we use
//  Replacement #1.  (Replacement #1 can be NULL if we know that the particular
//  search string will never be replaced by the name of a template file in
//  the generated custom AppWizard.)
// See CZap::ZapLine and its references in CZap::ZapFile & CZap::ZapFileName,
//  to see how zrdGeneral is used.
static ZapRawData zrdGeneral[] =
{
	{_T(""), ZRD_ROOT, _T(".h"), {_T("$$root$$.h"), _T("root.h")}},
	{_T(""), ZRD_ROOT, _T(".cpp"), {_T("$$root$$.cpp"), _T("root.cpp")}},
	{_T(""), ZRD_ROOT, _T(".rc"), {_T("$$root$$.rc"), _T("root.rc")}},
	{_T(""), ZRD_ROOT_UPR, _T(".RC"), {_T("$$ROOT$$.RC"), NULL}},
	{_T(""), ZRD_ROOT, _T(".rc2"), {_T("$$root$$.rc2"), _T("root.rc2")}},
	{_T(""), ZRD_ROOT_UPR, _T(".RC2"), {_T("$$ROOT$$.RC2"), NULL}},
	{_T(""), ZRD_ROOT, _T(".ico"), {_T("$$root$$.ico"), _T("root.ico")}},
	{_T(""), ZRD_ROOT, _T(".dll"), {_T("$$root$$.dll"), NULL}},
	{_T(""), ZRD_ROOT_UPR, _T(".DLL"), {_T("$$ROOT$$.DLL"), NULL}},
	{_T(""), ZRD_ROOT, _T(".exe"), {_T("$$root$$.exe"), NULL}},
	{_T(""), ZRD_ROOT_UPR, _T(".EXE"), {_T("$$ROOT$$.EXE"), NULL}},
	{_T(""), ZRD_ROOT, _T(".mak"), {_T("$$root$$.mak"), _T("root.mak")}},
	{_T(""), ZRD_ROOT, _T(".hm"), {_T("$$root$$.hm"), _T("root.hm")}},
	{_T(""), ZRD_ROOT_UPR, _T(".HM"), {_T("$$ROOT$$.HM"), NULL}},
	{_T(""), ZRD_ROOT, _T(".hpj"), {_T("$$root$$.hpj"), _T("root.hpj")}},
	{_T(""), ZRD_ROOT_UPR, _T(".HPJ"), {_T("$$ROOT$$.HPJ"), NULL}},
	{_T(""), ZRD_ROOT, _T(".cnt"), {_T("$$root$$.cnt"), _T("root.cnt")}},
	{_T(""), ZRD_ROOT, _T(".clw"), {_T("$$root$$.clw"), _T("root.clw")}},
	{_T(""), ZRD_ROOT, _T(".reg"), {_T("$$root$$.reg"), _T("root.reg")}},
	{_T(""), ZRD_ROOT, _T(".def"), {_T("$$root$$.def"), _T("root.def")}},
	{_T(""), ZRD_ROOT, _T(".odl"), {_T("$$root$$.odl"), _T("root.odl")}},
	{_T(""), ZRD_ROOT, _T(".tlb"), {_T("$$root$$.tlb"), _T("root.tlb")}},
	{_T(""), ZRD_R_FILE, _T(".reg"), {_T("$$root$$Mac.reg"), _T("MacRoot.reg")}},
	{_T(""), ZRD_R_FILE, _T(".r"), {_T("$$root$$Mac.r"), _T("root.r")}},
	{_T(""), ZRD_ROOT, _T(".pch"), {_T("$$root$$.pch"), NULL}},
	{_T(""), ZRD_ROOT, _T("DLL"), {_T("$$safe_root$$DLL"), NULL}},
	{_T("library "), ZRD_CLASS, _T(""), {_T("library $$root$$"), NULL}},// (root.odl)
	{_T("IID_I"), ZRD_DOC, _T(""), {_T("IID_I$$Doc$$"), NULL}},
	{_T("IDD_"), ZRD_SAFE_ROOT, _T("_"), {_T("IDD_$$SAFE_ROOT$$_"), NULL}},
	{_T("C"), ZRD_CLASS, _T("App"), {_T("C$$Safe_root$$App"), NULL}},
	{_T("C"), ZRD_CLASS, _T("Doc"), {_T("C$$Safe_root$$Doc"), NULL}},
	{_T("C"), ZRD_CLASS, _T("View"), {_T("C$$Safe_root$$View"), NULL}},
	{_T("C"), ZRD_CLASS, _T("Set"), {_T("C$$Safe_root$$Set"), NULL}},
	{_T("C"), ZRD_CLASS, _T("CntrItem"), {_T("C$$Safe_root$$CntrItem"), NULL}},
	{_T("C"), ZRD_CLASS, _T("SrvrItem"), {_T("C$$Safe_root$$SrvrItem"), NULL}},
	{_T("C"), ZRD_CLASS, _T("Dlg"), {_T("C$$Safe_root$$Dlg"), NULL}},
	{_T(""), ZRD_FILEBASE, _T("Doc."), {_T("$$root$$Doc."), _T("Doc.")}},
	{_T(""), ZRD_FILEBASE, _T("View."), {_T("$$root$$View."), _T("View.")}},
	{_T(""), ZRD_FILEBASE, _T("Set."), {_T("$$root$$Set."), _T("RecSet.")}},
	{_T(""), ZRD_FILEBASE, _T("Dlg."), {_T("$$root$$Dlg."), _T("Dialog.")}},
	{_T("m_"), ZRD_ROOT, _T("Set"), {_T("m_$$safe_root$$Set"), NULL}},
	{_T("IDR_"), ZRD_DOC_UPR, _T("TYPE"), {_T("IDR_$$DOC$$TYPE"), NULL}},
	{_T("dispinterface I"), ZRD_DOC, _T(""), {_T("dispinterface I$$Doc$$"), NULL}},
	{_T("\\n"), ZRD_DOC, _T("\\n"), {_T("\\n$$Doc$$\\n"), NULL}},
	{_T("\""), ZRD_ROOT, _T("\""), {_T("\"$$root$$\""), NULL}},
	{_T("\""), ZRD_CLASS, _T("\""), {_T("\"$$Root$$\""), NULL}},
	{_T("= "), ZRD_ROOT, _T(""), {_T("= $$Root$$"), NULL}},
	{_T("= "), ZRD_DOC, _T(""), {_T("= $$Doc$$"), NULL}},
	{_T("\""), ZRD_ROOT_UPR, _T(" MFC Extension DLL"), {_T("\"$$ROOT$$ MFC Extension DLL"), NULL}},
	{_T("\""), ZRD_ROOT_UPR, _T(" MFC Application"), {_T("\"$$ROOT$$ MFC Application"), NULL}},
	{_T("\""), ZRD_ROOT_UPR, _T("\\0\""), {_T("\"$$ROOT$$\\0\""), NULL}},
	{_T("\""), ZRD_ROOT_UPR, _T(" Dynamic Link Library"), {_T("\"$$ROOT$$ Dynamic Link Library"), NULL}},
	{_T(""), ZRD_ROOT, _T(" application"), {_T("$$root$$ application"), NULL}},
	{_T(""), ZRD_ROOT, _T(" Application"), {_T("$$root$$ Application"), NULL}},
	{_T(""), ZRD_ROOT_UPR, _T(" application"), {_T("$$ROOT$$ application"), NULL}},
	{_T(""), ZRD_ROOT_UPR, _T(" Application"), {_T("$$ROOT$$ Application"), NULL}},
	{_T(""), ZRD_ROOT_UPR, _T(" DLL"), {_T("$$ROOT$$ DLL"), NULL}},
	{_T("\""), ZRD_ROOT, _T(" Version 1.0"), {_T("\"$$Root$$ Version 1.0"), NULL}},
	{_T("\"&About "), ZRD_ROOT, _T("..."), {_T("\"&About $$Root$$..."), NULL}},
	{_T("\"About "), ZRD_ROOT, _T("\""), {_T("\"About $$Root$$\""), NULL}},
	{_T("Make help for Project "), ZRD_ROOT_UPR, _T(""), {_T("Make help for Project $$ROOT$$"), NULL}},
	{_T("// "), ZRD_ROOT, _T(" bundle resource ID"), {_T("// $$root$$ bundle resource ID"), NULL}},
	{_T("// "), ZRD_ROOT, _T(" document"), {_T("// $$root$$ document"), NULL}},
	{_T("// "), ZRD_ROOT, _T(" signature"), {_T("// $$root$$ signature"), NULL}},
	{_T(""), ZRD_DOC, _T(" Document"), {_T("$$Doc$$ Document"), NULL}},
	{_T("MICROSOFT FOUNDATION CLASS LIBRARY : "), ZRD_ROOT, _T(""),
		{_T("MICROSOFT FOUNDATION CLASS LIBRARY : $$root$$"), NULL}},
	{_T(""), ZRD_CLASS, _T(".Document"), {_T("$$Doc$$.Document"), NULL}},
	{_T("\\n"), ZRD_MAC_TYPE, _T(" Files\""), {_T("\\n $$MAC_TYPE$$ Files\""), NULL}},
	{_T("'"), ZRD_MAC_TYPE, _T("'"), {_T("'$$MAC_TYPE$$'"), NULL}},
	{_T("\\n"), ZRD_MAC_TYPE, _T("\\n"), {_T("\\n$$MAC_TYPE$$\\n"), NULL}},
};

// Called when the zap dialog is dismissed.  It initializes the matches
//  array (m_pStrMatches) for the particular project's root name.
void CZap::SetRoot(LPCTSTR lpszDir, LPCTSTR lpszRoot)
{
	m_strRoot = lpszRoot;

	m_strRootDir = lpszDir;
	m_strRootDir.MakeLower();

	m_strGeneratedNewProjInfo.Empty();
	m_posTplName = NULL;

	InitMatches();
}

// Changes each space in str to an underscore
void SpaceToUnderscore(CString& str)
{
	LPCTSTR pch = str;
	for (int i=0; *pch != _T('\0'); i += _tclen(pch), pch = _tcsinc(pch))
	{
		if (str[i] == _T(' '))
			str.SetAt(i, _T('_'));
	}
}

// This fills the array m_pStrMatches with an entry for each search string
//  from zrdGeneral.  Each entry is the concatenation of the three components
//  of the search string (i.e., Pre, RootType, and Post).
// Example (loosely expressed):
//	m_pStrMatches[0] = "" + ZRD_ROOT + ".h"
//  m_pStrMatches[1] = "" + ZRD_ROOT_UPR ".H"
//  etc.
void CZap::InitMatches()
{
	// First, translate ZRD_ROOT, ZRD_ROOT_UPR, etc. into strings based
	//  on the name of the "zappee" (i.e., project off of which we're
	//  basing the custom AppWizard).

	CString strRootTypes[NUM_ROOT_TYPES];
	strRootTypes[ZRD_ROOT] = m_strRoot;

	strRootTypes[ZRD_ROOT_UPR] = strRootTypes[ZRD_ROOT];
	strRootTypes[ZRD_ROOT_UPR].MakeUpper();

	strRootTypes[ZRD_SAFE_ROOT] = m_strRoot;
	strRootTypes[ZRD_SAFE_ROOT].MakeUpper();

	strRootTypes[ZRD_CLASS] = m_strRoot;
	strRootTypes[ZRD_CLASS].SetAt(0, _totupper(strRootTypes[ZRD_CLASS][0]));

	strRootTypes[ZRD_FILEBASE] = m_strRoot;

	strRootTypes[ZRD_DOC] = m_strRoot.Left(6);
	strRootTypes[ZRD_DOC].SetAt(0, _totupper(strRootTypes[ZRD_DOC][0]));

	strRootTypes[ZRD_DOC_UPR] = strRootTypes[ZRD_DOC];
	strRootTypes[ZRD_DOC_UPR].MakeUpper();

	strRootTypes[ZRD_MAC_TYPE] = m_strRoot.Left(4);
	int nLen = strRootTypes[ZRD_MAC_TYPE].GetLength();
	if (nLen < 4)
	{
		CString strPad(_T(' '), 4 - nLen);
		strRootTypes[ZRD_MAC_TYPE] += strPad;
	}
	strRootTypes[ZRD_MAC_TYPE].MakeUpper();

	strRootTypes[ZRD_R_FILE] = m_strRoot + _T("Mac");

	strRootTypes[ZRD_R_FILE_UPR] = strRootTypes[ZRD_R_FILE];
	strRootTypes[ZRD_R_FILE_UPR].MakeUpper();

	// Now, using these values, traverse zrdGeneral and concatenate the three
	//  components of each search string.  Store the value in the
	//  corresponding entry of the array m_pStrMatches.

	FreeMatches();
	int nSize = sizeof(zrdGeneral)/sizeof(ZapRawData);
	m_pStrMatches = new CString[nSize];

	for (int i=0; i < nSize; i++)
	{
		m_pStrMatches[i] =
			zrdGeneral[i].lpszPre +
			strRootTypes[zrdGeneral[i].nRootType] +
			zrdGeneral[i].lpszPost;
	}
}

void CZap::FreeMatches()
{
	if (m_pStrMatches != NULL)
		delete [sizeof(zrdGeneral)/sizeof(ZapRawData)] m_pStrMatches;
	m_pStrMatches = NULL;
}

CZap::~CZap()
{
	ASSERT(m_posTplName == NULL);
	FreeMatches();
}


void CZap::ZapLine(CString& strLine, int iReplace /* = 0 */)
{
	ASSERT(m_pStrMatches != NULL);
	ASSERT(0 <= iReplace && iReplace <= 1);

	for (int i=0; i < sizeof(zrdGeneral)/sizeof(ZapRawData); i++)
	{
		int nStart = strLine.Find(m_pStrMatches[i]);
		if (nStart != -1)
		{
			ASSERT(zrdGeneral[i].lpszReplace[iReplace] != NULL);
			strLine = strLine.Left(nStart)				// Region to left of match
				+ zrdGeneral[i].lpszReplace[iReplace]	// String to replace match with
				+ (((LPCTSTR) strLine) + nStart + m_pStrMatches[i].GetLength());
					// String to right of match
			i--;				// In case there are more occurrences of this match
		}
	}
}

void CZap::ZapFileName(LPCTSTR lpszFileName, CString& rStrZappedFile, int iReplace /* = 0 */)
{
	rStrZappedFile = lpszFileName;
	ZapLine(rStrZappedFile, iReplace);
}


