2 * Copyright 2007 Jacek Caban for CodeWeavers
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
24 #include "wine/debug.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(htmlhelp
);
28 #define BLOCK_SIZE 0x1000
35 static void free_content_item(ContentItem
*item
)
42 free_content_item(item
->child
);
44 heap_free(item
->name
);
45 heap_free(item
->local
);
46 heap_free(item
->merge
.chm_file
);
47 heap_free(item
->merge
.chm_index
);
59 static void strbuf_init(strbuf_t
*buf
)
63 buf
->buf
= heap_alloc(buf
->size
);
66 static void strbuf_zero(strbuf_t
*buf
)
71 static void strbuf_free(strbuf_t
*buf
)
76 static void strbuf_append(strbuf_t
*buf
, const char *data
, int len
)
78 if(buf
->len
+len
> buf
->size
) {
79 buf
->size
= buf
->len
+len
;
80 buf
->buf
= heap_realloc(buf
->buf
, buf
->size
);
83 memcpy(buf
->buf
+buf
->len
, data
, len
);
94 static void stream_init(stream_t
*stream
, IStream
*str
)
96 memset(stream
, 0, sizeof(stream_t
));
100 static BOOL
stream_chr(stream_t
*stream
, strbuf_t
*buf
, char c
)
106 for(i
=stream
->p
; i
<stream
->size
; i
++) {
107 if(stream
->buf
[i
] == c
) {
113 if(buf
&& i
> stream
->p
)
114 strbuf_append(buf
, stream
->buf
+stream
->p
, i
-stream
->p
);
117 if(stream
->p
== stream
->size
) {
119 IStream_Read(stream
->str
, stream
->buf
, sizeof(stream
->buf
), &stream
->size
);
125 return stream
->size
!= 0;
128 static void get_node_name(strbuf_t
*node
, strbuf_t
*name
)
130 const char *ptr
= node
->buf
+1;
134 while(*ptr
!= '>' && !isspace(*ptr
))
137 strbuf_append(name
, node
->buf
+1, ptr
-node
->buf
-1);
138 strbuf_append(name
, "", 1);
141 static BOOL
next_node(stream_t
*stream
, strbuf_t
*buf
)
143 if(!stream_chr(stream
, NULL
, '<'))
146 if(!stream_chr(stream
, buf
, '>'))
149 strbuf_append(buf
, ">", 2);
154 static const char *get_attr(const char *node
, const char *name
, int *len
)
156 const char *ptr
, *ptr2
;
161 memcpy(name_buf
, name
, nlen
);
162 name_buf
[nlen
++] = '=';
163 name_buf
[nlen
++] = '\"';
166 ptr
= strstr(node
, name_buf
);
168 WARN("name not found\n");
173 ptr2
= strchr(ptr
, '\"');
181 static void parse_obj_node_param(ContentItem
*item
, ContentItem
*hhc_root
, const char *text
)
184 LPWSTR
*param
, merge
;
187 ptr
= get_attr(text
, "name", &len
);
189 WARN("name attr not found\n");
193 if(!strncasecmp("name", ptr
, len
)) {
195 }else if(!strncasecmp("merge", ptr
, len
)) {
197 }else if(!strncasecmp("local", ptr
, len
)) {
198 param
= &item
->local
;
200 WARN("unhandled param %s\n", debugstr_an(ptr
, len
));
204 ptr
= get_attr(text
, "value", &len
);
206 WARN("value attr not found\n");
210 wlen
= MultiByteToWideChar(CP_ACP
, 0, ptr
, len
, NULL
, 0);
211 *param
= heap_alloc((wlen
+1)*sizeof(WCHAR
));
212 MultiByteToWideChar(CP_ACP
, 0, ptr
, len
, *param
, wlen
);
215 if(param
== &merge
) {
216 SetChmPath(&item
->merge
, hhc_root
->merge
.chm_file
, merge
);
221 static ContentItem
*parse_hhc(HHInfo
*,IStream
*,ContentItem
*,insert_type_t
*);
223 static ContentItem
*insert_item(ContentItem
*item
, ContentItem
*new_item
, insert_type_t insert_type
)
231 switch(insert_type
) {
233 item
->next
= new_item
;
237 ContentItem
*iter
= item
->child
;
240 iter
->next
= new_item
;
242 item
->child
= new_item
;
250 static ContentItem
*parse_sitemap_object(HHInfo
*info
, stream_t
*stream
, ContentItem
*hhc_root
,
251 insert_type_t
*insert_type
)
253 strbuf_t node
, node_name
;
256 *insert_type
= INSERT_NEXT
;
259 strbuf_init(&node_name
);
261 item
= heap_alloc_zero(sizeof(ContentItem
));
263 while(next_node(stream
, &node
)) {
264 get_node_name(&node
, &node_name
);
266 TRACE("%s\n", node
.buf
);
268 if(!strcasecmp(node_name
.buf
, "/object"))
270 if(!strcasecmp(node_name
.buf
, "param"))
271 parse_obj_node_param(item
, hhc_root
, node
.buf
);
277 strbuf_free(&node_name
);
279 if(item
->merge
.chm_index
) {
280 IStream
*merge_stream
;
282 merge_stream
= GetChmStream(info
->pCHMInfo
, item
->merge
.chm_file
, &item
->merge
);
284 item
->child
= parse_hhc(info
, merge_stream
, hhc_root
, insert_type
);
285 IStream_Release(merge_stream
);
287 WARN("Could not get %s::%s stream\n", debugstr_w(item
->merge
.chm_file
),
288 debugstr_w(item
->merge
.chm_file
));
291 free_content_item(item
);
301 static ContentItem
*parse_ul(HHInfo
*info
, stream_t
*stream
, ContentItem
*hhc_root
)
303 strbuf_t node
, node_name
;
304 ContentItem
*ret
= NULL
, *prev
= NULL
, *new_item
= NULL
;
308 strbuf_init(&node_name
);
310 while(next_node(stream
, &node
)) {
311 get_node_name(&node
, &node_name
);
313 TRACE("%s\n", node
.buf
);
315 if(!strcasecmp(node_name
.buf
, "object")) {
319 static const char sz_text_sitemap
[] = "text/sitemap";
321 ptr
= get_attr(node
.buf
, "type", &len
);
323 if(ptr
&& len
== sizeof(sz_text_sitemap
)-1
324 && !memcmp(ptr
, sz_text_sitemap
, len
)) {
325 new_item
= parse_sitemap_object(info
, stream
, hhc_root
, &it
);
326 prev
= insert_item(prev
, new_item
, it
);
330 }else if(!strcasecmp(node_name
.buf
, "ul")) {
331 new_item
= parse_ul(info
, stream
, hhc_root
);
332 insert_item(prev
, new_item
, INSERT_CHILD
);
333 }else if(!strcasecmp(node_name
.buf
, "/ul")) {
341 strbuf_free(&node_name
);
346 static ContentItem
*parse_hhc(HHInfo
*info
, IStream
*str
, ContentItem
*hhc_root
,
347 insert_type_t
*insert_type
)
350 strbuf_t node
, node_name
;
351 ContentItem
*ret
= NULL
, *prev
= NULL
;
353 *insert_type
= INSERT_NEXT
;
356 strbuf_init(&node_name
);
358 stream_init(&stream
, str
);
360 while(next_node(&stream
, &node
)) {
361 get_node_name(&node
, &node_name
);
363 TRACE("%s\n", node
.buf
);
365 if(!strcasecmp(node_name
.buf
, "ul")) {
366 ContentItem
*item
= parse_ul(info
, &stream
, hhc_root
);
367 prev
= insert_item(prev
, item
, INSERT_CHILD
);
370 *insert_type
= INSERT_CHILD
;
377 strbuf_free(&node_name
);
382 static void insert_content_item(HWND hwnd
, ContentItem
*parent
, ContentItem
*item
)
384 TVINSERTSTRUCTW tvis
;
386 memset(&tvis
, 0, sizeof(tvis
));
387 tvis
.u
.item
.mask
= TVIF_TEXT
|TVIF_PARAM
;
388 tvis
.u
.item
.cchTextMax
= strlenW(item
->name
)+1;
389 tvis
.u
.item
.pszText
= item
->name
;
390 tvis
.u
.item
.lParam
= (LPARAM
)item
;
391 tvis
.hParent
= parent
? parent
->id
: 0;
392 tvis
.hInsertAfter
= TVI_LAST
;
394 item
->id
= (HTREEITEM
)SendMessageW(hwnd
, TVM_INSERTITEMW
, 0, (LPARAM
)&tvis
);
397 static void fill_content_tree(HWND hwnd
, ContentItem
*parent
, ContentItem
*item
)
401 insert_content_item(hwnd
, parent
, item
);
402 fill_content_tree(hwnd
, item
, item
->child
);
404 fill_content_tree(hwnd
, parent
, item
->child
);
410 static void set_item_parents(ContentItem
*parent
, ContentItem
*item
)
413 item
->parent
= parent
;
414 set_item_parents(item
, item
->child
);
419 void InitContent(HHInfo
*info
)
422 insert_type_t insert_type
;
424 info
->content
= heap_alloc_zero(sizeof(ContentItem
));
425 SetChmPath(&info
->content
->merge
, info
->pCHMInfo
->szFile
, info
->WinType
.pszToc
);
427 stream
= GetChmStream(info
->pCHMInfo
, info
->pCHMInfo
->szFile
, &info
->content
->merge
);
429 TRACE("Could not get content stream\n");
433 info
->content
->child
= parse_hhc(info
, stream
, info
->content
, &insert_type
);
434 IStream_Release(stream
);
436 set_item_parents(NULL
, info
->content
);
437 fill_content_tree(info
->tabs
[TAB_CONTENTS
].hwnd
, NULL
, info
->content
);
440 void ReleaseContent(HHInfo
*info
)
442 free_content_item(info
->content
);