/*==============================================================================
Microsoft Denali

Microsoft Confidential.
Copyright 1996 Microsoft Corporation. All Rights Reserved.

File:           template.h
Maintained by:  DaveK
Component:      include file for Denali Compiled Template object
==============================================================================*/

#ifndef _TEMPLATE_H
#define _TEMPLATE_H

#include "vector.h"
#include "LinkHash.h"
#include "Compcol.h"
#include "util.h"
#include "activdbg.h"
#include "ConnPt.h"
#include "DblLink.h"
#include "aspdmon.h"
#include "ie449.h"
#include "memcls.h"

#define PER_TEMPLATE_REFLOG 0

/*  NOTE we ensure that C_COUNTS_IN_HEADER is a multiple of 4 because the offsets which follow
    the counts in template header are dword-aligned.  It is easiest (and fastest at runtime)
    to make sure those offsets start on a dword-alignment point; thus no runtime alignment calc
    is needed in GetAddress().
*/
#define C_REAL_COUNTS_IN_HEADER     3       // actual number of count fields in template header
#define C_COUNTS_IN_HEADER          (C_REAL_COUNTS_IN_HEADER/4 + 1) * 4     // allocated number of count fields in template header

#define C_OFFOFFS_IN_HEADER         4       // number of 'ptr-to-ptr' fields in template header
#define CB_TEMPLATE_DEFAULT         2500    // default memory allocation for new template
#define C_TARGET_LINES_DEFAULT      50      // default count of target script lines per engine

#define C_TEMPLATES_PER_INCFILE_DEFAULT 4   // default count of templates per inc-file

#define         SZ_NULL         "\0"
#define         WSTR_NULL      L"\0"
#define         SZ_NEWLINE      "\r\n"
#define         WSZ_NEWLINE      L"\r\n"
const unsigned  CB_NEWLINE      = strlen(SZ_NEWLINE);

const   LPSTR   g_szWriteBlockOpen  = "Response.WriteBlock(";
const   LPSTR   g_szWriteBlockClose = ")";
const   LPSTR   g_szWriteOpen       = "Response.Write(";
const   LPSTR   g_szWriteClose      = ")";

// defaults for buffering interim compile results
#define     C_SCRIPTENGINESDEFAULT  2       // default count of script engines
#define     C_SCRIPTSEGMENTSDEFAULT 20      // default count of script segments
#define     C_OBJECTINFOS_DEFAULT   10      // default count of object-infos
#define     C_HTMLSEGMENTSDEFAULT   20      // default count of HTML segments
#define     C_INCLUDELINESDEFAULT   5       // default count of include lines
#define     CB_TOKENS_DEFAULT       400     // default byte count for tokens

#define     CH_ATTRIBUTE_SEPARATOR  '='     // separator for attribute-value pair
#define     CH_SINGLE_QUOTE         '\''    // single-quote character
#define     CH_DOUBLE_QUOTE         '"'     // double-quote character
#define     CH_ESCAPE               '\\'    // escape character - tells us to ignore following token

// ACLs: the following code should in future be shared with IIS (see creatfil.cxx in IIS project)
// NOTE we want SECURITY_DESC_DEFAULT_SIZE to be relatively small, since it affects template memory reqt dramatically
#define     SECURITY_DESC_GRANULARITY   128        // 'chunk' size for re-sizing file security descriptor
#define     SECURITY_DESC_DEFAULT_SIZE  256        // initial default size of file security descriptor
#define     SIZE_PRIVILEGE_SET          128        // size of privilege set

// macros
// use outside of CTokenList class
#define SZ_TOKEN(i)                 (*gm_pTokenList).m_bufTokens.PszLocal(i)
#define CCH_TOKEN(i)                (*gm_pTokenList)[i]->m_cb
#define _TOKEN                      CTemplate::CTokenList::TOKEN
// use within CTokenList class
#define CCH_TOKEN_X(i)              (*this)[i]->m_cb
#define BR_TOKEN_X(i)               *((*this)[i])

// Use to specify which source file name you want (pathInfo or pathTranslated)
#ifndef _SRCPATHTYPE_DEFINED
#define _SRCPATHTYPE_DEFINED
enum SOURCEPATHTYPE
    {
    SOURCEPATHTYPE_VIRTUAL = 0,
    SOURCEPATHTYPE_PHYSICAL = 1
    };
#endif

// CTemplate error codes
#define E_COULDNT_OPEN_SOURCE_FILE              0x8000D001L
#define E_SOURCE_FILE_IS_EMPTY                  0x8000D002L
#define E_TEMPLATE_COMPILE_FAILED               0x8000D003L
#define E_USER_LACKS_PERMISSIONS                0x8000D004L
#define E_TEMPLATE_COMPILE_FAILED_DONT_CACHE    0x8000D005L
#define E_TEMPLATE_MAGIC_FAILURE                0x8000D006L

inline BOOL FIsPreprocessorError(HRESULT hr)
    {
    return
        (
        hr == E_SOURCE_FILE_IS_EMPTY                ||
        hr == E_TEMPLATE_COMPILE_FAILED             ||
        hr == E_TEMPLATE_COMPILE_FAILED_DONT_CACHE  ||
        hr == E_TEMPLATE_MAGIC_FAILURE
        );
    }

//Can not use same index as CErrorInfo anymore.
//Index for lastErrorInfo in Template
#define ILE_szFileName      0
#define ILE_szLineNum       1
#define ILE_szEngine        2
#define ILE_szErrorCode     3
#define ILE_szShortDes      4
#define ILE_szLongDes       5
#define ILE_MAX             6

// forward references
class CTemplate;
class CTemplateCacheManager;
class CHitObj;
class CTokenList;
class CIncFile;
typedef CLSID PROGLANG_ID;  // NOTE also defined in scrptmgr.h; we define here to avoid include file circularity

/*  ============================================================================
    Class:      CByteRange
    Synopsis:   A range of bytes
    NOTE fLocal member is only used if the byte range is stored in a CBuffer

    NOTE 2
        m_pfilemap is really a pointer to a CFileMap - however, it's impossible
        to declare that type here because the CFileMap struct is nested inside
        CTemplate, and C++ won't let you forward declare nested classes.  Since
        the CTemplate definition depends on CByteRange, properly declaring the
        type of "m_pfilemap" is impossible.
*/
class CByteRange
{
public:
    BYTE*   m_pb;               // ptr to bytes
    ULONG   m_cb;               // count of bytes
    ULONG   m_fLocal:1;         // whether bytes are stored in buffer (TRUE) or elsewhere (FALSE)
    UINT    m_idSequence:31;    // byte range's sequence id
    void*   m_pfilemap;         // file the byte range comes from

            CByteRange(): m_pb(NULL), m_cb(0), m_fLocal(FALSE), m_idSequence(0), m_pfilemap(NULL) {}
            CByteRange(BYTE* pb, ULONG cb): m_fLocal(FALSE), m_idSequence(0) {m_pb = pb; m_cb = cb;}
    BOOLB   IsNull() { return((m_pb == NULL) || (m_cb == 0)) ; }
    void    Nullify() { m_pb = NULL; m_cb = 0; }
    void    operator=(const CByteRange& br)
                { m_pb = br.m_pb; m_cb = br.m_cb; m_fLocal = br.m_fLocal; m_idSequence = br.m_idSequence; }
    BOOLB   FMatchesSz(LPCSTR psz);
    void    Advance(UINT i);
    BYTE*   PbString(LPSTR psz, LONG lCodePage);
    BYTE*   PbOneOfAspOpenerStringTokens(LPSTR rgszTokens[], UINT rgcchTokens[],
                                         UINT nTokens, UINT *pidToken);
    BOOLB   FEarlierInSourceThan(CByteRange& br);
};

/*  ============================================================================
    Enum type:  TEMPLATE_COMPONENT
    Synopsis:   A component of a template, e.g. script block, html block, etc.
*/
enum TEMPLATE_COMPONENT
{
    // NOTE enum values and order are tightly coupled with template layout order
    // DO NOT CHANGE
    tcompScriptEngine = 0,
    tcompScriptBlock,
    tcompObjectInfo,
    tcompHTMLBlock,
};

/*  ****************************************************************************
    Class:      CTemplateConnPt
    Synopsis:   Connection point for IDebugDocumentTextEvents
*/
class CTemplateConnPt : public CConnectionPoint
{
public:
    // ctor
    CTemplateConnPt(IConnectionPointContainer *pContainer, const GUID &uidConnPt)
        : CConnectionPoint(pContainer, uidConnPt) {}

    // IUnknown methods
    STDMETHOD(QueryInterface)(const GUID &, void **);
    STDMETHOD_(ULONG, AddRef)();
    STDMETHOD_(ULONG, Release)();
};

/*	****************************************************************************
	Class:		CTemplateKey
	Synopsis:	Packaged data to locate template in hash table
	               (instance ID, and template name)
*/	
#define MATCH_ALL_INSTANCE_IDS 0xFFFBAD1D	// unlikely instance ID.  sort of spells "BAD ID"
struct CTemplateKey
	{
	const TCHAR *	szPathTranslated;
	DWORD			dwInstanceID;

	CTemplateKey(const TCHAR *_szPathTranslated = NULL, UINT _dwInstanceID = MATCH_ALL_INSTANCE_IDS)
		: szPathTranslated(_szPathTranslated),
		  dwInstanceID(_dwInstanceID)  {}
	};


/*  ****************************************************************************
    Class:      CTemplate
    Synopsis:   A Denali compiled template.

    NOTE: CTemplate's primary client is CTemplateCacheManager, which maintains
    a cache of compiled templates.

    USAGE
    -----
    The CTemplate class must be used as follows:

    CLASS INIT - InitClass must be called before any CTemplate can be created.

    NEW TEMPLATE - For each new template the client wants to create, the client
      must do the following, in order:
      1) New a CTemplate
      2) Initialize the CTemplate by calling Init, passing a source file name
      3) Load the CTemplate by calling Load; when Load returns, the new CTemplate is ready for use.

    EXISTING TEMPLATE - To use an existing template, the client must:
      1) Call Deliver; when Deliver returns, the existing CTemplate is ready for use.

    CLASS UNINIT - UnInitClass must be called after the last CTemplate has been destroyed.

    To ensure thread-safety, the client must implement a critical section
    around the call to Init.  Init is designed to be as fast as possible,
    so the client can quickly learn that it has a pending template for a given
    source file, and queue up other requests for the same source file.

    CTemplate provides implementations for Debug documents, namely
    IDebugDocumentProvider & IDebugDocumentText
*/
class CActiveScriptEngine;

class CTemplate :
            public CDblLink,
            public IDebugDocumentProvider,
            public IDebugDocumentText,
            public IConnectionPointContainer    // Source of IDebugDocumentTextEvents
{
private:
#include "templcap.h"   // 'captive' classes, only used internally within CTemplate

friend HRESULT InitMemCls();
friend HRESULT UnInitMemCls();

friend class	CTemplateCacheManager;			// TODO: decouple CTemplate class from it's manager class
friend class    CFileMap;
friend class    CIncFile;                       // IncFiles are privy to debugging data structures

// CScriptStore::Init() must access gm_brDefaultScriptLanguage, gm_progLangIdDefault
friend HRESULT  CTemplate::CScriptStore::Init(LPCSTR szDefaultScriptLanguage, CLSID *pCLSIDDefaultEngine);

private:
    CWorkStore* m_pWorkStore;               // ptr to working storage for source segments
    HANDLE      m_hEventReadyForUse;        // ready-for-use event handle

public:
    BYTE*       m_pbStart;                  // ptr to start of template memory
    ULONG       m_cbTemplate;               // bytes allocated for template
    LONG        m_cRefs;                    // ref count - NOTE LONG required by InterlockedIncrement
    LONG        m_cUseCount;                // count of times template was used
public:
    CTemplateConnPt m_CPTextEvents;         // Connection point for IDebugDocumentTextEvents

    // support for compile-time errors
    BYTE*       m_pbErrorLocation;          // ptr to error location in source file
    UINT        m_idErrMsg;                 // error message id
    UINT        m_cMsgInserts;              // count of insert strings for error msg
    char**      m_ppszMsgInserts;           // array of ptrs to error msg insert strings
    // support for run-time errors and debugging
    UINT        m_cScriptEngines;           // count of script engines
    CActiveScriptEngine **m_rgpDebugScripts;// array (indexed by engine) of scripts CURRENTLY BEING DEBUGGED
    vector<CSourceInfo> *m_rgrgSourceInfos; // array of arrays of script source line infos, one per script engine per target line
	ULONG       m_cbTargetOffsetPrevT;		// running total of last source offset processed
    CRITICAL_SECTION m_csDebuggerDetach;    // CS needed to avoid race condition with detaching from debugger
    CDblLink    m_listDocNodes;             // list of document nodes we are attached to
    CFileMap**  m_rgpFilemaps;              // array of ptrs to filemaps of source files
    CTemplateKey m_LKHashKey;               // bundled info for key (contains copy of m_rgpfilemaps[0] filename to make things simpler
    UINT        m_cFilemaps;                // count of filemaps of source files
    CFileMap**  m_rgpSegmentFilemaps;       // array of filemap ptrs per source segment
    UINT        m_cSegmentFilemapSlots;     // count of per-source-segment filemap ptrs
    LPSTR       m_pszLastErrorInfo[6];      // text of last error - cached for new requests on this template
                                            //  FileName, LineNum, Engine, ShortDes, LongDes
    DWORD       m_dwLastErrorMask;          // cached for new requests on this template
	DWORD		m_hrOnNoCache;				// HRESULT when don't cache is set.
    TCHAR*      m_szApplnVirtPath;          // application virtual path (substring of Application URL)
    TCHAR*      m_szApplnURL;               // application URL (starts with "http://")
    // for best structure packing we all boleans here as bitfields
    unsigned    m_fGlobalAsa:1;             // is template for global.asa file?
    unsigned    m_fIsValid:1;               // is template in valid state?
    unsigned    m_fDontCache:1;             // don't cache this template
    unsigned    m_fReadyForUse:1;           // is template ready for use?

    unsigned    m_fDebuggerDetachCSInited:1;// has debugger attach critical section been initialized?
    unsigned    m_fDontAttach:1;            // should not be attached to debugger (not in cache)
    unsigned    m_fSession:1;               // does this page require session state
    unsigned    m_fScriptless:1;            // doesn't have any scripts

    unsigned    m_fDebuggable:1;            // is this page part of at least one debuggable app?
    unsigned    m_fZombie:1;                // File template is based on has changed since obtained from cache
    unsigned    m_fCodePageSet:1;           // Did template contain a code page directive
    unsigned    m_fLCIDSet:1;               // Did template contain an LCID directive

    unsigned    m_fIsPersisted:1;
    unsigned    m_fIsUNC:1;
    unsigned    m_fIsEncrypted:1;
    unsigned    m_fTemplateLockInited:1;
    
    TransType   m_ttTransacted;             // type of transaction support

    // class-wide support for compilation
    static      CTokenList*     gm_pTokenList;              // array of tokens
    unsigned    m_wCodePage;                                // Compiler Time CodePage
    long        m_lLCID;                                    // Compile Time LCID

    vector<ITypeLib *>  m_rgpTypeLibs;          // array of ptrs to typelibs
    IDispatch*          m_pdispTypeLibWrapper;  // typelib wrapper object

    vector<C449Cookie *> m_rgp449;              // array of ptrs to 449 requests

    LPSTR               m_szPersistTempName;    // filename of persisted template, if any
    void               *m_pHashTable;           // CacheMgr hash table that this template is on

    IUnknown           *m_pServicesConfig;

    static  HANDLE sm_hSmallHeap;
    static  HANDLE sm_hLargeHeap;

	// DirMon related fields.
	BOOL		m_fNeedsMonitoring;
	BOOL		m_fInCheck;
    DWORD		m_dwLastMonitored;
    DWORD		m_dwCacheTag;

    // UNC related last impersonation handle
    DWORD		m_dwLastAccessCheck;
    LPVOID      m_pMostRecentImpersonatedTokenUser;
    PSID		m_pMostRecentImpersonatedSID;
    DWORD		m_cbTokenUser;
    CRITICAL_SECTION m_csTemplateLock;

public:
    /**
     **  Initialization and destruction public interfaces
     **/

    // Initializes CTemplate static members; must be called on denali.dll load
    static HRESULT InitClass();

    // Un-initilaizes CTemplate static members; must be called on denali.dll unload
    static void UnInitClass();

    //  Inits template in preparation for compilation
    //  Called by template cache mgr before calling Load
    HRESULT Init(CHitObj* pHitObj, BOOL fGlobalAsp, const CTemplateKey &rTemplateKey);

    //  Compiles the template from its main source file (and include files, if any)
    HRESULT Compile(CHitObj* pHitObj);

    // Called by requestor of existing template to determine if template is ready for use
    HRESULT Deliver(CHitObj* pHitObj);

    // Given the Token Handle this function will return the pointer to the SID and the token buffer
    HRESULT GetSIDFromTokenHandle (HANDLE tokenHandle, PSID pSid, LPVOID pBuffer, DWORD *pcbSize);

    // Create this template's CServicesConfig object
    HRESULT CreateTransServiceConfig(BOOL  fEnableTracker);

            CTemplate();
            ~CTemplate();
    void    RemoveIncFile(CIncFile* pIncFile);

	// Trace Log info
	static PTRACE_LOG gm_pTraceLog;

public:
#if PER_TEMPLATE_REFLOG
    PTRACE_LOG  m_pTraceLog;
#endif
    
    /*
        'Consumer' public interfaces
        Methods for getting info out of a CTemplate
    */

    // Returns name of source file on which this template is based
    LPTSTR GetSourceFileName(SOURCEPATHTYPE = SOURCEPATHTYPE_PHYSICAL);

    // Returns virtual path of the source file
    LPTSTR GetApplnPath(SOURCEPATHTYPE = SOURCEPATHTYPE_PHYSICAL);

    // Returns hashing key of the template
    const CTemplateKey *ExtractHashKey() const;

    // Returns version stamp of compiler by which this template was compiled
    LPSTR GetCompilerVersion();

    // Component counts
    USHORT Count(TEMPLATE_COMPONENT tcomp);
    USHORT CountScriptEngines() { return (USHORT)m_cScriptEngines; }

    // Returns i-th script block as ptr to prog lang id and ptr to script text
    void GetScriptBlock(UINT i, LPSTR* pszScriptEngine, PROGLANG_ID** ppProgLangId, LPCOLESTR* pwstrScriptText);

    // Returns i-th object-info as object name, clsid, scope, model
    HRESULT GetObjectInfo(UINT i, LPSTR* ppszObjectName,
            CLSID *pClsid, CompScope *pScope, CompModel *pcmModel);

    // Returns i-th HTML block as ptr, count of bytes, original offset, incl filename
    HRESULT GetHTMLBlock(UINT i, LPSTR* pszHTML, ULONG* pcbHTML, ULONG* pcbSrcOffs, LPSTR* pszSrcIncFile);

    // Returns line number and source file name a given target line in a given script engine.
    void GetScriptSourceInfo(UINT idEngine, int iTargetLine, LPTSTR* pszPathInfo, LPTSTR* pszPathTranslated, ULONG* piSourceLine, ULONG* pichSourceLine, BOOLB* pfGuessedLine);

    // Converts a character offset from the target script to the offset in the source
    void GetSourceOffset(ULONG idEngine, ULONG cchTargetOffset, TCHAR **pszSourceFile, ULONG *pcchSourceOffset, ULONG *pcchSourceText);

    // Converts a character offset from the source document to the offset in the target
    BOOL GetTargetOffset(TCHAR *szSourceFile, ULONG cchSourceOffset, ULONG *pidEngine, ULONG *pcchTargetOffset);

    // Get the character position of a line (directly implements debugging interface)
    HRESULT GetPositionOfLine(CFileMap *pFilemap, ULONG cLineNumber, ULONG *pcCharacterPosition);

    // Get the line # of a character position (directly implements debugging interface)
    HRESULT GetLineOfPosition(CFileMap *pFilemap, ULONG cCharacterPosition, ULONG *pcLineNumber, ULONG *pcCharacterOffsetInLine);

    // Return a RUNNING script based on the engine, or NULL if code context has never been requested yet
    CActiveScriptEngine *GetActiveScript(ULONG idEngine);

    // associate a running script for an engine ID (Use after you get the first code context)
    HRESULT AddScript(ULONG idEngine, CActiveScriptEngine *pScriptEngine);

    // attach the CTemplate object to an application (debugger tree view)
    HRESULT AttachTo(CAppln *pAppln);

    // detach the CTemplate object from an application (debugger tree view)
    HRESULT DetachFrom(CAppln *pAppln);

    // detach the CTemplate object all applications (and release script engines)
    HRESULT Detach();

    // Signifies last use of template as a recylable object. Any outstanding references
    // should be from currently executing scripts.
    ULONG End();

    // Let debugger know about page start/end
    HRESULT NotifyDebuggerOnPageEvent(BOOL fStart);

    // Generate 449 response in cookie negotiations with IE when needed
    HRESULT Do449Processing(CHitObj *pHitObj);

    HRESULT PersistData(char    *pszTempFilePath);
    HRESULT UnPersistData();
    HRESULT PersistCleanup();
    ULONG   TemplateSize()  { return m_cbTemplate; }

    BOOL FTransacted();
    BOOL FSession();
    BOOL FScriptless();
    BOOL FDebuggable();
    BOOL FIsValid();        // determine if compilation succeeded
    BOOL FTemplateObsolete();
    BOOL FGlobalAsa();
    BOOL FIsZombie();
    BOOL FDontAttach();
    BOOL FIsPersisted();
    BOOL FIsUNC();
    BOOL FIsEncrypted();
    BOOL ValidateSourceFiles        (CIsapiReqInfo *pIReq);
    BOOL FNeedsValidation();
    BOOL CheckTTLTimingWindow(DWORD dwLastMonitored, DWORD timeoutSecs);
    VOID Zombify();

    IUnknown    *PServicesConfig() {return m_pServicesConfig;};

    IDispatch *PTypeLibWrapper();

    void       SetHashTablePtr(void  *pTable) { m_pHashTable = pTable; }
    void      *GetHashTablePtr() { return m_pHashTable; }

    void    IncrUseCount() { InterlockedIncrement(&m_cUseCount); }

public:
    /*
        COM public interfaces
        Implementation of debugging documents.
    */

    // IUnknown methods
    STDMETHOD(QueryInterface)(const GUID &, void **);
    STDMETHOD_(ULONG, AddRef)();
    STDMETHOD_(ULONG, Release)();

    // IDebugDocumentProvider methods
    STDMETHOD(GetDocument)(/* [out] */ IDebugDocument **ppDebugDoc);

    // IDebugDocumentInfo (also IDebugDocumentProvider) methods
    STDMETHOD(GetName)(
        /* [in] */ DOCUMENTNAMETYPE dnt,
        /* [out] */ BSTR *pbstrName);

    STDMETHOD(GetDocumentClassId)(/* [out] */ CLSID *)
        {
        return E_NOTIMPL;
        }

    // IDebugDocumentText methods
    STDMETHOD(GetDocumentAttributes)(
        /* [out] */ TEXT_DOC_ATTR *ptextdocattr);

    STDMETHOD(GetSize)(
        /* [out] */ ULONG *pcLines,
        /* [out] */ ULONG *pcChars);

    STDMETHOD(GetPositionOfLine)(
        /* [in] */ ULONG cLineNumber,
        /* [out] */ ULONG *pcCharacterPosition);

    STDMETHOD(GetLineOfPosition)(
        /* [in] */ ULONG cCharacterPosition,
        /* [out] */ ULONG *pcLineNumber,
        /* [out] */ ULONG *pcCharacterOffsetInLine);

    STDMETHOD(GetText)(
        /* [in] */ ULONG cCharacterPosition,
        /* [size_is][length_is][out][in] */ WCHAR *pcharText,
        /* [size_is][length_is][out][in] */ SOURCE_TEXT_ATTR *pstaTextAttr,
        /* [out][in] */ ULONG *pcChars,
        /* [in] */ ULONG cMaxChars);

    STDMETHOD(GetPositionOfContext)(
        /* [in] */ IDebugDocumentContext *psc,
        /* [out] */ ULONG *pcCharacterPosition,
        /* [out] */ ULONG *cNumChars);

    STDMETHOD(GetContextOfPosition)(
        /* [in] */ ULONG cCharacterPosition,
        /* [in] */ ULONG cNumChars,
        /* [out] */ IDebugDocumentContext **ppsc);

    // IConnectionPointContainer methods
    STDMETHOD(EnumConnectionPoints)(
        /* [out] */ IEnumConnectionPoints __RPC_FAR *__RPC_FAR *ppEnum)
            {
            return E_NOTIMPL;   // doubt we need this - client is expecting only TextEvents
            }

    STDMETHOD(FindConnectionPoint)(
        /* [in] */ const IID &iid,
        /* [out] */ IConnectionPoint **ppCP);

private:
    /*  NOTE Compile() works by calling GetSegmentsFromFile followed by WriteTemplate
        Most other private methods support one of these two workhorse functions
    */

    void        AppendMapFile(LPCTSTR szFileSpec, CFileMap* pfilemapParent, BOOLB fVirtual,
                                    CHitObj* pHitObj, BOOLB fGlobalAsp);
    void        GetSegmentsFromFile(CFileMap& filemap, CWorkStore& WorkStore, CHitObj* pHitObj, BOOL fIsHTML = TRUE);
    void        GetLanguageEquivalents();
    void        SetLanguageEquivalent(HANDLE hKeyScriptLanguage, LPSTR szLanguageItem, LPSTR* pszOpen, UINT* pcchOpen, LPSTR* pszClose, UINT* pcchClose);
    void        ThrowError(BYTE* pbErrorLocation, UINT idErrMsg);
    void        AppendErrorMessageInsert(BYTE* pbInsert, UINT cbInsert);
    void        ThrowErrorSingleInsert(BYTE* pbErrorLocation, UINT idErrMsg, BYTE* pbInsert, UINT cbInsert);
    HRESULT     ShowErrorInDebugger(CFileMap* pFilemap, UINT cchErrorLocation, char* szDescription, CHitObj* pHitObj, BOOL fAttachDocument);
    void        ProcessSpecificError(CFileMap& filemap, CHitObj* pHitObj);
    void        HandleCTemplateError(CFileMap* pfilemap, BYTE* pbErrorLocation,
                                        UINT idErrMsg, UINT cInserts, char** ppszInserts, CHitObj *pHitObj);
    void        FreeGoodTemplateMemory();
    void        UnmapFiles();

    // ExtractAndProcessSegment: gets and processes next source segment in search range
    void        ExtractAndProcessSegment(CByteRange& brSearch, const SOURCE_SEGMENT& ssegLeading,
                    _TOKEN* rgtknOpeners, UINT ctknOpeners, CFileMap* pfilemapCurrent, CWorkStore& WorkStore,
                    CHitObj* pHitObj, BOOL fScriptTagProcessed = FALSE, BOOL fIsHTML = TRUE);
    // Support methods for ExtractAndProcessSegment()
    SOURCE_SEGMENT  SsegFromHTMLComment(CByteRange& brSegment);
    void        ProcessSegment(SOURCE_SEGMENT sseg, CByteRange& brSegment, CFileMap* pfilemapCurrent,
                                CWorkStore& WorkStore, BOOL fScriptTagProcessed, CHitObj* pHitObj,
                                BOOL fIsHTML);
    void        ProcessHTMLSegment(CByteRange& brHTML, CBuffer& bufHTMLBlocks, UINT idSequence, CFileMap* pfilemapCurrent);
    void        ProcessHTMLCommentSegment(CByteRange& brSegment, CFileMap* pfilemapCurrent, CWorkStore& WorkStore, CHitObj* pHitObj);
    void        ProcessScriptSegment(SOURCE_SEGMENT sseg, CByteRange& brSegment, CFileMap* pfilemapCurrent,
                                        CWorkStore& WorkStore, UINT idSequence, BOOLB fScriptTagProcessed, CHitObj* pHitObj);
    HRESULT     ProcessMetadataSegment(const CByteRange& brSegment, UINT *pidError, CHitObj* pHitObj);
    HRESULT     ProcessMetadataTypelibSegment(const CByteRange& brSegment, UINT *pidError, CHitObj* pHitObj);
    HRESULT     ProcessMetadataCookieSegment(const CByteRange& brSegment, UINT *pidError, CHitObj* pHitObj);
	void		GetScriptEngineOfSegment(CByteRange& brSegment, CByteRange& brEngine, CByteRange& brInclude);
    void        ProcessTaggedScriptSegment(CByteRange& brSegment, CFileMap* pfilemapCurrent, CWorkStore& WorkStore, CHitObj* pHitObj);
    void        ProcessObjectSegment(CByteRange& brSegment, CFileMap* pfilemapCurrent, CWorkStore& WorkStore,
                                        UINT idSequence);
    void        GetCLSIDFromBrClassIDText(CByteRange& brClassIDText, LPCLSID pclsid);
    void        GetCLSIDFromBrProgIDText(CByteRange& brProgIDText, LPCLSID pclsid);
    BOOLB       FValidObjectName(CByteRange& brName);
    void        ProcessIncludeFile(CByteRange& brSegment, CFileMap* pfilemapCurrent, CWorkStore& WorkStore, UINT idSequence, CHitObj* pHitObj, BOOL fIsHTML);
    void        ProcessIncludeFile2(CHAR* szFileSpec, CByteRange& brFileSpec, CFileMap* pfilemapCurrent, CWorkStore& WorkStore, UINT idSequence, CHitObj* pHitObj, BOOL fIsHTML);
    BYTE*       GetOpenToken(CByteRange brSearch, SOURCE_SEGMENT ssegLeading, _TOKEN* rgtknOpeners, UINT ctknOpeners, _TOKEN* ptknOpen);
    BYTE*       GetCloseToken(CByteRange brSearch, _TOKEN tknClose);
    _TOKEN      GetComplementToken(_TOKEN tkn);
    SOURCE_SEGMENT  GetSegmentOfOpenToken(_TOKEN tknOpen);
    CByteRange  BrTagsFromSegment(CByteRange brSegment, _TOKEN tknClose, BYTE** ppbCloseTag);
    CByteRange  BrValueOfTag(CByteRange brTags, _TOKEN tknTag);
    BYTE*       GetTagName(CByteRange brTags, _TOKEN tknTagName);

    BOOL        GetTag(CByteRange &brTags, int nIndex = 1);
    BOOL        CompTagName(CByteRange &brTags, _TOKEN tknTagName);

    BOOLB       FTagHasValue(const CByteRange& brTags, _TOKEN tknTag, _TOKEN tknValue);
    void        CopySzAdv(char* pchWrite, LPSTR psz);

    // WriteTemplate: writes the template to a contiguous block of memory
    void    WriteTemplate(CWorkStore& WorkStore, CHitObj* pHitObj);
    // Support methods for WriteTemplate()
    // NOTE Adv suffix on some function names == advance ptr after writing
    void    WriteHeader(USHORT cScriptBlocks,USHORT cObjectInfos, USHORT cHTMLBlocks, UINT* pcbHeaderOffset, UINT* pcbOffsetToOffset);
    void    WriteScriptBlockOfEngine(USHORT idEnginePrelim, USHORT idEngine, CWorkStore& WorkStore, UINT* pcbDataOffset,
                                        UINT* pcbOffsetToOffset, CHitObj* pHitObj);
    void    WritePrimaryScriptProcedure(USHORT idEngine, CWorkStore& WorkStore, UINT* pcbDataOffset, UINT cbScriptBlockStart);
    void    WriteScriptSegment(USHORT idEngine, CFileMap* pfilemap, CByteRange& brScript, UINT* pcbDataOffset, UINT cbScriptBlockStart,
                                BOOL fAllowExprWrite);
    void    WriteScriptMinusEscapeChars(CByteRange brScript, UINT* pcbDataOffset, UINT* pcbPtrOffset);
    BOOLB   FVbsComment(CByteRange& brLine);
    BOOLB   FExpression(CByteRange& brLine);
    void    WriteOffsetToOffset(USHORT cBlocks, UINT* pcbHeaderOffset, UINT* pcbOffsetToOffset);
    void    WriteSzAsBytesAdv(LPCSTR szSource, UINT* pcbDataOffset);
    void    WriteByteRangeAdv(CByteRange& brSource, BOOLB fWriteAsBsz, UINT* pcbDataOffset, UINT* pcbPtrOffset);
    void    WriteLongAdv(ULONG uSource, UINT* pcbOffset);
    void    WriteShortAdv(USHORT uSource, UINT* pcbOffset);
    void    MemCpyAdv(UINT* pcbOffset, void* pbSource, ULONG cbSource, UINT cbByteAlign = 0);

    // Memory access primitives
    // NOTE invalid until WriteTemplate() has succeeded
    BYTE*   GetAddress(TEMPLATE_COMPONENT tcomp, USHORT i);

    // Debugging methods
    void    AppendSourceInfo(USHORT idEngine, CFileMap* pfilemap, BYTE* pbSource,
							 ULONG cbSourceOffset, ULONG cbScriptBlockOffset, ULONG cbTargetOffset,
							 ULONG cchSourceText, BOOL fIsHTML);
    UINT    SourceLineNumberFromPb(CFileMap* pfilemap, BYTE* pbSource);
    HRESULT CreateDocumentTree(CFileMap *pfilemapRoot, IDebugApplicationNode **ppDocRoot);
    BOOLB   FIsLangVBScriptOrJScript(USHORT idEngine);

#if 0
    void OutputDebugTables();
    void OutputIncludeHierarchy(CFileMap *pfilemap, int cchIndent);
    void GetScriptSnippets(ULONG cchSourceOffset, CFileMap *pFilemapSource, ULONG cchTargetOffset, ULONG idTargetEngine, wchar_t wszSourceText[], wchar_t wszTargetText[]);
#endif

    void    RemoveFromIncFiles();

    void    ReleaseTypeLibs();
    void    WrapTypeLibs(CHitObj *pHitObj);

    void    Release449();

    HRESULT BuildPersistedDACL(PACL  *ppRetDACL);

    // Cache on per-class basis
    ACACHE_INCLASS_DEFINITIONS()

public:
    // memory allocation
    static void* SmallMalloc(SIZE_T dwBytes);
    static void* SmallReAlloc(void* pvMem, SIZE_T dwBytes);
    static void  SmallFree(void* pvMem);

    static void* LargeMalloc(SIZE_T dwBytes);
    static void* LargeReAlloc(void* pvMem, SIZE_T dwBytes);
    static void  LargeFree(void* pvMem);
};

////////////////////////////////////////////////////////////////////////////////
//  Inline functions

// Write a long or short to memory, then advance target-ptr
inline void     CTemplate::WriteLongAdv(ULONG uSource, UINT* pcbOffset)
                    { MemCpyAdv(pcbOffset, &uSource, sizeof(ULONG), sizeof(ULONG)); }
inline void     CTemplate::WriteShortAdv(USHORT uSource, UINT* pcbOffset)
                    { MemCpyAdv(pcbOffset, &uSource, sizeof(USHORT), sizeof(USHORT)); }
inline const CTemplateKey * CTemplate::ExtractHashKey() const
					{ return &m_LKHashKey; }
inline BOOL     CTemplate::FTransacted()
                    { return (m_ttTransacted != ttUndefined); }
inline BOOL     CTemplate::FDebuggable()
                    { return(m_fDebuggable); }
inline BOOL     CTemplate::FSession()
                    { return(m_fSession); }
inline BOOL     CTemplate::FScriptless()
                    { return m_fScriptless; }
inline BOOL     CTemplate::FIsValid()
                    { return(m_fIsValid); }
inline BOOL     CTemplate::FGlobalAsa()
                    { return(m_fGlobalAsa); }
inline BOOL     CTemplate::FIsZombie()
                    { return m_fZombie; }
inline VOID     CTemplate::Zombify()
                    { m_fZombie = TRUE; }
inline BOOL     CTemplate::FDontAttach()
                    { return (m_fDontAttach); }
inline BOOL     CTemplate::FIsPersisted()
                    { return (m_fIsPersisted); }
inline BOOL     CTemplate::FIsUNC()
                    { return (m_fIsUNC); }
inline BOOL     CTemplate::FIsEncrypted()
                    { return (m_fIsEncrypted); }
inline LPTSTR   CTemplate::GetApplnPath(SOURCEPATHTYPE pathtype)
                    { Assert (pathtype == SOURCEPATHTYPE_VIRTUAL); return m_szApplnVirtPath; }
inline IDispatch *CTemplate::PTypeLibWrapper()
                    { return (m_pdispTypeLibWrapper); }


/*  ****************************************************************************
    Class:      CIncFile
    Synopsis:   A file included by one or more templates.

    NOTE: We store an incfile-template dependency by storing a template ptr in m_rgpTemplates.
    This is efficient but ***will break if we ever change Denali to move its memory around***
*/
class CIncFile :
            private CLinkElem,
            public IDebugDocumentProvider,
            public IDebugDocumentText,
            public IConnectionPointContainer    // Source of IDebugDocumentTextEvents
{
// CIncFileMap is a friend so that it can manipulate CLinkElem private members and to access m_ftLastWriteTime
friend class CIncFileMap;

private:
    LONG                m_cRefs;            // ref count - NOTE LONG required by InterlockedIncrement
    TCHAR *             m_szIncFile;        // include file name - NOTE we keep this as a stable ptr to hash table key
    CRITICAL_SECTION    m_csUpdate;         // CS for updating the template ptrs array
    vector<CTemplate *> m_rgpTemplates;     // array of ptrs to templates which include this include file
    CTemplateConnPt     m_CPTextEvents;     // Connection point for IDebugDocumentTextEvents
    BOOLB               m_fCsInited;        // has CS been initialized yet?

    CTemplate::CFileMap *GetFilemap();      // Return the filemap pointer from a template

public:
                CIncFile();
    HRESULT     Init(const TCHAR* szIncFile);
                ~CIncFile();
    HRESULT     AddTemplate(CTemplate* pTemplate);
    void        RemoveTemplate(CTemplate* pTemplate);
    CTemplate*  GetTemplate(int iTemplate);
    BOOL        FlushTemplates();
    TCHAR *     GetIncFileName() { return m_szIncFile; }
    void        OnIncFileDecache();

    /*
        COM public interfaces
        Implementation of debugging documents.
    */

    // IUnknown methods
    STDMETHOD(QueryInterface)(const GUID &, void **);
    STDMETHOD_(ULONG, AddRef)();
    STDMETHOD_(ULONG, Release)();

    // IDebugDocumentProvider methods
    STDMETHOD(GetDocument)(/* [out] */ IDebugDocument **ppDebugDoc);

    // IDebugDocumentInfo (also IDebugDocumentProvider) methods
    STDMETHOD(GetName)(
        /* [in] */ DOCUMENTNAMETYPE dnt,
        /* [out] */ BSTR *pbstrName);

    STDMETHOD(GetDocumentClassId)(/* [out] */ CLSID *)
        {
        return E_NOTIMPL;
        }

    // IDebugDocumentText methods
    STDMETHOD(GetDocumentAttributes)(
        /* [out] */ TEXT_DOC_ATTR *ptextdocattr);

    STDMETHOD(GetSize)(
        /* [out] */ ULONG *pcLines,
        /* [out] */ ULONG *pcChars);

    STDMETHOD(GetPositionOfLine)(
        /* [in] */ ULONG cLineNumber,
        /* [out] */ ULONG *pcCharacterPosition);

    STDMETHOD(GetLineOfPosition)(
        /* [in] */ ULONG cCharacterPosition,
        /* [out] */ ULONG *pcLineNumber,
        /* [out] */ ULONG *pcCharacterOffsetInLine);

    STDMETHOD(GetText)(
        /* [in] */ ULONG cCharacterPosition,
        /* [size_is][length_is][out][in] */ WCHAR *pcharText,
        /* [size_is][length_is][out][in] */ SOURCE_TEXT_ATTR *pstaTextAttr,
        /* [out][in] */ ULONG *pcChars,
        /* [in] */ ULONG cMaxChars);

    STDMETHOD(GetPositionOfContext)(
        /* [in] */ IDebugDocumentContext *psc,
        /* [out] */ ULONG *pcCharacterPosition,
        /* [out] */ ULONG *cNumChars);

    STDMETHOD(GetContextOfPosition)(
        /* [in] */ ULONG cCharacterPosition,
        /* [in] */ ULONG cNumChars,
        /* [out] */ IDebugDocumentContext **ppsc);

    // IConnectionPointContainer methods
    STDMETHOD(EnumConnectionPoints)(
        /* [out] */ IEnumConnectionPoints __RPC_FAR *__RPC_FAR *ppEnum)
            {
            return E_NOTIMPL;   // doubt we need this - client is expecting only TextEvents
            }

    STDMETHOD(FindConnectionPoint)(
        /* [in] */ const IID &iid,
        /* [out] */ IConnectionPoint **ppCP);

};
#endif /* _TEMPLATE_H */