2 * Copyright 2010 Erich Hoover
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #define NONAMELESSUNION
20 #define NONAMELESSSTRUCT
25 #include "wine/debug.h"
27 WINE_DEFAULT_DEBUG_CHANNEL(htmlhelp);
29 static SearchItem *SearchCHM_Folder(SearchItem *item, IStorage *pStorage,
30 const WCHAR *folder, const char *needle);
32 /* Allocate a ListView entry for a search result. */
33 static SearchItem *alloc_search_item(WCHAR *title, const WCHAR *filename)
35 int filename_len = filename ? (strlenW(filename)+1)*sizeof(WCHAR) : 0;
38 item = heap_alloc_zero(sizeof(SearchItem));
41 item->filename = heap_alloc(filename_len);
42 memcpy(item->filename, filename, filename_len);
44 item->title = title; /* Already allocated */
49 /* Fill the ListView object corresponding to the found Search tab items */
50 static void fill_search_tree(HWND hwndList, SearchItem *item)
55 SendMessageW(hwndList, LVM_DELETEALLITEMS, 0, 0);
57 TRACE("list debug: %s\n", debugstr_w(item->filename));
59 memset(&lvi, 0, sizeof(lvi));
61 lvi.mask = LVIF_TEXT|LVIF_PARAM;
62 lvi.cchTextMax = strlenW(item->title)+1;
63 lvi.pszText = item->title;
64 lvi.lParam = (LPARAM)item;
65 item->id = (HTREEITEM)SendMessageW(hwndList, LVM_INSERTITEMW, 0, (LPARAM)&lvi);
70 /* Search the CHM storage stream (an HTML file) for the requested text.
72 * Before searching the HTML file all HTML tags are removed so that only
73 * the content of the document is scanned. If the search string is found
74 * then the title of the document is returned.
76 static WCHAR *SearchCHM_File(IStorage *pStorage, const WCHAR *file, const char *needle)
78 char *buffer = heap_alloc(BLOCK_SIZE);
79 strbuf_t content, node, node_name;
80 IStream *temp_stream = NULL;
81 DWORD i, buffer_size = 0;
87 hres = IStorage_OpenStream(pStorage, file, NULL, STGM_READ, 0, &temp_stream);
89 FIXME("Could not open '%s' stream: %08x\n", debugstr_w(file), hres);
94 strbuf_init(&content);
95 strbuf_init(&node_name);
97 stream_init(&stream, temp_stream);
99 /* Remove all HTML formatting and record the title */
100 while(next_node(&stream, &node)) {
101 get_node_name(&node, &node_name);
103 if(next_content(&stream, &content) && content.len > 1)
105 char *text = &content.buf[1];
106 int textlen = content.len-1;
108 if(!strcasecmp(node_name.buf, "title"))
110 int wlen = MultiByteToWideChar(CP_ACP, 0, text, textlen, NULL, 0);
111 title = heap_alloc((wlen+1)*sizeof(WCHAR));
112 MultiByteToWideChar(CP_ACP, 0, text, textlen, title, wlen);
116 buffer = heap_realloc(buffer, buffer_size + textlen + 1);
117 memcpy(&buffer[buffer_size], text, textlen);
118 buffer[buffer_size + textlen] = '\0';
119 buffer_size += textlen;
123 strbuf_zero(&content);
126 /* Convert the buffer to lower case for comparison against the
127 * requested text (already in lower case).
129 for(i=0;i<buffer_size;i++)
130 buffer[i] = tolower(buffer[i]);
132 /* Search the decoded buffer for the requested text */
133 if(strstr(buffer, needle))
137 strbuf_free(&content);
138 strbuf_free(&node_name);
143 IStream_Release(temp_stream);
152 /* Search all children of a CHM storage object for the requested text and
153 * return the last found search item.
155 static SearchItem *SearchCHM_Storage(SearchItem *item, IStorage *pStorage,
158 const WCHAR szHTMext[] = {'.','h','t','m',0};
159 IEnumSTATSTG *elem = NULL;
160 WCHAR *filename = NULL;
165 hres = IStorage_EnumElements(pStorage, 0, NULL, 0, &elem);
168 FIXME("Could not enumerate '/' storage elements: %08x\n", hres);
171 while (IEnumSTATSTG_Next(elem, 1, &entries, &retr) == NOERROR)
173 switch(entries.type) {
175 item = SearchCHM_Folder(item, pStorage, entries.pwcsName, needle);
178 filename = entries.pwcsName;
179 while(strchrW(filename, '/'))
180 filename = strchrW(filename, '/')+1;
181 if(strstrW(filename, szHTMext))
183 WCHAR *title = SearchCHM_File(pStorage, filename, needle);
187 item->next = alloc_search_item(title, entries.pwcsName);
193 FIXME("Unhandled IStorage stream element.\n");
196 IEnumSTATSTG_Release(elem);
200 /* Open a CHM storage object (folder) by name and find all items with
201 * the requested text. The last found item is returned.
203 static SearchItem *SearchCHM_Folder(SearchItem *item, IStorage *pStorage,
204 const WCHAR *folder, const char *needle)
206 IStorage *temp_storage = NULL;
209 hres = IStorage_OpenStorage(pStorage, folder, NULL, STGM_READ, NULL, 0, &temp_storage);
212 FIXME("Could not open '%s' storage object: %08x\n", debugstr_w(folder), hres);
215 item = SearchCHM_Storage(item, temp_storage, needle);
217 IStorage_Release(temp_storage);
221 /* Search the entire CHM file for the requested text and add all of
222 * the found items to a ListView for the user to choose the item
225 void InitSearch(HHInfo *info, const char *needle)
227 CHMInfo *chm = info->pCHMInfo;
228 SearchItem *root_item = alloc_search_item(NULL, NULL);
230 SearchCHM_Storage(root_item, chm->pStorage, needle);
231 fill_search_tree(info->search.hwndList, root_item->next);
232 if(info->search.root)
234 info->search.root = root_item;
237 /* Free all of the found Search items. */
238 void ReleaseSearch(HHInfo *info)
240 SearchItem *item = info->search.root;
242 info->search.root = NULL;
244 heap_free(item->filename);