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 // ---------------------------------------------------------------------------- 
 148 // ---------------------------------------------------------------------------- 
 150 // MIME code tracing mask 
 151 #define TRACE_MIME wxT("mime") 
 153 // give trace messages about the results of mailcap tests 
 154 #define TRACE_MIME_TEST wxT("mimetest") 
 156 // ---------------------------------------------------------------------------- 
 158 // ---------------------------------------------------------------------------- 
 160 // there are some fields which we don't understand but for which we don't give 
 161 // warnings as we know that they're not important - this function is used to 
 163 static bool IsKnownUnimportantField(const wxString
& field
); 
 165 // ---------------------------------------------------------------------------- 
 167 // ---------------------------------------------------------------------------- 
 170 // This class uses both mailcap and mime.types to gather information about file 
 173 // The information about mailcap file was extracted from metamail(1) sources 
 174 // and documentation and subsequently revised when I found the RFC 1524 
 177 // Format of mailcap file: spaces are ignored, each line is either a comment 
 178 // (starts with '#') or a line of the form <field1>;<field2>;...;<fieldN>. 
 179 // A backslash can be used to quote semicolons and newlines (and, in fact, 
 180 // anything else including itself). 
 182 // The first field is always the MIME type in the form of type/subtype (see RFC 
 183 // 822) where subtype may be '*' meaning "any". Following metamail, we accept 
 184 // "type" which means the same as "type/*", although I'm not sure whether this 
 187 // The second field is always the command to run. It is subject to 
 188 // parameter/filename expansion described below. 
 190 // All the following fields are optional and may not be present at all. If 
 191 // they're present they may appear in any order, although each of them should 
 192 // appear only once. The optional fields are the following: 
 193 //  * notes=xxx is an uninterpreted string which is silently ignored 
 194 //  * test=xxx is the command to be used to determine whether this mailcap line 
 195 //    applies to our data or not. The RHS of this field goes through the 
 196 //    parameter/filename expansion (as the 2nd field) and the resulting string 
 197 //    is executed. The line applies only if the command succeeds, i.e. returns 0 
 199 //  * print=xxx is the command to be used to print (and not view) the data of 
 200 //    this type (parameter/filename expansion is done here too) 
 201 //  * edit=xxx is the command to open/edit the data of this type 
 202 //  * needsterminal means that a new interactive console must be created for 
 204 //  * copiousoutput means that the viewer doesn't interact with the user but 
 205 //    produces (possibly) a lof of lines of output on stdout (i.e. "cat" is a 
 206 //    good example), thus it might be a good idea to use some kind of paging 
 208 //  * textualnewlines means not to perform CR/LF translation (not honored) 
 209 //  * compose and composetyped fields are used to determine the program to be 
 210 //    called to create a new message pert in the specified format (unused). 
 212 // Parameter/filename expansion: 
 213 //  * %s is replaced with the (full) file name 
 214 //  * %t is replaced with MIME type/subtype of the entry 
 215 //  * for multipart type only %n is replaced with the nnumber of parts and %F is 
 216 //    replaced by an array of (content-type, temporary file name) pairs for all 
 217 //    message parts (TODO) 
 218 //  * %{parameter} is replaced with the value of parameter taken from 
 219 //    Content-type header line of the message. 
 222 // There are 2 possible formats for mime.types file, one entry per line (used 
 223 // for global mime.types and called Mosaic format) and "expanded" format where 
 224 // an entry takes multiple lines (used for users mime.types and called 
 227 // For both formats spaces are ignored and lines starting with a '#' are 
 228 // comments. Each record has one of two following forms: 
 229 //  a) for "brief" format: 
 230 //      <mime type>  <space separated list of extensions> 
 231 //  b) for "expanded" format: 
 232 //      type=<mime type> BACKSLASH 
 233 //      desc="<description>" BACKSLASH 
 234 //      exts="<comma separated list of extensions>" 
 236 // (where BACKSLASH is a literal '\\' which we can't put here because cpp 
 239 // We try to autodetect the format of mime.types: if a non-comment line starts 
 240 // with "type=" we assume the second format, otherwise the first one. 
 242 // there may be more than one entry for one and the same mime type, to 
 243 // choose the right one we have to run the command specified in the test 
 244 // field on our data. 
 246 // ---------------------------------------------------------------------------- 
 248 // ---------------------------------------------------------------------------- 
 250 // GNOME stores the info we're interested in in several locations: 
 251 //  1. xxx.keys files under /usr/share/mime-info 
 252 //  2. xxx.keys files under ~/.gnome/mime-info 
 254 // Update (Chris Elliott): apparently there may be an optional "[lang]" prefix 
 255 // just before the field name. 
 258 void wxMimeTypesManagerImpl::LoadGnomeDataFromKeyFile(const wxString
& filename
, 
 259                                                       const wxArrayString
& dirs
) 
 261     wxMimeTextFile 
textfile(filename
); 
 262     if ( !textfile
.Open() ) 
 265     wxLogTrace(TRACE_MIME
, wxT("--- Opened Gnome file %s  ---"), 
 268     wxArrayString 
search_dirs( dirs 
); 
 270     // values for the entry being parsed 
 271     wxString curMimeType
, curIconFile
; 
 272     wxMimeTypeCommands 
* entry 
= new wxMimeTypeCommands
; 
 274     wxArrayString strExtensions
; 
 278     size_t nLineCount 
= textfile
.GetLineCount(); 
 280     while ( nLine 
< nLineCount 
) 
 282         pc 
= textfile
[nLine
].c_str(); 
 283         if ( *pc 
!= wxT('#') ) 
 286             wxLogTrace(TRACE_MIME
, wxT("--- Reading from Gnome file %s '%s' ---"), 
 287                     filename
.c_str(), pc
); 
 289             // trim trailing space and tab 
 290             while ((*pc 
== wxT(' ')) || (*pc 
== wxT('\t'))) 
 294             int equal_pos 
= sTmp
.Find( wxT('=') ); 
 297                 wxString left_of_equal 
= sTmp
.Left( equal_pos 
); 
 298                 const wxChar 
*right_of_equal 
= pc
; 
 299                 right_of_equal 
+= equal_pos
+1; 
 301                 if (left_of_equal 
== wxT("icon_filename")) 
 304                     curIconFile 
= right_of_equal
; 
 306                     wxFileName 
newFile( curIconFile 
); 
 307                     if (newFile
.IsRelative() || newFile
.FileExists()) 
 309                         size_t nDirs 
= search_dirs
.GetCount(); 
 311                         for (size_t nDir 
= 0; nDir 
< nDirs
; nDir
++) 
 313                             newFile
.SetPath( search_dirs
[nDir
] ); 
 314                             newFile
.AppendDir( wxT("pixmaps") ); 
 315                             newFile
.AppendDir( wxT("document-icons") ); 
 316                             newFile
.SetExt( wxT("png") ); 
 317                             if (newFile
.FileExists()) 
 319                                 curIconFile 
= newFile
.GetFullPath(); 
 320                                 // reorder search_dirs for speedup (fewer 
 321                                 // calls to FileExist() required) 
 324                                     const wxString 
&tmp 
= search_dirs
[nDir
]; 
 325                                     search_dirs
.RemoveAt( nDir 
); 
 326                                     search_dirs
.Insert( tmp
, 0 ); 
 333                 else if (left_of_equal 
== wxT("open")) 
 335                     sTmp 
= right_of_equal
; 
 336                     sTmp
.Replace( wxT("%f"), wxT("%s") ); 
 337                     sTmp
.Prepend( wxT("open=") ); 
 340                 else if (left_of_equal 
== wxT("view")) 
 342                     sTmp 
= right_of_equal
; 
 343                     sTmp
.Replace( wxT("%f"), wxT("%s") ); 
 344                     sTmp
.Prepend( wxT("view=") ); 
 347                 else if (left_of_equal 
== wxT("print")) 
 349                     sTmp 
= right_of_equal
; 
 350                     sTmp
.Replace( wxT("%f"), wxT("%s") ); 
 351                     sTmp
.Prepend( wxT("print=") ); 
 354                 else if (left_of_equal 
== wxT("description")) 
 356                     strDesc 
= right_of_equal
; 
 358                 else if (left_of_equal 
== wxT("short_list_application_ids_for_novice_user_level")) 
 360                     sTmp 
= right_of_equal
; 
 361                     if (sTmp
.Contains( wxT(",") )) 
 362                         sTmp 
= sTmp
.BeforeFirst( wxT(',') ); 
 363                     sTmp
.Prepend( wxT("open=") ); 
 364                     sTmp
.Append( wxT(" %s") ); 
 368             } // emd of has an equals sign 
 371                 // not a comment and not an equals sign 
 372                 if (sTmp
.Contains(wxT('/'))) 
 374                     // this is the start of the new mimetype 
 375                     // overwrite any existing data 
 376                     if (! curMimeType
.empty()) 
 378                         AddToMimeData( curMimeType
, curIconFile
, entry
, strExtensions
, strDesc 
); 
 380                         // now get ready for next bit 
 381                         entry 
= new wxMimeTypeCommands
; 
 384                     curMimeType 
= sTmp
.BeforeFirst(wxT(':')); 
 387         } // end of not a comment 
 389         // ignore blank lines 
 391     } // end of while, save any data 
 393     if ( curMimeType
.empty() ) 
 396         AddToMimeData( curMimeType
, curIconFile
, entry
, strExtensions
, strDesc
); 
 399 void wxMimeTypesManagerImpl::LoadGnomeMimeTypesFromMimeFile(const wxString
& filename
) 
 401     wxMimeTextFile 
textfile(filename
); 
 402     if ( !textfile
.Open() ) 
 405     wxLogTrace(TRACE_MIME
, 
 406                wxT("--- Opened Gnome file %s  ---"), 
 409     // values for the entry being parsed 
 410     wxString curMimeType
, curExtList
; 
 413     size_t nLineCount 
= textfile
.GetLineCount(); 
 414     for ( size_t nLine 
= 0; /* nothing */; nLine
++ ) 
 416         if ( nLine 
< nLineCount 
) 
 418             pc 
= textfile
[nLine
].c_str(); 
 419             if ( *pc 
== wxT('#') ) 
 427             // so that we will fall into the "if" below 
 434             if ( !curMimeType
.empty() && !curExtList
.empty() ) 
 436                  wxLogTrace(TRACE_MIME
, 
 437                             wxT("--- At end of Gnome file  finding mimetype %s  ---"), 
 438                             curMimeType
.c_str()); 
 440                  AddMimeTypeInfo(curMimeType
, curExtList
, wxEmptyString
); 
 445                 // the end: this can only happen if nLine == nLineCount 
 454         // what do we have here? 
 455         if ( *pc 
== wxT('\t') ) 
 457             // this is a field=value ling 
 458             pc
++; // skip leading TAB 
 460             static const int lenField 
= 5; // strlen("ext: ") 
 461             if ( wxStrncmp(pc
, wxT("ext: "), lenField
) == 0 ) 
 463                 // skip it and take everything left until the end of line 
 464                 curExtList 
= pc 
+ lenField
; 
 466             //else: some other field, we don't care 
 470             // this is the start of the new section 
 471             wxLogTrace(TRACE_MIME
, 
 472                        wxT("--- In Gnome file  finding mimetype %s  ---"), 
 473                        curMimeType
.c_str()); 
 475             if (! curMimeType
.empty()) 
 476                 AddMimeTypeInfo(curMimeType
, curExtList
, wxEmptyString
); 
 480             while ( *pc 
!= wxT(':') && *pc 
!= wxT('\0') ) 
 482                 curMimeType 
+= *pc
++; 
 489 void wxMimeTypesManagerImpl::LoadGnomeMimeFilesFromDir( 
 490                       const wxString
& dirbase
, const wxArrayString
& dirs
) 
 492     wxASSERT_MSG( !dirbase
.empty() && !wxEndsWithPathSeparator(dirbase
), 
 493                   wxT("base directory shouldn't end with a slash") ); 
 495     wxString dirname 
= dirbase
; 
 496     dirname 
<< wxT("/mime-info"); 
 498     if ( !wxDir::Exists(dirname
) ) 
 502     if ( !dir
.IsOpened() ) 
 505     // we will concatenate it with filename to get the full path below 
 511     cont 
= dir
.GetFirst(&filename
, wxT("*.mime"), wxDIR_FILES
); 
 514         LoadGnomeMimeTypesFromMimeFile(dirname 
+ filename
); 
 516         cont 
= dir
.GetNext(&filename
); 
 519     cont 
= dir
.GetFirst(&filename
, wxT("*.keys"), wxDIR_FILES
); 
 522         LoadGnomeDataFromKeyFile(dirname 
+ filename
, dirs
); 
 524         cont 
= dir
.GetNext(&filename
); 
 527     // FIXME: Hack alert: We scan all icons and deduce the 
 528     //             mime-type from the file name. 
 530     dirname 
<< wxT("/pixmaps/document-icons"); 
 532     // these are always empty in this file 
 533     wxArrayString strExtensions
; 
 536     if ( !wxDir::Exists(dirname
) ) 
 538         // Just test for default GPE dir also 
 539         dirname 
= wxT("/usr/share/gpe/pixmaps/default/filemanager/document-icons"); 
 541         if ( !wxDir::Exists(dirname
) ) 
 545     wxDir 
dir2( dirname 
); 
 547     cont 
= dir2
.GetFirst(&filename
, wxT("gnome-*.png"), wxDIR_FILES
); 
 550         wxString mimeType 
= filename
; 
 551         mimeType
.Remove( 0, 6 ); // remove "gnome-" 
 552         mimeType
.Remove( mimeType
.Len() - 4, 4 ); // remove ".png" 
 553         int pos 
= mimeType
.Find( wxT("-") ); 
 554         if (pos 
!= wxNOT_FOUND
) 
 556             mimeType
.SetChar( pos
, wxT('/') ); 
 557             wxString iconFile 
= dirname
; 
 558             iconFile 
<< wxT("/"); 
 559             iconFile 
<< filename
; 
 560             AddToMimeData( mimeType
, iconFile
, NULL
, strExtensions
, strDesc
, true ); 
 563         cont 
= dir2
.GetNext(&filename
); 
 567 void wxMimeTypesManagerImpl::GetGnomeMimeInfo(const wxString
& sExtraDir
) 
 571     wxString gnomedir 
= wxGetenv( wxT("GNOMEDIR") ); 
 572     if (!gnomedir
.empty()) 
 574         gnomedir 
<< wxT("/share"); 
 575         dirs
.Add( gnomedir 
); 
 578     dirs
.Add(wxT("/usr/share")); 
 579     dirs
.Add(wxT("/usr/local/share")); 
 581     gnomedir 
= wxGetHomeDir(); 
 582     gnomedir 
<< wxT("/.gnome"); 
 583     dirs
.Add( gnomedir 
); 
 585     if (!sExtraDir
.empty()) 
 586         dirs
.Add( sExtraDir 
); 
 588     size_t nDirs 
= dirs
.GetCount(); 
 589     for ( size_t nDir 
= 0; nDir 
< nDirs
; nDir
++ ) 
 591         LoadGnomeMimeFilesFromDir(dirs
[nDir
], dirs
); 
 595 // ---------------------------------------------------------------------------- 
 597 // ---------------------------------------------------------------------------- 
 600 // KDE stores the icon info in its .kdelnk files. The file for mimetype/subtype 
 601 // may be found in either of the following locations 
 603 //  1. $KDEDIR/share/mimelnk/mimetype/subtype.kdelnk 
 604 //  2. ~/.kde/share/mimelnk/mimetype/subtype.kdelnk 
 606 // The format of a .kdelnk file is almost the same as the one used by 
 607 // wxFileConfig, i.e. there are groups, comments and entries. The icon is the 
 608 // value for the entry "Type" 
 610 // kde writing; see http://webcvs.kde.org/cgi-bin/cvsweb.cgi/~checkout~/kdelibs/kio/DESKTOP_ENTRY_STANDARD 
 611 // for now write to .kdelnk but should eventually do .desktop instead (in preference??) 
 613 bool wxMimeTypesManagerImpl::CheckKDEDirsExist( const wxString 
&sOK
, const wxString 
&sTest 
) 
 617         return wxDir::Exists(sOK
); 
 621         wxString sStart 
= sOK 
+ wxT("/") + sTest
.BeforeFirst(wxT('/')); 
 622         if (!wxDir::Exists(sStart
)) 
 624         wxString sEnd 
= sTest
.AfterFirst(wxT('/')); 
 625         return CheckKDEDirsExist(sStart
, sEnd
); 
 629 bool wxMimeTypesManagerImpl::WriteKDEMimeFile(int index
, bool delete_index
) 
 631     wxMimeTextFile appoutfile
, mimeoutfile
; 
 632     wxString sHome 
= wxGetHomeDir(); 
 633     wxString sTmp 
= wxT(".kde/share/mimelnk/"); 
 634     wxString sMime 
= m_aTypes
[index
]; 
 635     CheckKDEDirsExist(sHome
, sTmp 
+ sMime
.BeforeFirst(wxT('/')) ); 
 636     sTmp 
= sHome 
+ wxT('/') + sTmp 
+ sMime 
+ wxT(".kdelnk"); 
 639     bool bMimeExists 
= mimeoutfile
.Open(sTmp
); 
 642         bTemp 
= mimeoutfile
.Create(sTmp
); 
 643         // some unknown error eg out of disk space 
 648     sTmp 
= wxT(".kde/share/applnk/"); 
 649     CheckKDEDirsExist(sHome
, sTmp 
+ sMime
.AfterFirst(wxT('/')) ); 
 650     sTmp 
= sHome 
+ wxT('/') + sTmp 
+ sMime
.AfterFirst(wxT('/')) + wxT(".kdelnk"); 
 653     bAppExists 
= appoutfile
.Open(sTmp
); 
 656         bTemp 
= appoutfile
.Create(sTmp
); 
 657         // some unknown error eg out of disk space 
 662     // fixed data; write if new file 
 665         mimeoutfile
.AddLine(wxT("#KDE Config File")); 
 666         mimeoutfile
.AddLine(wxT("[KDE Desktop Entry]")); 
 667         mimeoutfile
.AddLine(wxT("Version=1.0")); 
 668         mimeoutfile
.AddLine(wxT("Type=MimeType")); 
 669         mimeoutfile
.AddLine(wxT("MimeType=") + sMime
); 
 674         mimeoutfile
.AddLine(wxT("#KDE Config File")); 
 675         mimeoutfile
.AddLine(wxT("[KDE Desktop Entry]")); 
 676         appoutfile
.AddLine(wxT("Version=1.0")); 
 677         appoutfile
.AddLine(wxT("Type=Application")); 
 678         appoutfile
.AddLine(wxT("MimeType=") + sMime 
+ wxT(';')); 
 683     mimeoutfile
.CommentLine(wxT("Comment=")); 
 685         mimeoutfile
.AddLine(wxT("Comment=") + m_aDescriptions
[index
]); 
 686     appoutfile
.CommentLine(wxT("Name=")); 
 688         appoutfile
.AddLine(wxT("Comment=") + m_aDescriptions
[index
]); 
 690     sTmp 
= m_aIcons
[index
]; 
 691     // we can either give the full path, or the shortfilename if its in 
 692     // one of the directories we search 
 693     mimeoutfile
.CommentLine(wxT("Icon=") ); 
 695         mimeoutfile
.AddLine(wxT("Icon=") + sTmp 
); 
 696     appoutfile
.CommentLine(wxT("Icon=") ); 
 698         appoutfile
.AddLine(wxT("Icon=") + sTmp 
); 
 700     sTmp 
= wxT(" ") + m_aExtensions
[index
]; 
 702     wxStringTokenizer 
tokenizer(sTmp
, wxT(" ")); 
 703     sTmp 
= wxT("Patterns="); 
 704     mimeoutfile
.CommentLine(sTmp
); 
 705     while ( tokenizer
.HasMoreTokens() ) 
 707         // holds an extension; need to change it to *.ext; 
 708         wxString e 
= wxT("*.") + tokenizer
.GetNextToken() + wxT(";"); 
 713         mimeoutfile
.AddLine(sTmp
); 
 715     wxMimeTypeCommands 
* entries 
= m_aEntries
[index
]; 
 716     // if we don't find open just have an empty string ... FIX this 
 717     sTmp 
= entries
->GetCommandForVerb(wxT("open")); 
 718     sTmp
.Replace( wxT("%s"), wxT("%f") ); 
 720     mimeoutfile
.CommentLine(wxT("DefaultApp=") ); 
 722         mimeoutfile
.AddLine(wxT("DefaultApp=") + sTmp
); 
 724     sTmp
.Replace( wxT("%f"), wxT("") ); 
 725     appoutfile
.CommentLine(wxT("Exec=")); 
 727         appoutfile
.AddLine(wxT("Exec=") + sTmp
); 
 729     if (entries
->GetCount() > 1) 
 731         //other actions as well as open 
 735     if (mimeoutfile
.Write()) 
 738     if (appoutfile
.Write()) 
 745 void wxMimeTypesManagerImpl::LoadKDELinksForMimeSubtype(const wxString
& dirbase
, 
 746                                                const wxString
& subdir
, 
 747                                                const wxString
& filename
, 
 748                                                const wxArrayString
& icondirs
) 
 750     wxFileName 
fullname(dirbase
, filename
); 
 751     wxLogTrace(TRACE_MIME
, wxT("loading KDE file %s"), 
 752                            fullname
.GetFullPath().c_str()); 
 755     if ( !file
.Open(fullname
.GetFullPath()) ) 
 758     wxMimeTypeCommands 
* entry 
= new wxMimeTypeCommands
; 
 760     wxString mimetype
, mime_desc
, strIcon
; 
 762     int nIndex 
= file
.pIndexOf( wxT("MimeType=") ); 
 763     if (nIndex 
== wxNOT_FOUND
) 
 765         // construct mimetype from the directory name and the basename of the 
 766         // file (it always has .kdelnk extension) 
 767         mimetype 
<< subdir 
<< wxT('/') << filename
.BeforeLast( wxT('.') ); 
 770         mimetype 
= file
.GetCmd(nIndex
); 
 772     // first find the description string: it is the value in either "Comment=" 
 773     // line or "Comment[<locale_name>]=" one 
 774     nIndex 
= wxNOT_FOUND
; 
 779     wxLocale 
*locale 
= wxGetLocale(); 
 782         // try "Comment[locale name]" first 
 783         comment 
<< wxT("Comment[") + locale
->GetName() + wxT("]="); 
 784         nIndex 
= file
.pIndexOf(comment
); 
 788     if ( nIndex 
== wxNOT_FOUND 
) 
 790         comment 
= wxT("Comment="); 
 791         nIndex 
= file
.pIndexOf(comment
); 
 794     if ( nIndex 
!= wxNOT_FOUND 
) 
 795         mime_desc 
= file
.GetCmd(nIndex
); 
 796     //else: no description 
 798     // next find the extensions 
 799     wxString mime_extension
; 
 801     nIndex 
= file
.pIndexOf(wxT("Patterns=")); 
 802     if ( nIndex 
!= wxNOT_FOUND 
) 
 804         wxString exts 
= file
.GetCmd(nIndex
); 
 806         wxStringTokenizer 
tokenizer(exts
, wxT(";")); 
 807         while ( tokenizer
.HasMoreTokens() ) 
 809             wxString e 
= tokenizer
.GetNextToken(); 
 811             // don't support too difficult patterns 
 812             if ( e
.Left(2) != wxT("*.") ) 
 815             if ( !mime_extension
.empty() ) 
 817                 // separate from the previous ext 
 818                 mime_extension 
<< wxT(' '); 
 821             mime_extension 
<< e
.Mid(2); 
 825     sExts
.Add(mime_extension
); 
 827     // ok, now we can take care of icon: 
 829     nIndex 
= file
.pIndexOf(wxT("Icon=")); 
 830     if ( nIndex 
!= wxNOT_FOUND 
) 
 832         strIcon 
= file
.GetCmd(nIndex
); 
 834         wxLogTrace(TRACE_MIME
, wxT("  icon %s"), strIcon
.c_str()); 
 836         // it could be the real path, but more often a short name 
 837         if (!wxFileExists(strIcon
)) 
 839             // icon is just the short name 
 840             if ( !strIcon
.empty() ) 
 842                 // we must check if the file exists because it may be stored 
 843                 // in many locations, at least ~/.kde and $KDEDIR 
 844                 size_t nDir
, nDirs 
= icondirs
.GetCount(); 
 845                 for ( nDir 
= 0; nDir 
< nDirs
; nDir
++ ) 
 847                     wxFileName 
fnameIcon( strIcon 
); 
 848                     wxFileName 
fname( icondirs
[nDir
], fnameIcon
.GetName() ); 
 849                     fname
.SetExt( wxT("png") ); 
 850                     if (fname
.FileExists()) 
 852                         strIcon 
= fname
.GetFullPath(); 
 853                         wxLogTrace(TRACE_MIME
, wxT("  iconfile %s"), strIcon
.c_str()); 
 861     // now look for lines which know about the application 
 862     // exec= or DefaultApp= 
 864     nIndex 
= file
.pIndexOf(wxT("DefaultApp")); 
 866     if ( nIndex 
== wxNOT_FOUND 
) 
 869         nIndex 
= file
.pIndexOf(wxT("Exec")); 
 872     if ( nIndex 
!= wxNOT_FOUND 
) 
 874         // we expect %f; others including  %F and %U and %u are possible 
 875         wxString sTmp 
= file
.GetCmd(nIndex
); 
 876         if (0 == sTmp
.Replace( wxT("%f"), wxT("%s") )) 
 878         entry
->AddOrReplaceVerb(wxString(wxT("open")), sTmp 
); 
 881     AddToMimeData(mimetype
, strIcon
, entry
, sExts
, mime_desc
); 
 884 void wxMimeTypesManagerImpl::LoadKDELinksForMimeType(const wxString
& dirbase
, 
 885                                             const wxString
& subdir
, 
 886                                             const wxArrayString
& icondirs
) 
 888     wxFileName 
dirname(dirbase
, wxEmptyString
); 
 889     dirname
.AppendDir(subdir
); 
 890     wxDir 
dir(dirname
.GetPath()); 
 894     wxLogTrace(TRACE_MIME
, wxT("--- Loading from KDE directory %s  ---"), 
 895                            dirname
.GetPath().c_str()); 
 898     bool cont 
= dir
.GetFirst(&filename
, wxT("*.kdelnk"), wxDIR_FILES
); 
 900         LoadKDELinksForMimeSubtype(dirname
.GetPath(), subdir
, 
 902         cont 
= dir
.GetNext(&filename
); 
 905     // new standard for Gnome and KDE 
 906     cont 
= dir
.GetFirst(&filename
, wxT("*.desktop"), wxDIR_FILES
); 
 908         LoadKDELinksForMimeSubtype(dirname
.GetPath(), subdir
, 
 910         cont 
= dir
.GetNext(&filename
); 
 914 void wxMimeTypesManagerImpl::LoadKDELinkFilesFromDir(const wxString
& dirname
, 
 915                                             const wxArrayString
& icondirs
) 
 917     if(! wxDir::Exists(dirname
)) 
 921     if ( !dir
.IsOpened() ) 
 925     bool cont 
= dir
.GetFirst(&subdir
, wxEmptyString
, wxDIR_DIRS
); 
 928         LoadKDELinksForMimeType(dirname
, subdir
, icondirs
); 
 930         cont 
= dir
.GetNext(&subdir
); 
 934 // Read a KDE .desktop file of type 'Application' 
 935 void wxMimeTypesManagerImpl::LoadKDEApp(const wxString
& filename
) 
 937     wxLogTrace(TRACE_MIME
, wxT("loading KDE file %s"), filename
.c_str()); 
 940     if ( !file
.Open(filename
) ) 
 943     // Here, only type 'Application' should be considered. 
 944     int nIndex 
= file
.pIndexOf( wxT("Type=") ); 
 945     if (nIndex 
!= wxNOT_FOUND 
&& 
 946         file
.GetCmd(nIndex
).Lower() != wxT("application")) 
 949     // The hidden entry specifies a file to be ignored. 
 950     nIndex 
= file
.pIndexOf( wxT("Hidden=") ); 
 951     if (nIndex 
!= wxNOT_FOUND 
&& file
.GetCmd(nIndex
).Lower() == wxT("true")) 
 954     // Semicolon separated list of mime types handled by the application. 
 955     nIndex 
= file
.pIndexOf( wxT("MimeType=") ); 
 956     if (nIndex 
== wxNOT_FOUND
) 
 958     wxString mimetypes 
= file
.GetCmd (nIndex
); 
 960     // Name of the application 
 962     nIndex 
= wxNOT_FOUND
; 
 963 #if wxUSE_INTL // try "Name[locale name]" first 
 964     wxLocale 
*locale 
= wxGetLocale(); 
 966         nIndex 
= file
.pIndexOf(_T("Name[")+locale
->GetName()+_T("]=")); 
 968     if(nIndex 
== wxNOT_FOUND
) 
 969         nIndex 
= file
.pIndexOf( wxT("Name=") ); 
 970     if(nIndex 
!= wxNOT_FOUND
) 
 971         nameapp 
= file
.GetCmd(nIndex
); 
 973     // Icon of the application. 
 974     wxString nameicon
, namemini
; 
 975     nIndex 
= wxNOT_FOUND
; 
 976 #if wxUSE_INTL // try "Icon[locale name]" first 
 978         nIndex 
= file
.pIndexOf(_T("Icon[")+locale
->GetName()+_T("]=")); 
 980     if(nIndex 
== wxNOT_FOUND
) 
 981         nIndex 
= file
.pIndexOf( wxT("Icon=") ); 
 982     if(nIndex 
!= wxNOT_FOUND
) { 
 983         nameicon 
= wxString(wxT("--icon ")) + file
.GetCmd(nIndex
); 
 984         namemini 
= wxString(wxT("--miniicon ")) + file
.GetCmd(nIndex
); 
 987     // Replace some of the field code in the 'Exec' entry. 
 988     // TODO: deal with %d, %D, %n, %N, %k and %v (but last one is deprecated) 
 989     nIndex 
= file
.pIndexOf( wxT("Exec=") ); 
 990     if (nIndex 
== wxNOT_FOUND
) 
 992     wxString sCmd 
= file
.GetCmd(nIndex
); 
 993     // we expect %f; others including  %F and %U and %u are possible 
 994     sCmd
.Replace(wxT("%F"), wxT("%f")); 
 995     sCmd
.Replace(wxT("%U"), wxT("%f")); 
 996     sCmd
.Replace(wxT("%u"), wxT("%f")); 
 997     if (0 == sCmd
.Replace ( wxT("%f"), wxT("%s") )) 
 998         sCmd 
= sCmd 
+ wxT(" %s"); 
 999     sCmd
.Replace(wxT("%c"), nameapp
); 
1000     sCmd
.Replace(wxT("%i"), nameicon
); 
1001     sCmd
.Replace(wxT("%m"), namemini
); 
1003     wxStringTokenizer 
tokenizer(mimetypes
, _T(";")); 
1004     while(tokenizer
.HasMoreTokens()) { 
1005         wxString mimetype 
= tokenizer
.GetNextToken().Lower(); 
1006         nIndex 
= m_aTypes
.Index(mimetype
); 
1007         if(nIndex 
!= wxNOT_FOUND
) { // is this a known MIME type? 
1008             wxMimeTypeCommands
* entry 
= m_aEntries
[nIndex
]; 
1009             entry
->AddOrReplaceVerb(wxT("open"), sCmd
); 
1014 void wxMimeTypesManagerImpl::LoadKDEAppsFilesFromDir(const wxString
& dirname
) 
1016     if(! wxDir::Exists(dirname
)) 
1019     if ( !dir
.IsOpened() ) 
1023     // Look into .desktop files 
1024     bool cont 
= dir
.GetFirst(&filename
, _T("*.desktop"), wxDIR_FILES
); 
1026         wxFileName 
p(dirname
, filename
); 
1027         LoadKDEApp( p
.GetFullPath() ); 
1028         cont 
= dir
.GetNext(&filename
); 
1030     // Look recursively into subdirs 
1031     cont 
= dir
.GetFirst(&filename
, wxEmptyString
, wxDIR_DIRS
); 
1033         wxFileName 
p(dirname
, wxEmptyString
); 
1034         p
.AppendDir(filename
); 
1035         LoadKDEAppsFilesFromDir( p
.GetPath() ); 
1036         cont 
= dir
.GetNext(&filename
); 
1040 // Return base KDE directories. 
1041 // 1) Environment variable $KDEHOME, or "~/.kde" if not set. 
1042 // 2) List of directories in colon separated environment variable $KDEDIRS. 
1043 // 3) Environment variable $KDEDIR in case $KDEDIRS is not set. 
1044 // Notice at least the local kde directory is added to the list. If it is the 
1045 // only one, use later the application 'kde-config' to get additional paths. 
1046 static void GetKDEBaseDirs(wxArrayString
& basedirs
) 
1048     wxString env 
= wxGetenv( wxT("KDEHOME") ); 
1050         env 
= wxGetHomeDir() + wxT("/.kde"); 
1053     env 
= wxGetenv( wxT("KDEDIRS") ); 
1055         env 
= wxGetenv( wxT("KDEDIR") ); 
1059         wxStringTokenizer 
tokenizer(env
, wxT(":")); 
1060         while(tokenizer
.HasMoreTokens()) 
1061             basedirs
.Add( tokenizer
.GetNextToken() ); 
1065 static wxString 
ReadPathFromKDEConfig(const wxString
& request
) 
1068     wxArrayString output
; 
1069     if(wxExecute(wxT("kde-config --path ")+request
, output
) == 0 && 
1070        output
.GetCount() > 0) 
1071         str 
= output
.Item(0); 
1075 // Try to find the "Theme" entry in the configuration file, provided it exists. 
1076 static wxString 
GetKDEThemeInFile(const wxFileName
& filename
) 
1079     wxMimeTextFile config
; 
1080     if ( filename
.FileExists() && config
.Open(filename
.GetFullPath()) ) 
1082         size_t cnt 
= config
.GetLineCount(); 
1083         for ( size_t i 
= 0; i 
< cnt
; i
++ ) 
1085             if ( config
[i
].StartsWith(wxT("Theme="), &theme
) ) 
1093 // Try to find a file "kdeglobals" in one of the directories and read the 
1094 // "Theme" entry there. 
1095 static wxString 
GetKDETheme(const wxArrayString
& basedirs
) 
1098     for(size_t i 
= 0; i 
< basedirs
.GetCount(); i
++) { 
1099         wxFileName 
filename(basedirs
.Item(i
), wxEmptyString
); 
1100         filename
.AppendDir( wxT("share") ); 
1101         filename
.AppendDir( wxT("config") ); 
1102         filename
.SetName( wxT("kdeglobals") ); 
1103         theme 
= GetKDEThemeInFile(filename
); 
1104         if(! theme
.IsEmpty()) 
1107     // If $KDEDIRS and $KDEDIR were set, we try nothing more. Otherwise, we 
1108     // try to get the configuration file with 'kde-config'. 
1109     if(basedirs
.GetCount() > 1) 
1111     wxString paths 
= ReadPathFromKDEConfig(wxT("config")); 
1112     if(! paths
.IsEmpty()) { 
1113         wxStringTokenizer 
tokenizer(paths
, wxT(":")); 
1114         while( tokenizer
.HasMoreTokens() ) { 
1115             wxFileName 
filename(tokenizer
.GetNextToken(), wxT("kdeglobals")); 
1116             theme 
= GetKDEThemeInFile(filename
); 
1117             if(! theme
.IsEmpty()) 
1124 // Get list of directories of icons. 
1125 static void GetKDEIconDirs(const wxArrayString
& basedirs
, 
1126                            wxArrayString
& icondirs
) 
1128     wxString theme 
= GetKDETheme(basedirs
); 
1130         theme 
= wxT("default.kde"); 
1132     for(size_t i 
= 0; i 
< basedirs
.GetCount(); i
++) { 
1133         wxFileName 
dirname(basedirs
.Item(i
), wxEmptyString
); 
1134         dirname
.AppendDir( wxT("share") ); 
1135         dirname
.AppendDir( wxT("icons") ); 
1136         dirname
.AppendDir(theme
); 
1137         dirname
.AppendDir( wxT("32x32") ); 
1138         dirname
.AppendDir( wxT("mimetypes") ); 
1139         if( wxDir::Exists( dirname
.GetPath() ) ) 
1140             icondirs
.Add( dirname
.GetPath() ); 
1143     // If $KDEDIRS and $KDEDIR were not set, use 'kde-config' 
1144     if(basedirs
.GetCount() > 1) 
1146     wxString paths 
= ReadPathFromKDEConfig(wxT("icon")); 
1147     if(! paths
.IsEmpty()) { 
1148         wxStringTokenizer 
tokenizer(paths
, wxT(":")); 
1149         while( tokenizer
.HasMoreTokens() ) { 
1150             wxFileName 
dirname(tokenizer
.GetNextToken(), wxEmptyString
); 
1151             dirname
.AppendDir(theme
); 
1152             dirname
.AppendDir( wxT("32x32") ); 
1153             dirname
.AppendDir( wxT("mimetypes") ); 
1154             if(icondirs
.Index(dirname
.GetPath()) == wxNOT_FOUND 
&& 
1155                wxDir::Exists( dirname
.GetPath() ) ) 
1156                 icondirs
.Add( dirname
.GetPath() ); 
1161 // Get list of directories of mime types. 
1162 static void GetKDEMimeDirs(const wxArrayString
& basedirs
, 
1163                            wxArrayString
& mimedirs
) 
1165     for(size_t i 
= 0; i 
< basedirs
.GetCount(); i
++) { 
1166         wxFileName 
dirname(basedirs
.Item(i
), wxEmptyString
); 
1167         dirname
.AppendDir( wxT("share") ); 
1168         dirname
.AppendDir( wxT("mimelnk") ); 
1169         if( wxDir::Exists( dirname
.GetPath() ) ) 
1170             mimedirs
.Add( dirname
.GetPath() ); 
1173     // If $KDEDIRS and $KDEDIR were not set, use 'kde-config' 
1174     if(basedirs
.GetCount() > 1) 
1176     wxString paths 
= ReadPathFromKDEConfig(wxT("mime")); 
1177     if(! paths
.IsEmpty()) { 
1178         wxStringTokenizer 
tokenizer(paths
, wxT(":")); 
1179         while( tokenizer
.HasMoreTokens() ) { 
1180             wxFileName 
p(tokenizer
.GetNextToken(), wxEmptyString
); 
1181             wxString dirname 
= p
.GetPath(); // To remove possible trailing '/' 
1182             if(mimedirs
.Index(dirname
) == wxNOT_FOUND 
&& 
1183                wxDir::Exists(dirname
) ) 
1184                 mimedirs
.Add(dirname
); 
1189 // Get list of directories of application desktop files. 
1190 static void GetKDEAppsDirs(const wxArrayString
& basedirs
, 
1191                            wxArrayString
& appsdirs
) 
1193     for(size_t i 
= 0; i 
< basedirs
.GetCount(); i
++) { 
1194         wxFileName 
dirname(basedirs
.Item(i
), wxEmptyString
); 
1195         dirname
.AppendDir( wxT("share") ); 
1196         dirname
.AppendDir( wxT("applnk") ); 
1197         if( wxDir::Exists( dirname
.GetPath() ) ) 
1198             appsdirs
.Add( dirname
.GetPath() ); 
1201     // If $KDEDIRS and $KDEDIR were not set, use 'kde-config' 
1202     if(basedirs
.GetCount() > 1) 
1204     wxString paths 
= ReadPathFromKDEConfig(wxT("apps")); 
1205     if(! paths
.IsEmpty()) { 
1206         wxStringTokenizer 
tokenizer(paths
, wxT(":")); 
1207         while( tokenizer
.HasMoreTokens() ) { 
1208             wxFileName 
p(tokenizer
.GetNextToken(), wxEmptyString
); 
1209             wxString dirname 
= p
.GetPath(); // To remove possible trailing '/' 
1210             if(appsdirs
.Index(dirname
) == wxNOT_FOUND 
&& 
1211                wxDir::Exists(dirname
) ) 
1212                 appsdirs
.Add(dirname
); 
1215     paths 
= ReadPathFromKDEConfig(wxT("xdgdata-apps")); 
1216     if(! paths
.IsEmpty()) { 
1217         wxStringTokenizer 
tokenizer(paths
, wxT(":")); 
1218         while( tokenizer
.HasMoreTokens() ) { 
1219             wxFileName 
p(tokenizer
.GetNextToken(), wxEmptyString
); 
1220             wxString dirname 
= p
.GetPath(); // To remove possible trailing '/' 
1221             if(appsdirs
.Index(dirname
) == wxNOT_FOUND 
&& 
1222                wxDir::Exists(dirname
) ) 
1223                 appsdirs
.Add(dirname
); 
1228 // Fill database with all mime types. 
1229 void wxMimeTypesManagerImpl::GetKDEMimeInfo(const wxString
& sExtraDir
) 
1231     wxArrayString basedirs
; 
1232     GetKDEBaseDirs(basedirs
); 
1234     wxArrayString icondirs
; 
1235     GetKDEIconDirs(basedirs
, icondirs
); 
1236     wxArrayString mimedirs
; 
1237     GetKDEMimeDirs(basedirs
, mimedirs
); 
1238     wxArrayString appsdirs
; 
1239     GetKDEAppsDirs(basedirs
, appsdirs
); 
1241     if(! sExtraDir
.IsEmpty()) { 
1242         icondirs
.Add(sExtraDir 
+ wxT("/icons")); 
1243         mimedirs
.Add(sExtraDir 
+ wxT("/mimelnk")); 
1244         appsdirs
.Add(sExtraDir 
+ wxT("/applnk")); 
1248     size_t nDirs 
= mimedirs
.GetCount(), nDir
; 
1249     for(nDir 
= 0; nDir 
< nDirs
; nDir
++) 
1250         LoadKDELinkFilesFromDir(mimedirs
[nDir
], icondirs
); 
1252     // Load application files and associate them to corresponding mime types. 
1253     nDirs 
= appsdirs
.GetCount(); 
1254     for(nDir 
= 0; nDir 
< nDirs
; nDir
++) 
1255         LoadKDEAppsFilesFromDir(appsdirs
[nDir
]); 
1258 // ---------------------------------------------------------------------------- 
1259 // wxFileTypeImpl (Unix) 
1260 // ---------------------------------------------------------------------------- 
1262 wxString 
wxFileTypeImpl::GetExpandedCommand(const wxString 
& verb
, const wxFileType::MessageParameters
& params
) const 
1266     while ( (i 
< m_index
.GetCount() ) && sTmp
.empty() ) 
1268         sTmp 
= m_manager
->GetCommand( verb
, m_index
[i
] ); 
1272     return wxFileType::ExpandCommand(sTmp
, params
); 
1275 bool wxFileTypeImpl::GetIcon(wxIconLocation 
*iconLoc
) const 
1279     while ( (i 
< m_index
.GetCount() ) && sTmp
.empty() ) 
1281         sTmp 
= m_manager
->m_aIcons
[m_index
[i
]]; 
1290         iconLoc
->SetFileName(sTmp
); 
1296 bool wxFileTypeImpl::GetMimeTypes(wxArrayString
& mimeTypes
) const 
1299     size_t nCount 
= m_index
.GetCount(); 
1300     for (size_t i 
= 0; i 
< nCount
; i
++) 
1301         mimeTypes
.Add(m_manager
->m_aTypes
[m_index
[i
]]); 
1306 size_t wxFileTypeImpl::GetAllCommands(wxArrayString 
*verbs
, 
1307                                   wxArrayString 
*commands
, 
1308                                   const wxFileType::MessageParameters
& params
) const 
1310     wxString vrb
, cmd
, sTmp
; 
1312     wxMimeTypeCommands 
* sPairs
; 
1314     // verbs and commands have been cleared already in mimecmn.cpp... 
1315     // if we find no entries in the exact match, try the inexact match 
1316     for (size_t n 
= 0; ((count 
== 0) && (n 
< m_index
.GetCount())); n
++) 
1318         // list of verb = command pairs for this mimetype 
1319         sPairs 
= m_manager
->m_aEntries 
[m_index
[n
]]; 
1321         for ( i 
= 0; i 
< sPairs
->GetCount(); i
++ ) 
1323             vrb 
= sPairs
->GetVerb(i
); 
1324             // some gnome entries have "." inside 
1325             vrb 
= vrb
.AfterLast(wxT('.')); 
1326             cmd 
= sPairs
->GetCmd(i
); 
1329                  cmd 
= wxFileType::ExpandCommand(cmd
, params
); 
1331                  if ( vrb
.IsSameAs(wxT("open"))) 
1334                         verbs
->Insert(vrb
, 0u); 
1336                         commands 
->Insert(cmd
, 0u); 
1352 bool wxFileTypeImpl::GetExtensions(wxArrayString
& extensions
) 
1354     const wxString strExtensions 
= m_manager
->GetExtension(m_index
[0]); 
1357     // one extension in the space or comma-delimited list 
1359     wxString::const_iterator end 
= strExtensions
.end(); 
1360     for ( wxString::const_iterator p 
= strExtensions
.begin(); /* nothing */; ++p 
) 
1362         if ( p 
== end 
|| *p 
== wxT(' ') || *p 
== wxT(',') ) 
1364             if ( !strExt
.empty() ) 
1366                 extensions
.Add(strExt
); 
1369             //else: repeated spaces 
1370             // (shouldn't happen, but it's not that important if it does happen) 
1375         else if ( *p 
== wxT('.') ) 
1377             // remove the dot from extension (but only if it's the first char) 
1378             if ( !strExt
.empty() ) 
1382             //else: no, don't append it 
1393 // set an arbitrary command: 
1394 // could adjust the code to ask confirmation if it already exists and 
1395 // overwriteprompt is true, but this is currently ignored as *Associate* has 
1396 // no overwrite prompt 
1398 wxFileTypeImpl::SetCommand(const wxString
& cmd
, 
1399                            const wxString
& verb
, 
1400                            bool WXUNUSED(overwriteprompt
)) 
1402     wxArrayString strExtensions
; 
1403     wxString strDesc
, strIcon
; 
1405     wxArrayString strTypes
; 
1406     GetMimeTypes(strTypes
); 
1407     if ( strTypes
.IsEmpty() ) 
1410     wxMimeTypeCommands 
*entry 
= new wxMimeTypeCommands(); 
1411     entry
->Add(verb 
+ wxT("=")  + cmd 
+ wxT(" %s ")); 
1414     size_t nCount 
= strTypes
.GetCount(); 
1415     for ( size_t i 
= 0; i 
< nCount
; i
++ ) 
1417         if ( m_manager
->DoAssociation
 
1426             // DoAssociation() took ownership of entry, don't delete it below 
1437 // ignore index on the grounds that we only have one icon in a Unix file 
1438 bool wxFileTypeImpl::SetDefaultIcon(const wxString
& strIcon
, int WXUNUSED(index
)) 
1440     if (strIcon
.empty()) 
1443     wxArrayString strExtensions
; 
1446     wxArrayString strTypes
; 
1447     GetMimeTypes(strTypes
); 
1448     if ( strTypes
.IsEmpty() ) 
1451     wxMimeTypeCommands 
*entry 
= new wxMimeTypeCommands(); 
1453     size_t nCount 
= strTypes
.GetCount(); 
1454     for ( size_t i 
= 0; i 
< nCount
; i
++ ) 
1456         if ( m_manager
->DoAssociation
 
1465             // we don't need to free entry now, DoAssociation() took ownership 
1477 // ---------------------------------------------------------------------------- 
1478 // wxMimeTypesManagerImpl (Unix) 
1479 // ---------------------------------------------------------------------------- 
1481 wxMimeTypesManagerImpl::wxMimeTypesManagerImpl() 
1483     m_initialized 
= false; 
1484     m_mailcapStylesInited 
= 0; 
1487 void wxMimeTypesManagerImpl::InitIfNeeded() 
1489     if ( !m_initialized 
) 
1491         // set the flag first to prevent recursion 
1492         m_initialized 
= true; 
1494         wxString wm 
= wxTheApp
->GetTraits()->GetDesktopEnvironment(); 
1496         if (wm 
== wxT("KDE")) 
1497             Initialize( wxMAILCAP_KDE  
); 
1498         else if (wm 
== wxT("GNOME")) 
1499             Initialize( wxMAILCAP_GNOME 
); 
1505 // read system and user mailcaps and other files 
1506 void wxMimeTypesManagerImpl::Initialize(int mailcapStyles
, 
1507                                         const wxString
& sExtraDir
) 
1509     // read mimecap amd mime.types 
1510     if ( (mailcapStyles 
& wxMAILCAP_NETSCAPE
) || 
1511          (mailcapStyles 
& wxMAILCAP_STANDARD
) ) 
1512         GetMimeInfo(sExtraDir
); 
1514     // read GNOME tables 
1515     if (mailcapStyles 
& wxMAILCAP_GNOME
) 
1516         GetGnomeMimeInfo(sExtraDir
); 
1518     // read KDE tables which are never installed on OpenVMS 
1520     if (mailcapStyles 
& wxMAILCAP_KDE
) 
1521         GetKDEMimeInfo(sExtraDir
); 
1524     m_mailcapStylesInited 
|= mailcapStyles
; 
1527 // clear data so you can read another group of WM files 
1528 void wxMimeTypesManagerImpl::ClearData() 
1532     m_aExtensions
.Clear(); 
1533     m_aDescriptions
.Clear(); 
1535     WX_CLEAR_ARRAY(m_aEntries
); 
1538     m_mailcapStylesInited 
= 0; 
1541 wxMimeTypesManagerImpl::~wxMimeTypesManagerImpl() 
1546 void wxMimeTypesManagerImpl::GetMimeInfo(const wxString
& sExtraDir
) 
1548     // read this for netscape or Metamail formats 
1550     // directories where we look for mailcap and mime.types by default 
1551     // used by netscape and pine and other mailers, using 2 different formats! 
1553     // (taken from metamail(1) sources) 
1555     // although RFC 1524 specifies the search path of 
1556     // /etc/:/usr/etc:/usr/local/etc only, it doesn't hurt to search in more 
1557     // places - OTOH, the RFC also says that this path can be changed with 
1558     // MAILCAPS environment variable (containing the colon separated full 
1559     // filenames to try) which is not done yet (TODO?) 
1561     wxString strHome 
= wxGetenv(wxT("HOME")); 
1564     dirs
.Add( strHome 
+ wxT("/.") ); 
1565     dirs
.Add( wxT("/etc/") ); 
1566     dirs
.Add( wxT("/usr/etc/") ); 
1567     dirs
.Add( wxT("/usr/local/etc/") ); 
1568     dirs
.Add( wxT("/etc/mail/") ); 
1569     dirs
.Add( wxT("/usr/public/lib/") ); 
1570     if (!sExtraDir
.empty()) 
1571         dirs
.Add( sExtraDir 
+ wxT("/") ); 
1574     size_t nDirs 
= dirs
.GetCount(); 
1575     for ( size_t nDir 
= 0; nDir 
< nDirs
; nDir
++ ) 
1578         file 
+= wxT("mailcap"); 
1579         if ( wxFile::Exists(file
) ) 
1585         file 
+= wxT("mime.types"); 
1586         if ( wxFile::Exists(file
) ) 
1587             ReadMimeTypes(file
); 
1591 bool wxMimeTypesManagerImpl::WriteToMimeTypes(int index
, bool delete_index
) 
1593     // check we have the right manager 
1594     if (! ( m_mailcapStylesInited 
& wxMAILCAP_STANDARD
) ) 
1598     wxString strHome 
= wxGetenv(wxT("HOME")); 
1600     // and now the users mailcap 
1601     wxString strUserMailcap 
= strHome 
+ wxT("/.mime.types"); 
1603     wxMimeTextFile file
; 
1604     if ( wxFile::Exists(strUserMailcap
) ) 
1606         bTemp 
= file
.Open(strUserMailcap
); 
1613         bTemp 
= file
.Create(strUserMailcap
); 
1619         // test for netscape's header and return false if its found 
1620         nIndex 
= file
.pIndexOf(wxT("#--Netscape")); 
1621         if (nIndex 
!= wxNOT_FOUND
) 
1623             wxFAIL_MSG(wxT("Error in .mime.types\nTrying to mix Netscape and Metamail formats\nFile not modified")); 
1627         // write it in alternative format 
1628         // get rid of unwanted entries 
1629         wxString strType 
= m_aTypes
[index
]; 
1630         nIndex 
= file
.pIndexOf(strType
); 
1632         // get rid of all the unwanted entries... 
1633         if (nIndex 
!= wxNOT_FOUND
) 
1634             file
.CommentLine(nIndex
); 
1638             // add the new entries in 
1639             wxString sTmp 
= strType
.Append( wxT(' '), 40 - strType
.Len() ); 
1640             sTmp 
+= m_aExtensions
[index
]; 
1644         bTemp 
= file
.Write(); 
1651 bool wxMimeTypesManagerImpl::WriteToNSMimeTypes(int index
, bool delete_index
) 
1653     //check we have the right managers 
1654     if (! ( m_mailcapStylesInited 
& wxMAILCAP_NETSCAPE
) ) 
1658     wxString strHome 
= wxGetenv(wxT("HOME")); 
1660     // and now the users mailcap 
1661     wxString strUserMailcap 
= strHome 
+ wxT("/.mime.types"); 
1663     wxMimeTextFile file
; 
1664     if ( wxFile::Exists(strUserMailcap
) ) 
1666         bTemp 
= file
.Open(strUserMailcap
); 
1673         bTemp 
= file
.Create(strUserMailcap
); 
1678         // write it in the format that Netscape uses 
1680         // test for netscape's header and insert if required... 
1681         // this is a comment so use true 
1682         nIndex 
= file
.pIndexOf(wxT("#--Netscape"), true); 
1683         if (nIndex 
== wxNOT_FOUND
) 
1685             // either empty file or metamail format 
1686             // at present we can't cope with mixed formats, so exit to preseve 
1687             // metamail entreies 
1688             if (file
.GetLineCount() > 0) 
1690                 wxFAIL_MSG(wxT(".mime.types File not in Netscape format\nNo entries written to\n.mime.types or to .mailcap")); 
1694             file
.InsertLine(wxT( "#--Netscape Communications Corporation MIME Information" ), 0); 
1698         wxString strType 
= wxT("type=") + m_aTypes
[index
]; 
1699         nIndex 
= file
.pIndexOf(strType
); 
1701         // get rid of all the unwanted entries... 
1702         if (nIndex 
!= wxNOT_FOUND
) 
1704             wxString sOld 
= file
[nIndex
]; 
1705             while ( (sOld
.Contains(wxT("\\"))) && (nIndex 
< (int) file
.GetLineCount()) ) 
1707                 file
.CommentLine(nIndex
); 
1708                 sOld 
= file
[nIndex
]; 
1710                 wxLogTrace(TRACE_MIME
, wxT("--- Deleting from mime.types line '%d %s' ---"), nIndex
, sOld
.c_str()); 
1715             if (nIndex 
< (int) file
.GetLineCount()) 
1716                 file
.CommentLine(nIndex
); 
1719             nIndex 
= (int) file
.GetLineCount(); 
1721         wxString sTmp 
= strType 
+ wxT(" \\"); 
1723             file
.InsertLine(sTmp
, nIndex
); 
1725         if ( ! m_aDescriptions
.Item(index
).empty() ) 
1727             sTmp 
= wxT("desc=\"") + m_aDescriptions
[index
]+ wxT("\" \\"); //.trim ?? 
1731                 file
.InsertLine(sTmp
, nIndex
); 
1735         wxString sExts 
= m_aExtensions
.Item(index
); 
1736         sTmp 
= wxT("exts=\"") + sExts
.Trim(false).Trim() + wxT("\""); 
1740             file
.InsertLine(sTmp
, nIndex
); 
1743         bTemp 
= file
.Write(); 
1750 bool wxMimeTypesManagerImpl::WriteToMailCap(int index
, bool delete_index
) 
1752     //check we have the right managers 
1753     if ( !( ( m_mailcapStylesInited 
& wxMAILCAP_NETSCAPE
) || 
1754             ( m_mailcapStylesInited 
& wxMAILCAP_STANDARD
) ) ) 
1758     wxString strHome 
= wxGetenv(wxT("HOME")); 
1760     // and now the users mailcap 
1761     wxString strUserMailcap 
= strHome 
+ wxT("/.mailcap"); 
1763     wxMimeTextFile file
; 
1764     if ( wxFile::Exists(strUserMailcap
) ) 
1766         bTemp 
= file
.Open(strUserMailcap
); 
1773         bTemp 
= file
.Create(strUserMailcap
); 
1778         // now got a file we can write to .... 
1779         wxMimeTypeCommands 
* entries 
= m_aEntries
[index
]; 
1781         wxString sCmd 
= entries
->GetCommandForVerb(wxT("open"), &iOpen
); 
1784         sTmp 
= m_aTypes
[index
]; 
1786         int nIndex 
= file
.pIndexOf(sTmp
); 
1788         // get rid of all the unwanted entries... 
1789         if (nIndex 
== wxNOT_FOUND
) 
1791             nIndex 
= (int) file
.GetLineCount(); 
1795             sOld 
= file
[nIndex
]; 
1796             wxLogTrace(TRACE_MIME
, wxT("--- Deleting from mailcap line '%d' ---"), nIndex
); 
1798             while ( (sOld
.Contains(wxT("\\"))) && (nIndex 
< (int) file
.GetLineCount()) ) 
1800                 file
.CommentLine(nIndex
); 
1801                 if (nIndex 
< (int) file
.GetLineCount()) 
1802                     sOld 
= sOld 
+ file
[nIndex
]; 
1806                 file
.GetLineCount()) file
.CommentLine(nIndex
); 
1809         sTmp 
+= wxT(";") + sCmd
; //includes wxT(" %s "); 
1811         // write it in the format that Netscape uses (default) 
1812         if (! ( m_mailcapStylesInited 
& wxMAILCAP_STANDARD 
) ) 
1815                 file
.InsertLine(sTmp
, nIndex
); 
1820             // write extended format 
1822             // TODO - FIX this code: 
1824             // sOld holds all the entries, but our data store only has some 
1825             // eg test= is not stored 
1827             // so far we have written the mimetype and command out 
1828             wxStringTokenizer 
sT(sOld
, wxT(";\\")); 
1829             if (sT
.CountTokens() > 2) 
1831                 // first one mimetype; second one command, rest unknown... 
1833                 s 
= sT
.GetNextToken(); 
1834                 s 
= sT
.GetNextToken(); 
1837                 s 
= sT
.GetNextToken(); 
1838                 while ( ! s
.empty() ) 
1840                     bool bKnownToken 
= false; 
1841                     if (s
.Contains(wxT("description="))) 
1843                     if (s
.Contains(wxT("x11-bitmap="))) 
1847                     size_t nCount 
= entries
->GetCount(); 
1848                     for (i
=0; i 
< nCount
; i
++) 
1850                         if (s
.Contains(entries
->GetVerb(i
))) 
1856                         sTmp 
+= wxT("; \\"); 
1857                         file
.InsertLine(sTmp
, nIndex
); 
1861                     s 
= sT
.GetNextToken(); 
1865             if (! m_aDescriptions
[index
].empty() ) 
1867                 sTmp 
+= wxT("; \\"); 
1868                 file
.InsertLine(sTmp
, nIndex
); 
1870                 sTmp 
= wxT("       description=\"") + m_aDescriptions
[index
] + wxT("\""); 
1873             if (! m_aIcons
[index
].empty() ) 
1875                 sTmp 
+= wxT("; \\"); 
1876                 file
.InsertLine(sTmp
, nIndex
); 
1878                 sTmp 
= wxT("       x11-bitmap=\"") + m_aIcons
[index
] + wxT("\""); 
1881             if ( entries
->GetCount() > 1 ) 
1884                 for (i
=0; i 
< entries
->GetCount(); i
++) 
1887                         sTmp 
+= wxT("; \\"); 
1888                         file
.InsertLine(sTmp
, nIndex
); 
1890                         sTmp 
= wxT("       ") + entries
->GetVerbCmd(i
); 
1894             file
.InsertLine(sTmp
, nIndex
); 
1898         bTemp 
= file
.Write(); 
1905 wxFileType 
* wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo
& ftInfo
) 
1909     wxString strType 
= ftInfo
.GetMimeType(); 
1910     wxString strDesc 
= ftInfo
.GetDescription(); 
1911     wxString strIcon 
= ftInfo
.GetIconFile(); 
1913     wxMimeTypeCommands 
*entry 
= new wxMimeTypeCommands(); 
1915     if ( ! ftInfo
.GetOpenCommand().empty()) 
1916         entry
->Add(wxT("open=")  + ftInfo
.GetOpenCommand() + wxT(" %s ")); 
1917     if ( ! ftInfo
.GetPrintCommand().empty()) 
1918         entry
->Add(wxT("print=") + ftInfo
.GetPrintCommand() + wxT(" %s ")); 
1920     // now find where these extensions are in the data store and remove them 
1921     wxArrayString sA_Exts 
= ftInfo
.GetExtensions(); 
1922     wxString sExt
, sExtStore
; 
1924     size_t nExtCount 
= sA_Exts
.GetCount(); 
1925     for (i
=0; i 
< nExtCount
; i
++) 
1927         sExt 
= sA_Exts
.Item(i
); 
1929         // clean up to just a space before and after 
1930         sExt
.Trim().Trim(false); 
1931         sExt 
= wxT(' ') + sExt 
+ wxT(' '); 
1932         size_t nCount 
= m_aExtensions
.GetCount(); 
1933         for (nIndex 
= 0; nIndex 
< nCount
; nIndex
++) 
1935             sExtStore 
= m_aExtensions
.Item(nIndex
); 
1936             if (sExtStore
.Replace(sExt
, wxT(" ") ) > 0) 
1937                 m_aExtensions
.Item(nIndex
) = sExtStore
; 
1941     if ( !DoAssociation(strType
, strIcon
, entry
, sA_Exts
, strDesc
) ) 
1944     return GetFileTypeFromMimeType(strType
); 
1947 bool wxMimeTypesManagerImpl::DoAssociation(const wxString
& strType
, 
1948                                            const wxString
& strIcon
, 
1949                                            wxMimeTypeCommands 
*entry
, 
1950                                            const wxArrayString
& strExtensions
, 
1951                                            const wxString
& strDesc
) 
1953     int nIndex 
= AddToMimeData(strType
, strIcon
, entry
, strExtensions
, strDesc
, true); 
1955     if ( nIndex 
== wxNOT_FOUND 
) 
1958     return WriteMimeInfo(nIndex
, false); 
1961 bool wxMimeTypesManagerImpl::WriteMimeInfo(int nIndex
, bool delete_mime 
) 
1965     if ( m_mailcapStylesInited 
& wxMAILCAP_STANDARD 
) 
1967         // write in metamail  format; 
1968         if (WriteToMimeTypes(nIndex
, delete_mime
) ) 
1969             if ( WriteToMailCap(nIndex
, delete_mime
) ) 
1973     if ( m_mailcapStylesInited 
& wxMAILCAP_NETSCAPE 
) 
1975         // write in netsacpe format; 
1976         if (WriteToNSMimeTypes(nIndex
, delete_mime
) ) 
1977             if ( WriteToMailCap(nIndex
, delete_mime
) ) 
1981     // Don't write GNOME files here as this is not 
1982     // allowed and simply doesn't work 
1984     if (m_mailcapStylesInited 
& wxMAILCAP_KDE
) 
1986         // write in KDE format; 
1987         if (WriteKDEMimeFile(nIndex
, delete_mime
) ) 
1994 int wxMimeTypesManagerImpl::AddToMimeData(const wxString
& strType
, 
1995                                           const wxString
& strIcon
, 
1996                                           wxMimeTypeCommands 
*entry
, 
1997                                           const wxArrayString
& strExtensions
, 
1998                                           const wxString
& strDesc
, 
1999                                           bool replaceExisting
) 
2003     // ensure mimetype is always lower case 
2004     wxString mimeType 
= strType
.Lower(); 
2006     // is this a known MIME type? 
2007     int nIndex 
= m_aTypes
.Index(mimeType
); 
2008     if ( nIndex 
== wxNOT_FOUND 
) 
2011         m_aTypes
.Add(mimeType
); 
2012         m_aIcons
.Add(strIcon
); 
2013         m_aEntries
.Add(entry 
? entry 
: new wxMimeTypeCommands
); 
2015         // change nIndex so we can use it below to add the extensions 
2016         m_aExtensions
.Add(wxEmptyString
); 
2017         nIndex 
= m_aExtensions
.size() - 1; 
2019         m_aDescriptions
.Add(strDesc
); 
2021     else // yes, we already have it 
2023         if ( replaceExisting 
) 
2025             // if new description change it 
2026             if ( !strDesc
.empty()) 
2027                 m_aDescriptions
[nIndex
] = strDesc
; 
2029             // if new icon change it 
2030             if ( !strIcon
.empty()) 
2031                 m_aIcons
[nIndex
] = strIcon
; 
2035                 delete m_aEntries
[nIndex
]; 
2036                 m_aEntries
[nIndex
] = entry
; 
2039         else // add data we don't already have ... 
2041             // if new description add only if none 
2042             if ( m_aDescriptions
[nIndex
].empty() ) 
2043                 m_aDescriptions
[nIndex
] = strDesc
; 
2045             // if new icon and no existing icon 
2046             if ( m_aIcons
[nIndex
].empty() ) 
2047                 m_aIcons
[nIndex
] = strIcon
; 
2049             // add any new entries... 
2052                 wxMimeTypeCommands 
*entryOld 
= m_aEntries
[nIndex
]; 
2054                 size_t count 
= entry
->GetCount(); 
2055                 for ( size_t i 
= 0; i 
< count
; i
++ ) 
2057                     const wxString
& verb 
= entry
->GetVerb(i
); 
2058                     if ( !entryOld
->HasVerb(verb
) ) 
2060                         entryOld
->AddOrReplaceVerb(verb
, entry
->GetCmd(i
)); 
2064                 // as we don't store it anywhere, it won't be deleted later as 
2065                 // usual -- do it immediately instead 
2071     // always add the extensions to this mimetype 
2072     wxString
& exts 
= m_aExtensions
[nIndex
]; 
2074     // add all extensions we don't have yet 
2076     size_t count 
= strExtensions
.GetCount(); 
2077     for ( size_t i 
= 0; i 
< count
; i
++ ) 
2079         ext 
= strExtensions
[i
]; 
2082         if ( exts
.Find(ext
) == wxNOT_FOUND 
) 
2088     // check data integrity 
2089     wxASSERT( m_aTypes
.GetCount() == m_aEntries
.GetCount() && 
2090               m_aTypes
.GetCount() == m_aExtensions
.GetCount() && 
2091               m_aTypes
.GetCount() == m_aIcons
.GetCount() && 
2092               m_aTypes
.GetCount() == m_aDescriptions
.GetCount() ); 
2097 wxFileType 
* wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString
& ext
) 
2104     size_t count 
= m_aExtensions
.GetCount(); 
2105     for ( size_t n 
= 0; n 
< count
; n
++ ) 
2107         wxStringTokenizer 
tk(m_aExtensions
[n
], wxT(' ')); 
2109         while ( tk
.HasMoreTokens() ) 
2111             // consider extensions as not being case-sensitive 
2112             if ( tk
.GetNextToken().IsSameAs(ext
, false /* no case */) ) 
2115                 wxFileType 
*fileType 
= new wxFileType
; 
2116                 fileType
->m_impl
->Init(this, n
); 
2126 wxFileType 
* wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString
& mimeType
) 
2130     wxFileType 
* fileType 
= NULL
; 
2131     // mime types are not case-sensitive 
2132     wxString 
mimetype(mimeType
); 
2133     mimetype
.MakeLower(); 
2135     // first look for an exact match 
2136     int index 
= m_aTypes
.Index(mimetype
); 
2137     if ( index 
!= wxNOT_FOUND 
) 
2139         fileType 
= new wxFileType
; 
2140         fileType
->m_impl
->Init(this, index
); 
2143     // then try to find "text/*" as match for "text/plain" (for example) 
2144     // NB: if mimeType doesn't contain '/' at all, BeforeFirst() will return 
2145     //     the whole string - ok. 
2147     index 
= wxNOT_FOUND
; 
2148     wxString strCategory 
= mimetype
.BeforeFirst(wxT('/')); 
2150     size_t nCount 
= m_aTypes
.GetCount(); 
2151     for ( size_t n 
= 0; n 
< nCount
; n
++ ) 
2153         if ( (m_aTypes
[n
].BeforeFirst(wxT('/')) == strCategory 
) && 
2154                 m_aTypes
[n
].AfterFirst(wxT('/')) == wxT("*") ) 
2161     if ( index 
!= wxNOT_FOUND 
) 
2163        // don't throw away fileType that was already found 
2165             fileType 
= new wxFileType
; 
2166         fileType
->m_impl
->Init(this, index
); 
2172 wxString 
wxMimeTypesManagerImpl::GetCommand(const wxString 
& verb
, size_t nIndex
) const 
2174     wxString command
, testcmd
, sV
, sTmp
; 
2175     sV 
= verb 
+ wxT("="); 
2177     // list of verb = command pairs for this mimetype 
2178     wxMimeTypeCommands 
* sPairs 
= m_aEntries 
[nIndex
]; 
2181     size_t nCount 
= sPairs
->GetCount(); 
2182     for ( i 
= 0; i 
< nCount
; i
++ ) 
2184         sTmp 
= sPairs
->GetVerbCmd (i
); 
2185         if ( sTmp
.Contains(sV
) ) 
2186             command 
= sTmp
.AfterFirst(wxT('=')); 
2192 void wxMimeTypesManagerImpl::AddFallback(const wxFileTypeInfo
& filetype
) 
2196     wxString extensions
; 
2197     const wxArrayString
& exts 
= filetype
.GetExtensions(); 
2198     size_t nExts 
= exts
.GetCount(); 
2199     for ( size_t nExt 
= 0; nExt 
< nExts
; nExt
++ ) 
2202             extensions 
+= wxT(' '); 
2204         extensions 
+= exts
[nExt
]; 
2207     AddMimeTypeInfo(filetype
.GetMimeType(), 
2209                     filetype
.GetDescription()); 
2211     AddMailcapInfo(filetype
.GetMimeType(), 
2212                    filetype
.GetOpenCommand(), 
2213                    filetype
.GetPrintCommand(), 
2215                    filetype
.GetDescription()); 
2218 void wxMimeTypesManagerImpl::AddMimeTypeInfo(const wxString
& strMimeType
, 
2219                                              const wxString
& strExtensions
, 
2220                                              const wxString
& strDesc
) 
2222     // reading mailcap may find image/* , while 
2223     // reading mime.types finds image/gif and no match is made 
2224     // this means all the get functions don't work  fix this 
2226     wxString sTmp 
= strExtensions
; 
2228     wxArrayString sExts
; 
2229     sTmp
.Trim().Trim(false); 
2231     while (!sTmp
.empty()) 
2233         sExts
.Add(sTmp
.AfterLast(wxT(' '))); 
2234         sTmp 
= sTmp
.BeforeLast(wxT(' ')); 
2237     AddToMimeData(strMimeType
, strIcon
, NULL
, sExts
, strDesc
, true); 
2240 void wxMimeTypesManagerImpl::AddMailcapInfo(const wxString
& strType
, 
2241                                             const wxString
& strOpenCmd
, 
2242                                             const wxString
& strPrintCmd
, 
2243                                             const wxString
& strTest
, 
2244                                             const wxString
& strDesc
) 
2248     wxMimeTypeCommands 
*entry 
= new wxMimeTypeCommands
; 
2249     entry
->Add(wxT("open=")  + strOpenCmd
); 
2250     entry
->Add(wxT("print=") + strPrintCmd
); 
2251     entry
->Add(wxT("test=")  + strTest
); 
2254     wxArrayString strExtensions
; 
2256     AddToMimeData(strType
, strIcon
, entry
, strExtensions
, strDesc
, true); 
2259 bool wxMimeTypesManagerImpl::ReadMimeTypes(const wxString
& strFileName
) 
2261     wxLogTrace(TRACE_MIME
, wxT("--- Parsing mime.types file '%s' ---"), 
2262                strFileName
.c_str()); 
2264     wxMimeTextFile 
file(strFileName
); 
2268     // the information we extract 
2269     wxString strMimeType
, strDesc
, strExtensions
; 
2271     size_t nLineCount 
= file
.GetLineCount(); 
2272     const wxChar 
*pc 
= NULL
; 
2273     for ( size_t nLine 
= 0; nLine 
< nLineCount
; nLine
++ ) 
2277             // now we're at the start of the line 
2278             pc 
= file
[nLine
].c_str(); 
2282             // we didn't finish with the previous line yet 
2287         while ( wxIsspace(*pc
) ) 
2290         // comment or blank line? 
2291         if ( *pc 
== wxT('#') || !*pc 
) 
2293             // skip the whole line 
2298         // detect file format 
2299         const wxChar 
*pEqualSign 
= wxStrchr(pc
, wxT('=')); 
2300         if ( pEqualSign 
== NULL 
) 
2305             // first field is mime type 
2306             for ( strMimeType
.Empty(); !wxIsspace(*pc
) && *pc 
!= wxT('\0'); pc
++ ) 
2312             while ( wxIsspace(*pc
) ) 
2315             // take all the rest of the string 
2318             // no description... 
2326             // the string on the left of '=' is the field name 
2327             wxString 
strLHS(pc
, pEqualSign 
- pc
); 
2330             for ( pc 
= pEqualSign 
+ 1; wxIsspace(*pc
); pc
++ ) 
2334             if ( *pc 
== wxT('"') ) 
2336                 // the string is quoted and ends at the matching quote 
2337                 pEnd 
= wxStrchr(++pc
, wxT('"')); 
2340                     wxLogWarning(wxT("Mime.types file %s, line %lu: unterminated quoted string."), 
2341                                  strFileName
.c_str(), nLine 
+ 1L); 
2346                 // unquoted string ends at the first space or at the end of 
2348                 for ( pEnd 
= pc
; *pEnd 
&& !wxIsspace(*pEnd
); pEnd
++ ) 
2352             // now we have the RHS (field value) 
2353             wxString 
strRHS(pc
, pEnd 
- pc
); 
2355             // check what follows this entry 
2356             if ( *pEnd 
== wxT('"') ) 
2362             for ( pc 
= pEnd
; wxIsspace(*pc
); pc
++ ) 
2365             // if there is something left, it may be either a '\\' to continue 
2366             // the line or the next field of the same entry 
2367             bool entryEnded 
= *pc 
== wxT('\0'); 
2368             bool nextFieldOnSameLine 
= false; 
2371                 nextFieldOnSameLine 
= ((*pc 
!= wxT('\\')) || (pc
[1] != wxT('\0'))); 
2374             // now see what we got 
2375             if ( strLHS 
== wxT("type") ) 
2377                 strMimeType 
= strRHS
; 
2379             else if ( strLHS
.StartsWith(wxT("desc")) ) 
2383             else if ( strLHS 
== wxT("exts") ) 
2385                 strExtensions 
= strRHS
; 
2387             else if ( strLHS 
== wxT("icon") ) 
2389                 // this one is simply ignored: it usually refers to Netscape 
2390                 // built in icons which are useless for us anyhow 
2392             else if ( !strLHS
.StartsWith(wxT("x-")) ) 
2394                 // we suppose that all fields starting with "X-" are 
2395                 // unregistered extensions according to the standard practice, 
2396                 // but it may be worth telling the user about other junk in 
2397                 // his mime.types file 
2398                 wxLogWarning(wxT("Unknown field in file %s, line %lu: '%s'."), 
2399                              strFileName
.c_str(), nLine 
+ 1L, strLHS
.c_str()); 
2404                 if ( !nextFieldOnSameLine 
) 
2406                 //else: don't reset it 
2408                 // as we don't reset strMimeType, the next field in this entry 
2409                 // will be interpreted correctly. 
2415         // depending on the format (Mosaic or Netscape) either space or comma 
2416         // is used to separate the extensions 
2417         strExtensions
.Replace(wxT(","), wxT(" ")); 
2419         // also deal with the leading dot 
2420         if ( !strExtensions
.empty() && strExtensions
[0u] == wxT('.') ) 
2422             strExtensions
.erase(0, 1); 
2425         wxLogTrace(TRACE_MIME
, wxT("mime.types: '%s' => '%s' (%s)"), 
2426                    strExtensions
.c_str(), 
2427                    strMimeType
.c_str(), 
2430         AddMimeTypeInfo(strMimeType
, strExtensions
, strDesc
); 
2432         // finished with this line 
2439 // ---------------------------------------------------------------------------- 
2440 // UNIX mailcap files parsing 
2441 // ---------------------------------------------------------------------------- 
2443 // the data for a single MIME type 
2444 struct MailcapLineData
 
2453     wxArrayString verbs
, 
2461     MailcapLineData() { testfailed 
= needsterminal 
= copiousoutput 
= false; } 
2464 // process a non-standard (i.e. not the first or second one) mailcap field 
2466 wxMimeTypesManagerImpl::ProcessOtherMailcapField(MailcapLineData
& data
, 
2467                                                  const wxString
& curField
) 
2469     if ( curField
.empty() ) 
2475     // is this something of the form foo=bar? 
2476     if ( curField
.find('=') != wxString::npos 
) 
2478         // split "LHS = RHS" in 2 
2479         wxString lhs 
= curField
.BeforeFirst(wxT('=')), 
2480                  rhs 
= curField
.AfterFirst(wxT('=')); 
2482         lhs
.Trim(true);     // from right 
2483         rhs
.Trim(false);    // from left 
2485         // it might be quoted 
2486         if ( !rhs
.empty() && rhs
[0u] == wxT('"') && rhs
.Last() == wxT('"') ) 
2488             rhs 
= rhs
.Mid(1, rhs
.length() - 2); 
2491         // is it a command verb or something else? 
2492         if ( lhs 
== wxT("test") ) 
2494             if ( wxSystem(rhs
) == 0 ) 
2497                 wxLogTrace(TRACE_MIME_TEST
, 
2498                            wxT("Test '%s' for mime type '%s' succeeded."), 
2499                            rhs
.c_str(), data
.type
.c_str()); 
2503                 wxLogTrace(TRACE_MIME_TEST
, 
2504                            wxT("Test '%s' for mime type '%s' failed, skipping."), 
2505                            rhs
.c_str(), data
.type
.c_str()); 
2507                 data
.testfailed 
= true; 
2510         else if ( lhs 
== wxT("desc") ) 
2514         else if ( lhs 
== wxT("x11-bitmap") ) 
2518         else if ( lhs 
== wxT("notes") ) 
2522         else // not a (recognized) special case, must be a verb (e.g. "print") 
2524             data
.verbs
.Add(lhs
); 
2525             data
.commands
.Add(rhs
); 
2528     else // '=' not found 
2530         // so it must be a simple flag 
2531         if ( curField 
== wxT("needsterminal") ) 
2533             data
.needsterminal 
= true; 
2535         else if ( curField 
== wxT("copiousoutput")) 
2537             // copiousoutput impies that the viewer is a console program 
2538             data
.needsterminal 
= 
2539             data
.copiousoutput 
= true; 
2541         else if ( !IsKnownUnimportantField(curField
) ) 
2550 bool wxMimeTypesManagerImpl::ReadMailcap(const wxString
& strFileName
, 
2553     wxLogTrace(TRACE_MIME
, wxT("--- Parsing mailcap file '%s' ---"), 
2554                strFileName
.c_str()); 
2556     wxMimeTextFile 
file(strFileName
); 
2560     // indices of MIME types (in m_aTypes) we already found in this file 
2562     // (see the comments near the end of function for the reason we need this) 
2563     wxArrayInt aIndicesSeenHere
; 
2565     // accumulator for the current field 
2567     curField
.reserve(1024); 
2569     const wxChar 
*pPagerEnv 
= wxGetenv(wxT("PAGER")); 
2571     const wxArrayString empty_extensions_list
; 
2573     size_t nLineCount 
= file
.GetLineCount(); 
2574     for ( size_t nLine 
= 0; nLine 
< nLineCount
; nLine
++ ) 
2576         // now we're at the start of the line 
2577         const wxChar 
*pc 
= file
[nLine
].c_str(); 
2580         while ( wxIsspace(*pc
) ) 
2583         // comment or empty string? 
2584         if ( *pc 
== wxT('#') || *pc 
== wxT('\0') ) 
2590         // what field are we currently in? The first 2 are fixed and there may 
2591         // be an arbitrary number of other fields parsed by 
2592         // ProcessOtherMailcapField() 
2594         // the first field is the MIME type 
2600         } currentToken 
= Field_Type
; 
2602         // the flags and field values on the current line 
2603         MailcapLineData data
; 
2611                     // interpret the next character literally (notice that 
2612                     // backslash can be used for line continuation) 
2613                     if ( *++pc 
== wxT('\0') ) 
2615                         // fetch the next line if there is one 
2616                         if ( nLine 
== nLineCount 
- 1 ) 
2618                             // something is wrong, bail out 
2621                             wxLogDebug(wxT("Mailcap file %s, line %lu: '\\' on the end of the last line ignored."), 
2622                                        strFileName
.c_str(), 
2627                             // pass to the beginning of the next line 
2628                             pc 
= file
[++nLine
].c_str(); 
2630                             // skip pc++ at the end of the loop 
2636                         // just a normal character 
2642                     cont 
= false;   // end of line reached, exit the loop 
2644                     // fall through to still process this field 
2647                     // trim whitespaces from both sides 
2648                     curField
.Trim(true).Trim(false); 
2650                     switch ( currentToken 
) 
2653                             data
.type 
= curField
.Lower(); 
2654                             if ( data
.type
.empty() ) 
2656                                 // I don't think that this is a valid mailcap 
2657                                 // entry, but try to interpret it somehow 
2658                                 data
.type 
= wxT('*'); 
2661                             if ( data
.type
.Find(wxT('/')) == wxNOT_FOUND 
) 
2663                                 // we interpret "type" as "type/*" 
2664                                 data
.type 
+= wxT("/*"); 
2667                             currentToken 
= Field_OpenCmd
; 
2671                             data
.cmdOpen 
= curField
; 
2673                             currentToken 
= Field_Other
; 
2677                             if ( !ProcessOtherMailcapField(data
, curField
) ) 
2679                                 // don't flood the user with error messages if 
2680                                 // we don't understand something in his 
2681                                 // mailcap, but give them in debug mode because 
2682                                 // this might be useful for the programmer 
2685                                     wxT("Mailcap file %s, line %lu: unknown field '%s' for the MIME type '%s' ignored."), 
2686                                     strFileName
.c_str(), 
2692                             else if ( data
.testfailed 
) 
2694                                 // skip this entry entirely 
2698                             // it already has this value 
2699                             //currentToken = Field_Other; 
2703                             wxFAIL_MSG(wxT("unknown field type in mailcap")); 
2706                     // next token starts immediately after ';' 
2714             // continue in the same line 
2718         // we read the entire entry, check what have we got 
2719         // ------------------------------------------------ 
2721         // check that we really read something reasonable 
2722         if ( currentToken 
< Field_Other 
) 
2724             wxLogWarning(wxT("Mailcap file %s, line %lu: incomplete entry ignored."), 
2725                          strFileName
.c_str(), nLine 
+ 1L); 
2730         // if the test command failed, it's as if the entry were not there at all 
2731         if ( data
.testfailed 
) 
2736         // support for flags: 
2737         //  1. create an xterm for 'needsterminal' 
2738         //  2. append "| $PAGER" for 'copiousoutput' 
2740         // Note that the RFC says that having both needsterminal and 
2741         // copiousoutput is probably a mistake, so it seems that running 
2742         // programs with copiousoutput inside an xterm as it is done now 
2743         // is a bad idea (FIXME) 
2744         if ( data
.copiousoutput 
) 
2746             data
.cmdOpen 
<< wxT(" | ") << (pPagerEnv 
? pPagerEnv 
: wxT("more")); 
2749         if ( data
.needsterminal 
) 
2751             data
.cmdOpen
.insert(0, wxT("xterm -e sh -c '")); 
2752             data
.cmdOpen
.append(wxT("'")); 
2755         if ( !data
.cmdOpen
.empty() ) 
2757             data
.verbs
.Insert(wxT("open"), 0); 
2758             data
.commands
.Insert(data
.cmdOpen
, 0); 
2761         // we have to decide whether the new entry should replace any entries 
2762         // for the same MIME type we had previously found or not 
2765         // the fall back entries have the lowest priority, by definition 
2772             // have we seen this one before? 
2773             int nIndex 
= m_aTypes
.Index(data
.type
); 
2775             // and if we have, was it in this file? if not, we should 
2776             // overwrite the previously seen one 
2777             overwrite 
= nIndex 
== wxNOT_FOUND 
|| 
2778                             aIndicesSeenHere
.Index(nIndex
) == wxNOT_FOUND
; 
2781         wxLogTrace(TRACE_MIME
, wxT("mailcap %s: %s [%s]"), 
2782                    data
.type
.c_str(), data
.cmdOpen
.c_str(), 
2783                    overwrite 
? wxT("replace") : wxT("add")); 
2785         int n 
= AddToMimeData
 
2789                     new wxMimeTypeCommands(data
.verbs
, data
.commands
), 
2790                     empty_extensions_list
, 
2797             aIndicesSeenHere
.Add(n
); 
2804 size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString
& mimetypes
) 
2810     size_t count 
= m_aTypes
.GetCount(); 
2811     for ( size_t n 
= 0; n 
< count
; n
++ ) 
2813         // don't return template types from here (i.e. anything containg '*') 
2814         const wxString 
&type 
= m_aTypes
[n
]; 
2815         if ( type
.Find(wxT('*')) == wxNOT_FOUND 
) 
2817             mimetypes
.Add(type
); 
2821     return mimetypes
.GetCount(); 
2824 // ---------------------------------------------------------------------------- 
2825 // writing to MIME type files 
2826 // ---------------------------------------------------------------------------- 
2828 bool wxMimeTypesManagerImpl::Unassociate(wxFileType 
*ft
) 
2832     wxArrayString sMimeTypes
; 
2833     ft
->GetMimeTypes(sMimeTypes
); 
2836     size_t nCount 
= sMimeTypes
.GetCount(); 
2837     for (i 
= 0; i 
< nCount
; i 
++) 
2839         const wxString 
&sMime 
= sMimeTypes
.Item(i
); 
2840         int nIndex 
= m_aTypes
.Index(sMime
); 
2841         if ( nIndex 
== wxNOT_FOUND
) 
2843             // error if we get here ?? 
2848             WriteMimeInfo(nIndex
, true); 
2849             m_aTypes
.RemoveAt(nIndex
); 
2850             m_aEntries
.RemoveAt(nIndex
); 
2851             m_aExtensions
.RemoveAt(nIndex
); 
2852             m_aDescriptions
.RemoveAt(nIndex
); 
2853             m_aIcons
.RemoveAt(nIndex
); 
2856     // check data integrity 
2857     wxASSERT( m_aTypes
.GetCount() == m_aEntries
.GetCount() && 
2858             m_aTypes
.GetCount() == m_aExtensions
.GetCount() && 
2859             m_aTypes
.GetCount() == m_aIcons
.GetCount() && 
2860             m_aTypes
.GetCount() == m_aDescriptions
.GetCount() ); 
2865 // ---------------------------------------------------------------------------- 
2866 // private functions 
2867 // ---------------------------------------------------------------------------- 
2869 static bool IsKnownUnimportantField(const wxString
& fieldAll
) 
2871     static const wxChar 
* const knownFields
[] = 
2873         wxT("x-mozilla-flags"), 
2874         wxT("nametemplate"), 
2875         wxT("textualnewlines"), 
2878     wxString field 
= fieldAll
.BeforeFirst(wxT('=')); 
2879     for ( size_t n 
= 0; n 
< WXSIZEOF(knownFields
); n
++ ) 
2881         if ( field
.CmpNoCase(knownFields
[n
]) == 0 ) 
2889   // wxUSE_MIMETYPE && wxUSE_FILE && wxUSE_TEXTFILE