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
);