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