1 ///////////////////////////////////////////////////////////////////////////// 
   2 // Name:        common/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 license (part of wxExtra library) 
  10 ///////////////////////////////////////////////////////////////////////////// 
  13     #pragma implementation "mimetype.h" 
  16 // ============================================================================ 
  18 // ============================================================================ 
  20 // ---------------------------------------------------------------------------- 
  22 // ---------------------------------------------------------------------------- 
  24 // for compilers that support precompilation, includes "wx.h". 
  25 #include "wx/wxprec.h" 
  33     #include  "wx/string.h" 
  37 // Doesn't compile in WIN16 mode 
  42 #include "wx/dynarray.h" 
  43 #include "wx/confbase.h" 
  46     #include "wx/msw/registry.h" 
  49     #include "wx/textfile.h" 
  52 #include "wx/mimetype.h" 
  54 // other standard headers 
  57 // ---------------------------------------------------------------------------- 
  59 // ---------------------------------------------------------------------------- 
  61 // implementation classes, platform dependent 
  64 // These classes use Windows registry to retrieve the required information. 
  66 // Keys used (not all of them are documented, so it might actually stop working 
  67 // in futur versions of Windows...): 
  68 //  1. "HKCR\MIME\Database\Content Type" contains subkeys for all known MIME 
  69 //     types, each key has a string value "Extension" which gives (dot preceded) 
  70 //     extension for the files of this MIME type. 
  72 //  2. "HKCR\.ext" contains 
  73 //   a) unnamed value containing the "filetype" 
  74 //   b) value "Content Type" containing the MIME type 
  76 // 3. "HKCR\filetype" contains 
  77 //   a) unnamed value containing the description 
  78 //   b) subkey "DefaultIcon" with single unnamed value giving the icon index in 
  80 //   c) shell\open\command and shell\open\print subkeys containing the commands 
  81 //      to open/print the file (the positional parameters are introduced by %1, 
  82 //      %2, ... in these strings, we change them to %s ourselves) 
  90     // initialize us with our file type name 
  91     void SetFileType(const wxString
& strFileType
) 
  92         { m_strFileType 
= strFileType
; } 
  93     void SetExt(const wxString
& ext
) 
  96     // implement accessor functions 
  97     bool GetExtensions(wxArrayString
& extensions
); 
  98     bool GetMimeType(wxString 
*mimeType
) const; 
  99     bool GetIcon(wxIcon 
*icon
) const; 
 100     bool GetDescription(wxString 
*desc
) const; 
 101     bool GetOpenCommand(wxString 
*openCmd
, 
 102                         const wxFileType::MessageParameters
&) const 
 103         { return GetCommand(openCmd
, "open"); } 
 104     bool GetPrintCommand(wxString 
*printCmd
, 
 105                          const wxFileType::MessageParameters
&) const 
 106         { return GetCommand(printCmd
, "print"); } 
 110     bool GetCommand(wxString 
*command
, const char *verb
) const; 
 112     wxString m_strFileType
, m_ext
; 
 115 class wxMimeTypesManagerImpl
 
 118     // nothing to do here, we don't load any data but just go and fetch it from 
 119     // the registry when asked for 
 120     wxMimeTypesManagerImpl() { } 
 122     // implement containing class functions 
 123     wxFileType 
*GetFileTypeFromExtension(const wxString
& ext
); 
 124     wxFileType 
*GetFileTypeFromMimeType(const wxString
& mimeType
); 
 126     // this are NOPs under Windows 
 127     void ReadMailcap(const wxString
& filename
) { } 
 128     void ReadMimeTypes(const wxString
& filename
) { } 
 133 // this class uses both mailcap and mime.types to gather information about file 
 136 // The information about mailcap file was extracted from metamail(1) sources and 
 139 // Format of mailcap file: spaces are ignored, each line is either a comment 
 140 // (starts with '#') or a line of the form <field1>;<field2>;...;<fieldN>. 
 141 // A backslash can be used to quote semicolons and newlines (and, in fact, 
 142 // anything else including itself). 
 144 // The first field is always the MIME type in the form of type/subtype (see RFC 
 145 // 822) where subtype may be '*' meaning "any". Following metamail, we accept 
 146 // "type" which means the same as "type/*", although I'm not sure whether this 
 149 // The second field is always the command to run. It is subject to 
 150 // parameter/filename expansion described below. 
 152 // All the following fields are optional and may not be present at all. If 
 153 // they're present they may appear in any order, although each of them should 
 154 // appear only once. The optional fields are the following: 
 155 //  * notes=xxx is an uninterpreted string which is silently ignored 
 156 //  * test=xxx is the command to be used to determine whether this mailcap line 
 157 //    applies to our data or not. The RHS of this field goes through the 
 158 //    parameter/filename expansion (as the 2nd field) and the resulting string 
 159 //    is executed. The line applies only if the command succeeds, i.e. returns 0 
 161 //  * print=xxx is the command to be used to print (and not view) the data of 
 162 //    this type (parameter/filename expansion is done here too) 
 163 //  * edit=xxx is the command to open/edit the data of this type 
 164 //  * needsterminal means that a new console must be created for the viewer 
 165 //  * copiousoutput means that the viewer doesn't interact with the user but 
 166 //    produces (possibly) a lof of lines of output on stdout (i.e. "cat" is a 
 167 //    good example), thus it might be a good idea to use some kind of paging 
 169 //  * textualnewlines means not to perform CR/LF translation (not honored) 
 170 //  * compose and composetyped fields are used to determine the program to be 
 171 //    called to create a new message pert in the specified format (unused). 
 173 // Parameter/filename xpansion: 
 174 //  * %s is replaced with the (full) file name 
 175 //  * %t is replaced with MIME type/subtype of the entry 
 176 //  * for multipart type only %n is replaced with the nnumber of parts and %F is 
 177 //    replaced by an array of (content-type, temporary file name) pairs for all 
 178 //    message parts (TODO) 
 179 //  * %{parameter} is replaced with the value of parameter taken from 
 180 //    Content-type header line of the message. 
 182 // FIXME any docs with real descriptions of these files?? 
 184 // There are 2 possible formats for mime.types file, one entry per line (used 
 185 // for global mime.types) and "expanded" format where an entry takes multiple 
 186 // lines (used for users mime.types). 
 188 // For both formats spaces are ignored and lines starting with a '#' are 
 189 // comments. Each record has one of two following forms: 
 190 //  a) for "brief" format: 
 191 //      <mime type>  <space separated list of extensions> 
 192 //  b) for "expanded" format: 
 193 //      type=<mime type> \ desc="<description>" \ exts="ext" 
 195 // We try to autodetect the format of mime.types: if a non-comment line starts 
 196 // with "type=" we assume the second format, otherwise the first one. 
 198 // there may be more than one entry for one and the same mime type, to 
 199 // choose the right one we have to run the command specified in the test 
 200 // field on our data. 
 205     MailCapEntry(const wxString
& openCmd
, 
 206                  const wxString
& printCmd
, 
 207                  const wxString
& testCmd
) 
 208         : m_openCmd(openCmd
), m_printCmd(printCmd
), m_testCmd(testCmd
) 
 214     const wxString
& GetOpenCmd()  const { return m_openCmd
;  } 
 215     const wxString
& GetPrintCmd() const { return m_printCmd
; } 
 216     const wxString
& GetTestCmd()  const { return m_testCmd
;  } 
 218     MailCapEntry 
*GetNext() const { return m_next
; } 
 221         // prepend this element to the list 
 222     void Prepend(MailCapEntry 
*next
) { m_next 
= next
; } 
 223         // append to the list 
 224     void Append(MailCapEntry 
*next
) 
 228         for ( cur 
= next
; cur
->m_next 
!= NULL
; cur 
= cur
->m_next 
) 
 233         // we initialize it in the ctor and there is no reason to both Prepend() 
 234         // and Append() one and the same entry 
 235         wxASSERT( m_next 
== NULL 
); 
 239     wxString m_openCmd
,         // command to use to open/view the file 
 241              m_testCmd
;         // only apply this entry if test yields 
 242                                 // true (i.e. the command returns 0) 
 244     MailCapEntry 
*m_next
;       // in the linked list 
 247 WX_DEFINE_ARRAY(MailCapEntry 
*, ArrayTypeEntries
); 
 249 class wxMimeTypesManagerImpl
 
 251 friend class wxFileTypeImpl
; // give it access to m_aXXX variables 
 254     // ctor loads all info into memory for quicker access later on 
 255     // @@ it would be nice to load them all, but parse on demand only... 
 256     wxMimeTypesManagerImpl(); 
 258     // implement containing class functions 
 259     wxFileType 
*GetFileTypeFromExtension(const wxString
& ext
); 
 260     wxFileType 
*GetFileTypeFromMimeType(const wxString
& mimeType
); 
 262     void ReadMailcap(const wxString
& filename
); 
 263     void ReadMimeTypes(const wxString
& filename
); 
 266         // get the string containing space separated extensions for the given 
 268     wxString 
GetExtension(size_t index
) { return m_aExtensions
[index
]; } 
 271     wxArrayString m_aTypes
,         // MIME types 
 272                   m_aDescriptions
,  // descriptions (just some text) 
 273                   m_aExtensions
;    // space separated list of extensions 
 274     ArrayTypeEntries m_aEntries
;    // commands and tests for this file type 
 280     // initialization functions 
 281     void Init(wxMimeTypesManagerImpl 
*manager
, size_t index
) 
 282         { m_manager 
= manager
; m_index 
= index
; } 
 285     bool GetExtensions(wxArrayString
& extensions
); 
 286     bool GetMimeType(wxString 
*mimeType
) const 
 287         { *mimeType 
= m_manager
->m_aTypes
[m_index
]; return TRUE
; } 
 288     bool GetIcon(wxIcon 
* WXUNUSED(icon
)) const 
 289         { return FALSE
; }   // @@ maybe with Gnome/KDE integration... 
 290     bool GetDescription(wxString 
*desc
) const 
 291         { *desc 
= m_manager
->m_aDescriptions
[m_index
]; return TRUE
; } 
 293     bool GetOpenCommand(wxString 
*openCmd
, 
 294                         const wxFileType::MessageParameters
& params
) const 
 296         return GetExpandedCommand(openCmd
, params
, TRUE
); 
 299     bool GetPrintCommand(wxString 
*printCmd
, 
 300                          const wxFileType::MessageParameters
& params
) const 
 302         return GetExpandedCommand(printCmd
, params
, FALSE
); 
 306     // get the entry which passes the test (may return NULL) 
 307     MailCapEntry 
*GetEntry(const wxFileType::MessageParameters
& params
) const; 
 309     // choose the correct entry to use and expand the command 
 310     bool GetExpandedCommand(wxString 
*expandedCmd
, 
 311                             const wxFileType::MessageParameters
& params
, 
 314     wxMimeTypesManagerImpl 
*m_manager
; 
 315     size_t                  m_index
; // in the wxMimeTypesManagerImpl arrays 
 320 // ============================================================================ 
 321 // implementation of the wrapper classes 
 322 // ============================================================================ 
 324 // ---------------------------------------------------------------------------- 
 326 // ---------------------------------------------------------------------------- 
 328 wxString 
wxFileType::ExpandCommand(const wxString
& command
, 
 329                                    const wxFileType::MessageParameters
& params
) 
 331     bool hasFilename 
= FALSE
; 
 334     for ( const char *pc 
= command
.c_str(); *pc 
!= '\0'; pc
++ ) { 
 338                     // '%s' expands into file name (quoted because it might 
 339                     // contain spaces) - except if there are already quotes 
 340                     // there because otherwise some programs may get confused 
 341                     // by double double quotes 
 343                     if ( *(pc 
- 2) == '"' ) 
 344                         str 
<< params
.GetFileName(); 
 346                         str 
<< '"' << params
.GetFileName() << '"'; 
 348                     str 
<< params
.GetFileName(); 
 353                     // '%t' expands into MIME type (quote it too just to be 
 355                     str 
<< '\'' << params
.GetMimeType() << '\''; 
 360                         const char *pEnd 
= strchr(pc
, '}'); 
 361                         if ( pEnd 
== NULL 
) { 
 363                             wxLogWarning(_("Unmatched '{' in an entry for " 
 365                                          params
.GetMimeType().c_str()); 
 369                             wxString 
param(pc 
+ 1, pEnd 
- pc 
- 1); 
 370                             str 
<< '\'' << params
.GetParamValue(param
) << '\''; 
 378                     // TODO %n is the number of parts, %F is an array containing 
 379                     //      the names of temp files these parts were written to 
 380                     //      and their mime types. 
 384                     wxLogDebug("Unknown field %%%c in command '%s'.", 
 385                                *pc
, command
.c_str()); 
 394     // metamail(1) man page states that if the mailcap entry doesn't have '%s' 
 395     // the program will accept the data on stdin: so give it to it! 
 396     if ( !hasFilename 
&& !str
.IsEmpty() ) { 
 397         str 
<< " < '" << params
.GetFileName() << '\''; 
 403 wxFileType::wxFileType() 
 405     m_impl 
= new wxFileTypeImpl
; 
 408 wxFileType::~wxFileType() 
 413 bool wxFileType::GetExtensions(wxArrayString
& extensions
) 
 415     return m_impl
->GetExtensions(extensions
); 
 418 bool wxFileType::GetMimeType(wxString 
*mimeType
) const 
 420     return m_impl
->GetMimeType(mimeType
); 
 423 bool wxFileType::GetIcon(wxIcon 
*icon
) const 
 425     return m_impl
->GetIcon(icon
); 
 428 bool wxFileType::GetDescription(wxString 
*desc
) const 
 430     return m_impl
->GetDescription(desc
); 
 434 wxFileType::GetOpenCommand(wxString 
*openCmd
, 
 435                            const wxFileType::MessageParameters
& params
) const 
 437     return m_impl
->GetOpenCommand(openCmd
, params
); 
 441 wxFileType::GetPrintCommand(wxString 
*printCmd
, 
 442                             const wxFileType::MessageParameters
& params
) const 
 444     return m_impl
->GetPrintCommand(printCmd
, params
); 
 447 // ---------------------------------------------------------------------------- 
 448 // wxMimeTypesManager 
 449 // ---------------------------------------------------------------------------- 
 451 bool wxMimeTypesManager::IsOfType(const wxString
& mimeType
, 
 452                                   const wxString
& wildcard
) 
 454     wxASSERT_MSG( mimeType
.Find('*') == wxNOT_FOUND
, 
 455                   "first MIME type can't contain wildcards" ); 
 457     // all comparaisons are case insensitive (2nd arg of IsSameAs() is FALSE) 
 458     if ( wildcard
.BeforeFirst('/').IsSameAs(mimeType
.BeforeFirst('/'), FALSE
) ) 
 460         wxString strSubtype 
= wildcard
.AfterFirst('/'); 
 462         if ( strSubtype 
== '*' || 
 463              strSubtype
.IsSameAs(mimeType
.AfterFirst('/'), FALSE
) ) 
 465             // matches (either exactly or it's a wildcard) 
 473 wxMimeTypesManager::wxMimeTypesManager() 
 475     m_impl 
= new wxMimeTypesManagerImpl
; 
 478 wxMimeTypesManager::~wxMimeTypesManager() 
 484 wxMimeTypesManager::GetFileTypeFromExtension(const wxString
& ext
) 
 486     return m_impl
->GetFileTypeFromExtension(ext
); 
 490 wxMimeTypesManager::GetFileTypeFromMimeType(const wxString
& mimeType
) 
 492     return m_impl
->GetFileTypeFromMimeType(mimeType
); 
 495 // ============================================================================ 
 496 // real (OS specific) implementation 
 497 // ============================================================================ 
 501 bool wxFileTypeImpl::GetCommand(wxString 
*command
, const char *verb
) const 
 503     // suppress possible error messages 
 506     strKey 
<< m_strFileType 
<< "\\shell\\" << verb 
<< "\\command"; 
 507     wxRegKey 
key(wxRegKey::HKCR
, strKey
); 
 510         // it's the default value of the key 
 511         if ( key
.QueryValue("", *command
) ) { 
 512             // transform it from '%1' to '%s' style format string 
 513             // @@ we don't make any attempt to verify that the string is valid, 
 514             //    i.e. doesn't contain %2, or second %1 or .... But we do make 
 515             //    sure that we return a string with _exactly_ one '%s'! 
 516             size_t len 
= command
->Len(); 
 517             for ( size_t n 
= 0; n 
< len
; n
++ ) { 
 518                 if ( command
->GetChar(n
) == '%' && 
 519                      (n 
+ 1 < len
) && command
->GetChar(n 
+ 1) == '1' ) { 
 520                     // replace it with '%s' 
 521                     command
->SetChar(n 
+ 1, 's'); 
 527             // we didn't find any '%1'! 
 528             // @@@ hack: append the filename at the end, hope that it will do 
 535     // no such file type or no value 
 539 // @@ this function is half implemented 
 540 bool wxFileTypeImpl::GetExtensions(wxArrayString
& extensions
) 
 542     if ( m_ext
.IsEmpty() ) { 
 543         // the only way to get the list of extensions from the file type is to 
 544         // scan through all extensions in the registry - too slow... 
 549         extensions
.Add(m_ext
); 
 551         // it's a lie too, we don't return _all_ extensions... 
 556 bool wxFileTypeImpl::GetMimeType(wxString 
*mimeType
) const 
 558     // suppress possible error messages 
 560     wxRegKey 
key(wxRegKey::HKCR
, /*m_strFileType*/ "." + m_ext
); 
 561     if ( key
.Open() && key
.QueryValue("Content Type", *mimeType
) ) { 
 569 bool wxFileTypeImpl::GetIcon(wxIcon 
*icon
) const 
 572     strIconKey 
<< m_strFileType 
<< "\\DefaultIcon"; 
 574     // suppress possible error messages 
 576     wxRegKey 
key(wxRegKey::HKCR
, strIconKey
); 
 580         // it's the default value of the key 
 581         if ( key
.QueryValue("", strIcon
) ) { 
 582             // the format is the following: <full path to file>, <icon index> 
 583             // NB: icon index may be negative as well as positive and the full 
 584             //     path may contain the environment variables inside '%' 
 585             wxString strFullPath 
= strIcon
.BeforeLast(','), 
 586             strIndex 
= strIcon
.AfterLast(','); 
 588             // index may be omitted, in which case BeforeLast(',') is empty and 
 589             // AfterLast(',') is the whole string 
 590             if ( strFullPath
.IsEmpty() ) { 
 591                 strFullPath 
= strIndex
; 
 595             wxString strExpPath 
= wxExpandEnvVars(strFullPath
); 
 596             int nIndex 
= atoi(strIndex
); 
 598             HICON hIcon 
= ExtractIcon(GetModuleHandle(NULL
), strExpPath
, nIndex
); 
 599             switch ( (int)hIcon 
) { 
 600                 case 0: // means no icons were found 
 601                 case 1: // means no such file or it wasn't a DLL/EXE/OCX/ICO/... 
 602                     wxLogDebug("incorrect registry entry '%s': no such icon.", 
 603                                key
.GetName().c_str()); 
 607                     icon
->SetHICON((WXHICON
)hIcon
); 
 613     // no such file type or no value or incorrect icon entry 
 617 bool wxFileTypeImpl::GetDescription(wxString 
*desc
) const 
 619     // suppress possible error messages 
 621     wxRegKey 
key(wxRegKey::HKCR
, m_strFileType
); 
 624         // it's the default value of the key 
 625         if ( key
.QueryValue("", *desc
) ) { 
 633 // extension -> file type 
 635 wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString
& ext
) 
 637     // add the leading point if necessary 
 639     if ( ext
[0u] != '.' ) { 
 644     // suppress possible error messages 
 647     wxString strFileType
; 
 648     wxRegKey 
key(wxRegKey::HKCR
, str
); 
 650         // it's the default value of the key 
 651         if ( key
.QueryValue("", strFileType
) ) { 
 652             // create the new wxFileType object 
 653             wxFileType 
*fileType 
= new wxFileType
; 
 654             fileType
->m_impl
->SetFileType(strFileType
); 
 655             fileType
->m_impl
->SetExt(ext
); 
 665 // MIME type -> extension -> file type 
 667 wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString
& mimeType
) 
 669     // @@@ I don't know of any official documentation which mentions this 
 670     //     location, but as a matter of fact IE uses it, so why not we? 
 671     static const char *szMimeDbase 
= "MIME\\Database\\Content Type\\"; 
 673     wxString strKey 
= szMimeDbase
; 
 676     // suppress possible error messages 
 680     wxRegKey 
key(wxRegKey::HKCR
, strKey
); 
 682         if ( key
.QueryValue("Extension", ext
) ) { 
 683             return GetFileTypeFromExtension(ext
); 
 694 wxFileTypeImpl::GetEntry(const wxFileType::MessageParameters
& params
) const 
 697     MailCapEntry 
*entry 
= m_manager
->m_aEntries
[m_index
]; 
 698     while ( entry 
!= NULL 
) { 
 699         // notice that an empty command would always succeed (@@ is it ok?) 
 700         command 
= wxFileType::ExpandCommand(entry
->GetTestCmd(), params
); 
 702         if ( command
.IsEmpty() || (system(command
) == 0) ) { 
 704             wxLogTrace("Test '%s' for mime type '%s' succeeded.", 
 705                        command
.c_str(), params
.GetMimeType().c_str()); 
 709             wxLogTrace("Test '%s' for mime type '%s' failed.", 
 710                        command
.c_str(), params
.GetMimeType().c_str()); 
 713         entry 
= entry
->GetNext(); 
 720 wxFileTypeImpl::GetExpandedCommand(wxString 
*expandedCmd
, 
 721                                    const wxFileType::MessageParameters
& params
, 
 724     MailCapEntry 
*entry 
= GetEntry(params
); 
 725     if ( entry 
== NULL 
) { 
 726         // all tests failed... 
 730     wxString cmd 
= open 
? entry
->GetOpenCmd() : entry
->GetPrintCmd(); 
 731     if ( cmd
.IsEmpty() ) { 
 732         // may happen, especially for "print" 
 736     *expandedCmd 
= wxFileType::ExpandCommand(cmd
, params
); 
 740 bool wxFileTypeImpl::GetExtensions(wxArrayString
& extensions
) 
 742     wxString strExtensions 
= m_manager
->GetExtension(m_index
); 
 745     // one extension in the space or comma delimitid list 
 747     for ( const char *p 
= strExtensions
; ; p
++ ) { 
 748         if ( *p 
== ' ' || *p 
== ',' || *p 
== '\0' ) { 
 749             if ( !strExt
.IsEmpty() ) { 
 750                 extensions
.Add(strExt
); 
 753             //else: repeated spaces (shouldn't happen, but it's not that 
 754             //      important if it does happen) 
 759         else if ( *p 
== '.' ) { 
 760             // remove the dot from extension (but only if it's the first char) 
 761             if ( !strExt
.IsEmpty() ) { 
 764             //else: no, don't append it 
 774 // read system and user mailcaps (TODO implement mime.types support) 
 775 wxMimeTypesManagerImpl::wxMimeTypesManagerImpl() 
 777     // directories where we look for mailcap and mime.types by default 
 778     // (taken from metamail(1) sources) 
 779     static const char *aStandardLocations
[] = 
 788     // first read the system wide file(s) 
 789     for ( size_t n 
= 0; n 
< WXSIZEOF(aStandardLocations
); n
++ ) { 
 790         wxString dir 
= aStandardLocations
[n
]; 
 792         wxString file 
= dir 
+ "/mailcap"; 
 793         if ( wxFile::Exists(file
) ) { 
 797         file 
= dir 
+ "/mime.types"; 
 798         if ( wxFile::Exists(file
) ) { 
 803     wxString strHome 
= getenv("HOME"); 
 805     // and now the users mailcap 
 806     wxString strUserMailcap 
= strHome 
+ "/.mailcap"; 
 807     if ( wxFile::Exists(strUserMailcap
) ) { 
 808         ReadMailcap(strUserMailcap
); 
 811     // read the users mime.types 
 812     wxString strUserMimeTypes 
= strHome 
+ "/.mime.types"; 
 813     if ( wxFile::Exists(strUserMimeTypes
) ) { 
 814         ReadMimeTypes(strUserMimeTypes
); 
 819 wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString
& ext
) 
 821     size_t count 
= m_aExtensions
.GetCount(); 
 822     for ( size_t n 
= 0; n 
< count
; n
++ ) { 
 823         wxString extensions 
= m_aExtensions
[n
]; 
 824         while ( !extensions
.IsEmpty() ) { 
 825             wxString field 
= extensions
.BeforeFirst(' '); 
 826             extensions 
= extensions
.AfterFirst(' '); 
 828             // consider extensions as not being case-sensitive 
 829             if ( field
.IsSameAs(ext
, FALSE 
/* no case */) ) { 
 831                 wxFileType 
*fileType 
= new wxFileType
; 
 832                 fileType
->m_impl
->Init(this, n
); 
 844 wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString
& mimeType
) 
 846     // mime types are not case-sensitive 
 847     wxString 
mimetype(mimeType
); 
 848     mimetype
.MakeLower(); 
 850     // first look for an exact match 
 851     int index 
= m_aTypes
.Index(mimetype
); 
 852     if ( index 
== wxNOT_FOUND 
) { 
 853         // then try to find "text/*" as match for "text/plain" (for example) 
 854         // NB: if mimeType doesn't contain '/' at all, BeforeFirst() will return 
 855         //     the whole string - ok. 
 856         wxString strCategory 
= mimetype
.BeforeFirst('/'); 
 858         size_t nCount 
= m_aTypes
.Count(); 
 859         for ( size_t n 
= 0; n 
< nCount
; n
++ ) { 
 860             if ( (m_aTypes
[n
].BeforeFirst('/') == strCategory 
) && 
 861                  m_aTypes
[n
].AfterFirst('/') == "*" ) { 
 868     if ( index 
!= wxNOT_FOUND 
) { 
 869         wxFileType 
*fileType 
= new wxFileType
; 
 870         fileType
->m_impl
->Init(this, index
); 
 880 void wxMimeTypesManagerImpl::ReadMimeTypes(const wxString
& strFileName
) 
 882     wxLogTrace("--- Parsing mime.types file '%s' ---", strFileName
.c_str()); 
 884     wxTextFile 
file(strFileName
); 
 888     // the information we extract 
 889     wxString strMimeType
, strDesc
, strExtensions
; 
 891     size_t nLineCount 
= file
.GetLineCount(); 
 892     for ( size_t nLine 
= 0; nLine 
< nLineCount
; nLine
++ ) { 
 893         // now we're at the start of the line 
 894         const char *pc 
= file
[nLine
].c_str(); 
 897         while ( isspace(*pc
) ) 
 904         // detect file format 
 905         const char *pEqualSign 
= strchr(pc
, '='); 
 906         if ( pEqualSign 
== NULL 
) { 
 910             // first field is mime type 
 911             for ( strMimeType
.Empty(); !isspace(*pc
) && *pc 
!= '\0'; pc
++ ) { 
 916             while ( isspace(*pc
) ) 
 919             // take all the rest of the string 
 929             // the string on the left of '=' is the field name 
 930             wxString 
strLHS(pc
, pEqualSign 
- pc
); 
 933             for ( pc 
= pEqualSign 
+ 1; isspace(*pc
); pc
++ ) 
 938                 // the string is quoted and ends at the matching quote 
 939                 pEnd 
= strchr(++pc
, '"'); 
 940                 if ( pEnd 
== NULL 
) { 
 941                     wxLogWarning(_("Mime.types file %s, line %d: unterminated " 
 943                                  strFileName
.c_str(), nLine 
+ 1); 
 947                 // unquoted stringends at the first space 
 948                 for ( pEnd 
= pc
; !isspace(*pEnd
); pEnd
++ ) 
 952             // now we have the RHS (field value) 
 953             wxString 
strRHS(pc
, pEnd 
- pc
); 
 955             // check that it's more or less what we're waiting for, i.e. that 
 956             // only '\' is left on the line 
 957             if ( *pEnd 
== '"' ) { 
 962             for ( pc 
= pEnd
; isspace(*pc
); pc
++ ) 
 965             // only '\\' may be left on the line normally 
 966             bool entryEnded 
= *pc 
== '\0'; 
 967             if ( !entryEnded 
&& ((*pc 
!= '\\') || (*++pc 
!= '\0')) ) { 
 968                 wxLogWarning(_("Mime.types file %s, line %d: extra characters " 
 969                                "after the field value ignored."), 
 970                              strFileName
.c_str(), nLine 
+ 1); 
 972             // if there is a trailing backslash entryEnded = FALSE 
 974             // now see what we got 
 975             if ( strLHS 
== "type" ) { 
 976                 strMimeType 
= strRHS
; 
 978             else if ( strLHS 
== "desc" ) { 
 981             else if ( strLHS 
== "exts" ) { 
 982                 strExtensions 
= strRHS
; 
 985                 wxLogWarning(_("Unknown field in file %s, line %d: '%s'."), 
 986                              strFileName
.c_str(), nLine 
+ 1, strLHS
.c_str()); 
 990                 // as we don't reset strMimeType, the next line in this entry 
 991                 // will be interpreted correctly. 
 996         // although it doesn't seem to be covered by RFCs, some programs 
 997         // (notably Netscape) create their entries with several comma 
 998         // separated extensions (RFC mention the spaces only) 
 999         strExtensions
.Replace(",", " "); 
1001         // also deal with the leading dot 
1002         if ( !strExtensions
.IsEmpty() && strExtensions
[0] == '.' ) { 
1003             strExtensions
.erase(0, 1); 
1006         int index 
= m_aTypes
.Index(strMimeType
); 
1007         if ( index 
== wxNOT_FOUND 
) { 
1009             m_aTypes
.Add(strMimeType
); 
1010             m_aEntries
.Add(NULL
); 
1011             m_aExtensions
.Add(strExtensions
); 
1012             m_aDescriptions
.Add(strDesc
); 
1015             // modify an existing one 
1016             if ( !strDesc
.IsEmpty() ) { 
1017                 m_aDescriptions
[index
] = strDesc
;   // replace old value 
1019             m_aExtensions
[index
] += strExtensions
; 
1023     // check our data integriry 
1024     wxASSERT( m_aTypes
.Count() == m_aEntries
.Count() && 
1025               m_aTypes
.Count() == m_aExtensions
.Count() && 
1026               m_aTypes
.Count() == m_aDescriptions
.Count() ); 
1029 void wxMimeTypesManagerImpl::ReadMailcap(const wxString
& strFileName
) 
1031     wxLogTrace("--- Parsing mailcap file '%s' ---", strFileName
.c_str()); 
1033     wxTextFile 
file(strFileName
); 
1037     // see the comments near the end of function for the reason we need this 
1038     wxArrayInt aEntryIndices
; 
1040     size_t nLineCount 
= file
.GetLineCount(); 
1041     for ( size_t nLine 
= 0; nLine 
< nLineCount
; nLine
++ ) { 
1042         // now we're at the start of the line 
1043         const char *pc 
= file
[nLine
].c_str(); 
1046         while ( isspace(*pc
) ) 
1049         // comment or empty string? 
1050         if ( *pc 
== '#' || *pc 
== '\0' ) 
1055         // what field are we currently in? The first 2 are fixed and there may 
1056         // be an arbitrary number of other fields -- currently, we are not 
1057         // interested in any of them, but we should parse them as well... 
1063         } currentToken 
= Field_Type
; 
1065         // the flags and field values on the current line 
1066         bool needsterminal 
= FALSE
, 
1067              copiousoutput 
= FALSE
; 
1073                  curField
; // accumulator 
1074         for ( bool cont 
= TRUE
; cont
; pc
++ ) { 
1077                     // interpret the next character literally (notice that 
1078                     // backslash can be used for line continuation) 
1079                     if ( *++pc 
== '\0' ) { 
1080                         // fetch the next line. 
1082                         // pc currently points to nowhere, but after the next 
1083                         // pc++ in the for line it will point to the beginning 
1084                         // of the next line in the file 
1085                         pc 
= file
[++nLine
].c_str() - 1; 
1088                         // just a normal character 
1094                     cont 
= FALSE
;   // end of line reached, exit the loop 
1099                     // store this field and start looking for the next one 
1101                     // trim whitespaces from both sides 
1102                     curField
.Trim(TRUE
).Trim(FALSE
); 
1104                     switch ( currentToken 
) { 
1107                             if ( strType
.Find('/') == wxNOT_FOUND 
) { 
1108                                 // we interpret "type" as "type/*" 
1112                             currentToken 
= Field_OpenCmd
; 
1116                             strOpenCmd 
= curField
; 
1118                             currentToken 
= Field_Other
; 
1123                                 // "good" mailcap entry? 
1126                                 // is this something of the form foo=bar? 
1127                                 const char *pEq 
= strchr(curField
, '='); 
1128                                 if ( pEq 
!= NULL 
) { 
1129                                     wxString lhs 
= curField
.BeforeFirst('='), 
1130                                              rhs 
= curField
.AfterFirst('='); 
1132                                     lhs
.Trim(TRUE
);     // from right 
1133                                     rhs
.Trim(FALSE
);    // from left 
1135                                     if ( lhs 
== "print" ) 
1137                                     else if ( lhs 
== "test" ) 
1139                                     else if ( lhs 
== "description" ) { 
1140                                         // it might be quoted 
1141                                         if ( rhs
[0u] == '"' && 
1142                                              rhs
.Last() == '"' ) { 
1143                                             strDesc 
= wxString(rhs
.c_str() + 1, 
1150                                     else if ( lhs 
== "compose" || 
1151                                               lhs 
== "composetyped" || 
1160                                     // no, it's a simple flag 
1161                                     // TODO support the flags: 
1162                                     //  1. create an xterm for 'needsterminal' 
1163                                     //  2. append "| $PAGER" for 'copiousoutput' 
1164                                     if ( curField 
== "needsterminal" ) 
1165                                         needsterminal 
= TRUE
; 
1166                                     else if ( curField 
== "copiousoutput" ) 
1167                                         copiousoutput 
= TRUE
; 
1168                                     else if ( curField 
== "textualnewlines" ) 
1176                                     // don't flood the user with error messages 
1177                                     // if we don't understand something in his 
1178                                     // mailcap, but give them in debug mode 
1179                                     // because this might be useful for the 
1183                                       "Mailcap file %s, line %d: unknown " 
1184                                       "field '%s' for the MIME type " 
1186                                       strFileName
.c_str(), 
1194                             // it already has this value 
1195                             //currentToken = Field_Other; 
1199                             wxFAIL_MSG("unknown field type in mailcap"); 
1202                     // next token starts immediately after ';' 
1211         // check that we really read something reasonable 
1212         if ( currentToken 
== Field_Type 
|| currentToken 
== Field_OpenCmd 
) { 
1213             wxLogWarning(_("Mailcap file %s, line %d: incomplete entry " 
1215                          strFileName
.c_str(), nLine 
+ 1); 
1218             MailCapEntry 
*entry 
= new MailCapEntry(strOpenCmd
, 
1222             strType
.MakeLower(); 
1223             int nIndex 
= m_aTypes
.Index(strType
); 
1224             if ( nIndex 
== wxNOT_FOUND 
) { 
1226                 m_aTypes
.Add(strType
); 
1228                 m_aEntries
.Add(entry
); 
1229                 m_aExtensions
.Add(""); 
1230                 m_aDescriptions
.Add(strDesc
); 
1233                 // modify the existing entry: the entry in one and the same file 
1234                 // are read in top-to-bottom order, i.e. the entries read first 
1235                 // should be tried before the entries below. However, the files 
1236                 // read later should override the settings in the files read 
1237                 // before, thus we Append() the new entry to the list if it has 
1238                 // already occured in _this_ file, but Prepend() it if it 
1239                 // occured in some of the previous ones. 
1240                 if ( aEntryIndices
.Index(nIndex
) == wxNOT_FOUND 
) { 
1241                     // first time in this file 
1242                     aEntryIndices
.Add(nIndex
); 
1243                     entry
->Prepend(m_aEntries
[nIndex
]); 
1244                     m_aEntries
[nIndex
] = entry
; 
1247                     // not the first time in _this_ file 
1248                     entry
->Append(m_aEntries
[nIndex
]); 
1251                 if ( !strDesc
.IsEmpty() ) { 
1252                     // @@ replace the old one - what else can we do?? 
1253                     m_aDescriptions
[nIndex
] = strDesc
; 
1258         // check our data integriry 
1259         wxASSERT( m_aTypes
.Count() == m_aEntries
.Count() && 
1260                   m_aTypes
.Count() == m_aExtensions
.Count() && 
1261                   m_aTypes
.Count() == m_aDescriptions
.Count() );