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