// Copyright (C) 1996-1997 Microsoft Corporation. All rights reserved. #include "header.h" #include "sitemap.h" #include #include #ifndef HHCTRL #include "hha_strtable.h" #else #include "hha_strtable.h" #include "hhctrl.h" #include "strtable.h" #endif #include "web.h" #ifdef _DEBUG #undef THIS_FILE static const char THIS_FILE[] = __FILE__; #endif #ifndef _DEBUG #pragma optimize("at", on) #endif #include "sitechar.cpp" #ifndef HHCTRL static const char txtSaveTypeHidden[] = "SaveHidden"; static const char txtSaveTypeExclusive[] = "SaveExclusive"; static const char txtSaveType[] = "SaveType"; static const char txtSaveTypeDesc[] = "SaveTypeDesc"; #endif class CParseSitemap : public SITEMAP_ENTRY { public: CParseSitemap(CSiteMap* pSiteMap) { m_pSiteMap = pSiteMap; Clear(); m_fGlobal = FALSE; m_cMaxTitles = 50; m_ppszTitles = (PCSTR*) lcCalloc(m_cMaxTitles * sizeof(PCSTR)); m_pSiteUrl = new CSiteEntryUrl(pSiteMap->InfoTypeSize()); } ~CParseSitemap() { lcFree(m_ppszTitles); delete m_pSiteUrl; } BOOL IsGlobal(void) { return m_fGlobal; } void SetGlobal(BOOL fSetting) { m_fGlobal = fSetting; } void AddTitle(PCSTR psz); PCSTR* m_ppszTitles; int m_cMaxTitles; CStr m_cszValue; BOOL m_fGlobal; CSiteMap* m_pSiteMap; CSiteEntryUrl* m_pSiteUrl; void SaveUrlEntry() { m_pSiteUrl->SaveUrlEntry(m_pSiteMap, this); } operator CStr*() { return &m_cszValue; } operator PSTR() { return m_cszValue.psz; } operator PCSTR() const { return m_cszValue.psz; } }; #ifndef _DEBUG // REVIEW: check this from time to time to see if it is necessary... #pragma optimize("a", on) #endif // Function prototypes PSTR FindDblQuote(CAInput* painput, CStr* pcsz, PSTR psz); PSTR GetValue(CAInput* pinput, CStr* pcsz, PSTR psz, CStr* pcszValue); PSTR FindCharacter(CAInput* pinput, CStr* pcsz, PSTR psz, char ch); PSTR GetType(CAInput* pinput, CStr* pcsz, PSTR psz, CStr* pcszValue); #ifdef HHCTRL #ifdef HHA BOOL CSiteMap::ReadFromFile(PCSTR pszFileName, BOOL fIndex, CHtmlHelpControl* phhctrl ) #else BOOL CSiteMap::ReadFromFile(PCSTR pszFileName, BOOL fIndex, CHtmlHelpControl* phhctrl, UINT CodePage ) #endif #else BOOL CSiteMap::ReadFromFile(PCSTR pszFileName, BOOL fIndex, PCSTR pszBaseFile) #endif { #ifdef HHCTRL #ifndef HHA m_CodePage = CodePage; #endif #endif CAInput ainput; int curInput = 0; if (!ainput.Add(pszFileName)) { #ifdef HHCTRL if (phhctrl) phhctrl->AuthorMsg(IDS_CANT_OPEN, pszFileName); SITEMAP_ENTRY* pSiteMapEntry = AddEntry(); ASSERT_COMMENT(!pSiteMapEntry->iFrameName && !pSiteMapEntry->iWindowName, "Add() function not returning zero'd memory"); pSiteMapEntry->level = 1; CStr cszLine(IDS_CANT_FIND_FILE, pszFileName); pSiteMapEntry->pszText = StrDup(cszLine); pSiteMapEntry->SetTopic(TRUE); #else MsgBox(IDS_CANT_OPEN, pszFileName); #endif return FALSE; } SetSiteMapFile(pszFileName); m_fIndex = fIndex; // We don't want the first file to have a basename #if (defined(HHCTRL) || defined(HHA)) m_pszBase = ainput.GetBaseName(); *m_pszBase = '\0'; #endif // used for indexes CParseSitemap cparse(this); CTable *ptblSubSets=NULL; // To hold any subset declarations. int curLevel = 0; CStr cszLine; BOOL fSiteMap = FALSE; BOOL fMergePending = FALSE; // TRUE if we need to include another file CStr cszMergeFile; // file to merge into current #ifdef _DEBUG int cLoops = 0; // so we know when to set a breakpoint #endif for (;;) { #ifdef _DEBUG cLoops++; // lcHeapCheck(); #endif if (fMergePending) { #if (defined(HHCTRL) || defined(HHA)) char szUrl[INTERNET_MAX_URL_LENGTH]; #endif #ifdef HHA // When compiling, find the full path to the file strcpy(szUrl, cszMergeFile); ConvertToFull(pszBaseFile, szUrl); ainput.Add(szUrl); { #else if (!ainput.Add(cszMergeFile)) { #endif #ifdef HHCTRL if (ConvertToCacheFile(cszMergeFile, szUrl)) ainput.Add(szUrl); else if (phhctrl && phhctrl->m_pWebBrowserApp) { CStr cszUrl; phhctrl->m_pWebBrowserApp->GetLocationURL(&cszUrl); PSTR psz = (PSTR) FindFilePortion(cszUrl); if (psz) { *psz = '\0'; cszUrl += cszMergeFile; if (ConvertToCacheFile(cszUrl, szUrl)) ainput.Add(szUrl); } } #endif } fMergePending = FALSE; #if (defined(HHCTRL) || defined(HHA)) m_pszBase = ainput.GetBaseName(); /* * If we are in the same location as our parent, ignore the * base name */ if (!IsEmptyString(m_pszBase)) { CStr cszBase(m_pszBase); CStr cszSitemap(GetSiteMapFile()); PSTR psz = (PSTR) FindFilePortion(cszSitemap); ASSERT(psz); if (psz) *psz = '\0'; ConvertBackSlashToForwardSlash(cszBase); ConvertBackSlashToForwardSlash(cszSitemap); if (lstrcmpi(cszBase, cszSitemap) == 0) m_pszBase = NULL; } #endif } if (curInput < 0) break; if (!ainput.getline(&cszLine)) { if (!ainput.Remove()) { if (m_fIndex) return TRUE; break; // we're all done. } /* * Set parse to global so that if this was part of a merged * file we won't rewrite the last item when an is * encountered. */ // 16-Dec-1997 [ralphw] Don't call SetGlobal -- it deletes the // next folder // cparse.SetGlobal(TRUE); cparse.Clear(); #if (defined(HHCTRL) || defined(HHA)) m_pszBase = ainput.GetBaseName(); #endif continue; } PSTR psz = FirstNonSpace(cszLine); Loop: if ( !psz || !*psz) continue; // ignore blank lines if (*psz == '<') { int cb; if ((cb = CompareSz(psz, txtParam))) { psz = FindDblQuote(&ainput, &cszLine, psz + cb); if (!psz) goto FlushAndExit; PSTR pszParamType = psz; psz = GetValue(&ainput, &cszLine, psz, cparse); if (!psz) goto FlushAndExit; if (ParseSiteMapParam(&cparse, pszParamType, ptblSubSets)) { goto Loop; } else if (isSameString(pszParamType, txtParamHHI)) { m_pszHHIFile = StrDup(cparse.m_cszValue.psz); goto Loop; } else if (isSameString(pszParamType, txtParamMerge)) { #if (!defined(HHCTRL) && !defined(HHA)) cszMergeFile = GetStringResource(IDS_INCLUDED_FILE); cszMergeFile += cparse.m_cszValue.psz; cparse.pszText = StrDup(cszMergeFile); cparse.fInclude = TRUE; cparse.iImage = IMAGE_CLOSED_FOLDER; AddEntry(&cparse); cparse.SetGlobal(TRUE); // so end object doesn't rewrite #elif defined(HHA) if (stristr(cparse.m_cszValue.psz, ".CHM")) { cparse.pszText = StrDup(cparse.m_cszValue.psz); cparse.fInclude = TRUE; cparse.iImage = IMAGE_CLOSED_FOLDER; AddEntry(&cparse); } else { cszMergeFile = cparse.m_cszValue.psz; fMergePending = TRUE; } #else cszMergeFile = cparse.m_cszValue.psz; fMergePending = TRUE; #endif goto Loop; } else if (!m_fIndex && isSameString(pszParamType, txtFavorites)) { #if (defined(HHCTRL) || defined(HHA)) CreateFavorites(); goto Loop; #endif // REVIEW: how do we deal with this in HHW? } } else if ((cb = CompareSz(psz, txtBeginList))) { curLevel++; psz = FirstNonSpace(psz + cb); goto Loop; } else if ((cb = CompareSz(psz, txtEndList))) { curLevel--; psz = FirstNonSpace(psz + cb); goto Loop; } else if ((cb = CompareSz(psz, txtBeginListItem))) { psz = FindCharacter(&ainput, &cszLine, psz + cb, '>'); if (!psz) goto FlushAndExit; if ((cb = CompareSz(psz, txtBeginObject))) { psz = GetType(&ainput, &cszLine, psz + cb, cparse); if (!psz) { if (!ainput.getline(&cszLine)) goto FlushAndExit; psz = GetType(&ainput, &cszLine, cszLine, cparse); if (!psz) goto FlushAndExit; } //////////////////// New Node ///////////////////////////////////////////// if (isSameString(cparse, txtSiteMapObject)) { // Create a new entry cparse.Clear(); cparse.level = (BYTE)curLevel; goto Loop; } else if (isSameString(cparse, txtSitemapProperties)) { cparse.SetGlobal(TRUE); } } } else if ((cb = CompareSz(psz, txtEndObject))) { //////////////////// End Node ///////////////////////////////////////////// if (!cparse.IsGlobal()) { if (m_fIndex) { if (!cparse.m_pSiteUrl->m_fUrlSpecified) { // BUGBUG: nag the author -- entry without a URL psz += cb; goto Loop; } cparse.SaveUrlEntry(); if (cparse.pszText && cparse.cUrls) { AddEntry(&cparse); } else { #if (defined(HHCTRL) || defined(HHA)) cparse.pszText = StrDup(GetStringResource(IDSHHA_INVALID_KEYWORD)); AddEntry(&cparse); #endif // we ignore the entry entirely in hhctrl.ocx } cparse.Clear(); cparse.level = (BYTE)curLevel; } else { if (cparse.m_pSiteUrl->m_fUrlSpecified) cparse.SaveUrlEntry(); AddEntry(&cparse); } } else { cparse.SetGlobal(FALSE); //#ifdef HHCTRL // having this conditional for HHCTRL causes IT declarations to not be present in HHW until the info type tab is visited // bug 4762 if (IsNonEmpty(m_pszHHIFile)) { cszMergeFile = cparse.m_cszValue.psz; fMergePending = TRUE; m_pszHHIFile = NULL; // so we don't merge twice } //#endif } psz += cb; goto Loop; } else if (isSameString(psz, txtSitemap) || isSameString(psz, txtSitemap1) || isSameString(psz, txtSitemap2)) { fSiteMap = TRUE; psz = FindCharacter(&ainput, &cszLine, psz, '>'); if (!psz) goto FlushAndExit; else if (!*psz) continue; } // Global properties? else if ((cb = CompareSz(psz, txtBeginObject))) { psz = GetType(&ainput, &cszLine, psz + cb, cparse); if (!psz) { if (!ainput.getline(&cszLine)) goto FlushAndExit; psz = GetType(&ainput, &cszLine, psz + cb, cparse); if (!psz) goto FlushAndExit; } if (isSameString(cparse, txtSitemapProperties)) { cparse.SetGlobal(TRUE); goto Loop; } else { // ******** Check for Merge (include another file) ******* /* * This is an object that appears outside of a list * item. If it's a merge tag, then extract the merge * information. Otherwise, ignore it. */ psz = strstr(psz, txtEndTag); if (psz) psz = FirstNonSpace(psz + 1); for (;;) { if (!psz) { if (!ainput.getline(&cszLine)) goto FlushAndExit; psz = FirstNonSpace(cszLine.psz); } if ( !psz || !*psz) { psz = NULL; // so we'll read the next line continue; // ignore blank lines } if (*psz == '<') { if ((cb = CompareSz(psz, txtEndObject))) { psz += cb; goto Loop; } else if ((cb = CompareSz(psz, txtParam))) { psz = FindDblQuote(&ainput, &cszLine, psz + cb); if (!psz) goto FlushAndExit; PSTR pszParamType = psz; psz = GetValue(&ainput, &cszLine, psz, cparse); if (!psz) goto FlushAndExit; if (isSameString(pszParamType, txtParamMerge)) { #if (!defined(HHCTRL) && !defined(HHA)) cszMergeFile = GetStringResource(IDS_INCLUDED_FILE); cszMergeFile += cparse.m_cszValue.psz; cparse.pszText = StrDup(cszMergeFile); cparse.fInclude = TRUE; cparse.iImage = IMAGE_CLOSED_FOLDER; cparse.level = curLevel; AddEntry(&cparse); #elif defined(HHA) if (stristr(cparse.m_cszValue.psz, ".CHM")) { cparse.pszText = StrDup(cparse.m_cszValue.psz); cparse.fInclude = TRUE; cparse.iImage = IMAGE_CLOSED_FOLDER; cparse.level = curLevel; AddEntry(&cparse); } else { cszMergeFile = cparse.m_cszValue.psz; fMergePending = TRUE; } #else cszMergeFile = cparse.m_cszValue.psz; fMergePending = TRUE; #endif } } } } } } else if (isSameString(psz, txtBeginHref)) { // Skip over all of HREF for (;;) { psz = strstr(psz, txtEndTag); if (psz) { if (strncmp(psz, txtEndHref, (int)strlen(txtEndHref)) == 0) break; psz = stristr(psz, txtEndHref); } if (!psz) { if (!ainput.getline(&cszLine)) goto FlushAndExit; psz = cszLine.psz; } else break; } psz = FindCharacter(&ainput, &cszLine, psz, '>'); goto Loop; } else { // Ignore whatever it is psz = FindCharacter(&ainput, &cszLine, psz, '>'); goto Loop; } } if (fSiteMap) { // In sitemaps, we only care about what appears inside angle // brackets. for(;;) { psz = StrChr(psz, '<'); if (!psz) { if (!ainput.getline(&cszLine)) { psz = ""; // so Loop processing works break; } psz = cszLine.psz; } else break; } goto Loop; } } # ifdef NOTYET // populate subsets if ( ptblSubSets ) { if ( !m_pSubSets ) m_pSubSets = new CSubSets(m_itTables.m_itSize, ptblSubSets, this, TRUE); delete ptblSubSets; } #endif if (!fIndex) { // Now determine what is a book -- anything without a child is a topic curLevel = 0; int pos; for (pos = 1; pos < endpos - 1; pos++) { SITEMAP_ENTRY* pSiteMapEntry = GetSiteMapEntry(pos); if (pSiteMapEntry->level > curLevel + 1) pSiteMapEntry->level = curLevel + 1; curLevel = pSiteMapEntry->level; if (pSiteMapEntry->cUrls == 0) continue; // if there is no URL, then it's a book #ifdef _DEBUG SITEMAP_ENTRY* pSiteMapEntryNext = GetSiteMapEntry(pos + 1); #endif if (pSiteMapEntry->level >= GetSiteMapEntry(pos + 1)->level) pSiteMapEntry->SetTopic(TRUE); } // You can never end with a book if (endpos > 1) GetSiteMapEntry(endpos - 1)->SetTopic(TRUE); } return TRUE; FlushAndExit: return FALSE; } BOOL CSiteMap::ParseSiteMapParam(CParseSitemap* pcparse, PCSTR pszParamType, CTable *ptblSubSets) { int iLastType = 0; // Deal with any escape sequences starting with '&' ReplaceEscapes(*pcparse, (PSTR) *pcparse, ESCAPE_ENTITY); if (pcparse->IsGlobal()) { int cb; // WARNING! Check for txtParamTypeDesc BEFORE the check for txtParamType // (because it's a substring search). #ifdef NOTYET // parse Subset Declarations if ( isSameString( pszParamType, txtParamSSInclusive ) || isSameString( pszParamType, txtParamSSExclusive)) { if( !ptblSubSets ) ptblSubsets = new CTable(16 * 1024); ptblSubSets->AddString(*pcparse); }else #endif // parse Information Types Declarations if (isSameString(pszParamType, txtParamTypeExclusive) #ifndef HHCTRL || isSameString(pszParamType, txtSaveTypeExclusive) #endif ) { if (m_itTables.m_ptblInfoTypes && m_itTables.m_ptblInfoTypes->IsStringInTable(*pcparse)) { iLastType = GetITIndex( *pcparse ); AddToCategory( iLastType ); return TRUE; }else { iLastType = AddTypeName(*pcparse); AddExclusiveIT( iLastType ); AddToCategory( iLastType ); return TRUE; } } else if (isSameString(pszParamType, txtParamTypeHidden) #ifndef HHCTRL || isSameString(pszParamType, txtSaveTypeHidden) #endif ) { if (m_itTables.m_ptblInfoTypes && m_itTables.m_ptblInfoTypes->IsStringInTable(*pcparse)) { iLastType = GetITIndex( *pcparse ); AddToCategory( iLastType ); return TRUE; }else { iLastType = AddTypeName(*pcparse); AddHiddenIT( iLastType ); AddToCategory( iLastType ); return TRUE; } } else if ((cb = CompareSz(pszParamType, txtParamTypeDesc)) #ifndef HHCTRL || isSameString(pszParamType, txtSaveTypeDesc) #endif ) { if (!m_itTables.m_ptblInfoTypeDescriptions) return TRUE; // BUGBUG: nag author about description before type // add a blank description to infotype description, description not provided for last infotype while (m_itTables.m_ptblInfoTypes->CountStrings() > m_itTables.m_ptblInfoTypeDescriptions->CountStrings()+1) m_itTables.m_ptblInfoTypeDescriptions->AddString(""); if ( m_itTables.m_ptblInfoTypes->CountStrings() != m_itTables.m_ptblInfoTypeDescriptions->CountStrings() ) m_itTables.m_ptblInfoTypeDescriptions->AddString(*pcparse); return TRUE; } // this matches anything that begins with "type" else if (isSameString(pszParamType, txtParamType) #ifndef HHCTRL || isSameString(pszParamType, txtSaveType) #endif ) { if (m_itTables.m_ptblInfoTypes && m_itTables.m_ptblInfoTypes->IsStringInTable(*pcparse)) { iLastType = GetITIndex( *pcparse ); AddToCategory( iLastType ); return TRUE; } else { iLastType = AddTypeName(*pcparse); AddToCategory( iLastType ); return TRUE; } } // WARNING! Check for txtParamCategoryDesc BEFORE the check for txtParamCategory // (because it's a substring search). else if ((cb = CompareSz(pszParamType, txtParamCategoryDesc))) { if (!m_itTables.m_ptblCatDescription) return TRUE; // BUGBUG: nag author about description before type // add a blank description to category description, description not provided for last Category while (m_itTables.m_ptblCategories->CountStrings() > m_itTables.m_ptblCatDescription->CountStrings()+1) m_itTables.m_ptblCatDescription->AddString(""); m_itTables.m_ptblCatDescription->AddString(*pcparse); return TRUE; } else if (isSameString(pszParamType, txtParamCategory)) { if (m_itTables.m_ptblCategories && m_itTables.m_ptblCategories->IsStringInTable(*pcparse)) { // BUGBUG: we should nag the help author return TRUE; } if (!m_itTables.m_ptblCategories) { m_itTables.m_ptblCategories = new CTable(16 * 1024); m_itTables.m_ptblCatDescription = new CTable(16 * 1024); } m_itTables.m_ptblCategories->AddString(*pcparse); return TRUE; } else if (isSameString(pszParamType, txtParamFrame)) { if (!IsFrameDefined()) // can't override in a sitemap file SetFrameName(*pcparse); return TRUE; } else if (isSameString(pszParamType, txtParamWindow)) { if (!IsWindowDefined()) SetWindowName(*pcparse); return TRUE; } else if (isSameString(pszParamType, txtImageType)) { if (isSameString(*pcparse, txtFolderType)) m_fFolderImages = TRUE; } else if (isSameString(pszParamType, txtImageList)) { m_pszImageList = StrDup(*pcparse); return TRUE; } else if (isSameString(pszParamType, txtQueryType)) { m_fPromotForInfoTypes = TRUE; return TRUE; } else if (isSameString(pszParamType, txtBackGround)) { m_clrBackground = Atoi(*pcparse); return TRUE; } else if (isSameString(pszParamType, txtBackGroundImage)) { m_pszBackBitmap = StrDup(*pcparse); return TRUE; } else if (isSameString(pszParamType, txtForeGround)) { m_clrForeground = Atoi(*pcparse); return TRUE; } else if (isSameString(pszParamType, txtFont)) { m_pszFont = StrDup(*pcparse); return TRUE; } else if (isSameString(pszParamType, txtNumberImages)) { m_cImages = Atoi(*pcparse); return TRUE; } else if (isSameString(pszParamType, txtColorMask)) { m_clrMask = Atoi(*pcparse); return TRUE; } else if (isSameString(pszParamType, txtImageWidth)) { m_cImageWidth = Atoi(*pcparse); return TRUE; } else if (isSameString(pszParamType, txtWindowStyles) && m_tvStyles == (DWORD) -1) { m_tvStyles = Atoi(*pcparse); return TRUE; } else if (isSameString(pszParamType, txtExWindowStyles)) { m_exStyles = Atoi(*pcparse); return TRUE; } #if (!defined(HHCTRL) && !defined(HHA)) else if (isSameString(pszParamType, txtAutoGenerated)) { m_fAutoGenerated = YesNo(*pcparse); return TRUE; } #endif return FALSE; } if (m_fIndex) { if (isSameString(pszParamType, txtParamSectionTitle) || isSameString(pszParamType, txtParamName)) { if (!pcparse->pszText) pcparse->pszText = StrDup(*pcparse); else { if (pcparse->m_pSiteUrl->m_pUrl->pszTitle) { if (pcparse->m_pSiteUrl->m_fUrlSpecified) pcparse->SaveUrlEntry(); else FreeMemory(pcparse->pszText, -1); } pcparse->AddTitle(*pcparse); } return TRUE; } else if (isSameString(pszParamType, txtParamKeyword)) { // BUGBUG: check for duplicates first! pcparse->pszText = StrDup(*pcparse); return TRUE; } } else { // not an Index if (isSameString(pszParamType, txtParamName)) { if (pcparse->m_pSiteUrl->m_fUrlSpecified) pcparse->SaveUrlEntry(); // BUGBUG: deal with duplicate names being specified pcparse->pszText = StrDup(*pcparse); return TRUE; } } if (isSameString(pszParamType, txtParamLocal)) { if (pcparse->m_pSiteUrl->m_fUrlSpecified) { // save previous URL entry pcparse->SaveUrlEntry(); } URL url = AddUrl(*pcparse); if (pcparse->m_pSiteUrl->m_pUrl->urlSecondary == url) return TRUE; // ignore duplicate URLs for primary and secondary pcparse->m_pSiteUrl->m_pUrl->urlPrimary = url; pcparse->m_pSiteUrl->m_fUrlSpecified = TRUE; return TRUE; } else if (isSameString(pszParamType, txtParamUrl) || isSameString(pszParamType, txtParamSecondary)) { if (pcparse->m_pSiteUrl->m_fUrlSpecified && pcparse->m_pSiteUrl->m_pUrl->urlSecondary) { // save previous URL entry pcparse->SaveUrlEntry(); } URL url = AddUrl(*pcparse); if (pcparse->m_pSiteUrl->m_pUrl->urlPrimary == url) return TRUE; // ignore duplicate URLs for primary and secondary pcparse->m_pSiteUrl->m_pUrl->urlSecondary = url; pcparse->m_pSiteUrl->m_fUrlSpecified = TRUE; return TRUE; } else if (isSameString(pszParamType, txtParamType) || isSameString(pszParamType, txtParamTypeExclusive) || isSameString(pszParamType, txtParamTypeHidden)) { if (pcparse->m_pSiteUrl->m_fUrlSpecified) // save previous URL entry pcparse->SaveUrlEntry(); if (m_itTables.m_ptblInfoTypes) { INFOTYPE iType = GetInfoType(*pcparse); if (iType == (INFOTYPE) -1) iType = 0; else AddIT(iType, pcparse->m_pSiteUrl->m_pUrl->ainfoTypes ); } return TRUE; } else if (isSameString(pszParamType, txtParamNew)) { pcparse->fNew = TRUE; return TRUE; } else if (isSameString(pszParamType, txtParamImageNumber)) { pcparse->iImage = (BYTE) Atoi(*pcparse); return TRUE; } else if (isSameString(pszParamType, txtParamDisplay)) { if (isSameString(*pcparse, txtNo)) pcparse->fNoDisplay = TRUE; return TRUE; } else if (isSameString(pszParamType, txtParamFrame)) { SetEntryFrame(pcparse, *pcparse); return TRUE; } else if (isSameString(pszParamType, txtParamWindow)) { SetEntryWindow(pcparse, *pcparse); return TRUE; } else if (isSameString(pszParamType, txtSeeAlso)) { pcparse->m_pSiteUrl->m_pUrl->urlPrimary = AddUrl(*pcparse); pcparse->fSeeAlso = TRUE; pcparse->m_pSiteUrl->m_fUrlSpecified = TRUE; return TRUE; } else if (isSameString(pszParamType, txtSendEvent)) { pcparse->m_pSiteUrl->m_pUrl->urlPrimary = AddUrl(*pcparse); pcparse->SaveUrlEntry(); pcparse->fSendEvent = TRUE; return TRUE; } else if (isSameString(pszParamType, txtSendMessage)) { pcparse->m_pSiteUrl->m_pUrl->urlPrimary = AddUrl(*pcparse); pcparse->SaveUrlEntry(); pcparse->fSendMessage = TRUE; return TRUE; } #if (!defined(HHCTRL) && !defined(HHA)) else if (isSameString(pszParamType, txtParamComment)) { pcparse->pszComment = StrDup(*pcparse); return TRUE; } #endif return FALSE; } URL CSiteMap::AddUrl(PCSTR pszUrl) { if (IsEmptyString(pszUrl)) return NULL; #if (defined(HHCTRL) || defined(HHA)) if (!IsEmptyString(m_pszBase) && !StrChr(pszUrl, ':')) { char szPath[INTERNET_MAX_URL_LENGTH]; strcpy(szPath, m_pszBase); strcat(szPath, pszUrl); return (URL) StrDup(szPath); } #endif PCSTR psz = FirstNonSpace(pszUrl); return (URL) StrDup(psz?psz:""); } void CSiteMap::AddToCategory( int iLastType ) { // Add the info type to the last category defined; if there is a Category if ( m_itTables.m_ptblCategories ) { int posCat = m_itTables.m_ptblCategories->CountStrings(); if (posCat <= 0 ) return; AddITtoCategory( posCat-1, iLastType); } } int CompareSz(PCSTR psz, PCSTR pszSub) { int cb; if (IsSamePrefix(psz, pszSub, cb = (int)strlen(pszSub))) return cb; else return 0; } void CParseSitemap::AddTitle(PCSTR psz) { if (m_pSiteUrl->m_pUrl->pszTitle) m_pSiteMap->FreeMemory(m_pSiteUrl->m_pUrl->pszTitle, -1); m_pSiteUrl->m_pUrl->pszTitle = m_pSiteMap->StrDup(psz); } void CSiteEntryUrl::SaveUrlEntry(CSiteMap* pSiteMap, SITEMAP_ENTRY* pSiteMapEntry) { SITE_ENTRY_URL* pNewUrls = (SITE_ENTRY_URL*) pSiteMap->TableMalloc( sizeof(SITE_ENTRY_URL) * (pSiteMapEntry->cUrls + 1)); if (pSiteMapEntry->cUrls) { ASSERT(pSiteMapEntry->pUrls); memcpy(pNewUrls, pSiteMapEntry->pUrls, sizeof(SITE_ENTRY_URL) * pSiteMapEntry->cUrls); pSiteMap->FreeMemory((PCSTR) pSiteMapEntry->pUrls, sizeof(SITE_ENTRY_URL) * pSiteMapEntry->cUrls); } memcpy((PBYTE) pNewUrls + sizeof(SITE_ENTRY_URL) * pSiteMapEntry->cUrls, m_pUrl, sizeof(SITE_ENTRY_URL)); pSiteMapEntry->cUrls++; pSiteMapEntry->pUrls = pNewUrls; if (!pSiteMap->AreAnyInfoTypesDefined(pSiteMapEntry)) pSiteMapEntry->fShowToEveryOne = TRUE; ZeroMemory(m_pUrl, sizeof(SITE_ENTRY_URL)); m_pUrl->ainfoTypes = (INFOTYPE*)lcCalloc( pSiteMap->InfoTypeSize() ); m_fUrlSpecified = FALSE; } /*************************************************************************** FUNCTION: HashFromSz PURPOSE: Convert a string into a hash representation PARAMETERS: pszKey RETURNS: Hash number COMMENTS: Shamelessly stolen from the WinHelp code, since after 6 years of use by up to 1 million help authors, there were no reports of collisions. MODIFICATION DATES: 10-Aug-1996 [ralphw] Stolen from WinHelp, removed special-case hash characters ***************************************************************************/ // This constant defines the alphabet size for our hash function. static const HASH MAX_CHARS = 43; HASH WINAPI HashFromSz(PCSTR pszKey) { HASH hash = 0; int cch = (int)strlen(pszKey); for (int ich = 0; ich < cch; ++ich) { // treat '/' and '\' as the same if (pszKey[ich] == '/') hash = (hash * MAX_CHARS) + ('\\' - '0'); else if (pszKey[ich] <= 'Z') hash = (hash * MAX_CHARS) + (pszKey[ich] - '0'); else hash = (hash * MAX_CHARS) + (pszKey[ich] - '0' - ('a' - 'A')); } /* * Since the value 0 is reserved as a nil value, if any context * string actually hashes to this value, we just move it. */ return (hash == 0 ? 0 + 1 : hash); } int CSiteMap::AddName(PCSTR psz) { if (IsEmptyString(psz)) return 0; psz = FirstNonSpace(psz); HASH hash = HashFromSz(psz); if (!m_ptblStrings) m_ptblStrings = new CTable(16 * 1024); int pos = m_ptblStrings->IsHashInTable(hash); if (pos > 0) return pos; else { m_ptblStrings->AddString(hash, psz); return m_ptblStrings->CountStrings(); } } PSTR FindCharacter(CAInput* pinput, CStr* pcsz, PSTR psz, char ch) { for(;;) { psz = StrChr(psz, ch); if (!psz) { if (!pinput) return NULL; if (!pinput->getline(pcsz)) return NULL; psz = pcsz->psz; } else break; } return FirstNonSpace(psz + 1); } PSTR FindDblQuote(CAInput* pinput, CStr* pcsz, PSTR psz) { for(;;) { psz = StrChr(psz, '\042'); if (!psz) { if (!pinput->getline(pcsz)) return NULL; psz = pcsz->psz; } else break; } return FirstNonSpace(psz + 1); } /*************************************************************************** FUNCTION: GetValue PURPOSE: Copy the value of a param type into pszValue, and return a pointer to the first character after the closing angle bracket PARAMETERS: pinput pointer to current CInput file pcsz CStr to read new line into psz position in current line pcszValue CStr to receive value pszPrefix prefix to look for RETURNS: COMMENTS: MODIFICATION DATES: 24-Aug-1996 [ralphw] ***************************************************************************/ PSTR GetValue(CAInput* pinput, CStr* pcsz, PSTR psz, CStr* pcszValue) { // Skip past ending quote of parameter type psz = FindDblQuote(pinput, pcsz, psz); if (!psz) return NULL; if (!isSameString(psz, txtValue)) { psz = stristr(psz, txtValue); if (!psz) { if (!pinput->getline(pcsz)) return NULL; psz = stristr(pcsz->psz, txtValue); if (!psz) return NULL; } } // Now find opening quote of the value psz = FindDblQuote(pinput, pcsz, psz); if (!psz) return NULL; if (!pcszValue->psz) pcszValue->psz = (PSTR) lcMalloc(256); PSTR pszDst = pcszValue->psz; PSTR pszEnd = pszDst + lcSize(pcszValue->psz); while (*psz != '\042') { if (!*psz) { if (!pinput->getline(pcsz)) return NULL; psz = pcsz->psz; continue; } *pszDst++ = *psz++; if (pszDst >= pszEnd) { /* * Our destination buffer is too small, so increase it by * 128 bytes. */ int offset = (int)(pszDst - pcszValue->psz); pcszValue->ReSize((int)(pszEnd - pcszValue->psz) + 128); pszDst = pcszValue->psz + offset; pszEnd = pcszValue->psz + pcszValue->SizeAlloc(); lcHeapCheck(); } } *pszDst = '\0'; return FindCharacter(pinput, pcsz, psz, '>'); } PSTR GetType(CAInput* pinput, CStr* pcsz, PSTR psz, CStr* pcszValue) { psz = FirstNonSpace(psz); ASSERT(strlen(txtType) == sizeof(txtType) - 1); // make sure the compiler doesn't mess this up if (IsSamePrefix(psz, txtType, sizeof(txtType) - 1)) { psz = stristr(psz, txtType); if (!psz) { if (!pinput->getline(pcsz)) return NULL; psz = stristr(pcsz->psz, txtType); if (!psz) return NULL; } } // Now find opening quote of the value psz = FindDblQuote(pinput, pcsz, psz); if (!psz) return NULL; if (!pcszValue->psz) pcszValue->psz = (PSTR) lcMalloc(256); PSTR pszDst = *pcszValue; PSTR pszEnd = pszDst + lcSize(pszDst); while (*psz != '\042') { if (!*psz) { if (!pinput->getline(pcsz)) return NULL; psz = pcsz->psz; continue; } *pszDst++ = *psz++; if (pszDst >= pszEnd) { /* * Our input buffer is too small, so increase it by * 128 bytes. */ int offset = (int)(pszDst - pcsz->psz); pcsz->ReSize((int)(pszEnd - pcsz->psz) + 128); pszDst = pcsz->psz + offset; pszEnd = pcsz->psz + pcsz->SizeAlloc(); lcHeapCheck(); } } *pszDst = '\0'; return FindCharacter(pinput, pcsz, psz, '>'); } // This is called from the code that parses the sitemap in the information type section. // Information types found outside of the declaration section are NOT added to the // information types declared. So we don't have to worry about increasing the size of // SITE_ENTRY_URLs after they have been defined. At least in this routine. We will have to // deal with increasing the size of SITE_ENTRY_URLs when adding information types. int CSiteMap::AddTypeName(PCSTR pszTypeName) { ASSERT(!IsEmptyString(pszTypeName)); if (!m_itTables.m_ptblInfoTypes) { m_itTables.m_ptblInfoTypes = new CTable(16 * 1024); ASSERT(!m_itTables.m_ptblInfoTypeDescriptions); m_itTables.m_ptblInfoTypeDescriptions = new CTable(64 * 1024); } ASSERT(m_itTables.m_ptblInfoTypes); /* * If we have an Information type, but the table counts don't match, * then we didn't get a description for the last type, so we add a blank * description to keep the tables in sync. */ while (m_itTables.m_ptblInfoTypes->CountStrings() > m_itTables.m_ptblInfoTypeDescriptions->CountStrings()) m_itTables.m_ptblInfoTypeDescriptions->AddString(""); /* A category and type is specified as: category::type name */ CStr cszCategory; CStr cszType; int category = 0; PCSTR pszCat = strstr(pszTypeName, "::"); if (pszCat) { // separate category name from type name cszType = pszCat + 2; // make a local copy cszCategory = pszTypeName; cszCategory.psz[pszCat - pszTypeName] = '\0'; if (!m_itTables.m_ptblCategories) { m_itTables.m_ptblCategories = new CTable(16 * 1024); } category = m_itTables.m_ptblCategories->IsStringInTable(cszCategory); if (!category) category = m_itTables.m_ptblCategories->AddString(cszCategory); pszTypeName = cszType.psz; } int pos = m_itTables.m_ptblInfoTypes->IsStringInTable(pszTypeName); if (pos <= 0) { if ( (m_itTables.m_cTypes > 0) && ((m_itTables.m_cTypes+1) % 32 == 0) ) ReSizeIT(); // increase by one DWORD pos = m_itTables.m_ptblInfoTypes->AddString(pszTypeName); m_itTables.m_cTypes++; int offset = 0; int type = pos; while (type > 32) { offset++; type -= 32; } INFOTYPE* pInfoType = m_pInfoTypes + offset; *pInfoType |= (1 << type); } category--; // zero offset if ( category >=0 ) AddITtoCategory(category, pos ); #if 0 /* * If we have more then 32 information types, then we need to be certain * the size of the SITE_ENTRY_URL* structure allocated for Sitemap Entries * is large enough. */ // BUGBUG: we need to deal with the situation where an information type // gets specified AFTER a sitemap entry has been specified. if (pos > m_pSiteUrl->m_cMaxTypes) { m_cUrlEntry = sizeof(SITE_ENTRY_URL) + pos / sizeof(UINT); m_pSiteUrl->m_pUrl = (SITE_ENTRY_URL*) lcReAlloc(m_pSiteUrl->m_pUrl, m_cUrlEntry); m_pSiteUrl->m_cMaxTypes += 32; m_cInfoTypeOffsets++; } if ( pos % 32 == 0 ) { // Increase the Categories for(int i=0; i