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