xref: /reactos/dll/win32/hhctrl.ocx/search.c (revision c2c66aff)
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 
21 static SearchItem *SearchCHM_Folder(SearchItem *item, IStorage *pStorage,
22                                     const WCHAR *folder, const char *needle);
23 
24 /* Allocate a ListView entry for a search result. */
25 static SearchItem *alloc_search_item(WCHAR *title, const WCHAR *filename)
26 {
27     int filename_len = filename ? (strlenW(filename)+1)*sizeof(WCHAR) : 0;
28     SearchItem *item;
29 
30     item = heap_alloc_zero(sizeof(SearchItem));
31     if(filename)
32     {
33         item->filename = heap_alloc(filename_len);
34         memcpy(item->filename, filename, filename_len);
35     }
36     item->title = title; /* Already allocated */
37 
38     return item;
39 }
40 
41 /* Fill the ListView object corresponding to the found Search tab items */
42 static void fill_search_tree(HWND hwndList, SearchItem *item)
43 {
44     int index = 0;
45     LVITEMW lvi;
46 
47     SendMessageW(hwndList, LVM_DELETEALLITEMS, 0, 0);
48     while(item) {
49         TRACE("list debug: %s\n", debugstr_w(item->filename));
50 
51         memset(&lvi, 0, sizeof(lvi));
52         lvi.iItem = index++;
53         lvi.mask = LVIF_TEXT|LVIF_PARAM;
54         lvi.cchTextMax = strlenW(item->title)+1;
55         lvi.pszText = item->title;
56         lvi.lParam = (LPARAM)item;
57         item->id = (HTREEITEM)SendMessageW(hwndList, LVM_INSERTITEMW, 0, (LPARAM)&lvi);
58         item = item->next;
59     }
60 }
61 
62 /* Search the CHM storage stream (an HTML file) for the requested text.
63  *
64  * Before searching the HTML file all HTML tags are removed so that only
65  * the content of the document is scanned.  If the search string is found
66  * then the title of the document is returned.
67  */
68 static WCHAR *SearchCHM_File(IStorage *pStorage, const WCHAR *file, const char *needle)
69 {
70     char *buffer = heap_alloc(BLOCK_SIZE);
71     strbuf_t content, node, node_name;
72     IStream *temp_stream = NULL;
73     DWORD i, buffer_size = 0;
74     WCHAR *title = NULL;
75     BOOL found = FALSE;
76     stream_t stream;
77     HRESULT hres;
78 
79     hres = IStorage_OpenStream(pStorage, file, NULL, STGM_READ, 0, &temp_stream);
80     if(FAILED(hres)) {
81         FIXME("Could not open '%s' stream: %08x\n", debugstr_w(file), hres);
82         goto cleanup;
83     }
84 
85     strbuf_init(&node);
86     strbuf_init(&content);
87     strbuf_init(&node_name);
88 
89     stream_init(&stream, temp_stream);
90 
91     /* Remove all HTML formatting and record the title */
92     while(next_node(&stream, &node)) {
93         get_node_name(&node, &node_name);
94 
95         if(next_content(&stream, &content) && content.len > 1)
96         {
97             char *text = &content.buf[1];
98             int textlen = content.len-1;
99 
100             if(!strcasecmp(node_name.buf, "title"))
101             {
102                 int wlen = MultiByteToWideChar(CP_ACP, 0, text, textlen, NULL, 0);
103                 title = heap_alloc((wlen+1)*sizeof(WCHAR));
104                 MultiByteToWideChar(CP_ACP, 0, text, textlen, title, wlen);
105                 title[wlen] = 0;
106             }
107 
108             buffer = heap_realloc(buffer, buffer_size + textlen + 1);
109             memcpy(&buffer[buffer_size], text, textlen);
110             buffer[buffer_size + textlen] = '\0';
111             buffer_size += textlen;
112         }
113 
114         strbuf_zero(&node);
115         strbuf_zero(&content);
116     }
117 
118     /* Convert the buffer to lower case for comparison against the
119      * requested text (already in lower case).
120      */
121     for(i=0;i<buffer_size;i++)
122         buffer[i] = tolower(buffer[i]);
123 
124     /* Search the decoded buffer for the requested text */
125     if(strstr(buffer, needle))
126         found = TRUE;
127 
128     strbuf_free(&node);
129     strbuf_free(&content);
130     strbuf_free(&node_name);
131 
132 cleanup:
133     heap_free(buffer);
134     if(temp_stream)
135         IStream_Release(temp_stream);
136     if(!found)
137     {
138         heap_free(title);
139         return NULL;
140     }
141     return title;
142 }
143 
144 /* Search all children of a CHM storage object for the requested text and
145  * return the last found search item.
146  */
147 static SearchItem *SearchCHM_Storage(SearchItem *item, IStorage *pStorage,
148                                      const char *needle)
149 {
150     const WCHAR szHTMext[] = {'.','h','t','m',0};
151     IEnumSTATSTG *elem = NULL;
152     WCHAR *filename = NULL;
153     STATSTG entries;
154     HRESULT hres;
155     ULONG retr;
156 
157     hres = IStorage_EnumElements(pStorage, 0, NULL, 0, &elem);
158     if(hres != S_OK)
159     {
160         FIXME("Could not enumerate '/' storage elements: %08x\n", hres);
161         return NULL;
162     }
163     while (IEnumSTATSTG_Next(elem, 1, &entries, &retr) == NOERROR)
164     {
165         filename = entries.pwcsName;
166         while(strchrW(filename, '/'))
167             filename = strchrW(filename, '/')+1;
168         switch(entries.type) {
169         case STGTY_STORAGE:
170             item = SearchCHM_Folder(item, pStorage, filename, needle);
171             break;
172         case STGTY_STREAM:
173             if(strstrW(filename, szHTMext))
174             {
175                 WCHAR *title = SearchCHM_File(pStorage, filename, needle);
176 
177                 if(title)
178                 {
179                     item->next = alloc_search_item(title, entries.pwcsName);
180                     item = item->next;
181                 }
182             }
183             break;
184         default:
185             FIXME("Unhandled IStorage stream element.\n");
186         }
187     }
188     IEnumSTATSTG_Release(elem);
189     return item;
190 }
191 
192 /* Open a CHM storage object (folder) by name and find all items with
193  * the requested text.  The last found item is returned.
194  */
195 static SearchItem *SearchCHM_Folder(SearchItem *item, IStorage *pStorage,
196                                     const WCHAR *folder, const char *needle)
197 {
198     IStorage *temp_storage = NULL;
199     HRESULT hres;
200 
201     hres = IStorage_OpenStorage(pStorage, folder, NULL, STGM_READ, NULL, 0, &temp_storage);
202     if(FAILED(hres))
203     {
204         FIXME("Could not open '%s' storage object: %08x\n", debugstr_w(folder), hres);
205         return NULL;
206     }
207     item = SearchCHM_Storage(item, temp_storage, needle);
208 
209     IStorage_Release(temp_storage);
210     return item;
211 }
212 
213 /* Search the entire CHM file for the requested text and add all of
214  * the found items to a ListView for the user to choose the item
215  * they want.
216  */
217 void InitSearch(HHInfo *info, const char *needle)
218 {
219     CHMInfo *chm = info->pCHMInfo;
220     SearchItem *root_item = alloc_search_item(NULL, NULL);
221 
222     SearchCHM_Storage(root_item, chm->pStorage, needle);
223     fill_search_tree(info->search.hwndList, root_item->next);
224     if(info->search.root)
225         ReleaseSearch(info);
226     info->search.root = root_item;
227 }
228 
229 /* Free all of the found Search items. */
230 void ReleaseSearch(HHInfo *info)
231 {
232     SearchItem *item = info->search.root;
233 
234     info->search.root = NULL;
235     while(item) {
236         heap_free(item->filename);
237         item = item->next;
238     }
239 }
240