fixed bug #429351 (compilation broken in CJK environment (whatever it is) in 'agrave...
[wxWidgets.git] / src / html / helpdata.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: helpdata.cpp
3 // Purpose: wxHtmlHelpData
4 // Notes: Based on htmlhelp.cpp, implementing a monolithic
5 // HTML Help controller class, by Vaclav Slavik
6 // Author: Harm van der Heijden and Vaclav Slavik
7 // RCS-ID: $Id$
8 // Copyright: (c) Harm van der Heijden and Vaclav Slavik
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation
14 #endif
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #ifdef __BORLANDC__
20 #pragma hdrstop
21 #endif
22
23 #include "wx/defs.h"
24
25 #if wxUSE_HTML && wxUSE_STREAMS
26
27 #ifndef WXPRECOMP
28 #include "wx/wx.h"
29 #endif
30
31 #include "wx/html/helpdata.h"
32 #include "wx/tokenzr.h"
33 #include "wx/wfstream.h"
34 #include "wx/busyinfo.h"
35 #include "wx/encconv.h"
36 #include "wx/fontmap.h"
37 #include "wx/log.h"
38 #include "wx/html/htmlpars.h"
39 #include "wx/html/htmldefs.h"
40
41 #include "wx/arrimpl.cpp"
42 WX_DEFINE_OBJARRAY(wxHtmlBookRecArray)
43
44 //-----------------------------------------------------------------------------
45 // static helper functions
46 //-----------------------------------------------------------------------------
47
48 // Reads one line, stores it into buf and returns pointer to new line or NULL.
49 static char* ReadLine(char *line, char *buf)
50 {
51 char *writeptr = buf, *readptr = line;
52
53 while (*readptr != 0 && *readptr != '\r' && *readptr != '\n') *(writeptr++) = *(readptr++);
54 *writeptr = 0;
55 while (*readptr == '\r' || *readptr == '\n') readptr++;
56 if (*readptr == 0) return NULL;
57 else return readptr;
58 }
59
60
61
62 static int LINKAGEMODE IndexCompareFunc(const void *a, const void *b)
63 {
64 return wxStrcmp(((wxHtmlContentsItem*)a)->m_Name, ((wxHtmlContentsItem*)b)->m_Name);
65 }
66
67
68 //-----------------------------------------------------------------------------
69 // HP_Parser
70 //-----------------------------------------------------------------------------
71
72 class HP_Parser : public wxHtmlParser
73 {
74 public:
75 void AddText(const char* WXUNUSED(text)) { }
76 wxObject* GetProduct() { return NULL; }
77 };
78
79
80 //-----------------------------------------------------------------------------
81 // HP_TagHandler
82 //-----------------------------------------------------------------------------
83
84 class HP_TagHandler : public wxHtmlTagHandler
85 {
86 private:
87 wxString m_Name, m_Page;
88 int m_Level;
89 int m_ID;
90 int m_Index;
91 wxHtmlContentsItem *m_Items;
92 int m_ItemsCnt;
93 wxHtmlBookRecord *m_Book;
94
95 public:
96 HP_TagHandler(wxHtmlBookRecord *b) : wxHtmlTagHandler()
97 { m_Book = b; m_Items = NULL; m_ItemsCnt = 0; m_Name = m_Page = wxEmptyString;
98 m_Level = 0; m_ID = -1; }
99 wxString GetSupportedTags() { return wxT("UL,OBJECT,PARAM"); }
100 bool HandleTag(const wxHtmlTag& tag);
101 void WriteOut(wxHtmlContentsItem*& array, int& size);
102 void ReadIn(wxHtmlContentsItem* array, int size);
103 };
104
105
106 bool HP_TagHandler::HandleTag(const wxHtmlTag& tag)
107 {
108 if (tag.GetName() == wxT("UL"))
109 {
110 m_Level++;
111 ParseInner(tag);
112 m_Level--;
113 return TRUE;
114 }
115 else if (tag.GetName() == wxT("OBJECT"))
116 {
117 m_Name = m_Page = wxEmptyString;
118 ParseInner(tag);
119
120 #if 0
121 if (!m_Page.IsEmpty())
122 /* Valid HHW's file may contain only two object tags:
123
124 <OBJECT type="text/site properties">
125 <param name="ImageType" value="Folder">
126 </OBJECT>
127
128 or
129
130 <OBJECT type="text/sitemap">
131 <param name="Name" value="main page">
132 <param name="Local" value="another.htm">
133 </OBJECT>
134
135 We're interested in the latter. !m_Page.IsEmpty() is valid
136 condition because text/site properties does not contain Local param
137 */
138 #endif
139 if (tag.GetParam(wxT("TYPE")) == wxT("text/sitemap"))
140 {
141 if (m_ItemsCnt % wxHTML_REALLOC_STEP == 0)
142 m_Items = (wxHtmlContentsItem*) realloc(m_Items,
143 (m_ItemsCnt + wxHTML_REALLOC_STEP) *
144 sizeof(wxHtmlContentsItem));
145
146 m_Items[m_ItemsCnt].m_Level = m_Level;
147 m_Items[m_ItemsCnt].m_ID = m_ID;
148 m_Items[m_ItemsCnt].m_Page = new wxChar[m_Page.Length() + 1];
149 wxStrcpy(m_Items[m_ItemsCnt].m_Page, m_Page.c_str());
150 m_Items[m_ItemsCnt].m_Name = new wxChar [m_Name.Length() + 1];
151 wxStrcpy(m_Items[m_ItemsCnt].m_Name, m_Name.c_str());
152 m_Items[m_ItemsCnt].m_Book = m_Book;
153 m_ItemsCnt++;
154 }
155
156 return TRUE;
157 }
158 else
159 { // "PARAM"
160 if (m_Name == wxEmptyString && tag.GetParam(wxT("NAME")) == wxT("Name"))
161 {
162 m_Name = tag.GetParam(wxT("VALUE"));
163 if (m_Name.Find(wxT('&')) != -1)
164 {
165 #define ESCSEQ(escape, subst) \
166 { _T("&") _T(escape) _T(";"), _T("&") _T(escape) _T(" "), _T("&") _T(escape), _T(subst) }
167 static wxChar* substitutions[][4] =
168 {
169 ESCSEQ("quot", "\""),
170 ESCSEQ("#34", "\""),
171 ESCSEQ("#8220", "\""),
172 ESCSEQ("#8221", "\""),
173 ESCSEQ("lt", "<"),
174 ESCSEQ("#60", "<"),
175 ESCSEQ("gt", ">"),
176 ESCSEQ("#62", ">"),
177
178 ESCSEQ("#94", "^"), /* ^ */
179
180 ESCSEQ("nbsp", " "),
181 ESCSEQ("#32", " "),
182 ESCSEQ("iexcl", "!"),
183 ESCSEQ("#33", "!"),
184 ESCSEQ("cent", "¢"/* ¢ */),
185 ESCSEQ("#162", "¢"/* ¢ */),
186
187 ESCSEQ("trade", "(TM)"),
188 ESCSEQ("#153", "(TM)"),
189 ESCSEQ("#8482", "(TM)"),
190
191 ESCSEQ("yen", "¥"),
192 ESCSEQ("#165", "¥"),
193 ESCSEQ("brkbar", "¦"),
194 ESCSEQ("#166", "¦"),
195 ESCSEQ("sect", "§"),
196 ESCSEQ("#167", "§"),
197 ESCSEQ("uml", "¨"),
198 ESCSEQ("#168", "¨"),
199
200 ESCSEQ("copy", "©"), /* © */
201 ESCSEQ("#169", "©"),
202 ESCSEQ("ordf", "ª"),
203 ESCSEQ("#170", "ª"),
204 ESCSEQ("laquo", "«"), /* « */
205 ESCSEQ("#171", "«"),
206 ESCSEQ("not", "¬"),
207 ESCSEQ("#172", "¬"),
208
209 ESCSEQ("reg", "®"), /* ® */
210 ESCSEQ("#174", "®"),
211
212 ESCSEQ("deg", "°"), /* ° */
213 ESCSEQ("#176", "°"),
214 ESCSEQ("plusm", "±"), /* ± */
215 ESCSEQ("#177", "±"),
216
217 ESCSEQ("acute", "´"),
218 ESCSEQ("#180", "´"),
219 ESCSEQ("macron", "¯"),
220 ESCSEQ("#175", "¯"),
221 ESCSEQ("micro", "µ"), /* µ */
222 ESCSEQ("#181", "µ"),
223 ESCSEQ("para", "¶"), /* ¶ */
224 ESCSEQ("#182", "¶"),
225
226 ESCSEQ("ordm", "º"), /* º */
227 ESCSEQ("#186", "º"),
228 ESCSEQ("raquo", "»"), /* » */
229 ESCSEQ("#187", "»"),
230
231 ESCSEQ("iquest", "¿"), /* ¿ */
232 ESCSEQ("#191", "¿"),
233 ESCSEQ("Agrave", "\300"/* À */),
234 ESCSEQ("#193", "\300"/* À */),
235
236 ESCSEQ("Acirc", "\302"/* Â */),
237 ESCSEQ("Atilde", "\303"/* Ã */),
238 ESCSEQ("Auml", "\304"/* Ä */),
239 ESCSEQ("Aring", " "),
240 ESCSEQ("AElig", " "),
241 ESCSEQ("Ccedil", "\347"/* ç */),
242 ESCSEQ("Egrave", "\310"/* È */),
243 ESCSEQ("Eacute", "\311"/* É */),
244 ESCSEQ("Ecirc", "\312"/* Ê */),
245 ESCSEQ("Euml", "\313"/* Ë */),
246 ESCSEQ("Igrave", "\314"/* Ì */),
247
248 ESCSEQ("Icirc", "\316"/* Î */),
249 ESCSEQ("Iuml", "\317"/* Ï */),
250
251 ESCSEQ("Ntilde", "\321"/* Ñ */),
252 ESCSEQ("Ograve", "\322"/* Ò */),
253
254 ESCSEQ("Ocirc", "\324"/* Ô */),
255 ESCSEQ("Otilde", "\325"/* Õ */),
256 ESCSEQ("Ouml", "\326"/* Ö */),
257
258 ESCSEQ("Oslash", " "),
259 ESCSEQ("Ugrave", "\331"/* Ù */),
260
261 ESCSEQ("Ucirc", " "),
262 ESCSEQ("Uuml", "\334"/* Ü */),
263
264 ESCSEQ("szlig", "\247"/* § */),
265 ESCSEQ("agrave","\340"/* à */),
266 ESCSEQ("aacute", "\341"/* á */),
267 ESCSEQ("acirc", "\342"/* â */),
268 ESCSEQ("atilde", "\343"/* ã */),
269 ESCSEQ("auml", "\344"/* ä */),
270 ESCSEQ("aring", "a"),
271 ESCSEQ("aelig", "ae"),
272 ESCSEQ("ccedil", "\347"/* ç */),
273 ESCSEQ("egrave", "\350"/* è */),
274 ESCSEQ("eacute", "\351"/* é */),
275 ESCSEQ("ecirc", "\352"/* ê */),
276 ESCSEQ("euml", "\353"/* ë */),
277 ESCSEQ("igrave", "\354"/* ì */),
278 ESCSEQ("iacute", "\355"/* í */),
279 ESCSEQ("icirc", " "),
280 ESCSEQ("iuml", "\357"/* ï */),
281 ESCSEQ("eth", " "),
282 ESCSEQ("ntilde", "\361"/* ñ */),
283 ESCSEQ("ograve", "\362"/* ò */),
284 ESCSEQ("oacute", "\363"/* ó */),
285 ESCSEQ("ocirc", "\364"/* ô */),
286 ESCSEQ("otilde", "\365"/* õ */),
287 ESCSEQ("ouml", "\366"/* ö */),
288 ESCSEQ("divide", " "),
289 ESCSEQ("oslash", " "),
290 ESCSEQ("ugrave", "\371"/* ù */),
291 ESCSEQ("uacute", "\372"/* ú */),
292 ESCSEQ("ucirc", "\373"/* û */),
293 ESCSEQ("uuml", "\374"/* ü */),
294
295 ESCSEQ("yuml", ""),
296
297 /* this one should ALWAYS stay the last one!!! */
298 ESCSEQ("amp", "&"),
299 ESCSEQ("#38", "&"),
300
301 { NULL, NULL, NULL }
302 };
303
304 for (int i = 0; substitutions[i][0] != NULL; i++)
305 {
306 m_Name.Replace(substitutions[i][0], substitutions[i][3], TRUE);
307 m_Name.Replace(substitutions[i][1], substitutions[i][3], TRUE);
308 m_Name.Replace(substitutions[i][2], substitutions[i][3], TRUE);
309 }
310 }
311 }
312 if (tag.GetParam(wxT("NAME")) == wxT("Local")) m_Page = tag.GetParam(wxT("VALUE"));
313 if (tag.GetParam(wxT("NAME")) == wxT("ID")) tag.ScanParam(wxT("VALUE"), wxT("%i"), &m_ID);
314 return FALSE;
315 }
316 }
317
318
319
320 void HP_TagHandler::WriteOut(wxHtmlContentsItem*& array, int& size)
321 {
322 array = m_Items;
323 size = m_ItemsCnt;
324 m_Items = NULL;
325 m_ItemsCnt = 0;
326 }
327
328 void HP_TagHandler::ReadIn(wxHtmlContentsItem* array, int size)
329 {
330 m_Items = array;
331 m_ItemsCnt = size;
332 }
333
334
335
336
337 //-----------------------------------------------------------------------------
338 // wxHtmlHelpData
339 //-----------------------------------------------------------------------------
340
341 wxString wxHtmlBookRecord::GetFullPath(const wxString &page) const
342 {
343 if (wxIsAbsolutePath(page))
344 return page;
345 else
346 return m_BasePath + page;
347 }
348
349
350
351 IMPLEMENT_DYNAMIC_CLASS(wxHtmlHelpData, wxObject)
352
353 wxHtmlHelpData::wxHtmlHelpData()
354 {
355 m_TempPath = wxEmptyString;
356
357 m_Contents = NULL;
358 m_ContentsCnt = 0;
359 m_Index = NULL;
360 m_IndexCnt = 0;
361 }
362
363 wxHtmlHelpData::~wxHtmlHelpData()
364 {
365 int i;
366
367 m_BookRecords.Empty();
368 if (m_Contents)
369 {
370 for (i = 0; i < m_ContentsCnt; i++)
371 {
372 delete[] m_Contents[i].m_Page;
373 delete[] m_Contents[i].m_Name;
374 }
375 free(m_Contents);
376 }
377 if (m_Index)
378 {
379 for (i = 0; i < m_IndexCnt; i++)
380 {
381 delete[] m_Index[i].m_Page;
382 delete[] m_Index[i].m_Name;
383 }
384 free(m_Index);
385 }
386 }
387
388 bool wxHtmlHelpData::LoadMSProject(wxHtmlBookRecord *book, wxFileSystem& fsys, const wxString& indexfile, const wxString& contentsfile)
389 {
390 wxFSFile *f;
391 char *buf;
392 int sz;
393 wxString string;
394
395 HP_Parser parser;
396 HP_TagHandler *handler = new HP_TagHandler(book);
397 parser.AddTagHandler(handler);
398
399 f = ( contentsfile.IsEmpty() ? (wxFSFile*) NULL : fsys.OpenFile(contentsfile) );
400 if (f)
401 {
402 sz = f->GetStream()->GetSize();
403 buf = new char[sz + 1];
404 buf[sz] = 0;
405 f->GetStream()->Read(buf, sz);
406 delete f;
407 handler->ReadIn(m_Contents, m_ContentsCnt);
408 parser.Parse(buf);
409 handler->WriteOut(m_Contents, m_ContentsCnt);
410 delete[] buf;
411 }
412 else
413 wxLogError(_("Cannot open contents file: %s"), contentsfile.c_str());
414
415 f = ( indexfile.IsEmpty() ? (wxFSFile*) NULL : fsys.OpenFile(indexfile) );
416 if (f)
417 {
418 sz = f->GetStream()->GetSize();
419 buf = new char[sz + 1];
420 buf[sz] = 0;
421 f->GetStream()->Read(buf, sz);
422 delete f;
423 handler->ReadIn(m_Index, m_IndexCnt);
424 parser.Parse(buf);
425 handler->WriteOut(m_Index, m_IndexCnt);
426 delete[] buf;
427 }
428 else if (!indexfile.IsEmpty())
429 wxLogError(_("Cannot open index file: %s"), indexfile.c_str());
430 return TRUE;
431 }
432
433
434
435
436 #if wxUSE_UNICODE
437
438 #define READ_STRING(f, s, lng) { char tmpc; for (int i = 0; i < lng; i++) { f->Read(&tmpc, 1); s[i] = (wxChar)tmpc;} }
439 #define WRITE_STRING(f, s, lng) { char tmpc; for (int i = 0; i < lng; i++) { tmpc = (char)s[i]; f->Write(&tmpc, 1);} }
440
441 #else
442
443 #define READ_STRING(f, s, lng) f->Read(s, lng * sizeof(char));
444 #define WRITE_STRING(f, s, lng) f->Write(s, lng * sizeof(char));
445
446 #endif
447
448
449 #define CURRENT_CACHED_BOOK_VERSION 1
450
451 bool wxHtmlHelpData::LoadCachedBook(wxHtmlBookRecord *book, wxInputStream *f)
452 {
453 int i, st;
454 wxInt32 x;
455 wxInt32 version;
456
457 /* load header - version info : */
458
459 f->Read(&x, sizeof(x));
460 version = wxINT32_SWAP_ON_BE(x);
461
462 if (version != CURRENT_CACHED_BOOK_VERSION)
463 {
464 wxLogError(_("Incorrect version of HTML help book"));
465 return FALSE;
466 // NOTE: when adding new version, please ensure backward compatibility!
467 }
468
469 /* load contents : */
470
471 f->Read(&x, sizeof(x));
472 st = m_ContentsCnt;
473 m_ContentsCnt += wxINT32_SWAP_ON_BE(x);
474 m_Contents = (wxHtmlContentsItem*) realloc(m_Contents,
475 (m_ContentsCnt / wxHTML_REALLOC_STEP + 1) *
476 wxHTML_REALLOC_STEP * sizeof(wxHtmlContentsItem));
477 for (i = st; i < m_ContentsCnt; i++)
478 {
479 f->Read(&x, sizeof(x));
480 m_Contents[i].m_Level = wxINT32_SWAP_ON_BE(x);
481 f->Read(&x, sizeof(x));
482 m_Contents[i].m_ID = wxINT32_SWAP_ON_BE(x);
483 f->Read(&x, sizeof(x)); x = wxINT32_SWAP_ON_BE(x);
484 m_Contents[i].m_Name = new wxChar[x];
485 READ_STRING(f, m_Contents[i].m_Name, x);
486 f->Read(&x, sizeof(x)); x = wxINT32_SWAP_ON_BE(x);
487 m_Contents[i].m_Page = new wxChar[x];
488 READ_STRING(f, m_Contents[i].m_Page, x);
489 m_Contents[i].m_Book = book;
490 }
491
492 /* load index : */
493
494 f->Read(&x, sizeof(x));
495 st = m_IndexCnt;
496 m_IndexCnt += wxINT32_SWAP_ON_BE(x);
497 m_Index = (wxHtmlContentsItem*) realloc(m_Index, (m_IndexCnt / wxHTML_REALLOC_STEP + 1) *
498 wxHTML_REALLOC_STEP * sizeof(wxHtmlContentsItem));
499 for (i = st; i < m_IndexCnt; i++)
500 {
501 f->Read(&x, sizeof(x)); x = wxINT32_SWAP_ON_BE(x);
502 m_Index[i].m_Name = new wxChar[x];
503 READ_STRING(f, m_Index[i].m_Name, x);
504 f->Read(&x, sizeof(x)); x = wxINT32_SWAP_ON_BE(x);
505 m_Index[i].m_Page = new wxChar[x];
506 READ_STRING(f, m_Index[i].m_Page, x);
507 m_Index[i].m_Book = book;
508 }
509 return TRUE;
510 }
511
512
513 bool wxHtmlHelpData::SaveCachedBook(wxHtmlBookRecord *book, wxOutputStream *f)
514 {
515 int i;
516 wxInt32 x;
517
518 /* save header - version info : */
519
520 x = wxINT32_SWAP_ON_BE(CURRENT_CACHED_BOOK_VERSION);
521 f->Write(&x, sizeof(x));
522
523 /* save contents : */
524
525 x = 0;
526 for (i = 0; i < m_ContentsCnt; i++) if (m_Contents[i].m_Book == book && m_Contents[i].m_Level > 0) x++;
527 x = wxINT32_SWAP_ON_BE(x);
528 f->Write(&x, sizeof(x));
529 for (i = 0; i < m_ContentsCnt; i++)
530 {
531 if (m_Contents[i].m_Book != book || m_Contents[i].m_Level == 0) continue;
532 x = wxINT32_SWAP_ON_BE(m_Contents[i].m_Level);
533 f->Write(&x, sizeof(x));
534 x = wxINT32_SWAP_ON_BE(m_Contents[i].m_ID);
535 f->Write(&x, sizeof(x));
536 x = wxINT32_SWAP_ON_BE(wxStrlen(m_Contents[i].m_Name) + 1);
537 f->Write(&x, sizeof(x));
538 WRITE_STRING(f, m_Contents[i].m_Name, x);
539 x = wxINT32_SWAP_ON_BE(wxStrlen(m_Contents[i].m_Page) + 1);
540 f->Write(&x, sizeof(x));
541 WRITE_STRING(f, m_Contents[i].m_Page, x);
542 }
543
544 /* save index : */
545
546 x = 0;
547 for (i = 0; i < m_IndexCnt; i++) if (m_Index[i].m_Book == book && m_Index[i].m_Level > 0) x++;
548 x = wxINT32_SWAP_ON_BE(x);
549 f->Write(&x, sizeof(x));
550 for (i = 0; i < m_IndexCnt; i++)
551 {
552 if (m_Index[i].m_Book != book || m_Index[i].m_Level == 0) continue;
553 x = wxINT32_SWAP_ON_BE(wxStrlen(m_Index[i].m_Name) + 1);
554 f->Write(&x, sizeof(x));
555 WRITE_STRING(f, m_Index[i].m_Name, x);
556 x = wxINT32_SWAP_ON_BE(wxStrlen(m_Index[i].m_Page) + 1);
557 f->Write(&x, sizeof(x));
558 WRITE_STRING(f, m_Index[i].m_Page, x);
559 }
560 return TRUE;
561 }
562
563
564 void wxHtmlHelpData::SetTempDir(const wxString& path)
565 {
566 if (path == wxEmptyString) m_TempPath = path;
567 else
568 {
569 if (wxIsAbsolutePath(path)) m_TempPath = path;
570 else m_TempPath = wxGetCwd() + _T("/") + path;
571
572 if (m_TempPath[m_TempPath.Length() - 1] != _T('/'))
573 m_TempPath << _T('/');
574 }
575 }
576
577
578
579 static wxString SafeFileName(const wxString& s)
580 {
581 wxString res(s);
582 res.Replace(wxT("#"), wxT("_"));
583 res.Replace(wxT(":"), wxT("_"));
584 res.Replace(wxT("\\"), wxT("_"));
585 res.Replace(wxT("/"), wxT("_"));
586 return res;
587 }
588
589 bool wxHtmlHelpData::AddBookParam(const wxFSFile& bookfile,
590 wxFontEncoding encoding,
591 const wxString& title, const wxString& contfile,
592 const wxString& indexfile, const wxString& deftopic,
593 const wxString& path)
594 {
595 wxFileSystem fsys;
596 wxFSFile *fi;
597 wxHtmlBookRecord *bookr;
598
599 int IndexOld = m_IndexCnt,
600 ContentsOld = m_ContentsCnt;
601
602 if (! path.IsEmpty())
603 fsys.ChangePathTo(path, TRUE);
604
605 bookr = new wxHtmlBookRecord(fsys.GetPath(), title, deftopic);
606
607 if (m_ContentsCnt % wxHTML_REALLOC_STEP == 0)
608 m_Contents = (wxHtmlContentsItem*) realloc(m_Contents, (m_ContentsCnt + wxHTML_REALLOC_STEP) * sizeof(wxHtmlContentsItem));
609 m_Contents[m_ContentsCnt].m_Level = 0;
610 m_Contents[m_ContentsCnt].m_ID = 0;
611 m_Contents[m_ContentsCnt].m_Page = new wxChar[deftopic.Length() + 1];
612 wxStrcpy(m_Contents[m_ContentsCnt].m_Page, deftopic.c_str());
613 m_Contents[m_ContentsCnt].m_Name = new wxChar [title.Length() + 1];
614 wxStrcpy(m_Contents[m_ContentsCnt].m_Name, title.c_str());
615 m_Contents[m_ContentsCnt].m_Book = bookr;
616
617 // store the contents index for later
618 int cont_start = m_ContentsCnt++;
619
620 // Try to find cached binary versions:
621 // 1. save file as book, but with .hhp.cached extension
622 // 2. same as 1. but in temp path
623 // 3. otherwise or if cache load failed, load it from MS.
624
625 fi = fsys.OpenFile(bookfile.GetLocation() + wxT(".cached"));
626
627 if (fi == NULL ||
628 fi->GetModificationTime() < bookfile.GetModificationTime() ||
629 !LoadCachedBook(bookr, fi->GetStream()))
630 {
631 if (fi != NULL) delete fi;
632 fi = fsys.OpenFile(m_TempPath + wxFileNameFromPath(bookfile.GetLocation()) + wxT(".cached"));
633 if (m_TempPath == wxEmptyString || fi == NULL ||
634 fi->GetModificationTime() < bookfile.GetModificationTime() ||
635 !LoadCachedBook(bookr, fi->GetStream()))
636 {
637 LoadMSProject(bookr, fsys, indexfile, contfile);
638 if (m_TempPath != wxEmptyString)
639 {
640 wxFileOutputStream *outs = new wxFileOutputStream(m_TempPath +
641 SafeFileName(wxFileNameFromPath(bookfile.GetLocation())) + wxT(".cached"));
642 SaveCachedBook(bookr, outs);
643 delete outs;
644 }
645 }
646 }
647
648 if (fi != NULL) delete fi;
649
650 // Now store the contents range
651 bookr->SetContentsRange(cont_start, m_ContentsCnt);
652
653 // Convert encoding, if neccessary:
654 if (encoding != wxFONTENCODING_SYSTEM)
655 {
656 wxFontEncodingArray a = wxEncodingConverter::GetPlatformEquivalents(encoding);
657 if (a.GetCount() != 0 && a[0] != encoding)
658 {
659 int i;
660 wxEncodingConverter conv;
661 conv.Init(encoding, a[0]);
662
663 for (i = IndexOld; i < m_IndexCnt; i++)
664 conv.Convert(m_Index[i].m_Name);
665 for (i = ContentsOld; i < m_ContentsCnt; i++)
666 conv.Convert(m_Contents[i].m_Name);
667 }
668 }
669
670 m_BookRecords.Add(bookr);
671 if (m_IndexCnt > 0)
672 qsort(m_Index, m_IndexCnt, sizeof(wxHtmlContentsItem), IndexCompareFunc);
673
674 return TRUE;
675 }
676
677
678 bool wxHtmlHelpData::AddBook(const wxString& book)
679 {
680 if (book.Right(4).Lower() == wxT(".zip") ||
681 book.Right(4).Lower() == wxT(".htb") /*html book*/)
682
683 {
684 wxFileSystem fsys;
685 wxString s;
686 bool rt = FALSE;
687
688 s = fsys.FindFirst(book + wxT("#zip:") + wxT("*.hhp"), wxFILE);
689 while (!s.IsEmpty())
690 {
691 if (AddBook(s)) rt = TRUE;
692 s = fsys.FindNext();
693 }
694
695 return rt;
696 }
697
698
699 else
700 {
701 wxFSFile *fi;
702 wxFileSystem fsys;
703 wxInputStream *s;
704 wxString bookFull;
705
706 int sz;
707 char *buff, *lineptr;
708 char linebuf[300];
709
710 wxString title = _("noname"),
711 safetitle,
712 start = wxEmptyString,
713 contents = wxEmptyString,
714 index = wxEmptyString,
715 charset = wxEmptyString;
716
717 if (wxIsAbsolutePath(book)) bookFull = book;
718 else bookFull = wxGetCwd() + "/" + book;
719
720 fi = fsys.OpenFile(bookFull);
721 if (fi == NULL)
722 {
723 wxLogError(_("Cannot open HTML help book: %s"), bookFull.c_str());
724 return FALSE;
725 }
726 fsys.ChangePathTo(bookFull);
727 s = fi->GetStream();
728 sz = s->GetSize();
729 buff = new char[sz + 1];
730 buff[sz] = 0;
731 s->Read(buff, sz);
732 lineptr = buff;
733
734 do {
735 lineptr = ReadLine(lineptr, linebuf);
736
737 if (strstr(linebuf, "Title=") == linebuf)
738 title = linebuf + strlen("Title=");
739 if (strstr(linebuf, "Default topic=") == linebuf)
740 start = linebuf + strlen("Default topic=");
741 if (strstr(linebuf, "Index file=") == linebuf)
742 index = linebuf + strlen("Index file=");
743 if (strstr(linebuf, "Contents file=") == linebuf)
744 contents = linebuf + strlen("Contents file=");
745 if (strstr(linebuf, "Charset=") == linebuf)
746 charset = linebuf + strlen("Charset=");
747 } while (lineptr != NULL);
748 delete[] buff;
749
750 wxFontEncoding enc;
751 if (charset == wxEmptyString) enc = wxFONTENCODING_SYSTEM;
752 else enc = wxTheFontMapper->CharsetToEncoding(charset);
753 bool rtval = AddBookParam(*fi, enc,
754 title, contents, index, start, fsys.GetPath());
755 delete fi;
756 return rtval;
757 }
758 }
759
760 wxString wxHtmlHelpData::FindPageByName(const wxString& x)
761 {
762 int cnt;
763 int i;
764 wxFileSystem fsys;
765 wxFSFile *f;
766 wxString url(wxEmptyString);
767
768 /* 1. try to open given file: */
769
770 cnt = m_BookRecords.GetCount();
771 for (i = 0; i < cnt; i++)
772 {
773 f = fsys.OpenFile(m_BookRecords[i].GetFullPath(x));
774 if (f)
775 {
776 url = m_BookRecords[i].GetFullPath(x);
777 delete f;
778 return url;
779 }
780 }
781
782
783 /* 2. try to find a book: */
784
785 for (i = 0; i < cnt; i++)
786 {
787 if (m_BookRecords[i].GetTitle() == x)
788 {
789 url = m_BookRecords[i].GetFullPath(m_BookRecords[i].GetStart());
790 return url;
791 }
792 }
793
794 /* 3. try to find in contents: */
795
796 cnt = m_ContentsCnt;
797 for (i = 0; i < cnt; i++)
798 {
799 if (wxStrcmp(m_Contents[i].m_Name, x) == 0)
800 {
801 url = m_Contents[i].GetFullPath();
802 return url;
803 }
804 }
805
806
807 /* 4. try to find in index: */
808
809 cnt = m_IndexCnt;
810 for (i = 0; i < cnt; i++)
811 {
812 if (wxStrcmp(m_Index[i].m_Name, x) == 0)
813 {
814 url = m_Index[i].GetFullPath();
815 return url;
816 }
817 }
818
819 return url;
820 }
821
822 wxString wxHtmlHelpData::FindPageById(int id)
823 {
824 int i;
825 wxString url(wxEmptyString);
826
827 for (i = 0; i < m_ContentsCnt; i++)
828 {
829 if (m_Contents[i].m_ID == id)
830 {
831 url = m_Contents[i].GetFullPath();
832 return url;
833 }
834 }
835
836 return url;
837 }
838
839 //----------------------------------------------------------------------------------
840 // wxHtmlSearchStatus functions
841 //----------------------------------------------------------------------------------
842
843 wxHtmlSearchStatus::wxHtmlSearchStatus(wxHtmlHelpData* data, const wxString& keyword,
844 bool case_sensitive, bool whole_words_only,
845 const wxString& book)
846 {
847 m_Data = data;
848 m_Keyword = keyword;
849 wxHtmlBookRecord* bookr = NULL;
850 if (book != wxEmptyString)
851 {
852 // we have to search in a specific book. Find it first
853 int i, cnt = data->m_BookRecords.GetCount();
854 for (i = 0; i < cnt; i++)
855 if (data->m_BookRecords[i].GetTitle() == book)
856 {
857 bookr = &(data->m_BookRecords[i]);
858 m_CurIndex = bookr->GetContentsStart();
859 m_MaxIndex = bookr->GetContentsEnd();
860 break;
861 }
862 // check; we won't crash if the book doesn't exist, but it's Bad Anyway.
863 wxASSERT(bookr);
864 }
865 if (! bookr)
866 {
867 // no book specified; search all books
868 m_CurIndex = 0;
869 m_MaxIndex = m_Data->m_ContentsCnt;
870 }
871 m_Engine.LookFor(keyword, case_sensitive, whole_words_only);
872 m_Active = (m_CurIndex < m_MaxIndex);
873 m_LastPage = NULL;
874 }
875
876 bool wxHtmlSearchStatus::Search()
877 {
878 wxFSFile *file;
879 int i = m_CurIndex; // shortcut
880 bool found = FALSE;
881 wxChar *thepage;
882
883 if (!m_Active)
884 {
885 // sanity check. Illegal use, but we'll try to prevent a crash anyway
886 wxASSERT(m_Active);
887 return FALSE;
888 }
889
890 m_Name = wxEmptyString;
891 m_ContentsItem = NULL;
892 thepage = m_Data->m_Contents[i].m_Page;
893
894 m_Active = (++m_CurIndex < m_MaxIndex);
895 // check if it is same page with different anchor:
896 if (m_LastPage != NULL)
897 {
898 wxChar *p1, *p2;
899 for (p1 = thepage, p2 = m_LastPage;
900 *p1 != 0 && *p1 != _T('#') && *p1 == *p2; p1++, p2++) {}
901
902 m_LastPage = thepage;
903
904 if (*p1 == 0 || *p1 == _T('#'))
905 return FALSE;
906 }
907 else m_LastPage = thepage;
908
909 wxFileSystem fsys;
910 file = fsys.OpenFile(m_Data->m_Contents[i].m_Book->GetFullPath(thepage));
911 if (file)
912 {
913 if (m_Engine.Scan(file->GetStream()))
914 {
915 m_Name = m_Data->m_Contents[i].m_Name;
916 m_ContentsItem = m_Data->m_Contents + i;
917 found = TRUE;
918 }
919 delete file;
920 }
921 return found;
922 }
923
924
925
926
927
928
929
930
931 //--------------------------------------------------------------------------------
932 // wxSearchEngine
933 //--------------------------------------------------------------------------------
934
935 void wxSearchEngine::LookFor(const wxString& keyword, bool case_sensitive, bool whole_words_only)
936 {
937 m_CaseSensitive = case_sensitive;
938 m_WholeWords = whole_words_only;
939 if (m_Keyword) delete[] m_Keyword;
940 m_Keyword = new wxChar[keyword.Length() + 1];
941 wxStrcpy(m_Keyword, keyword.c_str());
942
943 if (!m_CaseSensitive)
944 {
945 for (int i = wxStrlen(m_Keyword) - 1; i >= 0; i--)
946 {
947 if ((m_Keyword[i] >= wxT('A')) && (m_Keyword[i] <= wxT('Z')))
948 m_Keyword[i] += wxT('a') - wxT('A');
949 }
950 }
951 }
952
953
954
955 #define WHITESPACE(c) (c == ' ' || c == '\n' || c == '\r' || c == '\t')
956
957 bool wxSearchEngine::Scan(wxInputStream *stream)
958 {
959 wxASSERT_MSG(m_Keyword != NULL, wxT("wxSearchEngine::LookFor must be called before scanning!"));
960
961 int i, j;
962 int lng = stream ->GetSize();
963 int wrd = wxStrlen(m_Keyword);
964 bool found = FALSE;
965 char *buf = new char[lng + 1];
966 stream->Read(buf, lng);
967 buf[lng] = 0;
968
969 if (!m_CaseSensitive)
970 for (i = 0; i < lng; i++)
971 if ((buf[i] >= 'A') && (buf[i] <= 'Z')) buf[i] += 'a' - 'A';
972
973 if (m_WholeWords)
974 {
975 for (i = 0; i < lng - wrd; i++)
976 {
977 if (WHITESPACE(buf[i])) continue;
978 j = 0;
979 while ((j < wrd) && (buf[i + j] == m_Keyword[j])) j++;
980 if (j == wrd && WHITESPACE(buf[i + j])) { found = TRUE; break; }
981 }
982 }
983
984 else
985 {
986 for (i = 0; i < lng - wrd; i++)
987 {
988 j = 0;
989 while ((j < wrd) && (buf[i + j] == m_Keyword[j])) j++;
990 if (j == wrd) { found = TRUE; break; }
991 }
992 }
993
994 delete[] buf;
995 return found;
996 }
997
998
999
1000 #endif