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 extends wxTextFile 
  74 class wxMimeTextFile 
: public wxTextFile
 
  78     wxMimeTextFile () : wxTextFile () {}; 
  79     wxMimeTextFile(const wxString
& strFile
) : wxTextFile(strFile
) {}; 
  81     int pIndexOf(const wxString 
& sSearch
, bool bIncludeComments 
= false, int iStart 
= 0) 
  84         int nResult 
= wxNOT_FOUND
; 
  85         if (i 
>= GetLineCount()) 
  88         wxString sTest 
= sSearch
; 
  94             while ( i 
< GetLineCount() ) 
  98                 if (sLine
.Contains(sTest
)) 
 106             while ( (i 
< GetLineCount()) ) 
 110                 if ( ! sLine
.StartsWith(wxT("#"))) 
 112                     if (sLine
.Contains(sTest
)) 
 123     bool CommentLine(int nIndex
) 
 127         if (nIndex 
>= (int)GetLineCount() ) 
 130         GetLine(nIndex
) = GetLine(nIndex
).Prepend(wxT("#")); 
 134     bool CommentLine(const wxString 
& sTest
) 
 136         int nIndex 
= pIndexOf(sTest
); 
 139         if (nIndex 
>= (int)GetLineCount() ) 
 142         GetLine(nIndex
) = GetLine(nIndex
).Prepend(wxT("#")); 
 146     wxString 
GetVerb(size_t i
) 
 148         if (i 
> GetLineCount() ) 
 149             return wxEmptyString
; 
 151         wxString sTmp 
= GetLine(i
).BeforeFirst(wxT('=')); 
 155     wxString 
GetCmd(size_t i
) 
 157         if (i 
> GetLineCount() ) 
 158             return wxEmptyString
; 
 160         wxString sTmp 
= GetLine(i
).AfterFirst(wxT('=')); 
 165 // in case we're compiling in non-GUI mode 
 166 class WXDLLEXPORT wxIcon
; 
 168 // ---------------------------------------------------------------------------- 
 170 // ---------------------------------------------------------------------------- 
 172 // MIME code tracing mask 
 173 #define TRACE_MIME wxT("mime") 
 175 // give trace messages about the results of mailcap tests 
 176 #define TRACE_MIME_TEST wxT("mimetest") 
 178 // ---------------------------------------------------------------------------- 
 180 // ---------------------------------------------------------------------------- 
 182 // there are some fields which we don't understand but for which we don't give 
 183 // warnings as we know that they're not important - this function is used to 
 185 static bool IsKnownUnimportantField(const wxString
& field
); 
 187 // ---------------------------------------------------------------------------- 
 189 // ---------------------------------------------------------------------------- 
 192 // This class uses both mailcap and mime.types to gather information about file 
 195 // The information about mailcap file was extracted from metamail(1) sources 
 196 // and documentation and subsequently revised when I found the RFC 1524 
 199 // Format of mailcap file: spaces are ignored, each line is either a comment 
 200 // (starts with '#') or a line of the form <field1>;<field2>;...;<fieldN>. 
 201 // A backslash can be used to quote semicolons and newlines (and, in fact, 
 202 // anything else including itself). 
 204 // The first field is always the MIME type in the form of type/subtype (see RFC 
 205 // 822) where subtype may be '*' meaning "any". Following metamail, we accept 
 206 // "type" which means the same as "type/*", although I'm not sure whether this 
 209 // The second field is always the command to run. It is subject to 
 210 // parameter/filename expansion described below. 
 212 // All the following fields are optional and may not be present at all. If 
 213 // they're present they may appear in any order, although each of them should 
 214 // appear only once. The optional fields are the following: 
 215 //  * notes=xxx is an uninterpreted string which is silently ignored 
 216 //  * test=xxx is the command to be used to determine whether this mailcap line 
 217 //    applies to our data or not. The RHS of this field goes through the 
 218 //    parameter/filename expansion (as the 2nd field) and the resulting string 
 219 //    is executed. The line applies only if the command succeeds, i.e. returns 0 
 221 //  * print=xxx is the command to be used to print (and not view) the data of 
 222 //    this type (parameter/filename expansion is done here too) 
 223 //  * edit=xxx is the command to open/edit the data of this type 
 224 //  * needsterminal means that a new interactive console must be created for 
 226 //  * copiousoutput means that the viewer doesn't interact with the user but 
 227 //    produces (possibly) a lof of lines of output on stdout (i.e. "cat" is a 
 228 //    good example), thus it might be a good idea to use some kind of paging 
 230 //  * textualnewlines means not to perform CR/LF translation (not honored) 
 231 //  * compose and composetyped fields are used to determine the program to be 
 232 //    called to create a new message pert in the specified format (unused). 
 234 // Parameter/filename expansion: 
 235 //  * %s is replaced with the (full) file name 
 236 //  * %t is replaced with MIME type/subtype of the entry 
 237 //  * for multipart type only %n is replaced with the nnumber of parts and %F is 
 238 //    replaced by an array of (content-type, temporary file name) pairs for all 
 239 //    message parts (TODO) 
 240 //  * %{parameter} is replaced with the value of parameter taken from 
 241 //    Content-type header line of the message. 
 244 // There are 2 possible formats for mime.types file, one entry per line (used 
 245 // for global mime.types and called Mosaic format) and "expanded" format where 
 246 // an entry takes multiple lines (used for users mime.types and called 
 249 // For both formats spaces are ignored and lines starting with a '#' are 
 250 // comments. Each record has one of two following forms: 
 251 //  a) for "brief" format: 
 252 //      <mime type>  <space separated list of extensions> 
 253 //  b) for "expanded" format: 
 254 //      type=<mime type> BACKSLASH 
 255 //      desc="<description>" BACKSLASH 
 256 //      exts="<comma separated list of extensions>" 
 258 // (where BACKSLASH is a literal '\\' which we can't put here because cpp 
 261 // We try to autodetect the format of mime.types: if a non-comment line starts 
 262 // with "type=" we assume the second format, otherwise the first one. 
 264 // there may be more than one entry for one and the same mime type, to 
 265 // choose the right one we have to run the command specified in the test 
 266 // field on our data. 
 268 // ---------------------------------------------------------------------------- 
 270 // ---------------------------------------------------------------------------- 
 272 // GNOME stores the info we're interested in in several locations: 
 273 //  1. xxx.keys files under /usr/share/mime-info 
 274 //  2. xxx.keys files under ~/.gnome/mime-info 
 276 // Update (Chris Elliott): apparently there may be an optional "[lang]" prefix 
 277 // just before the field name. 
 280 void wxMimeTypesManagerImpl::LoadGnomeDataFromKeyFile(const wxString
& filename
, 
 281                                                       const wxArrayString
& dirs
) 
 283     wxTextFile 
textfile(filename
); 
 284 #if defined(__WXGTK20__) && wxUSE_UNICODE 
 285     if ( !textfile
.Open(wxMBConvUTF8(wxMBConvUTF8::MAP_INVALID_UTF8_TO_OCTAL
)) ) 
 287     if ( !textfile
.Open() ) 
 291     wxLogTrace(TRACE_MIME
, wxT("--- Opened Gnome file %s  ---"), 
 294     wxArrayString 
search_dirs( dirs 
); 
 296     // values for the entry being parsed 
 297     wxString curMimeType
, curIconFile
; 
 298     wxMimeTypeCommands 
* entry 
= new wxMimeTypeCommands
; 
 300     wxArrayString strExtensions
; 
 304     size_t nLineCount 
= textfile
.GetLineCount(); 
 306     while ( nLine 
< nLineCount 
) 
 308         pc 
= textfile
[nLine
].c_str(); 
 309         if ( *pc 
!= wxT('#') ) 
 312             wxLogTrace(TRACE_MIME
, wxT("--- Reading from Gnome file %s '%s' ---"), 
 313                     filename
.c_str(), pc
); 
 315             // trim trailing space and tab 
 316             while ((*pc 
== wxT(' ')) || (*pc 
== wxT('\t'))) 
 320             int equal_pos 
= sTmp
.Find( wxT('=') ); 
 323                 wxString left_of_equal 
= sTmp
.Left( equal_pos 
); 
 324                 const wxChar 
*right_of_equal 
= pc
; 
 325                 right_of_equal 
+= equal_pos
+1; 
 327                 if (left_of_equal 
== wxT("icon_filename")) 
 330                     curIconFile 
= right_of_equal
; 
 332                     wxFileName 
newFile( curIconFile 
); 
 333                     if (newFile
.IsRelative() || newFile
.FileExists()) 
 335                         size_t nDirs 
= search_dirs
.GetCount(); 
 337                         for (size_t nDir 
= 0; nDir 
< nDirs
; nDir
++) 
 339                             newFile
.SetPath( search_dirs
[nDir
] ); 
 340                             newFile
.AppendDir( wxT("pixmaps") ); 
 341                             newFile
.AppendDir( wxT("document-icons") ); 
 342                             newFile
.SetExt( wxT("png") ); 
 343                             if (newFile
.FileExists()) 
 345                                 curIconFile 
= newFile
.GetFullPath(); 
 346                                 // reorder search_dirs for speedup (fewer 
 347                                 // calls to FileExist() required) 
 350                                     const wxString 
&tmp 
= search_dirs
[nDir
]; 
 351                                     search_dirs
.RemoveAt( nDir 
); 
 352                                     search_dirs
.Insert( tmp
, 0 ); 
 359                 else if (left_of_equal 
== wxT("open")) 
 361                     sTmp 
= right_of_equal
; 
 362                     sTmp
.Replace( wxT("%f"), wxT("%s") ); 
 363                     sTmp
.Prepend( wxT("open=") ); 
 366                 else if (left_of_equal 
== wxT("view")) 
 368                     sTmp 
= right_of_equal
; 
 369                     sTmp
.Replace( wxT("%f"), wxT("%s") ); 
 370                     sTmp
.Prepend( wxT("view=") ); 
 373                 else if (left_of_equal 
== wxT("print")) 
 375                     sTmp 
= right_of_equal
; 
 376                     sTmp
.Replace( wxT("%f"), wxT("%s") ); 
 377                     sTmp
.Prepend( wxT("print=") ); 
 380                 else if (left_of_equal 
== wxT("description")) 
 382                     strDesc 
= right_of_equal
; 
 384                 else if (left_of_equal 
== wxT("short_list_application_ids_for_novice_user_level")) 
 386                     sTmp 
= right_of_equal
; 
 387                     if (sTmp
.Contains( wxT(",") )) 
 388                         sTmp 
= sTmp
.BeforeFirst( wxT(',') ); 
 389                     sTmp
.Prepend( wxT("open=") ); 
 390                     sTmp
.Append( wxT(" %s") ); 
 394             } // emd of has an equals sign 
 397                 // not a comment and not an equals sign 
 398                 if (sTmp
.Contains(wxT('/'))) 
 400                     // this is the start of the new mimetype 
 401                     // overwrite any existing data 
 402                     if (! curMimeType
.empty()) 
 404                         AddToMimeData( curMimeType
, curIconFile
, entry
, strExtensions
, strDesc 
); 
 406                         // now get ready for next bit 
 407                         entry 
= new wxMimeTypeCommands
; 
 410                     curMimeType 
= sTmp
.BeforeFirst(wxT(':')); 
 413         } // end of not a comment 
 415         // ignore blank lines 
 417     } // end of while, save any data 
 419     if ( curMimeType
.empty() ) 
 422         AddToMimeData( curMimeType
, curIconFile
, entry
, strExtensions
, strDesc
); 
 425 void wxMimeTypesManagerImpl::LoadGnomeMimeTypesFromMimeFile(const wxString
& filename
) 
 427     wxTextFile 
textfile(filename
); 
 428     if ( !textfile
.Open() ) 
 431     wxLogTrace(TRACE_MIME
, 
 432                wxT("--- Opened Gnome file %s  ---"), 
 435     // values for the entry being parsed 
 436     wxString curMimeType
, curExtList
; 
 439     size_t nLineCount 
= textfile
.GetLineCount(); 
 440     for ( size_t nLine 
= 0; /* nothing */; nLine
++ ) 
 442         if ( nLine 
< nLineCount 
) 
 444             pc 
= textfile
[nLine
].c_str(); 
 445             if ( *pc 
== wxT('#') ) 
 453             // so that we will fall into the "if" below 
 460             if ( !curMimeType
.empty() && !curExtList
.empty() ) 
 462                  wxLogTrace(TRACE_MIME
, 
 463                             wxT("--- At end of Gnome file  finding mimetype %s  ---"), 
 464                             curMimeType
.c_str()); 
 466                  AddMimeTypeInfo(curMimeType
, curExtList
, wxEmptyString
); 
 471                 // the end: this can only happen if nLine == nLineCount 
 480         // what do we have here? 
 481         if ( *pc 
== wxT('\t') ) 
 483             // this is a field=value ling 
 484             pc
++; // skip leading TAB 
 486             static const int lenField 
= 5; // strlen("ext: ") 
 487             if ( wxStrncmp(pc
, wxT("ext: "), lenField
) == 0 ) 
 489                 // skip it and take everything left until the end of line 
 490                 curExtList 
= pc 
+ lenField
; 
 492             //else: some other field, we don't care 
 496             // this is the start of the new section 
 497             wxLogTrace(TRACE_MIME
, 
 498                        wxT("--- In Gnome file  finding mimetype %s  ---"), 
 499                        curMimeType
.c_str()); 
 501             if (! curMimeType
.empty()) 
 502                 AddMimeTypeInfo(curMimeType
, curExtList
, wxEmptyString
); 
 506             while ( *pc 
!= wxT(':') && *pc 
!= wxT('\0') ) 
 508                 curMimeType 
+= *pc
++; 
 515 void wxMimeTypesManagerImpl::LoadGnomeMimeFilesFromDir( 
 516                       const wxString
& dirbase
, const wxArrayString
& dirs
) 
 518     wxASSERT_MSG( !dirbase
.empty() && !wxEndsWithPathSeparator(dirbase
), 
 519                   wxT("base directory shouldn't end with a slash") ); 
 521     wxString dirname 
= dirbase
; 
 522     dirname 
<< wxT("/mime-info"); 
 524     if ( !wxDir::Exists(dirname
) ) 
 528     if ( !dir
.IsOpened() ) 
 531     // we will concatenate it with filename to get the full path below 
 537     cont 
= dir
.GetFirst(&filename
, wxT("*.mime"), wxDIR_FILES
); 
 540         LoadGnomeMimeTypesFromMimeFile(dirname 
+ filename
); 
 542         cont 
= dir
.GetNext(&filename
); 
 545     cont 
= dir
.GetFirst(&filename
, wxT("*.keys"), wxDIR_FILES
); 
 548         LoadGnomeDataFromKeyFile(dirname 
+ filename
, dirs
); 
 550         cont 
= dir
.GetNext(&filename
); 
 553     // FIXME: Hack alert: We scan all icons and deduce the 
 554     //             mime-type from the file name. 
 556     dirname 
<< wxT("/pixmaps/document-icons"); 
 558     // these are always empty in this file 
 559     wxArrayString strExtensions
; 
 562     if ( !wxDir::Exists(dirname
) ) 
 564         // Just test for default GPE dir also 
 565         dirname 
= wxT("/usr/share/gpe/pixmaps/default/filemanager/document-icons"); 
 567         if ( !wxDir::Exists(dirname
) ) 
 571     wxDir 
dir2( dirname 
); 
 573     cont 
= dir2
.GetFirst(&filename
, wxT("gnome-*.png"), wxDIR_FILES
); 
 576         wxString mimeType 
= filename
; 
 577         mimeType
.Remove( 0, 6 ); // remove "gnome-" 
 578         mimeType
.Remove( mimeType
.Len() - 4, 4 ); // remove ".png" 
 579         int pos 
= mimeType
.Find( wxT("-") ); 
 580         if (pos 
!= wxNOT_FOUND
) 
 582             mimeType
.SetChar( pos
, wxT('/') ); 
 583             wxString iconFile 
= dirname
; 
 584             iconFile 
<< wxT("/"); 
 585             iconFile 
<< filename
; 
 586             AddToMimeData( mimeType
, iconFile
, NULL
, strExtensions
, strDesc
, true ); 
 589         cont 
= dir2
.GetNext(&filename
); 
 593 void wxMimeTypesManagerImpl::GetGnomeMimeInfo(const wxString
& sExtraDir
) 
 597     wxString gnomedir 
= wxGetenv( wxT("GNOMEDIR") ); 
 598     if (!gnomedir
.empty()) 
 600         gnomedir 
<< wxT("/share"); 
 601         dirs
.Add( gnomedir 
); 
 604     dirs
.Add(wxT("/usr/share")); 
 605     dirs
.Add(wxT("/usr/local/share")); 
 607     gnomedir 
= wxGetHomeDir(); 
 608     gnomedir 
<< wxT("/.gnome"); 
 609     dirs
.Add( gnomedir 
); 
 611     if (!sExtraDir
.empty()) 
 612         dirs
.Add( sExtraDir 
); 
 614     size_t nDirs 
= dirs
.GetCount(); 
 615     for ( size_t nDir 
= 0; nDir 
< nDirs
; nDir
++ ) 
 617         LoadGnomeMimeFilesFromDir(dirs
[nDir
], dirs
); 
 621 // ---------------------------------------------------------------------------- 
 623 // ---------------------------------------------------------------------------- 
 626 // KDE stores the icon info in its .kdelnk files. The file for mimetype/subtype 
 627 // may be found in either of the following locations 
 629 //  1. $KDEDIR/share/mimelnk/mimetype/subtype.kdelnk 
 630 //  2. ~/.kde/share/mimelnk/mimetype/subtype.kdelnk 
 632 // The format of a .kdelnk file is almost the same as the one used by 
 633 // wxFileConfig, i.e. there are groups, comments and entries. The icon is the 
 634 // value for the entry "Type" 
 636 // kde writing; see http://webcvs.kde.org/cgi-bin/cvsweb.cgi/~checkout~/kdelibs/kio/DESKTOP_ENTRY_STANDARD 
 637 // for now write to .kdelnk but should eventually do .desktop instead (in preference??) 
 639 bool wxMimeTypesManagerImpl::CheckKDEDirsExist( const wxString 
&sOK
, const wxString 
&sTest 
) 
 643         return wxDir::Exists(sOK
); 
 647         wxString sStart 
= sOK 
+ wxT("/") + sTest
.BeforeFirst(wxT('/')); 
 648         if (!wxDir::Exists(sStart
)) 
 650         wxString sEnd 
= sTest
.AfterFirst(wxT('/')); 
 651         return CheckKDEDirsExist(sStart
, sEnd
); 
 655 bool wxMimeTypesManagerImpl::WriteKDEMimeFile(int index
, bool delete_index
) 
 657     wxMimeTextFile appoutfile
, mimeoutfile
; 
 658     wxString sHome 
= wxGetHomeDir(); 
 659     wxString sTmp 
= wxT(".kde/share/mimelnk/"); 
 660     wxString sMime 
= m_aTypes
[index
]; 
 661     CheckKDEDirsExist(sHome
, sTmp 
+ sMime
.BeforeFirst(wxT('/')) ); 
 662     sTmp 
= sHome 
+ wxT('/') + sTmp 
+ sMime 
+ wxT(".kdelnk"); 
 665     bool bMimeExists 
= mimeoutfile
.Open(sTmp
); 
 668         bTemp 
= mimeoutfile
.Create(sTmp
); 
 669         // some unknown error eg out of disk space 
 674     sTmp 
= wxT(".kde/share/applnk/"); 
 675     CheckKDEDirsExist(sHome
, sTmp 
+ sMime
.AfterFirst(wxT('/')) ); 
 676     sTmp 
= sHome 
+ wxT('/') + sTmp 
+ sMime
.AfterFirst(wxT('/')) + wxT(".kdelnk"); 
 679     bAppExists 
= appoutfile
.Open(sTmp
); 
 682         bTemp 
= appoutfile
.Create(sTmp
); 
 683         // some unknown error eg out of disk space 
 688     // fixed data; write if new file 
 691         mimeoutfile
.AddLine(wxT("#KDE Config File")); 
 692         mimeoutfile
.AddLine(wxT("[KDE Desktop Entry]")); 
 693         mimeoutfile
.AddLine(wxT("Version=1.0")); 
 694         mimeoutfile
.AddLine(wxT("Type=MimeType")); 
 695         mimeoutfile
.AddLine(wxT("MimeType=") + sMime
); 
 700         mimeoutfile
.AddLine(wxT("#KDE Config File")); 
 701         mimeoutfile
.AddLine(wxT("[KDE Desktop Entry]")); 
 702         appoutfile
.AddLine(wxT("Version=1.0")); 
 703         appoutfile
.AddLine(wxT("Type=Application")); 
 704         appoutfile
.AddLine(wxT("MimeType=") + sMime 
+ wxT(';')); 
 709     mimeoutfile
.CommentLine(wxT("Comment=")); 
 711         mimeoutfile
.AddLine(wxT("Comment=") + m_aDescriptions
[index
]); 
 712     appoutfile
.CommentLine(wxT("Name=")); 
 714         appoutfile
.AddLine(wxT("Comment=") + m_aDescriptions
[index
]); 
 716     sTmp 
= m_aIcons
[index
]; 
 717     // we can either give the full path, or the shortfilename if its in 
 718     // one of the directories we search 
 719     mimeoutfile
.CommentLine(wxT("Icon=") ); 
 721         mimeoutfile
.AddLine(wxT("Icon=") + sTmp 
); 
 722     appoutfile
.CommentLine(wxT("Icon=") ); 
 724         appoutfile
.AddLine(wxT("Icon=") + sTmp 
); 
 726     sTmp 
= wxT(" ") + m_aExtensions
[index
]; 
 728     wxStringTokenizer 
tokenizer(sTmp
, wxT(" ")); 
 729     sTmp 
= wxT("Patterns="); 
 730     mimeoutfile
.CommentLine(sTmp
); 
 731     while ( tokenizer
.HasMoreTokens() ) 
 733         // holds an extension; need to change it to *.ext; 
 734         wxString e 
= wxT("*.") + tokenizer
.GetNextToken() + wxT(";"); 
 739         mimeoutfile
.AddLine(sTmp
); 
 741     wxMimeTypeCommands 
* entries 
= m_aEntries
[index
]; 
 742     // if we don't find open just have an empty string ... FIX this 
 743     sTmp 
= entries
->GetCommandForVerb(wxT("open")); 
 744     sTmp
.Replace( wxT("%s"), wxT("%f") ); 
 746     mimeoutfile
.CommentLine(wxT("DefaultApp=") ); 
 748         mimeoutfile
.AddLine(wxT("DefaultApp=") + sTmp
); 
 750     sTmp
.Replace( wxT("%f"), wxT("") ); 
 751     appoutfile
.CommentLine(wxT("Exec=")); 
 753         appoutfile
.AddLine(wxT("Exec=") + sTmp
); 
 755     if (entries
->GetCount() > 1) 
 757         //other actions as well as open 
 761     if (mimeoutfile
.Write()) 
 764     if (appoutfile
.Write()) 
 771 void wxMimeTypesManagerImpl::LoadKDELinksForMimeSubtype(const wxString
& dirbase
, 
 772                                                const wxString
& subdir
, 
 773                                                const wxString
& filename
, 
 774                                                const wxArrayString
& icondirs
) 
 777     if ( !file
.Open(dirbase 
+ filename
) ) 
 780     wxLogTrace(TRACE_MIME
, wxT("loading KDE file %s"), 
 781                            (dirbase 
+ filename
).c_str()); 
 783     wxMimeTypeCommands 
* entry 
= new wxMimeTypeCommands
; 
 785     wxString mimetype
, mime_desc
, strIcon
; 
 787     int nIndex 
= file
.pIndexOf( wxT("MimeType=") ); 
 788     if (nIndex 
== wxNOT_FOUND
) 
 790         // construct mimetype from the directory name and the basename of the 
 791         // file (it always has .kdelnk extension) 
 792         mimetype 
<< subdir 
<< wxT('/') << filename
.BeforeLast( wxT('.') ); 
 795         mimetype 
= file
.GetCmd(nIndex
); 
 797     // first find the description string: it is the value in either "Comment=" 
 798     // line or "Comment[<locale_name>]=" one 
 799     nIndex 
= wxNOT_FOUND
; 
 804     wxLocale 
*locale 
= wxGetLocale(); 
 807         // try "Comment[locale name]" first 
 808         comment 
<< wxT("Comment[") + locale
->GetName() + wxT("]="); 
 809         nIndex 
= file
.pIndexOf(comment
); 
 813     if ( nIndex 
== wxNOT_FOUND 
) 
 815         comment 
= wxT("Comment="); 
 816         nIndex 
= file
.pIndexOf(comment
); 
 819     if ( nIndex 
!= wxNOT_FOUND 
) 
 820         mime_desc 
= file
.GetCmd(nIndex
); 
 821     //else: no description 
 823     // next find the extensions 
 824     wxString mime_extension
; 
 826     nIndex 
= file
.pIndexOf(wxT("Patterns=")); 
 827     if ( nIndex 
!= wxNOT_FOUND 
) 
 829         wxString exts 
= file
.GetCmd(nIndex
); 
 831         wxStringTokenizer 
tokenizer(exts
, wxT(";")); 
 832         while ( tokenizer
.HasMoreTokens() ) 
 834             wxString e 
= tokenizer
.GetNextToken(); 
 836             // don't support too difficult patterns 
 837             if ( e
.Left(2) != wxT("*.") ) 
 840             if ( !mime_extension
.empty() ) 
 842                 // separate from the previous ext 
 843                 mime_extension 
<< wxT(' '); 
 846             mime_extension 
<< e
.Mid(2); 
 850     sExts
.Add(mime_extension
); 
 852     // ok, now we can take care of icon: 
 854     nIndex 
= file
.pIndexOf(wxT("Icon=")); 
 855     if ( nIndex 
!= wxNOT_FOUND 
) 
 857         strIcon 
= file
.GetCmd(nIndex
); 
 859         wxLogTrace(TRACE_MIME
, wxT("  icon %s"), strIcon
.c_str()); 
 861         // it could be the real path, but more often a short name 
 862         if (!wxFileExists(strIcon
)) 
 864             // icon is just the short name 
 865             if ( !strIcon
.empty() ) 
 867                 // we must check if the file exists because it may be stored 
 868                 // in many locations, at least ~/.kde and $KDEDIR 
 869                 size_t nDir
, nDirs 
= icondirs
.GetCount(); 
 870                 for ( nDir 
= 0; nDir 
< nDirs
; nDir
++ ) 
 872                     wxFileName 
fnameIcon( strIcon 
); 
 873                     wxFileName 
fname( icondirs
[nDir
], fnameIcon
.GetName() ); 
 874                     fname
.SetExt( wxT("png") ); 
 875                     if (fname
.FileExists()) 
 877                         strIcon 
= fname
.GetFullPath(); 
 878                         wxLogTrace(TRACE_MIME
, wxT("  iconfile %s"), strIcon
.c_str()); 
 886     // now look for lines which know about the application 
 887     // exec= or DefaultApp= 
 889     nIndex 
= file
.pIndexOf(wxT("DefaultApp")); 
 891     if ( nIndex 
== wxNOT_FOUND 
) 
 894         nIndex 
= file
.pIndexOf(wxT("Exec")); 
 897     if ( nIndex 
!= wxNOT_FOUND 
) 
 899         // we expect %f; others including  %F and %U and %u are possible 
 900         wxString sTmp 
= file
.GetCmd(nIndex
); 
 901         if (0 == sTmp
.Replace( wxT("%f"), wxT("%s") )) 
 903         entry
->AddOrReplaceVerb(wxString(wxT("open")), sTmp 
); 
 906     AddToMimeData(mimetype
, strIcon
, entry
, sExts
, mime_desc
); 
 909 void wxMimeTypesManagerImpl::LoadKDELinksForMimeType(const wxString
& dirbase
, 
 910                                             const wxString
& subdir
, 
 911                                             const wxArrayString
& icondirs
) 
 913     wxString dirname 
= dirbase
; 
 916     if ( !dir
.IsOpened() ) 
 919     wxLogTrace(TRACE_MIME
, wxT("--- Loading from KDE directory %s  ---"), 
 925     bool cont 
= dir
.GetFirst(&filename
, wxT("*.kdelnk"), wxDIR_FILES
); 
 928         LoadKDELinksForMimeSubtype(dirname
, subdir
, filename
, icondirs
); 
 930         cont 
= dir
.GetNext(&filename
); 
 933     // new standard for Gnome and KDE 
 934     cont 
= dir
.GetFirst(&filename
, wxT("*.desktop"), wxDIR_FILES
); 
 937         LoadKDELinksForMimeSubtype(dirname
, subdir
, filename
, icondirs
); 
 939         cont 
= dir
.GetNext(&filename
); 
 943 void wxMimeTypesManagerImpl::LoadKDELinkFilesFromDir(const wxString
& dirbase
, 
 944                                             const wxArrayString
& icondirs
) 
 946     wxASSERT_MSG( !dirbase
.empty() && !wxEndsWithPathSeparator(dirbase
), 
 947                   wxT("base directory shouldn't end with a slash") ); 
 949     wxString dirname 
= dirbase
; 
 950     dirname 
<< wxT("/mimelnk"); 
 952     if ( !wxDir::Exists(dirname
) ) 
 956     if ( !dir
.IsOpened() ) 
 959     // we will concatenate it with dir name to get the full path below 
 963     bool cont 
= dir
.GetFirst(&subdir
, wxEmptyString
, wxDIR_DIRS
); 
 966         LoadKDELinksForMimeType(dirname
, subdir
, icondirs
); 
 968         cont 
= dir
.GetNext(&subdir
); 
 972 void wxMimeTypesManagerImpl::GetKDEMimeInfo(const wxString
& sExtraDir
) 
 975     wxArrayString icondirs
; 
 977     // FIXME: This code is heavily broken. There are three bugs in it: 
 978     //        1) it uses only KDEDIR, which is deprecated, instead of using 
 979     //           list of paths from KDEDIRS and using KDEDIR only if KDEDIRS 
 981     //        2) it doesn't look into ~/.kde/share/config/kdeglobals where 
 982     //           user's settings are stored and thus *ignores* user's settings 
 983     //           instead of respecting them 
 984     //        3) it "tries to guess KDEDIR" and "tries a few likely theme 
 985     //           names", both of which is completely arbitrary; instead, the 
 986     //           code should give up if KDEDIR(S) is not set and/or the icon 
 987     //           theme cannot be determined, because it means that the user is 
 988     //           not using KDE (and thus is not interested in KDE icons anyway) 
 990     // the variable $KDEDIR is set when KDE is running 
 991     wxString kdedir 
= wxGetenv( wxT("KDEDIR") ); 
 995         // $(KDEDIR)/share/config/kdeglobals holds info 
 996         // the current icons theme 
 997         wxFileName 
configFile( kdedir
, wxEmptyString 
); 
 998         configFile
.AppendDir( wxT("share") ); 
 999         configFile
.AppendDir( wxT("config") ); 
1000         configFile
.SetName( wxT("kdeglobals") ); 
1003         if (configFile
.FileExists() && config
.Open(configFile
.GetFullPath())) 
1005             // $(KDEDIR)/share/config -> $(KDEDIR)/share 
1006             configFile
.RemoveDir( configFile
.GetDirCount() - 1 ); 
1007             // $(KDEDIR)/share/ -> $(KDEDIR)/share/icons 
1008             configFile
.AppendDir( wxT("icons") ); 
1011             wxString 
theme(wxT("default.kde")); 
1012             size_t nCount 
= config
.GetLineCount(); 
1013             for (size_t i 
= 0; i 
< nCount
; i
++) 
1015                 if (config
[i
].StartsWith(wxT("Theme="), &theme
/*rest*/)) 
1019             configFile
.AppendDir(theme
); 
1023             // $(KDEDIR)/share/config -> $(KDEDIR)/share 
1024             configFile
.RemoveDir( configFile
.GetDirCount() - 1 ); 
1026             // $(KDEDIR)/share/ -> $(KDEDIR)/share/icons 
1027             configFile
.AppendDir( wxT("icons") ); 
1029             // $(KDEDIR)/share/icons -> $(KDEDIR)/share/icons/default.kde 
1030             configFile
.AppendDir( wxT("default.kde") ); 
1033         configFile
.SetName( wxEmptyString 
); 
1034         configFile
.AppendDir( wxT("32x32") ); 
1035         configFile
.AppendDir( wxT("mimetypes") ); 
1037         // Just try a few likely icons theme names 
1039         int pos 
= configFile
.GetDirCount() - 3; 
1041         if (!wxDir::Exists(configFile
.GetPath())) 
1043             configFile
.RemoveDir( pos 
); 
1044             configFile
.InsertDir( pos
, wxT("default.kde") ); 
1047         if (!wxDir::Exists(configFile
.GetPath())) 
1049             configFile
.RemoveDir( pos 
); 
1050             configFile
.InsertDir( pos
, wxT("default") ); 
1053         if (!wxDir::Exists(configFile
.GetPath())) 
1055             configFile
.RemoveDir( pos 
); 
1056             configFile
.InsertDir( pos
, wxT("crystalsvg") ); 
1059         if (!wxDir::Exists(configFile
.GetPath())) 
1061             configFile
.RemoveDir( pos 
); 
1062             configFile
.InsertDir( pos
, wxT("crystal") ); 
1065         if (wxDir::Exists(configFile
.GetPath())) 
1066             icondirs
.Add( configFile
.GetFullPath() ); 
1069     // settings in ~/.kde have maximal priority 
1070     dirs
.Add(wxGetHomeDir() + wxT("/.kde/share")); 
1071     icondirs
.Add(wxGetHomeDir() + wxT("/.kde/share/icons/")); 
1075         dirs
.Add( wxString(kdedir
) + wxT("/share") ); 
1076         icondirs
.Add( wxString(kdedir
) + wxT("/share/icons/") ); 
1080         // try to guess KDEDIR 
1081         dirs
.Add(wxT("/usr/share")); 
1082         dirs
.Add(wxT("/opt/kde/share")); 
1083         icondirs
.Add(wxT("/usr/share/icons/")); 
1084         icondirs
.Add(wxT("/usr/X11R6/share/icons/")); // Debian/Corel linux 
1085         icondirs
.Add(wxT("/opt/kde/share/icons/")); 
1088     if (!sExtraDir
.empty()) 
1089         dirs
.Add(sExtraDir
); 
1090     icondirs
.Add(sExtraDir 
+ wxT("/icons")); 
1092     size_t nDirs 
= dirs
.GetCount(); 
1093     for ( size_t nDir 
= 0; nDir 
< nDirs
; nDir
++ ) 
1095         LoadKDELinkFilesFromDir(dirs
[nDir
], icondirs
); 
1099 // ---------------------------------------------------------------------------- 
1100 // wxFileTypeImpl (Unix) 
1101 // ---------------------------------------------------------------------------- 
1103 wxString 
wxFileTypeImpl::GetExpandedCommand(const wxString 
& verb
, const wxFileType::MessageParameters
& params
) const 
1107     while ( (i 
< m_index
.GetCount() ) && sTmp
.empty() ) 
1109         sTmp 
= m_manager
->GetCommand( verb
, m_index
[i
] ); 
1113     return wxFileType::ExpandCommand(sTmp
, params
); 
1116 bool wxFileTypeImpl::GetIcon(wxIconLocation 
*iconLoc
) const 
1120     while ( (i 
< m_index
.GetCount() ) && sTmp
.empty() ) 
1122         sTmp 
= m_manager
->m_aIcons
[m_index
[i
]]; 
1131         iconLoc
->SetFileName(sTmp
); 
1137 bool wxFileTypeImpl::GetMimeTypes(wxArrayString
& mimeTypes
) const 
1140     size_t nCount 
= m_index
.GetCount(); 
1141     for (size_t i 
= 0; i 
< nCount
; i
++) 
1142         mimeTypes
.Add(m_manager
->m_aTypes
[m_index
[i
]]); 
1147 size_t wxFileTypeImpl::GetAllCommands(wxArrayString 
*verbs
, 
1148                                   wxArrayString 
*commands
, 
1149                                   const wxFileType::MessageParameters
& params
) const 
1151     wxString vrb
, cmd
, sTmp
; 
1153     wxMimeTypeCommands 
* sPairs
; 
1155     // verbs and commands have been cleared already in mimecmn.cpp... 
1156     // if we find no entries in the exact match, try the inexact match 
1157     for (size_t n 
= 0; ((count 
== 0) && (n 
< m_index
.GetCount())); n
++) 
1159         // list of verb = command pairs for this mimetype 
1160         sPairs 
= m_manager
->m_aEntries 
[m_index
[n
]]; 
1162         for ( i 
= 0; i 
< sPairs
->GetCount(); i
++ ) 
1164             vrb 
= sPairs
->GetVerb(i
); 
1165             // some gnome entries have "." inside 
1166             vrb 
= vrb
.AfterLast(wxT('.')); 
1167             cmd 
= sPairs
->GetCmd(i
); 
1170                  cmd 
= wxFileType::ExpandCommand(cmd
, params
); 
1172                  if ( vrb
.IsSameAs(wxT("open"))) 
1175                         verbs
->Insert(vrb
, 0u); 
1177                         commands 
->Insert(cmd
, 0u); 
1193 bool wxFileTypeImpl::GetExtensions(wxArrayString
& extensions
) 
1195     wxString strExtensions 
= m_manager
->GetExtension(m_index
[0]); 
1198     // one extension in the space or comma-delimited list 
1200     for ( const wxChar 
*p 
= strExtensions
; /* nothing */; p
++ ) 
1202         if ( *p 
== wxT(' ') || *p 
== wxT(',') || *p 
== wxT('\0') ) 
1204             if ( !strExt
.empty() ) 
1206                 extensions
.Add(strExt
); 
1209             //else: repeated spaces 
1210             // (shouldn't happen, but it's not that important if it does happen) 
1212             if ( *p 
== wxT('\0') ) 
1215         else if ( *p 
== wxT('.') ) 
1217             // remove the dot from extension (but only if it's the first char) 
1218             if ( !strExt
.empty() ) 
1222             //else: no, don't append it 
1233 // set an arbitrary command: 
1234 // could adjust the code to ask confirmation if it already exists and 
1235 // overwriteprompt is true, but this is currently ignored as *Associate* has 
1236 // no overwrite prompt 
1238 wxFileTypeImpl::SetCommand(const wxString
& cmd
, 
1239                            const wxString
& verb
, 
1240                            bool WXUNUSED(overwriteprompt
)) 
1242     wxArrayString strExtensions
; 
1243     wxString strDesc
, strIcon
; 
1245     wxArrayString strTypes
; 
1246     GetMimeTypes(strTypes
); 
1247     if ( strTypes
.IsEmpty() ) 
1250     wxMimeTypeCommands 
*entry 
= new wxMimeTypeCommands(); 
1251     entry
->Add(verb 
+ wxT("=")  + cmd 
+ wxT(" %s ")); 
1254     size_t nCount 
= strTypes
.GetCount(); 
1255     for ( size_t i 
= 0; i 
< nCount
; i
++ ) 
1257         if (!m_manager
->DoAssociation(strTypes
[i
], strIcon
, entry
, strExtensions
, strDesc
)) 
1264 // ignore index on the grounds that we only have one icon in a Unix file 
1265 bool wxFileTypeImpl::SetDefaultIcon(const wxString
& strIcon
, int WXUNUSED(index
)) 
1267     if (strIcon
.empty()) 
1270     wxArrayString strExtensions
; 
1273     wxArrayString strTypes
; 
1274     GetMimeTypes(strTypes
); 
1275     if ( strTypes
.IsEmpty() ) 
1278     wxMimeTypeCommands 
*entry 
= new wxMimeTypeCommands(); 
1280     size_t nCount 
= strTypes
.GetCount(); 
1281     for ( size_t i 
= 0; i 
< nCount
; i
++ ) 
1283         if ( !m_manager
->DoAssociation
 
1299 // ---------------------------------------------------------------------------- 
1300 // wxMimeTypesManagerImpl (Unix) 
1301 // ---------------------------------------------------------------------------- 
1303 wxMimeTypesManagerImpl::wxMimeTypesManagerImpl() 
1305     m_initialized 
= false; 
1306     m_mailcapStylesInited 
= 0; 
1309 void wxMimeTypesManagerImpl::InitIfNeeded() 
1311     if ( !m_initialized 
) 
1313         // set the flag first to prevent recursion 
1314         m_initialized 
= true; 
1316         wxString wm 
= wxTheApp
->GetTraits()->GetDesktopEnvironment(); 
1318         if (wm 
== wxT("KDE")) 
1319             Initialize( wxMAILCAP_KDE  
); 
1320         else if (wm 
== wxT("GNOME")) 
1321             Initialize( wxMAILCAP_GNOME 
); 
1327 // read system and user mailcaps and other files 
1328 void wxMimeTypesManagerImpl::Initialize(int mailcapStyles
, 
1329                                         const wxString
& sExtraDir
) 
1331     // read mimecap amd mime.types 
1332     if ( (mailcapStyles 
& wxMAILCAP_NETSCAPE
) || 
1333          (mailcapStyles 
& wxMAILCAP_STANDARD
) ) 
1334         GetMimeInfo(sExtraDir
); 
1336     // read GNOME tables 
1337     if (mailcapStyles 
& wxMAILCAP_GNOME
) 
1338         GetGnomeMimeInfo(sExtraDir
); 
1341     if (mailcapStyles 
& wxMAILCAP_KDE
) 
1342         GetKDEMimeInfo(sExtraDir
); 
1344     m_mailcapStylesInited 
|= mailcapStyles
; 
1347 // clear data so you can read another group of WM files 
1348 void wxMimeTypesManagerImpl::ClearData() 
1352     m_aExtensions
.Clear(); 
1353     m_aDescriptions
.Clear(); 
1355     WX_CLEAR_ARRAY(m_aEntries
); 
1358     m_mailcapStylesInited 
= 0; 
1361 wxMimeTypesManagerImpl::~wxMimeTypesManagerImpl() 
1366 void wxMimeTypesManagerImpl::GetMimeInfo(const wxString
& sExtraDir
) 
1368     // read this for netscape or Metamail formats 
1370     // directories where we look for mailcap and mime.types by default 
1371     // used by netscape and pine and other mailers, using 2 different formats! 
1373     // (taken from metamail(1) sources) 
1375     // although RFC 1524 specifies the search path of 
1376     // /etc/:/usr/etc:/usr/local/etc only, it doesn't hurt to search in more 
1377     // places - OTOH, the RFC also says that this path can be changed with 
1378     // MAILCAPS environment variable (containing the colon separated full 
1379     // filenames to try) which is not done yet (TODO?) 
1381     wxString strHome 
= wxGetenv(wxT("HOME")); 
1384     dirs
.Add( strHome 
+ wxT("/.") ); 
1385     dirs
.Add( wxT("/etc/") ); 
1386     dirs
.Add( wxT("/usr/etc/") ); 
1387     dirs
.Add( wxT("/usr/local/etc/") ); 
1388     dirs
.Add( wxT("/etc/mail/") ); 
1389     dirs
.Add( wxT("/usr/public/lib/") ); 
1390     if (!sExtraDir
.empty()) 
1391         dirs
.Add( sExtraDir 
+ wxT("/") ); 
1394     size_t nDirs 
= dirs
.GetCount(); 
1395     for ( size_t nDir 
= 0; nDir 
< nDirs
; nDir
++ ) 
1398         file 
+= wxT("mailcap"); 
1399         if ( wxFile::Exists(file
) ) 
1405         file 
+= wxT("mime.types"); 
1406         if ( wxFile::Exists(file
) ) 
1407             ReadMimeTypes(file
); 
1411 bool wxMimeTypesManagerImpl::WriteToMimeTypes(int index
, bool delete_index
) 
1413     // check we have the right manager 
1414     if (! ( m_mailcapStylesInited 
& wxMAILCAP_STANDARD
) ) 
1418     wxString strHome 
= wxGetenv(wxT("HOME")); 
1420     // and now the users mailcap 
1421     wxString strUserMailcap 
= strHome 
+ wxT("/.mime.types"); 
1423     wxMimeTextFile file
; 
1424     if ( wxFile::Exists(strUserMailcap
) ) 
1426         bTemp 
= file
.Open(strUserMailcap
); 
1433         bTemp 
= file
.Create(strUserMailcap
); 
1439         // test for netscape's header and return false if its found 
1440         nIndex 
= file
.pIndexOf(wxT("#--Netscape")); 
1441         if (nIndex 
!= wxNOT_FOUND
) 
1443             wxFAIL_MSG(wxT("Error in .mime.types\nTrying to mix Netscape and Metamail formats\nFile not modified")); 
1447         // write it in alternative format 
1448         // get rid of unwanted entries 
1449         wxString strType 
= m_aTypes
[index
]; 
1450         nIndex 
= file
.pIndexOf(strType
); 
1452         // get rid of all the unwanted entries... 
1453         if (nIndex 
!= wxNOT_FOUND
) 
1454             file
.CommentLine(nIndex
); 
1458             // add the new entries in 
1459             wxString sTmp 
= strType
.Append( wxT(' '), 40 - strType
.Len() ); 
1460             sTmp 
+= m_aExtensions
[index
]; 
1464         bTemp 
= file
.Write(); 
1471 bool wxMimeTypesManagerImpl::WriteToNSMimeTypes(int index
, bool delete_index
) 
1473     //check we have the right managers 
1474     if (! ( m_mailcapStylesInited 
& wxMAILCAP_NETSCAPE
) ) 
1478     wxString strHome 
= wxGetenv(wxT("HOME")); 
1480     // and now the users mailcap 
1481     wxString strUserMailcap 
= strHome 
+ wxT("/.mime.types"); 
1483     wxMimeTextFile file
; 
1484     if ( wxFile::Exists(strUserMailcap
) ) 
1486         bTemp 
= file
.Open(strUserMailcap
); 
1493         bTemp 
= file
.Create(strUserMailcap
); 
1498         // write it in the format that Netscape uses 
1500         // test for netscape's header and insert if required... 
1501         // this is a comment so use true 
1502         nIndex 
= file
.pIndexOf(wxT("#--Netscape"), true); 
1503         if (nIndex 
== wxNOT_FOUND
) 
1505             // either empty file or metamail format 
1506             // at present we can't cope with mixed formats, so exit to preseve 
1507             // metamail entreies 
1508             if (file
.GetLineCount() > 0) 
1510                 wxFAIL_MSG(wxT(".mime.types File not in Netscape format\nNo entries written to\n.mime.types or to .mailcap")); 
1514             file
.InsertLine(wxT( "#--Netscape Communications Corporation MIME Information" ), 0); 
1518         wxString strType 
= wxT("type=") + m_aTypes
[index
]; 
1519         nIndex 
= file
.pIndexOf(strType
); 
1521         // get rid of all the unwanted entries... 
1522         if (nIndex 
!= wxNOT_FOUND
) 
1524             wxString sOld 
= file
[nIndex
]; 
1525             while ( (sOld
.Contains(wxT("\\"))) && (nIndex 
< (int) file
.GetLineCount()) ) 
1527                 file
.CommentLine(nIndex
); 
1528                 sOld 
= file
[nIndex
]; 
1530                 wxLogTrace(TRACE_MIME
, wxT("--- Deleting from mime.types line '%d %s' ---"), nIndex
, sOld
.c_str()); 
1535             if (nIndex 
< (int) file
.GetLineCount()) 
1536                 file
.CommentLine(nIndex
); 
1539             nIndex 
= (int) file
.GetLineCount(); 
1541         wxString sTmp 
= strType 
+ wxT(" \\"); 
1543             file
.InsertLine(sTmp
, nIndex
); 
1545         if ( ! m_aDescriptions
.Item(index
).empty() ) 
1547             sTmp 
= wxT("desc=\"") + m_aDescriptions
[index
]+ wxT("\" \\"); //.trim ?? 
1551                 file
.InsertLine(sTmp
, nIndex
); 
1555         wxString sExts 
= m_aExtensions
.Item(index
); 
1556         sTmp 
= wxT("exts=\"") + sExts
.Trim(false).Trim() + wxT("\""); 
1560             file
.InsertLine(sTmp
, nIndex
); 
1563         bTemp 
= file
.Write(); 
1570 bool wxMimeTypesManagerImpl::WriteToMailCap(int index
, bool delete_index
) 
1572     //check we have the right managers 
1573     if ( !( ( m_mailcapStylesInited 
& wxMAILCAP_NETSCAPE
) || 
1574             ( m_mailcapStylesInited 
& wxMAILCAP_STANDARD
) ) ) 
1578     wxString strHome 
= wxGetenv(wxT("HOME")); 
1580     // and now the users mailcap 
1581     wxString strUserMailcap 
= strHome 
+ wxT("/.mailcap"); 
1583     wxMimeTextFile file
; 
1584     if ( wxFile::Exists(strUserMailcap
) ) 
1586         bTemp 
= file
.Open(strUserMailcap
); 
1593         bTemp 
= file
.Create(strUserMailcap
); 
1598         // now got a file we can write to .... 
1599         wxMimeTypeCommands 
* entries 
= m_aEntries
[index
]; 
1601         wxString sCmd 
= entries
->GetCommandForVerb(wxT("open"), &iOpen
); 
1604         sTmp 
= m_aTypes
[index
]; 
1606         int nIndex 
= file
.pIndexOf(sTmp
); 
1608         // get rid of all the unwanted entries... 
1609         if (nIndex 
== wxNOT_FOUND
) 
1611             nIndex 
= (int) file
.GetLineCount(); 
1615             sOld 
= file
[nIndex
]; 
1616             wxLogTrace(TRACE_MIME
, wxT("--- Deleting from mailcap line '%d' ---"), nIndex
); 
1618             while ( (sOld
.Contains(wxT("\\"))) && (nIndex 
< (int) file
.GetLineCount()) ) 
1620                 file
.CommentLine(nIndex
); 
1621                 if (nIndex 
< (int) file
.GetLineCount()) 
1622                     sOld 
= sOld 
+ file
[nIndex
]; 
1626                 file
.GetLineCount()) file
.CommentLine(nIndex
); 
1629         sTmp 
+= wxT(";") + sCmd
; //includes wxT(" %s "); 
1631         // write it in the format that Netscape uses (default) 
1632         if (! ( m_mailcapStylesInited 
& wxMAILCAP_STANDARD 
) ) 
1635                 file
.InsertLine(sTmp
, nIndex
); 
1640             // write extended format 
1642             // TODO - FIX this code: 
1644             // sOld holds all the entries, but our data store only has some 
1645             // eg test= is not stored 
1647             // so far we have written the mimetype and command out 
1648             wxStringTokenizer 
sT(sOld
, wxT(";\\")); 
1649             if (sT
.CountTokens() > 2) 
1651                 // first one mimetype; second one command, rest unknown... 
1653                 s 
= sT
.GetNextToken(); 
1654                 s 
= sT
.GetNextToken(); 
1657                 s 
= sT
.GetNextToken(); 
1658                 while ( ! s
.empty() ) 
1660                     bool bKnownToken 
= false; 
1661                     if (s
.Contains(wxT("description="))) 
1663                     if (s
.Contains(wxT("x11-bitmap="))) 
1667                     size_t nCount 
= entries
->GetCount(); 
1668                     for (i
=0; i 
< nCount
; i
++) 
1670                         if (s
.Contains(entries
->GetVerb(i
))) 
1676                         sTmp 
+= wxT("; \\"); 
1677                         file
.InsertLine(sTmp
, nIndex
); 
1681                     s 
= sT
.GetNextToken(); 
1685             if (! m_aDescriptions
[index
].empty() ) 
1687                 sTmp 
+= wxT("; \\"); 
1688                 file
.InsertLine(sTmp
, nIndex
); 
1690                 sTmp 
= wxT("       description=\"") + m_aDescriptions
[index
] + wxT("\""); 
1693             if (! m_aIcons
[index
].empty() ) 
1695                 sTmp 
+= wxT("; \\"); 
1696                 file
.InsertLine(sTmp
, nIndex
); 
1698                 sTmp 
= wxT("       x11-bitmap=\"") + m_aIcons
[index
] + wxT("\""); 
1701             if ( entries
->GetCount() > 1 ) 
1704                 for (i
=0; i 
< entries
->GetCount(); i
++) 
1707                         sTmp 
+= wxT("; \\"); 
1708                         file
.InsertLine(sTmp
, nIndex
); 
1710                         sTmp 
= wxT("       ") + entries
->GetVerbCmd(i
); 
1714             file
.InsertLine(sTmp
, nIndex
); 
1718         bTemp 
= file
.Write(); 
1725 wxFileType 
* wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo
& ftInfo
) 
1729     wxString strType 
= ftInfo
.GetMimeType(); 
1730     wxString strDesc 
= ftInfo
.GetDescription(); 
1731     wxString strIcon 
= ftInfo
.GetIconFile(); 
1733     wxMimeTypeCommands 
*entry 
= new wxMimeTypeCommands(); 
1735     if ( ! ftInfo
.GetOpenCommand().empty()) 
1736         entry
->Add(wxT("open=")  + ftInfo
.GetOpenCommand() + wxT(" %s ")); 
1737     if ( ! ftInfo
.GetPrintCommand().empty()) 
1738         entry
->Add(wxT("print=") + ftInfo
.GetPrintCommand() + wxT(" %s ")); 
1740     // now find where these extensions are in the data store and remove them 
1741     wxArrayString sA_Exts 
= ftInfo
.GetExtensions(); 
1742     wxString sExt
, sExtStore
; 
1744     size_t nExtCount 
= sA_Exts
.GetCount(); 
1745     for (i
=0; i 
< nExtCount
; i
++) 
1747         sExt 
= sA_Exts
.Item(i
); 
1749         // clean up to just a space before and after 
1750         sExt
.Trim().Trim(false); 
1751         sExt 
= wxT(' ') + sExt 
+ wxT(' '); 
1752         size_t nCount 
= m_aExtensions
.GetCount(); 
1753         for (nIndex 
= 0; nIndex 
< nCount
; nIndex
++) 
1755             sExtStore 
= m_aExtensions
.Item(nIndex
); 
1756             if (sExtStore
.Replace(sExt
, wxT(" ") ) > 0) 
1757                 m_aExtensions
.Item(nIndex
) = sExtStore
; 
1761     if ( !DoAssociation(strType
, strIcon
, entry
, sA_Exts
, strDesc
) ) 
1764     return GetFileTypeFromMimeType(strType
); 
1767 bool wxMimeTypesManagerImpl::DoAssociation(const wxString
& strType
, 
1768                                            const wxString
& strIcon
, 
1769                                            wxMimeTypeCommands 
*entry
, 
1770                                            const wxArrayString
& strExtensions
, 
1771                                            const wxString
& strDesc
) 
1773     int nIndex 
= AddToMimeData(strType
, strIcon
, entry
, strExtensions
, strDesc
, true); 
1775     if ( nIndex 
== wxNOT_FOUND 
) 
1778     return WriteMimeInfo(nIndex
, false); 
1781 bool wxMimeTypesManagerImpl::WriteMimeInfo(int nIndex
, bool delete_mime 
) 
1785     if ( m_mailcapStylesInited 
& wxMAILCAP_STANDARD 
) 
1787         // write in metamail  format; 
1788         if (WriteToMimeTypes(nIndex
, delete_mime
) ) 
1789             if ( WriteToMailCap(nIndex
, delete_mime
) ) 
1793     if ( m_mailcapStylesInited 
& wxMAILCAP_NETSCAPE 
) 
1795         // write in netsacpe format; 
1796         if (WriteToNSMimeTypes(nIndex
, delete_mime
) ) 
1797             if ( WriteToMailCap(nIndex
, delete_mime
) ) 
1801     // Don't write GNOME files here as this is not 
1802     // allowed and simply doesn't work 
1804     if (m_mailcapStylesInited 
& wxMAILCAP_KDE
) 
1806         // write in KDE format; 
1807         if (WriteKDEMimeFile(nIndex
, delete_mime
) ) 
1814 int wxMimeTypesManagerImpl::AddToMimeData(const wxString
& strType
, 
1815                                           const wxString
& strIcon
, 
1816                                           wxMimeTypeCommands 
*entry
, 
1817                                           const wxArrayString
& strExtensions
, 
1818                                           const wxString
& strDesc
, 
1819                                           bool replaceExisting
) 
1823     // ensure mimetype is always lower case 
1824     wxString mimeType 
= strType
.Lower(); 
1826     // is this a known MIME type? 
1827     int nIndex 
= m_aTypes
.Index(mimeType
); 
1828     if ( nIndex 
== wxNOT_FOUND 
) 
1831         m_aTypes
.Add(mimeType
); 
1832         m_aIcons
.Add(strIcon
); 
1833         m_aEntries
.Add(entry 
? entry 
: new wxMimeTypeCommands
); 
1835         // change nIndex so we can use it below to add the extensions 
1836         m_aExtensions
.Add(wxEmptyString
); 
1837         nIndex 
= m_aExtensions
.size() - 1; 
1839         m_aDescriptions
.Add(strDesc
); 
1841     else // yes, we already have it 
1843         if ( replaceExisting 
) 
1845             // if new description change it 
1846             if ( !strDesc
.empty()) 
1847                 m_aDescriptions
[nIndex
] = strDesc
; 
1849             // if new icon change it 
1850             if ( !strIcon
.empty()) 
1851                 m_aIcons
[nIndex
] = strIcon
; 
1855                 delete m_aEntries
[nIndex
]; 
1856                 m_aEntries
[nIndex
] = entry
; 
1859         else // add data we don't already have ... 
1861             // if new description add only if none 
1862             if ( m_aDescriptions
[nIndex
].empty() ) 
1863                 m_aDescriptions
[nIndex
] = strDesc
; 
1865             // if new icon and no existing icon 
1866             if ( m_aIcons
[nIndex
].empty() ) 
1867                 m_aIcons
[nIndex
] = strIcon
; 
1869             // add any new entries... 
1872                 wxMimeTypeCommands 
*entryOld 
= m_aEntries
[nIndex
]; 
1874                 size_t count 
= entry
->GetCount(); 
1875                 for ( size_t i 
= 0; i 
< count
; i
++ ) 
1877                     const wxString
& verb 
= entry
->GetVerb(i
); 
1878                     if ( !entryOld
->HasVerb(verb
) ) 
1880                         entryOld
->AddOrReplaceVerb(verb
, entry
->GetCmd(i
)); 
1884                 // as we don't store it anywhere, it won't be deleted later as 
1885                 // usual -- do it immediately instead 
1891     // always add the extensions to this mimetype 
1892     wxString
& exts 
= m_aExtensions
[nIndex
]; 
1894     // add all extensions we don't have yet 
1896     size_t count 
= strExtensions
.GetCount(); 
1897     for ( size_t i 
= 0; i 
< count
; i
++ ) 
1899         ext 
= strExtensions
[i
]; 
1902         if ( exts
.Find(ext
) == wxNOT_FOUND 
) 
1908     // check data integrity 
1909     wxASSERT( m_aTypes
.Count() == m_aEntries
.Count() && 
1910               m_aTypes
.Count() == m_aExtensions
.Count() && 
1911               m_aTypes
.Count() == m_aIcons
.Count() && 
1912               m_aTypes
.Count() == m_aDescriptions
.Count() ); 
1917 wxFileType 
* wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString
& ext
) 
1924     size_t count 
= m_aExtensions
.GetCount(); 
1925     for ( size_t n 
= 0; n 
< count
; n
++ ) 
1927         wxStringTokenizer 
tk(m_aExtensions
[n
], wxT(' ')); 
1929         while ( tk
.HasMoreTokens() ) 
1931             // consider extensions as not being case-sensitive 
1932             if ( tk
.GetNextToken().IsSameAs(ext
, false /* no case */) ) 
1935                 wxFileType 
*fileType 
= new wxFileType
; 
1936                 fileType
->m_impl
->Init(this, n
); 
1946 wxFileType 
* wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString
& mimeType
) 
1950     wxFileType 
* fileType 
= NULL
; 
1951     // mime types are not case-sensitive 
1952     wxString 
mimetype(mimeType
); 
1953     mimetype
.MakeLower(); 
1955     // first look for an exact match 
1956     int index 
= m_aTypes
.Index(mimetype
); 
1957     if ( index 
!= wxNOT_FOUND 
) 
1959         fileType 
= new wxFileType
; 
1960         fileType
->m_impl
->Init(this, index
); 
1963     // then try to find "text/*" as match for "text/plain" (for example) 
1964     // NB: if mimeType doesn't contain '/' at all, BeforeFirst() will return 
1965     //     the whole string - ok. 
1967     index 
= wxNOT_FOUND
; 
1968     wxString strCategory 
= mimetype
.BeforeFirst(wxT('/')); 
1970     size_t nCount 
= m_aTypes
.Count(); 
1971     for ( size_t n 
= 0; n 
< nCount
; n
++ ) 
1973         if ( (m_aTypes
[n
].BeforeFirst(wxT('/')) == strCategory 
) && 
1974                 m_aTypes
[n
].AfterFirst(wxT('/')) == wxT("*") ) 
1981     if ( index 
!= wxNOT_FOUND 
) 
1983        // don't throw away fileType that was already found 
1985             fileType 
= new wxFileType
; 
1986         fileType
->m_impl
->Init(this, index
); 
1992 wxString 
wxMimeTypesManagerImpl::GetCommand(const wxString 
& verb
, size_t nIndex
) const 
1994     wxString command
, testcmd
, sV
, sTmp
; 
1995     sV 
= verb 
+ wxT("="); 
1997     // list of verb = command pairs for this mimetype 
1998     wxMimeTypeCommands 
* sPairs 
= m_aEntries 
[nIndex
]; 
2001     size_t nCount 
= sPairs
->GetCount(); 
2002     for ( i 
= 0; i 
< nCount
; i
++ ) 
2004         sTmp 
= sPairs
->GetVerbCmd (i
); 
2005         if ( sTmp
.Contains(sV
) ) 
2006             command 
= sTmp
.AfterFirst(wxT('=')); 
2012 void wxMimeTypesManagerImpl::AddFallback(const wxFileTypeInfo
& filetype
) 
2016     wxString extensions
; 
2017     const wxArrayString
& exts 
= filetype
.GetExtensions(); 
2018     size_t nExts 
= exts
.GetCount(); 
2019     for ( size_t nExt 
= 0; nExt 
< nExts
; nExt
++ ) 
2022             extensions 
+= wxT(' '); 
2024         extensions 
+= exts
[nExt
]; 
2027     AddMimeTypeInfo(filetype
.GetMimeType(), 
2029                     filetype
.GetDescription()); 
2031     AddMailcapInfo(filetype
.GetMimeType(), 
2032                    filetype
.GetOpenCommand(), 
2033                    filetype
.GetPrintCommand(), 
2035                    filetype
.GetDescription()); 
2038 void wxMimeTypesManagerImpl::AddMimeTypeInfo(const wxString
& strMimeType
, 
2039                                              const wxString
& strExtensions
, 
2040                                              const wxString
& strDesc
) 
2042     // reading mailcap may find image/* , while 
2043     // reading mime.types finds image/gif and no match is made 
2044     // this means all the get functions don't work  fix this 
2046     wxString sTmp 
= strExtensions
; 
2048     wxArrayString sExts
; 
2049     sTmp
.Trim().Trim(false); 
2051     while (!sTmp
.empty()) 
2053         sExts
.Add(sTmp
.AfterLast(wxT(' '))); 
2054         sTmp 
= sTmp
.BeforeLast(wxT(' ')); 
2057     AddToMimeData(strMimeType
, strIcon
, NULL
, sExts
, strDesc
, true); 
2060 void wxMimeTypesManagerImpl::AddMailcapInfo(const wxString
& strType
, 
2061                                             const wxString
& strOpenCmd
, 
2062                                             const wxString
& strPrintCmd
, 
2063                                             const wxString
& strTest
, 
2064                                             const wxString
& strDesc
) 
2068     wxMimeTypeCommands 
*entry 
= new wxMimeTypeCommands
; 
2069     entry
->Add(wxT("open=")  + strOpenCmd
); 
2070     entry
->Add(wxT("print=") + strPrintCmd
); 
2071     entry
->Add(wxT("test=")  + strTest
); 
2074     wxArrayString strExtensions
; 
2076     AddToMimeData(strType
, strIcon
, entry
, strExtensions
, strDesc
, true); 
2079 bool wxMimeTypesManagerImpl::ReadMimeTypes(const wxString
& strFileName
) 
2081     wxLogTrace(TRACE_MIME
, wxT("--- Parsing mime.types file '%s' ---"), 
2082                strFileName
.c_str()); 
2084     wxTextFile 
file(strFileName
); 
2085 #if defined(__WXGTK20__) && wxUSE_UNICODE 
2086     if ( !file
.Open(wxConvUTF8
) ) 
2092     // the information we extract 
2093     wxString strMimeType
, strDesc
, strExtensions
; 
2095     size_t nLineCount 
= file
.GetLineCount(); 
2096     const wxChar 
*pc 
= NULL
; 
2097     for ( size_t nLine 
= 0; nLine 
< nLineCount
; nLine
++ ) 
2101             // now we're at the start of the line 
2102             pc 
= file
[nLine
].c_str(); 
2106             // we didn't finish with the previous line yet 
2111         while ( wxIsspace(*pc
) ) 
2114         // comment or blank line? 
2115         if ( *pc 
== wxT('#') || !*pc 
) 
2117             // skip the whole line 
2122         // detect file format 
2123         const wxChar 
*pEqualSign 
= wxStrchr(pc
, wxT('=')); 
2124         if ( pEqualSign 
== NULL 
) 
2129             // first field is mime type 
2130             for ( strMimeType
.Empty(); !wxIsspace(*pc
) && *pc 
!= wxT('\0'); pc
++ ) 
2136             while ( wxIsspace(*pc
) ) 
2139             // take all the rest of the string 
2142             // no description... 
2150             // the string on the left of '=' is the field name 
2151             wxString 
strLHS(pc
, pEqualSign 
- pc
); 
2154             for ( pc 
= pEqualSign 
+ 1; wxIsspace(*pc
); pc
++ ) 
2158             if ( *pc 
== wxT('"') ) 
2160                 // the string is quoted and ends at the matching quote 
2161                 pEnd 
= wxStrchr(++pc
, wxT('"')); 
2164                     wxLogWarning(wxT("Mime.types file %s, line %lu: unterminated quoted string."), 
2165                                  strFileName
.c_str(), nLine 
+ 1L); 
2170                 // unquoted string ends at the first space or at the end of 
2172                 for ( pEnd 
= pc
; *pEnd 
&& !wxIsspace(*pEnd
); pEnd
++ ) 
2176             // now we have the RHS (field value) 
2177             wxString 
strRHS(pc
, pEnd 
- pc
); 
2179             // check what follows this entry 
2180             if ( *pEnd 
== wxT('"') ) 
2186             for ( pc 
= pEnd
; wxIsspace(*pc
); pc
++ ) 
2189             // if there is something left, it may be either a '\\' to continue 
2190             // the line or the next field of the same entry 
2191             bool entryEnded 
= *pc 
== wxT('\0'); 
2192             bool nextFieldOnSameLine 
= false; 
2195                 nextFieldOnSameLine 
= ((*pc 
!= wxT('\\')) || (pc
[1] != wxT('\0'))); 
2198             // now see what we got 
2199             if ( strLHS 
== wxT("type") ) 
2201                 strMimeType 
= strRHS
; 
2203             else if ( strLHS
.StartsWith(wxT("desc")) ) 
2207             else if ( strLHS 
== wxT("exts") ) 
2209                 strExtensions 
= strRHS
; 
2211             else if ( strLHS 
== wxT("icon") ) 
2213                 // this one is simply ignored: it usually refers to Netscape 
2214                 // built in icons which are useless for us anyhow 
2216             else if ( !strLHS
.StartsWith(wxT("x-")) ) 
2218                 // we suppose that all fields starting with "X-" are 
2219                 // unregistered extensions according to the standard practice, 
2220                 // but it may be worth telling the user about other junk in 
2221                 // his mime.types file 
2222                 wxLogWarning(wxT("Unknown field in file %s, line %lu: '%s'."), 
2223                              strFileName
.c_str(), nLine 
+ 1L, strLHS
.c_str()); 
2228                 if ( !nextFieldOnSameLine 
) 
2230                 //else: don't reset it 
2232                 // as we don't reset strMimeType, the next field in this entry 
2233                 // will be interpreted correctly. 
2239         // depending on the format (Mosaic or Netscape) either space or comma 
2240         // is used to separate the extensions 
2241         strExtensions
.Replace(wxT(","), wxT(" ")); 
2243         // also deal with the leading dot 
2244         if ( !strExtensions
.empty() && strExtensions
[0u] == wxT('.') ) 
2246             strExtensions
.erase(0, 1); 
2249         wxLogTrace(TRACE_MIME
, wxT("mime.types: '%s' => '%s' (%s)"), 
2250                    strExtensions
.c_str(), 
2251                    strMimeType
.c_str(), 
2254         AddMimeTypeInfo(strMimeType
, strExtensions
, strDesc
); 
2256         // finished with this line 
2263 // ---------------------------------------------------------------------------- 
2264 // UNIX mailcap files parsing 
2265 // ---------------------------------------------------------------------------- 
2267 // the data for a single MIME type 
2268 struct MailcapLineData
 
2277     wxArrayString verbs
, 
2285     MailcapLineData() { testfailed 
= needsterminal 
= copiousoutput 
= false; } 
2288 // process a non-standard (i.e. not the first or second one) mailcap field 
2290 wxMimeTypesManagerImpl::ProcessOtherMailcapField(MailcapLineData
& data
, 
2291                                                  const wxString
& curField
) 
2293     if ( curField
.empty() ) 
2299     // is this something of the form foo=bar? 
2300     const wxChar 
*pEq 
= wxStrchr(curField
, wxT('=')); 
2303         // split "LHS = RHS" in 2 
2304         wxString lhs 
= curField
.BeforeFirst(wxT('=')), 
2305                  rhs 
= curField
.AfterFirst(wxT('=')); 
2307         lhs
.Trim(true);     // from right 
2308         rhs
.Trim(false);    // from left 
2310         // it might be quoted 
2311         if ( !rhs
.empty() && rhs
[0u] == wxT('"') && rhs
.Last() == wxT('"') ) 
2313             rhs 
= rhs
.Mid(1, rhs
.length() - 2); 
2316         // is it a command verb or something else? 
2317         if ( lhs 
== wxT("test") ) 
2319             if ( wxSystem(rhs
) == 0 ) 
2322                 wxLogTrace(TRACE_MIME_TEST
, 
2323                            wxT("Test '%s' for mime type '%s' succeeded."), 
2324                            rhs
.c_str(), data
.type
.c_str()); 
2328                 wxLogTrace(TRACE_MIME_TEST
, 
2329                            wxT("Test '%s' for mime type '%s' failed, skipping."), 
2330                            rhs
.c_str(), data
.type
.c_str()); 
2332                 data
.testfailed 
= true; 
2335         else if ( lhs 
== wxT("desc") ) 
2339         else if ( lhs 
== wxT("x11-bitmap") ) 
2343         else if ( lhs 
== wxT("notes") ) 
2347         else // not a (recognized) special case, must be a verb (e.g. "print") 
2349             data
.verbs
.Add(lhs
); 
2350             data
.commands
.Add(rhs
); 
2353     else // '=' not found 
2355         // so it must be a simple flag 
2356         if ( curField 
== wxT("needsterminal") ) 
2358             data
.needsterminal 
= true; 
2360         else if ( curField 
== wxT("copiousoutput")) 
2362             // copiousoutput impies that the viewer is a console program 
2363             data
.needsterminal 
= 
2364             data
.copiousoutput 
= true; 
2366         else if ( !IsKnownUnimportantField(curField
) ) 
2375 bool wxMimeTypesManagerImpl::ReadMailcap(const wxString
& strFileName
, 
2378     wxLogTrace(TRACE_MIME
, wxT("--- Parsing mailcap file '%s' ---"), 
2379                strFileName
.c_str()); 
2381     wxTextFile 
file(strFileName
); 
2382 #if defined(__WXGTK20__) && wxUSE_UNICODE 
2383     if ( !file
.Open(wxConvUTF8
) ) 
2389     // indices of MIME types (in m_aTypes) we already found in this file 
2391     // (see the comments near the end of function for the reason we need this) 
2392     wxArrayInt aIndicesSeenHere
; 
2394     // accumulator for the current field 
2396     curField
.reserve(1024); 
2398     const wxChar 
*pPagerEnv 
= wxGetenv(wxT("PAGER")); 
2400     const wxArrayString empty_extensions_list
; 
2402     size_t nLineCount 
= file
.GetLineCount(); 
2403     for ( size_t nLine 
= 0; nLine 
< nLineCount
; nLine
++ ) 
2405         // now we're at the start of the line 
2406         const wxChar 
*pc 
= file
[nLine
].c_str(); 
2409         while ( wxIsspace(*pc
) ) 
2412         // comment or empty string? 
2413         if ( *pc 
== wxT('#') || *pc 
== wxT('\0') ) 
2419         // what field are we currently in? The first 2 are fixed and there may 
2420         // be an arbitrary number of other fields parsed by 
2421         // ProcessOtherMailcapField() 
2423         // the first field is the MIME type 
2430         currentToken 
= Field_Type
; 
2432         // the flags and field values on the current line 
2433         MailcapLineData data
; 
2441                     // interpret the next character literally (notice that 
2442                     // backslash can be used for line continuation) 
2443                     if ( *++pc 
== wxT('\0') ) 
2445                         // fetch the next line if there is one 
2446                         if ( nLine 
== nLineCount 
- 1 ) 
2448                             // something is wrong, bail out 
2451                             wxLogDebug(wxT("Mailcap file %s, line %lu: '\\' on the end of the last line ignored."), 
2452                                        strFileName
.c_str(), 
2457                             // pass to the beginning of the next line 
2458                             pc 
= file
[++nLine
].c_str(); 
2460                             // skip pc++ at the end of the loop 
2466                         // just a normal character 
2472                     cont 
= false;   // end of line reached, exit the loop 
2474                     // fall through to still process this field 
2477                     // trim whitespaces from both sides 
2478                     curField
.Trim(true).Trim(false); 
2480                     switch ( currentToken 
) 
2483                             data
.type 
= curField
.Lower(); 
2484                             if ( data
.type
.empty() ) 
2486                                 // I don't think that this is a valid mailcap 
2487                                 // entry, but try to interpret it somehow 
2488                                 data
.type 
= wxT('*'); 
2491                             if ( data
.type
.Find(wxT('/')) == wxNOT_FOUND 
) 
2493                                 // we interpret "type" as "type/*" 
2494                                 data
.type 
+= wxT("/*"); 
2497                             currentToken 
= Field_OpenCmd
; 
2501                             data
.cmdOpen 
= curField
; 
2503                             currentToken 
= Field_Other
; 
2507                             if ( !ProcessOtherMailcapField(data
, curField
) ) 
2509                                 // don't flood the user with error messages if 
2510                                 // we don't understand something in his 
2511                                 // mailcap, but give them in debug mode because 
2512                                 // this might be useful for the programmer 
2515                                     wxT("Mailcap file %s, line %lu: unknown field '%s' for the MIME type '%s' ignored."), 
2516                                     strFileName
.c_str(), 
2522                             else if ( data
.testfailed 
) 
2524                                 // skip this entry entirely 
2528                             // it already has this value 
2529                             //currentToken = Field_Other; 
2533                             wxFAIL_MSG(wxT("unknown field type in mailcap")); 
2536                     // next token starts immediately after ';' 
2544             // continue in the same line 
2548         // we read the entire entry, check what have we got 
2549         // ------------------------------------------------ 
2551         // check that we really read something reasonable 
2552         if ( currentToken 
< Field_Other 
) 
2554             wxLogWarning(wxT("Mailcap file %s, line %lu: incomplete entry ignored."), 
2555                          strFileName
.c_str(), nLine 
+ 1L); 
2560         // if the test command failed, it's as if the entry were not there at all 
2561         if ( data
.testfailed 
) 
2566         // support for flags: 
2567         //  1. create an xterm for 'needsterminal' 
2568         //  2. append "| $PAGER" for 'copiousoutput' 
2570         // Note that the RFC says that having both needsterminal and 
2571         // copiousoutput is probably a mistake, so it seems that running 
2572         // programs with copiousoutput inside an xterm as it is done now 
2573         // is a bad idea (FIXME) 
2574         if ( data
.copiousoutput 
) 
2576             data
.cmdOpen 
<< wxT(" | ") << (pPagerEnv 
? pPagerEnv 
: wxT("more")); 
2579         if ( data
.needsterminal 
) 
2581             data
.cmdOpen
.Printf(wxT("xterm -e sh -c '%s'"), 
2582                                             data
.cmdOpen
.c_str()); 
2585         if ( !data
.cmdOpen
.empty() ) 
2587             data
.verbs
.Insert(wxT("open"), 0); 
2588             data
.commands
.Insert(data
.cmdOpen
, 0); 
2591         // we have to decide whether the new entry should replace any entries 
2592         // for the same MIME type we had previously found or not 
2595         // the fall back entries have the lowest priority, by definition 
2602             // have we seen this one before? 
2603             int nIndex 
= m_aTypes
.Index(data
.type
); 
2605             // and if we have, was it in this file? if not, we should 
2606             // overwrite the previously seen one 
2607             overwrite 
= nIndex 
== wxNOT_FOUND 
|| 
2608                             aIndicesSeenHere
.Index(nIndex
) == wxNOT_FOUND
; 
2611         wxLogTrace(TRACE_MIME
, wxT("mailcap %s: %s [%s]"), 
2612                    data
.type
.c_str(), data
.cmdOpen
.c_str(), 
2613                    overwrite 
? wxT("replace") : wxT("add")); 
2615         int n 
= AddToMimeData
 
2619                     new wxMimeTypeCommands(data
.verbs
, data
.commands
), 
2620                     empty_extensions_list
, 
2627             aIndicesSeenHere
.Add(n
); 
2634 size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString
& mimetypes
) 
2640     size_t count 
= m_aTypes
.GetCount(); 
2641     for ( size_t n 
= 0; n 
< count
; n
++ ) 
2643         // don't return template types from here (i.e. anything containg '*') 
2644         const wxString 
&type 
= m_aTypes
[n
]; 
2645         if ( type
.Find(wxT('*')) == wxNOT_FOUND 
) 
2647             mimetypes
.Add(type
); 
2651     return mimetypes
.GetCount(); 
2654 // ---------------------------------------------------------------------------- 
2655 // writing to MIME type files 
2656 // ---------------------------------------------------------------------------- 
2658 bool wxMimeTypesManagerImpl::Unassociate(wxFileType 
*ft
) 
2662     wxArrayString sMimeTypes
; 
2663     ft
->GetMimeTypes(sMimeTypes
); 
2666     size_t nCount 
= sMimeTypes
.GetCount(); 
2667     for (i 
= 0; i 
< nCount
; i 
++) 
2669         const wxString 
&sMime 
= sMimeTypes
.Item(i
); 
2670         int nIndex 
= m_aTypes
.Index(sMime
); 
2671         if ( nIndex 
== wxNOT_FOUND
) 
2673             // error if we get here ?? 
2678             WriteMimeInfo(nIndex
, true); 
2679             m_aTypes
.RemoveAt(nIndex
); 
2680             m_aEntries
.RemoveAt(nIndex
); 
2681             m_aExtensions
.RemoveAt(nIndex
); 
2682             m_aDescriptions
.RemoveAt(nIndex
); 
2683             m_aIcons
.RemoveAt(nIndex
); 
2686     // check data integrity 
2687     wxASSERT( m_aTypes
.Count() == m_aEntries
.Count() && 
2688             m_aTypes
.Count() == m_aExtensions
.Count() && 
2689             m_aTypes
.Count() == m_aIcons
.Count() && 
2690             m_aTypes
.Count() == m_aDescriptions
.Count() ); 
2695 // ---------------------------------------------------------------------------- 
2696 // private functions 
2697 // ---------------------------------------------------------------------------- 
2699 static bool IsKnownUnimportantField(const wxString
& fieldAll
) 
2701     static const wxChar 
* const knownFields
[] = 
2703         wxT("x-mozilla-flags"), 
2704         wxT("nametemplate"), 
2705         wxT("textualnewlines"), 
2708     wxString field 
= fieldAll
.BeforeFirst(wxT('=')); 
2709     for ( size_t n 
= 0; n 
< WXSIZEOF(knownFields
); n
++ ) 
2711         if ( field
.CmpNoCase(knownFields
[n
]) == 0 ) 
2719   // wxUSE_MIMETYPE && wxUSE_FILE && wxUSE_TEXTFILE