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"
38 #include "wx/dynarray.h"
41 #include "wx/msw/registry.h"
43 #include "wx/textfile.h"
46 #include "wx/mimetype.h"
48 // other standard headers
51 // ----------------------------------------------------------------------------
53 // ----------------------------------------------------------------------------
55 // implementation classes, platform dependent
58 // These classes use Windows registry to retrieve the required information.
60 // Keys used (not all of them are documented, so it might actually stop working
61 // in futur versions of Windows...):
62 // 1. "HKCR\MIME\Database\Content Type" contains subkeys for all known MIME
63 // types, each key has a string value "Extension" which gives (dot preceded)
64 // extension for the files of this MIME type.
66 // 2. "HKCR\.ext" contains
67 // a) unnamed value containing the "filetype"
68 // b) value "Content Type" containing the MIME type
70 // 3. "HKCR\filetype" contains
71 // a) unnamed value containing the description
72 // b) subkey "DefaultIcon" with single unnamed value giving the icon index in
74 // c) shell\open\command and shell\open\print subkeys containing the commands
75 // to open/print the file (the positional parameters are introduced by %1,
76 // %2, ... in these strings, we change them to %s ourselves)
84 // initialize us with our file type name
85 void SetFileType(const wxString
& strFileType
)
86 { m_strFileType
= strFileType
; }
87 void SetExt(const wxString
& ext
)
90 // implement accessor functions
91 bool GetExtensions(wxArrayString
& extensions
);
92 bool GetMimeType(wxString
*mimeType
) const;
93 bool GetIcon(wxIcon
*icon
) const;
94 bool GetDescription(wxString
*desc
) const;
95 bool GetOpenCommand(wxString
*openCmd
,
96 const wxFileType::MessageParameters
&) const
97 { return GetCommand(openCmd
, "open"); }
98 bool GetPrintCommand(wxString
*printCmd
,
99 const wxFileType::MessageParameters
&) const
100 { return GetCommand(printCmd
, "print"); }
104 bool GetCommand(wxString
*command
, const char *verb
) const;
106 wxString m_strFileType
, m_ext
;
109 class wxMimeTypesManagerImpl
112 // nothing to do here, we don't load any data but just go and fetch it from
113 // the registry when asked for
114 wxMimeTypesManagerImpl() { }
116 // implement containing class functions
117 wxFileType
*GetFileTypeFromExtension(const wxString
& ext
);
118 wxFileType
*GetFileTypeFromMimeType(const wxString
& mimeType
);
120 // this are NOPs under Windows
121 void ReadMailcap(const wxString
& filename
) { }
122 void ReadMimeTypes(const wxString
& filename
) { }
127 // this class uses both mailcap and mime.types to gather information about file
130 // The information about mailcap file was extracted from metamail(1) sources and
133 // Format of mailcap file: spaces are ignored, each line is either a comment
134 // (starts with '#') or a line of the form <field1>;<field2>;...;<fieldN>.
135 // A backslash can be used to quote semicolons and newlines (and, in fact,
136 // anything else including itself).
138 // The first field is always the MIME type in the form of type/subtype (see RFC
139 // 822) where subtype may be '*' meaning "any". Following metamail, we accept
140 // "type" which means the same as "type/*", although I'm not sure whether this
143 // The second field is always the command to run. It is subject to
144 // parameter/filename expansion described below.
146 // All the following fields are optional and may not be present at all. If
147 // they're present they may appear in any order, although each of them should
148 // appear only once. The optional fields are the following:
149 // * notes=xxx is an uninterpreted string which is silently ignored
150 // * test=xxx is the command to be used to determine whether this mailcap line
151 // applies to our data or not. The RHS of this field goes through the
152 // parameter/filename expansion (as the 2nd field) and the resulting string
153 // is executed. The line applies only if the command succeeds, i.e. returns 0
155 // * print=xxx is the command to be used to print (and not view) the data of
156 // this type (parameter/filename expansion is done here too)
157 // * edit=xxx is the command to open/edit the data of this type
158 // * needsterminal means that a new console must be created for the viewer
159 // * copiousoutput means that the viewer doesn't interact with the user but
160 // produces (possibly) a lof of lines of output on stdout (i.e. "cat" is a
161 // good example), thus it might be a good idea to use some kind of paging
163 // * textualnewlines means not to perform CR/LF translation (not honored)
164 // * compose and composetyped fields are used to determine the program to be
165 // called to create a new message pert in the specified format (unused).
167 // Parameter/filename xpansion:
168 // * %s is replaced with the (full) file name
169 // * %t is replaced with MIME type/subtype of the entry
170 // * for multipart type only %n is replaced with the nnumber of parts and %F is
171 // replaced by an array of (content-type, temporary file name) pairs for all
172 // message parts (TODO)
173 // * %{parameter} is replaced with the value of parameter taken from
174 // Content-type header line of the message.
176 // FIXME any docs with real descriptions of these files??
178 // There are 2 possible formats for mime.types file, one entry per line (used
179 // for global mime.types) and "expanded" format where an entry takes multiple
180 // lines (used for users mime.types).
182 // For both formats spaces are ignored and lines starting with a '#' are
183 // comments. Each record has one of two following forms:
184 // a) for "brief" format:
185 // <mime type> <space separated list of extensions>
186 // b) for "expanded" format:
187 // type=<mime type> \ desc="<description>" \ exts="ext"
189 // We try to autodetect the format of mime.types: if a non-comment line starts
190 // with "type=" we assume the second format, otherwise the first one.
192 // there may be more than one entry for one and the same mime type, to
193 // choose the right one we have to run the command specified in the test
194 // field on our data.
199 MailCapEntry(const wxString
& openCmd
,
200 const wxString
& printCmd
,
201 const wxString
& testCmd
)
202 : m_openCmd(openCmd
), m_printCmd(printCmd
), m_testCmd(testCmd
)
208 const wxString
& GetOpenCmd() const { return m_openCmd
; }
209 const wxString
& GetPrintCmd() const { return m_printCmd
; }
210 const wxString
& GetTestCmd() const { return m_testCmd
; }
212 MailCapEntry
*GetNext() const { return m_next
; }
215 // prepend this element to the list
216 void Prepend(MailCapEntry
*next
) { m_next
= next
; }
217 // append to the list
218 void Append(MailCapEntry
*next
)
222 for ( cur
= next
; cur
->m_next
!= NULL
; cur
= cur
->m_next
)
227 // we initialize it in the ctor and there is no reason to both Prepend()
228 // and Append() one and the same entry
229 wxASSERT( m_next
== NULL
);
233 wxString m_openCmd
, // command to use to open/view the file
235 m_testCmd
; // only apply this entry if test yields
236 // true (i.e. the command returns 0)
238 MailCapEntry
*m_next
; // in the linked list
241 WX_DEFINE_ARRAY(MailCapEntry
*, ArrayTypeEntries
);
243 class wxMimeTypesManagerImpl
245 friend class wxFileTypeImpl
; // give it access to m_aXXX variables
248 // ctor loads all info into memory for quicker access later on
249 // @@ it would be nice to load them all, but parse on demand only...
250 wxMimeTypesManagerImpl();
252 // implement containing class functions
253 wxFileType
*GetFileTypeFromExtension(const wxString
& ext
);
254 wxFileType
*GetFileTypeFromMimeType(const wxString
& mimeType
);
256 void ReadMailcap(const wxString
& filename
);
257 void ReadMimeTypes(const wxString
& filename
);
260 // get the string containing space separated extensions for the given
262 wxString
GetExtension(size_t index
) { return m_aExtensions
[index
]; }
265 wxArrayString m_aTypes
, // MIME types
266 m_aDescriptions
, // descriptions (just some text)
267 m_aExtensions
; // space separated list of extensions
268 ArrayTypeEntries m_aEntries
; // commands and tests for this file type
274 // initialization functions
275 void Init(wxMimeTypesManagerImpl
*manager
, size_t index
)
276 { m_manager
= manager
; m_index
= index
; }
279 bool GetExtensions(wxArrayString
& extensions
);
280 bool GetMimeType(wxString
*mimeType
) const
281 { *mimeType
= m_manager
->m_aTypes
[m_index
]; return TRUE
; }
282 bool GetIcon(wxIcon
*icon
) const
283 { return FALSE
; } // @@ maybe with Gnome/KDE integration...
284 bool GetDescription(wxString
*desc
) const
285 { *desc
= m_manager
->m_aDescriptions
[m_index
]; return TRUE
; }
287 bool GetOpenCommand(wxString
*openCmd
,
288 const wxFileType::MessageParameters
& params
) const
290 return GetExpandedCommand(openCmd
, params
, TRUE
);
293 bool GetPrintCommand(wxString
*printCmd
,
294 const wxFileType::MessageParameters
& params
) const
296 return GetExpandedCommand(printCmd
, params
, FALSE
);
300 // get the entry which passes the test (may return NULL)
301 MailCapEntry
*GetEntry(const wxFileType::MessageParameters
& params
) const;
303 // choose the correct entry to use and expand the command
304 bool GetExpandedCommand(wxString
*expandedCmd
,
305 const wxFileType::MessageParameters
& params
,
308 wxMimeTypesManagerImpl
*m_manager
;
309 size_t m_index
; // in the wxMimeTypesManagerImpl arrays
314 // ============================================================================
315 // implementation of the wrapper classes
316 // ============================================================================
318 // ----------------------------------------------------------------------------
320 // ----------------------------------------------------------------------------
322 wxString
wxFileType::ExpandCommand(const wxString
& command
,
323 const wxFileType::MessageParameters
& params
)
325 bool hasFilename
= FALSE
;
328 for ( const char *pc
= command
.c_str(); *pc
!= '\0'; pc
++ ) {
332 // '%s' expands into file name (quoted because it might
333 // contain spaces) - except if there are already quotes
334 // there because otherwise some programs may get confused by
335 // double double quotes
337 if ( *(pc
- 2) == '"' )
338 str
<< params
.GetFileName();
340 str
<< '"' << params
.GetFileName() << '"';
342 str
<< params
.GetFileName();
347 // '%t' expands into MIME type (quote it too just to be
349 str
<< '\'' << params
.GetMimeType() << '\'';
354 const char *pEnd
= strchr(pc
, '}');
355 if ( pEnd
== NULL
) {
357 wxLogWarning(_("Unmatched '{' in an entry for "
359 params
.GetMimeType().c_str());
363 wxString
param(pc
+ 1, pEnd
- pc
- 1);
364 str
<< '\'' << params
.GetParamValue(param
) << '\'';
372 // TODO %n is the number of parts, %F is an array containing
373 // the names of temp files these parts were written to
374 // and their mime types.
378 wxLogDebug("Unknown field %%%c in command '%s'.",
379 *pc
, command
.c_str());
388 // metamail(1) man page states that if the mailcap entry doesn't have '%s'
389 // the program will accept the data on stdin: so give it to it!
390 if ( !hasFilename
&& !str
.IsEmpty() ) {
391 str
<< " < '" << params
.GetFileName() << '\'';
397 wxFileType::wxFileType()
399 m_impl
= new wxFileTypeImpl
;
402 wxFileType::~wxFileType()
407 bool wxFileType::GetExtensions(wxArrayString
& extensions
)
409 return m_impl
->GetExtensions(extensions
);
412 bool wxFileType::GetMimeType(wxString
*mimeType
) const
414 return m_impl
->GetMimeType(mimeType
);
417 bool wxFileType::GetIcon(wxIcon
*icon
) const
419 return m_impl
->GetIcon(icon
);
422 bool wxFileType::GetDescription(wxString
*desc
) const
424 return m_impl
->GetDescription(desc
);
428 wxFileType::GetOpenCommand(wxString
*openCmd
,
429 const wxFileType::MessageParameters
& params
) const
431 return m_impl
->GetOpenCommand(openCmd
, params
);
435 wxFileType::GetPrintCommand(wxString
*printCmd
,
436 const wxFileType::MessageParameters
& params
) const
438 return m_impl
->GetPrintCommand(printCmd
, params
);
441 // ----------------------------------------------------------------------------
442 // wxMimeTypesManager
443 // ----------------------------------------------------------------------------
445 wxMimeTypesManager::wxMimeTypesManager()
447 m_impl
= new wxMimeTypesManagerImpl
;
450 wxMimeTypesManager::~wxMimeTypesManager()
456 wxMimeTypesManager::GetFileTypeFromExtension(const wxString
& ext
)
458 return m_impl
->GetFileTypeFromExtension(ext
);
462 wxMimeTypesManager::GetFileTypeFromMimeType(const wxString
& mimeType
)
464 return m_impl
->GetFileTypeFromMimeType(mimeType
);
467 // ============================================================================
468 // real (OS specific) implementation
469 // ============================================================================
473 bool wxFileTypeImpl::GetCommand(wxString
*command
, const char *verb
) const
475 // suppress possible error messages
478 strKey
<< m_strFileType
<< "\\shell\\" << verb
<< "\\command";
479 wxRegKey
key(wxRegKey::HKCR
, strKey
);
482 // it's the default value of the key
483 if ( key
.QueryValue("", *command
) ) {
484 // transform it from '%1' to '%s' style format string
485 // @@ we don't make any attempt to verify that the string is valid,
486 // i.e. doesn't contain %2, or second %1 or .... But we do make
487 // sure that we return a string with _exactly_ one '%s'!
488 size_t len
= command
->Len();
489 for ( size_t n
= 0; n
< len
; n
++ ) {
490 if ( command
->GetChar(n
) == '%' &&
491 (n
+ 1 < len
) && command
->GetChar(n
+ 1) == '1' ) {
492 // replace it with '%s'
493 command
->SetChar(n
+ 1, 's');
499 // we didn't find any '%1'!
500 // @@@ hack: append the filename at the end, hope that it will do
507 // no such file type or no value
511 // @@ this function is half implemented
512 bool wxFileTypeImpl::GetExtensions(wxArrayString
& extensions
)
514 if ( m_ext
.IsEmpty() ) {
515 // the only way to get the list of extensions from the file type is to
516 // scan through all extensions in the registry - too slow...
521 extensions
.Add(m_ext
);
523 // it's a lie too, we don't return _all_ extensions...
528 bool wxFileTypeImpl::GetMimeType(wxString
*mimeType
) const
530 // suppress possible error messages
532 wxRegKey
key(wxRegKey::HKCR
, m_strFileType
);
533 if ( key
.Open() && key
.QueryValue("Content Type", *mimeType
) ) {
541 bool wxFileTypeImpl::GetIcon(wxIcon
*icon
) const
544 strIconKey
<< m_strFileType
<< "\\DefaultIcon";
546 // suppress possible error messages
548 wxRegKey
key(wxRegKey::HKCR
, strIconKey
);
552 // it's the default value of the key
553 if ( key
.QueryValue("", strIcon
) ) {
554 // the format is the following: <full path to file>, <icon index>
555 // NB: icon index may be negative as well as positive and the full
556 // path may contain the environment variables inside '%'
557 wxString strFullPath
= strIcon
.Before(','),
558 strIndex
= strIcon
.Right(',');
560 // index may be omitted, in which case Before(',') is empty and
561 // Right(',') is the whole string
562 if ( strFullPath
.IsEmpty() ) {
563 strFullPath
= strIndex
;
567 wxString strExpPath
= wxExpandEnvVars(strFullPath
);
568 int nIndex
= atoi(strIndex
);
570 HICON hIcon
= ExtractIcon(GetModuleHandle(NULL
), strExpPath
, nIndex
);
571 switch ( (int)hIcon
) {
572 case 0: // means no icons were found
573 case 1: // means no such file or it wasn't a DLL/EXE/OCX/ICO/...
574 wxLogDebug("incorrect registry entry '%s': no such icon.",
575 key
.GetName().c_str());
579 icon
->SetHICON((WXHICON
)hIcon
);
585 // no such file type or no value or incorrect icon entry
589 bool wxFileTypeImpl::GetDescription(wxString
*desc
) const
591 // suppress possible error messages
593 wxRegKey
key(wxRegKey::HKCR
, m_strFileType
);
596 // it's the default value of the key
597 if ( key
.QueryValue("", *desc
) ) {
605 // extension -> file type
607 wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString
& ext
)
609 // add the leading point if necessary
611 if ( ext
[0u] != '.' ) {
616 // suppress possible error messages
619 wxString strFileType
;
620 wxRegKey
key(wxRegKey::HKCR
, str
);
622 // it's the default value of the key
623 if ( key
.QueryValue("", strFileType
) ) {
624 // create the new wxFileType object
625 wxFileType
*fileType
= new wxFileType
;
626 fileType
->m_impl
->SetFileType(strFileType
);
627 fileType
->m_impl
->SetExt(ext
);
637 // MIME type -> extension -> file type
639 wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString
& mimeType
)
641 // @@@ I don't know of any official documentation which mentions this
642 // location, but as a matter of fact IE uses it, so why not we?
643 static const char *szMimeDbase
= "MIME\\Database\\Content Type\\";
645 wxString strKey
= szMimeDbase
;
648 // suppress possible error messages
652 wxRegKey
key(wxRegKey::HKCR
, strKey
);
654 if ( key
.QueryValue("Extension", ext
) ) {
655 return GetFileTypeFromExtension(ext
);
666 wxFileTypeImpl::GetEntry(const wxFileType::MessageParameters
& params
) const
669 MailCapEntry
*entry
= m_manager
->m_aEntries
[m_index
];
670 while ( entry
!= NULL
) {
671 // notice that an empty command would always succeed (@@ is it ok?)
672 command
= wxFileType::ExpandCommand(entry
->GetTestCmd(), params
);
674 if ( command
.IsEmpty() || (system(command
) == 0) ) {
676 wxLogTrace("Test '%s' for mime type '%s' succeeded.",
677 command
.c_str(), params
.GetMimeType().c_str());
681 wxLogTrace("Test '%s' for mime type '%s' failed.",
682 command
.c_str(), params
.GetMimeType().c_str());
685 entry
= entry
->GetNext();
692 wxFileTypeImpl::GetExpandedCommand(wxString
*expandedCmd
,
693 const wxFileType::MessageParameters
& params
,
696 MailCapEntry
*entry
= GetEntry(params
);
697 if ( entry
== NULL
) {
698 // all tests failed...
702 wxString cmd
= open
? entry
->GetOpenCmd() : entry
->GetPrintCmd();
703 if ( cmd
.IsEmpty() ) {
704 // may happen, especially for "print"
708 *expandedCmd
= wxFileType::ExpandCommand(cmd
, params
);
712 bool wxFileTypeImpl::GetExtensions(wxArrayString
& extensions
)
714 wxString strExtensions
= m_manager
->GetExtension(m_index
);
717 // one extension in the space or comma delimitid list
719 for ( const char *p
= strExtensions
; ; p
++ ) {
720 if ( *p
== ' ' || *p
== ',' || *p
== '\0' ) {
721 if ( !strExt
.IsEmpty() ) {
722 extensions
.Add(strExt
);
725 //else: repeated spaces (shouldn't happen, but it's not that
726 // important if it does happen)
731 else if ( *p
== '.' ) {
732 // remove the dot from extension (but only if it's the first char)
733 if ( !strExt
.IsEmpty() ) {
736 //else: no, don't append it
746 // read system and user mailcaps (TODO implement mime.types support)
747 wxMimeTypesManagerImpl::wxMimeTypesManagerImpl()
749 // directories where we look for mailcap and mime.types by default
750 // (taken from metamail(1) sources)
751 static const char *aStandardLocations
[] =
760 // first read the system wide file(s)
761 for ( size_t n
= 0; n
< WXSIZEOF(aStandardLocations
); n
++ ) {
762 wxString dir
= aStandardLocations
[n
];
764 wxString file
= dir
+ "/mailcap";
765 if ( wxFile::Exists(file
) ) {
769 file
= dir
+ "/mime.types";
770 if ( wxFile::Exists(file
) ) {
775 wxString strHome
= getenv("HOME");
777 // and now the users mailcap
778 wxString strUserMailcap
= strHome
+ "/.mailcap";
779 if ( wxFile::Exists(strUserMailcap
) ) {
780 ReadMailcap(strUserMailcap
);
783 // read the users mime.types
784 wxString strUserMimeTypes
= strHome
+ "/.mime.types";
785 if ( wxFile::Exists(strUserMimeTypes
) ) {
786 ReadMimeTypes(strUserMimeTypes
);
791 wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString
& ext
)
793 wxFAIL_MSG("not implemented (must parse mime.types)");
799 wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString
& mimeType
)
801 // mime types are not case-sensitive
802 wxString
mimetype(mimeType
);
803 mimetype
.MakeLower();
805 // first look for an exact match
806 int index
= m_aTypes
.Index(mimetype
);
807 if ( index
== NOT_FOUND
) {
808 // then try to find "text/*" as match for "text/plain" (for example)
809 // NB: if mimeType doesn't contain '/' at all, Left() will return the
810 // whole string - ok.
811 wxString strCategory
= mimetype
.Left('/');
813 size_t nCount
= m_aTypes
.Count();
814 for ( size_t n
= 0; n
< nCount
; n
++ ) {
815 if ( (m_aTypes
[n
].Before('/') == strCategory
) &&
816 m_aTypes
[n
].Right('/') == "*" ) {
823 if ( index
!= NOT_FOUND
) {
824 wxFileType
*fileType
= new wxFileType
;
825 fileType
->m_impl
->Init(this, index
);
835 void wxMimeTypesManagerImpl::ReadMimeTypes(const wxString
& strFileName
)
837 wxLogTrace("--- Parsing mime.types file '%s' ---", strFileName
.c_str());
839 wxTextFile
file(strFileName
);
843 // the information we extract
844 wxString strMimeType
, strDesc
, strExtensions
;
846 size_t nLineCount
= file
.GetLineCount();
847 for ( size_t nLine
= 0; nLine
< nLineCount
; nLine
++ ) {
848 // now we're at the start of the line
849 const char *pc
= file
[nLine
].c_str();
852 while ( isspace(*pc
) )
859 // detect file format
860 const char *pEqualSign
= strchr(pc
, '=');
861 if ( pEqualSign
== NULL
) {
865 // first field is mime type
866 for ( strMimeType
.Empty(); !isspace(*pc
) && *pc
!= '\0'; pc
++ ) {
871 while ( isspace(*pc
) )
874 // take all the rest of the string
884 // the string on the left of '=' is the field name
885 wxString
strLHS(pc
, pEqualSign
- pc
);
888 for ( pc
= pEqualSign
+ 1; isspace(*pc
); pc
++ )
893 // the string is quoted and ends at the matching quote
894 pEnd
= strchr(++pc
, '"');
895 if ( pEnd
== NULL
) {
896 wxLogWarning(_("Mime.types file %s, line %d: unterminated "
898 strFileName
.c_str(), nLine
+ 1);
902 // unquoted stringends at the first space
903 for ( pEnd
= pc
; !isspace(*pEnd
); pEnd
++ )
907 // now we have the RHS (field value)
908 wxString
strRHS(pc
, pEnd
- pc
);
910 // check that it's more or less what we're waiting for, i.e. that
911 // only '\' is left on the line
912 if ( *pEnd
== '"' ) {
917 for ( pc
= pEnd
; isspace(*pc
); pc
++ )
920 // only '\\' may be left on the line normally
921 bool entryEnded
= *pc
== '\0';
922 if ( !entryEnded
&& ((*pc
!= '\\') || (*++pc
!= '\0')) ) {
923 wxLogWarning(_("Mime.types file %s, line %d: extra characters "
924 "after the field value ignored."),
925 strFileName
.c_str(), nLine
+ 1);
927 // if there is a trailing backslash entryEnded = FALSE
929 // now see what we got
930 if ( strLHS
== "type" ) {
931 strMimeType
= strRHS
;
933 else if ( strLHS
== "desc" ) {
936 else if ( strLHS
== "exts" ) {
937 strExtensions
= strRHS
;
940 wxLogWarning(_("Unknown field in file %s, line %d: '%s'."),
941 strFileName
.c_str(), nLine
+ 1, strLHS
.c_str());
945 // as we don't reset strMimeType, the next line in this entry
946 // will be interpreted correctly.
951 int index
= m_aTypes
.Index(strMimeType
);
952 if ( index
== NOT_FOUND
) {
954 m_aTypes
.Add(strMimeType
);
955 m_aEntries
.Add(NULL
);
956 m_aExtensions
.Add(strExtensions
);
957 m_aDescriptions
.Add(strDesc
);
960 // modify an existing one
961 if ( !strDesc
.IsEmpty() ) {
962 m_aDescriptions
[index
] = strDesc
; // replace old value
964 m_aExtensions
[index
] += strExtensions
;
968 // check our data integriry
969 wxASSERT( m_aTypes
.Count() == m_aEntries
.Count() &&
970 m_aTypes
.Count() == m_aExtensions
.Count() &&
971 m_aTypes
.Count() == m_aDescriptions
.Count() );
974 void wxMimeTypesManagerImpl::ReadMailcap(const wxString
& strFileName
)
976 wxLogTrace("--- Parsing mailcap file '%s' ---", strFileName
.c_str());
978 wxTextFile
file(strFileName
);
982 // see the comments near the end of function for the reason we need this
983 wxArrayInt aEntryIndices
;
985 size_t nLineCount
= file
.GetLineCount();
986 for ( size_t nLine
= 0; nLine
< nLineCount
; nLine
++ ) {
987 // now we're at the start of the line
988 const char *pc
= file
[nLine
].c_str();
991 while ( isspace(*pc
) )
994 // comment or empty string?
995 if ( *pc
== '#' || *pc
== '\0' )
1000 // what field are we currently in? The first 2 are fixed and there may
1001 // be an arbitrary number of other fields -- currently, we are not
1002 // interested in any of them, but we should parse them as well...
1008 } currentToken
= Field_Type
;
1010 // the flags and field values on the current line
1011 bool needsterminal
= false,
1012 copiousoutput
= false;
1018 curField
; // accumulator
1019 for ( bool cont
= TRUE
; cont
; pc
++ ) {
1022 // interpret the next character literally (notice that
1023 // backslash can be used for line continuation)
1024 if ( *++pc
== '\0' ) {
1025 // fetch the next line.
1027 // pc currently points to nowhere, but after the next
1028 // pc++ in the for line it will point to the beginning
1029 // of the next line in the file
1030 pc
= file
[++nLine
].c_str() - 1;
1033 // just a normal character
1039 cont
= FALSE
; // end of line reached, exit the loop
1044 // store this field and start looking for the next one
1046 // trim whitespaces from both sides
1047 curField
.Trim(TRUE
).Trim(FALSE
);
1049 switch ( currentToken
) {
1052 if ( strType
.Find('/') == NOT_FOUND
) {
1053 // we interpret "type" as "type/*"
1057 currentToken
= Field_OpenCmd
;
1061 strOpenCmd
= curField
;
1063 currentToken
= Field_Other
;
1068 // "good" mailcap entry?
1071 // is this something of the form foo=bar?
1072 const char *pEq
= strchr(curField
, '=');
1073 if ( pEq
!= NULL
) {
1074 wxString lhs
= curField
.Left('='),
1075 rhs
= curField
.After('=');
1077 lhs
.Trim(TRUE
); // from right
1078 rhs
.Trim(FALSE
); // from left
1080 if ( lhs
== "print" )
1082 else if ( lhs
== "test" )
1084 else if ( lhs
== "description" ) {
1085 // it might be quoted
1086 if ( rhs
[0u] == '"' &&
1087 rhs
.Last() == '"' ) {
1088 strDesc
= wxString(rhs
.c_str() + 1,
1095 else if ( lhs
== "compose" ||
1096 lhs
== "composetyped" ||
1105 // no, it's a simple flag
1106 // TODO support the flags:
1107 // 1. create an xterm for 'needsterminal'
1108 // 2. append "| $PAGER" for 'copiousoutput'
1109 if ( curField
== "needsterminal" )
1110 needsterminal
= TRUE
;
1111 else if ( curField
== "copiousoutput" )
1112 copiousoutput
= TRUE
;
1113 else if ( curField
== "textualnewlines" )
1121 // don't flood the user with error messages
1122 // if we don't understand something in his
1126 _("Mailcap file %s, line %d: unknown "
1127 "field '%s' for the MIME type "
1129 strFileName
.c_str(),
1137 // it already has this value
1138 //currentToken = Field_Other;
1142 wxFAIL_MSG("unknown field type in mailcap");
1145 // next token starts immediately after ';'
1154 // check that we really read something reasonable
1155 if ( currentToken
== Field_Type
|| currentToken
== Field_OpenCmd
) {
1156 wxLogWarning(_("Mailcap file %s, line %d: incomplete entry "
1158 strFileName
.c_str(), nLine
+ 1);
1161 MailCapEntry
*entry
= new MailCapEntry(strOpenCmd
,
1165 strType
.MakeLower();
1166 int nIndex
= m_aTypes
.Index(strType
);
1167 if ( nIndex
== NOT_FOUND
) {
1169 m_aTypes
.Add(strType
);
1171 m_aEntries
.Add(entry
);
1172 m_aExtensions
.Add("");
1173 m_aDescriptions
.Add(strDesc
);
1176 // modify the existing entry: the entry in one and the same file
1177 // are read in top-to-bottom order, i.e. the entries read first
1178 // should be tried before the entries below. However, the files
1179 // read later should override the settings in the files read
1180 // before, thus we Append() the new entry to the list if it has
1181 // already occured in _this_ file, but Prepend() it if it
1182 // occured in some of the previous ones.
1183 if ( aEntryIndices
.Index(nIndex
) == NOT_FOUND
) {
1184 // first time in this file
1185 aEntryIndices
.Add(nIndex
);
1186 entry
->Prepend(m_aEntries
[nIndex
]);
1187 m_aEntries
[nIndex
] = entry
;
1190 // not the first time in _this_ file
1191 entry
->Append(m_aEntries
[nIndex
]);
1194 if ( !strDesc
.IsEmpty() ) {
1195 // @@ replace the old one - what else can we do??
1196 m_aDescriptions
[nIndex
] = strDesc
;
1201 // check our data integriry
1202 wxASSERT( m_aTypes
.Count() == m_aEntries
.Count() &&
1203 m_aTypes
.Count() == m_aExtensions
.Count() &&
1204 m_aTypes
.Count() == m_aDescriptions
.Count() );
1210 /* vi: set cin tw=80 ts=4 sw=4: */