2 ///////////////////////////////////////////////////////////////////////////// 
   3 // Name:        unix/mimetype.cpp 
   4 // Purpose:     classes and functions to manage MIME types 
   5 // Author:      Vadim Zeitlin 
   9 // Copyright:   (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr> 
  10 // Licence:     wxWindows license (part of wxExtra library) 
  11 ///////////////////////////////////////////////////////////////////////////// 
  13 // known bugs; there may be others!! chris elliott, biol75@york.ac.uk 27 Mar 01 
  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 
  31 // ============================================================================ 
  33 // ============================================================================ 
  35 // ---------------------------------------------------------------------------- 
  37 // ---------------------------------------------------------------------------- 
  40     #pragma implementation "mimetype.h" 
  43 // for compilers that support precompilation, includes "wx.h". 
  44 #include "wx/wxprec.h" 
  54 #if wxUSE_MIMETYPE && wxUSE_FILE && wxUSE_TEXTFILE 
  57   #include "wx/string.h" 
  68 #include "wx/dynarray.h" 
  69 #include "wx/confbase.h" 
  72 #include "wx/textfile.h" 
  75 #include "wx/tokenzr.h" 
  77 #include "wx/unix/mimetype.h" 
  79 // other standard headers 
  83 /* silence warnings for comparing unsigned int's <0 */ 
  84 # pragma message disable unscomzer 
  87 // this is a class to extend wxArrayString... 
  88 class wxMimeArrayString 
: public wxArrayString
 
  96     size_t pIndexOf (const wxString 
& verb
) 
 100         // avoid a problem with modifying const parameter 
 102         while ( (i 
< GetCount()) && (! Item(i
).MakeLower().Contains(sTmp
)) ) 
 106         if ( i
==GetCount() ) i 
= (size_t)wxNOT_FOUND
; 
 110     bool ReplaceOrAddLineCmd (const wxString verb
, const wxString 
& cmd
) 
 112         size_t nIndex 
= pIndexOf (verb
); 
 113         if (nIndex 
== (size_t)wxNOT_FOUND
) 
 114             Add(verb 
+ wxT("=") + cmd
); 
 116             Item(nIndex
) = verb 
+ wxT("=") + cmd
; 
 120     wxString 
GetVerb (size_t i
) 
 122         if (i 
> GetCount() ) return wxEmptyString
; 
 123         wxString sTmp 
= Item(i
).BeforeFirst(wxT('=')); 
 127     wxString 
GetCmd (size_t i
) 
 129         if (i 
> GetCount() ) return wxEmptyString
; 
 130         wxString sTmp 
= Item(i
).AfterFirst(wxT('=')); 
 135 //this class extends wxTextFile 
 136 class wxMimeTextFile 
: public wxTextFile
 
 140     wxMimeTextFile () : wxTextFile () {}; 
 141     wxMimeTextFile (const wxString
& strFile
) : wxTextFile (strFile
)  {  }; 
 143     int pIndexOf(const wxString 
& sSearch
, bool bIncludeComments 
= FALSE
, int iStart 
= 0) 
 146         int nResult 
= wxNOT_FOUND
; 
 147         if (i
>=GetLineCount()) return wxNOT_FOUND
; 
 149         wxString sTest 
= sSearch
; 
 153         if (bIncludeComments
) 
 155             while ( (i 
< GetLineCount())   ) 
 159                 if (sLine
.Contains(sTest
)) nResult 
= (int) i
; 
 165             while ( (i 
< GetLineCount()) ) 
 169                 if ( ! sLine
.StartsWith(wxT("#"))) 
 171                     if (sLine
.Contains(sTest
)) nResult 
= (int) i
; 
 179     bool CommentLine(int nIndex
) 
 181         if (nIndex 
<0) return FALSE
; 
 182         if (nIndex 
>= (int)GetLineCount() ) return FALSE
; 
 183         GetLine(nIndex
) = GetLine(nIndex
).Prepend(wxT("#")); 
 187     bool CommentLine(const wxString 
& sTest
) 
 189         int nIndex 
= pIndexOf(sTest
); 
 190         if (nIndex 
<0) return FALSE
; 
 191         if (nIndex 
>= (int)GetLineCount() ) return FALSE
; 
 192         GetLine(nIndex
) = GetLine(nIndex
).Prepend(wxT("#")); 
 196     wxString 
GetVerb (size_t i
) 
 198         if (i 
> GetLineCount() ) return wxEmptyString
; 
 199         wxString sTmp 
= GetLine(i
).BeforeFirst(wxT('=')); 
 203     wxString 
GetCmd (size_t i
) 
 205         if (i 
> GetLineCount() ) return wxEmptyString
; 
 206         wxString sTmp 
= GetLine(i
).AfterFirst(wxT('=')); 
 211 // in case we're compiling in non-GUI mode 
 212 class WXDLLEXPORT wxIcon
; 
 214 // ---------------------------------------------------------------------------- 
 216 // ---------------------------------------------------------------------------- 
 218 // MIME code tracing mask 
 219 #define TRACE_MIME _T("mime") 
 221 // ---------------------------------------------------------------------------- 
 223 // ---------------------------------------------------------------------------- 
 225 // there are some fields which we don't understand but for which we don't give 
 226 // warnings as we know that they're not important - this function is used to 
 228 static bool IsKnownUnimportantField(const wxString
& field
); 
 230 // ---------------------------------------------------------------------------- 
 232 // ---------------------------------------------------------------------------- 
 236 // This class uses both mailcap and mime.types to gather information about file 
 239 // The information about mailcap file was extracted from metamail(1) sources 
 240 // and documentation and subsequently revised when I found the RFC 1524 
 243 // Format of mailcap file: spaces are ignored, each line is either a comment 
 244 // (starts with '#') or a line of the form <field1>;<field2>;...;<fieldN>. 
 245 // A backslash can be used to quote semicolons and newlines (and, in fact, 
 246 // anything else including itself). 
 248 // The first field is always the MIME type in the form of type/subtype (see RFC 
 249 // 822) where subtype may be '*' meaning "any". Following metamail, we accept 
 250 // "type" which means the same as "type/*", although I'm not sure whether this 
 253 // The second field is always the command to run. It is subject to 
 254 // parameter/filename expansion described below. 
 256 // All the following fields are optional and may not be present at all. If 
 257 // they're present they may appear in any order, although each of them should 
 258 // appear only once. The optional fields are the following: 
 259 //  * notes=xxx is an uninterpreted string which is silently ignored 
 260 //  * test=xxx is the command to be used to determine whether this mailcap line 
 261 //    applies to our data or not. The RHS of this field goes through the 
 262 //    parameter/filename expansion (as the 2nd field) and the resulting string 
 263 //    is executed. The line applies only if the command succeeds, i.e. returns 0 
 265 //  * print=xxx is the command to be used to print (and not view) the data of 
 266 //    this type (parameter/filename expansion is done here too) 
 267 //  * edit=xxx is the command to open/edit the data of this type 
 268 //  * needsterminal means that a new interactive console must be created for 
 270 //  * copiousoutput means that the viewer doesn't interact with the user but 
 271 //    produces (possibly) a lof of lines of output on stdout (i.e. "cat" is a 
 272 //    good example), thus it might be a good idea to use some kind of paging 
 274 //  * textualnewlines means not to perform CR/LF translation (not honored) 
 275 //  * compose and composetyped fields are used to determine the program to be 
 276 //    called to create a new message pert in the specified format (unused). 
 278 // Parameter/filename expansion: 
 279 //  * %s is replaced with the (full) file name 
 280 //  * %t is replaced with MIME type/subtype of the entry 
 281 //  * for multipart type only %n is replaced with the nnumber of parts and %F is 
 282 //    replaced by an array of (content-type, temporary file name) pairs for all 
 283 //    message parts (TODO) 
 284 //  * %{parameter} is replaced with the value of parameter taken from 
 285 //    Content-type header line of the message. 
 288 // There are 2 possible formats for mime.types file, one entry per line (used 
 289 // for global mime.types and called Mosaic format) and "expanded" format where 
 290 // an entry takes multiple lines (used for users mime.types and called 
 293 // For both formats spaces are ignored and lines starting with a '#' are 
 294 // comments. Each record has one of two following forms: 
 295 //  a) for "brief" format: 
 296 //      <mime type>  <space separated list of extensions> 
 297 //  b) for "expanded" format: 
 298 //      type=<mime type> BACKSLASH 
 299 //      desc="<description>" BACKSLASH 
 300 //      exts="<comma separated list of extensions>" 
 302 // (where BACKSLASH is a literal '\\' which we can't put here because cpp 
 305 // We try to autodetect the format of mime.types: if a non-comment line starts 
 306 // with "type=" we assume the second format, otherwise the first one. 
 308 // there may be more than one entry for one and the same mime type, to 
 309 // choose the right one we have to run the command specified in the test 
 310 // field on our data. 
 312 // ---------------------------------------------------------------------------- 
 314 // ---------------------------------------------------------------------------- 
 316 // GNOME stores the info we're interested in in several locations: 
 317 //  1. xxx.keys files under /usr/share/mime-info 
 318 //  2. xxx.keys files under ~/.gnome/mime-info 
 320 // The format of xxx.keys file is the following: 
 325 // with blank lines separating the entries and indented lines starting with 
 326 // TABs. We're interested in the field icon-filename whose value is the path 
 327 // containing the icon. 
 329 // Update (Chris Elliott): apparently there may be an optional "[lang]" prefix 
 330 // just before the field name. 
 333 bool wxMimeTypesManagerImpl::CheckGnomeDirsExist () 
 336     wxGetHomeDir( &gnomedir 
); 
 337     wxString sTmp 
= gnomedir
; 
 338     sTmp 
= sTmp 
+ "/.gnome" ; 
 339     if (! wxDir::Exists ( sTmp 
) ) 
 341         if (!wxMkdir ( sTmp 
)) 
 343             wxFAIL_MSG (wxString ("Failed to create directory\n.gnome in \nCheckGnomeDirsExist") + sTmp 
); 
 347     sTmp 
= sTmp 
+ "/mime-info"; 
 348     if (! wxDir::Exists ( sTmp 
) ) 
 350         if (!wxMkdir ( sTmp 
)) 
 352             wxFAIL_MSG (wxString ("Failed to create directory\nmime-info in \nCheckGnomeDirsExist") + sTmp 
); 
 362 bool wxMimeTypesManagerImpl::WriteGnomeKeyFile(int index
, bool delete_index
) 
 365     wxGetHomeDir( &gnomedir 
); 
 367     wxMimeTextFile 
outfile ( gnomedir 
+ "/.gnome/mime-info/user.keys"); 
 368     // if this fails probably Gnome is not installed ?? 
 369     // create it anyway as a private mime store 
 371     if (! outfile
.Open () ) 
 373         if (delete_index
) return FALSE
; 
 374         if (!CheckGnomeDirsExist() ) return FALSE
; 
 378     wxString sTmp
, strType 
= m_aTypes
[index
]; 
 379     int nIndex 
= outfile
.pIndexOf(strType
); 
 380     if ( nIndex 
== wxNOT_FOUND 
) 
 382         outfile
.AddLine ( strType 
+ wxT(':') ); 
 383         // see file:/usr/doc/gnome-libs-devel-1.0.40/devel-docs/mime-type-handling.txt 
 384         // as this does not deal with internationalisation 
 385         //        wxT( "\t[en_US]") + verb + wxT ('=') + cmd + wxT(" %f"); 
 386         wxMimeArrayString 
* entries 
= m_aEntries
[index
]; 
 388         for (i
=0; i 
< entries
->GetCount(); i
++) 
 390             sTmp 
= entries
->Item(i
); 
 391             sTmp
.Replace( wxT("%s"), wxT("%f") ); 
 392             sTmp 
= wxT ( "\t") + sTmp
; 
 393             outfile
.AddLine ( sTmp 
); 
 395         //for international use do something like this 
 396         //outfile.AddLine ( wxString( "\t[en_US]icon-filename=") + cmd ); 
 397         outfile
.AddLine ( wxT( "\ticon-filename=") + m_aIcons
[index
] ); 
 401         if (delete_index
) outfile
.CommentLine(nIndex
); 
 402         wxMimeArrayString sOld
; 
 403         size_t nOld 
= nIndex 
+ 1; 
 404         bool oldEntryEnd 
= FALSE
; 
 405         while ( (nOld 
< outfile
.GetLineCount() )&& (oldEntryEnd 
== FALSE 
)) 
 407             sTmp 
= outfile
.GetLine(nOld
); 
 408             if ( (sTmp
[0u] == wxT('\t')) || (sTmp
[0u] == wxT('#')) ) 
 410                 // we have another line to deal with 
 411                 outfile
.CommentLine(nOld
); 
 413                 // add the line to our store 
 414                 if ((!delete_index
) && (sTmp
[0u] == wxT('\t'))) sOld
.Add(sTmp
); 
 416             // next mimetpye ??or blank line 
 417             else oldEntryEnd 
= TRUE
; 
 419         // list of entries in our data; these should all be in sOld, 
 420         // though sOld may also contain other entries , eg flags 
 423             wxMimeArrayString 
* entries 
= m_aEntries
[index
]; 
 425             for (i
=0; i 
< entries
->GetCount(); i
++) 
 427                 // replace any entries in sold that match verbs we know 
 428                 sOld
.ReplaceOrAddLineCmd ( entries
->GetVerb(i
), entries
->GetCmd (i
) ); 
 430             //sOld should also contain the icon 
 431             if ( !m_aIcons
[index
].IsEmpty() ) 
 432                 sOld
.ReplaceOrAddLineCmd ( wxT( "icon-filename"), m_aIcons
[index
] ); 
 434             for (i
=0; i 
< sOld
.GetCount(); i
++) 
 437                 sTmp
.Replace( wxT("%s"), wxT("%f") ); 
 438                 sTmp 
= wxT ( "\t") + sTmp
; 
 440                 outfile
.InsertLine ( sTmp
, nIndex 
); 
 444     bool bTmp 
= outfile
.Write (); 
 449 bool wxMimeTypesManagerImpl::WriteGnomeMimeFile(int index
, bool delete_index
) 
 452     wxGetHomeDir( &gnomedir 
); 
 454     wxMimeTextFile 
outfile ( gnomedir 
+ "/.gnome/mime-info/user.mime"); 
 455     // if this fails probably Gnome is not installed ?? 
 456     // create it anyway as a private mime store 
 457     if (! outfile
.Open () ) 
 459         if (delete_index
) return FALSE
; 
 460         if (!CheckGnomeDirsExist() ) return FALSE
; 
 463     wxString strType 
= m_aTypes
[index
]; 
 464     int nIndex 
= outfile
.pIndexOf(strType
); 
 465     if ( nIndex 
== wxNOT_FOUND 
) 
 467         outfile
.AddLine ( strType 
); 
 468         outfile
.AddLine ( wxT( "\text:") + m_aExtensions
.Item(index
) ); 
 474             outfile
.CommentLine(nIndex
); 
 475             outfile
.CommentLine(nIndex
+1); 
 478             {// check for next line being the right one to replace ?? 
 479             wxString sOld 
= outfile
.GetLine(nIndex
+1); 
 480             if (sOld
.Contains(wxT("\text: "))) 
 482                 outfile
.GetLine(nIndex
+1) = wxT( "\text: ") + m_aExtensions
.Item(index
); 
 486                 outfile
.InsertLine(wxT( "\text: ") + m_aExtensions
.Item(index
), nIndex 
+ 1 ); 
 490     bool bTmp 
= outfile
.Write (); 
 495 void wxMimeTypesManagerImpl::LoadGnomeDataFromKeyFile(const wxString
& filename
) 
 497     wxTextFile 
textfile(filename
); 
 498     if ( !textfile
.Open() ) 
 500     wxLogTrace(TRACE_MIME
, wxT("--- Opened Gnome file %s  ---"), 
 503     // values for the entry being parsed 
 504     wxString curMimeType
, curIconFile
; 
 505     wxMimeArrayString 
* entry 
= new wxMimeArrayString
; 
 507     // these are always empty in this file 
 508     wxArrayString strExtensions
; 
 512     size_t nLineCount 
= textfile
.GetLineCount(); 
 514     while ( nLine 
< nLineCount
) 
 516         pc 
= textfile
[nLine
].c_str(); 
 517         if ( *pc 
!= _T('#') ) 
 520             wxLogTrace(TRACE_MIME
, wxT("--- Reading from Gnome file %s '%s' ---"), 
 521                  filename
.c_str(),pc
); 
 524             if (sTmp
.Contains(wxT("=")) ) 
 526             if (sTmp
.Contains( wxT("icon-filename=") ) ) 
 528                 curIconFile 
= sTmp
.AfterFirst(wxT('=')); 
 530             else //: some other field, 
 532                 //may contain lines like this (RH7) 
 533                 // \t[lang]open.tex."TeX this file"=tex %f 
 534                 // \tflags.tex.flags=needsterminal 
 535                 // \topen.latex."LaTeX this file"=latex %f 
 536                 // \tflags.latex.flags=needsterminal 
 540                 // \topen.convert.Convert file to Postscript=dvips %f -o `basename %f .dvi`.ps 
 542                 // for now ignore lines with flags in...FIX 
 543                 sTmp 
= sTmp
.AfterLast(wxT(']')); 
 544                 sTmp 
= sTmp
.AfterLast(wxT('\t')); 
 545                 sTmp
.Trim(FALSE
).Trim(); 
 546                 if (0 == sTmp
.Replace ( wxT("%f"), wxT("%s") )) sTmp 
= sTmp 
+ wxT(" %s"); 
 551             } // emd of has an equals sign 
 554                 // not a comment and not an equals sign 
 555                 if (sTmp
.Contains(wxT('/'))) 
 557                     // this is the start of the new mimetype 
 558                     // overwrite any existing data 
 559                     if (! curMimeType
.IsEmpty()) 
 561                         AddToMimeData ( curMimeType
, curIconFile
, entry
, strExtensions
, strDesc
); 
 563                         // now get ready for next bit 
 564                         entry 
= new wxMimeArrayString
; 
 566                     curMimeType 
= sTmp
.BeforeFirst(wxT(':')); 
 569         } // end of not a comment 
 570     // ignore blank lines 
 572     } // end of while, save any data 
 573     if (! curMimeType
.IsEmpty()) 
 575         AddToMimeData ( curMimeType
, curIconFile
, entry
, strExtensions
, strDesc
); 
 582 void wxMimeTypesManagerImpl::LoadGnomeMimeTypesFromMimeFile(const wxString
& filename
) 
 584     wxTextFile 
textfile(filename
); 
 585     if ( !textfile
.Open() ) 
 587     wxLogTrace(TRACE_MIME
, wxT("--- Opened Gnome file %s  ---"), 
 590     // values for the entry being parsed 
 591     wxString curMimeType
, curExtList
; 
 594     size_t nLineCount 
= textfile
.GetLineCount(); 
 595     for ( size_t nLine 
= 0; ; nLine
++ ) 
 597         if ( nLine 
< nLineCount 
) 
 599             pc 
= textfile
[nLine
].c_str(); 
 600             if ( *pc 
== _T('#') ) 
 608             // so that we will fall into the "if" below 
 615             if ( !!curMimeType 
&& !!curExtList 
) 
 617                  wxLogTrace(TRACE_MIME
, wxT("--- At end of Gnome file  finding mimetype %s  ---"), 
 618                  curMimeType
.c_str()); 
 620                  AddMimeTypeInfo(curMimeType
, curExtList
, wxEmptyString
); 
 625                 // the end - this can only happen if nLine == nLineCount 
 634         // what do we have here? 
 635         if ( *pc 
== _T('\t') ) 
 637             // this is a field=value ling 
 638             pc
++; // skip leading TAB 
 640             static const int lenField 
= 4; // strlen("ext:") 
 641             if ( wxStrncmp(pc
, _T("ext:"), lenField
) == 0 ) 
 643                 // skip ' ' which follows and take everything left until the end 
 645                 curExtList 
= pc 
+ lenField 
+ 1; 
 647             //else: some other field, we don't care 
 651             // this is the start of the new section 
 652             wxLogTrace(TRACE_MIME
, wxT("--- In Gnome file  finding mimetype %s  ---"), 
 653                  curMimeType
.c_str()); 
 655             if (! curMimeType
.IsEmpty()) AddMimeTypeInfo(curMimeType
, curExtList
, wxEmptyString
); 
 659             while ( *pc 
!= _T(':') && *pc 
!= _T('\0') ) 
 661                 curMimeType 
+= *pc
++; 
 668 void wxMimeTypesManagerImpl::LoadGnomeMimeFilesFromDir(const wxString
& dirbase
) 
 670     wxASSERT_MSG( !!dirbase 
&& !wxEndsWithPathSeparator(dirbase
), 
 671                   _T("base directory shouldn't end with a slash") ); 
 673     wxString dirname 
= dirbase
; 
 674     dirname 
<< _T("/mime-info"); 
 676     if ( !wxDir::Exists(dirname
) ) 
 680     if ( !dir
.IsOpened() ) 
 683     // we will concatenate it with filename to get the full path below 
 687     bool cont 
= dir
.GetFirst(&filename
, _T("*.mime"), wxDIR_FILES
); 
 690         LoadGnomeMimeTypesFromMimeFile(dirname 
+ filename
); 
 692         cont 
= dir
.GetNext(&filename
); 
 695     cont 
= dir
.GetFirst(&filename
, _T("*.keys"), wxDIR_FILES
); 
 698         LoadGnomeDataFromKeyFile(dirname 
+ filename
); 
 700         cont 
= dir
.GetNext(&filename
); 
 707 void wxMimeTypesManagerImpl::GetGnomeMimeInfo(const wxString
& sExtraDir
) 
 711     dirs
.Add(_T("/usr/share")); 
 712     dirs
.Add(_T("/usr/local/share")); 
 715     wxGetHomeDir( &gnomedir 
); 
 716     gnomedir 
+= _T("/.gnome"); 
 717     dirs
.Add( gnomedir 
); 
 718     if (!sExtraDir
.IsEmpty()) dirs
.Add( sExtraDir 
); 
 720     size_t nDirs 
= dirs
.GetCount(); 
 721     for ( size_t nDir 
= 0; nDir 
< nDirs
; nDir
++ ) 
 723         LoadGnomeMimeFilesFromDir(dirs
[nDir
]); 
 732 // ---------------------------------------------------------------------------- 
 734 // ---------------------------------------------------------------------------- 
 736 // KDE stores the icon info in its .kdelnk files. The file for mimetype/subtype 
 737 // may be found in either of the following locations 
 739 //  1. $KDEDIR/share/mimelnk/mimetype/subtype.kdelnk 
 740 //  2. ~/.kde/share/mimelnk/mimetype/subtype.kdelnk 
 742 // The format of a .kdelnk file is almost the same as the one used by 
 743 // wxFileConfig, i.e. there are groups, comments and entries. The icon is the 
 744 // value for the entry "Type" 
 746 // kde writing; see http://webcvs.kde.org/cgi-bin/cvsweb.cgi/~checkout~/kdelibs/kio/DESKTOP_ENTRY_STANDARD 
 747 // for now write to .kdelnk but should eventually do .desktop instead (in preference??) 
 749 bool wxMimeTypesManagerImpl::CheckKDEDirsExist ( const wxString 
& sOK
, const wxString 
& sTest 
) 
 754             if (wxDir::Exists(sOK
)) return TRUE
; 
 759             wxString sStart 
= sOK 
+ wxT("/") + sTest
.BeforeFirst(wxT('/')); 
 760             if (!wxDir::Exists(sStart
))  wxMkdir(sStart
); 
 761             wxString sEnd 
= sTest
.AfterFirst(wxT('/')); 
 762             return CheckKDEDirsExist(sStart
, sEnd
); 
 766 bool wxMimeTypesManagerImpl::WriteKDEMimeFile(int index
, bool delete_index
) 
 768     wxMimeTextFile appoutfile
, mimeoutfile
; 
 769     wxString sHome 
= wxGetHomeDir(); 
 770     wxString sTmp 
= wxT(".kde/share/mimelnk/"); 
 771     wxString sMime 
= m_aTypes
[index
] ; 
 772     CheckKDEDirsExist (sHome
, sTmp 
+ sMime
.BeforeFirst(wxT('/')) ); 
 773     sTmp 
= sHome 
+ wxT('/') + sTmp 
+ sMime 
+ wxT(".kdelnk"); 
 776     bool bMimeExists 
= mimeoutfile
.Open (sTmp
); 
 779        bTemp 
= mimeoutfile
.Create (sTmp
); 
 780        // some unknown error eg out of disk space 
 781        if (!bTemp
) return FALSE
; 
 784     sTmp 
= wxT(".kde/share/applnk/"); 
 785     CheckKDEDirsExist (sHome
, sTmp 
+ sMime
.AfterFirst(wxT('/')) ); 
 786     sTmp 
= sHome 
+ wxT('/') + sTmp 
+ sMime
.AfterFirst(wxT('/')) + wxT(".kdelnk"); 
 789     bAppExists 
= appoutfile
.Open (sTmp
); 
 792         bTemp 
= appoutfile
.Create (sTmp
); 
 793         // some unknown error eg out of disk space 
 794         if (!bTemp
) return FALSE
; 
 797     // fixed data; write if new file 
 800         mimeoutfile
.AddLine(wxT("#KDE Config File")); 
 801         mimeoutfile
.AddLine(wxT("[KDE Desktop Entry]")); 
 802         mimeoutfile
.AddLine(wxT("Version=1.0")); 
 803         mimeoutfile
.AddLine(wxT("Type=MimeType")); 
 804         mimeoutfile
.AddLine(wxT("MimeType=") + sMime
); 
 809         mimeoutfile
.AddLine(wxT("#KDE Config File")); 
 810         mimeoutfile
.AddLine(wxT("[KDE Desktop Entry]")); 
 811         appoutfile
.AddLine(wxT("Version=1.0")); 
 812         appoutfile
.AddLine(wxT("Type=Application")); 
 813         appoutfile
.AddLine(wxT("MimeType=") + sMime 
+ wxT(';')); 
 818     mimeoutfile
.CommentLine(wxT("Comment=")); 
 820         mimeoutfile
.AddLine(wxT("Comment=") + m_aDescriptions
[index
]); 
 821     appoutfile
.CommentLine(wxT("Name=")); 
 823         appoutfile
.AddLine(wxT("Comment=") + m_aDescriptions
[index
]); 
 825     sTmp 
= m_aIcons
[index
]; 
 826     // we can either give the full path, or the shortfilename if its in 
 827     // one of the directories we search 
 828     mimeoutfile
.CommentLine(wxT("Icon=") ); 
 829     if (!delete_index
) mimeoutfile
.AddLine(wxT("Icon=") + sTmp 
); 
 830     appoutfile
.CommentLine(wxT("Icon=")  ); 
 831     if (!delete_index
) appoutfile
.AddLine(wxT("Icon=") + sTmp 
); 
 833     sTmp 
= wxT(" ") + m_aExtensions
[index
]; 
 835     wxStringTokenizer 
tokenizer(sTmp
, _T(" ")); 
 836     sTmp 
= wxT("Patterns="); 
 837     mimeoutfile
.CommentLine(sTmp
); 
 838     while ( tokenizer
.HasMoreTokens() ) 
 840         // holds an extension; need to change it to *.ext; 
 841         wxString e 
= wxT("*.") + tokenizer
.GetNextToken() + wxT(";"); 
 844     if (!delete_index
) mimeoutfile
.AddLine(sTmp
); 
 846     wxMimeArrayString 
* entries 
= m_aEntries
[index
]; 
 847     // if we don't find open just have an empty string ... FIX this 
 848     size_t iOpen 
= entries
->pIndexOf(wxT("open")); 
 849     sTmp 
= entries
->GetCmd(iOpen
); 
 850     sTmp
.Replace( wxT("%s"), wxT("%f") ); 
 852     mimeoutfile
.CommentLine(wxT("DefaultApp=") ); 
 853     if (!delete_index
) mimeoutfile
.AddLine(wxT("DefaultApp=") + sTmp
); 
 855     sTmp
.Replace( wxT("%f"), wxT("") ); 
 856     appoutfile
.CommentLine(wxT("Exec=")); 
 857     if (!delete_index
) appoutfile
.AddLine(wxT("Exec=") + sTmp
); 
 859     if (entries
->GetCount() > 1) 
 861             //other actions as well as open 
 865     if (mimeoutfile
.Write ()) bTemp 
= TRUE
; 
 866     mimeoutfile
.Close (); 
 867     if (appoutfile
.Write ()) bTemp 
= TRUE
; 
 875 void wxMimeTypesManagerImpl::LoadKDELinksForMimeSubtype(const wxString
& dirbase
, 
 876                                                const wxString
& subdir
, 
 877                                                const wxString
& filename
, 
 878                                                const wxArrayString
& icondirs
) 
 881     if ( !file
.Open(dirbase 
+ filename
) ) return; 
 883     wxMimeArrayString 
* entry 
= new wxMimeArrayString
; 
 885     wxString mimetype
, mime_desc
, strIcon
; 
 887     int nIndex 
= file
.pIndexOf ("MimeType="); 
 888     if (nIndex 
== wxNOT_FOUND
) 
 890     // construct mimetype from the directory name and the basename of the 
 891     // file (it always has .kdelnk extension) 
 892     mimetype 
<< subdir 
<< _T('/') << filename
.BeforeLast(_T('.')); 
 894     else mimetype 
= file
.GetCmd (nIndex
); 
 896     // first find the description string: it is the value in either "Comment=" 
 897     // line or "Comment[<locale_name>]=" one 
 898     nIndex 
= wxNOT_FOUND
; 
 902     wxLocale 
*locale 
= wxGetLocale(); 
 905         // try "Comment[locale name]" first 
 906         comment 
<< _T("Comment[") + locale
->GetName() + _T("]="); 
 907         nIndex 
= file
.pIndexOf(comment
); 
 911     if ( nIndex 
== wxNOT_FOUND 
) 
 913         comment 
= _T("Comment="); 
 914         nIndex 
= file
.pIndexOf(comment
); 
 917     if ( nIndex 
!= wxNOT_FOUND 
) mime_desc 
= file
.GetCmd(nIndex
); 
 918     //else: no description 
 920     // next find the extensions 
 921     wxString mime_extension
; 
 923     nIndex 
= file
.pIndexOf(_T("Patterns=")); 
 924     if ( nIndex 
!= wxNOT_FOUND 
) 
 926         wxString exts 
= file
.GetCmd (nIndex
) ;; 
 928         wxStringTokenizer 
tokenizer(exts
, _T(";")); 
 929         while ( tokenizer
.HasMoreTokens() ) 
 931             wxString e 
= tokenizer
.GetNextToken(); 
 932             if ( e
.Left(2) != _T("*.") ) 
 933                 continue; // don't support too difficult patterns 
 935             if ( !mime_extension
.empty() ) 
 937                 // separate from the previous ext 
 938                 mime_extension 
<< _T(' '); 
 941             mime_extension 
<< e
.Mid(2); 
 944     sExts
.Add(mime_extension
); 
 947     // ok, now we can take care of icon: 
 949     nIndex 
= file
.pIndexOf(_T("Icon=")); 
 950     if ( nIndex 
!= wxNOT_FOUND 
) 
 952         strIcon 
= file
.GetCmd(nIndex
); 
 953         //it could be the real path, but more often a short name 
 954         if (!wxFileExists(strIcon
)) 
 956             // icon is just the short name 
 957             if ( !strIcon
.IsEmpty() ) 
 959         // we must check if the file exists because it may be stored 
 960         // in many locations, at least ~/.kde and $KDEDIR 
 961         size_t nDir
, nDirs 
= icondirs
.GetCount(); 
 962         for ( nDir 
= 0; nDir 
< nDirs
; nDir
++ ) 
 963                     if (wxFileExists(icondirs
[nDir
] + strIcon
)) 
 965                         strIcon
.Prepend(icondirs
[nDir
]); 
 971     // now look for lines which know about the application 
 972     // exec= or DefaultApp= 
 974     nIndex 
= file
.pIndexOf(wxT("DefaultApp")); 
 976     if ( nIndex 
== wxNOT_FOUND 
) 
 979         nIndex 
= file
.pIndexOf(wxT("Exec")); 
 982     if ( nIndex 
!= wxNOT_FOUND 
) 
 984         wxString sTmp 
= file
.GetCmd(nIndex
); 
 985         // we expect %f; others including  %F and %U and %u are possible 
 986         if (0 == sTmp
.Replace ( wxT("%f"), wxT("%s") )) sTmp 
= sTmp 
+ wxT(" %s"); 
 987         entry
->ReplaceOrAddLineCmd (wxString(wxT("open")), sTmp 
); 
 990     AddToMimeData (mimetype
, strIcon
, entry
, sExts
, mime_desc
); 
 995 void wxMimeTypesManagerImpl::LoadKDELinksForMimeType(const wxString
& dirbase
, 
 996                                             const wxString
& subdir
, 
 997                                             const wxArrayString
& icondirs
) 
 999     wxString dirname 
= dirbase
; 
1002     if ( !dir
.IsOpened() ) 
1008     bool cont 
= dir
.GetFirst(&filename
, _T("*.kdelnk"), wxDIR_FILES
); 
1011         LoadKDELinksForMimeSubtype(dirname
, subdir
, filename
, icondirs
); 
1013         cont 
= dir
.GetNext(&filename
); 
1015     // new standard for Gnome and KDE 
1016     cont 
= dir
.GetFirst(&filename
, _T("*.desktop"), wxDIR_FILES
); 
1019         LoadKDELinksForMimeSubtype(dirname
, subdir
, filename
, icondirs
); 
1021         cont 
= dir
.GetNext(&filename
); 
1025 void wxMimeTypesManagerImpl::LoadKDELinkFilesFromDir(const wxString
& dirbase
, 
1026                                             const wxArrayString
& icondirs
) 
1028     wxASSERT_MSG( !!dirbase 
&& !wxEndsWithPathSeparator(dirbase
), 
1029                   _T("base directory shouldn't end with a slash") ); 
1031     wxString dirname 
= dirbase
; 
1032     dirname 
<< _T("/mimelnk"); 
1034     if ( !wxDir::Exists(dirname
) ) 
1038     if ( !dir
.IsOpened() ) 
1041     // we will concatenate it with dir name to get the full path below 
1045     bool cont 
= dir
.GetFirst(&subdir
, wxEmptyString
, wxDIR_DIRS
); 
1048         LoadKDELinksForMimeType(dirname
, subdir
, icondirs
); 
1050         cont 
= dir
.GetNext(&subdir
); 
1054 void wxMimeTypesManagerImpl::GetKDEMimeInfo(const wxString
& sExtraDir
) 
1057     wxArrayString icondirs
; 
1059     // settings in ~/.kde have maximal priority 
1060     dirs
.Add(wxGetHomeDir() + _T("/.kde/share")); 
1061     icondirs
.Add(wxGetHomeDir() + _T("/.kde/share/icons/")); 
1063     // the variable KDEDIR is set when KDE is running 
1064     const char *kdedir 
= getenv("KDEDIR"); 
1067         dirs
.Add(wxString(kdedir
) + _T("/share")); 
1068         icondirs
.Add(wxString(kdedir
) + _T("/share/icons/")); 
1072         // try to guess KDEDIR 
1073         dirs
.Add(_T("/usr/share")); 
1074         dirs
.Add(_T("/opt/kde/share")); 
1075         icondirs
.Add(_T("/usr/share/icons/")); 
1076         icondirs
.Add(_T("/usr/X11R6/share/icons/")); // Debian/Corel linux 
1077         icondirs
.Add(_T("/opt/kde/share/icons/")); 
1080     if (!sExtraDir
.IsEmpty()) dirs
.Add (sExtraDir
); 
1081     icondirs
.Add(sExtraDir 
+ wxT("/icons")); 
1083     size_t nDirs 
= dirs
.GetCount(); 
1084     for ( size_t nDir 
= 0; nDir 
< nDirs
; nDir
++ ) 
1086         LoadKDELinkFilesFromDir(dirs
[nDir
], icondirs
); 
1094 // ---------------------------------------------------------------------------- 
1095 // wxFileTypeImpl (Unix) 
1096 // ---------------------------------------------------------------------------- 
1099 wxString 
wxFileTypeImpl::GetExpandedCommand(const wxString 
& verb
, const wxFileType::MessageParameters
& params
) const 
1103     while ( (i 
< m_index
.GetCount() ) && sTmp
.IsEmpty() ) 
1105             sTmp 
= m_manager
->GetCommand ( verb
, m_index
[i
] ); 
1109     return wxFileType::ExpandCommand(sTmp
, params
); 
1112 bool wxFileTypeImpl::GetIcon(wxIcon 
*icon
, 
1113                              wxString 
*iconFile 
/*= NULL */, 
1114                              int *iconIndex 
/*= NULL*/) const 
1120     while ( (i 
< m_index
.GetCount() ) && sTmp
.IsEmpty() ) 
1122         sTmp 
= m_manager
->m_aIcons
[m_index
[i
]]; 
1125     if ( sTmp
.IsEmpty () ) return FALSE
; 
1129     if (sTmp
.Right(4).MakeUpper() == _T(".XPM")) 
1132         icn 
= wxIcon(sTmp
, wxBITMAP_TYPE_ANY
); 
1137         if (iconFile
) *iconFile 
= sTmp
; 
1138         if (iconIndex
) *iconIndex 
= 0; 
1148 wxFileTypeImpl::GetMimeTypes(wxArrayString
& mimeTypes
) const 
1151     for (size_t i 
= 0; i 
< m_index
.GetCount(); i
++) 
1152         mimeTypes
.Add(m_manager
->m_aTypes
[m_index
[i
]]); 
1157 size_t wxFileTypeImpl::GetAllCommands(wxArrayString 
*verbs
, 
1158                                   wxArrayString 
*commands
, 
1159                                   const wxFileType::MessageParameters
& params
) const 
1162     wxString vrb
, cmd
, sTmp
; 
1164     wxMimeArrayString 
* sPairs
; 
1166     // verbs and commands have been cleared already in mimecmn.cpp... 
1167     // if we find no entries in the exact match, try the inexact match 
1168     for (size_t n 
= 0; ((count 
==0) && (n 
< m_index
.GetCount())); n
++) 
1170         // list of verb = command pairs for this mimetype 
1171         sPairs 
= m_manager
->m_aEntries 
[m_index
[n
]]; 
1173         for ( i 
= 0; i 
< sPairs
->GetCount () ; i
++ ) 
1175                 vrb 
= sPairs
->GetVerb(i
); 
1176                 // some gnome entries have . inside 
1177                 vrb 
= vrb
.AfterLast(wxT('.')); 
1178                 cmd 
= sPairs
->GetCmd (i
); 
1179                 if (! cmd
.IsEmpty() ) 
1181                      cmd 
= wxFileType::ExpandCommand(cmd
, params
); 
1183                      if ( vrb
.IsSameAs (wxT("open"))) 
1185                          verbs
->Insert(vrb
,0u); 
1186                          commands 
->Insert(cmd
,0u); 
1191                          commands
->Add (cmd
); 
1202 bool wxFileTypeImpl::GetExtensions(wxArrayString
& extensions
) 
1204     wxString strExtensions 
= m_manager
->GetExtension(m_index
[0]); 
1207     // one extension in the space or comma delimitid list 
1209     for ( const wxChar 
*p 
= strExtensions
; ; p
++ ) { 
1210         if ( *p 
== wxT(' ') || *p 
== wxT(',') || *p 
== wxT('\0') ) { 
1211             if ( !strExt
.IsEmpty() ) { 
1212                 extensions
.Add(strExt
); 
1215             //else: repeated spaces (shouldn't happen, but it's not that 
1216             //      important if it does happen) 
1218             if ( *p 
== wxT('\0') ) 
1221         else if ( *p 
== wxT('.') ) { 
1222             // remove the dot from extension (but only if it's the first char) 
1223             if ( !strExt
.IsEmpty() ) { 
1226             //else: no, don't append it 
1236 // set an arbitrary command, 
1237 // could adjust the code to ask confirmation if it already exists and 
1238 // overwriteprompt is TRUE, but this is currently ignored as *Associate* has 
1239 // no overwrite prompt 
1240 bool wxFileTypeImpl::SetCommand(const wxString
& cmd
, const wxString
& verb
, bool overwriteprompt 
/*= TRUE*/) 
1242     wxArrayString strExtensions
; 
1243     wxString strDesc
, strIcon 
; 
1245     wxMimeArrayString 
*entry 
= new wxMimeArrayString (); 
1246     entry
->Add(verb 
+ wxT("=")  + cmd 
+ wxT(" %s ")); 
1248     wxArrayString strTypes
; 
1249     GetMimeTypes (strTypes
); 
1250     if (strTypes
.GetCount() < 1) return FALSE
; 
1254     for (i 
= 0; i 
< strTypes
.GetCount(); i
++) 
1256         if (!m_manager
->DoAssociation (strTypes
[i
], strIcon
, entry
, strExtensions
, strDesc
)) 
1263 // ignore index on the grouds that we only have one icon in a Unix file 
1264 bool wxFileTypeImpl::SetDefaultIcon(const wxString
& strIcon 
/*= wxEmptyString*/, int /*index = 0*/) 
1266     if (strIcon
.IsEmpty()) return FALSE
; 
1267     wxArrayString strExtensions
; 
1270     wxMimeArrayString 
*entry 
= new wxMimeArrayString (); 
1272     wxArrayString strTypes
; 
1273     GetMimeTypes (strTypes
); 
1274     if (strTypes
.GetCount() < 1) return FALSE
; 
1278     for (i 
= 0; i 
< strTypes
.GetCount(); i
++) 
1280         if (!m_manager
->DoAssociation (strTypes
[i
], strIcon
, entry
, strExtensions
, strDesc
)) 
1286 // ---------------------------------------------------------------------------- 
1287 // wxMimeTypesManagerImpl (Unix) 
1288 // ---------------------------------------------------------------------------- 
1291 wxMimeTypesManagerImpl::wxMimeTypesManagerImpl() 
1293     m_initialized 
= FALSE
; 
1294     m_mailcapStylesInited 
= 0; 
1297 // read system and user mailcaps and other files 
1298 void wxMimeTypesManagerImpl::Initialize(int mailcapStyles
, 
1299                                         const wxString
& sExtraDir
) 
1301     // read mimecap amd mime.types 
1302     if ( (mailcapStyles 
& wxMAILCAP_NETSCAPE
) || 
1303          (mailcapStyles 
& wxMAILCAP_STANDARD
) ) 
1304         GetMimeInfo(sExtraDir
); 
1306     // read GNOME tables 
1307     if ( mailcapStyles 
& wxMAILCAP_GNOME
) 
1308         GetGnomeMimeInfo(sExtraDir
); 
1311     if ( mailcapStyles 
& wxMAILCAP_KDE
) 
1312         GetKDEMimeInfo(sExtraDir
); 
1314     m_mailcapStylesInited 
|= mailcapStyles
; 
1317 // clear data so you can read another group of WM files 
1318 void wxMimeTypesManagerImpl::ClearData() 
1322     m_aExtensions
.Clear (); 
1323     m_aDescriptions
.Clear (); 
1325     size_t cnt 
= m_aTypes
.GetCount(); 
1326     for (size_t i 
= 0; i 
< cnt
; i
++) 
1328         m_aEntries
[i
]->Clear (); 
1330     m_aEntries
.Clear (); 
1331     m_mailcapStylesInited 
= 0; 
1334 wxMimeTypesManagerImpl::~wxMimeTypesManagerImpl() 
1336     ClearData(); // do we need to delete the ArrayStrings too to avoid a leak 
1338 //    delete m_aEntries //fix a leak here ?; 
1342 void wxMimeTypesManagerImpl::GetMimeInfo (const wxString
& sExtraDir
) 
1344     // read this for netscape or Metamail formats 
1346     // directories where we look for mailcap and mime.types by default 
1347     // used by netscape and pine and other mailers, using 2 different formats! 
1349     // (taken from metamail(1) sources) 
1351     // although RFC 1524 specifies the search path of 
1352     // /etc/:/usr/etc:/usr/local/etc only, it doesn't hurt to search in more 
1353     // places - OTOH, the RFC also says that this path can be changed with 
1354     // MAILCAPS environment variable (containing the colon separated full 
1355     // filenames to try) which is not done yet (TODO?) 
1357     wxString strHome 
= wxGetenv(wxT("HOME")); 
1360     dirs
.Add ( strHome 
+ wxT("/.") ); 
1361     dirs
.Add ( wxT("/etc/") ); 
1362     dirs
.Add ( wxT("/usr/etc/") ); 
1363     dirs
.Add ( wxT("/usr/local/etc/") ); 
1364     dirs
.Add ( wxT("/etc/mail/") ); 
1365     dirs
.Add ( wxT("/usr/public/lib/") ); 
1366     if (!sExtraDir
.IsEmpty()) dirs
.Add ( sExtraDir 
+ wxT("/") ); 
1368     size_t nDirs 
= dirs
.GetCount(); 
1369     for ( size_t nDir 
= 0; nDir 
< nDirs
; nDir
++ ) 
1371         wxString file 
= dirs
[nDir
] + wxT("mailcap"); 
1372         if ( wxFile::Exists(file
) ) { 
1376         file 
= dirs
[nDir
] + wxT("mime.types"); 
1377         if ( wxFile::Exists(file
) ) { 
1378             ReadMimeTypes(file
); 
1384 bool wxMimeTypesManagerImpl::WriteToMimeTypes (int index
, bool delete_index
) 
1386     // check we have the right manager 
1387     if (! ( m_mailcapStylesInited 
& wxMAILCAP_STANDARD
) ) 
1391     wxString strHome 
= wxGetenv(wxT("HOME")); 
1393     // and now the users mailcap 
1394     wxString strUserMailcap 
= strHome 
+ wxT("/.mime.types"); 
1396     wxMimeTextFile file
; 
1397     if ( wxFile::Exists(strUserMailcap
) ) 
1399         bTemp 
=  file
.Open(strUserMailcap
); 
1403         if (delete_index
) return FALSE
; 
1404         bTemp 
= file
.Create(strUserMailcap
); 
1409         // test for netscape's header and return FALSE if its found 
1410         nIndex 
= file
.pIndexOf (wxT("#--Netscape")); 
1411         if (nIndex 
!= wxNOT_FOUND
) 
1413             wxASSERT_MSG(FALSE
,wxT("Error in .mime.types \nTrying to mix Netscape and Metamail formats\nFile not modiifed")); 
1416         // write it in alternative format 
1417         // get rid of unwanted entries 
1418         wxString strType 
= m_aTypes
[index
]; 
1419         nIndex 
= file
.pIndexOf (strType
); 
1420         // get rid of all the unwanted entries... 
1421         if (nIndex 
!= wxNOT_FOUND
) file
.CommentLine (nIndex
); 
1425             // add the new entries in 
1426             wxString sTmp 
= strType
.Append (wxT(' '), 40-strType
.Len() ); 
1427             sTmp 
= sTmp 
+ m_aExtensions
[index
]; 
1428             file
.AddLine (sTmp
); 
1432         bTemp 
= file
.Write (); 
1438 bool wxMimeTypesManagerImpl::WriteToNSMimeTypes (int index
, bool delete_index
) 
1440     //check we have the right managers 
1441     if (! ( m_mailcapStylesInited 
& wxMAILCAP_NETSCAPE
) ) 
1445     wxString strHome 
= wxGetenv(wxT("HOME")); 
1447     // and now the users mailcap 
1448     wxString strUserMailcap 
= strHome 
+ wxT("/.mime.types"); 
1450     wxMimeTextFile file
; 
1451     if ( wxFile::Exists(strUserMailcap
) ) 
1453         bTemp 
=  file
.Open(strUserMailcap
); 
1457         if (delete_index
) return FALSE
; 
1458         bTemp 
= file
.Create(strUserMailcap
); 
1463         // write it in the format that Netscape uses 
1465         // test for netscape's header and insert if required... 
1466         // this is a comment so use TRUE 
1467         nIndex 
= file
.pIndexOf (wxT("#--Netscape"), TRUE
); 
1468         if (nIndex 
== wxNOT_FOUND
) 
1470             // either empty file or metamail format 
1471             // at present we can't cope with mixed formats, so exit to preseve 
1472             // metamail entreies 
1473             if (file
.GetLineCount () > 0) 
1475                 wxASSERT_MSG(FALSE
, wxT(".mime.types File not in Netscape format\nNo entries written to\n.mime.types or to .mailcap")); 
1478             file
.InsertLine (wxT( "#--Netscape Communications Corporation MIME Information" ), 0); 
1482         wxString strType 
= wxT("type=") + m_aTypes
[index
]; 
1483         nIndex 
= file
.pIndexOf (strType
); 
1484         // get rid of all the unwanted entries... 
1485         if (nIndex 
!= wxNOT_FOUND
) 
1487             wxString sOld 
= file
[nIndex
]; 
1488             while ( (sOld
.Contains(wxT("\\"))) && (nIndex 
< (int) file
.GetLineCount()) ) 
1490                 file
.CommentLine(nIndex
); 
1491                 sOld 
= file
[nIndex
]; 
1492                 wxLogTrace(TRACE_MIME
, wxT("--- Deleting from mime.types line '%d %s' ---"), nIndex
, sOld
.c_str()); 
1495             if (nIndex 
< (int) file
.GetLineCount()) file
.CommentLine (nIndex
); 
1497         else nIndex 
= (int) file
.GetLineCount(); 
1499         wxString sTmp 
= strType 
+ wxT(" \\"); 
1500         if (!delete_index
) file
.InsertLine (sTmp
, nIndex
); 
1501         if ( ! m_aDescriptions
.Item(index
).IsEmpty() ) 
1503             sTmp 
=     wxT("desc=\"") + m_aDescriptions
[index
]+ wxT("\" \\") ; //.trim ?? 
1507                 file
.InsertLine (sTmp
, nIndex
); 
1510         wxString sExts 
=  m_aExtensions
.Item(index
); 
1511         sTmp 
=     wxT("exts=\"") + sExts
.Trim(FALSE
).Trim() + wxT("\""); 
1515             file
.InsertLine (sTmp
, nIndex
); 
1518         bTemp 
= file
.Write (); 
1525 bool wxMimeTypesManagerImpl::WriteToMailCap (int index
, bool delete_index
) 
1527     //check we have the right managers 
1528     if ( !( ( m_mailcapStylesInited 
& wxMAILCAP_NETSCAPE
) || 
1529             ( m_mailcapStylesInited 
& wxMAILCAP_STANDARD
) ) ) 
1533     wxString strHome 
= wxGetenv(wxT("HOME")); 
1535     // and now the users mailcap 
1536     wxString strUserMailcap 
= strHome 
+ wxT("/.mailcap"); 
1538     wxMimeTextFile file
; 
1539     if ( wxFile::Exists(strUserMailcap
) ) 
1541         bTemp 
=  file
.Open(strUserMailcap
); 
1545         if (delete_index
) return FALSE
; 
1546         bTemp 
= file
.Create(strUserMailcap
); 
1550         // now got a file we can write to .... 
1551         wxMimeArrayString 
* entries 
= m_aEntries
[index
]; 
1552         size_t iOpen 
= entries
->pIndexOf(wxT("open")); 
1553         wxString sCmd 
= entries
->GetCmd(iOpen
); 
1556         sTmp 
= m_aTypes
[index
]; 
1558         int nIndex 
= file
.pIndexOf(sTmp
); 
1559         // get rid of all the unwanted entries... 
1560         if (nIndex 
== wxNOT_FOUND
) 
1562             nIndex 
= (int) file
.GetLineCount(); 
1566             sOld 
= file
[nIndex
]; 
1567             wxLogTrace(TRACE_MIME
, wxT("--- Deleting from mailcap line '%d' ---"), nIndex
); 
1569             while ( (sOld
.Contains(wxT("\\"))) && (nIndex 
< (int) file
.GetLineCount()) ) 
1571                 file
.CommentLine(nIndex
); 
1572                 if (nIndex 
< (int) file
.GetLineCount()) sOld 
= sOld 
+ file
[nIndex
]; 
1574             if (nIndex 
< (int) file
.GetLineCount()) file
.CommentLine (nIndex
); 
1577         sTmp 
= sTmp 
+ wxT(";") + sCmd 
; //includes wxT(" %s "); 
1579         // write it in the format that Netscape uses (default) 
1580         if (! ( m_mailcapStylesInited 
& wxMAILCAP_STANDARD  
) ) 
1582             if (! delete_index
) file
.InsertLine (sTmp
, nIndex
); 
1586         // write extended format 
1589             // todo FIX this code; 
1591             // sOld holds all the entries, but our data store only has some 
1592             // eg test= is not stored 
1594             // so far we have written the mimetype and command out 
1595             wxStringTokenizer 
sT (sOld
, wxT(";\\")); 
1596             if (sT
.CountTokens () > 2) 
1598                 // first one mimetype; second one command, rest unknown... 
1600                 s 
= sT
.GetNextToken(); 
1601                 s 
= sT
.GetNextToken(); 
1604                 s 
= sT
.GetNextToken(); 
1605                 while ( ! s
.IsEmpty() ) 
1607                     bool bKnownToken 
= FALSE
; 
1608                     if (s
.Contains(wxT("description="))) bKnownToken 
= TRUE
; 
1609                     if (s
.Contains(wxT("x11-bitmap="))) bKnownToken 
= TRUE
; 
1611                     for (i
=0; i 
< entries
->GetCount(); i
++) 
1613                         if (s
.Contains(entries
->GetVerb(i
))) bKnownToken 
= TRUE
; 
1617                         sTmp 
= sTmp 
+ wxT("; \\"); 
1618                         file
.InsertLine (sTmp
, nIndex
); 
1621                     s 
= sT
.GetNextToken (); 
1626             if (! m_aDescriptions
[index
].IsEmpty() ) 
1628                 sTmp 
= sTmp 
+ wxT("; \\"); 
1629                 file
.InsertLine (sTmp
, nIndex
); 
1631                 sTmp 
= wxT("       description=\"") + m_aDescriptions
[index
] + wxT("\""); 
1634             if (! m_aIcons
[index
].IsEmpty() ) 
1636                 sTmp 
= sTmp 
+ wxT("; \\"); 
1637                 file
.InsertLine (sTmp
, nIndex
); 
1639                 sTmp 
= wxT("       x11-bitmap=\"") + m_aIcons
[index
] + wxT("\""); 
1641             if ( entries
->GetCount() > 1 ) 
1645                 for (i
=0; i 
< entries
->GetCount(); i
++) 
1648                         sTmp 
= sTmp 
+ wxT("; \\"); 
1649                         file
.InsertLine (sTmp
, nIndex
); 
1651                         sTmp 
= wxT("       ") + entries
->Item(i
); 
1655             file
.InsertLine (sTmp
, nIndex
); 
1659         bTemp 
= file
.Write (); 
1666 wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo
& ftInfo
) 
1670     wxString strType 
= ftInfo
.GetMimeType (); 
1671     wxString strDesc 
= ftInfo
.GetDescription (); 
1672     wxString strIcon 
= ftInfo
.GetIconFile (); 
1674     wxMimeArrayString 
*entry 
= new wxMimeArrayString (); 
1676     if ( ! ftInfo
.GetOpenCommand().IsEmpty()) 
1677         entry
->Add(wxT("open=")  + ftInfo
.GetOpenCommand  () + wxT(" %s ")); 
1678     if ( ! ftInfo
.GetPrintCommand  ().IsEmpty()) 
1679         entry
->Add(wxT("print=") + ftInfo
.GetPrintCommand () + wxT(" %s ")); 
1681     // now find where these extensions are in the data store and remove them 
1682     wxArrayString sA_Exts 
= ftInfo
.GetExtensions (); 
1683     wxString sExt
, sExtStore
; 
1685     for (i
=0; i 
< sA_Exts
.GetCount(); i
++) 
1687         sExt 
= sA_Exts
.Item(i
); 
1688         //clean up to just a space before and after 
1689         sExt
.Trim().Trim(FALSE
); 
1690         sExt 
= wxT(' ') + sExt 
+ wxT(' '); 
1691         for (nIndex 
= 0; nIndex 
< m_aExtensions
.GetCount(); nIndex 
++) 
1693             sExtStore 
= m_aExtensions
.Item(nIndex
); 
1694             if (sExtStore
.Replace(sExt
, wxT(" ") ) > 0) m_aExtensions
.Item(nIndex
) = sExtStore 
; 
1699     if ( !DoAssociation (strType
, strIcon
, entry
, sA_Exts
, strDesc
) ) 
1702     return GetFileTypeFromMimeType(strType
); 
1706 bool wxMimeTypesManagerImpl::DoAssociation(const wxString
& strType
, 
1707                                            const wxString
& strIcon
, 
1708                                            wxMimeArrayString 
*entry
, 
1709                                            const wxArrayString
& strExtensions
, 
1710                                            const wxString
& strDesc
) 
1712     int nIndex 
= AddToMimeData(strType
, strIcon
, entry
, strExtensions
, strDesc
, TRUE
); 
1714     if ( nIndex 
== wxNOT_FOUND 
) 
1717     return WriteMimeInfo (nIndex
, FALSE
); 
1720 bool wxMimeTypesManagerImpl::WriteMimeInfo(int nIndex
, bool delete_mime 
) 
1724     if ( m_mailcapStylesInited 
& wxMAILCAP_STANDARD 
) 
1726         // write in metamail  format; 
1727         if (WriteToMimeTypes (nIndex
, delete_mime
) ) 
1728             if ( WriteToMailCap   (nIndex
, delete_mime
) ) 
1731     if ( m_mailcapStylesInited 
& wxMAILCAP_NETSCAPE 
) 
1733         // write in netsacpe format; 
1734         if (WriteToNSMimeTypes (nIndex
, delete_mime
) ) 
1735             if ( WriteToMailCap   (nIndex
, delete_mime
) ) 
1738     if (m_mailcapStylesInited 
& wxMAILCAP_GNOME
) 
1740         // write in Gnome format; 
1741         if (WriteGnomeMimeFile (nIndex
, delete_mime
) ) 
1742             if (WriteGnomeKeyFile   (nIndex
, delete_mime
) ) 
1745     if (m_mailcapStylesInited 
& wxMAILCAP_KDE
) 
1747         // write in KDE format; 
1748         if (WriteKDEMimeFile (nIndex
, delete_mime
) ) 
1755 int wxMimeTypesManagerImpl::AddToMimeData(const wxString
& strType
, 
1756                                           const wxString
& strIcon
, 
1757                                           wxMimeArrayString 
*entry
, 
1758                                           const wxArrayString
& strExtensions
, 
1759                                           const wxString
& strDesc
, 
1760                                           bool ReplaceExisting
) 
1764     wxLogTrace(TRACE_MIME
, wxT("In Add to Mime data '%s' with %d entries and %d exts ---"), 
1765             strType
.c_str(), entry
->GetCount(), strExtensions
.GetCount() ); 
1767     // ensure mimetype is always lower case 
1768     wxString mimeType 
= strType
; 
1769     mimeType
.MakeLower(); 
1770     int nIndex 
= m_aTypes
.Index(mimeType
); 
1771     if ( nIndex 
== wxNOT_FOUND 
) 
1774         m_aTypes
.Add(mimeType
); 
1775         m_aIcons
.Add(strIcon
); 
1776         m_aEntries
.Add(entry
); 
1778         // change nIndex so we can add to the correct line 
1779         nIndex 
= m_aExtensions
.Add(wxT(' ')); 
1780         for (i 
= 0; i 
< strExtensions
.GetCount(); i 
++) 
1782             if (! m_aExtensions
.Item(nIndex
).Contains(wxT(' ') + strExtensions
.Item(i
) + wxT(' '))) 
1783                 m_aExtensions
.Item(nIndex
) +=  strExtensions
.Item(i
) + wxT(' '); 
1785         m_aDescriptions
.Add(strDesc
); 
1790         // nIndex has the existing data 
1791         // always add the extensions to this mimetype 
1793         for (i 
= 0; i 
< strExtensions
.GetCount(); i 
++) 
1795             if (! m_aExtensions
.Item(nIndex
).Contains(wxT(' ') + strExtensions
.Item(i
) + wxT(' '))) 
1796                 m_aExtensions
.Item(nIndex
) +=  strExtensions
.Item(i
) + wxT(' '); 
1798         if (ReplaceExisting
) 
1800             // if new description change it 
1801             if ( ! strDesc
.IsEmpty()) 
1802                 m_aDescriptions
[nIndex
] = strDesc
; 
1804             // if new icon change it 
1805             if ( ! strIcon
.IsEmpty()) 
1806                 m_aIcons
[nIndex
] = strIcon
; 
1808             wxMimeArrayString 
*entryOld  
=  m_aEntries
[nIndex
]; 
1809             // replace any matching entries... 
1810             for (i
=0; i 
< entry
->GetCount(); i
++) 
1811                 entryOld
->ReplaceOrAddLineCmd (entry
->GetVerb(i
), 
1812                         entry
->GetCmd (i
) ); 
1816             // add data we don't already have ... 
1817             // if new description add only if none 
1818             if ( ! strDesc
.IsEmpty() && m_aDescriptions
.Item(i
).IsEmpty() ) 
1819                 m_aDescriptions
[nIndex
] = strDesc
; 
1821             // if new icon and no existing icon 
1822             if ( ! strIcon
.IsEmpty() && m_aIcons
.Item(i
). IsEmpty () ) 
1823                 m_aIcons
[nIndex
] = strIcon
; 
1825             wxMimeArrayString 
*entryOld  
=  m_aEntries
[nIndex
]; 
1826             // add any new entries... 
1827             for (i
=0; i 
< entry
->GetCount(); i
++) 
1829                 wxString sVerb 
= entry
->GetVerb(i
); 
1830                 if ( entryOld
->pIndexOf ( sVerb 
) == (size_t) wxNOT_FOUND 
) 
1831                     entryOld
->Add (entry
->Item(i
)); 
1836     // check data integrity 
1837     wxASSERT( m_aTypes
.Count() == m_aEntries
.Count() && 
1838             m_aTypes
.Count() == m_aExtensions
.Count() && 
1839             m_aTypes
.Count() == m_aIcons
.Count() && 
1840             m_aTypes
.Count() == m_aDescriptions
.Count() ); 
1847 wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString
& ext
) 
1854     wxFileType 
*fileType 
= NULL
; 
1855     size_t count 
= m_aExtensions
.GetCount(); 
1856     for ( size_t n 
= 0; n 
< count
; n
++ ) 
1858         wxString extensions 
= m_aExtensions
[n
]; 
1859         while ( !extensions
.IsEmpty() ) { 
1860             wxString field 
= extensions
.BeforeFirst(wxT(' ')); 
1861             extensions 
= extensions
.AfterFirst(wxT(' ')); 
1863             // consider extensions as not being case-sensitive 
1864             if ( field
.IsSameAs(ext
, FALSE 
/* no case */) ) 
1867                 if (fileType 
== NULL
) fileType 
= new wxFileType
; 
1868                 fileType
->m_impl
->Init(this, n
); 
1869                      // adds this mime type to _list_ of mime types with this extension 
1878 wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString
& mimeType
) 
1882     wxFileType 
* fileType 
= NULL
; 
1883     // mime types are not case-sensitive 
1884     wxString 
mimetype(mimeType
); 
1885     mimetype
.MakeLower(); 
1887     // first look for an exact match 
1888     int index 
= m_aTypes
.Index(mimetype
); 
1889     if ( index 
!= wxNOT_FOUND 
) 
1891         fileType 
= new wxFileType
; 
1892         fileType
->m_impl
->Init(this, index
); 
1895     // then try to find "text/*" as match for "text/plain" (for example) 
1896     // NB: if mimeType doesn't contain '/' at all, BeforeFirst() will return 
1897     //     the whole string - ok. 
1899     index 
= wxNOT_FOUND
; 
1900     wxString strCategory 
= mimetype
.BeforeFirst(wxT('/')); 
1902     size_t nCount 
= m_aTypes
.Count(); 
1903     for ( size_t n 
= 0; n 
< nCount
; n
++ ) { 
1904         if ( (m_aTypes
[n
].BeforeFirst(wxT('/')) == strCategory 
) && 
1905                 m_aTypes
[n
].AfterFirst(wxT('/')) == wxT("*") ) { 
1912     if ( index 
!= wxNOT_FOUND 
) 
1914         fileType 
= new wxFileType
; 
1915         fileType
->m_impl
->Init(this, index
); 
1921 wxString 
wxMimeTypesManagerImpl::GetCommand(const wxString 
& verb
, size_t nIndex
) const 
1923     wxString command
, testcmd
, sV
, sTmp
; 
1924     sV 
= verb 
+ wxT("="); 
1925     // list of verb = command pairs for this mimetype 
1926     wxMimeArrayString 
* sPairs 
= m_aEntries 
[nIndex
]; 
1929     for ( i 
= 0; i 
< sPairs
->GetCount () ; i
++ ) 
1931         sTmp 
= sPairs
->Item (i
); 
1932         if ( sTmp
.Contains(sV
) ) command 
= sTmp
.AfterFirst(wxT('=')); 
1937 void wxMimeTypesManagerImpl::AddFallback(const wxFileTypeInfo
& filetype
) 
1941     wxString extensions
; 
1942     const wxArrayString
& exts 
= filetype
.GetExtensions(); 
1943     size_t nExts 
= exts
.GetCount(); 
1944     for ( size_t nExt 
= 0; nExt 
< nExts
; nExt
++ ) { 
1946             extensions 
+= wxT(' '); 
1948         extensions 
+= exts
[nExt
]; 
1951     AddMimeTypeInfo(filetype
.GetMimeType(), 
1953                     filetype
.GetDescription()); 
1955     AddMailcapInfo(filetype
.GetMimeType(), 
1956                    filetype
.GetOpenCommand(), 
1957                    filetype
.GetPrintCommand(), 
1959                    filetype
.GetDescription()); 
1962 void wxMimeTypesManagerImpl::AddMimeTypeInfo(const wxString
& strMimeType
, 
1963                                              const wxString
& strExtensions
, 
1964                                              const wxString
& strDesc
) 
1966     // reading mailcap may find image/* , while 
1967     // reading mime.types finds image/gif and no match is made 
1968     // this means all the get functions don't work  fix this 
1970     wxString sTmp 
= strExtensions
; 
1971     wxMimeArrayString 
* entry 
= new wxMimeArrayString () ; 
1973     wxArrayString sExts
; 
1974     sTmp
.Trim().Trim(FALSE
); 
1976     while (!sTmp
.IsEmpty()) 
1978         sExts
.Add (sTmp
.AfterLast(wxT(' '))); 
1979         sTmp 
= sTmp
.BeforeLast(wxT(' ')); 
1982     AddToMimeData (strMimeType
, strIcon
, entry
, sExts
, strDesc
, (bool)TRUE
); 
1985 void wxMimeTypesManagerImpl::AddMailcapInfo(const wxString
& strType
, 
1986                                             const wxString
& strOpenCmd
, 
1987                                             const wxString
& strPrintCmd
, 
1988                                             const wxString
& strTest
, 
1989                                             const wxString
& strDesc
) 
1993     wxMimeArrayString 
*entry 
= new wxMimeArrayString
; 
1994     entry
->Add(wxT("open=")  + strOpenCmd
); 
1995     entry
->Add(wxT("print=") + strPrintCmd
); 
1996     entry
->Add(wxT("test=")  + strTest
); 
1999     wxArrayString strExtensions
; 
2001     AddToMimeData (strType
, strIcon
, entry
, strExtensions
, strDesc
, TRUE
); 
2005 bool wxMimeTypesManagerImpl::ReadMimeTypes(const wxString
& strFileName
) 
2007     wxLogTrace(TRACE_MIME
, wxT("--- Parsing mime.types file '%s' ---"), 
2008                strFileName
.c_str()); 
2010     wxTextFile 
file(strFileName
); 
2014     // the information we extract 
2015     wxString strMimeType
, strDesc
, strExtensions
; 
2017     size_t nLineCount 
= file
.GetLineCount(); 
2018     const wxChar 
*pc 
= NULL
; 
2019     for ( size_t nLine 
= 0; nLine 
< nLineCount
; nLine
++ ) { 
2021             // now we're at the start of the line 
2022             pc 
= file
[nLine
].c_str(); 
2025             // we didn't finish with the previous line yet 
2030         while ( wxIsspace(*pc
) ) 
2033         // comment or blank line? 
2034         if ( *pc 
== wxT('#') || !*pc 
) { 
2035             // skip the whole line 
2040         // detect file format 
2041         const wxChar 
*pEqualSign 
= wxStrchr(pc
, wxT('=')); 
2042         if ( pEqualSign 
== NULL 
) { 
2046             // first field is mime type 
2047             for ( strMimeType
.Empty(); !wxIsspace(*pc
) && *pc 
!= wxT('\0'); pc
++ ) { 
2052             while ( wxIsspace(*pc
) ) 
2055             // take all the rest of the string 
2058             // no description... 
2065             // the string on the left of '=' is the field name 
2066             wxString 
strLHS(pc
, pEqualSign 
- pc
); 
2069             for ( pc 
= pEqualSign 
+ 1; wxIsspace(*pc
); pc
++ ) 
2073             if ( *pc 
== wxT('"') ) { 
2074                 // the string is quoted and ends at the matching quote 
2075                 pEnd 
= wxStrchr(++pc
, wxT('"')); 
2076                 if ( pEnd 
== NULL 
) { 
2077                     wxLogWarning(_("Mime.types file %s, line %d: unterminated " 
2079                                  strFileName
.c_str(), nLine 
+ 1); 
2083                 // unquoted string ends at the first space or at the end of 
2085                 for ( pEnd 
= pc
; *pEnd 
&& !wxIsspace(*pEnd
); pEnd
++ ) 
2089             // now we have the RHS (field value) 
2090             wxString 
strRHS(pc
, pEnd 
- pc
); 
2092             // check what follows this entry 
2093             if ( *pEnd 
== wxT('"') ) { 
2098             for ( pc 
= pEnd
; wxIsspace(*pc
); pc
++ ) 
2101             // if there is something left, it may be either a '\\' to continue 
2102             // the line or the next field of the same entry 
2103             bool entryEnded 
= *pc 
== wxT('\0'), 
2104                  nextFieldOnSameLine 
= FALSE
; 
2105             if ( !entryEnded 
) { 
2106                 nextFieldOnSameLine 
= ((*pc 
!= wxT('\\')) || (pc
[1] != wxT('\0'))); 
2109             // now see what we got 
2110             if ( strLHS 
== wxT("type") ) { 
2111                 strMimeType 
= strRHS
; 
2113             else if ( strLHS 
== wxT("desc") ) { 
2116             else if ( strLHS 
== wxT("exts") ) { 
2117                 strExtensions 
= strRHS
; 
2120                 // this one is simply ignored: it usually refers to Netscape 
2121                 // built in icons which are useless for us anyhow 
2122                 if ( strLHS 
!= _T("icon") ) 
2124                     wxLogWarning(_("Unknown field in file %s, line %d: '%s'."), 
2125                                  strFileName
.c_str(), nLine 
+ 1, strLHS
.c_str()); 
2129             if ( !entryEnded 
) { 
2130                 if ( !nextFieldOnSameLine 
) 
2132                 //else: don't reset it 
2134                 // as we don't reset strMimeType, the next field in this entry 
2135                 // will be interpreted correctly. 
2141         // depending on the format (Mosaic or Netscape) either space or comma 
2142         // is used to separate the extensions 
2143         strExtensions
.Replace(wxT(","), wxT(" ")); 
2145         // also deal with the leading dot 
2146         if ( !strExtensions
.IsEmpty() && strExtensions
[0u] == wxT('.') ) 
2148             strExtensions
.erase(0, 1); 
2151     wxLogTrace(TRACE_MIME
, wxT("--- Found Mimetype '%s' ---"), 
2152                strMimeType
.c_str()); 
2154         AddMimeTypeInfo(strMimeType
, strExtensions
, strDesc
); 
2156         // finished with this line 
2163 bool wxMimeTypesManagerImpl::ReadMailcap(const wxString
& strFileName
, 
2166 //    wxLog::AddTraceMask (TRACE_MIME); 
2167     wxLogTrace(TRACE_MIME
, wxT("--- Parsing mailcap file '%s' ---"), 
2168                strFileName
.c_str()); 
2170     wxTextFile 
file(strFileName
); 
2174     // see the comments near the end of function for the reason we need these 
2175     // variables (search for the next occurence of them) 
2176         // indices of MIME types (in m_aTypes) we already found in this file 
2177     wxArrayInt aEntryIndices
; 
2178         // aLastIndices[n] is the index of last element in 
2179         // m_aEntries[aEntryIndices[n]] from this file 
2180   //  wxArrayInt aLastIndices; 
2182     size_t nLineCount 
= file
.GetLineCount(); 
2183     for ( size_t nLine 
= 0; nLine 
< nLineCount
; nLine
++ ) { 
2184         // now we're at the start of the line 
2185         const wxChar 
*pc 
= file
[nLine
].c_str(); 
2188         while ( wxIsspace(*pc
) ) 
2191         // comment or empty string? 
2192         if ( *pc 
== wxT('#') || *pc 
== wxT('\0') ) 
2197         // what field are we currently in? The first 2 are fixed and there may 
2198         // be an arbitrary number of other fields -- currently, we are not 
2199         // interested in any of them, but we should parse them as well... 
2205         } currentToken 
= Field_Type
; 
2207         // the flags and field values on the current line 
2208         bool needsterminal 
= FALSE
, 
2209              copiousoutput 
= FALSE
; 
2210         wxMimeArrayString 
*entry 
= NULL
; // suppress compiler warning 
2217                  curField
; // accumulator 
2219         bool test_passed 
= TRUE
; 
2223                     // interpret the next character literally (notice that 
2224                     // backslash can be used for line continuation) 
2225                     if ( *++pc 
== wxT('\0') ) { 
2226                         // fetch the next line if there is one 
2227                         if ( nLine 
== nLineCount 
- 1 ) { 
2228                             // something is wrong, bail out 
2231                             wxLogDebug(wxT("Mailcap file %s, line %d: " 
2232                                            "'\\' on the end of the last line " 
2234                                        strFileName
.c_str(), 
2238                             // pass to the beginning of the next line 
2239                             pc 
= file
[++nLine
].c_str(); 
2241                             // skip pc++ at the end of the loop 
2246                         // just a normal character 
2252                     cont 
= FALSE
;   // end of line reached, exit the loop 
2257                     // store this field and start looking for the next one 
2259                     // trim whitespaces from both sides 
2260                     curField
.Trim(TRUE
).Trim(FALSE
); 
2262                     switch ( currentToken 
) { 
2265                             if ( strType
.empty() ) { 
2266                                 // I don't think that this is a valid mailcap 
2267                                 // entry, but try to interpret it somehow 
2271                             if ( strType
.Find(wxT('/')) == wxNOT_FOUND 
) { 
2272                                 // we interpret "type" as "type/*" 
2273                                 strType 
+= wxT("/*"); 
2276                             currentToken 
= Field_OpenCmd
; 
2280                             strOpenCmd 
= curField
; 
2281                             entry 
= new wxMimeArrayString (); 
2282                             entry
->Add(wxT("open=")  + strOpenCmd
); 
2284                             currentToken 
= Field_Other
; 
2288                             if ( !curField
.empty() ) { 
2289                                 // "good" mailcap entry? 
2292                                 if ( IsKnownUnimportantField(curField
) ) ok 
= FALSE
; 
2294                                 // is this something of the form foo=bar? 
2295                                 const wxChar 
*pEq 
= wxStrchr(curField
, wxT('=')); 
2300                                     wxString lhs 
= curField
.BeforeFirst(wxT('=')), 
2301                                              rhs 
= curField
.AfterFirst(wxT('=')); 
2303                                     lhs
.Trim(TRUE
);     // from right 
2304                                     rhs
.Trim(FALSE
);    // from left 
2306                                         // it might be quoted 
2307                                     if ( rhs
[0u] == wxT('"') && rhs
.Last() == wxT('"') ) 
2309                                         wxString sTmp 
= wxString(rhs
.c_str() + 1, rhs
.Len() - 2); 
2312                                     bool verbfound 
= TRUE
; 
2313                                     if ( lhs
.Contains (wxT("test"))) 
2315                                         if ( ! rhs
.IsEmpty() ) 
2317                                             if ( wxSystem(rhs
) == 0 ) { 
2320                                                 wxLogTrace(TRACE_MIME
, 
2321                                                            wxT("Test '%s' for mime type '%s' succeeded."), 
2322                                                            rhs
.c_str(), strType
.c_str()); 
2326                                                 test_passed 
= FALSE
; 
2327                                                 wxLogTrace(TRACE_MIME
, 
2328                                                            wxT("Test '%s' for mime type '%s' failed."), 
2329                                                            rhs
.c_str(), strType
.c_str()); 
2334                                     if ( lhs
.Contains (wxT("desc"))) 
2339                                     if ( lhs
.Contains (wxT("x11-bitmap"))) 
2344                                     if ( lhs
.Contains (wxT("notes"))) 
2349                                     if (verbfound
) entry
->Add ( lhs 
+ wxT('=') + rhs 
); 
2354                                     // no, it's a simple flag 
2355                                         if ( curField 
== wxT("needsterminal") ) { 
2356                                         needsterminal 
= TRUE
; 
2359                                         if ( curField 
== wxT("copiousoutput")) { 
2360                                         // copiousoutput impies that the 
2361                                         // viewer is a console program 
2368                                         // don't flood the user with error 
2369                                         // messages if we don't understand 
2370                                         // something in his mailcap, but give 
2371                                         // them in debug mode because this might 
2372                                         // be useful for the programmer 
2375                                           wxT("Mailcap file %s, line %d: " 
2376                                               "unknown field '%s' for the " 
2377                                               "MIME type '%s' ignored."), 
2378                                               strFileName
.c_str(), 
2395                             // it already has this value 
2396                             //currentToken = Field_Other; 
2400                             wxFAIL_MSG(wxT("unknown field type in mailcap")); 
2403                     // next token starts immediately after ';' 
2411             // continue in the same line 
2415         // check that we really read something reasonable 
2416         if ( currentToken 
== Field_Type 
|| currentToken 
== Field_OpenCmd 
) { 
2417             wxLogWarning(_("Mailcap file %s, line %d: incomplete entry " 
2419                          strFileName
.c_str(), nLine 
+ 1); 
2422             // support for flags: 
2423             //  1. create an xterm for 'needsterminal' 
2424             //  2. append "| $PAGER" for 'copiousoutput' 
2426             // Note that the RFC says that having both needsterminal and 
2427             // copiousoutput is probably a mistake, so it seems that running 
2428             // programs with copiousoutput inside an xterm as it is done now 
2429             // is a bad idea (FIXME) 
2430             if ( copiousoutput 
) 
2432                 const wxChar 
*p 
= wxGetenv(_T("PAGER")); 
2433                 strOpenCmd 
<< _T(" | ") << (p 
? p 
: _T("more")); 
2434                 wxLogTrace(TRACE_MIME
, wxT("Replacing .(for pager)...") + entry
->Item(0u) + wxT("with") + strOpenCmd 
); 
2436                 entry
->ReplaceOrAddLineCmd (wxString(wxT("open")), strOpenCmd 
); 
2439             if ( needsterminal 
) 
2441                 strOpenCmd
.Printf(_T("xterm -e sh -c '%s'"), strOpenCmd
.c_str()); 
2442                 wxLogTrace(TRACE_MIME
, wxT("Replacing .(for needs term)...") + entry
->Item(0u) + wxT("with") + strOpenCmd 
); 
2444                 entry
->ReplaceOrAddLineCmd (wxString(wxT("open")), strOpenCmd 
); 
2447             // NB: because of complications below (we must get entries priority 
2448             //     right), we can't use AddMailcapInfo() here, unfortunately. 
2451             strType
.MakeLower(); 
2452                 bool overwrite 
= TRUE
; 
2458                     int nIndex 
= m_aTypes
.Index(strType
); 
2459                     entryIndex 
= aEntryIndices
.Index(nIndex
); 
2460                     if ( entryIndex 
== wxNOT_FOUND 
) 
2463                         // first time in this file, so replace the icons, entries 
2464                         // and description (no extensions to replace so ignore these 
2466                         aEntryIndices
.Add(nIndex
); 
2467                         //aLastIndices.Add(0); 
2470                         // not the first time in _this_ file 
2471                         // so we don't want to overwrite 
2472                         // existing entries,but want to add to them 
2473                         // so we don't alter the mimetype 
2474                         // the indices were shifted by 1 
2480                 wxArrayString strExtensions
; 
2481                 AddToMimeData (strType
, strIcon
, entry
, strExtensions
, strDesc
, !overwrite 
); 
2491 size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString
& mimetypes
) 
2498     size_t count 
= m_aTypes
.GetCount(); 
2499     for ( size_t n 
= 0; n 
< count
; n
++ ) 
2501         // don't return template types from here (i.e. anything containg '*') 
2503         if ( type
.Find(_T('*')) == wxNOT_FOUND 
) 
2505             mimetypes
.Add(type
); 
2509     return mimetypes
.GetCount(); 
2512 // ---------------------------------------------------------------------------- 
2513 // writing to MIME type files 
2514 // ---------------------------------------------------------------------------- 
2516 bool wxMimeTypesManagerImpl::Unassociate(wxFileType 
*ft
) 
2518     wxArrayString sMimeTypes
; 
2519     ft
->GetMimeTypes (sMimeTypes
); 
2523     for (i 
= 0; i 
< sMimeTypes
.GetCount(); i 
++) 
2525         sMime 
= sMimeTypes
.Item(i
); 
2526         int nIndex 
= m_aTypes
.Index (sMime
); 
2527         if ( nIndex 
== wxNOT_FOUND
) 
2529             // error if we get here ?? 
2534             WriteMimeInfo(nIndex
, TRUE 
); 
2535             m_aTypes
.RemoveAt(nIndex
); 
2536             m_aEntries
.RemoveAt(nIndex
); 
2537             m_aExtensions
.RemoveAt(nIndex
); 
2538             m_aDescriptions
.RemoveAt(nIndex
); 
2539             m_aIcons
.RemoveAt(nIndex
); 
2542     // check data integrity 
2543     wxASSERT( m_aTypes
.Count() == m_aEntries
.Count() && 
2544             m_aTypes
.Count() == m_aExtensions
.Count() && 
2545             m_aTypes
.Count() == m_aIcons
.Count() && 
2546             m_aTypes
.Count() == m_aDescriptions
.Count() ); 
2551 // ---------------------------------------------------------------------------- 
2552 // private functions 
2553 // ---------------------------------------------------------------------------- 
2555 static bool IsKnownUnimportantField(const wxString
& fieldAll
) 
2557     static const wxChar 
*knownFields
[] = 
2559         _T("x-mozilla-flags"), 
2561         _T("textualnewlines"), 
2564     wxString field 
= fieldAll
.BeforeFirst(_T('=')); 
2565     for ( size_t n 
= 0; n 
< WXSIZEOF(knownFields
); n
++ ) 
2567         if ( field
.CmpNoCase(knownFields
[n
]) == 0 ) 
2575   // wxUSE_MIMETYPE && wxUSE_FILE && wxUSE_TEXTFILE