fixed memory leaks
[wxWidgets.git] / src / unix / mimetype.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: unix/mimetype.cpp
3 // Purpose: classes and functions to manage MIME types
4 // Author: Vadim Zeitlin
5 // Modified by:
6 // Created: 23.09.98
7 // RCS-ID: $Id$
8 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence: wxWindows license (part of wxExtra library)
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation "mimetype.h"
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 #ifndef WX_PRECOMP
24 #include "wx/defs.h"
25 #endif
26
27 #if (wxUSE_FILE && wxUSE_TEXTFILE) || defined(__WXMSW__)
28
29 #ifndef WX_PRECOMP
30 #include "wx/string.h"
31 #if wxUSE_GUI
32 #include "wx/icon.h"
33 #endif
34 #endif //WX_PRECOMP
35
36
37 #include "wx/log.h"
38 #include "wx/file.h"
39 #include "wx/intl.h"
40 #include "wx/dynarray.h"
41 #include "wx/confbase.h"
42
43 #include "wx/ffile.h"
44 #include "wx/textfile.h"
45 #include "wx/dir.h"
46 #include "wx/utils.h"
47 #include "wx/tokenzr.h"
48
49 #include "wx/unix/mimetype.h"
50
51 // other standard headers
52 #include <ctype.h>
53
54 // in case we're compiling in non-GUI mode
55 class WXDLLEXPORT wxIcon;
56
57 // ----------------------------------------------------------------------------
58 // private classes
59 // ----------------------------------------------------------------------------
60
61
62 // this class uses both mailcap and mime.types to gather information about file
63 // types.
64 //
65 // The information about mailcap file was extracted from metamail(1) sources and
66 // documentation.
67 //
68 // Format of mailcap file: spaces are ignored, each line is either a comment
69 // (starts with '#') or a line of the form <field1>;<field2>;...;<fieldN>.
70 // A backslash can be used to quote semicolons and newlines (and, in fact,
71 // anything else including itself).
72 //
73 // The first field is always the MIME type in the form of type/subtype (see RFC
74 // 822) where subtype may be '*' meaning "any". Following metamail, we accept
75 // "type" which means the same as "type/*", although I'm not sure whether this
76 // is standard.
77 //
78 // The second field is always the command to run. It is subject to
79 // parameter/filename expansion described below.
80 //
81 // All the following fields are optional and may not be present at all. If
82 // they're present they may appear in any order, although each of them should
83 // appear only once. The optional fields are the following:
84 // * notes=xxx is an uninterpreted string which is silently ignored
85 // * test=xxx is the command to be used to determine whether this mailcap line
86 // applies to our data or not. The RHS of this field goes through the
87 // parameter/filename expansion (as the 2nd field) and the resulting string
88 // is executed. The line applies only if the command succeeds, i.e. returns 0
89 // exit code.
90 // * print=xxx is the command to be used to print (and not view) the data of
91 // this type (parameter/filename expansion is done here too)
92 // * edit=xxx is the command to open/edit the data of this type
93 // * needsterminal means that a new console must be created for the viewer
94 // * copiousoutput means that the viewer doesn't interact with the user but
95 // produces (possibly) a lof of lines of output on stdout (i.e. "cat" is a
96 // good example), thus it might be a good idea to use some kind of paging
97 // mechanism.
98 // * textualnewlines means not to perform CR/LF translation (not honored)
99 // * compose and composetyped fields are used to determine the program to be
100 // called to create a new message pert in the specified format (unused).
101 //
102 // Parameter/filename xpansion:
103 // * %s is replaced with the (full) file name
104 // * %t is replaced with MIME type/subtype of the entry
105 // * for multipart type only %n is replaced with the nnumber of parts and %F is
106 // replaced by an array of (content-type, temporary file name) pairs for all
107 // message parts (TODO)
108 // * %{parameter} is replaced with the value of parameter taken from
109 // Content-type header line of the message.
110 //
111 // FIXME any docs with real descriptions of these files??
112 //
113 // There are 2 possible formats for mime.types file, one entry per line (used
114 // for global mime.types) and "expanded" format where an entry takes multiple
115 // lines (used for users mime.types).
116 //
117 // For both formats spaces are ignored and lines starting with a '#' are
118 // comments. Each record has one of two following forms:
119 // a) for "brief" format:
120 // <mime type> <space separated list of extensions>
121 // b) for "expanded" format:
122 // type=<mime type> \ desc="<description>" \ exts="ext"
123 //
124 // We try to autodetect the format of mime.types: if a non-comment line starts
125 // with "type=" we assume the second format, otherwise the first one.
126
127 // there may be more than one entry for one and the same mime type, to
128 // choose the right one we have to run the command specified in the test
129 // field on our data.
130 class MailCapEntry
131 {
132 public:
133 // ctor
134 MailCapEntry(const wxString& openCmd,
135 const wxString& printCmd,
136 const wxString& testCmd)
137 : m_openCmd(openCmd), m_printCmd(printCmd), m_testCmd(testCmd)
138 {
139 m_next = NULL;
140 }
141
142 ~MailCapEntry()
143 {
144 if (m_next) delete m_next;
145 }
146
147 // accessors
148 const wxString& GetOpenCmd() const { return m_openCmd; }
149 const wxString& GetPrintCmd() const { return m_printCmd; }
150 const wxString& GetTestCmd() const { return m_testCmd; }
151
152 MailCapEntry *GetNext() const { return m_next; }
153
154 // operations
155 // prepend this element to the list
156 void Prepend(MailCapEntry *next) { m_next = next; }
157 // insert into the list at given position
158 void Insert(MailCapEntry *next, size_t pos)
159 {
160 // FIXME slooow...
161 MailCapEntry *cur;
162 size_t n = 0;
163 for ( cur = next; cur != NULL; cur = cur->m_next, n++ ) {
164 if ( n == pos )
165 break;
166 }
167
168 wxASSERT_MSG( n == pos, wxT("invalid position in MailCapEntry::Insert") );
169
170 m_next = cur->m_next;
171 cur->m_next = this;
172 }
173 // append this element to the list
174 void Append(MailCapEntry *next)
175 {
176 wxCHECK_RET( next != NULL, wxT("Append()ing to what?") );
177
178 // FIXME slooow...
179 MailCapEntry *cur;
180 for ( cur = next; cur->m_next != NULL; cur = cur->m_next )
181 ;
182
183 cur->m_next = this;
184
185 wxASSERT_MSG( !m_next, wxT("Append()ing element already in the list?") );
186 }
187
188 private:
189 wxString m_openCmd, // command to use to open/view the file
190 m_printCmd, // print
191 m_testCmd; // only apply this entry if test yields
192 // true (i.e. the command returns 0)
193
194 MailCapEntry *m_next; // in the linked list
195 };
196
197
198 // the base class which may be used to find an icon for the MIME type
199 class wxMimeTypeIconHandler
200 {
201 public:
202 virtual bool GetIcon(const wxString& mimetype, wxIcon *icon) = 0;
203
204 // this function fills manager with MIME types information gathered
205 // (as side effect) when searching for icons. This may be particularly
206 // useful if mime.types is incomplete (e.g. RedHat distributions).
207 virtual void GetMimeInfoRecords(wxMimeTypesManagerImpl *manager) = 0;
208 };
209
210
211 // the icon handler which uses GNOME MIME database
212 class wxGNOMEIconHandler : public wxMimeTypeIconHandler
213 {
214 public:
215 virtual bool GetIcon(const wxString& mimetype, wxIcon *icon);
216 virtual void GetMimeInfoRecords(wxMimeTypesManagerImpl *manager);
217
218 private:
219 void Init();
220 void LoadIconsFromKeyFile(const wxString& filename);
221 void LoadKeyFilesFromDir(const wxString& dirbase);
222
223 void LoadMimeTypesFromMimeFile(const wxString& filename, wxMimeTypesManagerImpl *manager);
224 void LoadMimeFilesFromDir(const wxString& dirbase, wxMimeTypesManagerImpl *manager);
225
226 static bool m_inited;
227
228 static wxSortedArrayString ms_mimetypes;
229 static wxArrayString ms_icons;
230 };
231
232 // the icon handler which uses KDE MIME database
233 class wxKDEIconHandler : public wxMimeTypeIconHandler
234 {
235 public:
236 virtual bool GetIcon(const wxString& mimetype, wxIcon *icon);
237 virtual void GetMimeInfoRecords(wxMimeTypesManagerImpl *manager);
238
239 private:
240 void LoadLinksForMimeSubtype(const wxString& dirbase,
241 const wxString& subdir,
242 const wxString& filename,
243 const wxArrayString& icondirs);
244 void LoadLinksForMimeType(const wxString& dirbase,
245 const wxString& subdir,
246 const wxArrayString& icondirs);
247 void LoadLinkFilesFromDir(const wxString& dirbase,
248 const wxArrayString& icondirs);
249 void Init();
250
251 static bool m_inited;
252
253 static wxSortedArrayString ms_mimetypes;
254 static wxArrayString ms_icons;
255
256 static wxArrayString ms_infoTypes;
257 static wxArrayString ms_infoDescriptions;
258 static wxArrayString ms_infoExtensions;
259 };
260
261
262
263 // ----------------------------------------------------------------------------
264 // various statics
265 // ----------------------------------------------------------------------------
266
267 static wxGNOMEIconHandler gs_iconHandlerGNOME;
268 static wxKDEIconHandler gs_iconHandlerKDE;
269
270 bool wxGNOMEIconHandler::m_inited = FALSE;
271 wxSortedArrayString wxGNOMEIconHandler::ms_mimetypes;
272 wxArrayString wxGNOMEIconHandler::ms_icons;
273
274 bool wxKDEIconHandler::m_inited = FALSE;
275 wxSortedArrayString wxKDEIconHandler::ms_mimetypes;
276 wxArrayString wxKDEIconHandler::ms_icons;
277
278 wxArrayString wxKDEIconHandler::ms_infoTypes;
279 wxArrayString wxKDEIconHandler::ms_infoDescriptions;
280 wxArrayString wxKDEIconHandler::ms_infoExtensions;
281
282
283 ArrayIconHandlers wxMimeTypesManagerImpl::ms_iconHandlers;
284
285 // ----------------------------------------------------------------------------
286 // wxGNOMEIconHandler
287 // ----------------------------------------------------------------------------
288
289 // GNOME stores the info we're interested in in several locations:
290 // 1. xxx.keys files under /usr/share/mime-info
291 // 2. xxx.keys files under ~/.gnome/mime-info
292 //
293 // The format of xxx.keys file is the following:
294 //
295 // mimetype/subtype:
296 // field=value
297 //
298 // with blank lines separating the entries and indented lines starting with
299 // TABs. We're interested in the field icon-filename whose value is the path
300 // containing the icon.
301
302 void wxGNOMEIconHandler::LoadIconsFromKeyFile(const wxString& filename)
303 {
304 wxTextFile textfile(filename);
305 if ( !textfile.Open() )
306 return;
307
308 // values for the entry being parsed
309 wxString curMimeType, curIconFile;
310
311 const wxChar *pc;
312 size_t nLineCount = textfile.GetLineCount();
313 for ( size_t nLine = 0; ; nLine++ )
314 {
315 if ( nLine < nLineCount )
316 {
317 pc = textfile[nLine].c_str();
318 if ( *pc == _T('#') )
319 {
320 // skip comments
321 continue;
322 }
323 }
324 else
325 {
326 // so that we will fall into the "if" below
327 pc = NULL;
328 }
329
330 if ( !pc || !*pc )
331 {
332 // end of the entry
333 if ( !!curMimeType && !!curIconFile )
334 {
335 // do we already know this mimetype?
336 int i = ms_mimetypes.Index(curMimeType);
337 if ( i == wxNOT_FOUND )
338 {
339 // add a new entry
340 size_t n = ms_mimetypes.Add(curMimeType);
341 ms_icons.Insert(curIconFile, n);
342 }
343 else
344 {
345 // replace the existing one (this means that the directories
346 // should be searched in order of increased priority!)
347 ms_icons[(size_t)i] = curIconFile;
348 }
349 }
350
351 if ( !pc )
352 {
353 // the end - this can only happen if nLine == nLineCount
354 break;
355 }
356
357 curIconFile.Empty();
358
359 continue;
360 }
361
362 // what do we have here?
363 if ( *pc == _T('\t') )
364 {
365 // this is a field=value ling
366 pc++; // skip leading TAB
367
368 static const int lenField = 13; // strlen("icon-filename")
369 if ( wxStrncmp(pc, _T("icon-filename"), lenField) == 0 )
370 {
371 // skip '=' which follows and take everything left until the end
372 // of line
373 curIconFile = pc + lenField + 1;
374 }
375 //else: some other field, we don't care
376 }
377 else
378 {
379 // this is the start of the new section
380 curMimeType.Empty();
381
382 while ( *pc != _T(':') && *pc != _T('\0') )
383 {
384 curMimeType += *pc++;
385 }
386 }
387 }
388 }
389
390 void wxGNOMEIconHandler::LoadKeyFilesFromDir(const wxString& dirbase)
391 {
392 wxASSERT_MSG( !!dirbase && !wxEndsWithPathSeparator(dirbase),
393 _T("base directory shouldn't end with a slash") );
394
395 wxString dirname = dirbase;
396 dirname << _T("/mime-info");
397
398 if ( !wxDir::Exists(dirname) )
399 return;
400
401 wxDir dir(dirname);
402 if ( !dir.IsOpened() )
403 return;
404
405 // we will concatenate it with filename to get the full path below
406 dirname += _T('/');
407
408 wxString filename;
409 bool cont = dir.GetFirst(&filename, _T("*.keys"), wxDIR_FILES);
410 while ( cont )
411 {
412 LoadIconsFromKeyFile(dirname + filename);
413
414 cont = dir.GetNext(&filename);
415 }
416 }
417
418
419 void wxGNOMEIconHandler::LoadMimeTypesFromMimeFile(const wxString& filename, wxMimeTypesManagerImpl *manager)
420 {
421 wxTextFile textfile(filename);
422 if ( !textfile.Open() )
423 return;
424
425 // values for the entry being parsed
426 wxString curMimeType, curExtList;
427
428 const wxChar *pc;
429 size_t nLineCount = textfile.GetLineCount();
430 for ( size_t nLine = 0; ; nLine++ )
431 {
432 if ( nLine < nLineCount )
433 {
434 pc = textfile[nLine].c_str();
435 if ( *pc == _T('#') )
436 {
437 // skip comments
438 continue;
439 }
440 }
441 else
442 {
443 // so that we will fall into the "if" below
444 pc = NULL;
445 }
446
447 if ( !pc || !*pc )
448 {
449 // end of the entry
450 if ( !!curMimeType && !!curExtList )
451 {
452 manager -> AddMimeTypeInfo(curMimeType, curExtList, wxEmptyString);
453 }
454
455 if ( !pc )
456 {
457 // the end - this can only happen if nLine == nLineCount
458 break;
459 }
460
461 curExtList.Empty();
462
463 continue;
464 }
465
466 // what do we have here?
467 if ( *pc == _T('\t') )
468 {
469 // this is a field=value ling
470 pc++; // skip leading TAB
471
472 static const int lenField = 4; // strlen("ext:")
473 if ( wxStrncmp(pc, _T("ext:"), lenField) == 0 )
474 {
475 // skip ' ' which follows and take everything left until the end
476 // of line
477 curExtList = pc + lenField + 1;
478 }
479 //else: some other field, we don't care
480 }
481 else
482 {
483 // this is the start of the new section
484 curMimeType.Empty();
485
486 while ( *pc != _T(':') && *pc != _T('\0') )
487 {
488 curMimeType += *pc++;
489 }
490 }
491 }
492 }
493
494
495 void wxGNOMEIconHandler::LoadMimeFilesFromDir(const wxString& dirbase, wxMimeTypesManagerImpl *manager)
496 {
497 wxASSERT_MSG( !!dirbase && !wxEndsWithPathSeparator(dirbase),
498 _T("base directory shouldn't end with a slash") );
499
500 wxString dirname = dirbase;
501 dirname << _T("/mime-info");
502
503 if ( !wxDir::Exists(dirname) )
504 return;
505
506 wxDir dir(dirname);
507 if ( !dir.IsOpened() )
508 return;
509
510 // we will concatenate it with filename to get the full path below
511 dirname += _T('/');
512
513 wxString filename;
514 bool cont = dir.GetFirst(&filename, _T("*.mime"), wxDIR_FILES);
515 while ( cont )
516 {
517 LoadMimeTypesFromMimeFile(dirname + filename, manager);
518
519 cont = dir.GetNext(&filename);
520 }
521 }
522
523
524 void wxGNOMEIconHandler::Init()
525 {
526 wxArrayString dirs;
527 dirs.Add(_T("/usr/share"));
528 dirs.Add(_T("/usr/local/share"));
529
530 wxString gnomedir;
531 wxGetHomeDir( &gnomedir );
532 gnomedir += _T("/.gnome");
533 dirs.Add( gnomedir );
534
535 size_t nDirs = dirs.GetCount();
536 for ( size_t nDir = 0; nDir < nDirs; nDir++ )
537 {
538 LoadKeyFilesFromDir(dirs[nDir]);
539 }
540
541 m_inited = TRUE;
542 }
543
544
545 void wxGNOMEIconHandler::GetMimeInfoRecords(wxMimeTypesManagerImpl *manager)
546 {
547 if ( !m_inited )
548 {
549 Init();
550 }
551
552 wxArrayString dirs;
553 dirs.Add(_T("/usr/share"));
554 dirs.Add(_T("/usr/local/share"));
555
556 wxString gnomedir;
557 wxGetHomeDir( &gnomedir );
558 gnomedir += _T("/.gnome");
559 dirs.Add( gnomedir );
560
561 size_t nDirs = dirs.GetCount();
562 for ( size_t nDir = 0; nDir < nDirs; nDir++ )
563 {
564 LoadMimeFilesFromDir(dirs[nDir], manager);
565 }
566 }
567
568 #if wxUSE_GUI
569 #define WXUNUSED_UNLESS_GUI(p) p
570 #else
571 #define WXUNUSED_UNLESS_GUI(p)
572 #endif
573
574 bool wxGNOMEIconHandler::GetIcon(const wxString& mimetype,
575 wxIcon * WXUNUSED_UNLESS_GUI(icon))
576 {
577 if ( !m_inited )
578 {
579 Init();
580 }
581
582 int index = ms_mimetypes.Index(mimetype);
583 if ( index == wxNOT_FOUND )
584 return FALSE;
585
586 wxString iconname = ms_icons[(size_t)index];
587
588 #if wxUSE_GUI
589 wxLogNull nolog;
590 wxIcon icn;
591 if (iconname.Right(4).MakeUpper() == _T(".XPM"))
592 icn = wxIcon(iconname);
593 else
594 icn = wxIcon(iconname, wxBITMAP_TYPE_ANY);
595 if ( !icn.Ok() )
596 return FALSE;
597
598 if ( icon )
599 *icon = icn;
600 #else
601 // helpful for testing in console mode
602 wxLogDebug(_T("Found GNOME icon for '%s': '%s'\n"),
603 mimetype.c_str(), iconname.c_str());
604 #endif
605
606 return TRUE;
607 }
608
609 // ----------------------------------------------------------------------------
610 // wxKDEIconHandler
611 // ----------------------------------------------------------------------------
612
613 // KDE stores the icon info in its .kdelnk files. The file for mimetype/subtype
614 // may be found in either of the following locations
615 //
616 // 1. $KDEDIR/share/mimelnk/mimetype/subtype.kdelnk
617 // 2. ~/.kde/share/mimelnk/mimetype/subtype.kdelnk
618 //
619 // The format of a .kdelnk file is almost the same as the one used by
620 // wxFileConfig, i.e. there are groups, comments and entries. The icon is the
621 // value for the entry "Type"
622
623 void wxKDEIconHandler::LoadLinksForMimeSubtype(const wxString& dirbase,
624 const wxString& subdir,
625 const wxString& filename,
626 const wxArrayString& icondirs)
627 {
628 wxFFile file(dirbase + filename);
629 if ( !file.IsOpened() )
630 return;
631
632 // construct mimetype from the directory name and the basename of the
633 // file (it always has .kdelnk extension)
634 wxString mimetype;
635 mimetype << subdir << _T('/') << filename.BeforeLast(_T('.'));
636
637 // these files are small, slurp the entire file at once
638 wxString text;
639 if ( !file.ReadAll(&text) )
640 return;
641
642 int pos;
643 const wxChar *pc;
644
645 // before trying to find an icon, grab mimetype information
646 // (because BFU's machine would hardly have well-edited mime.types but (s)he might
647 // have edited it in control panel...)
648
649 wxString mime_extension, mime_desc;
650
651 pos = wxNOT_FOUND;
652 if (wxGetLocale() != NULL)
653 mime_desc = _T("Comment[") + wxGetLocale()->GetName() + _T("]=");
654 if (pos == wxNOT_FOUND) mime_desc = _T("Comment=");
655 pos = text.Find(mime_desc);
656 if (pos == wxNOT_FOUND) mime_desc = wxEmptyString;
657 else
658 {
659 pc = text.c_str() + pos + mime_desc.Length();
660 mime_desc = wxEmptyString;
661 while ( *pc && *pc != _T('\n') ) mime_desc += *pc++;
662 }
663
664 pos = text.Find(_T("Patterns="));
665 if (pos != wxNOT_FOUND)
666 {
667 wxString exts;
668 pc = text.c_str() + pos + 9;
669 while ( *pc && *pc != _T('\n') ) exts += *pc++;
670 wxStringTokenizer tokenizer(exts, _T(";"));
671 wxString e;
672
673 while (tokenizer.HasMoreTokens())
674 {
675 e = tokenizer.GetNextToken();
676 if (e.Left(2) != _T("*.")) continue; // don't support too difficult patterns
677 mime_extension << e.Mid(2);
678 mime_extension << _T(' ');
679 }
680 mime_extension.RemoveLast();
681 }
682
683 ms_infoTypes.Add(mimetype);
684 ms_infoDescriptions.Add(mime_desc);
685 ms_infoExtensions.Add(mime_extension);
686
687 // ok, now we can take care of icon:
688
689 pos = text.Find(_T("Icon="));
690 if ( pos == wxNOT_FOUND )
691 {
692 // no icon info
693 return;
694 }
695
696 wxString icon;
697
698 pc = text.c_str() + pos + 5; // 5 == strlen("Icon=")
699 while ( *pc && *pc != _T('\n') )
700 {
701 icon += *pc++;
702 }
703
704 if ( !!icon )
705 {
706 // we must check if the file exists because it may be stored
707 // in many locations, at least ~/.kde and $KDEDIR
708 size_t nDir, nDirs = icondirs.GetCount();
709 for ( nDir = 0; nDir < nDirs; nDir++ )
710 if (wxFileExists(icondirs[nDir] + icon))
711 {
712 icon.Prepend(icondirs[nDir]);
713 break;
714 }
715 if (nDir == nDirs) return; //does not exist
716
717 // do we already have this MIME type?
718 int i = ms_mimetypes.Index(mimetype);
719 if ( i == wxNOT_FOUND )
720 {
721 // add it
722 size_t n = ms_mimetypes.Add(mimetype);
723 ms_icons.Insert(icon, n);
724 }
725 else
726 {
727 // replace the old value
728 ms_icons[(size_t)i] = icon;
729 }
730 }
731 }
732
733 void wxKDEIconHandler::LoadLinksForMimeType(const wxString& dirbase,
734 const wxString& subdir,
735 const wxArrayString& icondirs)
736 {
737 wxString dirname = dirbase;
738 dirname += subdir;
739 wxDir dir(dirname);
740 if ( !dir.IsOpened() )
741 return;
742
743 dirname += _T('/');
744
745 wxString filename;
746 bool cont = dir.GetFirst(&filename, _T("*.kdelnk"), wxDIR_FILES);
747 while ( cont )
748 {
749 LoadLinksForMimeSubtype(dirname, subdir, filename, icondirs);
750
751 cont = dir.GetNext(&filename);
752 }
753 }
754
755 void wxKDEIconHandler::LoadLinkFilesFromDir(const wxString& dirbase,
756 const wxArrayString& icondirs)
757 {
758 wxASSERT_MSG( !!dirbase && !wxEndsWithPathSeparator(dirbase),
759 _T("base directory shouldn't end with a slash") );
760
761 wxString dirname = dirbase;
762 dirname << _T("/mimelnk");
763
764 if ( !wxDir::Exists(dirname) )
765 return;
766
767 wxDir dir(dirname);
768 if ( !dir.IsOpened() )
769 return;
770
771 // we will concatenate it with dir name to get the full path below
772 dirname += _T('/');
773
774 wxString subdir;
775 bool cont = dir.GetFirst(&subdir, wxEmptyString, wxDIR_DIRS);
776 while ( cont )
777 {
778 LoadLinksForMimeType(dirname, subdir, icondirs);
779
780 cont = dir.GetNext(&subdir);
781 }
782 }
783
784 void wxKDEIconHandler::Init()
785 {
786 wxArrayString dirs;
787 wxArrayString icondirs;
788
789 // settings in ~/.kde have maximal priority
790 dirs.Add(wxGetHomeDir() + _T("/.kde/share"));
791 icondirs.Add(wxGetHomeDir() + _T("/.kde/share/icons/"));
792
793 // the variable KDEDIR is set when KDE is running
794 const char *kdedir = getenv("KDEDIR");
795 if ( kdedir )
796 {
797 dirs.Add(wxString(kdedir) + _T("/share"));
798 icondirs.Add(wxString(kdedir) + _T("/share/icons/"));
799 }
800 else
801 {
802 // try to guess KDEDIR
803 dirs.Add(_T("/usr/share"));
804 dirs.Add(_T("/opt/kde/share"));
805 icondirs.Add(_T("/usr/share/icons/"));
806 icondirs.Add(_T("/usr/X11R6/share/icons/")); // Debian/Corel linux
807 icondirs.Add(_T("/opt/kde/share/icons/"));
808 }
809
810 size_t nDirs = dirs.GetCount();
811 for ( size_t nDir = 0; nDir < nDirs; nDir++ )
812 {
813 LoadLinkFilesFromDir(dirs[nDir], icondirs);
814 }
815
816 m_inited = TRUE;
817 }
818
819 bool wxKDEIconHandler::GetIcon(const wxString& mimetype,
820 wxIcon * WXUNUSED_UNLESS_GUI(icon))
821 {
822 if ( !m_inited )
823 {
824 Init();
825 }
826
827 int index = ms_mimetypes.Index(mimetype);
828 if ( index == wxNOT_FOUND )
829 return FALSE;
830
831 wxString iconname = ms_icons[(size_t)index];
832
833 #if wxUSE_GUI
834 wxLogNull nolog;
835 wxIcon icn;
836 if (iconname.Right(4).MakeUpper() == _T(".XPM"))
837 icn = wxIcon(iconname);
838 else
839 icn = wxIcon(iconname, wxBITMAP_TYPE_ANY);
840
841 if ( !icn.Ok() )
842 return FALSE;
843
844 if ( icon )
845 *icon = icn;
846 #else
847 // helpful for testing in console mode
848 wxLogDebug(_T("Found KDE icon for '%s': '%s'\n"),
849 mimetype.c_str(), iconname.c_str());
850 #endif
851
852 return TRUE;
853 }
854
855
856 void wxKDEIconHandler::GetMimeInfoRecords(wxMimeTypesManagerImpl *manager)
857 {
858 if ( !m_inited ) Init();
859
860 size_t cnt = ms_infoTypes.GetCount();
861 for (unsigned i = 0; i < cnt; i++)
862 manager -> AddMimeTypeInfo(ms_infoTypes[i], ms_infoExtensions[i], ms_infoDescriptions[i]);
863 }
864
865
866 // ----------------------------------------------------------------------------
867 // wxFileTypeImpl (Unix)
868 // ----------------------------------------------------------------------------
869
870 MailCapEntry *
871 wxFileTypeImpl::GetEntry(const wxFileType::MessageParameters& params) const
872 {
873 wxString command;
874 MailCapEntry *entry = m_manager->m_aEntries[m_index[0]];
875 while ( entry != NULL ) {
876 // notice that an empty command would always succeed (it's ok)
877 command = wxFileType::ExpandCommand(entry->GetTestCmd(), params);
878
879 if ( command.IsEmpty() || (wxSystem(command) == 0) ) {
880 // ok, passed
881 wxLogTrace(wxT("Test '%s' for mime type '%s' succeeded."),
882 command.c_str(), params.GetMimeType().c_str());
883 break;
884 }
885 else {
886 wxLogTrace(wxT("Test '%s' for mime type '%s' failed."),
887 command.c_str(), params.GetMimeType().c_str());
888 }
889
890 entry = entry->GetNext();
891 }
892
893 return entry;
894 }
895
896 bool wxFileTypeImpl::GetIcon(wxIcon *icon) const
897 {
898 wxArrayString mimetypes;
899 GetMimeTypes(mimetypes);
900
901 ArrayIconHandlers& handlers = m_manager->GetIconHandlers();
902 size_t count = handlers.GetCount();
903 size_t counttypes = mimetypes.GetCount();
904 for ( size_t n = 0; n < count; n++ )
905 {
906 for ( size_t n2 = 0; n2 < counttypes; n2++ )
907 {
908 if ( handlers[n]->GetIcon(mimetypes[n2], icon) )
909 return TRUE;
910 }
911 }
912
913 return FALSE;
914 }
915
916
917 bool
918 wxFileTypeImpl::GetMimeTypes(wxArrayString& mimeTypes) const
919 {
920 mimeTypes.Clear();
921 for (size_t i = 0; i < m_index.GetCount(); i++)
922 mimeTypes.Add(m_manager->m_aTypes[m_index[i]]);
923 return TRUE;
924 }
925
926
927 bool
928 wxFileTypeImpl::GetExpandedCommand(wxString *expandedCmd,
929 const wxFileType::MessageParameters& params,
930 bool open) const
931 {
932 MailCapEntry *entry = GetEntry(params);
933 if ( entry == NULL ) {
934 // all tests failed...
935 return FALSE;
936 }
937
938 wxString cmd = open ? entry->GetOpenCmd() : entry->GetPrintCmd();
939 if ( cmd.IsEmpty() ) {
940 // may happen, especially for "print"
941 return FALSE;
942 }
943
944 *expandedCmd = wxFileType::ExpandCommand(cmd, params);
945 return TRUE;
946 }
947
948 bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
949 {
950 wxString strExtensions = m_manager->GetExtension(m_index[0]);
951 extensions.Empty();
952
953 // one extension in the space or comma delimitid list
954 wxString strExt;
955 for ( const wxChar *p = strExtensions; ; p++ ) {
956 if ( *p == wxT(' ') || *p == wxT(',') || *p == wxT('\0') ) {
957 if ( !strExt.IsEmpty() ) {
958 extensions.Add(strExt);
959 strExt.Empty();
960 }
961 //else: repeated spaces (shouldn't happen, but it's not that
962 // important if it does happen)
963
964 if ( *p == wxT('\0') )
965 break;
966 }
967 else if ( *p == wxT('.') ) {
968 // remove the dot from extension (but only if it's the first char)
969 if ( !strExt.IsEmpty() ) {
970 strExt += wxT('.');
971 }
972 //else: no, don't append it
973 }
974 else {
975 strExt += *p;
976 }
977 }
978
979 return TRUE;
980 }
981
982 // ----------------------------------------------------------------------------
983 // wxMimeTypesManagerImpl (Unix)
984 // ----------------------------------------------------------------------------
985
986 /* static */
987 ArrayIconHandlers& wxMimeTypesManagerImpl::GetIconHandlers()
988 {
989 if ( ms_iconHandlers.GetCount() == 0 )
990 {
991 ms_iconHandlers.Add(&gs_iconHandlerGNOME);
992 ms_iconHandlers.Add(&gs_iconHandlerKDE);
993 }
994
995 return ms_iconHandlers;
996 }
997
998 // read system and user mailcaps (TODO implement mime.types support)
999 wxMimeTypesManagerImpl::wxMimeTypesManagerImpl()
1000 {
1001 // directories where we look for mailcap and mime.types by default
1002 // (taken from metamail(1) sources)
1003 static const wxChar *aStandardLocations[] =
1004 {
1005 wxT("/etc"),
1006 wxT("/usr/etc"),
1007 wxT("/usr/local/etc"),
1008 wxT("/etc/mail"),
1009 wxT("/usr/public/lib")
1010 };
1011
1012 // first read the system wide file(s)
1013 size_t n;
1014 for ( n = 0; n < WXSIZEOF(aStandardLocations); n++ ) {
1015 wxString dir = aStandardLocations[n];
1016
1017 wxString file = dir + wxT("/mailcap");
1018 if ( wxFile::Exists(file) ) {
1019 ReadMailcap(file);
1020 }
1021
1022 file = dir + wxT("/mime.types");
1023 if ( wxFile::Exists(file) ) {
1024 ReadMimeTypes(file);
1025 }
1026 }
1027
1028 wxString strHome = wxGetenv(wxT("HOME"));
1029
1030 // and now the users mailcap
1031 wxString strUserMailcap = strHome + wxT("/.mailcap");
1032 if ( wxFile::Exists(strUserMailcap) ) {
1033 ReadMailcap(strUserMailcap);
1034 }
1035
1036 // read the users mime.types
1037 wxString strUserMimeTypes = strHome + wxT("/.mime.types");
1038 if ( wxFile::Exists(strUserMimeTypes) ) {
1039 ReadMimeTypes(strUserMimeTypes);
1040 }
1041
1042 // read KDE/GNOME tables
1043 ArrayIconHandlers& handlers = GetIconHandlers();
1044 size_t count = handlers.GetCount();
1045 for ( size_t hn = 0; hn < count; hn++ )
1046 handlers[hn]->GetMimeInfoRecords(this);
1047 }
1048
1049
1050 wxMimeTypesManagerImpl::~wxMimeTypesManagerImpl()
1051 {
1052 size_t cnt = m_aEntries.GetCount();
1053 for (size_t i = 0; i < cnt; i++) delete m_aEntries[i];
1054 }
1055
1056
1057 wxFileType *
1058 wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& ext)
1059 {
1060 wxFileType *fileType = NULL;
1061 size_t count = m_aExtensions.GetCount();
1062 for ( size_t n = 0; n < count; n++ ) {
1063 wxString extensions = m_aExtensions[n];
1064 while ( !extensions.IsEmpty() ) {
1065 wxString field = extensions.BeforeFirst(wxT(' '));
1066 extensions = extensions.AfterFirst(wxT(' '));
1067
1068 // consider extensions as not being case-sensitive
1069 if ( field.IsSameAs(ext, FALSE /* no case */) ) {
1070 // found
1071 if (fileType == NULL) fileType = new wxFileType;
1072 fileType->m_impl->Init(this, n);
1073 // adds this mime type to _list_ of mime types with this extension
1074 }
1075 }
1076 }
1077
1078 return fileType;
1079 }
1080
1081 wxFileType *
1082 wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
1083 {
1084 // mime types are not case-sensitive
1085 wxString mimetype(mimeType);
1086 mimetype.MakeLower();
1087
1088 // first look for an exact match
1089 int index = m_aTypes.Index(mimetype);
1090 if ( index == wxNOT_FOUND ) {
1091 // then try to find "text/*" as match for "text/plain" (for example)
1092 // NB: if mimeType doesn't contain '/' at all, BeforeFirst() will return
1093 // the whole string - ok.
1094 wxString strCategory = mimetype.BeforeFirst(wxT('/'));
1095
1096 size_t nCount = m_aTypes.Count();
1097 for ( size_t n = 0; n < nCount; n++ ) {
1098 if ( (m_aTypes[n].BeforeFirst(wxT('/')) == strCategory ) &&
1099 m_aTypes[n].AfterFirst(wxT('/')) == wxT("*") ) {
1100 index = n;
1101 break;
1102 }
1103 }
1104 }
1105
1106 if ( index != wxNOT_FOUND ) {
1107 wxFileType *fileType = new wxFileType;
1108 fileType->m_impl->Init(this, index);
1109
1110 return fileType;
1111 }
1112 else {
1113 // not found...
1114 return NULL;
1115 }
1116 }
1117
1118 void wxMimeTypesManagerImpl::AddFallback(const wxFileTypeInfo& filetype)
1119 {
1120 wxString extensions;
1121 const wxArrayString& exts = filetype.GetExtensions();
1122 size_t nExts = exts.GetCount();
1123 for ( size_t nExt = 0; nExt < nExts; nExt++ ) {
1124 if ( nExt > 0 ) {
1125 extensions += wxT(' ');
1126 }
1127 extensions += exts[nExt];
1128 }
1129
1130 AddMimeTypeInfo(filetype.GetMimeType(),
1131 extensions,
1132 filetype.GetDescription());
1133
1134 AddMailcapInfo(filetype.GetMimeType(),
1135 filetype.GetOpenCommand(),
1136 filetype.GetPrintCommand(),
1137 wxT(""),
1138 filetype.GetDescription());
1139 }
1140
1141 void wxMimeTypesManagerImpl::AddMimeTypeInfo(const wxString& strMimeType,
1142 const wxString& strExtensions,
1143 const wxString& strDesc)
1144 {
1145 int index = m_aTypes.Index(strMimeType);
1146 if ( index == wxNOT_FOUND ) {
1147 // add a new entry
1148 m_aTypes.Add(strMimeType);
1149 m_aEntries.Add(NULL);
1150 m_aExtensions.Add(strExtensions);
1151 m_aDescriptions.Add(strDesc);
1152 }
1153 else {
1154 // modify an existing one
1155 if ( !strDesc.IsEmpty() ) {
1156 m_aDescriptions[index] = strDesc; // replace old value
1157 }
1158 m_aExtensions[index] += ' ' + strExtensions;
1159 }
1160 }
1161
1162 void wxMimeTypesManagerImpl::AddMailcapInfo(const wxString& strType,
1163 const wxString& strOpenCmd,
1164 const wxString& strPrintCmd,
1165 const wxString& strTest,
1166 const wxString& strDesc)
1167 {
1168 MailCapEntry *entry = new MailCapEntry(strOpenCmd, strPrintCmd, strTest);
1169
1170 int nIndex = m_aTypes.Index(strType);
1171 if ( nIndex == wxNOT_FOUND ) {
1172 // new file type
1173 m_aTypes.Add(strType);
1174
1175 m_aEntries.Add(entry);
1176 m_aExtensions.Add(wxT(""));
1177 m_aDescriptions.Add(strDesc);
1178 }
1179 else {
1180 // always append the entry in the tail of the list - info added with
1181 // this function can only come from AddFallbacks()
1182 MailCapEntry *entryOld = m_aEntries[nIndex];
1183 if ( entryOld )
1184 entry->Append(entryOld);
1185 else
1186 m_aEntries[nIndex] = entry;
1187 }
1188 }
1189
1190 bool wxMimeTypesManagerImpl::ReadMimeTypes(const wxString& strFileName)
1191 {
1192 wxLogTrace(wxT("--- Parsing mime.types file '%s' ---"), strFileName.c_str());
1193
1194 wxTextFile file(strFileName);
1195 if ( !file.Open() )
1196 return FALSE;
1197
1198 // the information we extract
1199 wxString strMimeType, strDesc, strExtensions;
1200
1201 size_t nLineCount = file.GetLineCount();
1202 const wxChar *pc = NULL;
1203 for ( size_t nLine = 0; nLine < nLineCount; nLine++ ) {
1204 if ( pc == NULL ) {
1205 // now we're at the start of the line
1206 pc = file[nLine].c_str();
1207 }
1208 else {
1209 // we didn't finish with the previous line yet
1210 nLine--;
1211 }
1212
1213 // skip whitespace
1214 while ( wxIsspace(*pc) )
1215 pc++;
1216
1217 // comment or blank line?
1218 if ( *pc == wxT('#') || !*pc ) {
1219 // skip the whole line
1220 pc = NULL;
1221 continue;
1222 }
1223
1224 // detect file format
1225 const wxChar *pEqualSign = wxStrchr(pc, wxT('='));
1226 if ( pEqualSign == NULL ) {
1227 // brief format
1228 // ------------
1229
1230 // first field is mime type
1231 for ( strMimeType.Empty(); !wxIsspace(*pc) && *pc != wxT('\0'); pc++ ) {
1232 strMimeType += *pc;
1233 }
1234
1235 // skip whitespace
1236 while ( wxIsspace(*pc) )
1237 pc++;
1238
1239 // take all the rest of the string
1240 strExtensions = pc;
1241
1242 // no description...
1243 strDesc.Empty();
1244 }
1245 else {
1246 // expanded format
1247 // ---------------
1248
1249 // the string on the left of '=' is the field name
1250 wxString strLHS(pc, pEqualSign - pc);
1251
1252 // eat whitespace
1253 for ( pc = pEqualSign + 1; wxIsspace(*pc); pc++ )
1254 ;
1255
1256 const wxChar *pEnd;
1257 if ( *pc == wxT('"') ) {
1258 // the string is quoted and ends at the matching quote
1259 pEnd = wxStrchr(++pc, wxT('"'));
1260 if ( pEnd == NULL ) {
1261 wxLogWarning(_("Mime.types file %s, line %d: unterminated "
1262 "quoted string."),
1263 strFileName.c_str(), nLine + 1);
1264 }
1265 }
1266 else {
1267 // unquoted string ends at the first space
1268 for ( pEnd = pc; !wxIsspace(*pEnd); pEnd++ )
1269 ;
1270 }
1271
1272 // now we have the RHS (field value)
1273 wxString strRHS(pc, pEnd - pc);
1274
1275 // check what follows this entry
1276 if ( *pEnd == wxT('"') ) {
1277 // skip this quote
1278 pEnd++;
1279 }
1280
1281 for ( pc = pEnd; wxIsspace(*pc); pc++ )
1282 ;
1283
1284 // if there is something left, it may be either a '\\' to continue
1285 // the line or the next field of the same entry
1286 bool entryEnded = *pc == wxT('\0'),
1287 nextFieldOnSameLine = FALSE;
1288 if ( !entryEnded ) {
1289 nextFieldOnSameLine = ((*pc != wxT('\\')) || (pc[1] != wxT('\0')));
1290 }
1291
1292 // now see what we got
1293 if ( strLHS == wxT("type") ) {
1294 strMimeType = strRHS;
1295 }
1296 else if ( strLHS == wxT("desc") ) {
1297 strDesc = strRHS;
1298 }
1299 else if ( strLHS == wxT("exts") ) {
1300 strExtensions = strRHS;
1301 }
1302 else {
1303 wxLogWarning(_("Unknown field in file %s, line %d: '%s'."),
1304 strFileName.c_str(), nLine + 1, strLHS.c_str());
1305 }
1306
1307 if ( !entryEnded ) {
1308 if ( !nextFieldOnSameLine )
1309 pc = NULL;
1310 //else: don't reset it
1311
1312 // as we don't reset strMimeType, the next field in this entry
1313 // will be interpreted correctly.
1314
1315 continue;
1316 }
1317 }
1318
1319 // although it doesn't seem to be covered by RFCs, some programs
1320 // (notably Netscape) create their entries with several comma
1321 // separated extensions (RFC mention the spaces only)
1322 strExtensions.Replace(wxT(","), wxT(" "));
1323
1324 // also deal with the leading dot
1325 if ( !strExtensions.IsEmpty() && strExtensions[0u] == wxT('.') )
1326 {
1327 strExtensions.erase(0, 1);
1328 }
1329
1330 AddMimeTypeInfo(strMimeType, strExtensions, strDesc);
1331
1332 // finished with this line
1333 pc = NULL;
1334 }
1335
1336 // check our data integriry
1337 wxASSERT( m_aTypes.Count() == m_aEntries.Count() &&
1338 m_aTypes.Count() == m_aExtensions.Count() &&
1339 m_aTypes.Count() == m_aDescriptions.Count() );
1340
1341 return TRUE;
1342 }
1343
1344 bool wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName,
1345 bool fallback)
1346 {
1347 wxLogTrace(wxT("--- Parsing mailcap file '%s' ---"), strFileName.c_str());
1348
1349 wxTextFile file(strFileName);
1350 if ( !file.Open() )
1351 return FALSE;
1352
1353 // see the comments near the end of function for the reason we need these
1354 // variables (search for the next occurence of them)
1355 // indices of MIME types (in m_aTypes) we already found in this file
1356 wxArrayInt aEntryIndices;
1357 // aLastIndices[n] is the index of last element in
1358 // m_aEntries[aEntryIndices[n]] from this file
1359 wxArrayInt aLastIndices;
1360
1361 size_t nLineCount = file.GetLineCount();
1362 for ( size_t nLine = 0; nLine < nLineCount; nLine++ ) {
1363 // now we're at the start of the line
1364 const wxChar *pc = file[nLine].c_str();
1365
1366 // skip whitespace
1367 while ( wxIsspace(*pc) )
1368 pc++;
1369
1370 // comment or empty string?
1371 if ( *pc == wxT('#') || *pc == wxT('\0') )
1372 continue;
1373
1374 // no, do parse
1375
1376 // what field are we currently in? The first 2 are fixed and there may
1377 // be an arbitrary number of other fields -- currently, we are not
1378 // interested in any of them, but we should parse them as well...
1379 enum
1380 {
1381 Field_Type,
1382 Field_OpenCmd,
1383 Field_Other
1384 } currentToken = Field_Type;
1385
1386 // the flags and field values on the current line
1387 bool needsterminal = FALSE,
1388 copiousoutput = FALSE;
1389 wxString strType,
1390 strOpenCmd,
1391 strPrintCmd,
1392 strTest,
1393 strDesc,
1394 curField; // accumulator
1395 for ( bool cont = TRUE; cont; pc++ ) {
1396 switch ( *pc ) {
1397 case wxT('\\'):
1398 // interpret the next character literally (notice that
1399 // backslash can be used for line continuation)
1400 if ( *++pc == wxT('\0') ) {
1401 // fetch the next line.
1402
1403 // pc currently points to nowhere, but after the next
1404 // pc++ in the for line it will point to the beginning
1405 // of the next line in the file
1406 pc = file[++nLine].c_str() - 1;
1407 }
1408 else {
1409 // just a normal character
1410 curField += *pc;
1411 }
1412 break;
1413
1414 case wxT('\0'):
1415 cont = FALSE; // end of line reached, exit the loop
1416
1417 // fall through
1418
1419 case wxT(';'):
1420 // store this field and start looking for the next one
1421
1422 // trim whitespaces from both sides
1423 curField.Trim(TRUE).Trim(FALSE);
1424
1425 switch ( currentToken ) {
1426 case Field_Type:
1427 strType = curField;
1428 if ( strType.Find(wxT('/')) == wxNOT_FOUND ) {
1429 // we interpret "type" as "type/*"
1430 strType += wxT("/*");
1431 }
1432
1433 currentToken = Field_OpenCmd;
1434 break;
1435
1436 case Field_OpenCmd:
1437 strOpenCmd = curField;
1438
1439 currentToken = Field_Other;
1440 break;
1441
1442 case Field_Other:
1443 {
1444 // "good" mailcap entry?
1445 bool ok = TRUE;
1446
1447 // is this something of the form foo=bar?
1448 const wxChar *pEq = wxStrchr(curField, wxT('='));
1449 if ( pEq != NULL ) {
1450 wxString lhs = curField.BeforeFirst(wxT('=')),
1451 rhs = curField.AfterFirst(wxT('='));
1452
1453 lhs.Trim(TRUE); // from right
1454 rhs.Trim(FALSE); // from left
1455
1456 if ( lhs == wxT("print") )
1457 strPrintCmd = rhs;
1458 else if ( lhs == wxT("test") )
1459 strTest = rhs;
1460 else if ( lhs == wxT("description") ) {
1461 // it might be quoted
1462 if ( rhs[0u] == wxT('"') &&
1463 rhs.Last() == wxT('"') ) {
1464 strDesc = wxString(rhs.c_str() + 1,
1465 rhs.Len() - 2);
1466 }
1467 else {
1468 strDesc = rhs;
1469 }
1470 }
1471 else if ( lhs == wxT("compose") ||
1472 lhs == wxT("composetyped") ||
1473 lhs == wxT("notes") ||
1474 lhs == wxT("edit") )
1475 ; // ignore
1476 else
1477 ok = FALSE;
1478
1479 }
1480 else {
1481 // no, it's a simple flag
1482 // TODO support the flags:
1483 // 1. create an xterm for 'needsterminal'
1484 // 2. append "| $PAGER" for 'copiousoutput'
1485 if ( curField == wxT("needsterminal") )
1486 needsterminal = TRUE;
1487 else if ( curField == wxT("copiousoutput") )
1488 copiousoutput = TRUE;
1489 else if ( curField == wxT("textualnewlines") )
1490 ; // ignore
1491 else
1492 ok = FALSE;
1493 }
1494
1495 if ( !ok )
1496 {
1497 // we don't understand this field, but
1498 // Netscape stores info in it, so don't warn
1499 // about it
1500 if ( curField.Left(16u) != "x-mozilla-flags=" )
1501 {
1502 // don't flood the user with error
1503 // messages if we don't understand
1504 // something in his mailcap, but give
1505 // them in debug mode because this might
1506 // be useful for the programmer
1507 wxLogDebug
1508 (
1509 wxT("Mailcap file %s, line %d: "
1510 "unknown field '%s' for the "
1511 "MIME type '%s' ignored."),
1512 strFileName.c_str(),
1513 nLine + 1,
1514 curField.c_str(),
1515 strType.c_str()
1516 );
1517 }
1518 }
1519 }
1520
1521 // it already has this value
1522 //currentToken = Field_Other;
1523 break;
1524
1525 default:
1526 wxFAIL_MSG(wxT("unknown field type in mailcap"));
1527 }
1528
1529 // next token starts immediately after ';'
1530 curField.Empty();
1531 break;
1532
1533 default:
1534 curField += *pc;
1535 }
1536 }
1537
1538 // check that we really read something reasonable
1539 if ( currentToken == Field_Type || currentToken == Field_OpenCmd ) {
1540 wxLogWarning(_("Mailcap file %s, line %d: incomplete entry "
1541 "ignored."),
1542 strFileName.c_str(), nLine + 1);
1543 }
1544 else {
1545 MailCapEntry *entry = new MailCapEntry(strOpenCmd,
1546 strPrintCmd,
1547 strTest);
1548
1549 // NB: because of complications below (we must get entries priority
1550 // right), we can't use AddMailcapInfo() here, unfortunately.
1551 strType.MakeLower();
1552 int nIndex = m_aTypes.Index(strType);
1553 if ( nIndex == wxNOT_FOUND ) {
1554 // new file type
1555 m_aTypes.Add(strType);
1556
1557 m_aEntries.Add(entry);
1558 m_aExtensions.Add(wxT(""));
1559 m_aDescriptions.Add(strDesc);
1560 }
1561 else {
1562 // modify the existing entry: the entries in one and the same
1563 // file are read in top-to-bottom order, i.e. the entries read
1564 // first should be tried before the entries below. However,
1565 // the files read later should override the settings in the
1566 // files read before (except if fallback is TRUE), thus we
1567 // Insert() the new entry to the list if it has already
1568 // occured in _this_ file, but Prepend() it if it occured in
1569 // some of the previous ones and Append() to it in the
1570 // fallback case
1571
1572 if ( fallback ) {
1573 // 'fallback' parameter prevents the entries from this
1574 // file from overriding the other ones - always append
1575 MailCapEntry *entryOld = m_aEntries[nIndex];
1576 if ( entryOld )
1577 entry->Append(entryOld);
1578 else
1579 m_aEntries[nIndex] = entry;
1580 }
1581 else {
1582 int entryIndex = aEntryIndices.Index(nIndex);
1583 if ( entryIndex == wxNOT_FOUND ) {
1584 // first time in this file
1585 aEntryIndices.Add(nIndex);
1586 aLastIndices.Add(0);
1587
1588 entry->Prepend(m_aEntries[nIndex]);
1589 m_aEntries[nIndex] = entry;
1590 }
1591 else {
1592 // not the first time in _this_ file
1593 size_t nEntryIndex = (size_t)entryIndex;
1594 MailCapEntry *entryOld = m_aEntries[nIndex];
1595 if ( entryOld )
1596 entry->Insert(entryOld, aLastIndices[nEntryIndex]);
1597 else
1598 m_aEntries[nIndex] = entry;
1599
1600 // the indices were shifted by 1
1601 aLastIndices[nEntryIndex]++;
1602 }
1603 }
1604
1605 if ( !strDesc.IsEmpty() ) {
1606 // replace the old one - what else can we do??
1607 m_aDescriptions[nIndex] = strDesc;
1608 }
1609 }
1610 }
1611
1612 // check our data integriry
1613 wxASSERT( m_aTypes.Count() == m_aEntries.Count() &&
1614 m_aTypes.Count() == m_aExtensions.Count() &&
1615 m_aTypes.Count() == m_aDescriptions.Count() );
1616 }
1617
1618 return TRUE;
1619 }
1620
1621 size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString& mimetypes)
1622 {
1623 mimetypes.Empty();
1624
1625 wxString type;
1626 size_t count = m_aTypes.GetCount();
1627 for ( size_t n = 0; n < count; n++ )
1628 {
1629 // don't return template types from here (i.e. anything containg '*')
1630 type = m_aTypes[n];
1631 if ( type.Find(_T('*')) == wxNOT_FOUND )
1632 {
1633 mimetypes.Add(type);
1634 }
1635 }
1636
1637 return mimetypes.GetCount();
1638 }
1639
1640 #endif
1641 // wxUSE_FILE && wxUSE_TEXTFILE
1642