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