/* * Help Access Library * A Library to access the contents of Windows Help files. * * Copyright (C) 1995-2000 Bernd Herd, http://www.herdsoft.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* ENUMKW.C ------------------------------------------- * @doc * @module ENUMKW.C | * Enumerates the list of Keywords contained in a Help file. * * Help Access Library Project. * *-----------------------------------------------------*/ #include #include #include #include #include #include "hlpacces.h" BOOL LOCAL nEnumerateKeywords(HIFSFILE hKWBTree, KWENUMPROC fnKWEnumProc, LPCSTR StartKeyWord, LPARAM lParam); BOOL LOCAL nEnumeratePage(HIFSFILE hKWBTree, int PageCount, int CurrentLevel, int PageSize, int CurrPage, KWENUMPROC fnKWEnumProc, LPCSTR StartKeyWord, LPARAM lParam); /* @func BOOL | HlpEnumKeyWords | * * This Function enumerates the List of Keywords from a Help or Media Viewer * Tile. This Function is relatively fast. * * Note that one Help or Media Viewer file can Have several Keyword lists. * Usually in Help Files the Keyword list is named "K" in Media Viewe it's * Named "0K", the ALink-List in Windows 95 Files is named "A", others are * also possible. * * @rdesc * TRUE if the Function was Successfull * FALSE else */ BOOL WINAPI DLLEXP HlpEnumKeyWords( HIFS ifs, // @parm Handle to a opened File-System opened by KWENUMPROC fnKWEnumProc, // @parm Application-Defined Callback-Procedure to be called for every Keyword retrieved. from the help file LPCSTR StartKeyWord, // @parm Keyword to start the scan with. LPCSTR KWListName, // @parm Name of the Keyword Table (K,0K,A...) LPARAM lParam) // @parm Aplication-defined parameter to be transfered to the Callback-function { BOOL Success=FALSE; HIFSFILE hKWBTree = IFSOpenFile(ifs, KWListName); if (hKWBTree) { Success = nEnumerateKeywords(hKWBTree, fnKWEnumProc, StartKeyWord, lParam); IFSCloseFile(hKWBTree); } return Success; } /* nEnumerateKeywords * * Does the bunch of Work to do enumeration */ BOOL LOCAL nEnumerateKeywords(HIFSFILE hKWBTree, KWENUMPROC fnKWEnumProc, LPCSTR StartKeyWord, LPARAM lParam) { BOOL Success; BTREEHEADER BTreeHdr; DWORD dwSize; UINT PageCount; //--------- Read Title B-Tree header ------------------ IFSReadFile(hKWBTree, &BTreeHdr, sizeof(BTreeHdr)); dwSize = IFSSeekFile(hKWBTree, 0L, 2); PageCount = (UINT) (dwSize/leword(BTreeHdr.PageSize)); Success = nEnumeratePage(hKWBTree, PageCount, leword(BTreeHdr.NLevels)-1, leword(BTreeHdr.PageSize), leword(BTreeHdr.RootPage), fnKWEnumProc, StartKeyWord, lParam); return Success; } #pragma pack(1) typedef struct { LEWORD Count; LEDWORD DataOffset; } PACKED KWREC, *PKWREC; #pragma pack() /** * Windows string comparison emulation: * * It seems that the comparison used when creating the Keyword * B-Tree for Windows help files comares "Ä" and "A" as being equal. * "strcasecmp" on Linux does not, even after setlocale(LC_ALL, "de_DE"). * * So to emulate the Windows-Behaviour we have to implement our own * comparison function to be able to go down the B-Tree in the right * manner. */ /** * Map one character to the windows compare character space * * @param c The raw upper/lower case character * * @todo It is likely that there are characters missing from this simple * implementation. * * @return The character after conversion to upper case. */ static char _kwmapchar(char c) { switch (c) { case '_': case '\"': c=' '; break; case 'ä': case 'Ä': case 'á': case 'Á': case 'à': case 'À': c='A'; break; case 'ö': case 'Ö': case 'ó': case 'Ó': case 'ò': case 'Ò': c='O'; break; case 'ü': case 'Ü': case 'ú': case 'Ú': case 'ù': case 'Ù': c='U'; break; case 'é': case 'É': case 'è': case 'È': c='E'; break; case 'ñ': case 'Ñ': c='N'; break; case 'ß': c='S'; break; default: c=toupper(c); } return c; } /** * Perform a byte-for-byte comparison between two strings, * that emulates the windows-type behaviour. * * @param s1 First string to compare * @param s2 Second string to compare * * @return >0 if s1 is alphabetically later than s2, 0 if the match, <0 if s1 is alphabetically before s2 */ static int _kwstrcmp(const char *s1, const char *s2) { int result = 0; const char *p1=s1; const char *p2=s2; while ((*s1 || *s2) && result==0) { result=_kwmapchar(*s1++) - _kwmapchar(*s2++); } // fprintf(stderr, "_kwstrcmp(\"%s\", \"%s\")=%d\n", p1, p2, result); return result; } BOOL LOCAL nEnumeratePage(HIFSFILE hKWBTree, int PageCount, int CurrentLevel, int PageSize, int CurrPage, KWENUMPROC fnKWEnumProc, LPCSTR StartKeyWord, LPARAM lParam) { BOOL Success = TRUE; DWORD loc = sizeof(BTREEHEADER) + PageSize * (DWORD) CurrPage; LPSTR page = (LPSTR) Hlpmalloc(PageSize), nxt; PKWREC pKWRec; BTREENODEHEADER *CurrNode = (BTREENODEHEADER *)page; /* Current Node in B-Tree */ int i; if (page) { //----------- Read Page ---------------------------------------- IFSSeekFile(hKWBTree, loc, 0); if (PageSize == IFSReadFile(hKWBTree, page, PageSize)) { if (CurrentLevel > 0) { Success = TRUE; //-- We're in a node, search for next sub-node or leaf -- for (i=0, nxt = page+sizeof(BTREEINDEXHEADER); iNEntries)+1 && Success; ++i, nxt+=lstrlen(nxt+2)+1+sizeof(WORD)) { //------- We're currently in a node, so recurse down... ---- if (_kwstrcmp(nxt+2,StartKeyWord)>=0 || i==leword(CurrNode->NEntries)) Success &= nEnumeratePage(hKWBTree, PageCount, CurrentLevel-1, PageSize, CurrPage=leword(((LEWORD *) (nxt))[0]), fnKWEnumProc, StartKeyWord, lParam); } } else { //-- We're in the right leaf now, scan for Data --------- for (i=0, nxt = page+sizeof(BTREENODEHEADER); iNEntries) && Success; ++i ,nxt+=lstrlen(nxt)+1+sizeof(KWREC)) {pKWRec=(PKWREC)(nxt+lstrlen(nxt)+1); if (_kwstrcmp(nxt,StartKeyWord)>=0) Success &= fnKWEnumProc(nxt, ledword(pKWRec->DataOffset), leword(pKWRec->Count), lParam); } } } else Success=FALSE; //----------- Free Page data except Header --------------------- Hlpfree(page); } else Success = FALSE; return Success; }