winejoystick: Fix a crash on accessing a CFArray past its end due to an off-by-one...
[wine/multimedia.git] / dlls / hhctrl.ocx / search.c
blob287e6d0d38802d6bd2ea08eaa4417b5ece5bbe86
1 /*
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 #include "hhctrl.h"
20 #include "stream.h"
22 #include "wine/debug.h"
24 WINE_DEFAULT_DEBUG_CHANNEL(htmlhelp);
26 static SearchItem *SearchCHM_Folder(SearchItem *item, IStorage *pStorage,
27 const WCHAR *folder, const char *needle);
29 /* Allocate a ListView entry for a search result. */
30 static SearchItem *alloc_search_item(WCHAR *title, const WCHAR *filename)
32 int filename_len = filename ? (strlenW(filename)+1)*sizeof(WCHAR) : 0;
33 SearchItem *item;
35 item = heap_alloc_zero(sizeof(SearchItem));
36 if(filename)
38 item->filename = heap_alloc(filename_len);
39 memcpy(item->filename, filename, filename_len);
41 item->title = title; /* Already allocated */
43 return item;
46 /* Fill the ListView object corresponding to the found Search tab items */
47 static void fill_search_tree(HWND hwndList, SearchItem *item)
49 int index = 0;
50 LVITEMW lvi;
52 SendMessageW(hwndList, LVM_DELETEALLITEMS, 0, 0);
53 while(item) {
54 TRACE("list debug: %s\n", debugstr_w(item->filename));
56 memset(&lvi, 0, sizeof(lvi));
57 lvi.iItem = index++;
58 lvi.mask = LVIF_TEXT|LVIF_PARAM;
59 lvi.cchTextMax = strlenW(item->title)+1;
60 lvi.pszText = item->title;
61 lvi.lParam = (LPARAM)item;
62 item->id = (HTREEITEM)SendMessageW(hwndList, LVM_INSERTITEMW, 0, (LPARAM)&lvi);
63 item = item->next;
67 /* Search the CHM storage stream (an HTML file) for the requested text.
69 * Before searching the HTML file all HTML tags are removed so that only
70 * the content of the document is scanned. If the search string is found
71 * then the title of the document is returned.
73 static WCHAR *SearchCHM_File(IStorage *pStorage, const WCHAR *file, const char *needle)
75 char *buffer = heap_alloc(BLOCK_SIZE);
76 strbuf_t content, node, node_name;
77 IStream *temp_stream = NULL;
78 DWORD i, buffer_size = 0;
79 WCHAR *title = NULL;
80 BOOL found = FALSE;
81 stream_t stream;
82 HRESULT hres;
84 hres = IStorage_OpenStream(pStorage, file, NULL, STGM_READ, 0, &temp_stream);
85 if(FAILED(hres)) {
86 FIXME("Could not open '%s' stream: %08x\n", debugstr_w(file), hres);
87 goto cleanup;
90 strbuf_init(&node);
91 strbuf_init(&content);
92 strbuf_init(&node_name);
94 stream_init(&stream, temp_stream);
96 /* Remove all HTML formatting and record the title */
97 while(next_node(&stream, &node)) {
98 get_node_name(&node, &node_name);
100 if(next_content(&stream, &content) && content.len > 1)
102 char *text = &content.buf[1];
103 int textlen = content.len-1;
105 if(!strcasecmp(node_name.buf, "title"))
107 int wlen = MultiByteToWideChar(CP_ACP, 0, text, textlen, NULL, 0);
108 title = heap_alloc((wlen+1)*sizeof(WCHAR));
109 MultiByteToWideChar(CP_ACP, 0, text, textlen, title, wlen);
110 title[wlen] = 0;
113 buffer = heap_realloc(buffer, buffer_size + textlen + 1);
114 memcpy(&buffer[buffer_size], text, textlen);
115 buffer[buffer_size + textlen] = '\0';
116 buffer_size += textlen;
119 strbuf_zero(&node);
120 strbuf_zero(&content);
123 /* Convert the buffer to lower case for comparison against the
124 * requested text (already in lower case).
126 for(i=0;i<buffer_size;i++)
127 buffer[i] = tolower(buffer[i]);
129 /* Search the decoded buffer for the requested text */
130 if(strstr(buffer, needle))
131 found = TRUE;
133 strbuf_free(&node);
134 strbuf_free(&content);
135 strbuf_free(&node_name);
137 cleanup:
138 heap_free(buffer);
139 if(temp_stream)
140 IStream_Release(temp_stream);
141 if(!found)
143 heap_free(title);
144 return NULL;
146 return title;
149 /* Search all children of a CHM storage object for the requested text and
150 * return the last found search item.
152 static SearchItem *SearchCHM_Storage(SearchItem *item, IStorage *pStorage,
153 const char *needle)
155 const WCHAR szHTMext[] = {'.','h','t','m',0};
156 IEnumSTATSTG *elem = NULL;
157 WCHAR *filename = NULL;
158 STATSTG entries;
159 HRESULT hres;
160 ULONG retr;
162 hres = IStorage_EnumElements(pStorage, 0, NULL, 0, &elem);
163 if(hres != S_OK)
165 FIXME("Could not enumerate '/' storage elements: %08x\n", hres);
166 return NULL;
168 while (IEnumSTATSTG_Next(elem, 1, &entries, &retr) == NOERROR)
170 switch(entries.type) {
171 case STGTY_STORAGE:
172 item = SearchCHM_Folder(item, pStorage, entries.pwcsName, needle);
173 break;
174 case STGTY_STREAM:
175 filename = entries.pwcsName;
176 while(strchrW(filename, '/'))
177 filename = strchrW(filename, '/')+1;
178 if(strstrW(filename, szHTMext))
180 WCHAR *title = SearchCHM_File(pStorage, filename, needle);
182 if(title)
184 item->next = alloc_search_item(title, entries.pwcsName);
185 item = item->next;
188 break;
189 default:
190 FIXME("Unhandled IStorage stream element.\n");
193 IEnumSTATSTG_Release(elem);
194 return item;
197 /* Open a CHM storage object (folder) by name and find all items with
198 * the requested text. The last found item is returned.
200 static SearchItem *SearchCHM_Folder(SearchItem *item, IStorage *pStorage,
201 const WCHAR *folder, const char *needle)
203 IStorage *temp_storage = NULL;
204 HRESULT hres;
206 hres = IStorage_OpenStorage(pStorage, folder, NULL, STGM_READ, NULL, 0, &temp_storage);
207 if(FAILED(hres))
209 FIXME("Could not open '%s' storage object: %08x\n", debugstr_w(folder), hres);
210 return NULL;
212 item = SearchCHM_Storage(item, temp_storage, needle);
214 IStorage_Release(temp_storage);
215 return item;
218 /* Search the entire CHM file for the requested text and add all of
219 * the found items to a ListView for the user to choose the item
220 * they want.
222 void InitSearch(HHInfo *info, const char *needle)
224 CHMInfo *chm = info->pCHMInfo;
225 SearchItem *root_item = alloc_search_item(NULL, NULL);
227 SearchCHM_Storage(root_item, chm->pStorage, needle);
228 fill_search_tree(info->search.hwndList, root_item->next);
229 if(info->search.root)
230 ReleaseSearch(info);
231 info->search.root = root_item;
234 /* Free all of the found Search items. */
235 void ReleaseSearch(HHInfo *info)
237 SearchItem *item = info->search.root;
239 info->search.root = NULL;
240 while(item) {
241 heap_free(item->filename);
242 item = item->next;