1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        src/unix/mimetype.cpp 
   3 // Purpose:     classes and functions to manage MIME types 
   4 // Author:      Vadim Zeitlin 
   8 // Copyright:   (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr> 
   9 // Licence:     wxWindows licence (part of wxExtra library) 
  10 ///////////////////////////////////////////////////////////////////////////// 
  12 // known bugs; there may be others!! chris elliott, biol75@york.ac.uk 27 Mar 01 
  14 // 1) .mailcap and .mimetypes can be either in a netscape or metamail format 
  15 //    and entries may get confused during writing (I've tried to fix this; please let me know 
  16 //    any files that fail) 
  17 // 2) KDE and Gnome do not yet fully support international read/write 
  18 // 3) Gnome key lines like open.latex."LaTeX this file"=latex %f will have odd results 
  19 // 4) writing to files comments out the existing data; I hope this avoids losing 
  20 //    any data which we could not read, and data which we did not store like test= 
  21 // 5) results from reading files with multiple entries (especially matches with type/* ) 
  22 //    may (or may not) work for getXXX commands 
  23 // 6) Loading the png icons in Gnome doesn't work for me... 
  24 // 7) In Gnome, if keys.mime exists but keys.users does not, there is 
  25 //    an error message in debug mode, but the file is still written OK 
  26 // 8) Deleting entries is only allowed from the user file; sytem wide entries 
  27 //    will be preserved during unassociate 
  28 // 9) KDE does not yet handle multiple actions; Netscape mode never will 
  30 // TODO: this file is a mess, we need to split it and review everything (VZ) 
  32 // for compilers that support precompilation, includes "wx.h". 
  33 #include "wx/wxprec.h" 
  39 #if wxUSE_MIMETYPE && wxUSE_FILE && wxUSE_TEXTFILE 
  41 #include "wx/unix/mimetype.h" 
  44     #include "wx/dynarray.h" 
  45     #include "wx/string.h" 
  52 #include "wx/confbase.h" 
  55 #include "wx/textfile.h" 
  57 #include "wx/tokenzr.h" 
  58 #include "wx/iconloc.h" 
  59 #include "wx/filename.h" 
  61 #include "wx/apptrait.h" 
  65     #include "wx/gtk/gnome/gvfs.h" 
  68 // other standard headers 
  71 // this class is a wxTextFile specialization for dealing with files storing 
  72 // various MIME-related information 
  74 // it should be used instead of wxTextFile even if none of its additional 
  75 // methods are used just because it handles files with mixed encodings (often 
  76 // the case for MIME files which contain strings for different languages) 
  77 // correctly, see OnRead() 
  78 class wxMimeTextFile 
: public wxTextFile
 
  82     wxMimeTextFile () : wxTextFile () { } 
  83     wxMimeTextFile(const wxString
& strFile
) : wxTextFile(strFile
) { } 
  85     int pIndexOf(const wxString
& sSearch
, 
  86                  bool bIncludeComments 
= false, 
  89         wxString sTest 
= sSearch
; 
  91         for(size_t i 
= iStart
; i 
< GetLineCount(); i
++) 
  93             wxString sLine 
= GetLine(i
).Trim(false); 
  94             if(bIncludeComments 
|| ! sLine
.StartsWith(wxT("#"))) 
  97                 if(sLine
.StartsWith(sTest
)) 
 104     bool CommentLine(int nIndex
) 
 108         if (nIndex 
>= (int)GetLineCount() ) 
 111         GetLine(nIndex
) = GetLine(nIndex
).Prepend(wxT("#")); 
 115     bool CommentLine(const wxString 
& sTest
) 
 117         int nIndex 
= pIndexOf(sTest
); 
 120         if (nIndex 
>= (int)GetLineCount() ) 
 123         GetLine(nIndex
) = GetLine(nIndex
).Prepend(wxT("#")); 
 127     wxString 
GetVerb(size_t i
) 
 129         if (i 
> GetLineCount() ) 
 130             return wxEmptyString
; 
 132         wxString sTmp 
= GetLine(i
).BeforeFirst(wxT('=')); 
 136     wxString 
GetCmd(size_t i
) 
 138         if (i 
> GetLineCount() ) 
 139             return wxEmptyString
; 
 141         wxString sTmp 
= GetLine(i
).AfterFirst(wxT('=')); 
 146     // we override this virtual method because we want to always use UTF-8 
 147     // conversion allowing for invalid characters as MIME information files 
 148     // often contain lines in different encodings and can't be read using any 
 149     // single conversion in Unicode build, so we just try to read what we can 
 150     // suing the most common encoding (UTF-8 is almost ubiquitous nowadays) and 
 152     virtual bool OnRead(const wxMBConv
& WXUNUSED(conv
)) 
 154         return wxTextFile::OnRead( 
 155                     wxMBConvUTF8(wxMBConvUTF8::MAP_INVALID_UTF8_TO_PUA
)); 
 159 // ---------------------------------------------------------------------------- 
 161 // ---------------------------------------------------------------------------- 
 163 // MIME code tracing mask 
 164 #define TRACE_MIME wxT("mime") 
 166 // give trace messages about the results of mailcap tests 
 167 #define TRACE_MIME_TEST wxT("mimetest") 
 169 // ---------------------------------------------------------------------------- 
 171 // ---------------------------------------------------------------------------- 
 173 // there are some fields which we don't understand but for which we don't give 
 174 // warnings as we know that they're not important - this function is used to 
 176 static bool IsKnownUnimportantField(const wxString
& field
); 
 178 // ---------------------------------------------------------------------------- 
 180 // ---------------------------------------------------------------------------- 
 183 // This class uses both mailcap and mime.types to gather information about file 
 186 // The information about mailcap file was extracted from metamail(1) sources 
 187 // and documentation and subsequently revised when I found the RFC 1524 
 190 // Format of mailcap file: spaces are ignored, each line is either a comment 
 191 // (starts with '#') or a line of the form <field1>;<field2>;...;<fieldN>. 
 192 // A backslash can be used to quote semicolons and newlines (and, in fact, 
 193 // anything else including itself). 
 195 // The first field is always the MIME type in the form of type/subtype (see RFC 
 196 // 822) where subtype may be '*' meaning "any". Following metamail, we accept 
 197 // "type" which means the same as "type/*", although I'm not sure whether this 
 200 // The second field is always the command to run. It is subject to 
 201 // parameter/filename expansion described below. 
 203 // All the following fields are optional and may not be present at all. If 
 204 // they're present they may appear in any order, although each of them should 
 205 // appear only once. The optional fields are the following: 
 206 //  * notes=xxx is an uninterpreted string which is silently ignored 
 207 //  * test=xxx is the command to be used to determine whether this mailcap line 
 208 //    applies to our data or not. The RHS of this field goes through the 
 209 //    parameter/filename expansion (as the 2nd field) and the resulting string 
 210 //    is executed. The line applies only if the command succeeds, i.e. returns 0 
 212 //  * print=xxx is the command to be used to print (and not view) the data of 
 213 //    this type (parameter/filename expansion is done here too) 
 214 //  * edit=xxx is the command to open/edit the data of this type 
 215 //  * needsterminal means that a new interactive console must be created for 
 217 //  * copiousoutput means that the viewer doesn't interact with the user but 
 218 //    produces (possibly) a lof of lines of output on stdout (i.e. "cat" is a 
 219 //    good example), thus it might be a good idea to use some kind of paging 
 221 //  * textualnewlines means not to perform CR/LF translation (not honored) 
 222 //  * compose and composetyped fields are used to determine the program to be 
 223 //    called to create a new message pert in the specified format (unused). 
 225 // Parameter/filename expansion: 
 226 //  * %s is replaced with the (full) file name 
 227 //  * %t is replaced with MIME type/subtype of the entry 
 228 //  * for multipart type only %n is replaced with the nnumber of parts and %F is 
 229 //    replaced by an array of (content-type, temporary file name) pairs for all 
 230 //    message parts (TODO) 
 231 //  * %{parameter} is replaced with the value of parameter taken from 
 232 //    Content-type header line of the message. 
 235 // There are 2 possible formats for mime.types file, one entry per line (used 
 236 // for global mime.types and called Mosaic format) and "expanded" format where 
 237 // an entry takes multiple lines (used for users mime.types and called 
 240 // For both formats spaces are ignored and lines starting with a '#' are 
 241 // comments. Each record has one of two following forms: 
 242 //  a) for "brief" format: 
 243 //      <mime type>  <space separated list of extensions> 
 244 //  b) for "expanded" format: 
 245 //      type=<mime type> BACKSLASH 
 246 //      desc="<description>" BACKSLASH 
 247 //      exts="<comma separated list of extensions>" 
 249 // (where BACKSLASH is a literal '\\' which we can't put here because cpp 
 252 // We try to autodetect the format of mime.types: if a non-comment line starts 
 253 // with "type=" we assume the second format, otherwise the first one. 
 255 // there may be more than one entry for one and the same mime type, to 
 256 // choose the right one we have to run the command specified in the test 
 257 // field on our data. 
 259 // ---------------------------------------------------------------------------- 
 261 // ---------------------------------------------------------------------------- 
 263 // GNOME stores the info we're interested in in several locations: 
 264 //  1. xxx.keys files under /usr/share/mime-info 
 265 //  2. xxx.keys files under ~/.gnome/mime-info 
 267 // Update (Chris Elliott): apparently there may be an optional "[lang]" prefix 
 268 // just before the field name. 
 271 void wxMimeTypesManagerImpl::LoadGnomeDataFromKeyFile(const wxString
& filename
, 
 272                                                       const wxArrayString
& dirs
) 
 274     wxMimeTextFile 
textfile(filename
); 
 275     if ( !textfile
.Open() ) 
 278     wxLogTrace(TRACE_MIME
, wxT("--- Opened Gnome file %s  ---"), 
 281     wxArrayString 
search_dirs( dirs 
); 
 283     // values for the entry being parsed 
 284     wxString curMimeType
, curIconFile
; 
 285     wxMimeTypeCommands 
* entry 
= new wxMimeTypeCommands
; 
 287     wxArrayString strExtensions
; 
 291     size_t nLineCount 
= textfile
.GetLineCount(); 
 293     while ( nLine 
< nLineCount 
) 
 295         pc 
= textfile
[nLine
].c_str(); 
 296         if ( *pc 
!= wxT('#') ) 
 299             wxLogTrace(TRACE_MIME
, wxT("--- Reading from Gnome file %s '%s' ---"), 
 300                     filename
.c_str(), pc
); 
 302             // trim trailing space and tab 
 303             while ((*pc 
== wxT(' ')) || (*pc 
== wxT('\t'))) 
 307             int equal_pos 
= sTmp
.Find( wxT('=') ); 
 310                 wxString left_of_equal 
= sTmp
.Left( equal_pos 
); 
 311                 const wxChar 
*right_of_equal 
= pc
; 
 312                 right_of_equal 
+= equal_pos
+1; 
 314                 if (left_of_equal 
== wxT("icon_filename")) 
 317                     curIconFile 
= right_of_equal
; 
 319                     wxFileName 
newFile( curIconFile 
); 
 320                     if (newFile
.IsRelative() || newFile
.FileExists()) 
 322                         size_t nDirs 
= search_dirs
.GetCount(); 
 324                         for (size_t nDir 
= 0; nDir 
< nDirs
; nDir
++) 
 326                             newFile
.SetPath( search_dirs
[nDir
] ); 
 327                             newFile
.AppendDir( wxT("pixmaps") ); 
 328                             newFile
.AppendDir( wxT("document-icons") ); 
 329                             newFile
.SetExt( wxT("png") ); 
 330                             if (newFile
.FileExists()) 
 332                                 curIconFile 
= newFile
.GetFullPath(); 
 333                                 // reorder search_dirs for speedup (fewer 
 334                                 // calls to FileExist() required) 
 337                                     const wxString 
&tmp 
= search_dirs
[nDir
]; 
 338                                     search_dirs
.RemoveAt( nDir 
); 
 339                                     search_dirs
.Insert( tmp
, 0 ); 
 346                 else if (left_of_equal 
== wxT("open")) 
 348                     sTmp 
= right_of_equal
; 
 349                     sTmp
.Replace( wxT("%f"), wxT("%s") ); 
 350                     sTmp
.Prepend( wxT("open=") ); 
 353                 else if (left_of_equal 
== wxT("view")) 
 355                     sTmp 
= right_of_equal
; 
 356                     sTmp
.Replace( wxT("%f"), wxT("%s") ); 
 357                     sTmp
.Prepend( wxT("view=") ); 
 360                 else if (left_of_equal 
== wxT("print")) 
 362                     sTmp 
= right_of_equal
; 
 363                     sTmp
.Replace( wxT("%f"), wxT("%s") ); 
 364                     sTmp
.Prepend( wxT("print=") ); 
 367                 else if (left_of_equal 
== wxT("description")) 
 369                     strDesc 
= right_of_equal
; 
 371                 else if (left_of_equal 
== wxT("short_list_application_ids_for_novice_user_level")) 
 373                     sTmp 
= right_of_equal
; 
 374                     if (sTmp
.Contains( wxT(",") )) 
 375                         sTmp 
= sTmp
.BeforeFirst( wxT(',') ); 
 376                     sTmp
.Prepend( wxT("open=") ); 
 377                     sTmp
.Append( wxT(" %s") ); 
 381             } // emd of has an equals sign 
 384                 // not a comment and not an equals sign 
 385                 if (sTmp
.Contains(wxT('/'))) 
 387                     // this is the start of the new mimetype 
 388                     // overwrite any existing data 
 389                     if (! curMimeType
.empty()) 
 391                         AddToMimeData( curMimeType
, curIconFile
, entry
, strExtensions
, strDesc 
); 
 393                         // now get ready for next bit 
 394                         entry 
= new wxMimeTypeCommands
; 
 397                     curMimeType 
= sTmp
.BeforeFirst(wxT(':')); 
 400         } // end of not a comment 
 402         // ignore blank lines 
 404     } // end of while, save any data 
 406     if ( curMimeType
.empty() ) 
 409         AddToMimeData( curMimeType
, curIconFile
, entry
, strExtensions
, strDesc
); 
 412 void wxMimeTypesManagerImpl::LoadGnomeMimeTypesFromMimeFile(const wxString
& filename
) 
 414     wxMimeTextFile 
textfile(filename
); 
 415     if ( !textfile
.Open() ) 
 418     wxLogTrace(TRACE_MIME
, 
 419                wxT("--- Opened Gnome file %s  ---"), 
 422     // values for the entry being parsed 
 423     wxString curMimeType
, curExtList
; 
 426     size_t nLineCount 
= textfile
.GetLineCount(); 
 427     for ( size_t nLine 
= 0; /* nothing */; nLine
++ ) 
 429         if ( nLine 
< nLineCount 
) 
 431             pc 
= textfile
[nLine
].c_str(); 
 432             if ( *pc 
== wxT('#') ) 
 440             // so that we will fall into the "if" below 
 447             if ( !curMimeType
.empty() && !curExtList
.empty() ) 
 449                  wxLogTrace(TRACE_MIME
, 
 450                             wxT("--- At end of Gnome file  finding mimetype %s  ---"), 
 451                             curMimeType
.c_str()); 
 453                  AddMimeTypeInfo(curMimeType
, curExtList
, wxEmptyString
); 
 458                 // the end: this can only happen if nLine == nLineCount 
 467         // what do we have here? 
 468         if ( *pc 
== wxT('\t') ) 
 470             // this is a field=value ling 
 471             pc
++; // skip leading TAB 
 473             static const int lenField 
= 5; // strlen("ext: ") 
 474             if ( wxStrncmp(pc
, wxT("ext: "), lenField
) == 0 ) 
 476                 // skip it and take everything left until the end of line 
 477                 curExtList 
= pc 
+ lenField
; 
 479             //else: some other field, we don't care 
 483             // this is the start of the new section 
 484             wxLogTrace(TRACE_MIME
, 
 485                        wxT("--- In Gnome file  finding mimetype %s  ---"), 
 486                        curMimeType
.c_str()); 
 488             if (! curMimeType
.empty()) 
 489                 AddMimeTypeInfo(curMimeType
, curExtList
, wxEmptyString
); 
 493             while ( *pc 
!= wxT(':') && *pc 
!= wxT('\0') ) 
 495                 curMimeType 
+= *pc
++; 
 502 void wxMimeTypesManagerImpl::LoadGnomeMimeFilesFromDir( 
 503                       const wxString
& dirbase
, const wxArrayString
& dirs
) 
 505     wxASSERT_MSG( !dirbase
.empty() && !wxEndsWithPathSeparator(dirbase
), 
 506                   wxT("base directory shouldn't end with a slash") ); 
 508     wxString dirname 
= dirbase
; 
 509     dirname 
<< wxT("/mime-info"); 
 511     if ( !wxDir::Exists(dirname
) ) 
 515     if ( !dir
.IsOpened() ) 
 518     // we will concatenate it with filename to get the full path below 
 524     cont 
= dir
.GetFirst(&filename
, wxT("*.mime"), wxDIR_FILES
); 
 527         LoadGnomeMimeTypesFromMimeFile(dirname 
+ filename
); 
 529         cont 
= dir
.GetNext(&filename
); 
 532     cont 
= dir
.GetFirst(&filename
, wxT("*.keys"), wxDIR_FILES
); 
 535         LoadGnomeDataFromKeyFile(dirname 
+ filename
, dirs
); 
 537         cont 
= dir
.GetNext(&filename
); 
 540     // FIXME: Hack alert: We scan all icons and deduce the 
 541     //             mime-type from the file name. 
 543     dirname 
<< wxT("/pixmaps/document-icons"); 
 545     // these are always empty in this file 
 546     wxArrayString strExtensions
; 
 549     if ( !wxDir::Exists(dirname
) ) 
 551         // Just test for default GPE dir also 
 552         dirname 
= wxT("/usr/share/gpe/pixmaps/default/filemanager/document-icons"); 
 554         if ( !wxDir::Exists(dirname
) ) 
 558     wxDir 
dir2( dirname 
); 
 560     cont 
= dir2
.GetFirst(&filename
, wxT("gnome-*.png"), wxDIR_FILES
); 
 563         wxString mimeType 
= filename
; 
 564         mimeType
.Remove( 0, 6 ); // remove "gnome-" 
 565         mimeType
.Remove( mimeType
.Len() - 4, 4 ); // remove ".png" 
 566         int pos 
= mimeType
.Find( wxT("-") ); 
 567         if (pos 
!= wxNOT_FOUND
) 
 569             mimeType
.SetChar( pos
, wxT('/') ); 
 570             wxString iconFile 
= dirname
; 
 571             iconFile 
<< wxT("/"); 
 572             iconFile 
<< filename
; 
 573             AddToMimeData( mimeType
, iconFile
, NULL
, strExtensions
, strDesc
, true ); 
 576         cont 
= dir2
.GetNext(&filename
); 
 580 void wxMimeTypesManagerImpl::GetGnomeMimeInfo(const wxString
& sExtraDir
) 
 584     wxString gnomedir 
= wxGetenv( wxT("GNOMEDIR") ); 
 585     if (!gnomedir
.empty()) 
 587         gnomedir 
<< wxT("/share"); 
 588         dirs
.Add( gnomedir 
); 
 591     dirs
.Add(wxT("/usr/share")); 
 592     dirs
.Add(wxT("/usr/local/share")); 
 594     gnomedir 
= wxGetHomeDir(); 
 595     gnomedir 
<< wxT("/.gnome"); 
 596     dirs
.Add( gnomedir 
); 
 598     if (!sExtraDir
.empty()) 
 599         dirs
.Add( sExtraDir 
); 
 601     size_t nDirs 
= dirs
.GetCount(); 
 602     for ( size_t nDir 
= 0; nDir 
< nDirs
; nDir
++ ) 
 604         LoadGnomeMimeFilesFromDir(dirs
[nDir
], dirs
); 
 608 // ---------------------------------------------------------------------------- 
 610 // ---------------------------------------------------------------------------- 
 613 // KDE stores the icon info in its .kdelnk files. The file for mimetype/subtype 
 614 // may be found in either of the following locations 
 616 //  1. $KDEDIR/share/mimelnk/mimetype/subtype.kdelnk 
 617 //  2. ~/.kde/share/mimelnk/mimetype/subtype.kdelnk 
 619 // The format of a .kdelnk file is almost the same as the one used by 
 620 // wxFileConfig, i.e. there are groups, comments and entries. The icon is the 
 621 // value for the entry "Type" 
 623 // kde writing; see http://webcvs.kde.org/cgi-bin/cvsweb.cgi/~checkout~/kdelibs/kio/DESKTOP_ENTRY_STANDARD 
 624 // for now write to .kdelnk but should eventually do .desktop instead (in preference??) 
 626 bool wxMimeTypesManagerImpl::CheckKDEDirsExist( const wxString 
&sOK
, const wxString 
&sTest 
) 
 630         return wxDir::Exists(sOK
); 
 634         wxString sStart 
= sOK 
+ wxT("/") + sTest
.BeforeFirst(wxT('/')); 
 635         if (!wxDir::Exists(sStart
)) 
 637         wxString sEnd 
= sTest
.AfterFirst(wxT('/')); 
 638         return CheckKDEDirsExist(sStart
, sEnd
); 
 642 bool wxMimeTypesManagerImpl::WriteKDEMimeFile(int index
, bool delete_index
) 
 644     wxMimeTextFile appoutfile
, mimeoutfile
; 
 645     wxString sHome 
= wxGetHomeDir(); 
 646     wxString sTmp 
= wxT(".kde/share/mimelnk/"); 
 647     wxString sMime 
= m_aTypes
[index
]; 
 648     CheckKDEDirsExist(sHome
, sTmp 
+ sMime
.BeforeFirst(wxT('/')) ); 
 649     sTmp 
= sHome 
+ wxT('/') + sTmp 
+ sMime 
+ wxT(".kdelnk"); 
 652     bool bMimeExists 
= mimeoutfile
.Open(sTmp
); 
 655         bTemp 
= mimeoutfile
.Create(sTmp
); 
 656         // some unknown error eg out of disk space 
 661     sTmp 
= wxT(".kde/share/applnk/"); 
 662     CheckKDEDirsExist(sHome
, sTmp 
+ sMime
.AfterFirst(wxT('/')) ); 
 663     sTmp 
= sHome 
+ wxT('/') + sTmp 
+ sMime
.AfterFirst(wxT('/')) + wxT(".kdelnk"); 
 666     bAppExists 
= appoutfile
.Open(sTmp
); 
 669         bTemp 
= appoutfile
.Create(sTmp
); 
 670         // some unknown error eg out of disk space 
 675     // fixed data; write if new file 
 678         mimeoutfile
.AddLine(wxT("#KDE Config File")); 
 679         mimeoutfile
.AddLine(wxT("[KDE Desktop Entry]")); 
 680         mimeoutfile
.AddLine(wxT("Version=1.0")); 
 681         mimeoutfile
.AddLine(wxT("Type=MimeType")); 
 682         mimeoutfile
.AddLine(wxT("MimeType=") + sMime
); 
 687         mimeoutfile
.AddLine(wxT("#KDE Config File")); 
 688         mimeoutfile
.AddLine(wxT("[KDE Desktop Entry]")); 
 689         appoutfile
.AddLine(wxT("Version=1.0")); 
 690         appoutfile
.AddLine(wxT("Type=Application")); 
 691         appoutfile
.AddLine(wxT("MimeType=") + sMime 
+ wxT(';')); 
 696     mimeoutfile
.CommentLine(wxT("Comment=")); 
 698         mimeoutfile
.AddLine(wxT("Comment=") + m_aDescriptions
[index
]); 
 699     appoutfile
.CommentLine(wxT("Name=")); 
 701         appoutfile
.AddLine(wxT("Comment=") + m_aDescriptions
[index
]); 
 703     sTmp 
= m_aIcons
[index
]; 
 704     // we can either give the full path, or the shortfilename if its in 
 705     // one of the directories we search 
 706     mimeoutfile
.CommentLine(wxT("Icon=") ); 
 708         mimeoutfile
.AddLine(wxT("Icon=") + sTmp 
); 
 709     appoutfile
.CommentLine(wxT("Icon=") ); 
 711         appoutfile
.AddLine(wxT("Icon=") + sTmp 
); 
 713     sTmp 
= wxT(" ") + m_aExtensions
[index
]; 
 715     wxStringTokenizer 
tokenizer(sTmp
, wxT(" ")); 
 716     sTmp 
= wxT("Patterns="); 
 717     mimeoutfile
.CommentLine(sTmp
); 
 718     while ( tokenizer
.HasMoreTokens() ) 
 720         // holds an extension; need to change it to *.ext; 
 721         wxString e 
= wxT("*.") + tokenizer
.GetNextToken() + wxT(";"); 
 726         mimeoutfile
.AddLine(sTmp
); 
 728     wxMimeTypeCommands 
* entries 
= m_aEntries
[index
]; 
 729     // if we don't find open just have an empty string ... FIX this 
 730     sTmp 
= entries
->GetCommandForVerb(wxT("open")); 
 731     sTmp
.Replace( wxT("%s"), wxT("%f") ); 
 733     mimeoutfile
.CommentLine(wxT("DefaultApp=") ); 
 735         mimeoutfile
.AddLine(wxT("DefaultApp=") + sTmp
); 
 737     sTmp
.Replace( wxT("%f"), wxT("") ); 
 738     appoutfile
.CommentLine(wxT("Exec=")); 
 740         appoutfile
.AddLine(wxT("Exec=") + sTmp
); 
 742     if (entries
->GetCount() > 1) 
 744         //other actions as well as open 
 748     if (mimeoutfile
.Write()) 
 751     if (appoutfile
.Write()) 
 758 void wxMimeTypesManagerImpl::LoadKDELinksForMimeSubtype(const wxString
& dirbase
, 
 759                                                const wxString
& subdir
, 
 760                                                const wxString
& filename
, 
 761                                                const wxArrayString
& icondirs
) 
 763     wxFileName 
fullname(dirbase
, filename
); 
 764     wxLogTrace(TRACE_MIME
, wxT("loading KDE file %s"), 
 765                            fullname
.GetFullPath().c_str()); 
 768     if ( !file
.Open(fullname
.GetFullPath()) ) 
 771     wxMimeTypeCommands 
* entry 
= new wxMimeTypeCommands
; 
 773     wxString mimetype
, mime_desc
, strIcon
; 
 775     int nIndex 
= file
.pIndexOf( wxT("MimeType=") ); 
 776     if (nIndex 
== wxNOT_FOUND
) 
 778         // construct mimetype from the directory name and the basename of the 
 779         // file (it always has .kdelnk extension) 
 780         mimetype 
<< subdir 
<< wxT('/') << filename
.BeforeLast( wxT('.') ); 
 783         mimetype 
= file
.GetCmd(nIndex
); 
 785     // first find the description string: it is the value in either "Comment=" 
 786     // line or "Comment[<locale_name>]=" one 
 787     nIndex 
= wxNOT_FOUND
; 
 792     wxLocale 
*locale 
= wxGetLocale(); 
 795         // try "Comment[locale name]" first 
 796         comment 
<< wxT("Comment[") + locale
->GetName() + wxT("]="); 
 797         nIndex 
= file
.pIndexOf(comment
); 
 801     if ( nIndex 
== wxNOT_FOUND 
) 
 803         comment 
= wxT("Comment="); 
 804         nIndex 
= file
.pIndexOf(comment
); 
 807     if ( nIndex 
!= wxNOT_FOUND 
) 
 808         mime_desc 
= file
.GetCmd(nIndex
); 
 809     //else: no description 
 811     // next find the extensions 
 812     wxString mime_extension
; 
 814     nIndex 
= file
.pIndexOf(wxT("Patterns=")); 
 815     if ( nIndex 
!= wxNOT_FOUND 
) 
 817         wxString exts 
= file
.GetCmd(nIndex
); 
 819         wxStringTokenizer 
tokenizer(exts
, wxT(";")); 
 820         while ( tokenizer
.HasMoreTokens() ) 
 822             wxString e 
= tokenizer
.GetNextToken(); 
 824             // don't support too difficult patterns 
 825             if ( e
.Left(2) != wxT("*.") ) 
 828             if ( !mime_extension
.empty() ) 
 830                 // separate from the previous ext 
 831                 mime_extension 
<< wxT(' '); 
 834             mime_extension 
<< e
.Mid(2); 
 838     sExts
.Add(mime_extension
); 
 840     // ok, now we can take care of icon: 
 842     nIndex 
= file
.pIndexOf(wxT("Icon=")); 
 843     if ( nIndex 
!= wxNOT_FOUND 
) 
 845         strIcon 
= file
.GetCmd(nIndex
); 
 847         wxLogTrace(TRACE_MIME
, wxT("  icon %s"), strIcon
.c_str()); 
 849         // it could be the real path, but more often a short name 
 850         if (!wxFileExists(strIcon
)) 
 852             // icon is just the short name 
 853             if ( !strIcon
.empty() ) 
 855                 // we must check if the file exists because it may be stored 
 856                 // in many locations, at least ~/.kde and $KDEDIR 
 857                 size_t nDir
, nDirs 
= icondirs
.GetCount(); 
 858                 for ( nDir 
= 0; nDir 
< nDirs
; nDir
++ ) 
 860                     wxFileName 
fnameIcon( strIcon 
); 
 861                     wxFileName 
fname( icondirs
[nDir
], fnameIcon
.GetName() ); 
 862                     fname
.SetExt( wxT("png") ); 
 863                     if (fname
.FileExists()) 
 865                         strIcon 
= fname
.GetFullPath(); 
 866                         wxLogTrace(TRACE_MIME
, wxT("  iconfile %s"), strIcon
.c_str()); 
 874     // now look for lines which know about the application 
 875     // exec= or DefaultApp= 
 877     nIndex 
= file
.pIndexOf(wxT("DefaultApp")); 
 879     if ( nIndex 
== wxNOT_FOUND 
) 
 882         nIndex 
= file
.pIndexOf(wxT("Exec")); 
 885     if ( nIndex 
!= wxNOT_FOUND 
) 
 887         // we expect %f; others including  %F and %U and %u are possible 
 888         wxString sTmp 
= file
.GetCmd(nIndex
); 
 889         if (0 == sTmp
.Replace( wxT("%f"), wxT("%s") )) 
 891         entry
->AddOrReplaceVerb(wxString(wxT("open")), sTmp 
); 
 894     AddToMimeData(mimetype
, strIcon
, entry
, sExts
, mime_desc
); 
 897 void wxMimeTypesManagerImpl::LoadKDELinksForMimeType(const wxString
& dirbase
, 
 898                                             const wxString
& subdir
, 
 899                                             const wxArrayString
& icondirs
) 
 901     wxFileName 
dirname(dirbase
, wxEmptyString
); 
 902     dirname
.AppendDir(subdir
); 
 903     wxDir 
dir(dirname
.GetPath()); 
 907     wxLogTrace(TRACE_MIME
, wxT("--- Loading from KDE directory %s  ---"), 
 908                            dirname
.GetPath().c_str()); 
 911     bool cont 
= dir
.GetFirst(&filename
, wxT("*.kdelnk"), wxDIR_FILES
); 
 913         LoadKDELinksForMimeSubtype(dirname
.GetPath(), subdir
, 
 915         cont 
= dir
.GetNext(&filename
); 
 918     // new standard for Gnome and KDE 
 919     cont 
= dir
.GetFirst(&filename
, wxT("*.desktop"), wxDIR_FILES
); 
 921         LoadKDELinksForMimeSubtype(dirname
.GetPath(), subdir
, 
 923         cont 
= dir
.GetNext(&filename
); 
 927 void wxMimeTypesManagerImpl::LoadKDELinkFilesFromDir(const wxString
& dirname
, 
 928                                             const wxArrayString
& icondirs
) 
 930     if(! wxDir::Exists(dirname
)) 
 934     if ( !dir
.IsOpened() ) 
 938     bool cont 
= dir
.GetFirst(&subdir
, wxEmptyString
, wxDIR_DIRS
); 
 941         LoadKDELinksForMimeType(dirname
, subdir
, icondirs
); 
 943         cont 
= dir
.GetNext(&subdir
); 
 947 // Read a KDE .desktop file of type 'Application' 
 948 void wxMimeTypesManagerImpl::LoadKDEApp(const wxString
& filename
) 
 950     wxLogTrace(TRACE_MIME
, wxT("loading KDE file %s"), filename
.c_str()); 
 953     if ( !file
.Open(filename
) ) 
 956     // Here, only type 'Application' should be considered. 
 957     int nIndex 
= file
.pIndexOf( wxT("Type=") ); 
 958     if (nIndex 
!= wxNOT_FOUND 
&& 
 959         file
.GetCmd(nIndex
).Lower() != wxT("application")) 
 962     // The hidden entry specifies a file to be ignored. 
 963     nIndex 
= file
.pIndexOf( wxT("Hidden=") ); 
 964     if (nIndex 
!= wxNOT_FOUND 
&& file
.GetCmd(nIndex
).Lower() == wxT("true")) 
 967     // Semicolon separated list of mime types handled by the application. 
 968     nIndex 
= file
.pIndexOf( wxT("MimeType=") ); 
 969     if (nIndex 
== wxNOT_FOUND
) 
 971     wxString mimetypes 
= file
.GetCmd (nIndex
); 
 973     // Name of the application 
 975     nIndex 
= wxNOT_FOUND
; 
 976 #if wxUSE_INTL // try "Name[locale name]" first 
 977     wxLocale 
*locale 
= wxGetLocale(); 
 979         nIndex 
= file
.pIndexOf(_T("Name[")+locale
->GetName()+_T("]=")); 
 981     if(nIndex 
== wxNOT_FOUND
) 
 982         nIndex 
= file
.pIndexOf( wxT("Name=") ); 
 983     if(nIndex 
!= wxNOT_FOUND
) 
 984         nameapp 
= file
.GetCmd(nIndex
); 
 986     // Icon of the application. 
 987     wxString nameicon
, namemini
; 
 988     nIndex 
= wxNOT_FOUND
; 
 989 #if wxUSE_INTL // try "Icon[locale name]" first 
 991         nIndex 
= file
.pIndexOf(_T("Icon[")+locale
->GetName()+_T("]=")); 
 993     if(nIndex 
== wxNOT_FOUND
) 
 994         nIndex 
= file
.pIndexOf( wxT("Icon=") ); 
 995     if(nIndex 
!= wxNOT_FOUND
) { 
 996         nameicon 
= wxString(wxT("--icon ")) + file
.GetCmd(nIndex
); 
 997         namemini 
= wxString(wxT("--miniicon ")) + file
.GetCmd(nIndex
); 
1000     // Replace some of the field code in the 'Exec' entry. 
1001     // TODO: deal with %d, %D, %n, %N, %k and %v (but last one is deprecated) 
1002     nIndex 
= file
.pIndexOf( wxT("Exec=") ); 
1003     if (nIndex 
== wxNOT_FOUND
) 
1005     wxString sCmd 
= file
.GetCmd(nIndex
); 
1006     // we expect %f; others including  %F and %U and %u are possible 
1007     sCmd
.Replace(wxT("%F"), wxT("%f")); 
1008     sCmd
.Replace(wxT("%U"), wxT("%f")); 
1009     sCmd
.Replace(wxT("%u"), wxT("%f")); 
1010     if (0 == sCmd
.Replace ( wxT("%f"), wxT("%s") )) 
1011         sCmd 
= sCmd 
+ wxT(" %s"); 
1012     sCmd
.Replace(wxT("%c"), nameapp
); 
1013     sCmd
.Replace(wxT("%i"), nameicon
); 
1014     sCmd
.Replace(wxT("%m"), namemini
); 
1016     wxStringTokenizer 
tokenizer(mimetypes
, _T(";")); 
1017     while(tokenizer
.HasMoreTokens()) { 
1018         wxString mimetype 
= tokenizer
.GetNextToken().Lower(); 
1019         int nIndex 
= m_aTypes
.Index(mimetype
); 
1020         if(nIndex 
!= wxNOT_FOUND
) { // is this a known MIME type? 
1021             wxMimeTypeCommands
* entry 
= m_aEntries
[nIndex
]; 
1022             entry
->AddOrReplaceVerb(wxT("open"), sCmd
); 
1027 void wxMimeTypesManagerImpl::LoadKDEAppsFilesFromDir(const wxString
& dirname
) 
1029     if(! wxDir::Exists(dirname
)) 
1032     if ( !dir
.IsOpened() ) 
1036     // Look into .desktop files 
1037     bool cont 
= dir
.GetFirst(&filename
, _T("*.desktop"), wxDIR_FILES
); 
1039         wxFileName 
p(dirname
, filename
); 
1040         LoadKDEApp( p
.GetFullPath() ); 
1041         cont 
= dir
.GetNext(&filename
); 
1043     // Look recursively into subdirs 
1044     cont 
= dir
.GetFirst(&filename
, wxEmptyString
, wxDIR_DIRS
); 
1046         wxFileName 
p(dirname
, wxEmptyString
); 
1047         p
.AppendDir(filename
); 
1048         LoadKDEAppsFilesFromDir( p
.GetPath() ); 
1049         cont 
= dir
.GetNext(&filename
); 
1053 // Return base KDE directories. 
1054 // 1) Environment variable $KDEHOME, or "~/.kde" if not set. 
1055 // 2) List of directories in colon separated environment variable $KDEDIRS. 
1056 // 3) Environment variable $KDEDIR in case $KDEDIRS is not set. 
1057 // Notice at least the local kde directory is added to the list. If it is the 
1058 // only one, use later the application 'kde-config' to get additional paths. 
1059 static void GetKDEBaseDirs(wxArrayString
& basedirs
) 
1061     wxString env 
= wxGetenv( wxT("KDEHOME") ); 
1063         env 
= wxGetHomeDir() + wxT("/.kde"); 
1066     env 
= wxGetenv( wxT("KDEDIRS") ); 
1068         env 
= wxGetenv( wxT("KDEDIR") ); 
1072         wxStringTokenizer 
tokenizer(env
, wxT(":")); 
1073         while(tokenizer
.HasMoreTokens()) 
1074             basedirs
.Add( tokenizer
.GetNextToken() ); 
1078 static wxString 
ReadPathFromKDEConfig(const wxString
& request
) 
1081     wxArrayString output
; 
1082     if(wxExecute(wxT("kde-config --path ")+request
, output
) == 0 && 
1083        output
.GetCount() > 0) 
1084         str 
= output
.Item(0); 
1088 // Try to find the "Theme" entry in the configuration file, provided it exists. 
1089 static wxString 
GetKDEThemeInFile(const wxFileName
& filename
) 
1092     wxMimeTextFile config
; 
1093     if ( filename
.FileExists() && config
.Open(filename
.GetFullPath()) ) 
1095         size_t cnt 
= config
.GetLineCount(); 
1096         for ( size_t i 
= 0; i 
< cnt
; i
++ ) 
1098             if ( config
[i
].StartsWith(wxT("Theme="), &theme
) ) 
1106 // Try to find a file "kdeglobals" in one of the directories and read the 
1107 // "Theme" entry there. 
1108 static wxString 
GetKDETheme(const wxArrayString
& basedirs
) 
1111     for(size_t i 
= 0; i 
< basedirs
.GetCount(); i
++) { 
1112         wxFileName 
filename(basedirs
.Item(i
), wxEmptyString
); 
1113         filename
.AppendDir( wxT("share") ); 
1114         filename
.AppendDir( wxT("config") ); 
1115         filename
.SetName( wxT("kdeglobals") ); 
1116         theme 
= GetKDEThemeInFile(filename
); 
1117         if(! theme
.IsEmpty()) 
1120     // If $KDEDIRS and $KDEDIR were set, we try nothing more. Otherwise, we 
1121     // try to get the configuration file with 'kde-config'. 
1122     if(basedirs
.GetCount() > 1) 
1124     wxString paths 
= ReadPathFromKDEConfig(wxT("config")); 
1125     if(! paths
.IsEmpty()) { 
1126         wxStringTokenizer 
tokenizer(paths
, wxT(":")); 
1127         while( tokenizer
.HasMoreTokens() ) { 
1128             wxFileName 
filename(tokenizer
.GetNextToken(), wxT("kdeglobals")); 
1129             theme 
= GetKDEThemeInFile(filename
); 
1130             if(! theme
.IsEmpty()) 
1137 // Get list of directories of icons. 
1138 static void GetKDEIconDirs(const wxArrayString
& basedirs
, 
1139                            wxArrayString
& icondirs
) 
1141     wxString theme 
= GetKDETheme(basedirs
); 
1143         theme 
= wxT("default.kde"); 
1145     for(size_t i 
= 0; i 
< basedirs
.GetCount(); i
++) { 
1146         wxFileName 
dirname(basedirs
.Item(i
), wxEmptyString
); 
1147         dirname
.AppendDir( wxT("share") ); 
1148         dirname
.AppendDir( wxT("icons") ); 
1149         dirname
.AppendDir(theme
); 
1150         dirname
.AppendDir( wxT("32x32") ); 
1151         dirname
.AppendDir( wxT("mimetypes") ); 
1152         if( wxDir::Exists( dirname
.GetPath() ) ) 
1153             icondirs
.Add( dirname
.GetPath() ); 
1156     // If $KDEDIRS and $KDEDIR were not set, use 'kde-config' 
1157     if(basedirs
.GetCount() > 1) 
1159     wxString paths 
= ReadPathFromKDEConfig(wxT("icon")); 
1160     if(! paths
.IsEmpty()) { 
1161         wxStringTokenizer 
tokenizer(paths
, wxT(":")); 
1162         while( tokenizer
.HasMoreTokens() ) { 
1163             wxFileName 
dirname(tokenizer
.GetNextToken(), wxEmptyString
); 
1164             dirname
.AppendDir(theme
); 
1165             dirname
.AppendDir( wxT("32x32") ); 
1166             dirname
.AppendDir( wxT("mimetypes") ); 
1167             if(icondirs
.Index(dirname
.GetPath()) == wxNOT_FOUND 
&& 
1168                wxDir::Exists( dirname
.GetPath() ) ) 
1169                 icondirs
.Add( dirname
.GetPath() ); 
1174 // Get list of directories of mime types. 
1175 static void GetKDEMimeDirs(const wxArrayString
& basedirs
, 
1176                            wxArrayString
& mimedirs
) 
1178     for(size_t i 
= 0; i 
< basedirs
.GetCount(); i
++) { 
1179         wxFileName 
dirname(basedirs
.Item(i
), wxEmptyString
); 
1180         dirname
.AppendDir( wxT("share") ); 
1181         dirname
.AppendDir( wxT("mimelnk") ); 
1182         if( wxDir::Exists( dirname
.GetPath() ) ) 
1183             mimedirs
.Add( dirname
.GetPath() ); 
1186     // If $KDEDIRS and $KDEDIR were not set, use 'kde-config' 
1187     if(basedirs
.GetCount() > 1) 
1189     wxString paths 
= ReadPathFromKDEConfig(wxT("mime")); 
1190     if(! paths
.IsEmpty()) { 
1191         wxStringTokenizer 
tokenizer(paths
, wxT(":")); 
1192         while( tokenizer
.HasMoreTokens() ) { 
1193             wxFileName 
p(tokenizer
.GetNextToken(), wxEmptyString
); 
1194             wxString dirname 
= p
.GetPath(); // To remove possible trailing '/' 
1195             if(mimedirs
.Index(dirname
) == wxNOT_FOUND 
&& 
1196                wxDir::Exists(dirname
) ) 
1197                 mimedirs
.Add(dirname
); 
1202 // Get list of directories of application desktop files. 
1203 static void GetKDEAppsDirs(const wxArrayString
& basedirs
, 
1204                            wxArrayString
& appsdirs
) 
1206     for(size_t i 
= 0; i 
< basedirs
.GetCount(); i
++) { 
1207         wxFileName 
dirname(basedirs
.Item(i
), wxEmptyString
); 
1208         dirname
.AppendDir( wxT("share") ); 
1209         dirname
.AppendDir( wxT("applnk") ); 
1210         if( wxDir::Exists( dirname
.GetPath() ) ) 
1211             appsdirs
.Add( dirname
.GetPath() ); 
1214     // If $KDEDIRS and $KDEDIR were not set, use 'kde-config' 
1215     if(basedirs
.GetCount() > 1) 
1217     wxString paths 
= ReadPathFromKDEConfig(wxT("apps")); 
1218     if(! paths
.IsEmpty()) { 
1219         wxStringTokenizer 
tokenizer(paths
, wxT(":")); 
1220         while( tokenizer
.HasMoreTokens() ) { 
1221             wxFileName 
p(tokenizer
.GetNextToken(), wxEmptyString
); 
1222             wxString dirname 
= p
.GetPath(); // To remove possible trailing '/' 
1223             if(appsdirs
.Index(dirname
) == wxNOT_FOUND 
&& 
1224                wxDir::Exists(dirname
) ) 
1225                 appsdirs
.Add(dirname
); 
1228     paths 
= ReadPathFromKDEConfig(wxT("xdgdata-apps")); 
1229     if(! paths
.IsEmpty()) { 
1230         wxStringTokenizer 
tokenizer(paths
, wxT(":")); 
1231         while( tokenizer
.HasMoreTokens() ) { 
1232             wxFileName 
p(tokenizer
.GetNextToken(), wxEmptyString
); 
1233             wxString dirname 
= p
.GetPath(); // To remove possible trailing '/' 
1234             if(appsdirs
.Index(dirname
) == wxNOT_FOUND 
&& 
1235                wxDir::Exists(dirname
) ) 
1236                 appsdirs
.Add(dirname
); 
1241 // Fill database with all mime types. 
1242 void wxMimeTypesManagerImpl::GetKDEMimeInfo(const wxString
& sExtraDir
) 
1244     wxArrayString basedirs
; 
1245     GetKDEBaseDirs(basedirs
); 
1247     wxArrayString icondirs
; 
1248     GetKDEIconDirs(basedirs
, icondirs
); 
1249     wxArrayString mimedirs
; 
1250     GetKDEMimeDirs(basedirs
, mimedirs
); 
1251     wxArrayString appsdirs
; 
1252     GetKDEAppsDirs(basedirs
, appsdirs
); 
1254     if(! sExtraDir
.IsEmpty()) { 
1255         icondirs
.Add(sExtraDir 
+ wxT("/icons")); 
1256         mimedirs
.Add(sExtraDir 
+ wxT("/mimelnk")); 
1257         appsdirs
.Add(sExtraDir 
+ wxT("/applnk")); 
1261     size_t nDirs 
= mimedirs
.GetCount(), nDir
; 
1262     for(nDir 
= 0; nDir 
< nDirs
; nDir
++) 
1263         LoadKDELinkFilesFromDir(mimedirs
[nDir
], icondirs
); 
1265     // Load application files and associate them to corresponding mime types. 
1266     nDirs 
= appsdirs
.GetCount(); 
1267     for(nDir 
= 0; nDir 
< nDirs
; nDir
++) 
1268         LoadKDEAppsFilesFromDir(appsdirs
[nDir
]); 
1271 // ---------------------------------------------------------------------------- 
1272 // wxFileTypeImpl (Unix) 
1273 // ---------------------------------------------------------------------------- 
1275 wxString 
wxFileTypeImpl::GetExpandedCommand(const wxString 
& verb
, const wxFileType::MessageParameters
& params
) const 
1279     while ( (i 
< m_index
.GetCount() ) && sTmp
.empty() ) 
1281         sTmp 
= m_manager
->GetCommand( verb
, m_index
[i
] ); 
1285     return wxFileType::ExpandCommand(sTmp
, params
); 
1288 bool wxFileTypeImpl::GetIcon(wxIconLocation 
*iconLoc
) const 
1292     while ( (i 
< m_index
.GetCount() ) && sTmp
.empty() ) 
1294         sTmp 
= m_manager
->m_aIcons
[m_index
[i
]]; 
1303         iconLoc
->SetFileName(sTmp
); 
1309 bool wxFileTypeImpl::GetMimeTypes(wxArrayString
& mimeTypes
) const 
1312     size_t nCount 
= m_index
.GetCount(); 
1313     for (size_t i 
= 0; i 
< nCount
; i
++) 
1314         mimeTypes
.Add(m_manager
->m_aTypes
[m_index
[i
]]); 
1319 size_t wxFileTypeImpl::GetAllCommands(wxArrayString 
*verbs
, 
1320                                   wxArrayString 
*commands
, 
1321                                   const wxFileType::MessageParameters
& params
) const 
1323     wxString vrb
, cmd
, sTmp
; 
1325     wxMimeTypeCommands 
* sPairs
; 
1327     // verbs and commands have been cleared already in mimecmn.cpp... 
1328     // if we find no entries in the exact match, try the inexact match 
1329     for (size_t n 
= 0; ((count 
== 0) && (n 
< m_index
.GetCount())); n
++) 
1331         // list of verb = command pairs for this mimetype 
1332         sPairs 
= m_manager
->m_aEntries 
[m_index
[n
]]; 
1334         for ( i 
= 0; i 
< sPairs
->GetCount(); i
++ ) 
1336             vrb 
= sPairs
->GetVerb(i
); 
1337             // some gnome entries have "." inside 
1338             vrb 
= vrb
.AfterLast(wxT('.')); 
1339             cmd 
= sPairs
->GetCmd(i
); 
1342                  cmd 
= wxFileType::ExpandCommand(cmd
, params
); 
1344                  if ( vrb
.IsSameAs(wxT("open"))) 
1347                         verbs
->Insert(vrb
, 0u); 
1349                         commands 
->Insert(cmd
, 0u); 
1365 bool wxFileTypeImpl::GetExtensions(wxArrayString
& extensions
) 
1367     const wxString strExtensions 
= m_manager
->GetExtension(m_index
[0]); 
1370     // one extension in the space or comma-delimited list 
1372     wxString::const_iterator end 
= strExtensions
.end(); 
1373     for ( wxString::const_iterator p 
= strExtensions
.begin(); /* nothing */; ++p 
) 
1375         if ( p 
== end 
|| *p 
== wxT(' ') || *p 
== wxT(',') ) 
1377             if ( !strExt
.empty() ) 
1379                 extensions
.Add(strExt
); 
1382             //else: repeated spaces 
1383             // (shouldn't happen, but it's not that important if it does happen) 
1388         else if ( *p 
== wxT('.') ) 
1390             // remove the dot from extension (but only if it's the first char) 
1391             if ( !strExt
.empty() ) 
1395             //else: no, don't append it 
1406 // set an arbitrary command: 
1407 // could adjust the code to ask confirmation if it already exists and 
1408 // overwriteprompt is true, but this is currently ignored as *Associate* has 
1409 // no overwrite prompt 
1411 wxFileTypeImpl::SetCommand(const wxString
& cmd
, 
1412                            const wxString
& verb
, 
1413                            bool WXUNUSED(overwriteprompt
)) 
1415     wxArrayString strExtensions
; 
1416     wxString strDesc
, strIcon
; 
1418     wxArrayString strTypes
; 
1419     GetMimeTypes(strTypes
); 
1420     if ( strTypes
.IsEmpty() ) 
1423     wxMimeTypeCommands 
*entry 
= new wxMimeTypeCommands(); 
1424     entry
->Add(verb 
+ wxT("=")  + cmd 
+ wxT(" %s ")); 
1427     size_t nCount 
= strTypes
.GetCount(); 
1428     for ( size_t i 
= 0; i 
< nCount
; i
++ ) 
1430         if ( m_manager
->DoAssociation
 
1439             // DoAssociation() took ownership of entry, don't delete it below 
1450 // ignore index on the grounds that we only have one icon in a Unix file 
1451 bool wxFileTypeImpl::SetDefaultIcon(const wxString
& strIcon
, int WXUNUSED(index
)) 
1453     if (strIcon
.empty()) 
1456     wxArrayString strExtensions
; 
1459     wxArrayString strTypes
; 
1460     GetMimeTypes(strTypes
); 
1461     if ( strTypes
.IsEmpty() ) 
1464     wxMimeTypeCommands 
*entry 
= new wxMimeTypeCommands(); 
1466     size_t nCount 
= strTypes
.GetCount(); 
1467     for ( size_t i 
= 0; i 
< nCount
; i
++ ) 
1469         if ( m_manager
->DoAssociation
 
1478             // we don't need to free entry now, DoAssociation() took ownership 
1490 // ---------------------------------------------------------------------------- 
1491 // wxMimeTypesManagerImpl (Unix) 
1492 // ---------------------------------------------------------------------------- 
1494 wxMimeTypesManagerImpl::wxMimeTypesManagerImpl() 
1496     m_initialized 
= false; 
1497     m_mailcapStylesInited 
= 0; 
1500 void wxMimeTypesManagerImpl::InitIfNeeded() 
1502     if ( !m_initialized 
) 
1504         // set the flag first to prevent recursion 
1505         m_initialized 
= true; 
1507         wxString wm 
= wxTheApp
->GetTraits()->GetDesktopEnvironment(); 
1509         if (wm 
== wxT("KDE")) 
1510             Initialize( wxMAILCAP_KDE  
); 
1511         else if (wm 
== wxT("GNOME")) 
1512             Initialize( wxMAILCAP_GNOME 
); 
1518 // read system and user mailcaps and other files 
1519 void wxMimeTypesManagerImpl::Initialize(int mailcapStyles
, 
1520                                         const wxString
& sExtraDir
) 
1522     // read mimecap amd mime.types 
1523     if ( (mailcapStyles 
& wxMAILCAP_NETSCAPE
) || 
1524          (mailcapStyles 
& wxMAILCAP_STANDARD
) ) 
1525         GetMimeInfo(sExtraDir
); 
1527     // read GNOME tables 
1528     if (mailcapStyles 
& wxMAILCAP_GNOME
) 
1529         GetGnomeMimeInfo(sExtraDir
); 
1531     // read KDE tables which are never installed on OpenVMS 
1533     if (mailcapStyles 
& wxMAILCAP_KDE
) 
1534         GetKDEMimeInfo(sExtraDir
); 
1537     m_mailcapStylesInited 
|= mailcapStyles
; 
1540 // clear data so you can read another group of WM files 
1541 void wxMimeTypesManagerImpl::ClearData() 
1545     m_aExtensions
.Clear(); 
1546     m_aDescriptions
.Clear(); 
1548     WX_CLEAR_ARRAY(m_aEntries
); 
1551     m_mailcapStylesInited 
= 0; 
1554 wxMimeTypesManagerImpl::~wxMimeTypesManagerImpl() 
1559 void wxMimeTypesManagerImpl::GetMimeInfo(const wxString
& sExtraDir
) 
1561     // read this for netscape or Metamail formats 
1563     // directories where we look for mailcap and mime.types by default 
1564     // used by netscape and pine and other mailers, using 2 different formats! 
1566     // (taken from metamail(1) sources) 
1568     // although RFC 1524 specifies the search path of 
1569     // /etc/:/usr/etc:/usr/local/etc only, it doesn't hurt to search in more 
1570     // places - OTOH, the RFC also says that this path can be changed with 
1571     // MAILCAPS environment variable (containing the colon separated full 
1572     // filenames to try) which is not done yet (TODO?) 
1574     wxString strHome 
= wxGetenv(wxT("HOME")); 
1577     dirs
.Add( strHome 
+ wxT("/.") ); 
1578     dirs
.Add( wxT("/etc/") ); 
1579     dirs
.Add( wxT("/usr/etc/") ); 
1580     dirs
.Add( wxT("/usr/local/etc/") ); 
1581     dirs
.Add( wxT("/etc/mail/") ); 
1582     dirs
.Add( wxT("/usr/public/lib/") ); 
1583     if (!sExtraDir
.empty()) 
1584         dirs
.Add( sExtraDir 
+ wxT("/") ); 
1587     size_t nDirs 
= dirs
.GetCount(); 
1588     for ( size_t nDir 
= 0; nDir 
< nDirs
; nDir
++ ) 
1591         file 
+= wxT("mailcap"); 
1592         if ( wxFile::Exists(file
) ) 
1598         file 
+= wxT("mime.types"); 
1599         if ( wxFile::Exists(file
) ) 
1600             ReadMimeTypes(file
); 
1604 bool wxMimeTypesManagerImpl::WriteToMimeTypes(int index
, bool delete_index
) 
1606     // check we have the right manager 
1607     if (! ( m_mailcapStylesInited 
& wxMAILCAP_STANDARD
) ) 
1611     wxString strHome 
= wxGetenv(wxT("HOME")); 
1613     // and now the users mailcap 
1614     wxString strUserMailcap 
= strHome 
+ wxT("/.mime.types"); 
1616     wxMimeTextFile file
; 
1617     if ( wxFile::Exists(strUserMailcap
) ) 
1619         bTemp 
= file
.Open(strUserMailcap
); 
1626         bTemp 
= file
.Create(strUserMailcap
); 
1632         // test for netscape's header and return false if its found 
1633         nIndex 
= file
.pIndexOf(wxT("#--Netscape")); 
1634         if (nIndex 
!= wxNOT_FOUND
) 
1636             wxFAIL_MSG(wxT("Error in .mime.types\nTrying to mix Netscape and Metamail formats\nFile not modified")); 
1640         // write it in alternative format 
1641         // get rid of unwanted entries 
1642         wxString strType 
= m_aTypes
[index
]; 
1643         nIndex 
= file
.pIndexOf(strType
); 
1645         // get rid of all the unwanted entries... 
1646         if (nIndex 
!= wxNOT_FOUND
) 
1647             file
.CommentLine(nIndex
); 
1651             // add the new entries in 
1652             wxString sTmp 
= strType
.Append( wxT(' '), 40 - strType
.Len() ); 
1653             sTmp 
+= m_aExtensions
[index
]; 
1657         bTemp 
= file
.Write(); 
1664 bool wxMimeTypesManagerImpl::WriteToNSMimeTypes(int index
, bool delete_index
) 
1666     //check we have the right managers 
1667     if (! ( m_mailcapStylesInited 
& wxMAILCAP_NETSCAPE
) ) 
1671     wxString strHome 
= wxGetenv(wxT("HOME")); 
1673     // and now the users mailcap 
1674     wxString strUserMailcap 
= strHome 
+ wxT("/.mime.types"); 
1676     wxMimeTextFile file
; 
1677     if ( wxFile::Exists(strUserMailcap
) ) 
1679         bTemp 
= file
.Open(strUserMailcap
); 
1686         bTemp 
= file
.Create(strUserMailcap
); 
1691         // write it in the format that Netscape uses 
1693         // test for netscape's header and insert if required... 
1694         // this is a comment so use true 
1695         nIndex 
= file
.pIndexOf(wxT("#--Netscape"), true); 
1696         if (nIndex 
== wxNOT_FOUND
) 
1698             // either empty file or metamail format 
1699             // at present we can't cope with mixed formats, so exit to preseve 
1700             // metamail entreies 
1701             if (file
.GetLineCount() > 0) 
1703                 wxFAIL_MSG(wxT(".mime.types File not in Netscape format\nNo entries written to\n.mime.types or to .mailcap")); 
1707             file
.InsertLine(wxT( "#--Netscape Communications Corporation MIME Information" ), 0); 
1711         wxString strType 
= wxT("type=") + m_aTypes
[index
]; 
1712         nIndex 
= file
.pIndexOf(strType
); 
1714         // get rid of all the unwanted entries... 
1715         if (nIndex 
!= wxNOT_FOUND
) 
1717             wxString sOld 
= file
[nIndex
]; 
1718             while ( (sOld
.Contains(wxT("\\"))) && (nIndex 
< (int) file
.GetLineCount()) ) 
1720                 file
.CommentLine(nIndex
); 
1721                 sOld 
= file
[nIndex
]; 
1723                 wxLogTrace(TRACE_MIME
, wxT("--- Deleting from mime.types line '%d %s' ---"), nIndex
, sOld
.c_str()); 
1728             if (nIndex 
< (int) file
.GetLineCount()) 
1729                 file
.CommentLine(nIndex
); 
1732             nIndex 
= (int) file
.GetLineCount(); 
1734         wxString sTmp 
= strType 
+ wxT(" \\"); 
1736             file
.InsertLine(sTmp
, nIndex
); 
1738         if ( ! m_aDescriptions
.Item(index
).empty() ) 
1740             sTmp 
= wxT("desc=\"") + m_aDescriptions
[index
]+ wxT("\" \\"); //.trim ?? 
1744                 file
.InsertLine(sTmp
, nIndex
); 
1748         wxString sExts 
= m_aExtensions
.Item(index
); 
1749         sTmp 
= wxT("exts=\"") + sExts
.Trim(false).Trim() + wxT("\""); 
1753             file
.InsertLine(sTmp
, nIndex
); 
1756         bTemp 
= file
.Write(); 
1763 bool wxMimeTypesManagerImpl::WriteToMailCap(int index
, bool delete_index
) 
1765     //check we have the right managers 
1766     if ( !( ( m_mailcapStylesInited 
& wxMAILCAP_NETSCAPE
) || 
1767             ( m_mailcapStylesInited 
& wxMAILCAP_STANDARD
) ) ) 
1771     wxString strHome 
= wxGetenv(wxT("HOME")); 
1773     // and now the users mailcap 
1774     wxString strUserMailcap 
= strHome 
+ wxT("/.mailcap"); 
1776     wxMimeTextFile file
; 
1777     if ( wxFile::Exists(strUserMailcap
) ) 
1779         bTemp 
= file
.Open(strUserMailcap
); 
1786         bTemp 
= file
.Create(strUserMailcap
); 
1791         // now got a file we can write to .... 
1792         wxMimeTypeCommands 
* entries 
= m_aEntries
[index
]; 
1794         wxString sCmd 
= entries
->GetCommandForVerb(wxT("open"), &iOpen
); 
1797         sTmp 
= m_aTypes
[index
]; 
1799         int nIndex 
= file
.pIndexOf(sTmp
); 
1801         // get rid of all the unwanted entries... 
1802         if (nIndex 
== wxNOT_FOUND
) 
1804             nIndex 
= (int) file
.GetLineCount(); 
1808             sOld 
= file
[nIndex
]; 
1809             wxLogTrace(TRACE_MIME
, wxT("--- Deleting from mailcap line '%d' ---"), nIndex
); 
1811             while ( (sOld
.Contains(wxT("\\"))) && (nIndex 
< (int) file
.GetLineCount()) ) 
1813                 file
.CommentLine(nIndex
); 
1814                 if (nIndex 
< (int) file
.GetLineCount()) 
1815                     sOld 
= sOld 
+ file
[nIndex
]; 
1819                 file
.GetLineCount()) file
.CommentLine(nIndex
); 
1822         sTmp 
+= wxT(";") + sCmd
; //includes wxT(" %s "); 
1824         // write it in the format that Netscape uses (default) 
1825         if (! ( m_mailcapStylesInited 
& wxMAILCAP_STANDARD 
) ) 
1828                 file
.InsertLine(sTmp
, nIndex
); 
1833             // write extended format 
1835             // TODO - FIX this code: 
1837             // sOld holds all the entries, but our data store only has some 
1838             // eg test= is not stored 
1840             // so far we have written the mimetype and command out 
1841             wxStringTokenizer 
sT(sOld
, wxT(";\\")); 
1842             if (sT
.CountTokens() > 2) 
1844                 // first one mimetype; second one command, rest unknown... 
1846                 s 
= sT
.GetNextToken(); 
1847                 s 
= sT
.GetNextToken(); 
1850                 s 
= sT
.GetNextToken(); 
1851                 while ( ! s
.empty() ) 
1853                     bool bKnownToken 
= false; 
1854                     if (s
.Contains(wxT("description="))) 
1856                     if (s
.Contains(wxT("x11-bitmap="))) 
1860                     size_t nCount 
= entries
->GetCount(); 
1861                     for (i
=0; i 
< nCount
; i
++) 
1863                         if (s
.Contains(entries
->GetVerb(i
))) 
1869                         sTmp 
+= wxT("; \\"); 
1870                         file
.InsertLine(sTmp
, nIndex
); 
1874                     s 
= sT
.GetNextToken(); 
1878             if (! m_aDescriptions
[index
].empty() ) 
1880                 sTmp 
+= wxT("; \\"); 
1881                 file
.InsertLine(sTmp
, nIndex
); 
1883                 sTmp 
= wxT("       description=\"") + m_aDescriptions
[index
] + wxT("\""); 
1886             if (! m_aIcons
[index
].empty() ) 
1888                 sTmp 
+= wxT("; \\"); 
1889                 file
.InsertLine(sTmp
, nIndex
); 
1891                 sTmp 
= wxT("       x11-bitmap=\"") + m_aIcons
[index
] + wxT("\""); 
1894             if ( entries
->GetCount() > 1 ) 
1897                 for (i
=0; i 
< entries
->GetCount(); i
++) 
1900                         sTmp 
+= wxT("; \\"); 
1901                         file
.InsertLine(sTmp
, nIndex
); 
1903                         sTmp 
= wxT("       ") + entries
->GetVerbCmd(i
); 
1907             file
.InsertLine(sTmp
, nIndex
); 
1911         bTemp 
= file
.Write(); 
1918 wxFileType 
* wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo
& ftInfo
) 
1922     wxString strType 
= ftInfo
.GetMimeType(); 
1923     wxString strDesc 
= ftInfo
.GetDescription(); 
1924     wxString strIcon 
= ftInfo
.GetIconFile(); 
1926     wxMimeTypeCommands 
*entry 
= new wxMimeTypeCommands(); 
1928     if ( ! ftInfo
.GetOpenCommand().empty()) 
1929         entry
->Add(wxT("open=")  + ftInfo
.GetOpenCommand() + wxT(" %s ")); 
1930     if ( ! ftInfo
.GetPrintCommand().empty()) 
1931         entry
->Add(wxT("print=") + ftInfo
.GetPrintCommand() + wxT(" %s ")); 
1933     // now find where these extensions are in the data store and remove them 
1934     wxArrayString sA_Exts 
= ftInfo
.GetExtensions(); 
1935     wxString sExt
, sExtStore
; 
1937     size_t nExtCount 
= sA_Exts
.GetCount(); 
1938     for (i
=0; i 
< nExtCount
; i
++) 
1940         sExt 
= sA_Exts
.Item(i
); 
1942         // clean up to just a space before and after 
1943         sExt
.Trim().Trim(false); 
1944         sExt 
= wxT(' ') + sExt 
+ wxT(' '); 
1945         size_t nCount 
= m_aExtensions
.GetCount(); 
1946         for (nIndex 
= 0; nIndex 
< nCount
; nIndex
++) 
1948             sExtStore 
= m_aExtensions
.Item(nIndex
); 
1949             if (sExtStore
.Replace(sExt
, wxT(" ") ) > 0) 
1950                 m_aExtensions
.Item(nIndex
) = sExtStore
; 
1954     if ( !DoAssociation(strType
, strIcon
, entry
, sA_Exts
, strDesc
) ) 
1957     return GetFileTypeFromMimeType(strType
); 
1960 bool wxMimeTypesManagerImpl::DoAssociation(const wxString
& strType
, 
1961                                            const wxString
& strIcon
, 
1962                                            wxMimeTypeCommands 
*entry
, 
1963                                            const wxArrayString
& strExtensions
, 
1964                                            const wxString
& strDesc
) 
1966     int nIndex 
= AddToMimeData(strType
, strIcon
, entry
, strExtensions
, strDesc
, true); 
1968     if ( nIndex 
== wxNOT_FOUND 
) 
1971     return WriteMimeInfo(nIndex
, false); 
1974 bool wxMimeTypesManagerImpl::WriteMimeInfo(int nIndex
, bool delete_mime 
) 
1978     if ( m_mailcapStylesInited 
& wxMAILCAP_STANDARD 
) 
1980         // write in metamail  format; 
1981         if (WriteToMimeTypes(nIndex
, delete_mime
) ) 
1982             if ( WriteToMailCap(nIndex
, delete_mime
) ) 
1986     if ( m_mailcapStylesInited 
& wxMAILCAP_NETSCAPE 
) 
1988         // write in netsacpe format; 
1989         if (WriteToNSMimeTypes(nIndex
, delete_mime
) ) 
1990             if ( WriteToMailCap(nIndex
, delete_mime
) ) 
1994     // Don't write GNOME files here as this is not 
1995     // allowed and simply doesn't work 
1997     if (m_mailcapStylesInited 
& wxMAILCAP_KDE
) 
1999         // write in KDE format; 
2000         if (WriteKDEMimeFile(nIndex
, delete_mime
) ) 
2007 int wxMimeTypesManagerImpl::AddToMimeData(const wxString
& strType
, 
2008                                           const wxString
& strIcon
, 
2009                                           wxMimeTypeCommands 
*entry
, 
2010                                           const wxArrayString
& strExtensions
, 
2011                                           const wxString
& strDesc
, 
2012                                           bool replaceExisting
) 
2016     // ensure mimetype is always lower case 
2017     wxString mimeType 
= strType
.Lower(); 
2019     // is this a known MIME type? 
2020     int nIndex 
= m_aTypes
.Index(mimeType
); 
2021     if ( nIndex 
== wxNOT_FOUND 
) 
2024         m_aTypes
.Add(mimeType
); 
2025         m_aIcons
.Add(strIcon
); 
2026         m_aEntries
.Add(entry 
? entry 
: new wxMimeTypeCommands
); 
2028         // change nIndex so we can use it below to add the extensions 
2029         m_aExtensions
.Add(wxEmptyString
); 
2030         nIndex 
= m_aExtensions
.size() - 1; 
2032         m_aDescriptions
.Add(strDesc
); 
2034     else // yes, we already have it 
2036         if ( replaceExisting 
) 
2038             // if new description change it 
2039             if ( !strDesc
.empty()) 
2040                 m_aDescriptions
[nIndex
] = strDesc
; 
2042             // if new icon change it 
2043             if ( !strIcon
.empty()) 
2044                 m_aIcons
[nIndex
] = strIcon
; 
2048                 delete m_aEntries
[nIndex
]; 
2049                 m_aEntries
[nIndex
] = entry
; 
2052         else // add data we don't already have ... 
2054             // if new description add only if none 
2055             if ( m_aDescriptions
[nIndex
].empty() ) 
2056                 m_aDescriptions
[nIndex
] = strDesc
; 
2058             // if new icon and no existing icon 
2059             if ( m_aIcons
[nIndex
].empty() ) 
2060                 m_aIcons
[nIndex
] = strIcon
; 
2062             // add any new entries... 
2065                 wxMimeTypeCommands 
*entryOld 
= m_aEntries
[nIndex
]; 
2067                 size_t count 
= entry
->GetCount(); 
2068                 for ( size_t i 
= 0; i 
< count
; i
++ ) 
2070                     const wxString
& verb 
= entry
->GetVerb(i
); 
2071                     if ( !entryOld
->HasVerb(verb
) ) 
2073                         entryOld
->AddOrReplaceVerb(verb
, entry
->GetCmd(i
)); 
2077                 // as we don't store it anywhere, it won't be deleted later as 
2078                 // usual -- do it immediately instead 
2084     // always add the extensions to this mimetype 
2085     wxString
& exts 
= m_aExtensions
[nIndex
]; 
2087     // add all extensions we don't have yet 
2089     size_t count 
= strExtensions
.GetCount(); 
2090     for ( size_t i 
= 0; i 
< count
; i
++ ) 
2092         ext 
= strExtensions
[i
]; 
2095         if ( exts
.Find(ext
) == wxNOT_FOUND 
) 
2101     // check data integrity 
2102     wxASSERT( m_aTypes
.GetCount() == m_aEntries
.GetCount() && 
2103               m_aTypes
.GetCount() == m_aExtensions
.GetCount() && 
2104               m_aTypes
.GetCount() == m_aIcons
.GetCount() && 
2105               m_aTypes
.GetCount() == m_aDescriptions
.GetCount() ); 
2110 wxFileType 
* wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString
& ext
) 
2117     size_t count 
= m_aExtensions
.GetCount(); 
2118     for ( size_t n 
= 0; n 
< count
; n
++ ) 
2120         wxStringTokenizer 
tk(m_aExtensions
[n
], wxT(' ')); 
2122         while ( tk
.HasMoreTokens() ) 
2124             // consider extensions as not being case-sensitive 
2125             if ( tk
.GetNextToken().IsSameAs(ext
, false /* no case */) ) 
2128                 wxFileType 
*fileType 
= new wxFileType
; 
2129                 fileType
->m_impl
->Init(this, n
); 
2139 wxFileType 
* wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString
& mimeType
) 
2143     wxFileType 
* fileType 
= NULL
; 
2144     // mime types are not case-sensitive 
2145     wxString 
mimetype(mimeType
); 
2146     mimetype
.MakeLower(); 
2148     // first look for an exact match 
2149     int index 
= m_aTypes
.Index(mimetype
); 
2150     if ( index 
!= wxNOT_FOUND 
) 
2152         fileType 
= new wxFileType
; 
2153         fileType
->m_impl
->Init(this, index
); 
2156     // then try to find "text/*" as match for "text/plain" (for example) 
2157     // NB: if mimeType doesn't contain '/' at all, BeforeFirst() will return 
2158     //     the whole string - ok. 
2160     index 
= wxNOT_FOUND
; 
2161     wxString strCategory 
= mimetype
.BeforeFirst(wxT('/')); 
2163     size_t nCount 
= m_aTypes
.GetCount(); 
2164     for ( size_t n 
= 0; n 
< nCount
; n
++ ) 
2166         if ( (m_aTypes
[n
].BeforeFirst(wxT('/')) == strCategory 
) && 
2167                 m_aTypes
[n
].AfterFirst(wxT('/')) == wxT("*") ) 
2174     if ( index 
!= wxNOT_FOUND 
) 
2176        // don't throw away fileType that was already found 
2178             fileType 
= new wxFileType
; 
2179         fileType
->m_impl
->Init(this, index
); 
2185 wxString 
wxMimeTypesManagerImpl::GetCommand(const wxString 
& verb
, size_t nIndex
) const 
2187     wxString command
, testcmd
, sV
, sTmp
; 
2188     sV 
= verb 
+ wxT("="); 
2190     // list of verb = command pairs for this mimetype 
2191     wxMimeTypeCommands 
* sPairs 
= m_aEntries 
[nIndex
]; 
2194     size_t nCount 
= sPairs
->GetCount(); 
2195     for ( i 
= 0; i 
< nCount
; i
++ ) 
2197         sTmp 
= sPairs
->GetVerbCmd (i
); 
2198         if ( sTmp
.Contains(sV
) ) 
2199             command 
= sTmp
.AfterFirst(wxT('=')); 
2205 void wxMimeTypesManagerImpl::AddFallback(const wxFileTypeInfo
& filetype
) 
2209     wxString extensions
; 
2210     const wxArrayString
& exts 
= filetype
.GetExtensions(); 
2211     size_t nExts 
= exts
.GetCount(); 
2212     for ( size_t nExt 
= 0; nExt 
< nExts
; nExt
++ ) 
2215             extensions 
+= wxT(' '); 
2217         extensions 
+= exts
[nExt
]; 
2220     AddMimeTypeInfo(filetype
.GetMimeType(), 
2222                     filetype
.GetDescription()); 
2224     AddMailcapInfo(filetype
.GetMimeType(), 
2225                    filetype
.GetOpenCommand(), 
2226                    filetype
.GetPrintCommand(), 
2228                    filetype
.GetDescription()); 
2231 void wxMimeTypesManagerImpl::AddMimeTypeInfo(const wxString
& strMimeType
, 
2232                                              const wxString
& strExtensions
, 
2233                                              const wxString
& strDesc
) 
2235     // reading mailcap may find image/* , while 
2236     // reading mime.types finds image/gif and no match is made 
2237     // this means all the get functions don't work  fix this 
2239     wxString sTmp 
= strExtensions
; 
2241     wxArrayString sExts
; 
2242     sTmp
.Trim().Trim(false); 
2244     while (!sTmp
.empty()) 
2246         sExts
.Add(sTmp
.AfterLast(wxT(' '))); 
2247         sTmp 
= sTmp
.BeforeLast(wxT(' ')); 
2250     AddToMimeData(strMimeType
, strIcon
, NULL
, sExts
, strDesc
, true); 
2253 void wxMimeTypesManagerImpl::AddMailcapInfo(const wxString
& strType
, 
2254                                             const wxString
& strOpenCmd
, 
2255                                             const wxString
& strPrintCmd
, 
2256                                             const wxString
& strTest
, 
2257                                             const wxString
& strDesc
) 
2261     wxMimeTypeCommands 
*entry 
= new wxMimeTypeCommands
; 
2262     entry
->Add(wxT("open=")  + strOpenCmd
); 
2263     entry
->Add(wxT("print=") + strPrintCmd
); 
2264     entry
->Add(wxT("test=")  + strTest
); 
2267     wxArrayString strExtensions
; 
2269     AddToMimeData(strType
, strIcon
, entry
, strExtensions
, strDesc
, true); 
2272 bool wxMimeTypesManagerImpl::ReadMimeTypes(const wxString
& strFileName
) 
2274     wxLogTrace(TRACE_MIME
, wxT("--- Parsing mime.types file '%s' ---"), 
2275                strFileName
.c_str()); 
2277     wxMimeTextFile 
file(strFileName
); 
2281     // the information we extract 
2282     wxString strMimeType
, strDesc
, strExtensions
; 
2284     size_t nLineCount 
= file
.GetLineCount(); 
2285     const wxChar 
*pc 
= NULL
; 
2286     for ( size_t nLine 
= 0; nLine 
< nLineCount
; nLine
++ ) 
2290             // now we're at the start of the line 
2291             pc 
= file
[nLine
].c_str(); 
2295             // we didn't finish with the previous line yet 
2300         while ( wxIsspace(*pc
) ) 
2303         // comment or blank line? 
2304         if ( *pc 
== wxT('#') || !*pc 
) 
2306             // skip the whole line 
2311         // detect file format 
2312         const wxChar 
*pEqualSign 
= wxStrchr(pc
, wxT('=')); 
2313         if ( pEqualSign 
== NULL 
) 
2318             // first field is mime type 
2319             for ( strMimeType
.Empty(); !wxIsspace(*pc
) && *pc 
!= wxT('\0'); pc
++ ) 
2325             while ( wxIsspace(*pc
) ) 
2328             // take all the rest of the string 
2331             // no description... 
2339             // the string on the left of '=' is the field name 
2340             wxString 
strLHS(pc
, pEqualSign 
- pc
); 
2343             for ( pc 
= pEqualSign 
+ 1; wxIsspace(*pc
); pc
++ ) 
2347             if ( *pc 
== wxT('"') ) 
2349                 // the string is quoted and ends at the matching quote 
2350                 pEnd 
= wxStrchr(++pc
, wxT('"')); 
2353                     wxLogWarning(wxT("Mime.types file %s, line %lu: unterminated quoted string."), 
2354                                  strFileName
.c_str(), nLine 
+ 1L); 
2359                 // unquoted string ends at the first space or at the end of 
2361                 for ( pEnd 
= pc
; *pEnd 
&& !wxIsspace(*pEnd
); pEnd
++ ) 
2365             // now we have the RHS (field value) 
2366             wxString 
strRHS(pc
, pEnd 
- pc
); 
2368             // check what follows this entry 
2369             if ( *pEnd 
== wxT('"') ) 
2375             for ( pc 
= pEnd
; wxIsspace(*pc
); pc
++ ) 
2378             // if there is something left, it may be either a '\\' to continue 
2379             // the line or the next field of the same entry 
2380             bool entryEnded 
= *pc 
== wxT('\0'); 
2381             bool nextFieldOnSameLine 
= false; 
2384                 nextFieldOnSameLine 
= ((*pc 
!= wxT('\\')) || (pc
[1] != wxT('\0'))); 
2387             // now see what we got 
2388             if ( strLHS 
== wxT("type") ) 
2390                 strMimeType 
= strRHS
; 
2392             else if ( strLHS
.StartsWith(wxT("desc")) ) 
2396             else if ( strLHS 
== wxT("exts") ) 
2398                 strExtensions 
= strRHS
; 
2400             else if ( strLHS 
== wxT("icon") ) 
2402                 // this one is simply ignored: it usually refers to Netscape 
2403                 // built in icons which are useless for us anyhow 
2405             else if ( !strLHS
.StartsWith(wxT("x-")) ) 
2407                 // we suppose that all fields starting with "X-" are 
2408                 // unregistered extensions according to the standard practice, 
2409                 // but it may be worth telling the user about other junk in 
2410                 // his mime.types file 
2411                 wxLogWarning(wxT("Unknown field in file %s, line %lu: '%s'."), 
2412                              strFileName
.c_str(), nLine 
+ 1L, strLHS
.c_str()); 
2417                 if ( !nextFieldOnSameLine 
) 
2419                 //else: don't reset it 
2421                 // as we don't reset strMimeType, the next field in this entry 
2422                 // will be interpreted correctly. 
2428         // depending on the format (Mosaic or Netscape) either space or comma 
2429         // is used to separate the extensions 
2430         strExtensions
.Replace(wxT(","), wxT(" ")); 
2432         // also deal with the leading dot 
2433         if ( !strExtensions
.empty() && strExtensions
[0u] == wxT('.') ) 
2435             strExtensions
.erase(0, 1); 
2438         wxLogTrace(TRACE_MIME
, wxT("mime.types: '%s' => '%s' (%s)"), 
2439                    strExtensions
.c_str(), 
2440                    strMimeType
.c_str(), 
2443         AddMimeTypeInfo(strMimeType
, strExtensions
, strDesc
); 
2445         // finished with this line 
2452 // ---------------------------------------------------------------------------- 
2453 // UNIX mailcap files parsing 
2454 // ---------------------------------------------------------------------------- 
2456 // the data for a single MIME type 
2457 struct MailcapLineData
 
2466     wxArrayString verbs
, 
2474     MailcapLineData() { testfailed 
= needsterminal 
= copiousoutput 
= false; } 
2477 // process a non-standard (i.e. not the first or second one) mailcap field 
2479 wxMimeTypesManagerImpl::ProcessOtherMailcapField(MailcapLineData
& data
, 
2480                                                  const wxString
& curField
) 
2482     if ( curField
.empty() ) 
2488     // is this something of the form foo=bar? 
2489     if ( curField
.find('=') != wxString::npos 
) 
2491         // split "LHS = RHS" in 2 
2492         wxString lhs 
= curField
.BeforeFirst(wxT('=')), 
2493                  rhs 
= curField
.AfterFirst(wxT('=')); 
2495         lhs
.Trim(true);     // from right 
2496         rhs
.Trim(false);    // from left 
2498         // it might be quoted 
2499         if ( !rhs
.empty() && rhs
[0u] == wxT('"') && rhs
.Last() == wxT('"') ) 
2501             rhs 
= rhs
.Mid(1, rhs
.length() - 2); 
2504         // is it a command verb or something else? 
2505         if ( lhs 
== wxT("test") ) 
2507             if ( wxSystem(rhs
) == 0 ) 
2510                 wxLogTrace(TRACE_MIME_TEST
, 
2511                            wxT("Test '%s' for mime type '%s' succeeded."), 
2512                            rhs
.c_str(), data
.type
.c_str()); 
2516                 wxLogTrace(TRACE_MIME_TEST
, 
2517                            wxT("Test '%s' for mime type '%s' failed, skipping."), 
2518                            rhs
.c_str(), data
.type
.c_str()); 
2520                 data
.testfailed 
= true; 
2523         else if ( lhs 
== wxT("desc") ) 
2527         else if ( lhs 
== wxT("x11-bitmap") ) 
2531         else if ( lhs 
== wxT("notes") ) 
2535         else // not a (recognized) special case, must be a verb (e.g. "print") 
2537             data
.verbs
.Add(lhs
); 
2538             data
.commands
.Add(rhs
); 
2541     else // '=' not found 
2543         // so it must be a simple flag 
2544         if ( curField 
== wxT("needsterminal") ) 
2546             data
.needsterminal 
= true; 
2548         else if ( curField 
== wxT("copiousoutput")) 
2550             // copiousoutput impies that the viewer is a console program 
2551             data
.needsterminal 
= 
2552             data
.copiousoutput 
= true; 
2554         else if ( !IsKnownUnimportantField(curField
) ) 
2563 bool wxMimeTypesManagerImpl::ReadMailcap(const wxString
& strFileName
, 
2566     wxLogTrace(TRACE_MIME
, wxT("--- Parsing mailcap file '%s' ---"), 
2567                strFileName
.c_str()); 
2569     wxMimeTextFile 
file(strFileName
); 
2573     // indices of MIME types (in m_aTypes) we already found in this file 
2575     // (see the comments near the end of function for the reason we need this) 
2576     wxArrayInt aIndicesSeenHere
; 
2578     // accumulator for the current field 
2580     curField
.reserve(1024); 
2582     const wxChar 
*pPagerEnv 
= wxGetenv(wxT("PAGER")); 
2584     const wxArrayString empty_extensions_list
; 
2586     size_t nLineCount 
= file
.GetLineCount(); 
2587     for ( size_t nLine 
= 0; nLine 
< nLineCount
; nLine
++ ) 
2589         // now we're at the start of the line 
2590         const wxChar 
*pc 
= file
[nLine
].c_str(); 
2593         while ( wxIsspace(*pc
) ) 
2596         // comment or empty string? 
2597         if ( *pc 
== wxT('#') || *pc 
== wxT('\0') ) 
2603         // what field are we currently in? The first 2 are fixed and there may 
2604         // be an arbitrary number of other fields parsed by 
2605         // ProcessOtherMailcapField() 
2607         // the first field is the MIME type 
2613         } currentToken 
= Field_Type
; 
2615         // the flags and field values on the current line 
2616         MailcapLineData data
; 
2624                     // interpret the next character literally (notice that 
2625                     // backslash can be used for line continuation) 
2626                     if ( *++pc 
== wxT('\0') ) 
2628                         // fetch the next line if there is one 
2629                         if ( nLine 
== nLineCount 
- 1 ) 
2631                             // something is wrong, bail out 
2634                             wxLogDebug(wxT("Mailcap file %s, line %lu: '\\' on the end of the last line ignored."), 
2635                                        strFileName
.c_str(), 
2640                             // pass to the beginning of the next line 
2641                             pc 
= file
[++nLine
].c_str(); 
2643                             // skip pc++ at the end of the loop 
2649                         // just a normal character 
2655                     cont 
= false;   // end of line reached, exit the loop 
2657                     // fall through to still process this field 
2660                     // trim whitespaces from both sides 
2661                     curField
.Trim(true).Trim(false); 
2663                     switch ( currentToken 
) 
2666                             data
.type 
= curField
.Lower(); 
2667                             if ( data
.type
.empty() ) 
2669                                 // I don't think that this is a valid mailcap 
2670                                 // entry, but try to interpret it somehow 
2671                                 data
.type 
= wxT('*'); 
2674                             if ( data
.type
.Find(wxT('/')) == wxNOT_FOUND 
) 
2676                                 // we interpret "type" as "type/*" 
2677                                 data
.type 
+= wxT("/*"); 
2680                             currentToken 
= Field_OpenCmd
; 
2684                             data
.cmdOpen 
= curField
; 
2686                             currentToken 
= Field_Other
; 
2690                             if ( !ProcessOtherMailcapField(data
, curField
) ) 
2692                                 // don't flood the user with error messages if 
2693                                 // we don't understand something in his 
2694                                 // mailcap, but give them in debug mode because 
2695                                 // this might be useful for the programmer 
2698                                     wxT("Mailcap file %s, line %lu: unknown field '%s' for the MIME type '%s' ignored."), 
2699                                     strFileName
.c_str(), 
2705                             else if ( data
.testfailed 
) 
2707                                 // skip this entry entirely 
2711                             // it already has this value 
2712                             //currentToken = Field_Other; 
2716                             wxFAIL_MSG(wxT("unknown field type in mailcap")); 
2719                     // next token starts immediately after ';' 
2727             // continue in the same line 
2731         // we read the entire entry, check what have we got 
2732         // ------------------------------------------------ 
2734         // check that we really read something reasonable 
2735         if ( currentToken 
< Field_Other 
) 
2737             wxLogWarning(wxT("Mailcap file %s, line %lu: incomplete entry ignored."), 
2738                          strFileName
.c_str(), nLine 
+ 1L); 
2743         // if the test command failed, it's as if the entry were not there at all 
2744         if ( data
.testfailed 
) 
2749         // support for flags: 
2750         //  1. create an xterm for 'needsterminal' 
2751         //  2. append "| $PAGER" for 'copiousoutput' 
2753         // Note that the RFC says that having both needsterminal and 
2754         // copiousoutput is probably a mistake, so it seems that running 
2755         // programs with copiousoutput inside an xterm as it is done now 
2756         // is a bad idea (FIXME) 
2757         if ( data
.copiousoutput 
) 
2759             data
.cmdOpen 
<< wxT(" | ") << (pPagerEnv 
? pPagerEnv 
: wxT("more")); 
2762         if ( data
.needsterminal 
) 
2764             data
.cmdOpen
.insert(0, wxT("xterm -e sh -c '")); 
2765             data
.cmdOpen
.append(wxT("'")); 
2768         if ( !data
.cmdOpen
.empty() ) 
2770             data
.verbs
.Insert(wxT("open"), 0); 
2771             data
.commands
.Insert(data
.cmdOpen
, 0); 
2774         // we have to decide whether the new entry should replace any entries 
2775         // for the same MIME type we had previously found or not 
2778         // the fall back entries have the lowest priority, by definition 
2785             // have we seen this one before? 
2786             int nIndex 
= m_aTypes
.Index(data
.type
); 
2788             // and if we have, was it in this file? if not, we should 
2789             // overwrite the previously seen one 
2790             overwrite 
= nIndex 
== wxNOT_FOUND 
|| 
2791                             aIndicesSeenHere
.Index(nIndex
) == wxNOT_FOUND
; 
2794         wxLogTrace(TRACE_MIME
, wxT("mailcap %s: %s [%s]"), 
2795                    data
.type
.c_str(), data
.cmdOpen
.c_str(), 
2796                    overwrite 
? wxT("replace") : wxT("add")); 
2798         int n 
= AddToMimeData
 
2802                     new wxMimeTypeCommands(data
.verbs
, data
.commands
), 
2803                     empty_extensions_list
, 
2810             aIndicesSeenHere
.Add(n
); 
2817 size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString
& mimetypes
) 
2823     size_t count 
= m_aTypes
.GetCount(); 
2824     for ( size_t n 
= 0; n 
< count
; n
++ ) 
2826         // don't return template types from here (i.e. anything containg '*') 
2827         const wxString 
&type 
= m_aTypes
[n
]; 
2828         if ( type
.Find(wxT('*')) == wxNOT_FOUND 
) 
2830             mimetypes
.Add(type
); 
2834     return mimetypes
.GetCount(); 
2837 // ---------------------------------------------------------------------------- 
2838 // writing to MIME type files 
2839 // ---------------------------------------------------------------------------- 
2841 bool wxMimeTypesManagerImpl::Unassociate(wxFileType 
*ft
) 
2845     wxArrayString sMimeTypes
; 
2846     ft
->GetMimeTypes(sMimeTypes
); 
2849     size_t nCount 
= sMimeTypes
.GetCount(); 
2850     for (i 
= 0; i 
< nCount
; i 
++) 
2852         const wxString 
&sMime 
= sMimeTypes
.Item(i
); 
2853         int nIndex 
= m_aTypes
.Index(sMime
); 
2854         if ( nIndex 
== wxNOT_FOUND
) 
2856             // error if we get here ?? 
2861             WriteMimeInfo(nIndex
, true); 
2862             m_aTypes
.RemoveAt(nIndex
); 
2863             m_aEntries
.RemoveAt(nIndex
); 
2864             m_aExtensions
.RemoveAt(nIndex
); 
2865             m_aDescriptions
.RemoveAt(nIndex
); 
2866             m_aIcons
.RemoveAt(nIndex
); 
2869     // check data integrity 
2870     wxASSERT( m_aTypes
.GetCount() == m_aEntries
.GetCount() && 
2871             m_aTypes
.GetCount() == m_aExtensions
.GetCount() && 
2872             m_aTypes
.GetCount() == m_aIcons
.GetCount() && 
2873             m_aTypes
.GetCount() == m_aDescriptions
.GetCount() ); 
2878 // ---------------------------------------------------------------------------- 
2879 // private functions 
2880 // ---------------------------------------------------------------------------- 
2882 static bool IsKnownUnimportantField(const wxString
& fieldAll
) 
2884     static const wxChar 
* const knownFields
[] = 
2886         wxT("x-mozilla-flags"), 
2887         wxT("nametemplate"), 
2888         wxT("textualnewlines"), 
2891     wxString field 
= fieldAll
.BeforeFirst(wxT('=')); 
2892     for ( size_t n 
= 0; n 
< WXSIZEOF(knownFields
); n
++ ) 
2894         if ( field
.CmpNoCase(knownFields
[n
]) == 0 ) 
2902   // wxUSE_MIMETYPE && wxUSE_FILE && wxUSE_TEXTFILE