don't use use UTF-8 to PUA mapping hack, it's not necessary any more now that wxConvA...
[wxWidgets.git] / src / unix / mimetype.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/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 licence (part of wxExtra library)
10 /////////////////////////////////////////////////////////////////////////////
11
12 // known bugs; there may be others!! chris elliott, biol75@york.ac.uk 27 Mar 01
13
14 // 1) .mailcap and .mimetypes can be either in a netscape or metamail format
15 // and entries may get confused during writing (I've tried to fix this; please let me know
16 // any files that fail)
17 // 2) KDE and Gnome do not yet fully support international read/write
18 // 3) Gnome key lines like open.latex."LaTeX this file"=latex %f will have odd results
19 // 4) writing to files comments out the existing data; I hope this avoids losing
20 // any data which we could not read, and data which we did not store like test=
21 // 5) results from reading files with multiple entries (especially matches with type/* )
22 // may (or may not) work for getXXX commands
23 // 6) Loading the png icons in Gnome doesn't work for me...
24 // 7) In Gnome, if keys.mime exists but keys.users does not, there is
25 // an error message in debug mode, but the file is still written OK
26 // 8) Deleting entries is only allowed from the user file; sytem wide entries
27 // will be preserved during unassociate
28 // 9) KDE does not yet handle multiple actions; Netscape mode never will
29
30 // TODO: this file is a mess, we need to split it and review everything (VZ)
31
32 // for compilers that support precompilation, includes "wx.h".
33 #include "wx/wxprec.h"
34
35 #ifdef __BORLANDC__
36 #pragma hdrstop
37 #endif
38
39 #if wxUSE_MIMETYPE && wxUSE_FILE && wxUSE_TEXTFILE
40
41 #include "wx/unix/mimetype.h"
42
43 #ifndef WX_PRECOMP
44 #include "wx/dynarray.h"
45 #include "wx/string.h"
46 #include "wx/intl.h"
47 #include "wx/log.h"
48 #include "wx/utils.h"
49 #endif
50
51 #include "wx/file.h"
52 #include "wx/confbase.h"
53
54 #include "wx/ffile.h"
55 #include "wx/textfile.h"
56 #include "wx/dir.h"
57 #include "wx/tokenzr.h"
58 #include "wx/iconloc.h"
59 #include "wx/filename.h"
60 #include "wx/app.h"
61 #include "wx/apptrait.h"
62
63 #if wxUSE_LIBGNOMEVFS
64 // Not GUI dependent
65 #include "wx/gtk/gnome/gvfs.h"
66 #endif
67
68 // other standard headers
69 #include <ctype.h>
70
71 // this class is a wxTextFile specialization for dealing with files storing
72 // various MIME-related information
73 //
74 // it should be used instead of wxTextFile even if none of its additional
75 // methods are used just because it handles files with mixed encodings (often
76 // the case for MIME files which contain strings for different languages)
77 // correctly, see OnRead()
78 class wxMimeTextFile : public wxTextFile
79 {
80 public:
81 // constructors
82 wxMimeTextFile () : wxTextFile () { }
83 wxMimeTextFile(const wxString& strFile) : wxTextFile(strFile) { }
84
85 int pIndexOf(const wxString& sSearch,
86 bool bIncludeComments = false,
87 int iStart = 0)
88 {
89 wxString sTest = sSearch;
90 sTest.MakeLower();
91 for(size_t i = iStart; i < GetLineCount(); i++)
92 {
93 wxString sLine = GetLine(i).Trim(false);
94 if(bIncludeComments || ! sLine.StartsWith(wxT("#")))
95 {
96 sLine.MakeLower();
97 if(sLine.StartsWith(sTest))
98 return (int)i;
99 }
100 }
101 return wxNOT_FOUND;
102 }
103
104 bool CommentLine(int nIndex)
105 {
106 if (nIndex < 0)
107 return false;
108 if (nIndex >= (int)GetLineCount() )
109 return false;
110
111 GetLine(nIndex) = GetLine(nIndex).Prepend(wxT("#"));
112 return true;
113 }
114
115 bool CommentLine(const wxString & sTest)
116 {
117 int nIndex = pIndexOf(sTest);
118 if (nIndex < 0)
119 return false;
120 if (nIndex >= (int)GetLineCount() )
121 return false;
122
123 GetLine(nIndex) = GetLine(nIndex).Prepend(wxT("#"));
124 return true;
125 }
126
127 wxString GetVerb(size_t i)
128 {
129 if (i > GetLineCount() )
130 return wxEmptyString;
131
132 wxString sTmp = GetLine(i).BeforeFirst(wxT('='));
133 return sTmp;
134 }
135
136 wxString GetCmd(size_t i)
137 {
138 if (i > GetLineCount() )
139 return wxEmptyString;
140
141 wxString sTmp = GetLine(i).AfterFirst(wxT('='));
142 return sTmp;
143 }
144 };
145
146 // ----------------------------------------------------------------------------
147 // constants
148 // ----------------------------------------------------------------------------
149
150 // MIME code tracing mask
151 #define TRACE_MIME wxT("mime")
152
153 // give trace messages about the results of mailcap tests
154 #define TRACE_MIME_TEST wxT("mimetest")
155
156 // ----------------------------------------------------------------------------
157 // private functions
158 // ----------------------------------------------------------------------------
159
160 // there are some fields which we don't understand but for which we don't give
161 // warnings as we know that they're not important - this function is used to
162 // test for them
163 static bool IsKnownUnimportantField(const wxString& field);
164
165 // ----------------------------------------------------------------------------
166 // private classes
167 // ----------------------------------------------------------------------------
168
169
170 // This class uses both mailcap and mime.types to gather information about file
171 // types.
172 //
173 // The information about mailcap file was extracted from metamail(1) sources
174 // and documentation and subsequently revised when I found the RFC 1524
175 // describing it.
176 //
177 // Format of mailcap file: spaces are ignored, each line is either a comment
178 // (starts with '#') or a line of the form <field1>;<field2>;...;<fieldN>.
179 // A backslash can be used to quote semicolons and newlines (and, in fact,
180 // anything else including itself).
181 //
182 // The first field is always the MIME type in the form of type/subtype (see RFC
183 // 822) where subtype may be '*' meaning "any". Following metamail, we accept
184 // "type" which means the same as "type/*", although I'm not sure whether this
185 // is standard.
186 //
187 // The second field is always the command to run. It is subject to
188 // parameter/filename expansion described below.
189 //
190 // All the following fields are optional and may not be present at all. If
191 // they're present they may appear in any order, although each of them should
192 // appear only once. The optional fields are the following:
193 // * notes=xxx is an uninterpreted string which is silently ignored
194 // * test=xxx is the command to be used to determine whether this mailcap line
195 // applies to our data or not. The RHS of this field goes through the
196 // parameter/filename expansion (as the 2nd field) and the resulting string
197 // is executed. The line applies only if the command succeeds, i.e. returns 0
198 // exit code.
199 // * print=xxx is the command to be used to print (and not view) the data of
200 // this type (parameter/filename expansion is done here too)
201 // * edit=xxx is the command to open/edit the data of this type
202 // * needsterminal means that a new interactive console must be created for
203 // the viewer
204 // * copiousoutput means that the viewer doesn't interact with the user but
205 // produces (possibly) a lof of lines of output on stdout (i.e. "cat" is a
206 // good example), thus it might be a good idea to use some kind of paging
207 // mechanism.
208 // * textualnewlines means not to perform CR/LF translation (not honored)
209 // * compose and composetyped fields are used to determine the program to be
210 // called to create a new message pert in the specified format (unused).
211 //
212 // Parameter/filename expansion:
213 // * %s is replaced with the (full) file name
214 // * %t is replaced with MIME type/subtype of the entry
215 // * for multipart type only %n is replaced with the nnumber of parts and %F is
216 // replaced by an array of (content-type, temporary file name) pairs for all
217 // message parts (TODO)
218 // * %{parameter} is replaced with the value of parameter taken from
219 // Content-type header line of the message.
220 //
221 //
222 // There are 2 possible formats for mime.types file, one entry per line (used
223 // for global mime.types and called Mosaic format) and "expanded" format where
224 // an entry takes multiple lines (used for users mime.types and called
225 // Netscape format).
226 //
227 // For both formats spaces are ignored and lines starting with a '#' are
228 // comments. Each record has one of two following forms:
229 // a) for "brief" format:
230 // <mime type> <space separated list of extensions>
231 // b) for "expanded" format:
232 // type=<mime type> BACKSLASH
233 // desc="<description>" BACKSLASH
234 // exts="<comma separated list of extensions>"
235 //
236 // (where BACKSLASH is a literal '\\' which we can't put here because cpp
237 // misinterprets it)
238 //
239 // We try to autodetect the format of mime.types: if a non-comment line starts
240 // with "type=" we assume the second format, otherwise the first one.
241
242 // there may be more than one entry for one and the same mime type, to
243 // choose the right one we have to run the command specified in the test
244 // field on our data.
245
246 // ----------------------------------------------------------------------------
247 // wxGNOME
248 // ----------------------------------------------------------------------------
249
250 // GNOME stores the info we're interested in in several locations:
251 // 1. xxx.keys files under /usr/share/mime-info
252 // 2. xxx.keys files under ~/.gnome/mime-info
253 //
254 // Update (Chris Elliott): apparently there may be an optional "[lang]" prefix
255 // just before the field name.
256
257
258 void wxMimeTypesManagerImpl::LoadGnomeDataFromKeyFile(const wxString& filename,
259 const wxArrayString& dirs)
260 {
261 wxMimeTextFile textfile(filename);
262 if ( !textfile.Open() )
263 return;
264
265 wxLogTrace(TRACE_MIME, wxT("--- Opened Gnome file %s ---"),
266 filename.c_str());
267
268 wxArrayString search_dirs( dirs );
269
270 // values for the entry being parsed
271 wxString curMimeType, curIconFile;
272 wxMimeTypeCommands * entry = new wxMimeTypeCommands;
273
274 wxArrayString strExtensions;
275 wxString strDesc;
276
277 const wxChar *pc;
278 size_t nLineCount = textfile.GetLineCount();
279 size_t nLine = 0;
280 while ( nLine < nLineCount )
281 {
282 pc = textfile[nLine].c_str();
283 if ( *pc != wxT('#') )
284 {
285
286 wxLogTrace(TRACE_MIME, wxT("--- Reading from Gnome file %s '%s' ---"),
287 filename.c_str(), pc);
288
289 // trim trailing space and tab
290 while ((*pc == wxT(' ')) || (*pc == wxT('\t')))
291 pc++;
292
293 wxString sTmp(pc);
294 int equal_pos = sTmp.Find( wxT('=') );
295 if (equal_pos > 0)
296 {
297 wxString left_of_equal = sTmp.Left( equal_pos );
298 const wxChar *right_of_equal = pc;
299 right_of_equal += equal_pos+1;
300
301 if (left_of_equal == wxT("icon_filename"))
302 {
303 // GNOME 2:
304 curIconFile = right_of_equal;
305
306 wxFileName newFile( curIconFile );
307 if (newFile.IsRelative() || newFile.FileExists())
308 {
309 size_t nDirs = search_dirs.GetCount();
310
311 for (size_t nDir = 0; nDir < nDirs; nDir++)
312 {
313 newFile.SetPath( search_dirs[nDir] );
314 newFile.AppendDir( wxT("pixmaps") );
315 newFile.AppendDir( wxT("document-icons") );
316 newFile.SetExt( wxT("png") );
317 if (newFile.FileExists())
318 {
319 curIconFile = newFile.GetFullPath();
320 // reorder search_dirs for speedup (fewer
321 // calls to FileExist() required)
322 if (nDir != 0)
323 {
324 const wxString &tmp = search_dirs[nDir];
325 search_dirs.RemoveAt( nDir );
326 search_dirs.Insert( tmp, 0 );
327 }
328 break;
329 }
330 }
331 }
332 }
333 else if (left_of_equal == wxT("open"))
334 {
335 sTmp = right_of_equal;
336 sTmp.Replace( wxT("%f"), wxT("%s") );
337 sTmp.Prepend( wxT("open=") );
338 entry->Add(sTmp);
339 }
340 else if (left_of_equal == wxT("view"))
341 {
342 sTmp = right_of_equal;
343 sTmp.Replace( wxT("%f"), wxT("%s") );
344 sTmp.Prepend( wxT("view=") );
345 entry->Add(sTmp);
346 }
347 else if (left_of_equal == wxT("print"))
348 {
349 sTmp = right_of_equal;
350 sTmp.Replace( wxT("%f"), wxT("%s") );
351 sTmp.Prepend( wxT("print=") );
352 entry->Add(sTmp);
353 }
354 else if (left_of_equal == wxT("description"))
355 {
356 strDesc = right_of_equal;
357 }
358 else if (left_of_equal == wxT("short_list_application_ids_for_novice_user_level"))
359 {
360 sTmp = right_of_equal;
361 if (sTmp.Contains( wxT(",") ))
362 sTmp = sTmp.BeforeFirst( wxT(',') );
363 sTmp.Prepend( wxT("open=") );
364 sTmp.Append( wxT(" %s") );
365 entry->Add(sTmp);
366 }
367
368 } // emd of has an equals sign
369 else
370 {
371 // not a comment and not an equals sign
372 if (sTmp.Contains(wxT('/')))
373 {
374 // this is the start of the new mimetype
375 // overwrite any existing data
376 if (! curMimeType.empty())
377 {
378 AddToMimeData( curMimeType, curIconFile, entry, strExtensions, strDesc );
379
380 // now get ready for next bit
381 entry = new wxMimeTypeCommands;
382 }
383
384 curMimeType = sTmp.BeforeFirst(wxT(':'));
385 }
386 }
387 } // end of not a comment
388
389 // ignore blank lines
390 nLine++;
391 } // end of while, save any data
392
393 if ( curMimeType.empty() )
394 delete entry;
395 else
396 AddToMimeData( curMimeType, curIconFile, entry, strExtensions, strDesc);
397 }
398
399 void wxMimeTypesManagerImpl::LoadGnomeMimeTypesFromMimeFile(const wxString& filename)
400 {
401 wxMimeTextFile textfile(filename);
402 if ( !textfile.Open() )
403 return;
404
405 wxLogTrace(TRACE_MIME,
406 wxT("--- Opened Gnome file %s ---"),
407 filename.c_str());
408
409 // values for the entry being parsed
410 wxString curMimeType, curExtList;
411
412 const wxChar *pc;
413 size_t nLineCount = textfile.GetLineCount();
414 for ( size_t nLine = 0; /* nothing */; nLine++ )
415 {
416 if ( nLine < nLineCount )
417 {
418 pc = textfile[nLine].c_str();
419 if ( *pc == wxT('#') )
420 {
421 // skip comments
422 continue;
423 }
424 }
425 else
426 {
427 // so that we will fall into the "if" below
428 pc = NULL;
429 }
430
431 if ( !pc || !*pc )
432 {
433 // end of the entry
434 if ( !curMimeType.empty() && !curExtList.empty() )
435 {
436 wxLogTrace(TRACE_MIME,
437 wxT("--- At end of Gnome file finding mimetype %s ---"),
438 curMimeType.c_str());
439
440 AddMimeTypeInfo(curMimeType, curExtList, wxEmptyString);
441 }
442
443 if ( !pc )
444 {
445 // the end: this can only happen if nLine == nLineCount
446 break;
447 }
448
449 curExtList.Empty();
450
451 continue;
452 }
453
454 // what do we have here?
455 if ( *pc == wxT('\t') )
456 {
457 // this is a field=value ling
458 pc++; // skip leading TAB
459
460 static const int lenField = 5; // strlen("ext: ")
461 if ( wxStrncmp(pc, wxT("ext: "), lenField) == 0 )
462 {
463 // skip it and take everything left until the end of line
464 curExtList = pc + lenField;
465 }
466 //else: some other field, we don't care
467 }
468 else
469 {
470 // this is the start of the new section
471 wxLogTrace(TRACE_MIME,
472 wxT("--- In Gnome file finding mimetype %s ---"),
473 curMimeType.c_str());
474
475 if (! curMimeType.empty())
476 AddMimeTypeInfo(curMimeType, curExtList, wxEmptyString);
477
478 curMimeType.Empty();
479
480 while ( *pc != wxT(':') && *pc != wxT('\0') )
481 {
482 curMimeType += *pc++;
483 }
484 }
485 }
486 }
487
488
489 void wxMimeTypesManagerImpl::LoadGnomeMimeFilesFromDir(
490 const wxString& dirbase, const wxArrayString& dirs)
491 {
492 wxASSERT_MSG( !dirbase.empty() && !wxEndsWithPathSeparator(dirbase),
493 wxT("base directory shouldn't end with a slash") );
494
495 wxString dirname = dirbase;
496 dirname << wxT("/mime-info");
497
498 if ( !wxDir::Exists(dirname) )
499 return;
500
501 wxDir dir(dirname);
502 if ( !dir.IsOpened() )
503 return;
504
505 // we will concatenate it with filename to get the full path below
506 dirname += wxT('/');
507
508 wxString filename;
509 bool cont;
510
511 cont = dir.GetFirst(&filename, wxT("*.mime"), wxDIR_FILES);
512 while ( cont )
513 {
514 LoadGnomeMimeTypesFromMimeFile(dirname + filename);
515
516 cont = dir.GetNext(&filename);
517 }
518
519 cont = dir.GetFirst(&filename, wxT("*.keys"), wxDIR_FILES);
520 while ( cont )
521 {
522 LoadGnomeDataFromKeyFile(dirname + filename, dirs);
523
524 cont = dir.GetNext(&filename);
525 }
526
527 // FIXME: Hack alert: We scan all icons and deduce the
528 // mime-type from the file name.
529 dirname = dirbase;
530 dirname << wxT("/pixmaps/document-icons");
531
532 // these are always empty in this file
533 wxArrayString strExtensions;
534 wxString strDesc;
535
536 if ( !wxDir::Exists(dirname) )
537 {
538 // Just test for default GPE dir also
539 dirname = wxT("/usr/share/gpe/pixmaps/default/filemanager/document-icons");
540
541 if ( !wxDir::Exists(dirname) )
542 return;
543 }
544
545 wxDir dir2( dirname );
546
547 cont = dir2.GetFirst(&filename, wxT("gnome-*.png"), wxDIR_FILES);
548 while ( cont )
549 {
550 wxString mimeType = filename;
551 mimeType.Remove( 0, 6 ); // remove "gnome-"
552 mimeType.Remove( mimeType.Len() - 4, 4 ); // remove ".png"
553 int pos = mimeType.Find( wxT("-") );
554 if (pos != wxNOT_FOUND)
555 {
556 mimeType.SetChar( pos, wxT('/') );
557 wxString iconFile = dirname;
558 iconFile << wxT("/");
559 iconFile << filename;
560 AddToMimeData( mimeType, iconFile, NULL, strExtensions, strDesc, true );
561 }
562
563 cont = dir2.GetNext(&filename);
564 }
565 }
566
567 void wxMimeTypesManagerImpl::GetGnomeMimeInfo(const wxString& sExtraDir)
568 {
569 wxArrayString dirs;
570
571 wxString gnomedir = wxGetenv( wxT("GNOMEDIR") );
572 if (!gnomedir.empty())
573 {
574 gnomedir << wxT("/share");
575 dirs.Add( gnomedir );
576 }
577
578 dirs.Add(wxT("/usr/share"));
579 dirs.Add(wxT("/usr/local/share"));
580
581 gnomedir = wxGetHomeDir();
582 gnomedir << wxT("/.gnome");
583 dirs.Add( gnomedir );
584
585 if (!sExtraDir.empty())
586 dirs.Add( sExtraDir );
587
588 size_t nDirs = dirs.GetCount();
589 for ( size_t nDir = 0; nDir < nDirs; nDir++ )
590 {
591 LoadGnomeMimeFilesFromDir(dirs[nDir], dirs);
592 }
593 }
594
595 // ----------------------------------------------------------------------------
596 // KDE
597 // ----------------------------------------------------------------------------
598
599
600 // KDE stores the icon info in its .kdelnk files. The file for mimetype/subtype
601 // may be found in either of the following locations
602 //
603 // 1. $KDEDIR/share/mimelnk/mimetype/subtype.kdelnk
604 // 2. ~/.kde/share/mimelnk/mimetype/subtype.kdelnk
605 //
606 // The format of a .kdelnk file is almost the same as the one used by
607 // wxFileConfig, i.e. there are groups, comments and entries. The icon is the
608 // value for the entry "Type"
609
610 // kde writing; see http://webcvs.kde.org/cgi-bin/cvsweb.cgi/~checkout~/kdelibs/kio/DESKTOP_ENTRY_STANDARD
611 // for now write to .kdelnk but should eventually do .desktop instead (in preference??)
612
613 bool wxMimeTypesManagerImpl::CheckKDEDirsExist( const wxString &sOK, const wxString &sTest )
614 {
615 if (sTest.empty())
616 {
617 return wxDir::Exists(sOK);
618 }
619 else
620 {
621 wxString sStart = sOK + wxT("/") + sTest.BeforeFirst(wxT('/'));
622 if (!wxDir::Exists(sStart))
623 wxMkdir(sStart);
624 wxString sEnd = sTest.AfterFirst(wxT('/'));
625 return CheckKDEDirsExist(sStart, sEnd);
626 }
627 }
628
629 bool wxMimeTypesManagerImpl::WriteKDEMimeFile(int index, bool delete_index)
630 {
631 wxMimeTextFile appoutfile, mimeoutfile;
632 wxString sHome = wxGetHomeDir();
633 wxString sTmp = wxT(".kde/share/mimelnk/");
634 wxString sMime = m_aTypes[index];
635 CheckKDEDirsExist(sHome, sTmp + sMime.BeforeFirst(wxT('/')) );
636 sTmp = sHome + wxT('/') + sTmp + sMime + wxT(".kdelnk");
637
638 bool bTemp;
639 bool bMimeExists = mimeoutfile.Open(sTmp);
640 if (!bMimeExists)
641 {
642 bTemp = mimeoutfile.Create(sTmp);
643 // some unknown error eg out of disk space
644 if (!bTemp)
645 return false;
646 }
647
648 sTmp = wxT(".kde/share/applnk/");
649 CheckKDEDirsExist(sHome, sTmp + sMime.AfterFirst(wxT('/')) );
650 sTmp = sHome + wxT('/') + sTmp + sMime.AfterFirst(wxT('/')) + wxT(".kdelnk");
651
652 bool bAppExists;
653 bAppExists = appoutfile.Open(sTmp);
654 if (!bAppExists)
655 {
656 bTemp = appoutfile.Create(sTmp);
657 // some unknown error eg out of disk space
658 if (!bTemp)
659 return false;
660 }
661
662 // fixed data; write if new file
663 if (!bMimeExists)
664 {
665 mimeoutfile.AddLine(wxT("#KDE Config File"));
666 mimeoutfile.AddLine(wxT("[KDE Desktop Entry]"));
667 mimeoutfile.AddLine(wxT("Version=1.0"));
668 mimeoutfile.AddLine(wxT("Type=MimeType"));
669 mimeoutfile.AddLine(wxT("MimeType=") + sMime);
670 }
671
672 if (!bAppExists)
673 {
674 mimeoutfile.AddLine(wxT("#KDE Config File"));
675 mimeoutfile.AddLine(wxT("[KDE Desktop Entry]"));
676 appoutfile.AddLine(wxT("Version=1.0"));
677 appoutfile.AddLine(wxT("Type=Application"));
678 appoutfile.AddLine(wxT("MimeType=") + sMime + wxT(';'));
679 }
680
681 // variable data
682 // ignore locale
683 mimeoutfile.CommentLine(wxT("Comment="));
684 if (!delete_index)
685 mimeoutfile.AddLine(wxT("Comment=") + m_aDescriptions[index]);
686 appoutfile.CommentLine(wxT("Name="));
687 if (!delete_index)
688 appoutfile.AddLine(wxT("Comment=") + m_aDescriptions[index]);
689
690 sTmp = m_aIcons[index];
691 // we can either give the full path, or the shortfilename if its in
692 // one of the directories we search
693 mimeoutfile.CommentLine(wxT("Icon=") );
694 if (!delete_index)
695 mimeoutfile.AddLine(wxT("Icon=") + sTmp );
696 appoutfile.CommentLine(wxT("Icon=") );
697 if (!delete_index)
698 appoutfile.AddLine(wxT("Icon=") + sTmp );
699
700 sTmp = wxT(" ") + m_aExtensions[index];
701
702 wxStringTokenizer tokenizer(sTmp, wxT(" "));
703 sTmp = wxT("Patterns=");
704 mimeoutfile.CommentLine(sTmp);
705 while ( tokenizer.HasMoreTokens() )
706 {
707 // holds an extension; need to change it to *.ext;
708 wxString e = wxT("*.") + tokenizer.GetNextToken() + wxT(";");
709 sTmp += e;
710 }
711
712 if (!delete_index)
713 mimeoutfile.AddLine(sTmp);
714
715 wxMimeTypeCommands * entries = m_aEntries[index];
716 // if we don't find open just have an empty string ... FIX this
717 sTmp = entries->GetCommandForVerb(wxT("open"));
718 sTmp.Replace( wxT("%s"), wxT("%f") );
719
720 mimeoutfile.CommentLine(wxT("DefaultApp=") );
721 if (!delete_index)
722 mimeoutfile.AddLine(wxT("DefaultApp=") + sTmp);
723
724 sTmp.Replace( wxT("%f"), wxT("") );
725 appoutfile.CommentLine(wxT("Exec="));
726 if (!delete_index)
727 appoutfile.AddLine(wxT("Exec=") + sTmp);
728
729 if (entries->GetCount() > 1)
730 {
731 //other actions as well as open
732 }
733
734 bTemp = false;
735 if (mimeoutfile.Write())
736 bTemp = true;
737 mimeoutfile.Close();
738 if (appoutfile.Write())
739 bTemp = true;
740 appoutfile.Close();
741
742 return bTemp;
743 }
744
745 void wxMimeTypesManagerImpl::LoadKDELinksForMimeSubtype(const wxString& dirbase,
746 const wxString& subdir,
747 const wxString& filename,
748 const wxArrayString& icondirs)
749 {
750 wxFileName fullname(dirbase, filename);
751 wxLogTrace(TRACE_MIME, wxT("loading KDE file %s"),
752 fullname.GetFullPath().c_str());
753
754 wxMimeTextFile file;
755 if ( !file.Open(fullname.GetFullPath()) )
756 return;
757
758 wxMimeTypeCommands * entry = new wxMimeTypeCommands;
759 wxArrayString sExts;
760 wxString mimetype, mime_desc, strIcon;
761
762 int nIndex = file.pIndexOf( wxT("MimeType=") );
763 if (nIndex == wxNOT_FOUND)
764 {
765 // construct mimetype from the directory name and the basename of the
766 // file (it always has .kdelnk extension)
767 mimetype << subdir << wxT('/') << filename.BeforeLast( wxT('.') );
768 }
769 else
770 mimetype = file.GetCmd(nIndex);
771
772 // first find the description string: it is the value in either "Comment="
773 // line or "Comment[<locale_name>]=" one
774 nIndex = wxNOT_FOUND;
775
776 wxString comment;
777
778 #if wxUSE_INTL
779 wxLocale *locale = wxGetLocale();
780 if ( locale )
781 {
782 // try "Comment[locale name]" first
783 comment << wxT("Comment[") + locale->GetName() + wxT("]=");
784 nIndex = file.pIndexOf(comment);
785 }
786 #endif
787
788 if ( nIndex == wxNOT_FOUND )
789 {
790 comment = wxT("Comment=");
791 nIndex = file.pIndexOf(comment);
792 }
793
794 if ( nIndex != wxNOT_FOUND )
795 mime_desc = file.GetCmd(nIndex);
796 //else: no description
797
798 // next find the extensions
799 wxString mime_extension;
800
801 nIndex = file.pIndexOf(wxT("Patterns="));
802 if ( nIndex != wxNOT_FOUND )
803 {
804 wxString exts = file.GetCmd(nIndex);
805
806 wxStringTokenizer tokenizer(exts, wxT(";"));
807 while ( tokenizer.HasMoreTokens() )
808 {
809 wxString e = tokenizer.GetNextToken();
810
811 // don't support too difficult patterns
812 if ( e.Left(2) != wxT("*.") )
813 continue;
814
815 if ( !mime_extension.empty() )
816 {
817 // separate from the previous ext
818 mime_extension << wxT(' ');
819 }
820
821 mime_extension << e.Mid(2);
822 }
823 }
824
825 sExts.Add(mime_extension);
826
827 // ok, now we can take care of icon:
828
829 nIndex = file.pIndexOf(wxT("Icon="));
830 if ( nIndex != wxNOT_FOUND )
831 {
832 strIcon = file.GetCmd(nIndex);
833
834 wxLogTrace(TRACE_MIME, wxT(" icon %s"), strIcon.c_str());
835
836 // it could be the real path, but more often a short name
837 if (!wxFileExists(strIcon))
838 {
839 // icon is just the short name
840 if ( !strIcon.empty() )
841 {
842 // we must check if the file exists because it may be stored
843 // in many locations, at least ~/.kde and $KDEDIR
844 size_t nDir, nDirs = icondirs.GetCount();
845 for ( nDir = 0; nDir < nDirs; nDir++ )
846 {
847 wxFileName fnameIcon( strIcon );
848 wxFileName fname( icondirs[nDir], fnameIcon.GetName() );
849 fname.SetExt( wxT("png") );
850 if (fname.FileExists())
851 {
852 strIcon = fname.GetFullPath();
853 wxLogTrace(TRACE_MIME, wxT(" iconfile %s"), strIcon.c_str());
854 break;
855 }
856 }
857 }
858 }
859 }
860
861 // now look for lines which know about the application
862 // exec= or DefaultApp=
863
864 nIndex = file.pIndexOf(wxT("DefaultApp"));
865
866 if ( nIndex == wxNOT_FOUND )
867 {
868 // no entry try exec
869 nIndex = file.pIndexOf(wxT("Exec"));
870 }
871
872 if ( nIndex != wxNOT_FOUND )
873 {
874 // we expect %f; others including %F and %U and %u are possible
875 wxString sTmp = file.GetCmd(nIndex);
876 if (0 == sTmp.Replace( wxT("%f"), wxT("%s") ))
877 sTmp += wxT(" %s");
878 entry->AddOrReplaceVerb(wxString(wxT("open")), sTmp );
879 }
880
881 AddToMimeData(mimetype, strIcon, entry, sExts, mime_desc);
882 }
883
884 void wxMimeTypesManagerImpl::LoadKDELinksForMimeType(const wxString& dirbase,
885 const wxString& subdir,
886 const wxArrayString& icondirs)
887 {
888 wxFileName dirname(dirbase, wxEmptyString);
889 dirname.AppendDir(subdir);
890 wxDir dir(dirname.GetPath());
891 if(! dir.IsOpened())
892 return;
893
894 wxLogTrace(TRACE_MIME, wxT("--- Loading from KDE directory %s ---"),
895 dirname.GetPath().c_str());
896
897 wxString filename;
898 bool cont = dir.GetFirst(&filename, wxT("*.kdelnk"), wxDIR_FILES);
899 while(cont) {
900 LoadKDELinksForMimeSubtype(dirname.GetPath(), subdir,
901 filename, icondirs);
902 cont = dir.GetNext(&filename);
903 }
904
905 // new standard for Gnome and KDE
906 cont = dir.GetFirst(&filename, wxT("*.desktop"), wxDIR_FILES);
907 while(cont) {
908 LoadKDELinksForMimeSubtype(dirname.GetPath(), subdir,
909 filename, icondirs);
910 cont = dir.GetNext(&filename);
911 }
912 }
913
914 void wxMimeTypesManagerImpl::LoadKDELinkFilesFromDir(const wxString& dirname,
915 const wxArrayString& icondirs)
916 {
917 if(! wxDir::Exists(dirname))
918 return;
919
920 wxDir dir(dirname);
921 if ( !dir.IsOpened() )
922 return;
923
924 wxString subdir;
925 bool cont = dir.GetFirst(&subdir, wxEmptyString, wxDIR_DIRS);
926 while ( cont )
927 {
928 LoadKDELinksForMimeType(dirname, subdir, icondirs);
929
930 cont = dir.GetNext(&subdir);
931 }
932 }
933
934 // Read a KDE .desktop file of type 'Application'
935 void wxMimeTypesManagerImpl::LoadKDEApp(const wxString& filename)
936 {
937 wxLogTrace(TRACE_MIME, wxT("loading KDE file %s"), filename.c_str());
938
939 wxMimeTextFile file;
940 if ( !file.Open(filename) )
941 return;
942
943 // Here, only type 'Application' should be considered.
944 int nIndex = file.pIndexOf( wxT("Type=") );
945 if (nIndex != wxNOT_FOUND &&
946 file.GetCmd(nIndex).Lower() != wxT("application"))
947 return;
948
949 // The hidden entry specifies a file to be ignored.
950 nIndex = file.pIndexOf( wxT("Hidden=") );
951 if (nIndex != wxNOT_FOUND && file.GetCmd(nIndex).Lower() == wxT("true"))
952 return;
953
954 // Semicolon separated list of mime types handled by the application.
955 nIndex = file.pIndexOf( wxT("MimeType=") );
956 if (nIndex == wxNOT_FOUND)
957 return;
958 wxString mimetypes = file.GetCmd (nIndex);
959
960 // Name of the application
961 wxString nameapp;
962 nIndex = wxNOT_FOUND;
963 #if wxUSE_INTL // try "Name[locale name]" first
964 wxLocale *locale = wxGetLocale();
965 if ( locale )
966 nIndex = file.pIndexOf(_T("Name[")+locale->GetName()+_T("]="));
967 #endif // wxUSE_INTL
968 if(nIndex == wxNOT_FOUND)
969 nIndex = file.pIndexOf( wxT("Name=") );
970 if(nIndex != wxNOT_FOUND)
971 nameapp = file.GetCmd(nIndex);
972
973 // Icon of the application.
974 wxString nameicon, namemini;
975 nIndex = wxNOT_FOUND;
976 #if wxUSE_INTL // try "Icon[locale name]" first
977 if ( locale )
978 nIndex = file.pIndexOf(_T("Icon[")+locale->GetName()+_T("]="));
979 #endif // wxUSE_INTL
980 if(nIndex == wxNOT_FOUND)
981 nIndex = file.pIndexOf( wxT("Icon=") );
982 if(nIndex != wxNOT_FOUND) {
983 nameicon = wxString(wxT("--icon ")) + file.GetCmd(nIndex);
984 namemini = wxString(wxT("--miniicon ")) + file.GetCmd(nIndex);
985 }
986
987 // Replace some of the field code in the 'Exec' entry.
988 // TODO: deal with %d, %D, %n, %N, %k and %v (but last one is deprecated)
989 nIndex = file.pIndexOf( wxT("Exec=") );
990 if (nIndex == wxNOT_FOUND)
991 return;
992 wxString sCmd = file.GetCmd(nIndex);
993 // we expect %f; others including %F and %U and %u are possible
994 sCmd.Replace(wxT("%F"), wxT("%f"));
995 sCmd.Replace(wxT("%U"), wxT("%f"));
996 sCmd.Replace(wxT("%u"), wxT("%f"));
997 if (0 == sCmd.Replace ( wxT("%f"), wxT("%s") ))
998 sCmd = sCmd + wxT(" %s");
999 sCmd.Replace(wxT("%c"), nameapp);
1000 sCmd.Replace(wxT("%i"), nameicon);
1001 sCmd.Replace(wxT("%m"), namemini);
1002
1003 wxStringTokenizer tokenizer(mimetypes, _T(";"));
1004 while(tokenizer.HasMoreTokens()) {
1005 wxString mimetype = tokenizer.GetNextToken().Lower();
1006 int nIndex = m_aTypes.Index(mimetype);
1007 if(nIndex != wxNOT_FOUND) { // is this a known MIME type?
1008 wxMimeTypeCommands* entry = m_aEntries[nIndex];
1009 entry->AddOrReplaceVerb(wxT("open"), sCmd);
1010 }
1011 }
1012 }
1013
1014 void wxMimeTypesManagerImpl::LoadKDEAppsFilesFromDir(const wxString& dirname)
1015 {
1016 if(! wxDir::Exists(dirname))
1017 return;
1018 wxDir dir(dirname);
1019 if ( !dir.IsOpened() )
1020 return;
1021
1022 wxString filename;
1023 // Look into .desktop files
1024 bool cont = dir.GetFirst(&filename, _T("*.desktop"), wxDIR_FILES);
1025 while(cont) {
1026 wxFileName p(dirname, filename);
1027 LoadKDEApp( p.GetFullPath() );
1028 cont = dir.GetNext(&filename);
1029 }
1030 // Look recursively into subdirs
1031 cont = dir.GetFirst(&filename, wxEmptyString, wxDIR_DIRS);
1032 while(cont) {
1033 wxFileName p(dirname, wxEmptyString);
1034 p.AppendDir(filename);
1035 LoadKDEAppsFilesFromDir( p.GetPath() );
1036 cont = dir.GetNext(&filename);
1037 }
1038 }
1039
1040 // Return base KDE directories.
1041 // 1) Environment variable $KDEHOME, or "~/.kde" if not set.
1042 // 2) List of directories in colon separated environment variable $KDEDIRS.
1043 // 3) Environment variable $KDEDIR in case $KDEDIRS is not set.
1044 // Notice at least the local kde directory is added to the list. If it is the
1045 // only one, use later the application 'kde-config' to get additional paths.
1046 static void GetKDEBaseDirs(wxArrayString& basedirs)
1047 {
1048 wxString env = wxGetenv( wxT("KDEHOME") );
1049 if(env.IsEmpty())
1050 env = wxGetHomeDir() + wxT("/.kde");
1051 basedirs.Add(env);
1052
1053 env = wxGetenv( wxT("KDEDIRS") );
1054 if(env.IsEmpty()) {
1055 env = wxGetenv( wxT("KDEDIR") );
1056 if(! env.IsEmpty())
1057 basedirs.Add(env);
1058 } else {
1059 wxStringTokenizer tokenizer(env, wxT(":"));
1060 while(tokenizer.HasMoreTokens())
1061 basedirs.Add( tokenizer.GetNextToken() );
1062 }
1063 }
1064
1065 static wxString ReadPathFromKDEConfig(const wxString& request)
1066 {
1067 wxString str;
1068 wxArrayString output;
1069 if(wxExecute(wxT("kde-config --path ")+request, output) == 0 &&
1070 output.GetCount() > 0)
1071 str = output.Item(0);
1072 return str;
1073 }
1074
1075 // Try to find the "Theme" entry in the configuration file, provided it exists.
1076 static wxString GetKDEThemeInFile(const wxFileName& filename)
1077 {
1078 wxString theme;
1079 wxMimeTextFile config;
1080 if ( filename.FileExists() && config.Open(filename.GetFullPath()) )
1081 {
1082 size_t cnt = config.GetLineCount();
1083 for ( size_t i = 0; i < cnt; i++ )
1084 {
1085 if ( config[i].StartsWith(wxT("Theme="), &theme) )
1086 break;
1087 }
1088 }
1089
1090 return theme;
1091 }
1092
1093 // Try to find a file "kdeglobals" in one of the directories and read the
1094 // "Theme" entry there.
1095 static wxString GetKDETheme(const wxArrayString& basedirs)
1096 {
1097 wxString theme;
1098 for(size_t i = 0; i < basedirs.GetCount(); i++) {
1099 wxFileName filename(basedirs.Item(i), wxEmptyString);
1100 filename.AppendDir( wxT("share") );
1101 filename.AppendDir( wxT("config") );
1102 filename.SetName( wxT("kdeglobals") );
1103 theme = GetKDEThemeInFile(filename);
1104 if(! theme.IsEmpty())
1105 return theme;
1106 }
1107 // If $KDEDIRS and $KDEDIR were set, we try nothing more. Otherwise, we
1108 // try to get the configuration file with 'kde-config'.
1109 if(basedirs.GetCount() > 1)
1110 return theme;
1111 wxString paths = ReadPathFromKDEConfig(wxT("config"));
1112 if(! paths.IsEmpty()) {
1113 wxStringTokenizer tokenizer(paths, wxT(":"));
1114 while( tokenizer.HasMoreTokens() ) {
1115 wxFileName filename(tokenizer.GetNextToken(), wxT("kdeglobals"));
1116 theme = GetKDEThemeInFile(filename);
1117 if(! theme.IsEmpty())
1118 return theme;
1119 }
1120 }
1121 return theme;
1122 }
1123
1124 // Get list of directories of icons.
1125 static void GetKDEIconDirs(const wxArrayString& basedirs,
1126 wxArrayString& icondirs)
1127 {
1128 wxString theme = GetKDETheme(basedirs);
1129 if(theme.IsEmpty())
1130 theme = wxT("default.kde");
1131
1132 for(size_t i = 0; i < basedirs.GetCount(); i++) {
1133 wxFileName dirname(basedirs.Item(i), wxEmptyString);
1134 dirname.AppendDir( wxT("share") );
1135 dirname.AppendDir( wxT("icons") );
1136 dirname.AppendDir(theme);
1137 dirname.AppendDir( wxT("32x32") );
1138 dirname.AppendDir( wxT("mimetypes") );
1139 if( wxDir::Exists( dirname.GetPath() ) )
1140 icondirs.Add( dirname.GetPath() );
1141 }
1142
1143 // If $KDEDIRS and $KDEDIR were not set, use 'kde-config'
1144 if(basedirs.GetCount() > 1)
1145 return;
1146 wxString paths = ReadPathFromKDEConfig(wxT("icon"));
1147 if(! paths.IsEmpty()) {
1148 wxStringTokenizer tokenizer(paths, wxT(":"));
1149 while( tokenizer.HasMoreTokens() ) {
1150 wxFileName dirname(tokenizer.GetNextToken(), wxEmptyString);
1151 dirname.AppendDir(theme);
1152 dirname.AppendDir( wxT("32x32") );
1153 dirname.AppendDir( wxT("mimetypes") );
1154 if(icondirs.Index(dirname.GetPath()) == wxNOT_FOUND &&
1155 wxDir::Exists( dirname.GetPath() ) )
1156 icondirs.Add( dirname.GetPath() );
1157 }
1158 }
1159 }
1160
1161 // Get list of directories of mime types.
1162 static void GetKDEMimeDirs(const wxArrayString& basedirs,
1163 wxArrayString& mimedirs)
1164 {
1165 for(size_t i = 0; i < basedirs.GetCount(); i++) {
1166 wxFileName dirname(basedirs.Item(i), wxEmptyString);
1167 dirname.AppendDir( wxT("share") );
1168 dirname.AppendDir( wxT("mimelnk") );
1169 if( wxDir::Exists( dirname.GetPath() ) )
1170 mimedirs.Add( dirname.GetPath() );
1171 }
1172
1173 // If $KDEDIRS and $KDEDIR were not set, use 'kde-config'
1174 if(basedirs.GetCount() > 1)
1175 return;
1176 wxString paths = ReadPathFromKDEConfig(wxT("mime"));
1177 if(! paths.IsEmpty()) {
1178 wxStringTokenizer tokenizer(paths, wxT(":"));
1179 while( tokenizer.HasMoreTokens() ) {
1180 wxFileName p(tokenizer.GetNextToken(), wxEmptyString);
1181 wxString dirname = p.GetPath(); // To remove possible trailing '/'
1182 if(mimedirs.Index(dirname) == wxNOT_FOUND &&
1183 wxDir::Exists(dirname) )
1184 mimedirs.Add(dirname);
1185 }
1186 }
1187 }
1188
1189 // Get list of directories of application desktop files.
1190 static void GetKDEAppsDirs(const wxArrayString& basedirs,
1191 wxArrayString& appsdirs)
1192 {
1193 for(size_t i = 0; i < basedirs.GetCount(); i++) {
1194 wxFileName dirname(basedirs.Item(i), wxEmptyString);
1195 dirname.AppendDir( wxT("share") );
1196 dirname.AppendDir( wxT("applnk") );
1197 if( wxDir::Exists( dirname.GetPath() ) )
1198 appsdirs.Add( dirname.GetPath() );
1199 }
1200
1201 // If $KDEDIRS and $KDEDIR were not set, use 'kde-config'
1202 if(basedirs.GetCount() > 1)
1203 return;
1204 wxString paths = ReadPathFromKDEConfig(wxT("apps"));
1205 if(! paths.IsEmpty()) {
1206 wxStringTokenizer tokenizer(paths, wxT(":"));
1207 while( tokenizer.HasMoreTokens() ) {
1208 wxFileName p(tokenizer.GetNextToken(), wxEmptyString);
1209 wxString dirname = p.GetPath(); // To remove possible trailing '/'
1210 if(appsdirs.Index(dirname) == wxNOT_FOUND &&
1211 wxDir::Exists(dirname) )
1212 appsdirs.Add(dirname);
1213 }
1214 }
1215 paths = ReadPathFromKDEConfig(wxT("xdgdata-apps"));
1216 if(! paths.IsEmpty()) {
1217 wxStringTokenizer tokenizer(paths, wxT(":"));
1218 while( tokenizer.HasMoreTokens() ) {
1219 wxFileName p(tokenizer.GetNextToken(), wxEmptyString);
1220 wxString dirname = p.GetPath(); // To remove possible trailing '/'
1221 if(appsdirs.Index(dirname) == wxNOT_FOUND &&
1222 wxDir::Exists(dirname) )
1223 appsdirs.Add(dirname);
1224 }
1225 }
1226 }
1227
1228 // Fill database with all mime types.
1229 void wxMimeTypesManagerImpl::GetKDEMimeInfo(const wxString& sExtraDir)
1230 {
1231 wxArrayString basedirs;
1232 GetKDEBaseDirs(basedirs);
1233
1234 wxArrayString icondirs;
1235 GetKDEIconDirs(basedirs, icondirs);
1236 wxArrayString mimedirs;
1237 GetKDEMimeDirs(basedirs, mimedirs);
1238 wxArrayString appsdirs;
1239 GetKDEAppsDirs(basedirs, appsdirs);
1240
1241 if(! sExtraDir.IsEmpty()) {
1242 icondirs.Add(sExtraDir + wxT("/icons"));
1243 mimedirs.Add(sExtraDir + wxT("/mimelnk"));
1244 appsdirs.Add(sExtraDir + wxT("/applnk"));
1245 }
1246
1247 // Load mime types
1248 size_t nDirs = mimedirs.GetCount(), nDir;
1249 for(nDir = 0; nDir < nDirs; nDir++)
1250 LoadKDELinkFilesFromDir(mimedirs[nDir], icondirs);
1251
1252 // Load application files and associate them to corresponding mime types.
1253 nDirs = appsdirs.GetCount();
1254 for(nDir = 0; nDir < nDirs; nDir++)
1255 LoadKDEAppsFilesFromDir(appsdirs[nDir]);
1256 }
1257
1258 // ----------------------------------------------------------------------------
1259 // wxFileTypeImpl (Unix)
1260 // ----------------------------------------------------------------------------
1261
1262 wxString wxFileTypeImpl::GetExpandedCommand(const wxString & verb, const wxFileType::MessageParameters& params) const
1263 {
1264 wxString sTmp;
1265 size_t i = 0;
1266 while ( (i < m_index.GetCount() ) && sTmp.empty() )
1267 {
1268 sTmp = m_manager->GetCommand( verb, m_index[i] );
1269 i++;
1270 }
1271
1272 return wxFileType::ExpandCommand(sTmp, params);
1273 }
1274
1275 bool wxFileTypeImpl::GetIcon(wxIconLocation *iconLoc) const
1276 {
1277 wxString sTmp;
1278 size_t i = 0;
1279 while ( (i < m_index.GetCount() ) && sTmp.empty() )
1280 {
1281 sTmp = m_manager->m_aIcons[m_index[i]];
1282 i++;
1283 }
1284
1285 if ( sTmp.empty() )
1286 return false;
1287
1288 if ( iconLoc )
1289 {
1290 iconLoc->SetFileName(sTmp);
1291 }
1292
1293 return true;
1294 }
1295
1296 bool wxFileTypeImpl::GetMimeTypes(wxArrayString& mimeTypes) const
1297 {
1298 mimeTypes.Clear();
1299 size_t nCount = m_index.GetCount();
1300 for (size_t i = 0; i < nCount; i++)
1301 mimeTypes.Add(m_manager->m_aTypes[m_index[i]]);
1302
1303 return true;
1304 }
1305
1306 size_t wxFileTypeImpl::GetAllCommands(wxArrayString *verbs,
1307 wxArrayString *commands,
1308 const wxFileType::MessageParameters& params) const
1309 {
1310 wxString vrb, cmd, sTmp;
1311 size_t count = 0;
1312 wxMimeTypeCommands * sPairs;
1313
1314 // verbs and commands have been cleared already in mimecmn.cpp...
1315 // if we find no entries in the exact match, try the inexact match
1316 for (size_t n = 0; ((count == 0) && (n < m_index.GetCount())); n++)
1317 {
1318 // list of verb = command pairs for this mimetype
1319 sPairs = m_manager->m_aEntries [m_index[n]];
1320 size_t i;
1321 for ( i = 0; i < sPairs->GetCount(); i++ )
1322 {
1323 vrb = sPairs->GetVerb(i);
1324 // some gnome entries have "." inside
1325 vrb = vrb.AfterLast(wxT('.'));
1326 cmd = sPairs->GetCmd(i);
1327 if (! cmd.empty() )
1328 {
1329 cmd = wxFileType::ExpandCommand(cmd, params);
1330 count++;
1331 if ( vrb.IsSameAs(wxT("open")))
1332 {
1333 if ( verbs )
1334 verbs->Insert(vrb, 0u);
1335 if ( commands )
1336 commands ->Insert(cmd, 0u);
1337 }
1338 else
1339 {
1340 if ( verbs )
1341 verbs->Add(vrb);
1342 if ( commands )
1343 commands->Add(cmd);
1344 }
1345 }
1346 }
1347 }
1348
1349 return count;
1350 }
1351
1352 bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
1353 {
1354 const wxString strExtensions = m_manager->GetExtension(m_index[0]);
1355 extensions.Empty();
1356
1357 // one extension in the space or comma-delimited list
1358 wxString strExt;
1359 wxString::const_iterator end = strExtensions.end();
1360 for ( wxString::const_iterator p = strExtensions.begin(); /* nothing */; ++p )
1361 {
1362 if ( p == end || *p == wxT(' ') || *p == wxT(',') )
1363 {
1364 if ( !strExt.empty() )
1365 {
1366 extensions.Add(strExt);
1367 strExt.Empty();
1368 }
1369 //else: repeated spaces
1370 // (shouldn't happen, but it's not that important if it does happen)
1371
1372 if ( p == end )
1373 break;
1374 }
1375 else if ( *p == wxT('.') )
1376 {
1377 // remove the dot from extension (but only if it's the first char)
1378 if ( !strExt.empty() )
1379 {
1380 strExt += wxT('.');
1381 }
1382 //else: no, don't append it
1383 }
1384 else
1385 {
1386 strExt += *p;
1387 }
1388 }
1389
1390 return true;
1391 }
1392
1393 // set an arbitrary command:
1394 // could adjust the code to ask confirmation if it already exists and
1395 // overwriteprompt is true, but this is currently ignored as *Associate* has
1396 // no overwrite prompt
1397 bool
1398 wxFileTypeImpl::SetCommand(const wxString& cmd,
1399 const wxString& verb,
1400 bool WXUNUSED(overwriteprompt))
1401 {
1402 wxArrayString strExtensions;
1403 wxString strDesc, strIcon;
1404
1405 wxArrayString strTypes;
1406 GetMimeTypes(strTypes);
1407 if ( strTypes.IsEmpty() )
1408 return false;
1409
1410 wxMimeTypeCommands *entry = new wxMimeTypeCommands();
1411 entry->Add(verb + wxT("=") + cmd + wxT(" %s "));
1412
1413 bool ok = false;
1414 size_t nCount = strTypes.GetCount();
1415 for ( size_t i = 0; i < nCount; i++ )
1416 {
1417 if ( m_manager->DoAssociation
1418 (
1419 strTypes[i],
1420 strIcon,
1421 entry,
1422 strExtensions,
1423 strDesc
1424 ) )
1425 {
1426 // DoAssociation() took ownership of entry, don't delete it below
1427 ok = true;
1428 }
1429 }
1430
1431 if ( !ok )
1432 delete entry;
1433
1434 return ok;
1435 }
1436
1437 // ignore index on the grounds that we only have one icon in a Unix file
1438 bool wxFileTypeImpl::SetDefaultIcon(const wxString& strIcon, int WXUNUSED(index))
1439 {
1440 if (strIcon.empty())
1441 return false;
1442
1443 wxArrayString strExtensions;
1444 wxString strDesc;
1445
1446 wxArrayString strTypes;
1447 GetMimeTypes(strTypes);
1448 if ( strTypes.IsEmpty() )
1449 return false;
1450
1451 wxMimeTypeCommands *entry = new wxMimeTypeCommands();
1452 bool ok = false;
1453 size_t nCount = strTypes.GetCount();
1454 for ( size_t i = 0; i < nCount; i++ )
1455 {
1456 if ( m_manager->DoAssociation
1457 (
1458 strTypes[i],
1459 strIcon,
1460 entry,
1461 strExtensions,
1462 strDesc
1463 ) )
1464 {
1465 // we don't need to free entry now, DoAssociation() took ownership
1466 // of it
1467 ok = true;
1468 }
1469 }
1470
1471 if ( !ok )
1472 delete entry;
1473
1474 return ok;
1475 }
1476
1477 // ----------------------------------------------------------------------------
1478 // wxMimeTypesManagerImpl (Unix)
1479 // ----------------------------------------------------------------------------
1480
1481 wxMimeTypesManagerImpl::wxMimeTypesManagerImpl()
1482 {
1483 m_initialized = false;
1484 m_mailcapStylesInited = 0;
1485 }
1486
1487 void wxMimeTypesManagerImpl::InitIfNeeded()
1488 {
1489 if ( !m_initialized )
1490 {
1491 // set the flag first to prevent recursion
1492 m_initialized = true;
1493
1494 wxString wm = wxTheApp->GetTraits()->GetDesktopEnvironment();
1495
1496 if (wm == wxT("KDE"))
1497 Initialize( wxMAILCAP_KDE );
1498 else if (wm == wxT("GNOME"))
1499 Initialize( wxMAILCAP_GNOME );
1500 else
1501 Initialize();
1502 }
1503 }
1504
1505 // read system and user mailcaps and other files
1506 void wxMimeTypesManagerImpl::Initialize(int mailcapStyles,
1507 const wxString& sExtraDir)
1508 {
1509 // read mimecap amd mime.types
1510 if ( (mailcapStyles & wxMAILCAP_NETSCAPE) ||
1511 (mailcapStyles & wxMAILCAP_STANDARD) )
1512 GetMimeInfo(sExtraDir);
1513
1514 // read GNOME tables
1515 if (mailcapStyles & wxMAILCAP_GNOME)
1516 GetGnomeMimeInfo(sExtraDir);
1517
1518 // read KDE tables which are never installed on OpenVMS
1519 #ifndef __VMS
1520 if (mailcapStyles & wxMAILCAP_KDE)
1521 GetKDEMimeInfo(sExtraDir);
1522 #endif
1523
1524 m_mailcapStylesInited |= mailcapStyles;
1525 }
1526
1527 // clear data so you can read another group of WM files
1528 void wxMimeTypesManagerImpl::ClearData()
1529 {
1530 m_aTypes.Clear();
1531 m_aIcons.Clear();
1532 m_aExtensions.Clear();
1533 m_aDescriptions.Clear();
1534
1535 WX_CLEAR_ARRAY(m_aEntries);
1536 m_aEntries.Empty();
1537
1538 m_mailcapStylesInited = 0;
1539 }
1540
1541 wxMimeTypesManagerImpl::~wxMimeTypesManagerImpl()
1542 {
1543 ClearData();
1544 }
1545
1546 void wxMimeTypesManagerImpl::GetMimeInfo(const wxString& sExtraDir)
1547 {
1548 // read this for netscape or Metamail formats
1549
1550 // directories where we look for mailcap and mime.types by default
1551 // used by netscape and pine and other mailers, using 2 different formats!
1552
1553 // (taken from metamail(1) sources)
1554 //
1555 // although RFC 1524 specifies the search path of
1556 // /etc/:/usr/etc:/usr/local/etc only, it doesn't hurt to search in more
1557 // places - OTOH, the RFC also says that this path can be changed with
1558 // MAILCAPS environment variable (containing the colon separated full
1559 // filenames to try) which is not done yet (TODO?)
1560
1561 wxString strHome = wxGetenv(wxT("HOME"));
1562
1563 wxArrayString dirs;
1564 dirs.Add( strHome + wxT("/.") );
1565 dirs.Add( wxT("/etc/") );
1566 dirs.Add( wxT("/usr/etc/") );
1567 dirs.Add( wxT("/usr/local/etc/") );
1568 dirs.Add( wxT("/etc/mail/") );
1569 dirs.Add( wxT("/usr/public/lib/") );
1570 if (!sExtraDir.empty())
1571 dirs.Add( sExtraDir + wxT("/") );
1572
1573 wxString file;
1574 size_t nDirs = dirs.GetCount();
1575 for ( size_t nDir = 0; nDir < nDirs; nDir++ )
1576 {
1577 file = dirs[nDir];
1578 file += wxT("mailcap");
1579 if ( wxFile::Exists(file) )
1580 {
1581 ReadMailcap(file);
1582 }
1583
1584 file = dirs[nDir];
1585 file += wxT("mime.types");
1586 if ( wxFile::Exists(file) )
1587 ReadMimeTypes(file);
1588 }
1589 }
1590
1591 bool wxMimeTypesManagerImpl::WriteToMimeTypes(int index, bool delete_index)
1592 {
1593 // check we have the right manager
1594 if (! ( m_mailcapStylesInited & wxMAILCAP_STANDARD) )
1595 return false;
1596
1597 bool bTemp;
1598 wxString strHome = wxGetenv(wxT("HOME"));
1599
1600 // and now the users mailcap
1601 wxString strUserMailcap = strHome + wxT("/.mime.types");
1602
1603 wxMimeTextFile file;
1604 if ( wxFile::Exists(strUserMailcap) )
1605 {
1606 bTemp = file.Open(strUserMailcap);
1607 }
1608 else
1609 {
1610 if (delete_index)
1611 return false;
1612
1613 bTemp = file.Create(strUserMailcap);
1614 }
1615
1616 if (bTemp)
1617 {
1618 int nIndex;
1619 // test for netscape's header and return false if its found
1620 nIndex = file.pIndexOf(wxT("#--Netscape"));
1621 if (nIndex != wxNOT_FOUND)
1622 {
1623 wxFAIL_MSG(wxT("Error in .mime.types\nTrying to mix Netscape and Metamail formats\nFile not modified"));
1624 return false;
1625 }
1626
1627 // write it in alternative format
1628 // get rid of unwanted entries
1629 wxString strType = m_aTypes[index];
1630 nIndex = file.pIndexOf(strType);
1631
1632 // get rid of all the unwanted entries...
1633 if (nIndex != wxNOT_FOUND)
1634 file.CommentLine(nIndex);
1635
1636 if (!delete_index)
1637 {
1638 // add the new entries in
1639 wxString sTmp = strType.Append( wxT(' '), 40 - strType.Len() );
1640 sTmp += m_aExtensions[index];
1641 file.AddLine(sTmp);
1642 }
1643
1644 bTemp = file.Write();
1645 file.Close();
1646 }
1647
1648 return bTemp;
1649 }
1650
1651 bool wxMimeTypesManagerImpl::WriteToNSMimeTypes(int index, bool delete_index)
1652 {
1653 //check we have the right managers
1654 if (! ( m_mailcapStylesInited & wxMAILCAP_NETSCAPE) )
1655 return false;
1656
1657 bool bTemp;
1658 wxString strHome = wxGetenv(wxT("HOME"));
1659
1660 // and now the users mailcap
1661 wxString strUserMailcap = strHome + wxT("/.mime.types");
1662
1663 wxMimeTextFile file;
1664 if ( wxFile::Exists(strUserMailcap) )
1665 {
1666 bTemp = file.Open(strUserMailcap);
1667 }
1668 else
1669 {
1670 if (delete_index)
1671 return false;
1672
1673 bTemp = file.Create(strUserMailcap);
1674 }
1675
1676 if (bTemp)
1677 {
1678 // write it in the format that Netscape uses
1679 int nIndex;
1680 // test for netscape's header and insert if required...
1681 // this is a comment so use true
1682 nIndex = file.pIndexOf(wxT("#--Netscape"), true);
1683 if (nIndex == wxNOT_FOUND)
1684 {
1685 // either empty file or metamail format
1686 // at present we can't cope with mixed formats, so exit to preseve
1687 // metamail entreies
1688 if (file.GetLineCount() > 0)
1689 {
1690 wxFAIL_MSG(wxT(".mime.types File not in Netscape format\nNo entries written to\n.mime.types or to .mailcap"));
1691 return false;
1692 }
1693
1694 file.InsertLine(wxT( "#--Netscape Communications Corporation MIME Information" ), 0);
1695 nIndex = 0;
1696 }
1697
1698 wxString strType = wxT("type=") + m_aTypes[index];
1699 nIndex = file.pIndexOf(strType);
1700
1701 // get rid of all the unwanted entries...
1702 if (nIndex != wxNOT_FOUND)
1703 {
1704 wxString sOld = file[nIndex];
1705 while ( (sOld.Contains(wxT("\\"))) && (nIndex < (int) file.GetLineCount()) )
1706 {
1707 file.CommentLine(nIndex);
1708 sOld = file[nIndex];
1709
1710 wxLogTrace(TRACE_MIME, wxT("--- Deleting from mime.types line '%d %s' ---"), nIndex, sOld.c_str());
1711
1712 nIndex++;
1713 }
1714
1715 if (nIndex < (int) file.GetLineCount())
1716 file.CommentLine(nIndex);
1717 }
1718 else
1719 nIndex = (int) file.GetLineCount();
1720
1721 wxString sTmp = strType + wxT(" \\");
1722 if (!delete_index)
1723 file.InsertLine(sTmp, nIndex);
1724
1725 if ( ! m_aDescriptions.Item(index).empty() )
1726 {
1727 sTmp = wxT("desc=\"") + m_aDescriptions[index]+ wxT("\" \\"); //.trim ??
1728 if (!delete_index)
1729 {
1730 nIndex++;
1731 file.InsertLine(sTmp, nIndex);
1732 }
1733 }
1734
1735 wxString sExts = m_aExtensions.Item(index);
1736 sTmp = wxT("exts=\"") + sExts.Trim(false).Trim() + wxT("\"");
1737 if (!delete_index)
1738 {
1739 nIndex++;
1740 file.InsertLine(sTmp, nIndex);
1741 }
1742
1743 bTemp = file.Write();
1744 file.Close();
1745 }
1746
1747 return bTemp;
1748 }
1749
1750 bool wxMimeTypesManagerImpl::WriteToMailCap(int index, bool delete_index)
1751 {
1752 //check we have the right managers
1753 if ( !( ( m_mailcapStylesInited & wxMAILCAP_NETSCAPE) ||
1754 ( m_mailcapStylesInited & wxMAILCAP_STANDARD) ) )
1755 return false;
1756
1757 bool bTemp = false;
1758 wxString strHome = wxGetenv(wxT("HOME"));
1759
1760 // and now the users mailcap
1761 wxString strUserMailcap = strHome + wxT("/.mailcap");
1762
1763 wxMimeTextFile file;
1764 if ( wxFile::Exists(strUserMailcap) )
1765 {
1766 bTemp = file.Open(strUserMailcap);
1767 }
1768 else
1769 {
1770 if (delete_index)
1771 return false;
1772
1773 bTemp = file.Create(strUserMailcap);
1774 }
1775
1776 if (bTemp)
1777 {
1778 // now got a file we can write to ....
1779 wxMimeTypeCommands * entries = m_aEntries[index];
1780 size_t iOpen;
1781 wxString sCmd = entries->GetCommandForVerb(wxT("open"), &iOpen);
1782 wxString sTmp;
1783
1784 sTmp = m_aTypes[index];
1785 wxString sOld;
1786 int nIndex = file.pIndexOf(sTmp);
1787
1788 // get rid of all the unwanted entries...
1789 if (nIndex == wxNOT_FOUND)
1790 {
1791 nIndex = (int) file.GetLineCount();
1792 }
1793 else
1794 {
1795 sOld = file[nIndex];
1796 wxLogTrace(TRACE_MIME, wxT("--- Deleting from mailcap line '%d' ---"), nIndex);
1797
1798 while ( (sOld.Contains(wxT("\\"))) && (nIndex < (int) file.GetLineCount()) )
1799 {
1800 file.CommentLine(nIndex);
1801 if (nIndex < (int) file.GetLineCount())
1802 sOld = sOld + file[nIndex];
1803 }
1804
1805 if (nIndex < (int)
1806 file.GetLineCount()) file.CommentLine(nIndex);
1807 }
1808
1809 sTmp += wxT(";") + sCmd; //includes wxT(" %s ");
1810
1811 // write it in the format that Netscape uses (default)
1812 if (! ( m_mailcapStylesInited & wxMAILCAP_STANDARD ) )
1813 {
1814 if (! delete_index)
1815 file.InsertLine(sTmp, nIndex);
1816 nIndex++;
1817 }
1818 else
1819 {
1820 // write extended format
1821
1822 // TODO - FIX this code:
1823 // ii) lost entries
1824 // sOld holds all the entries, but our data store only has some
1825 // eg test= is not stored
1826
1827 // so far we have written the mimetype and command out
1828 wxStringTokenizer sT(sOld, wxT(";\\"));
1829 if (sT.CountTokens() > 2)
1830 {
1831 // first one mimetype; second one command, rest unknown...
1832 wxString s;
1833 s = sT.GetNextToken();
1834 s = sT.GetNextToken();
1835
1836 // first unknown
1837 s = sT.GetNextToken();
1838 while ( ! s.empty() )
1839 {
1840 bool bKnownToken = false;
1841 if (s.Contains(wxT("description=")))
1842 bKnownToken = true;
1843 if (s.Contains(wxT("x11-bitmap=")))
1844 bKnownToken = true;
1845
1846 size_t i;
1847 size_t nCount = entries->GetCount();
1848 for (i=0; i < nCount; i++)
1849 {
1850 if (s.Contains(entries->GetVerb(i)))
1851 bKnownToken = true;
1852 }
1853
1854 if (!bKnownToken)
1855 {
1856 sTmp += wxT("; \\");
1857 file.InsertLine(sTmp, nIndex);
1858 sTmp = s;
1859 }
1860
1861 s = sT.GetNextToken();
1862 }
1863 }
1864
1865 if (! m_aDescriptions[index].empty() )
1866 {
1867 sTmp += wxT("; \\");
1868 file.InsertLine(sTmp, nIndex);
1869 nIndex++;
1870 sTmp = wxT(" description=\"") + m_aDescriptions[index] + wxT("\"");
1871 }
1872
1873 if (! m_aIcons[index].empty() )
1874 {
1875 sTmp += wxT("; \\");
1876 file.InsertLine(sTmp, nIndex);
1877 nIndex++;
1878 sTmp = wxT(" x11-bitmap=\"") + m_aIcons[index] + wxT("\"");
1879 }
1880
1881 if ( entries->GetCount() > 1 )
1882 {
1883 size_t i;
1884 for (i=0; i < entries->GetCount(); i++)
1885 if ( i != iOpen )
1886 {
1887 sTmp += wxT("; \\");
1888 file.InsertLine(sTmp, nIndex);
1889 nIndex++;
1890 sTmp = wxT(" ") + entries->GetVerbCmd(i);
1891 }
1892 }
1893
1894 file.InsertLine(sTmp, nIndex);
1895 nIndex++;
1896 }
1897
1898 bTemp = file.Write();
1899 file.Close();
1900 }
1901
1902 return bTemp;
1903 }
1904
1905 wxFileType * wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo& ftInfo)
1906 {
1907 InitIfNeeded();
1908
1909 wxString strType = ftInfo.GetMimeType();
1910 wxString strDesc = ftInfo.GetDescription();
1911 wxString strIcon = ftInfo.GetIconFile();
1912
1913 wxMimeTypeCommands *entry = new wxMimeTypeCommands();
1914
1915 if ( ! ftInfo.GetOpenCommand().empty())
1916 entry->Add(wxT("open=") + ftInfo.GetOpenCommand() + wxT(" %s "));
1917 if ( ! ftInfo.GetPrintCommand().empty())
1918 entry->Add(wxT("print=") + ftInfo.GetPrintCommand() + wxT(" %s "));
1919
1920 // now find where these extensions are in the data store and remove them
1921 wxArrayString sA_Exts = ftInfo.GetExtensions();
1922 wxString sExt, sExtStore;
1923 size_t i, nIndex;
1924 size_t nExtCount = sA_Exts.GetCount();
1925 for (i=0; i < nExtCount; i++)
1926 {
1927 sExt = sA_Exts.Item(i);
1928
1929 // clean up to just a space before and after
1930 sExt.Trim().Trim(false);
1931 sExt = wxT(' ') + sExt + wxT(' ');
1932 size_t nCount = m_aExtensions.GetCount();
1933 for (nIndex = 0; nIndex < nCount; nIndex++)
1934 {
1935 sExtStore = m_aExtensions.Item(nIndex);
1936 if (sExtStore.Replace(sExt, wxT(" ") ) > 0)
1937 m_aExtensions.Item(nIndex) = sExtStore;
1938 }
1939 }
1940
1941 if ( !DoAssociation(strType, strIcon, entry, sA_Exts, strDesc) )
1942 return NULL;
1943
1944 return GetFileTypeFromMimeType(strType);
1945 }
1946
1947 bool wxMimeTypesManagerImpl::DoAssociation(const wxString& strType,
1948 const wxString& strIcon,
1949 wxMimeTypeCommands *entry,
1950 const wxArrayString& strExtensions,
1951 const wxString& strDesc)
1952 {
1953 int nIndex = AddToMimeData(strType, strIcon, entry, strExtensions, strDesc, true);
1954
1955 if ( nIndex == wxNOT_FOUND )
1956 return false;
1957
1958 return WriteMimeInfo(nIndex, false);
1959 }
1960
1961 bool wxMimeTypesManagerImpl::WriteMimeInfo(int nIndex, bool delete_mime )
1962 {
1963 bool ok = true;
1964
1965 if ( m_mailcapStylesInited & wxMAILCAP_STANDARD )
1966 {
1967 // write in metamail format;
1968 if (WriteToMimeTypes(nIndex, delete_mime) )
1969 if ( WriteToMailCap(nIndex, delete_mime) )
1970 ok = false;
1971 }
1972
1973 if ( m_mailcapStylesInited & wxMAILCAP_NETSCAPE )
1974 {
1975 // write in netsacpe format;
1976 if (WriteToNSMimeTypes(nIndex, delete_mime) )
1977 if ( WriteToMailCap(nIndex, delete_mime) )
1978 ok = false;
1979 }
1980
1981 // Don't write GNOME files here as this is not
1982 // allowed and simply doesn't work
1983
1984 if (m_mailcapStylesInited & wxMAILCAP_KDE)
1985 {
1986 // write in KDE format;
1987 if (WriteKDEMimeFile(nIndex, delete_mime) )
1988 ok = false;
1989 }
1990
1991 return ok;
1992 }
1993
1994 int wxMimeTypesManagerImpl::AddToMimeData(const wxString& strType,
1995 const wxString& strIcon,
1996 wxMimeTypeCommands *entry,
1997 const wxArrayString& strExtensions,
1998 const wxString& strDesc,
1999 bool replaceExisting)
2000 {
2001 InitIfNeeded();
2002
2003 // ensure mimetype is always lower case
2004 wxString mimeType = strType.Lower();
2005
2006 // is this a known MIME type?
2007 int nIndex = m_aTypes.Index(mimeType);
2008 if ( nIndex == wxNOT_FOUND )
2009 {
2010 // new file type
2011 m_aTypes.Add(mimeType);
2012 m_aIcons.Add(strIcon);
2013 m_aEntries.Add(entry ? entry : new wxMimeTypeCommands);
2014
2015 // change nIndex so we can use it below to add the extensions
2016 m_aExtensions.Add(wxEmptyString);
2017 nIndex = m_aExtensions.size() - 1;
2018
2019 m_aDescriptions.Add(strDesc);
2020 }
2021 else // yes, we already have it
2022 {
2023 if ( replaceExisting )
2024 {
2025 // if new description change it
2026 if ( !strDesc.empty())
2027 m_aDescriptions[nIndex] = strDesc;
2028
2029 // if new icon change it
2030 if ( !strIcon.empty())
2031 m_aIcons[nIndex] = strIcon;
2032
2033 if ( entry )
2034 {
2035 delete m_aEntries[nIndex];
2036 m_aEntries[nIndex] = entry;
2037 }
2038 }
2039 else // add data we don't already have ...
2040 {
2041 // if new description add only if none
2042 if ( m_aDescriptions[nIndex].empty() )
2043 m_aDescriptions[nIndex] = strDesc;
2044
2045 // if new icon and no existing icon
2046 if ( m_aIcons[nIndex].empty() )
2047 m_aIcons[nIndex] = strIcon;
2048
2049 // add any new entries...
2050 if ( entry )
2051 {
2052 wxMimeTypeCommands *entryOld = m_aEntries[nIndex];
2053
2054 size_t count = entry->GetCount();
2055 for ( size_t i = 0; i < count; i++ )
2056 {
2057 const wxString& verb = entry->GetVerb(i);
2058 if ( !entryOld->HasVerb(verb) )
2059 {
2060 entryOld->AddOrReplaceVerb(verb, entry->GetCmd(i));
2061 }
2062 }
2063
2064 // as we don't store it anywhere, it won't be deleted later as
2065 // usual -- do it immediately instead
2066 delete entry;
2067 }
2068 }
2069 }
2070
2071 // always add the extensions to this mimetype
2072 wxString& exts = m_aExtensions[nIndex];
2073
2074 // add all extensions we don't have yet
2075 wxString ext;
2076 size_t count = strExtensions.GetCount();
2077 for ( size_t i = 0; i < count; i++ )
2078 {
2079 ext = strExtensions[i];
2080 ext += wxT(' ');
2081
2082 if ( exts.Find(ext) == wxNOT_FOUND )
2083 {
2084 exts += ext;
2085 }
2086 }
2087
2088 // check data integrity
2089 wxASSERT( m_aTypes.GetCount() == m_aEntries.GetCount() &&
2090 m_aTypes.GetCount() == m_aExtensions.GetCount() &&
2091 m_aTypes.GetCount() == m_aIcons.GetCount() &&
2092 m_aTypes.GetCount() == m_aDescriptions.GetCount() );
2093
2094 return nIndex;
2095 }
2096
2097 wxFileType * wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& ext)
2098 {
2099 if (ext.empty() )
2100 return NULL;
2101
2102 InitIfNeeded();
2103
2104 size_t count = m_aExtensions.GetCount();
2105 for ( size_t n = 0; n < count; n++ )
2106 {
2107 wxStringTokenizer tk(m_aExtensions[n], wxT(' '));
2108
2109 while ( tk.HasMoreTokens() )
2110 {
2111 // consider extensions as not being case-sensitive
2112 if ( tk.GetNextToken().IsSameAs(ext, false /* no case */) )
2113 {
2114 // found
2115 wxFileType *fileType = new wxFileType;
2116 fileType->m_impl->Init(this, n);
2117
2118 return fileType;
2119 }
2120 }
2121 }
2122
2123 return NULL;
2124 }
2125
2126 wxFileType * wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
2127 {
2128 InitIfNeeded();
2129
2130 wxFileType * fileType = NULL;
2131 // mime types are not case-sensitive
2132 wxString mimetype(mimeType);
2133 mimetype.MakeLower();
2134
2135 // first look for an exact match
2136 int index = m_aTypes.Index(mimetype);
2137 if ( index != wxNOT_FOUND )
2138 {
2139 fileType = new wxFileType;
2140 fileType->m_impl->Init(this, index);
2141 }
2142
2143 // then try to find "text/*" as match for "text/plain" (for example)
2144 // NB: if mimeType doesn't contain '/' at all, BeforeFirst() will return
2145 // the whole string - ok.
2146
2147 index = wxNOT_FOUND;
2148 wxString strCategory = mimetype.BeforeFirst(wxT('/'));
2149
2150 size_t nCount = m_aTypes.GetCount();
2151 for ( size_t n = 0; n < nCount; n++ )
2152 {
2153 if ( (m_aTypes[n].BeforeFirst(wxT('/')) == strCategory ) &&
2154 m_aTypes[n].AfterFirst(wxT('/')) == wxT("*") )
2155 {
2156 index = n;
2157 break;
2158 }
2159 }
2160
2161 if ( index != wxNOT_FOUND )
2162 {
2163 // don't throw away fileType that was already found
2164 if (!fileType)
2165 fileType = new wxFileType;
2166 fileType->m_impl->Init(this, index);
2167 }
2168
2169 return fileType;
2170 }
2171
2172 wxString wxMimeTypesManagerImpl::GetCommand(const wxString & verb, size_t nIndex) const
2173 {
2174 wxString command, testcmd, sV, sTmp;
2175 sV = verb + wxT("=");
2176
2177 // list of verb = command pairs for this mimetype
2178 wxMimeTypeCommands * sPairs = m_aEntries [nIndex];
2179
2180 size_t i;
2181 size_t nCount = sPairs->GetCount();
2182 for ( i = 0; i < nCount; i++ )
2183 {
2184 sTmp = sPairs->GetVerbCmd (i);
2185 if ( sTmp.Contains(sV) )
2186 command = sTmp.AfterFirst(wxT('='));
2187 }
2188
2189 return command;
2190 }
2191
2192 void wxMimeTypesManagerImpl::AddFallback(const wxFileTypeInfo& filetype)
2193 {
2194 InitIfNeeded();
2195
2196 wxString extensions;
2197 const wxArrayString& exts = filetype.GetExtensions();
2198 size_t nExts = exts.GetCount();
2199 for ( size_t nExt = 0; nExt < nExts; nExt++ )
2200 {
2201 if ( nExt > 0 )
2202 extensions += wxT(' ');
2203
2204 extensions += exts[nExt];
2205 }
2206
2207 AddMimeTypeInfo(filetype.GetMimeType(),
2208 extensions,
2209 filetype.GetDescription());
2210
2211 AddMailcapInfo(filetype.GetMimeType(),
2212 filetype.GetOpenCommand(),
2213 filetype.GetPrintCommand(),
2214 wxT(""),
2215 filetype.GetDescription());
2216 }
2217
2218 void wxMimeTypesManagerImpl::AddMimeTypeInfo(const wxString& strMimeType,
2219 const wxString& strExtensions,
2220 const wxString& strDesc)
2221 {
2222 // reading mailcap may find image/* , while
2223 // reading mime.types finds image/gif and no match is made
2224 // this means all the get functions don't work fix this
2225 wxString strIcon;
2226 wxString sTmp = strExtensions;
2227
2228 wxArrayString sExts;
2229 sTmp.Trim().Trim(false);
2230
2231 while (!sTmp.empty())
2232 {
2233 sExts.Add(sTmp.AfterLast(wxT(' ')));
2234 sTmp = sTmp.BeforeLast(wxT(' '));
2235 }
2236
2237 AddToMimeData(strMimeType, strIcon, NULL, sExts, strDesc, true);
2238 }
2239
2240 void wxMimeTypesManagerImpl::AddMailcapInfo(const wxString& strType,
2241 const wxString& strOpenCmd,
2242 const wxString& strPrintCmd,
2243 const wxString& strTest,
2244 const wxString& strDesc)
2245 {
2246 InitIfNeeded();
2247
2248 wxMimeTypeCommands *entry = new wxMimeTypeCommands;
2249 entry->Add(wxT("open=") + strOpenCmd);
2250 entry->Add(wxT("print=") + strPrintCmd);
2251 entry->Add(wxT("test=") + strTest);
2252
2253 wxString strIcon;
2254 wxArrayString strExtensions;
2255
2256 AddToMimeData(strType, strIcon, entry, strExtensions, strDesc, true);
2257 }
2258
2259 bool wxMimeTypesManagerImpl::ReadMimeTypes(const wxString& strFileName)
2260 {
2261 wxLogTrace(TRACE_MIME, wxT("--- Parsing mime.types file '%s' ---"),
2262 strFileName.c_str());
2263
2264 wxMimeTextFile file(strFileName);
2265 if ( !file.Open() )
2266 return false;
2267
2268 // the information we extract
2269 wxString strMimeType, strDesc, strExtensions;
2270
2271 size_t nLineCount = file.GetLineCount();
2272 const wxChar *pc = NULL;
2273 for ( size_t nLine = 0; nLine < nLineCount; nLine++ )
2274 {
2275 if ( pc == NULL )
2276 {
2277 // now we're at the start of the line
2278 pc = file[nLine].c_str();
2279 }
2280 else
2281 {
2282 // we didn't finish with the previous line yet
2283 nLine--;
2284 }
2285
2286 // skip whitespace
2287 while ( wxIsspace(*pc) )
2288 pc++;
2289
2290 // comment or blank line?
2291 if ( *pc == wxT('#') || !*pc )
2292 {
2293 // skip the whole line
2294 pc = NULL;
2295 continue;
2296 }
2297
2298 // detect file format
2299 const wxChar *pEqualSign = wxStrchr(pc, wxT('='));
2300 if ( pEqualSign == NULL )
2301 {
2302 // brief format
2303 // ------------
2304
2305 // first field is mime type
2306 for ( strMimeType.Empty(); !wxIsspace(*pc) && *pc != wxT('\0'); pc++ )
2307 {
2308 strMimeType += *pc;
2309 }
2310
2311 // skip whitespace
2312 while ( wxIsspace(*pc) )
2313 pc++;
2314
2315 // take all the rest of the string
2316 strExtensions = pc;
2317
2318 // no description...
2319 strDesc.Empty();
2320 }
2321 else
2322 {
2323 // expanded format
2324 // ---------------
2325
2326 // the string on the left of '=' is the field name
2327 wxString strLHS(pc, pEqualSign - pc);
2328
2329 // eat whitespace
2330 for ( pc = pEqualSign + 1; wxIsspace(*pc); pc++ )
2331 ;
2332
2333 const wxChar *pEnd;
2334 if ( *pc == wxT('"') )
2335 {
2336 // the string is quoted and ends at the matching quote
2337 pEnd = wxStrchr(++pc, wxT('"'));
2338 if ( pEnd == NULL )
2339 {
2340 wxLogWarning(wxT("Mime.types file %s, line %lu: unterminated quoted string."),
2341 strFileName.c_str(), nLine + 1L);
2342 }
2343 }
2344 else
2345 {
2346 // unquoted string ends at the first space or at the end of
2347 // line
2348 for ( pEnd = pc; *pEnd && !wxIsspace(*pEnd); pEnd++ )
2349 ;
2350 }
2351
2352 // now we have the RHS (field value)
2353 wxString strRHS(pc, pEnd - pc);
2354
2355 // check what follows this entry
2356 if ( *pEnd == wxT('"') )
2357 {
2358 // skip this quote
2359 pEnd++;
2360 }
2361
2362 for ( pc = pEnd; wxIsspace(*pc); pc++ )
2363 ;
2364
2365 // if there is something left, it may be either a '\\' to continue
2366 // the line or the next field of the same entry
2367 bool entryEnded = *pc == wxT('\0');
2368 bool nextFieldOnSameLine = false;
2369 if ( !entryEnded )
2370 {
2371 nextFieldOnSameLine = ((*pc != wxT('\\')) || (pc[1] != wxT('\0')));
2372 }
2373
2374 // now see what we got
2375 if ( strLHS == wxT("type") )
2376 {
2377 strMimeType = strRHS;
2378 }
2379 else if ( strLHS.StartsWith(wxT("desc")) )
2380 {
2381 strDesc = strRHS;
2382 }
2383 else if ( strLHS == wxT("exts") )
2384 {
2385 strExtensions = strRHS;
2386 }
2387 else if ( strLHS == wxT("icon") )
2388 {
2389 // this one is simply ignored: it usually refers to Netscape
2390 // built in icons which are useless for us anyhow
2391 }
2392 else if ( !strLHS.StartsWith(wxT("x-")) )
2393 {
2394 // we suppose that all fields starting with "X-" are
2395 // unregistered extensions according to the standard practice,
2396 // but it may be worth telling the user about other junk in
2397 // his mime.types file
2398 wxLogWarning(wxT("Unknown field in file %s, line %lu: '%s'."),
2399 strFileName.c_str(), nLine + 1L, strLHS.c_str());
2400 }
2401
2402 if ( !entryEnded )
2403 {
2404 if ( !nextFieldOnSameLine )
2405 pc = NULL;
2406 //else: don't reset it
2407
2408 // as we don't reset strMimeType, the next field in this entry
2409 // will be interpreted correctly.
2410
2411 continue;
2412 }
2413 }
2414
2415 // depending on the format (Mosaic or Netscape) either space or comma
2416 // is used to separate the extensions
2417 strExtensions.Replace(wxT(","), wxT(" "));
2418
2419 // also deal with the leading dot
2420 if ( !strExtensions.empty() && strExtensions[0u] == wxT('.') )
2421 {
2422 strExtensions.erase(0, 1);
2423 }
2424
2425 wxLogTrace(TRACE_MIME, wxT("mime.types: '%s' => '%s' (%s)"),
2426 strExtensions.c_str(),
2427 strMimeType.c_str(),
2428 strDesc.c_str());
2429
2430 AddMimeTypeInfo(strMimeType, strExtensions, strDesc);
2431
2432 // finished with this line
2433 pc = NULL;
2434 }
2435
2436 return true;
2437 }
2438
2439 // ----------------------------------------------------------------------------
2440 // UNIX mailcap files parsing
2441 // ----------------------------------------------------------------------------
2442
2443 // the data for a single MIME type
2444 struct MailcapLineData
2445 {
2446 // field values
2447 wxString type,
2448 cmdOpen,
2449 test,
2450 icon,
2451 desc;
2452
2453 wxArrayString verbs,
2454 commands;
2455
2456 // flags
2457 bool testfailed,
2458 needsterminal,
2459 copiousoutput;
2460
2461 MailcapLineData() { testfailed = needsterminal = copiousoutput = false; }
2462 };
2463
2464 // process a non-standard (i.e. not the first or second one) mailcap field
2465 bool
2466 wxMimeTypesManagerImpl::ProcessOtherMailcapField(MailcapLineData& data,
2467 const wxString& curField)
2468 {
2469 if ( curField.empty() )
2470 {
2471 // we don't care
2472 return true;
2473 }
2474
2475 // is this something of the form foo=bar?
2476 if ( curField.find('=') != wxString::npos )
2477 {
2478 // split "LHS = RHS" in 2
2479 wxString lhs = curField.BeforeFirst(wxT('=')),
2480 rhs = curField.AfterFirst(wxT('='));
2481
2482 lhs.Trim(true); // from right
2483 rhs.Trim(false); // from left
2484
2485 // it might be quoted
2486 if ( !rhs.empty() && rhs[0u] == wxT('"') && rhs.Last() == wxT('"') )
2487 {
2488 rhs = rhs.Mid(1, rhs.length() - 2);
2489 }
2490
2491 // is it a command verb or something else?
2492 if ( lhs == wxT("test") )
2493 {
2494 if ( wxSystem(rhs) == 0 )
2495 {
2496 // ok, test passed
2497 wxLogTrace(TRACE_MIME_TEST,
2498 wxT("Test '%s' for mime type '%s' succeeded."),
2499 rhs.c_str(), data.type.c_str());
2500 }
2501 else
2502 {
2503 wxLogTrace(TRACE_MIME_TEST,
2504 wxT("Test '%s' for mime type '%s' failed, skipping."),
2505 rhs.c_str(), data.type.c_str());
2506
2507 data.testfailed = true;
2508 }
2509 }
2510 else if ( lhs == wxT("desc") )
2511 {
2512 data.desc = rhs;
2513 }
2514 else if ( lhs == wxT("x11-bitmap") )
2515 {
2516 data.icon = rhs;
2517 }
2518 else if ( lhs == wxT("notes") )
2519 {
2520 // ignore
2521 }
2522 else // not a (recognized) special case, must be a verb (e.g. "print")
2523 {
2524 data.verbs.Add(lhs);
2525 data.commands.Add(rhs);
2526 }
2527 }
2528 else // '=' not found
2529 {
2530 // so it must be a simple flag
2531 if ( curField == wxT("needsterminal") )
2532 {
2533 data.needsterminal = true;
2534 }
2535 else if ( curField == wxT("copiousoutput"))
2536 {
2537 // copiousoutput impies that the viewer is a console program
2538 data.needsterminal =
2539 data.copiousoutput = true;
2540 }
2541 else if ( !IsKnownUnimportantField(curField) )
2542 {
2543 return false;
2544 }
2545 }
2546
2547 return true;
2548 }
2549
2550 bool wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName,
2551 bool fallback)
2552 {
2553 wxLogTrace(TRACE_MIME, wxT("--- Parsing mailcap file '%s' ---"),
2554 strFileName.c_str());
2555
2556 wxMimeTextFile file(strFileName);
2557 if ( !file.Open() )
2558 return false;
2559
2560 // indices of MIME types (in m_aTypes) we already found in this file
2561 //
2562 // (see the comments near the end of function for the reason we need this)
2563 wxArrayInt aIndicesSeenHere;
2564
2565 // accumulator for the current field
2566 wxString curField;
2567 curField.reserve(1024);
2568
2569 const wxChar *pPagerEnv = wxGetenv(wxT("PAGER"));
2570
2571 const wxArrayString empty_extensions_list;
2572
2573 size_t nLineCount = file.GetLineCount();
2574 for ( size_t nLine = 0; nLine < nLineCount; nLine++ )
2575 {
2576 // now we're at the start of the line
2577 const wxChar *pc = file[nLine].c_str();
2578
2579 // skip whitespace
2580 while ( wxIsspace(*pc) )
2581 pc++;
2582
2583 // comment or empty string?
2584 if ( *pc == wxT('#') || *pc == wxT('\0') )
2585 continue;
2586
2587 // no, do parse
2588 // ------------
2589
2590 // what field are we currently in? The first 2 are fixed and there may
2591 // be an arbitrary number of other fields parsed by
2592 // ProcessOtherMailcapField()
2593 //
2594 // the first field is the MIME type
2595 enum
2596 {
2597 Field_Type,
2598 Field_OpenCmd,
2599 Field_Other
2600 } currentToken = Field_Type;
2601
2602 // the flags and field values on the current line
2603 MailcapLineData data;
2604
2605 bool cont = true;
2606 while ( cont )
2607 {
2608 switch ( *pc )
2609 {
2610 case wxT('\\'):
2611 // interpret the next character literally (notice that
2612 // backslash can be used for line continuation)
2613 if ( *++pc == wxT('\0') )
2614 {
2615 // fetch the next line if there is one
2616 if ( nLine == nLineCount - 1 )
2617 {
2618 // something is wrong, bail out
2619 cont = false;
2620
2621 wxLogDebug(wxT("Mailcap file %s, line %lu: '\\' on the end of the last line ignored."),
2622 strFileName.c_str(),
2623 nLine + 1L);
2624 }
2625 else
2626 {
2627 // pass to the beginning of the next line
2628 pc = file[++nLine].c_str();
2629
2630 // skip pc++ at the end of the loop
2631 continue;
2632 }
2633 }
2634 else
2635 {
2636 // just a normal character
2637 curField += *pc;
2638 }
2639 break;
2640
2641 case wxT('\0'):
2642 cont = false; // end of line reached, exit the loop
2643
2644 // fall through to still process this field
2645
2646 case wxT(';'):
2647 // trim whitespaces from both sides
2648 curField.Trim(true).Trim(false);
2649
2650 switch ( currentToken )
2651 {
2652 case Field_Type:
2653 data.type = curField.Lower();
2654 if ( data.type.empty() )
2655 {
2656 // I don't think that this is a valid mailcap
2657 // entry, but try to interpret it somehow
2658 data.type = wxT('*');
2659 }
2660
2661 if ( data.type.Find(wxT('/')) == wxNOT_FOUND )
2662 {
2663 // we interpret "type" as "type/*"
2664 data.type += wxT("/*");
2665 }
2666
2667 currentToken = Field_OpenCmd;
2668 break;
2669
2670 case Field_OpenCmd:
2671 data.cmdOpen = curField;
2672
2673 currentToken = Field_Other;
2674 break;
2675
2676 case Field_Other:
2677 if ( !ProcessOtherMailcapField(data, curField) )
2678 {
2679 // don't flood the user with error messages if
2680 // we don't understand something in his
2681 // mailcap, but give them in debug mode because
2682 // this might be useful for the programmer
2683 wxLogDebug
2684 (
2685 wxT("Mailcap file %s, line %lu: unknown field '%s' for the MIME type '%s' ignored."),
2686 strFileName.c_str(),
2687 nLine + 1L,
2688 curField.c_str(),
2689 data.type.c_str()
2690 );
2691 }
2692 else if ( data.testfailed )
2693 {
2694 // skip this entry entirely
2695 cont = false;
2696 }
2697
2698 // it already has this value
2699 //currentToken = Field_Other;
2700 break;
2701
2702 default:
2703 wxFAIL_MSG(wxT("unknown field type in mailcap"));
2704 }
2705
2706 // next token starts immediately after ';'
2707 curField.Empty();
2708 break;
2709
2710 default:
2711 curField += *pc;
2712 }
2713
2714 // continue in the same line
2715 pc++;
2716 }
2717
2718 // we read the entire entry, check what have we got
2719 // ------------------------------------------------
2720
2721 // check that we really read something reasonable
2722 if ( currentToken < Field_Other )
2723 {
2724 wxLogWarning(wxT("Mailcap file %s, line %lu: incomplete entry ignored."),
2725 strFileName.c_str(), nLine + 1L);
2726
2727 continue;
2728 }
2729
2730 // if the test command failed, it's as if the entry were not there at all
2731 if ( data.testfailed )
2732 {
2733 continue;
2734 }
2735
2736 // support for flags:
2737 // 1. create an xterm for 'needsterminal'
2738 // 2. append "| $PAGER" for 'copiousoutput'
2739 //
2740 // Note that the RFC says that having both needsterminal and
2741 // copiousoutput is probably a mistake, so it seems that running
2742 // programs with copiousoutput inside an xterm as it is done now
2743 // is a bad idea (FIXME)
2744 if ( data.copiousoutput )
2745 {
2746 data.cmdOpen << wxT(" | ") << (pPagerEnv ? pPagerEnv : wxT("more"));
2747 }
2748
2749 if ( data.needsterminal )
2750 {
2751 data.cmdOpen.insert(0, wxT("xterm -e sh -c '"));
2752 data.cmdOpen.append(wxT("'"));
2753 }
2754
2755 if ( !data.cmdOpen.empty() )
2756 {
2757 data.verbs.Insert(wxT("open"), 0);
2758 data.commands.Insert(data.cmdOpen, 0);
2759 }
2760
2761 // we have to decide whether the new entry should replace any entries
2762 // for the same MIME type we had previously found or not
2763 bool overwrite;
2764
2765 // the fall back entries have the lowest priority, by definition
2766 if ( fallback )
2767 {
2768 overwrite = false;
2769 }
2770 else
2771 {
2772 // have we seen this one before?
2773 int nIndex = m_aTypes.Index(data.type);
2774
2775 // and if we have, was it in this file? if not, we should
2776 // overwrite the previously seen one
2777 overwrite = nIndex == wxNOT_FOUND ||
2778 aIndicesSeenHere.Index(nIndex) == wxNOT_FOUND;
2779 }
2780
2781 wxLogTrace(TRACE_MIME, wxT("mailcap %s: %s [%s]"),
2782 data.type.c_str(), data.cmdOpen.c_str(),
2783 overwrite ? wxT("replace") : wxT("add"));
2784
2785 int n = AddToMimeData
2786 (
2787 data.type,
2788 data.icon,
2789 new wxMimeTypeCommands(data.verbs, data.commands),
2790 empty_extensions_list,
2791 data.desc,
2792 overwrite
2793 );
2794
2795 if ( overwrite )
2796 {
2797 aIndicesSeenHere.Add(n);
2798 }
2799 }
2800
2801 return true;
2802 }
2803
2804 size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString& mimetypes)
2805 {
2806 InitIfNeeded();
2807
2808 mimetypes.Empty();
2809
2810 size_t count = m_aTypes.GetCount();
2811 for ( size_t n = 0; n < count; n++ )
2812 {
2813 // don't return template types from here (i.e. anything containg '*')
2814 const wxString &type = m_aTypes[n];
2815 if ( type.Find(wxT('*')) == wxNOT_FOUND )
2816 {
2817 mimetypes.Add(type);
2818 }
2819 }
2820
2821 return mimetypes.GetCount();
2822 }
2823
2824 // ----------------------------------------------------------------------------
2825 // writing to MIME type files
2826 // ----------------------------------------------------------------------------
2827
2828 bool wxMimeTypesManagerImpl::Unassociate(wxFileType *ft)
2829 {
2830 InitIfNeeded();
2831
2832 wxArrayString sMimeTypes;
2833 ft->GetMimeTypes(sMimeTypes);
2834
2835 size_t i;
2836 size_t nCount = sMimeTypes.GetCount();
2837 for (i = 0; i < nCount; i ++)
2838 {
2839 const wxString &sMime = sMimeTypes.Item(i);
2840 int nIndex = m_aTypes.Index(sMime);
2841 if ( nIndex == wxNOT_FOUND)
2842 {
2843 // error if we get here ??
2844 return false;
2845 }
2846 else
2847 {
2848 WriteMimeInfo(nIndex, true);
2849 m_aTypes.RemoveAt(nIndex);
2850 m_aEntries.RemoveAt(nIndex);
2851 m_aExtensions.RemoveAt(nIndex);
2852 m_aDescriptions.RemoveAt(nIndex);
2853 m_aIcons.RemoveAt(nIndex);
2854 }
2855 }
2856 // check data integrity
2857 wxASSERT( m_aTypes.GetCount() == m_aEntries.GetCount() &&
2858 m_aTypes.GetCount() == m_aExtensions.GetCount() &&
2859 m_aTypes.GetCount() == m_aIcons.GetCount() &&
2860 m_aTypes.GetCount() == m_aDescriptions.GetCount() );
2861
2862 return true;
2863 }
2864
2865 // ----------------------------------------------------------------------------
2866 // private functions
2867 // ----------------------------------------------------------------------------
2868
2869 static bool IsKnownUnimportantField(const wxString& fieldAll)
2870 {
2871 static const wxChar * const knownFields[] =
2872 {
2873 wxT("x-mozilla-flags"),
2874 wxT("nametemplate"),
2875 wxT("textualnewlines"),
2876 };
2877
2878 wxString field = fieldAll.BeforeFirst(wxT('='));
2879 for ( size_t n = 0; n < WXSIZEOF(knownFields); n++ )
2880 {
2881 if ( field.CmpNoCase(knownFields[n]) == 0 )
2882 return true;
2883 }
2884
2885 return false;
2886 }
2887
2888 #endif
2889 // wxUSE_MIMETYPE && wxUSE_FILE && wxUSE_TEXTFILE