xref: /reactos/dll/win32/hhctrl.ocx/search.c (revision 6cbb6445)
1 /*
2  * Copyright 2010 Erich Hoover
3  *
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.
8  *
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.
13  *
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
17  */
18 
19 #include "hhctrl.h"
20 #include "stream.h"
21 
22 #include "wine/debug.h"
23 
24 WINE_DEFAULT_DEBUG_CHANNEL(htmlhelp);
25 
26 static SearchItem *SearchCHM_Folder(SearchItem *item, IStorage *pStorage,
27                                     const WCHAR *folder, const char *needle);
28 
29 /* Allocate a ListView entry for a search result. */
alloc_search_item(WCHAR * title,const WCHAR * filename)30 static SearchItem *alloc_search_item(WCHAR *title, const WCHAR *filename)
31 {
32     int filename_len = filename ? (lstrlenW(filename)+1)*sizeof(WCHAR) : 0;
33     SearchItem *item;
34 
35     item = heap_alloc_zero(sizeof(SearchItem));
36     if(filename)
37     {
38         item->filename = heap_alloc(filename_len);
39         memcpy(item->filename, filename, filename_len);
40     }
41     item->title = title; /* Already allocated */
42 
43     return item;
44 }
45 
46 /* Fill the ListView object corresponding to the found Search tab items */
fill_search_tree(HWND hwndList,SearchItem * item)47 static void fill_search_tree(HWND hwndList, SearchItem *item)
48 {
49     int index = 0;
50     LVITEMW lvi;
51 
52     SendMessageW(hwndList, LVM_DELETEALLITEMS, 0, 0);
53     while(item) {
54         TRACE("list debug: %s\n", debugstr_w(item->filename));
55 
56         memset(&lvi, 0, sizeof(lvi));
57         lvi.iItem = index++;
58         lvi.mask = LVIF_TEXT|LVIF_PARAM;
59         lvi.cchTextMax = lstrlenW(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;
64     }
65 }
66 
67 /* Search the CHM storage stream (an HTML file) for the requested text.
68  *
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.
72  */
SearchCHM_File(IStorage * pStorage,const WCHAR * file,const char * needle)73 static WCHAR *SearchCHM_File(IStorage *pStorage, const WCHAR *file, const char *needle)
74 {
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;
83 
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;
88     }
89 
90     strbuf_init(&node);
91     strbuf_init(&content);
92     strbuf_init(&node_name);
93 
94     stream_init(&stream, temp_stream);
95 
96     /* Remove all HTML formatting and record the title */
97     while(next_node(&stream, &node)) {
98         get_node_name(&node, &node_name);
99 
100         if(next_content(&stream, &content) && content.len > 1)
101         {
102             char *text = &content.buf[1];
103             int textlen = content.len-1;
104 
105             if(!_strnicmp(node_name.buf, "title", -1))
106             {
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;
111             }
112 
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;
117         }
118 
119         strbuf_zero(&node);
120         strbuf_zero(&content);
121     }
122 
123     /* Convert the buffer to lower case for comparison against the
124      * requested text (already in lower case).
125      */
126     for(i=0;i<buffer_size;i++)
127         buffer[i] = tolower(buffer[i]);
128 
129     /* Search the decoded buffer for the requested text */
130     if(strstr(buffer, needle))
131         found = TRUE;
132 
133     strbuf_free(&node);
134     strbuf_free(&content);
135     strbuf_free(&node_name);
136 
137 cleanup:
138     heap_free(buffer);
139     if(temp_stream)
140         IStream_Release(temp_stream);
141     if(!found)
142     {
143         heap_free(title);
144         return NULL;
145     }
146     return title;
147 }
148 
149 /* Search all children of a CHM storage object for the requested text and
150  * return the last found search item.
151  */
SearchCHM_Storage(SearchItem * item,IStorage * pStorage,const char * needle)152 static SearchItem *SearchCHM_Storage(SearchItem *item, IStorage *pStorage,
153                                      const char *needle)
154 {
155     static const WCHAR szHTMext[] = {'.','h','t','m',0};
156     IEnumSTATSTG *elem = NULL;
157     WCHAR *filename = NULL;
158     STATSTG entries;
159     HRESULT hres;
160     ULONG retr;
161 
162     hres = IStorage_EnumElements(pStorage, 0, NULL, 0, &elem);
163     if(hres != S_OK)
164     {
165         FIXME("Could not enumerate '/' storage elements: %08x\n", hres);
166         return NULL;
167     }
168     while (IEnumSTATSTG_Next(elem, 1, &entries, &retr) == NOERROR)
169     {
170         filename = entries.pwcsName;
171         while(wcschr(filename, '/'))
172             filename = wcschr(filename, '/')+1;
173         switch(entries.type) {
174         case STGTY_STORAGE:
175             item = SearchCHM_Folder(item, pStorage, filename, needle);
176             break;
177         case STGTY_STREAM:
178             if(wcsstr(filename, szHTMext))
179             {
180                 WCHAR *title = SearchCHM_File(pStorage, filename, needle);
181 
182                 if(title)
183                 {
184                     item->next = alloc_search_item(title, entries.pwcsName);
185                     item = item->next;
186                 }
187             }
188             break;
189         default:
190             FIXME("Unhandled IStorage stream element.\n");
191         }
192     }
193     IEnumSTATSTG_Release(elem);
194     return item;
195 }
196 
197 /* Open a CHM storage object (folder) by name and find all items with
198  * the requested text.  The last found item is returned.
199  */
SearchCHM_Folder(SearchItem * item,IStorage * pStorage,const WCHAR * folder,const char * needle)200 static SearchItem *SearchCHM_Folder(SearchItem *item, IStorage *pStorage,
201                                     const WCHAR *folder, const char *needle)
202 {
203     IStorage *temp_storage = NULL;
204     HRESULT hres;
205 
206     hres = IStorage_OpenStorage(pStorage, folder, NULL, STGM_READ, NULL, 0, &temp_storage);
207     if(FAILED(hres))
208     {
209         FIXME("Could not open '%s' storage object: %08x\n", debugstr_w(folder), hres);
210         return NULL;
211     }
212     item = SearchCHM_Storage(item, temp_storage, needle);
213 
214     IStorage_Release(temp_storage);
215     return item;
216 }
217 
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.
221  */
InitSearch(HHInfo * info,const char * needle)222 void InitSearch(HHInfo *info, const char *needle)
223 {
224     CHMInfo *chm = info->pCHMInfo;
225     SearchItem *root_item = alloc_search_item(NULL, NULL);
226 
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;
232 }
233 
234 /* Free all of the found Search items. */
ReleaseSearch(HHInfo * info)235 void ReleaseSearch(HHInfo *info)
236 {
237     SearchItem *item = info->search.root;
238 
239     info->search.root = NULL;
240     while(item) {
241         heap_free(item->filename);
242         item = item->next;
243     }
244 }
245