//depot/private/jasbr/inetsrv/iis/svcs/cmp/asp/template.cpp#19 - edit change 3548 (text) /*============================================================================== Microsoft Denali Microsoft Confidential. Copyright 1996 Microsoft Corporation. All Rights Reserved. File: template.cpp Maintained by: DaveK Component: source file for Denali Compiled Template object ==============================================================================*/ #include "denpre.h" #pragma hdrstop const int SNIPPET_SIZE = 20; // # of characters in the code snippets #pragma warning( disable : 4509 ) // suppress SEH/destructor warnings #pragma warning( disable : 4355 ) // ignore: "'this' used in base member init #include "debugger.h" #include "dbgutil.h" #include "tlbcache.h" #include "ie449.h" #include "memchk.h" #include "vecimpl.h" // Include after memchk to insure that vector uses our mem manager. #include "Accctrl.h" #include "aclapi.h" // Init class statics CTemplate::CTokenList *CTemplate::gm_pTokenList = NULL; PTRACE_LOG CTemplate::gm_pTraceLog = NULL; HANDLE CTemplate::sm_hSmallHeap = NULL; HANDLE CTemplate::sm_hLargeHeap = NULL; // Max # of opener tokens to look for #define TOKEN_OPENERS_MAX 8 /*=================================================================== Private non-class support functions ===================================================================*/ static void ByteRangeFromPb(BYTE* pbSource, CByteRange& brTarget); static BOOLB FByteRangesAreEqual(CByteRange& br1, CByteRange& br2); static unsigned CharAdvDBCS(WORD wCodePage, char *pchStart, char *pchEnd, unsigned cCharAdv, char **ppchEnd, BOOL fForceDBCS = FALSE); static void LineFromByteRangeAdv(CByteRange& br, CByteRange& brLine); static void LTrimWhiteSpace(CByteRange& br); static void RTrimWhiteSpace(CByteRange& br); static CByteRange BrNewLine(CByteRange br); static BOOLB FWhiteSpace(char ch, BOOLB fSpaceIsWhiteSpace = TRUE); static BOOLB FByteRangeIsWhiteSpace(CByteRange br); static BOOLB FTagName(BYTE* pb, UINT cb); static void ByteAlignOffset(UINT* pcbOffset, UINT cbAlignment); static void GetSzFromPatternInserts(char* pszPattern, UINT cInserts, char** ppszInserts, char* szReturned); static UINT CchPathFromFilespec(LPCTSTR szFile); static void GetPathFromParentAndFilespec(LPCTSTR szParentPath, LPCTSTR szFileSpec, LPTSTR* pszPath); static void HandleAccessFailure(CHitObj* pHitObj, TCHAR* szFile); static void SendToLog(DWORD dwMask, CHAR *szFileName, CHAR *szLineNum, CHAR *szShortDes, CHAR *szLongDes, CHAR *szEngine, CHitObj *pHitObj); static HRESULT GetProgLangId(CByteRange& brEngine, PROGLANG_ID* pProgLangId); inline void __cdecl DebugPrintf(LPCSTR fmt, ...) { #if DBG char msg[512]; va_list marker; va_start(marker, fmt); vsprintf(msg, fmt, marker); va_end(marker); OutputDebugStringA(msg); #endif } /* ============================================================================ ByteRangeFromPb Gets a byte range from a contiguous block of memory Returns: Nothing. Side effects: None. */ void ByteRangeFromPb ( BYTE* pbSource, CByteRange& brTarget ) { Assert(pbSource != NULL); brTarget.m_cb = *(ULONG*)pbSource; brTarget.m_pb = pbSource + sizeof(ULONG); } /* ============================================================================ FByteRangesAreEqual Compares two byte ranges Returns: BOOLB. True if byte ranges are equal, else false. Side effects: None. */ BOOLB FByteRangesAreEqual ( CByteRange& br1, CByteRange& br2 ) { if(br1.m_cb != br2.m_cb) return FALSE; return (!_strnicmp((LPCSTR)br1.m_pb, (LPCSTR)br2.m_pb, br1.m_cb)); } /* ============================================================================ CharAdvDBCS Advance "cchCharAdv" characters in a buffer SBCS: Degenerates to simple pointer arithmatic Arguments: wCodePage - code page pchStart - pointer to beginning of segment pchEnd - pointer to just past end of segment cCharAdv - # of characters to advance ppchEnd - [output], contains pointer "cCharAdv" chars past pchStart fForceDBCS - if TRUE, always use double byte algorithm. (for verifying correct behavior of func in debug mode) Returns: (int) # of characters that we actually advanced Notes: By passing INFINITE for "cCharAdv", you can use this function to count characters in a block Side effects: None. */ unsigned CharAdvDBCS ( WORD wCodePage, char *pchStart, char *pchEnd, unsigned cCharAdv, char **ppchEnd, BOOL fForceDBCS ) { CPINFO CpInfo; GetCPInfo(wCodePage, &CpInfo); if (!fForceDBCS && CpInfo.MaxCharSize == 1) { char *pchT = pchStart + min(cCharAdv, unsigned(pchEnd - pchStart)); if (ppchEnd) *ppchEnd = pchT; #if DBG // Verify DBCS algorithm (not often tested otherwise) char *pchTest; unsigned cchTest = CharAdvDBCS(wCodePage, pchStart, pchEnd, cCharAdv, &pchTest, TRUE); Assert (cchTest == unsigned(pchT - pchStart) && pchTest == pchT); #endif return DIFF(pchT - pchStart); } else { int cch = 0; char *pchNext = pchStart; // Count DBCS characters. We have to stop before pchEnd because // pchEnd may point past file map and CharNextExA AVs when advancing // past allocated memory while (cCharAdv > 0 && pchNext < pchEnd-2) { pchNext = *pchNext? AspCharNextA(wCodePage, pchNext) : pchNext + 1; --cCharAdv; ++cch; } // We could stop on the last or the before last character // depending on the DBCS char sequence if (cCharAdv > 0 && pchNext == pchEnd-1) { // Only one byte - has to be one single byte character ++pchNext; ++cch; } else if (cCharAdv > 0 && pchNext == pchEnd-2) { // 2 bytes left - either 1 2-byte char or 2 1-byte chars if (IsDBCSLeadByteEx(wCodePage, *pchNext)) { ++cch; pchNext += 2; } else { // Two characters left. If cCharAdv > 1, this means that user wants to // advance at least two more chars. Otherwise, cCharAdv == 1, and // we advance one char // if (cCharAdv > 1) { cch += 2; pchNext += 2; } else { Assert (cCharAdv == 1); ++cch; ++pchNext; } } } if (ppchEnd) *ppchEnd = pchNext; return cch; } } /* ============================================================================ LineFromByteRangeAdv Gets the first line in a byte range. Returns: Nothing Side effects: Advances source byte range to just beyond its first non-white-space line, if one is found. */ void LineFromByteRangeAdv ( CByteRange& brSource, CByteRange& brLine ) { CByteRange brTemp; if(brSource.IsNull()) { brLine.Nullify(); return; } brLine.m_pb = brSource.m_pb; brTemp = BrNewLine(brSource); if(brTemp.IsNull()) { // We found no newline in a non-empty byte range: // set line range to entire source byte range and empty source byte range brLine.m_cb = brSource.m_cb; brSource.Nullify(); } else { // We found a newline in a non-empty byte range: // set line range to portion of source byte range before new line; // set source range to portion of source range after new line brLine.m_cb = DIFF(brTemp.m_pb - brSource.m_pb); brSource.m_pb = brTemp.m_pb + brTemp.m_cb; brSource.m_cb -= (brLine.m_cb + brTemp.m_cb); } } /* ============================================================================ LTrimWhiteSpace Left-trim white space from byte-range Returns: Nothing Side effects: Advances byte range to just beyond its first non-white-space character. */ void LTrimWhiteSpace ( CByteRange& br ) { if(br.IsNull()) return; while(FWhiteSpace(*br.m_pb)) { br.m_pb++; if(--br.m_cb == 0) return; } } /* ============================================================================ RTrimWhiteSpace Right-trim white space from byte-range */ void RTrimWhiteSpace(CByteRange& br) { if(br.IsNull()) return; while(FWhiteSpace(*(br.m_pb + br.m_cb - 1))) { if(--br.m_cb == 0) return; } } /* ============================================================================ BrNewLine Returns ptr to the first newline in a byte range NOTE does not change byte range (since it is passed by value) */ CByteRange BrNewLine(CByteRange br) { while(!br.IsNull()) { if(*br.m_pb == '\r') return CByteRange(br.m_pb, (br.m_cb > 1 && br.m_pb[1] == '\n')? 2 : 1); else if (*br.m_pb == '\n') return CByteRange(br.m_pb, 1); ++br.m_pb; --br.m_cb; } return CByteRange(); } /* ============================================================================ FWhiteSpace Returns: TRUE if ch is a white-space character, else returns FALSE Certain character(s) (e.g. space) may be treated as non-white-space; to do this, caller passes FALSE for fSpaceIsWhiteSpace flag. */ BOOLB FWhiteSpace(char ch, BOOLB fSpaceIsWhiteSpace) { switch (ch) { case ' ': return fSpaceIsWhiteSpace; case '\0': return TRUE; case '\a': return TRUE; case '\b': return TRUE; case '\f': return TRUE; case '\n': return TRUE; case '\r': return TRUE; case '\t': return TRUE; case '\v': return TRUE; default: return FALSE; } } /* ============================================================================ FByteRangeIsWhiteSpace Is the entire input byte range white space? NOTE input byte range is byval; caller's copy is not changed */ BOOLB FByteRangeIsWhiteSpace(CByteRange br) { while(!br.IsNull()) { if(!FWhiteSpace(*(br.m_pb))) return FALSE; br.Advance(1); } return TRUE; } /* ============================================================================ FTagName Does pb point to a valid HTML tag name? (i.e., is *pb a valid HTML tag name and not a substring?) Returns TRUE or FALSE Side effects None */ BOOLB FTagName(BYTE* pb, UINT cb) { if((pb == NULL) || (cb == 0)) return FALSE; // a valid HTML tag name must be preceded by white space ... if( FWhiteSpace(*(pb - 1)) || *(pb - 1) == '@' ) { // ... and followed either by white space or the tag separator if(FWhiteSpace(*(pb + cb))) return TRUE; else if(*(pb + cb) == CH_ATTRIBUTE_SEPARATOR) return TRUE; } return FALSE; } /*=================================================================== ByteAlignOffset Byte-aligns an offset value, based on size of source data */ void ByteAlignOffset ( UINT* pcbOffset, // ptr to offset value UINT cbAlignment // Alignment boundary ) { // comment the below code out so that it works for 64 bit... // only byte-align for 2-, or 4-byte data // since our base pointer in only aligned to a 4 byte boundary //if(cbAlignment == 2 || cbAlignment == 4) //{ // if current offset does not fall on a byte-aligned location for current data type, // advance offset to next byte-aligned location Assert(cbAlignment > 0); --cbAlignment; if (*pcbOffset & cbAlignment) *pcbOffset = (*pcbOffset + cbAlignment + 1) & ~cbAlignment; } /* ============================================================================ GetSzFromPatternInserts Returns a 'resolved' version of a pattern string, i.e. a new string in which | characters have been replaced by caller-specified insert strings. NOTE this function allocates, but caller must free Returns: Nothing Side effects: allocates memory */ void GetSzFromPatternInserts ( char* pszPattern, // 'pattern' string UINT cInserts, // count of insert strings char** ppszInserts, // array of ptrs to insert strings char* szReturned // returned string MUST be allocated by caller ) { UINT cchRet = strlen(pszPattern); // length of return string char* pchStartCopy = pszPattern; // ptr to start of copy range in pattern char* pchEndCopy = pszPattern; // ptr to end of copy range in pattern UINT cActualInserts = 0; // count of actual insert strings // init return string to empty so we can concatenate onto it szReturned[0] = NULL; // zero out length of return string - we now use it to count actual length as we build return string cchRet = 0; while(TRUE) { // advance end-of-copy ptr through pattern looking for insertion points or end of string while ((*pchEndCopy != NULL) && (IsDBCSLeadByte(*pchEndCopy) || (*pchEndCopy != '|'))) pchEndCopy = CharNextA(pchEndCopy); // cat from start-of-copy to end-of-copy onto return string strncat(szReturned, pchStartCopy, DIFF(pchEndCopy - pchStartCopy)); // update return string length cchRet += DIFF(pchEndCopy - pchStartCopy); // if we are at end of pattern, exit if(*pchEndCopy == NULL) goto Exit; if(cActualInserts < cInserts) { // if inserts remain, cat the next one onto return string strcat(szReturned, ppszInserts[cActualInserts]); // update return string length cchRet += strlen(ppszInserts[cActualInserts]); cActualInserts++; } // advance end-of-copy and start-of-copy beyond insertion point pchEndCopy++; pchStartCopy = pchEndCopy; } Exit: // null-terminate return string szReturned[cchRet] = NULL; } /* ============================================================================ CchPathFromFilespec Returns a filespec's path length (exclusive of filespec) NOTE path string includes trailing '\' or '/' Returns: Length of path string Side effects: None */ UINT CchPathFromFilespec ( LPCTSTR szFileSpec // filespec ) { // BUG FIX 102010 DBCS fixes //int ich = lstrlen(szFileSpec) - 1; // index of char to compare // //while(*(szFileSpec + ich) != '\\' && *(szFileSpec + ich) != '/') // { // if(--ich < 0) // THROW(E_FAIL); // } //return (UINT) (ich + 1); // path length, including trailing '\' or '/', is char index + 1 TCHAR* p1 = _tcsrchr(szFileSpec, _T('\\')); TCHAR* p2 = _tcsrchr(szFileSpec, _T('/')); // this wont be a DBCS trail byte. if (p1 == NULL && p2 == NULL) THROW(E_FAIL); return (UINT) ((((LPTSTR)max(p1,p2) - szFileSpec)) + 1); } /* ============================================================================ GetPathFromParentAndFilespec Returns an absolute path which is a 'parent' file's path concatenated with a filespec. Returns: absolute path (out-parameter) Side effects: None */ void GetPathFromParentAndFilespec ( LPCTSTR szParentPath, // parent path LPCTSTR szFileSpec, // filespec LPTSTR* pszPath // resolved path (out-parameter) ) { UINT cchParentPath = CchPathFromFilespec(szParentPath); if ((cchParentPath + _tcslen(szFileSpec)) > MAX_PATH) THROW(E_FAIL); _tcsncpy(*pszPath, szParentPath, cchParentPath); _tcscpy(*pszPath + cchParentPath, szFileSpec); } /* ============================================================================ HandleAccessFailure Handles an access-denied failure Returns: nothing Side effects: none */ void HandleAccessFailure ( CHitObj* pHitObj, // browser's hitobj TCHAR * szFile // file path of main template ) { Assert(pHitObj); // debugging diagnostic print #if DBG STACK_BUFFER( authUserBuff, 32 ); char *szAuthUser; DWORD cbAuthUser; if (SERVER_GET(pHitObj->PIReq(), "AUTH_USER", &authUserBuff, &cbAuthUser)) { szAuthUser = (char*)authUserBuff.QueryPtr(); } else { szAuthUser = "anonymous"; } #if UNICODE DBGPRINTF((DBG_CONTEXT, "No permission to read file %S\n", szFile != NULL? szFile : pHitObj->PIReq()->QueryPszPathTranslated())); #else DBGPRINTF((DBG_CONTEXT, "No permission to read file %s\n", szFile != NULL? szFile : pHitObj->PIReq()->QueryPszPathTranslated())); #endif DBGPRINTF((DBG_CONTEXT, " The user account is \"%s\"\n", szAuthUser)); #endif CResponse *pResponse = pHitObj->PResponse(); if (!pResponse) return; HandleSysError(401,3,IDE_401_3_ACCESS_DENIED,IDH_401_3_ACCESS_DENIED,pHitObj->PIReq(),pHitObj); return; } /* ============================================================================ SendToLog Sends Error Info to Log Returns: Nothing Side effects: None. */ void SendToLog ( DWORD dwMask, CHAR *szFileName, CHAR *szLineNum, CHAR *szEngine, CHAR *szErrCode, CHAR *szShortDes, CHAR *szLongDes, CHitObj *pHitObj // browser's hitobj ) { CHAR *szFileNameT; CHAR *szLineNumT; CHAR *szEngineT; CHAR *szErrCodeT; CHAR *szShortDesT; CHAR *szLongDesT; if(pHitObj) { // NOTE - szFileName is assumed to be UTF8 when UNICODE is defined szFileNameT = StringDupA(szFileName); szLineNumT = StringDupA(szLineNum); szEngineT = StringDupA(szEngine); szErrCodeT = StringDupA(szErrCode); szShortDesT = StringDupA(szShortDes); szLongDesT = StringDupA(szLongDes); HandleError(szShortDesT, szLongDesT, dwMask, szFileNameT, szLineNumT, szEngineT, szErrCodeT, NULL, pHitObj); } } /* ============================================================================ FreeNullify Frees and nullifies a ptr to memory allocated with malloc. Returns: Nothing Side effects: None */ static void FreeNullify ( void** pp ) { if(*pp != NULL) { free(*pp); *pp = NULL; } } /* ============================================================================ SmallTemplateFreeNullify Frees and nullifies a ptr to memory allocated with CTemplate::SmallMalloc. Returns: Nothing Side effects: None */ static void SmallTemplateFreeNullify ( void** pp ) { if(*pp != NULL) { CTemplate::SmallFree(*pp); *pp = NULL; } } /* ============================================================================ LargeTemplateFreeNullify Frees and nullifies a ptr to memory allocated with CTemplate::LargeMalloc. Returns: Nothing Side effects: None */ static void LargeTemplateFreeNullify ( void** pp ) { if(*pp != NULL) { CTemplate::LargeFree(*pp); *pp = NULL; } } /* ============================================================================ GetProgLangId Gets the prog lang id for a script engine Returns: Nothing Side effects: throws on error */ HRESULT GetProgLangId ( CByteRange& brEngine, // engine name PROGLANG_ID* pProgLangId // prog lang id (out-parameter) ) { STACK_BUFFER( tempEngine, 128 ); if (!tempEngine.Resize(brEngine.m_cb + 1)) { return E_OUTOFMEMORY; } LPSTR szProgLang = static_cast (tempEngine.QueryPtr()); strncpy(szProgLang, (LPCSTR)brEngine.m_pb, brEngine.m_cb); szProgLang[brEngine.m_cb] = '\0'; return g_ScriptManager.ProgLangIdOfLangName((LPCSTR) szProgLang, pProgLangId); } /* **************************************************************************** CByteRange member functions */ /* ======================================================== CByteRange::Advance Advances a byte range. */ void CByteRange::Advance(UINT i) { if(i >= m_cb) { Nullify(); } else { m_pb += i; m_cb -= i; } } /* ======================================================== CByteRange::FMatchesSz Compares a byte range with a string, case-insensitively */ BOOLB CByteRange::FMatchesSz ( LPCSTR psz ) { if(IsNull() || (psz == NULL)) return FALSE; if((ULONG)strlen(psz) != m_cb) return FALSE; return !_strnicmp((const char*)m_pb, psz, m_cb); } /* ============================================================================ CByteRange::PbString Finds a case-insensitive string within a byte range Returns: Ptr to first case-insensitive occurrence of the string in this byte range; NULL if none found. Side effects: None */ BYTE* CByteRange::PbString ( LPSTR psz, LONG lCodePage ) { UINT cch = strlen(psz); if(cch == 0) return NULL; BYTE *pbLocal = m_pb; UINT cbLocal = m_cb; char ch0 = psz[0]; BYTE *pbTemp = NULL; UINT cbAdvanced = 0; if (IsCharAlpha(ch0)) { // cannot use strchr while (cbLocal >= cch) { if (_strnicmp((const char *)pbLocal, psz, cch) == 0) return pbLocal; // The following code simply performs a DBCS-enabled ByteRange.Advance() action. pbTemp = pbLocal; pbLocal = *pbLocal? (BYTE *)AspCharNextA((WORD)lCodePage, (const char *)pbLocal) : pbLocal + 1; cbAdvanced = DIFF(pbLocal - pbTemp); if (cbAdvanced >= cbLocal) { cbLocal = 0; pbLocal = NULL; } else cbLocal -= cbAdvanced; } } else { // can use strchr while (cbLocal >= cch) { pbTemp = (BYTE *)memchr(pbLocal, ch0, cbLocal); if (pbTemp == NULL) break; UINT cbOffset = DIFF(pbTemp - pbLocal); if (cbOffset >= cbLocal) break; pbLocal = pbTemp; cbLocal -= cbOffset; if (cch <= cbLocal && _strnicmp((const char *)pbLocal, psz, cch) == 0) return pbLocal; // The following code simply performs a DBCS-enabled ByteRange.Advance() action. pbTemp = pbLocal; pbLocal = *pbLocal? (BYTE *)AspCharNextA((WORD)lCodePage, (const char *)pbLocal) : pbLocal + 1; cbAdvanced = DIFF(pbLocal - pbTemp); if (cbAdvanced >= cbLocal) { cbLocal = 0; pbLocal = NULL; } else cbLocal -= cbAdvanced; } } return NULL; } /* ============================================================================ CByteRange::PbOneOfAspOpenerStringTokens Finds a case-insensitive string within a byte range that matches one of the strings passed !!! WILL ONLY WORK IF THE FOLLOWING IS TRUE: 1) All the tokens start with the same charater (for example '<') 2) This character is not alpha (so that strchr() would work) !!! THE ABOVE ASSUMPTIONS MAKE THE CODE WORK FASTER Returns: Ptr to first case-insensitive occurrence of the string in this byte range; NULL if none found. *pcindex is set to the index of string found Side effects: None */ BYTE* CByteRange::PbOneOfAspOpenerStringTokens ( LPSTR rgszTokens[], UINT rgcchTokens[], UINT nTokens, UINT *pidToken ) { if (nTokens == 0) return NULL; BYTE *pb = m_pb; // pointer to unsearched remainder of the range UINT cbRemainder = m_cb; // remaining byte range length char ch0 = rgszTokens[0][0]; // first char of every token while (cbRemainder > 0) { // BUG 82331: avoid strchr() because byte range is not null-terminated while (cbRemainder > 0 && *pb != ch0) { ++pb; --cbRemainder; } if (cbRemainder == 0) break; for (UINT i = 0; i < nTokens; i++) { if ((rgcchTokens[i] <= cbRemainder) && (rgszTokens[i] != NULL) && (_strnicmp((const char *)pb, rgszTokens[i], rgcchTokens[i]) == 0)) { *pidToken = i; return pb; } } ++pb; --cbRemainder; } return NULL; } /* ============================================================================ CByteRange::FEarlierInSourceThan Does this byte range occur earlier in source than parameter byte range? Returns TRUE or FALSE Side effects None */ BOOLB CByteRange::FEarlierInSourceThan(CByteRange& br) { if(br.IsNull()) return TRUE; return(m_idSequence < br.m_idSequence); } /* **************************************************************************** CTemplate member functions */ /* ============================================================================ CTemplate::InitClass Initilaizes CTemplate static members Returns: hresult Side effects: allocates memory for static members */ HRESULT CTemplate::InitClass ( ) { HRESULT hr = S_OK; TRY // init heaps sm_hSmallHeap = ::HeapCreate(0, 0, 0); sm_hLargeHeap = ::HeapCreate(0, 0, 0); // Init token list gm_pTokenList = new CTokenList; if (gm_pTokenList == NULL) return E_OUTOFMEMORY; gm_pTokenList->Init(); CATCH(hrException) hr = hrException; END_TRY return hr; } /* ============================================================================ CTemplate::UnInitClass Un-initilaizes CTemplate static members Returns: Nothing Side effects: None */ void CTemplate::UnInitClass() { delete gm_pTokenList; gm_pTokenList = NULL; ::HeapDestroy(sm_hLargeHeap); if (sm_hLargeHeap != sm_hSmallHeap) ::HeapDestroy(sm_hSmallHeap); sm_hLargeHeap = sm_hSmallHeap = NULL; } /* ============================================================================ CTemplate::Init Inits template in preparation for calling Compile Does the minimum needed Returns: Success or failure code Side effects: Allocates memory */ HRESULT CTemplate::Init ( CHitObj *pHitObj, // ptr to template's hit object BOOL fGlobalAsa, // is this the global.asa file? const CTemplateKey &rTemplateKey // hash table key ) { HRESULT hr; // Create debug critical section ErrInitCriticalSection(&m_csDebuggerDetach, hr); if (FAILED(hr)) return hr; // note critical section creation success m_fDebuggerDetachCSInited = TRUE; // Create event: manual-reset, ready-for-use event; non-signaled m_hEventReadyForUse = IIS_CREATE_EVENT( "CTemplate::m_hEventReadyForUse", this, TRUE, // flag for manual-reset event FALSE // flag for initial state ); if (!m_hEventReadyForUse) return E_OUTOFMEMORY; // cache GlobalAsp flag m_fGlobalAsa = BOOLB(fGlobalAsa); // CIsapiReqInfo better be present if (pHitObj->PIReq() == NULL) return E_POINTER; // Initialize the template's code page m_wCodePage = pHitObj->PAppln()->QueryAppConfig()->uCodePage(); m_lLCID = pHitObj->PAppln()->QueryAppConfig()->uLCID(); STACK_BUFFER( serverNameBuff, 32 ); STACK_BUFFER( serverPortBuff, 10 ); STACK_BUFFER( portSecureBuff, 8 ); DWORD cbServerName; DWORD cbServerPort; DWORD cbServerPortSecure; // Construct a URL for the application // Get the server name and port if (!SERVER_GET(pHitObj->PIReq(), "SERVER_NAME", &serverNameBuff, &cbServerName) || !SERVER_GET(pHitObj->PIReq(), "SERVER_PORT", &serverPortBuff, &cbServerPort)) { if (GetLastError() == E_OUTOFMEMORY) { hr = E_OUTOFMEMORY; } else { hr = E_FAIL; } return hr; } char *szServerPort = (char *)serverPortBuff.QueryPtr(); char *szServerName = (char *)serverNameBuff.QueryPtr(); BOOL fServerPortSecure = FALSE; // determine if server port is secure if (SERVER_GET(pHitObj->PIReq(), "SERVER_PORT_SECURE", &portSecureBuff, &cbServerPortSecure)) { char *szServerPortSecure = (char *)portSecureBuff.QueryPtr(); fServerPortSecure = (szServerPortSecure[0] == '1'); } // Get the application virtual path TCHAR szApplnVirtPath[256]; if (FAILED(hr = FindApplicationPath(pHitObj->PIReq(), szApplnVirtPath, sizeof szApplnVirtPath))) return hr; TCHAR *szServerNameT; TCHAR *szServerPortT; #if UNICODE CMBCSToWChar convServer; if (FAILED(hr = convServer.Init(szServerName))) { return hr; } szServerNameT = convServer.GetString(); #else szServerNameT = szServerName; #endif #if UNICODE CMBCSToWChar convPort; if (FAILED(hr = convPort.Init(szServerPort))) { return hr; } szServerPortT = convPort.GetString(); #else szServerPortT = szServerPort; #endif // Allocate space for and construct the application URL m_szApplnURL = new TCHAR [(9 /* sizeof "https://:" */ + _tcslen(szServerNameT) + _tcslen(szServerPortT) + _tcslen(szApplnVirtPath) + 1)]; if (m_szApplnURL == NULL) return E_OUTOFMEMORY; TCHAR *pT; // start with the protocol prefix... pT = strcpyEx(m_szApplnURL, fServerPortSecure? _T("https://") : _T("http://")); // next add the servername pT = strcpyEx(pT, szServerNameT); // next the colon between the servername and the serverport pT = strcpyEx(pT, _T(":")); // next the server port pT = strcpyEx(pT, szServerPortT); // now the applURL is built up to the appln path. The next step will be to // add the virtpath. m_szApplnVirtPath = pT; _tcscpy(m_szApplnVirtPath, szApplnVirtPath); m_LKHashKey.dwInstanceID = rTemplateKey.dwInstanceID; if ((m_LKHashKey.szPathTranslated = StringDup((TCHAR *)rTemplateKey.szPathTranslated)) == NULL) return E_OUTOFMEMORY; return S_OK; } /* ============================================================================ CTemplate::Compile Compiles the template from its source file and include files, if any, by calling GetSegmentsFromFile (to populate WorkStore), followed by WriteTemplate (to create the template from WorkStore). Returns: HRESULT indicating success or type of failure Side effects: Indirectly allocates memory (via WriteTemplate) Indirectly frees memory on error (via FreeGoodTemplateMemory) */ HRESULT CTemplate::Compile ( CHitObj* pHitObj ) { HRESULT hr = S_OK; // The following code moved from Init() (to make Init() lighter) Assert(pHitObj); // Create and Init WorkStore if (SUCCEEDED(hr)) { // construct the workstore - bail on fail if(NULL == (m_pWorkStore = new CWorkStore)) hr = E_OUTOFMEMORY; } if (SUCCEEDED(hr)) { hr = (m_pWorkStore->m_ScriptStore).Init(pHitObj->QueryAppConfig()->szScriptLanguage(), pHitObj->QueryAppConfig()->pCLSIDDefaultEngine()); if (hr == TYPE_E_ELEMENTNOTFOUND) { // default script language in registry is bogus - send error msg to browser HandleCTemplateError( NULL, // source file map NULL, // ptr to source location where error occurred IDE_TEMPLATE_BAD_PROGLANG_IN_REGISTRY, // error message id 0, // count of insert strings for error msg NULL, // array of ptrs to error msg insert strings pHitObj // Browser Request ); } if (FAILED(hr)) { delete m_pWorkStore; m_pWorkStore = NULL; } } // Try to init the workstore and map main file - this can fail with oom, etc or user lacks permissions if (SUCCEEDED(hr)) { TRY m_pWorkStore->Init(); AppendMapFile( NULL, // file spec for this file - NULL means get filespec from pHitObj NULL, // ptr to filemap of parent file FALSE, // don't care pHitObj, // ptr to template's hit object m_fGlobalAsa // is this the global.asa file? ); CATCH(hrException) delete m_pWorkStore; m_pWorkStore = NULL; hr = hrException; if(hr == E_USER_LACKS_PERMISSIONS) HandleAccessFailure(pHitObj, (m_rgpFilemaps && m_rgpFilemaps[0])? m_rgpFilemaps[0]->m_szPathTranslated : NULL); if (m_rgpFilemaps && m_rgpFilemaps[0]) { // empty file will fail to map but will have a handle, so we check for it here if (0 == GetFileSize(m_rgpFilemaps[0]->m_hFile, NULL)) hr = E_SOURCE_FILE_IS_EMPTY; m_rgpFilemaps[0]->UnmapFile(); } if (SUCCEEDED(hr)) hr = E_FAIL; // make sure the error is set END_TRY } if (SUCCEEDED(hr)) { Assert(m_rgpFilemaps[0]); Assert(m_rgpFilemaps[0]->m_szPathTranslated); Assert(FImplies(!m_fGlobalAsa, (0 == _tcscmp(m_rgpFilemaps[0]->m_szPathTranslated, pHitObj->PSzCurrTemplatePhysPath())))); Assert(FImplies(m_fGlobalAsa, (0 == _tcscmp(m_rgpFilemaps[0]->m_szPathTranslated, pHitObj->GlobalAspPath())))); Assert(0 < m_rgpFilemaps[0]->GetSize()); } if (FAILED(hr)) { m_fDontCache = TRUE; // OK, cache HR if m_fDontCache is true // later, another thread might find this template from the cache even if the template // has some error and marked as DontCache. m_hrOnNoCache = hr; m_fReadyForUse = TRUE; SetEvent(m_hEventReadyForUse); return hr; } // End of Code moved from Init() // By default we are not in a transaction m_ttTransacted = ttUndefined; // By default session is required m_fSession = TRUE; // By default assume script exists m_fScriptless = FALSE; // we assert, in effect, that template is already init'ed Assert(FImplies(!m_fGlobalAsa, (0 == _tcscmp(m_rgpFilemaps[0]->m_szPathTranslated, pHitObj->PSzCurrTemplatePhysPath())))); Assert(FImplies(m_fGlobalAsa, (0 == _tcscmp(m_rgpFilemaps[0]->m_szPathTranslated, pHitObj->GlobalAspPath())))); TRY // Get source segments from source file GetSegmentsFromFile(*(m_rgpFilemaps[0]), *m_pWorkStore, pHitObj); /* get "language equivalents" for primary languagefrom registry NOTE we do this here because the user can reset the primary language in the script file, so we must wait until after GetSegmentsFromFile() */ GetLanguageEquivalents(); // Call WriteTemplate, which writes out template components to contiguous memory, // resulting in a compiled template WriteTemplate(*m_pWorkStore, pHitObj); // Calculate the # of characters in a filemap before we unmap the file for all time. for (unsigned i = 0; i < m_cFilemaps; ++i) m_rgpFilemaps[i]->CountChars((WORD)m_wCodePage); // Wrap typelibs into single IDispatch* WrapTypeLibs(pHitObj); m_fIsValid = TRUE; CATCH(hrException) // NOTE: we used to free template memory here. Now we do not because if the // error was E_USER_LACKS_PERMISSIONS, and template is in cache, we don't want // to sabotage future requests. There's no need to decache the template. // // The template destructor will free this memory anyway. // hr = hrException; END_TRY // check if scriptless if (!m_fGlobalAsa) { // count various stuff to make the determination DWORD cScriptEngines = m_pWorkStore->m_ScriptStore.CountPreliminaryEngines(); DWORD cPrimaryScriptSegments = (cScriptEngines > 0) ? m_pWorkStore->m_ScriptStore.m_ppbufSegments[0]->Count() : 0; DWORD cObjectTags = m_pWorkStore->m_ObjectInfoStore.Count(); DWORD cHtmlSegments = m_pWorkStore->m_bufHTMLSegments.Count(); DWORD c449Cookies = m_rgp449.length(); BOOL fPageCommandsPresent = m_pWorkStore->m_fPageCommandsExecuted; if (cScriptEngines <= 1 && cPrimaryScriptSegments == 0 && cObjectTags == 0 && cHtmlSegments == 1 && c449Cookies == 0 && !fPageCommandsPresent) { m_fScriptless = TRUE; } } // free working storage - no longer needed delete m_pWorkStore; m_pWorkStore = NULL; // un-map filemaps - NOTE filemaps stay around for possible post-compile errors (e.g., script failure) UnmapFiles(); // Debugging: print data structure to debugger IF_DEBUG(SCRIPT_DEBUGGER) { if (SUCCEEDED(hr)) { DBGPRINTF((DBG_CONTEXT, "Script Compiled\n")); for (UINT i = 0; i < m_cScriptEngines; ++i) { char *szEngineName; PROGLANG_ID *pProgLangID; const wchar_t *wszScriptText; GetScriptBlock(i, &szEngineName, &pProgLangID, &wszScriptText); DBGPRINTF((DBG_CONTEXT, "Engine %d, Language=\"%s\":\n", i, szEngineName)); #ifndef _NO_TRACING_ DBGINFO((DBG_CONTEXT, (char *) wszScriptText)); DBGINFO((DBG_CONTEXT, "\n")); #else OutputDebugStringW(wszScriptText); OutputDebugStringA("\n"); #endif } #if 0 OutputDebugTables(); #endif } } if (hr == E_TEMPLATE_COMPILE_FAILED_DONT_CACHE) { m_fDontCache = TRUE; m_hrOnNoCache = hr; } // Set ready-for-use flag true and event to signaled // NOTE we do this whether success or failure, since even a failed-compile template // will remain in the cache to allow template cache mgr to satisfy requests on it m_fReadyForUse = TRUE; SetEvent(m_hEventReadyForUse); // Note whether the template currently is debuggable // BUG BUG: Template is debuggable or not based on first app. If shared between a debug // & non-debug app, the first application wins. m_fDebuggable = (BOOLB)!!pHitObj->PAppln()->FDebuggable(); return hr; } /* ============================================================================ CTemplate::Deliver Delivers template to caller once template is ready for use NOTE 'compile failure' == template is 'ready for use' but did not compile successfully; this allows cache mgr to keep a failed template in cache in case it gets requested again Returns success or failure Side effects none */ HRESULT CTemplate::Deliver ( CHitObj* pHitObj ) { // NOTE: There was a compiler bug where 'ps' would not be correctly aligned, // EVEN if it was declared to be a DWORD array, if 'ps' was nested in // a block. Thus declare it here. // BYTE ps[SIZE_PRIVILEGE_SET]; // privilege set HRESULT hr = S_OK; // if ready flag is not yet set block until template is ready for use if(!m_fReadyForUse) { WaitForSingleObject(m_hEventReadyForUse, INFINITE); Assert(m_fReadyForUse); // when event unblocks, flag will be set } if (m_pbStart == NULL) { if (m_fDontCache && m_dwLastErrorMask == 0) { DBGPRINTF((DBG_CONTEXT, "template compile failed with %08x\n", m_hrOnNoCache)); DBG_ASSERT(FAILED(m_hrOnNoCache)); // Safety net: always fail, even if "m_hrOnNoCache" did not get set somehow. hr = m_hrOnNoCache; if (SUCCEEDED(m_hrOnNoCache)) hr = E_FAIL; if(hr == E_USER_LACKS_PERMISSIONS) HandleAccessFailure(pHitObj, (m_rgpFilemaps && m_rgpFilemaps[0])? m_rgpFilemaps[0]->m_szPathTranslated : NULL); return hr; } // template compile failed - NOTE null start-of-template ptr == template compile failed // use cached error info SendToLog( m_dwLastErrorMask, m_pszLastErrorInfo[ILE_szFileName], m_pszLastErrorInfo[ILE_szLineNum], m_pszLastErrorInfo[ILE_szEngine], m_pszLastErrorInfo[ILE_szErrorCode], m_pszLastErrorInfo[ILE_szShortDes], m_pszLastErrorInfo[ILE_szLongDes], pHitObj); hr = E_TEMPLATE_COMPILE_FAILED; } else if (!pHitObj->FIsBrowserRequest()) { return hr; } else if (Glob(fWinNT)) // template compile succeeded - check user's file permissions // ACLs: the following code should in future be shared with IIS (see creatfil.cxx in IIS project) { HANDLE hUserAccessToken = pHitObj->HImpersonate(); // current user's access token DWORD dwPS = sizeof(ps); // privilege set size DWORD dwGrantedAccess; // granted access mask BOOL fAccessGranted; // access granted flag GENERIC_MAPPING gm = { // generic mapping struct FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE, FILE_ALL_ACCESS }; ((PRIVILEGE_SET*)&ps)->PrivilegeCount = 0; // set privilege count to 0 Assert(NULL != hUserAccessToken); for(UINT i = 0; i < m_cFilemaps; i++) { if(NULL == m_rgpFilemaps[i]->m_pSecurityDescriptor) continue; if(!AccessCheck( m_rgpFilemaps[i]->m_pSecurityDescriptor, // pointer to security descriptor hUserAccessToken, // handle to client access token FILE_GENERIC_READ, // access mask to request &gm, // address of generic-mapping structure (PRIVILEGE_SET*)ps, // address of privilege-set structure &dwPS, // address of size of privilege-set structure &dwGrantedAccess, // address of granted access mask &fAccessGranted // address of flag indicating whether access granted )) return E_FAIL; if(!fAccessGranted) { // if access is denied on any file, handle the failure and return HandleAccessFailure(pHitObj, m_rgpFilemaps[0]->m_szPathTranslated); return E_USER_LACKS_PERMISSIONS; } } } // Reset the Session.CodePage to the script compilation-time codepage // only if a code page directive was found during compilation if (m_fCodePageSet && (!pHitObj->FHasSession() || !pHitObj->PSession()->FCodePageSet())) { pHitObj->SetCodePage(m_wCodePage); } // Reset the Session.LCID to the script compilation-time LCID // only if an LCID directive was found during compilation if (m_fLCIDSet && (!pHitObj->FHasSession() || !pHitObj->PSession()->FLCIDSet())) { pHitObj->SetLCID(m_lLCID); } return hr; } /* ============================================================================ CTemplate::CTemplate Ctor */ CTemplate::CTemplate() : m_pWorkStore(NULL), m_fGlobalAsa(FALSE), m_fReadyForUse(FALSE), m_fDontAttach(FALSE), m_hEventReadyForUse(NULL), m_fDebuggerDetachCSInited(FALSE), m_pbStart(NULL), m_cbTemplate(0), m_cRefs(1), // NOTE ctor AddRefs implicitly m_pbErrorLocation(NULL), m_idErrMsg(0), m_cMsgInserts(0), m_ppszMsgInserts(NULL), m_cScriptEngines(0), m_rgrgSourceInfos(NULL), m_rgpDebugScripts(NULL), m_rgpFilemaps(NULL), m_cFilemaps(0), m_rgpSegmentFilemaps(NULL), m_cSegmentFilemapSlots(0), m_wCodePage(CP_ACP), m_lLCID(LOCALE_SYSTEM_DEFAULT), m_ttTransacted(ttUndefined), m_fSession(TRUE), m_fScriptless(FALSE), m_fDebuggable(FALSE), m_fIsValid(FALSE), m_fDontCache(FALSE), m_fZombie(FALSE), m_fCodePageSet(FALSE), m_fLCIDSet(FALSE), m_fIsPersisted(FALSE), m_szPersistTempName(NULL), m_szApplnVirtPath(NULL), m_szApplnURL(NULL), m_CPTextEvents(this, IID_IDebugDocumentTextEvents), m_pdispTypeLibWrapper(NULL), m_dwLastErrorMask(S_OK), m_hrOnNoCache(S_OK), m_cbTargetOffsetPrevT(0), m_pHashTable(NULL), m_pServicesConfig(NULL) { m_wCodePage = GetACP(); for (UINT i = 0; i < ILE_MAX; i++) { m_pszLastErrorInfo[i] = NULL; } IF_DEBUG(TEMPLATE) { WriteRefTraceLog(gm_pTraceLog, m_cRefs, this); } #if PER_TEMPLATE_REFLOG m_pTraceLog = CreateRefTraceLog (100,0); WriteRefTraceLog (m_pTraceLog,m_cRefs, this); #endif } /* ============================================================================ CTemplate::~CTemplate Destructor Returns: Nothing Side effects: None */ CTemplate::~CTemplate() { DBGPRINTF(( DBG_CONTEXT, "Deleting template, m_cFilemaps = %d, m_rgpFilemaps %p\n", m_cFilemaps, m_rgpFilemaps)); // first, remove this template from its inc-files' template lists // NOTE must do this before freeing template memory RemoveFromIncFiles(); // Remove the template from the debugger's list of documents Detach(); PersistCleanup(); if(m_rgpFilemaps) { for(UINT i = 0; i < m_cFilemaps; i++) delete m_rgpFilemaps[i]; SmallTemplateFreeNullify((void**) &m_rgpFilemaps); } FreeGoodTemplateMemory(); if (m_pWorkStore) delete m_pWorkStore; //FileName, LineNum, Engine, ErrorCode, ShortDes, LongDes for(UINT iErrInfo = 0; iErrInfo < ILE_MAX; iErrInfo++) { FreeNullify((void**) &m_pszLastErrorInfo[iErrInfo]); } if(m_hEventReadyForUse != NULL) CloseHandle(m_hEventReadyForUse); if (m_LKHashKey.szPathTranslated) free((void *)m_LKHashKey.szPathTranslated); if (m_szApplnURL) delete [] m_szApplnURL; if (m_fDebuggerDetachCSInited) DeleteCriticalSection(&m_csDebuggerDetach); if (m_pdispTypeLibWrapper) m_pdispTypeLibWrapper->Release(); if (m_szPersistTempName) CTemplate::LargeFree(m_szPersistTempName); if (m_pServicesConfig) m_pServicesConfig->Release(); m_pServicesConfig = NULL; #if PER_TEMPLATE_REFLOG DestroyRefTraceLog (m_pTraceLog); #endif } /* ============================================================================ CTemplate::QueryInterface Provides QueryInterface implementation for CTemplate NOTE: It is arbitrary which vtable we return for IDebugDocument & IDebugDocumentInfo. */ HRESULT CTemplate::QueryInterface(const GUID &uidInterface, void **ppvObj) { if (uidInterface == IID_IUnknown || uidInterface == IID_IDebugDocumentProvider) *ppvObj = static_cast(this); else if (uidInterface == IID_IDebugDocument || uidInterface == IID_IDebugDocumentInfo || uidInterface == IID_IDebugDocumentText) *ppvObj = static_cast(this); else if (uidInterface == IID_IConnectionPointContainer) *ppvObj = static_cast(this); else *ppvObj = NULL; if (*ppvObj) { AddRef(); return S_OK; } else return E_NOINTERFACE; } /* ============================================================================ CTemplate::AddRef Adds a ref to this template, thread-safely */ ULONG CTemplate::AddRef() { LONG cRefs = InterlockedIncrement(&m_cRefs); Assert(FImplies(m_fIsValid,FImplies(cRefs > 1, m_pbStart != NULL))); IF_DEBUG(TEMPLATE) { WriteRefTraceLog(gm_pTraceLog, cRefs, this); } #if PER_TEMPLATE_REFLOG WriteRefTraceLog(m_pTraceLog, cRefs, this); #endif return cRefs; } /* ============================================================================ CTemplate::Release Releases a ref to this template, thread-safely */ ULONG CTemplate::Release() { LONG cRefs = InterlockedDecrement(&m_cRefs); IF_DEBUG(TEMPLATE) { WriteRefTraceLog(gm_pTraceLog, cRefs, this); } #if PER_TEMPLATE_REFLOG WriteRefTraceLog(m_pTraceLog, cRefs, this); #endif if (cRefs == 0) delete this; return cRefs; } /* ============================================================================ CTemplate::RemoveIncFile Removes (by setting to null) an inc-file ptr from this template's inc-file list. Returns: Nothing Side effects: None */ void CTemplate::RemoveIncFile ( CIncFile* pIncFile ) { // If the filemap count is non-zero the pointer to // the array of filemaps has better not be null DBGPRINTF(( DBG_CONTEXT, "m_cFilemaps = %d, m_rgpFilemaps %p\n", m_cFilemaps, m_rgpFilemaps)); Assert((m_cFilemaps <= 0) || (m_rgpFilemaps != NULL)); // find the inc-file in list for(UINT i = 1; (i < m_cFilemaps) && (m_rgpFilemaps[i]->m_pIncFile != pIncFile); i++) ; // assert that we found the inc-file in list Assert((i < m_cFilemaps) && (m_rgpFilemaps[i]->m_pIncFile == pIncFile)); // set inc-file ptr null m_rgpFilemaps[i]->m_pIncFile = NULL; } /*=================================================================== CTemplate::FTemplateObsolete Test to see if the files this template depends on have changed since it was compiled. We use this in cases where we may have missed a change notification, for example, when there were too many changes to record in our change notification buffer. We check the last time the file was written too, and the security descriptor, since changes to the security descriptor aren't noted in the file last write time. Parameters: None Returns: TRUE if the template is obsolete, else FALSE */ BOOL CTemplate::FTemplateObsolete(VOID) { BOOL fStatus = FALSE; // On Windows 95 files should not be cached // so assume the template has changed if (!FIsWinNT()) { return TRUE; } for (UINT i = 0; i < m_cFilemaps; i++) { if (FFileChangedSinceCached(m_rgpFilemaps[i]->m_szPathTranslated, m_rgpFilemaps[i]->m_ftLastWriteTime)) { // If the file write time has changed we know enough // and can quit here fStatus = TRUE; break; } else { // The file hasn't been writen to, but the security descriptor may // have chagned // Assert on non-valid security descriptor if (NULL != m_rgpFilemaps[i]->m_pSecurityDescriptor) { PSECURITY_DESCRIPTOR pSecurityDescriptor = NULL; DWORD dwSize = m_rgpFilemaps[i]->m_dwSecDescSize; if( 0 == GetSecDescriptor(m_rgpFilemaps[i]->m_szPathTranslated, &pSecurityDescriptor, &dwSize)) { if (pSecurityDescriptor) { // if the size is not the same then set fStatus to TRUE no need to compare memory blocks. if(dwSize != GetSecurityDescriptorLength(m_rgpFilemaps[i]->m_pSecurityDescriptor)) { fStatus = TRUE; } else { // The size of the security descriptor hasn't changed // but we have to compare the contents to make sure they haven't changed fStatus = !(0 == memcmp(m_rgpFilemaps[i]->m_pSecurityDescriptor, pSecurityDescriptor, dwSize)); } // We are done with the descriptor free(pSecurityDescriptor); } else { // Since we failed to get a security descriptor // assume the file has changed. fStatus = TRUE; } } } } // Quit as soon as we find a change if (fStatus) { break; } } return fStatus; } /* ============================================================================ CTemplate::GetSourceFileName Returns name of source file on which this template is based Returns source file name Side effects none */ LPTSTR CTemplate::GetSourceFileName(SOURCEPATHTYPE pathtype) { if (!m_rgpFilemaps) { return NULL; } switch (pathtype) { case SOURCEPATHTYPE_PHYSICAL: return((m_rgpFilemaps[0] ? m_rgpFilemaps[0]->m_szPathTranslated : NULL)); case SOURCEPATHTYPE_VIRTUAL: return((m_rgpFilemaps[0] ? m_rgpFilemaps[0]->m_szPathInfo : NULL)); default: return(NULL); } } /* ============================================================================ CTemplate::Count Returns count of components of type tcomp contained in this template Returns: Count of components of type tcomp Side effects: None */ USHORT CTemplate::Count ( TEMPLATE_COMPONENT tcomp ) { Assert(NULL != m_pbStart); // script engines and script blocks have the same count, stored in same slot if(tcomp == tcompScriptEngine) tcomp = tcompScriptBlock; // counts are stored at start of template in sequential slots, starting with script blocks count return * (USHORT*) ((USHORT*)m_pbStart + (tcomp - tcompScriptBlock)); } /* ============================================================================ CTemplate::GetScriptBlock Gets ptrs to script engine name, prog lang id and script text of i-th script block. Returns: Out-parameters; see below Side effects: None */ void CTemplate::GetScriptBlock ( UINT i, // script block id LPSTR* pszScriptEngine, // ptr to script engine name (out-parameter) PROGLANG_ID** ppProgLangId, // ptr to prog lang id (out-parameter) LPCOLESTR* pwstrScriptText // ptr to wstr script text (out-parameter) ) { CByteRange brEngine; // engine name CByteRange brScriptText; // script text UINT cbAlignment; // count of bytes guid was shifted in WriteTemplate() to make it dword-aligned BYTE* pbEngineInfo = GetAddress(tcompScriptEngine, (USHORT)i); // ptr to engine info Assert(pbEngineInfo != NULL); Assert(i < CountScriptEngines()); // Get engine name from start of engine info ByteRangeFromPb(pbEngineInfo, brEngine); ByteRangeFromPb(GetAddress(tcompScriptBlock, (USHORT)i), brScriptText); Assert(!brEngine.IsNull()); Assert(!brScriptText.IsNull()); // Advance ptr past name to prog lang id // length of prefix + length of name + NULL pbEngineInfo += (sizeof(UINT) + (*pbEngineInfo) + 1); // Get prog lang id - it will be on the next pointer sized boundary cbAlignment = (UINT) (((DWORD_PTR) pbEngineInfo) % sizeof(DWORD)); if(cbAlignment > 0) {pbEngineInfo += (sizeof(DWORD) - cbAlignment);} *pszScriptEngine = (LPSTR)brEngine.m_pb; *ppProgLangId = (PROGLANG_ID*)pbEngineInfo; *pwstrScriptText = (LPCOLESTR)brScriptText.m_pb; } /* ============================================================================ CTemplate::GetObjectInfo Returns i-th object-info in template as object name and its clsid, scope, model Returns: HRESULT Out-parameters; see below Side effects: */ HRESULT CTemplate::GetObjectInfo ( UINT i, // object index LPSTR* ppszObjectName, // address of object name ptr (out-parameter) CLSID* pClsid, // address of object clsid CompScope* pcsScope, // address of object scope CompModel* pcmModel // address of object threading model ) { BYTE* pbObjectInfo = GetAddress(tcompObjectInfo, (USHORT)i); // ptr to current read location CByteRange brName; // object name UINT cbAlignment; // count of bytes guid was shifted in WriteTemplate() to make it dword-aligned Assert(i < Count(tcompObjectInfo)); // Get name from start of object-info ByteRangeFromPb(pbObjectInfo, brName); Assert(!brName.IsNull()); // Advance ptr past name // length of prefix + length of name + NULL pbObjectInfo += (sizeof(UINT) + (*pbObjectInfo) + 1); // Get clsid - it will be on the next DWORD boundary cbAlignment = (UINT)(((DWORD_PTR) pbObjectInfo) % sizeof(DWORD)); if(cbAlignment > 0) pbObjectInfo += (sizeof(DWORD) - cbAlignment); *pClsid = *(CLSID*)pbObjectInfo; pbObjectInfo += sizeof(CLSID); // Get scope *pcsScope = *(CompScope*)pbObjectInfo; pbObjectInfo += sizeof(CompScope); // Get model *pcmModel = *(CompModel*)pbObjectInfo; pbObjectInfo += sizeof(CompModel); *ppszObjectName = (LPSTR)brName.m_pb; return S_OK; } /* ============================================================================ CTemplate::GetHTMLBlock Returns i-th HTML block Parameters: UINT i block number LPSTR* pszHTML [out] html text ULONG* pcbHTML [out] html text length ULONG* pcbSrcOffs [out] offset in the source file LPSTR* pszSrcIncFile [out] include source file name Returns: Nothing Side effects: None */ HRESULT CTemplate::GetHTMLBlock ( UINT i, LPSTR* pszHTML, ULONG* pcbHTML, ULONG* pcbSrcOffs, LPSTR* pszSrcIncFile ) { Assert(i < Count(tcompHTMLBlock)); // this was added due to user attempt to access the method with an invalid array offset // if ( i >= Count(tcompHTMLBlock) ) return E_FAIL; // get address of the block start in template memory BYTE *pbBlock = GetAddress(tcompHTMLBlock, (USHORT)i); Assert(pbBlock); // retrieve the byte range of the html code CByteRange brHTML; ByteRangeFromPb(pbBlock, brHTML); *pszHTML = (LPSTR)brHTML.m_pb; *pcbHTML = brHTML.m_cb; // advance to the source offset pbBlock += sizeof(ULONG); // skip prefix pbBlock += brHTML.m_cb+1; // skip html bytes (incl. '\0') // Add byte aligment which is done in ByteAlignOffset() if ((reinterpret_cast(pbBlock)) & 3) pbBlock = reinterpret_cast((reinterpret_cast(pbBlock) + 4) & ~3); *pcbSrcOffs = *((ULONG*)pbBlock); // advance to the source name length pbBlock += sizeof(ULONG); // skip source offset prefix ULONG cbSrcIncFile = *((ULONG *)pbBlock); // inc file name length pbBlock += sizeof(ULONG); // skip inc file name length *pszSrcIncFile = (cbSrcIncFile > 0) ? (LPSTR)pbBlock : NULL; return S_OK; } /* ============================================================================ CTemplate::GetScriptSourceInfo Returns line number and source file name a given target line in a given script engine. Returns line number and source file name (as out-parameters) Side effects: None */ void CTemplate::GetScriptSourceInfo ( UINT idEngine, // script engine id int iTargetLine, // target line number LPTSTR* pszPathInfo, // ptr to source file virtual path (out-parameter) LPTSTR* pszPathTranslated, // ptr to source file real path (out-parameter) ULONG* piSourceLine, // ptr to source line number (out-parameter) ULONG* pichSourceLine, // ptr to source file offset (out-parameter) BOOLB* pfGuessedLine // ptr to flag: did we guess the source line? ) { // Initialize some out parameters if (pszPathInfo) *pszPathInfo = _T("?"); // In case we don't ever find the path if (pszPathTranslated) *pszPathTranslated = _T("?"); // In case we don't ever find the path if (piSourceLine) *piSourceLine = 0; if (pichSourceLine) *pichSourceLine = 0; if (pfGuessedLine) *pfGuessedLine = FALSE; if (iTargetLine <=0) { return; } // CHANGE: The rgSourceInfo array is now ZERO based. Decrement target line // to convert. --iTargetLine; // CONSIDER: Make these assertions? if(!m_rgrgSourceInfos) return; if(idEngine > (m_cScriptEngines - 1)) // bug 375: check vs. array bound return; if(size_t(iTargetLine) >= m_rgrgSourceInfos[idEngine].length()) // bug 375: check vs. array bound return; vector *prgSourceInfos = &m_rgrgSourceInfos[idEngine]; // bug 379: move backwards through target lines, starting with the caller's, until we find one whose // fIsHTML flag is false. this handles the case where vbs flags a manufactured line as in error; // we assume the actual error occurred at the most recent authored line while (iTargetLine >= 0 && (*prgSourceInfos)[iTargetLine].m_fIsHTML) { --iTargetLine; if (pfGuessedLine) *pfGuessedLine = TRUE; } if (iTargetLine >= 0) { if (pszPathInfo && (*prgSourceInfos)[iTargetLine].m_pfilemap != NULL) *pszPathInfo = (*prgSourceInfos)[iTargetLine].m_pfilemap->m_szPathInfo; if (pszPathTranslated && (*prgSourceInfos)[iTargetLine].m_pfilemap != NULL) *pszPathTranslated = (*prgSourceInfos)[iTargetLine].m_pfilemap->m_szPathTranslated; if (piSourceLine) *piSourceLine = (*prgSourceInfos)[iTargetLine].m_idLine; if (pichSourceLine) *pichSourceLine = (*prgSourceInfos)[iTargetLine].m_cchSourceOffset; } } /* ============================================================================ CTemplate::GetPositionOfLine Get the character offset of a line of source (Debugger API Extended to specify a filemap) */ HRESULT CTemplate::GetPositionOfLine ( CFileMap *pFilemap, ULONG cLineNumber, ULONG *pcCharacterPosition ) { // NOTE: // The table is not binary-searchable because include files // will start a new line ordering // // Algorithm: // // Find the largest source line N across all engines, such that // N <= cLineNumber and the line corresponds to an line // in the appropriate file. // CSourceInfo *pSourceInfoLE = NULL; ++cLineNumber; // Convert zero-based line # to one-based // Find the correct offset for (unsigned idEngine = 0; idEngine < m_cScriptEngines; ++idEngine) { vector *prgSourceInfos = &m_rgrgSourceInfos[idEngine]; // Loop through all lines EXCEPT the EOF line for (unsigned j = 0; j < prgSourceInfos->length() - 1; ++j) { CSourceInfo *pSourceInfo = &(*prgSourceInfos)[j]; if (pFilemap == pSourceInfo->m_pfilemap && pSourceInfo->m_idLine <= cLineNumber && (pSourceInfoLE == NULL || pSourceInfo->m_idLine > pSourceInfoLE->m_idLine)) { pSourceInfoLE = pSourceInfo; } } } // We had better be able to map all line numbers to offsets, unless they passed a bogus line // (in which case we still find an offset) // Assert (pSourceInfoLE != NULL); if (pSourceInfoLE == NULL) { return E_FAIL; } *pcCharacterPosition = pSourceInfoLE->m_cchSourceOffset; #if 0 IF_DEBUG(SCRIPT_DEBUGGER) { wchar_t wszSourceText[SNIPPET_SIZE + 1], wszTargetText[SNIPPET_SIZE + 1], wszDebugMessage[256]; GetScriptSnippets( pSourceInfoLE->m_cchSourceOffset, pSourceInfoLE->m_pfilemap, 0, 0, wszSourceText, NULL ); DBGPRINTF(( DBG_CONTEXT, "Source Line %d corresponds to source offset %d (Text: \"%S\")\n", cLineNumber - 1, pSourceInfoLE->m_cchSourceOffset, wszSourceText )); } #endif return S_OK; } /* ============================================================================ CTemplate::GetLineOfPosition Get the line # & offset in line of an arbitrary character offset in source (Debugger API Extended to specify a filemap) */ HRESULT CTemplate::GetLineOfPosition ( CFileMap *pFilemap, ULONG cCharacterPosition, ULONG *pcLineNumber, ULONG *pcCharacterOffsetInLine ) { // FAIL if source offset totally off-base if (cCharacterPosition >= pFilemap->m_cChars) return E_FAIL; // NOTE: // The table is not binary-searchable because include files // will start a new line ordering // // Algorithm: // // Find the largest source line N across all engines, such that // N <= cLineNumber and the line corresponds to an line // in the appropriate file. // CSourceInfo *pSourceInfoLE = NULL; // Find the correct offset for (unsigned idEngine = 0; idEngine < m_cScriptEngines; ++idEngine) { vector *prgSourceInfos = &m_rgrgSourceInfos[idEngine]; // Loop through all lines EXCEPT the EOF line for (unsigned j = 0; j < prgSourceInfos->length() - 1; ++j) { CSourceInfo *pSourceInfo = &(*prgSourceInfos)[j]; if (pFilemap == pSourceInfo->m_pfilemap && pSourceInfo->m_cchSourceOffset <= cCharacterPosition && (pSourceInfoLE == NULL || pSourceInfo->m_cchSourceOffset > pSourceInfoLE->m_cchSourceOffset)) { pSourceInfoLE = pSourceInfo; } } } // We had better be able to map all offsets to line numbers, unless they passed a bogus offset // (in which case we still find a line #, but may go out of range for the offset in line. // That case is handled later) // Assert (pSourceInfoLE != NULL); if (pSourceInfoLE == NULL) { return E_FAIL; } *pcLineNumber = pSourceInfoLE->m_idLine - 1; // Convert to zero-based line # *pcCharacterOffsetInLine = cCharacterPosition - pSourceInfoLE->m_cchSourceOffset; #if 0 IF_DEBUG(SCRIPT_DEBUGGER) { wchar_t wszSourceText[SNIPPET_SIZE + 1], wszTargetText[SNIPPET_SIZE + 1], wszDebugMessage[256]; GetScriptSnippets( pSourceInfoLE->m_cchSourceOffset, pSourceInfoLE->m_pfilemap, 0, 0, wszSourceText, NULL ); DBGPRINTF(( DBG_CONTEXT, "Source offset %d corresponds to source line %d (Text: \"%S\")\n", pSourceInfoLE->m_cchSourceOffset, *pcLineNumber, wszSourceText )); } DBGPRINTF(( DBG_CONTEXT, "Source offset %d corresponds to source line %d (Text: \"%S\")\n", pSourceInfoLE->m_cchSourceOffset, *pcLineNumber, wszSourceText )); } #endif return S_OK; } /* ============================================================================ CTemplate::GetSourceOffset Convert a character offset relative to the target script to the appropriate offset in the source. NOTE: offsets in the middle of a target line are converted to the offset relative to the beginning of source line - NOT to the precise source offset. this is OK because debugger ultimately wants the offset of the beginning of line. It is a lot of work to do the precise conversion due to the translation of "=" to Response.Write & HTML to Response.WriteBlock Also, because of these translations, we return the length of the segment calculated during compilation, and throw away the length the scripting engine sent to us. */ void CTemplate::GetSourceOffset ( ULONG idEngine, ULONG cchTargetOffset, TCHAR **pszSourceFile, ULONG *pcchSourceOffset, ULONG *pcchSourceText ) { Assert (idEngine < m_cScriptEngines); vector *prgSourceInfos = &m_rgrgSourceInfos[idEngine]; // Find the closest offset in the source // This is the largest target offset N, such that N <= cchTargetOffset CSourceInfo *pSourceInfo; GetBracketingPair( cchTargetOffset, // value to search for prgSourceInfos->begin(), prgSourceInfos->end(), // array to search CTargetOffsetOrder(), // ordering predicate &pSourceInfo, static_cast(NULL) // return values ); // Since the first offset is zero, which is less than all other conceivable offsets, // the offset must have been found or else there is a bug. Assert (pSourceInfo != NULL); Assert (cchTargetOffset >= pSourceInfo->m_cchTargetOffset); #if 0 IF_DEBUG(SCRIPT_DEBUGGER) { wchar_t wszSourceText[SNIPPET_SIZE + 1], wszTargetText[SNIPPET_SIZE + 1], wszDebugMessage[256]; GetScriptSnippets( pSourceInfo->m_cchSourceOffset, pSourceInfo->m_pfilemap, cchTargetOffset, idEngine, wszSourceText, wszTargetText ); DBGPRINTF(( DBG_CONTEXT, "Target offset %d (Text: \"%S\") corresponds to source offset %d (Text: \"%S\") (Length is %d)\n", cchTargetOffset, wszTargetText, pSourceInfo->m_cchSourceOffset, wszSourceText, pSourceInfo->m_cchSourceText )); } #endif *pszSourceFile = pSourceInfo->m_pfilemap->m_szPathTranslated; *pcchSourceOffset = pSourceInfo->m_cchSourceOffset; *pcchSourceText = pSourceInfo->m_cchSourceText; } /* ============================================================================ CTemplate::GetTargetOffset Convert a character offset relative to the source script to the appropriate offset in the target. Returns: TRUE - source offset corresponds to script FALSE - source offset corresponds to HTML NOTES: 1. This function is very slow. consider caching the value of this function (The CTemplateDocumentContext class does this.) 2. This function returns the source offset in the master include file - if the target offset corresponds to an offset in a header file, then the offset to the #include line in the source is returned. 3. offsets in the middle of a target line are converted to the offset relative to the beginning of source line - NOT to the precise source offset. this is OK because the debugger ultimately wants the offset of the beginning of line. It is a lot of work to do the precise conversion due to the translation of "=" to Response.Write & HTML to Response.WriteBlock CONSIDER: Figure out a better way to do this */ BOOL CTemplate::GetTargetOffset ( TCHAR *szSourceFile, ULONG cchSourceOffset, /* [out] */ ULONG *pidEngine, /* [out] */ ULONG *pcchTargetOffset ) { // NOTE: // The table is not binary-searchable because of two factors: // 1. Include files will start a new line ordering // 2. For engine 0, tagged scripts will be re-arranged in // the target code to reside after all primary script in // engine 0. // // Algorithm: // // Find the largest source offset N across all engines, such that // N <= cchSourceOffset and the offset corresponds to an offset // in the appropriate file. // CSourceInfo *pSourceInfoLE = NULL; unsigned idEngineLE = 0; // Find the correct offset for (unsigned idEngine = 0; idEngine < m_cScriptEngines; ++idEngine) { vector *prgSourceInfos = &m_rgrgSourceInfos[idEngine]; // Loop through all lines EXCEPT the EOF line for (unsigned j = 0; j < prgSourceInfos->length() - 1; ++j) { CSourceInfo *pSourceInfo = &(*prgSourceInfos)[j]; if (_tcscmp(pSourceInfo->m_pfilemap->m_szPathTranslated, szSourceFile) == 0 && pSourceInfo->m_cchSourceOffset <= cchSourceOffset && (pSourceInfoLE == NULL || pSourceInfo->m_cchSourceOffset > pSourceInfoLE->m_cchSourceOffset)) { pSourceInfoLE = pSourceInfo; idEngineLE = idEngine; } } } // There won't be a valid offset in the case where there is no // code corresponding to the first line in the file (this only // occurs when the first line is whitespace, because there is no // corresponding "Response.WriteBlock" call there) // // In that case, return FALSE, which will cause the caller to fail // if (pSourceInfoLE == NULL) { *pidEngine = 0; *pcchTargetOffset = 0; return FALSE; } *pidEngine = idEngineLE; *pcchTargetOffset = pSourceInfoLE->m_cchTargetOffset; #if 0 IF_DEBUG(SCRIPT_DEBUGGER) { wchar_t wszSourceText[SNIPPET_SIZE + 1], wszTargetText[SNIPPET_SIZE + 1], wszDebugMessage[256]; GetScriptSnippets( cchSourceOffset, pSourceInfoLE->m_pfilemap, *pcchTargetOffset, *pidEngine, wszSourceText, wszTargetText ); DBGPRINTF(( DBG_CONTEXT, "Source offset %d (Text: \"%S\") corresponds to target offset %d (Text: \"%S\")\n", cchSourceOffset, wszSourceText, *pcchTargetOffset, wszTargetText )); } #endif return !pSourceInfoLE->m_fIsHTML; } /* ============================================================================ CTemplate::GetActiveScript Return a cached script from the template - only used in debug mode */ CActiveScriptEngine *CTemplate::GetActiveScript(ULONG idEngine) { if (m_rgpDebugScripts == NULL) return NULL; else { Assert (idEngine < m_cScriptEngines); CActiveScriptEngine *pEng = m_rgpDebugScripts[idEngine]; if (pEng) pEng->AddRef(); return pEng; } } /* ============================================================================ CTemplate::AddScript add an active script to the template object */ HRESULT CTemplate::AddScript(ULONG idEngine, CActiveScriptEngine *pScriptEngine) { if (m_rgpDebugScripts == NULL) { if ( (m_rgpDebugScripts = new CActiveScriptEngine *[m_cScriptEngines]) == NULL ) { return E_OUTOFMEMORY; } memset(m_rgpDebugScripts, 0, m_cScriptEngines * sizeof(CActiveScriptEngine *)); } Assert (idEngine < m_cScriptEngines); CActiveScriptEngine **ppScriptElem = &m_rgpDebugScripts[idEngine]; if (*ppScriptElem != NULL) (*ppScriptElem)->Release(); *ppScriptElem = pScriptEngine; pScriptEngine->AddRef(); // Initialize the script engine now (is currently uninitialized) // so that the debugger user can set breakpoints. IActiveScript *pActiveScript = pScriptEngine->GetActiveScript(); HRESULT hr; TRY hr = pActiveScript->SetScriptSite(static_cast(pScriptEngine)); CATCH(nExcept) HandleErrorMissingFilename(IDE_SCRIPT_ENGINE_GPF, NULL, TRUE, nExcept, "IActiveScript::SetScriptSite()", "CTemplate::AddScript()"); hr = nExcept; END_TRY if (FAILED(hr)) { *ppScriptElem = NULL; return E_FAIL; } TRY hr = pActiveScript->SetScriptState(SCRIPTSTATE_INITIALIZED); CATCH(nExcept) HandleErrorMissingFilename(IDE_SCRIPT_ENGINE_GPF, NULL, TRUE, nExcept, "IActiveScript::SetScriptState()", "CTemplate::AddScript()"); hr = nExcept; END_TRY if (FAILED(hr)) return E_FAIL; return S_OK; } /* ============================================================================ CTemplate::AppendMapFile Appends a filemap to the workstore and memory-maps its file Returns: Nothing Side effects: Allocates memory; throws exception on error */ void CTemplate::AppendMapFile ( LPCTSTR szFileSpec, // file spec for this file CFileMap* pfilemapCurrent, // ptr to filemap of parent file BOOLB fVirtual, // is file spec virtual or relative? CHitObj* pHitObj, // ptr to template's hit object BOOLB fGlobalAsa // is this file the global.asa file? ) { // alloc or realloc as needed if(m_cFilemaps++ == 0) m_rgpFilemaps = (CFileMap**) CTemplate::SmallMalloc(sizeof(CFileMap*)); else m_rgpFilemaps = (CFileMap**) CTemplate::SmallReAlloc(m_rgpFilemaps, m_cFilemaps * sizeof(CFileMap*)); if(NULL == m_rgpFilemaps) THROW(E_OUTOFMEMORY); if(NULL == (m_rgpFilemaps[m_cFilemaps - 1] = new CFileMap)) THROW(E_OUTOFMEMORY); // map the file m_rgpFilemaps[m_cFilemaps - 1]->MapFile( szFileSpec, m_szApplnVirtPath, pfilemapCurrent, fVirtual, pHitObj, fGlobalAsa ); } /* ============================================================================ CTemplate::GetSegmentsFromFile Gets source segments from a source file by calling ExtractAndProcessSegment until there are no more segments; populates WorkStore with info on source segments. Returns: Nothing Side effects: None */ void CTemplate::GetSegmentsFromFile ( CFileMap& filemap, // this file's file map CWorkStore& WorkStore, // working storage for source segments CHitObj* pHitObj, // Browser request object BOOL fIsHTML ) { CByteRange brSearch; // byte range to search for source segments _TOKEN rgtknOpeners[TOKEN_OPENERS_MAX]; // array of permitted open tokens UINT ctknOpeners; // count of permitted open tokens SOURCE_SEGMENT ssegThisFile = ssegHTML; // Either HTML or "); AppendToken(tknCloseObject, ""); AppendToken(tknCloseHTMLComment, "-->"); AppendToken(tknEscapedClosePrimaryScript, "%\\>"); AppendToken(tknCloseTag, ">"); AppendToken(tknCommandINCLUDE, "#INCLUDE"); AppendToken(tknTagRunat, "RUNAT"); AppendToken(tknTagLanguage, "LANGUAGE"); AppendToken(tknTagCodePage, "CODEPAGE"); AppendToken(tknTagCodePage, "LCID"); AppendToken(tknTagTransacted, "TRANSACTION"); AppendToken(tknTagSession, "ENABLESESSIONSTATE"); AppendToken(tknTagID, "ID"); AppendToken(tknTagClassID, "CLASSID"); AppendToken(tknTagProgID, "PROGID"); AppendToken(tknTagScope, "SCOPE"); AppendToken(tknTagVirtual, "VIRTUAL"); AppendToken(tknTagFile, "FILE"); AppendToken(tknTagMETADATA, "METADATA"); // AppendToken(tknTagSetPriScriptLang, "@"); AppendToken(tknTagName, "NAME"); AppendToken(tknValueTypeLib, "TYPELIB"); AppendToken(tknTagType, "TYPE"); AppendToken(tknTagUUID, "UUID"); AppendToken(tknTagVersion, "VERSION"); AppendToken(tknTagStartspan, "STARTSPAN"); AppendToken(tknTagEndspan, "ENDSPAN"); AppendToken(tknValueCookie, "COOKIE"); AppendToken(tknTagSrc, "SRC"); AppendToken(tknValueServer, "Server"); AppendToken(tknValueApplication, "Application"); AppendToken(tknValueSession, "Session"); AppendToken(tknValuePage, "Page"); AppendToken(tknVBSCommentSQuote, "'"); AppendToken(tknVBSCommentRem, "REM "); // NOTE ends with space character AppendToken(tknTagFPBot, "webbot"); AppendToken(tknEOF, ""); AppendToken(tkncAll, ""); } /* ============================================================================ CTemplate::CTokenList::AppendToken Appends a string to tokens buffer NOTE we keep the unused tkn parameter because it enforces consistency and readability in CTemplate::CTokenList::Init(), e.g. AppendToken(tknOpenPrimaryScript, "<%"); rather than AppendToken("<%"); Returns: Nothing Side effects: None */ void CTemplate::CTokenList::AppendToken ( _TOKEN tkn, // token value char* sz // token string ) { // construct byte range from token string CByteRange br; br.m_pb = (BYTE*) sz; br.m_cb = strlen(sz); // append to tokens buffer as local string m_bufTokens.Append(br, TRUE, 0, NULL, TRUE); } /* ============================================================================ CTemplate::CTokenList::NextOpenToken Returns value of next open token in search range Returns token value of next open token in search range; ptr to ptr to open token (out-parameter) Side effects None */ _TOKEN CTemplate::CTokenList::NextOpenToken ( CByteRange& brSearch, // search byte range TOKEN* rgtknOpeners, // array of permitted open tokens UINT ctknOpeners, // count of permitted open tokens BYTE** ppbToken, // ptr to ptr to open token (out-parameter) LONG lCodePage ) { BYTE* pbTemp = NULL; // temp pointer _TOKEN tkn = tknEOF; // return value USHORT i; // loop index // Init caller's token ptr to null *ppbToken = NULL; // If input is empty, return if (brSearch.IsNull()) return tkn; // Prepare array of LPSTR pointers to tokens. // Do it here once, because to get LPSTR is not free. LPSTR rgszTokens[TOKEN_OPENERS_MAX]; UINT rgcchTokens[TOKEN_OPENERS_MAX]; Assert(ctknOpeners <= TOKEN_OPENERS_MAX); for (i = 0; i < ctknOpeners; i++) { LPSTR pszStr = m_bufTokens.PszLocal((UINT)(rgtknOpeners[i])); rgszTokens[i] = pszStr; rgcchTokens[i] = (pszStr != NULL) ? strlen(pszStr) : 0; } // Call a method to find one of the strings in the range UINT idToken; pbTemp = brSearch.PbOneOfAspOpenerStringTokens( rgszTokens, rgcchTokens, ctknOpeners, &idToken); if (pbTemp != NULL) { *ppbToken = pbTemp; tkn = rgtknOpeners[idToken]; } // If we found no open token, position token pointer at end of search range if (tkn == tknEOF) *ppbToken = brSearch.m_pb + brSearch.m_cb; return tkn; } /* ============================================================================ CTemplate::CTokenList::MovePastToken Moves a byte range past a token contained within it */ void CTemplate::CTokenList::MovePastToken ( _TOKEN tkn, BYTE* pbToken, CByteRange& brSearch ) { Assert(pbToken >= brSearch.m_pb); Assert(brSearch.m_cb >= (DIFF(pbToken - brSearch.m_pb) + CCH_TOKEN_X(tkn))); brSearch.Advance(DIFF(pbToken - brSearch.m_pb) + CCH_TOKEN_X(tkn)); } /* ============================================================================ CTemplate::CTokenList::GetToken Gets the next occurrence of a token within a byte range. Returns: ptr to token Side effects none */ BYTE* CTemplate::CTokenList::GetToken ( TOKEN tkn, CByteRange& brSearch, LONG lCodePage ) { return brSearch.PbString(m_bufTokens.PszLocal((UINT)tkn), lCodePage); } /* ============================================================================ The Big Three for CTemplateConnPt NOTES: Since this interface is embedded in CTemplate, AddRef() and Release() delegate to the container object (because that is the CTemplate pointer) */ HRESULT CTemplateConnPt::QueryInterface(const GUID &uidInterface, void **ppvObj) { if (uidInterface == IID_IUnknown || uidInterface == IID_IConnectionPoint) { *ppvObj = this; AddRef(); return S_OK; } else { *ppvObj = NULL; return E_NOINTERFACE; } } ULONG CTemplateConnPt::AddRef() { return m_pUnkContainer->AddRef(); } ULONG CTemplateConnPt::Release() { return m_pUnkContainer->Release(); } /* ============================================================================ Constructor for CDocNode */ CTemplate::CDocNodeElem::CDocNodeElem(CAppln *pAppln, IDebugApplicationNode *pDocRoot) { Assert (pAppln != NULL); Assert (pDocRoot != NULL); (m_pAppln = pAppln)->AddRef(); (m_pDocRoot = pDocRoot)->AddRef(); } /* ============================================================================ Destructor for CDocNode */ CTemplate::CDocNodeElem::~CDocNodeElem() { m_pAppln->Release(); DestroyDocumentTree(m_pDocRoot); } /* ============================================================================ CTemplate::fIsLangVBScriptOrJScript(USHORT idEngine) This function returns T/F to determine if the requested script engine is VBScript or JScript. This function is used as an indicator to determin if spaces need to be preserved for non MS Scripting languages There is an assumption here that the GUIDs for VBScript and JScript will not change Inputs Index to a script engine Returns BOOL */ BOOLB CTemplate::FIsLangVBScriptOrJScript(USHORT idEngine) { // {b54f3741-5b07-11cf-a4b0-00aa004a55e8} VBScript static const GUID uid_VBScript = {0xb54f3741, 0x5b07, 0x11cf, {0xa4, 0xb0, 0x00, 0xaa, 0x00, 0x4a, 0x55, 0xe8}}; // {f414c260-6ac0-11cf-b6d1-00aa00bbbb58} JavaScript static const GUID uid_JScript = {0xf414c260, 0x6ac0, 0x11cf, {0xb6, 0xd1, 0x00, 0xaa, 0x00, 0xbb, 0xbb, 0x58}}; // {b54f3743-5b07-11cf-a4b0-00aa004a55e8} VBScript.Encode static const GUID uid_VBScriptEncode = {0xb54f3743, 0x5b07, 0x11cf, {0xa4, 0xb0, 0x00, 0xaa, 0x00, 0x4a, 0x55, 0xe8}}; // {f414c262-6ac0-11cf-b6d1-00aa00bbbb58} JavaScript.Encode static const GUID uid_JScriptEncode = {0xf414c262, 0x6ac0, 0x11cf, {0xb6, 0xd1, 0x00, 0xaa, 0x00, 0xbb, 0xbb, 0x58}}; GUID &uidLang = m_pWorkStore->m_ScriptStore.m_rgProgLangId[idEngine]; return uidLang == uid_VBScript || uidLang == uid_VBScriptEncode || uidLang == uid_JScript || uidLang == uid_JScriptEncode; } SIZE_T _RoundUp( SIZE_T dwBytes) { #if 1 // 16KB <= dwBytes? Round up to next multiple of 4KB if (16*1024 <= dwBytes) dwBytes = ((dwBytes + (1<<12) - 1) >> 12) << 12; // 4KB <= dwBytes < 16KB? Round up to next multiple of 1KB else if (4*1024 <= dwBytes) dwBytes = ((dwBytes + (1<<10) - 1) >> 10) << 10; // 1KB <= dwBytes < 4KB? Round up to next multiple of 256 bytes else if (1024 <= dwBytes) dwBytes = ((dwBytes + (1<<8) - 1) >> 8) << 8; // dwBytes < 1KB? Round up to next multiple of 32 bytes else dwBytes = ((dwBytes + (1<<5) - 1) >> 5) << 5; #endif return dwBytes; } void* CTemplate::SmallMalloc(SIZE_T dwBytes) { DBG_ASSERT(sm_hSmallHeap != NULL); dwBytes = _RoundUp(dwBytes); return ::HeapAlloc(sm_hSmallHeap, 0, dwBytes); } void* CTemplate::SmallReAlloc(void* pvMem, SIZE_T dwBytes) { DBG_ASSERT(sm_hSmallHeap != NULL); dwBytes = _RoundUp(dwBytes); return ::HeapReAlloc(sm_hSmallHeap, 0, pvMem, dwBytes); } void CTemplate::SmallFree(void* pvMem) { DBG_ASSERT(sm_hSmallHeap != NULL); ::HeapFree(sm_hSmallHeap, 0, pvMem); } void* CTemplate::LargeMalloc(SIZE_T dwBytes) { DBG_ASSERT(sm_hLargeHeap != NULL); dwBytes = _RoundUp(dwBytes); return ::HeapAlloc(sm_hLargeHeap, 0, dwBytes); } void* CTemplate::LargeReAlloc(void* pvMem, SIZE_T dwBytes) { DBG_ASSERT(sm_hLargeHeap != NULL); dwBytes = _RoundUp(dwBytes); return ::HeapReAlloc(sm_hLargeHeap, 0, pvMem, dwBytes); } void CTemplate::LargeFree(void* pvMem) { DBG_ASSERT(sm_hLargeHeap != NULL); ::HeapFree(sm_hLargeHeap, 0, pvMem); }