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"
39 #include "wx/dynarray.h"
40 #include "wx/confbase.h"
43 #include "wx/msw/registry.h"
46 #include "wx/textfile.h"
49 #include "wx/mimetype.h"
51 // other standard headers
54 // ----------------------------------------------------------------------------
56 // ----------------------------------------------------------------------------
58 // implementation classes, platform dependent
61 // These classes use Windows registry to retrieve the required information.
63 // Keys used (not all of them are documented, so it might actually stop working
64 // in futur versions of Windows...):
65 // 1. "HKCR\MIME\Database\Content Type" contains subkeys for all known MIME
66 // types, each key has a string value "Extension" which gives (dot preceded)
67 // extension for the files of this MIME type.
69 // 2. "HKCR\.ext" contains
70 // a) unnamed value containing the "filetype"
71 // b) value "Content Type" containing the MIME type
73 // 3. "HKCR\filetype" contains
74 // a) unnamed value containing the description
75 // b) subkey "DefaultIcon" with single unnamed value giving the icon index in
77 // c) shell\open\command and shell\open\print subkeys containing the commands
78 // to open/print the file (the positional parameters are introduced by %1,
79 // %2, ... in these strings, we change them to %s ourselves)
87 // initialize us with our file type name
88 void SetFileType(const wxString
& strFileType
)
89 { m_strFileType
= strFileType
; }
90 void SetExt(const wxString
& ext
)
93 // implement accessor functions
94 bool GetExtensions(wxArrayString
& extensions
);
95 bool GetMimeType(wxString
*mimeType
) const;
96 bool GetIcon(wxIcon
*icon
) const;
97 bool GetDescription(wxString
*desc
) const;
98 bool GetOpenCommand(wxString
*openCmd
,
99 const wxFileType::MessageParameters
&) const
100 { return GetCommand(openCmd
, "open"); }
101 bool GetPrintCommand(wxString
*printCmd
,
102 const wxFileType::MessageParameters
&) const
103 { return GetCommand(printCmd
, "print"); }
107 bool GetCommand(wxString
*command
, const char *verb
) const;
109 wxString m_strFileType
, m_ext
;
112 class wxMimeTypesManagerImpl
115 // nothing to do here, we don't load any data but just go and fetch it from
116 // the registry when asked for
117 wxMimeTypesManagerImpl() { }
119 // implement containing class functions
120 wxFileType
*GetFileTypeFromExtension(const wxString
& ext
);
121 wxFileType
*GetFileTypeFromMimeType(const wxString
& mimeType
);
123 // this are NOPs under Windows
124 void ReadMailcap(const wxString
& filename
) { }
125 void ReadMimeTypes(const wxString
& filename
) { }
130 // this class uses both mailcap and mime.types to gather information about file
133 // The information about mailcap file was extracted from metamail(1) sources and
136 // Format of mailcap file: spaces are ignored, each line is either a comment
137 // (starts with '#') or a line of the form <field1>;<field2>;...;<fieldN>.
138 // A backslash can be used to quote semicolons and newlines (and, in fact,
139 // anything else including itself).
141 // The first field is always the MIME type in the form of type/subtype (see RFC
142 // 822) where subtype may be '*' meaning "any". Following metamail, we accept
143 // "type" which means the same as "type/*", although I'm not sure whether this
146 // The second field is always the command to run. It is subject to
147 // parameter/filename expansion described below.
149 // All the following fields are optional and may not be present at all. If
150 // they're present they may appear in any order, although each of them should
151 // appear only once. The optional fields are the following:
152 // * notes=xxx is an uninterpreted string which is silently ignored
153 // * test=xxx is the command to be used to determine whether this mailcap line
154 // applies to our data or not. The RHS of this field goes through the
155 // parameter/filename expansion (as the 2nd field) and the resulting string
156 // is executed. The line applies only if the command succeeds, i.e. returns 0
158 // * print=xxx is the command to be used to print (and not view) the data of
159 // this type (parameter/filename expansion is done here too)
160 // * edit=xxx is the command to open/edit the data of this type
161 // * needsterminal means that a new console must be created for the viewer
162 // * copiousoutput means that the viewer doesn't interact with the user but
163 // produces (possibly) a lof of lines of output on stdout (i.e. "cat" is a
164 // good example), thus it might be a good idea to use some kind of paging
166 // * textualnewlines means not to perform CR/LF translation (not honored)
167 // * compose and composetyped fields are used to determine the program to be
168 // called to create a new message pert in the specified format (unused).
170 // Parameter/filename xpansion:
171 // * %s is replaced with the (full) file name
172 // * %t is replaced with MIME type/subtype of the entry
173 // * for multipart type only %n is replaced with the nnumber of parts and %F is
174 // replaced by an array of (content-type, temporary file name) pairs for all
175 // message parts (TODO)
176 // * %{parameter} is replaced with the value of parameter taken from
177 // Content-type header line of the message.
179 // FIXME any docs with real descriptions of these files??
181 // There are 2 possible formats for mime.types file, one entry per line (used
182 // for global mime.types) and "expanded" format where an entry takes multiple
183 // lines (used for users mime.types).
185 // For both formats spaces are ignored and lines starting with a '#' are
186 // comments. Each record has one of two following forms:
187 // a) for "brief" format:
188 // <mime type> <space separated list of extensions>
189 // b) for "expanded" format:
190 // type=<mime type> \ desc="<description>" \ exts="ext"
192 // We try to autodetect the format of mime.types: if a non-comment line starts
193 // with "type=" we assume the second format, otherwise the first one.
195 // there may be more than one entry for one and the same mime type, to
196 // choose the right one we have to run the command specified in the test
197 // field on our data.
202 MailCapEntry(const wxString
& openCmd
,
203 const wxString
& printCmd
,
204 const wxString
& testCmd
)
205 : m_openCmd(openCmd
), m_printCmd(printCmd
), m_testCmd(testCmd
)
211 const wxString
& GetOpenCmd() const { return m_openCmd
; }
212 const wxString
& GetPrintCmd() const { return m_printCmd
; }
213 const wxString
& GetTestCmd() const { return m_testCmd
; }
215 MailCapEntry
*GetNext() const { return m_next
; }
218 // prepend this element to the list
219 void Prepend(MailCapEntry
*next
) { m_next
= next
; }
220 // append to the list
221 void Append(MailCapEntry
*next
)
225 for ( cur
= next
; cur
->m_next
!= NULL
; cur
= cur
->m_next
)
230 // we initialize it in the ctor and there is no reason to both Prepend()
231 // and Append() one and the same entry
232 wxASSERT( m_next
== NULL
);
236 wxString m_openCmd
, // command to use to open/view the file
238 m_testCmd
; // only apply this entry if test yields
239 // true (i.e. the command returns 0)
241 MailCapEntry
*m_next
; // in the linked list
244 WX_DEFINE_ARRAY(MailCapEntry
*, ArrayTypeEntries
);
246 class wxMimeTypesManagerImpl
248 friend class wxFileTypeImpl
; // give it access to m_aXXX variables
251 // ctor loads all info into memory for quicker access later on
252 // @@ it would be nice to load them all, but parse on demand only...
253 wxMimeTypesManagerImpl();
255 // implement containing class functions
256 wxFileType
*GetFileTypeFromExtension(const wxString
& ext
);
257 wxFileType
*GetFileTypeFromMimeType(const wxString
& mimeType
);
259 void ReadMailcap(const wxString
& filename
);
260 void ReadMimeTypes(const wxString
& filename
);
263 // get the string containing space separated extensions for the given
265 wxString
GetExtension(size_t index
) { return m_aExtensions
[index
]; }
268 wxArrayString m_aTypes
, // MIME types
269 m_aDescriptions
, // descriptions (just some text)
270 m_aExtensions
; // space separated list of extensions
271 ArrayTypeEntries m_aEntries
; // commands and tests for this file type
277 // initialization functions
278 void Init(wxMimeTypesManagerImpl
*manager
, size_t index
)
279 { m_manager
= manager
; m_index
= index
; }
282 bool GetExtensions(wxArrayString
& extensions
);
283 bool GetMimeType(wxString
*mimeType
) const
284 { *mimeType
= m_manager
->m_aTypes
[m_index
]; return TRUE
; }
285 bool GetIcon(wxIcon
*icon
) const
286 { return FALSE
; } // @@ maybe with Gnome/KDE integration...
287 bool GetDescription(wxString
*desc
) const
288 { *desc
= m_manager
->m_aDescriptions
[m_index
]; return TRUE
; }
290 bool GetOpenCommand(wxString
*openCmd
,
291 const wxFileType::MessageParameters
& params
) const
293 return GetExpandedCommand(openCmd
, params
, TRUE
);
296 bool GetPrintCommand(wxString
*printCmd
,
297 const wxFileType::MessageParameters
& params
) const
299 return GetExpandedCommand(printCmd
, params
, FALSE
);
303 // get the entry which passes the test (may return NULL)
304 MailCapEntry
*GetEntry(const wxFileType::MessageParameters
& params
) const;
306 // choose the correct entry to use and expand the command
307 bool GetExpandedCommand(wxString
*expandedCmd
,
308 const wxFileType::MessageParameters
& params
,
311 wxMimeTypesManagerImpl
*m_manager
;
312 size_t m_index
; // in the wxMimeTypesManagerImpl arrays
317 // ============================================================================
318 // implementation of the wrapper classes
319 // ============================================================================
321 // ----------------------------------------------------------------------------
323 // ----------------------------------------------------------------------------
325 wxString
wxFileType::ExpandCommand(const wxString
& command
,
326 const wxFileType::MessageParameters
& params
)
328 bool hasFilename
= FALSE
;
331 for ( const char *pc
= command
.c_str(); *pc
!= '\0'; pc
++ ) {
335 // '%s' expands into file name (quoted because it might
336 // contain spaces) - except if there are already quotes
337 // there because otherwise some programs may get confused by
338 // double double quotes
340 if ( *(pc
- 2) == '"' )
341 str
<< params
.GetFileName();
343 str
<< '"' << params
.GetFileName() << '"';
345 str
<< params
.GetFileName();
350 // '%t' expands into MIME type (quote it too just to be
352 str
<< '\'' << params
.GetMimeType() << '\'';
357 const char *pEnd
= strchr(pc
, '}');
358 if ( pEnd
== NULL
) {
360 wxLogWarning(_("Unmatched '{' in an entry for "
362 params
.GetMimeType().c_str());
366 wxString
param(pc
+ 1, pEnd
- pc
- 1);
367 str
<< '\'' << params
.GetParamValue(param
) << '\'';
375 // TODO %n is the number of parts, %F is an array containing
376 // the names of temp files these parts were written to
377 // and their mime types.
381 wxLogDebug("Unknown field %%%c in command '%s'.",
382 *pc
, command
.c_str());
391 // metamail(1) man page states that if the mailcap entry doesn't have '%s'
392 // the program will accept the data on stdin: so give it to it!
393 if ( !hasFilename
&& !str
.IsEmpty() ) {
394 str
<< " < '" << params
.GetFileName() << '\'';
400 wxFileType::wxFileType()
402 m_impl
= new wxFileTypeImpl
;
405 wxFileType::~wxFileType()
410 bool wxFileType::GetExtensions(wxArrayString
& extensions
)
412 return m_impl
->GetExtensions(extensions
);
415 bool wxFileType::GetMimeType(wxString
*mimeType
) const
417 return m_impl
->GetMimeType(mimeType
);
420 bool wxFileType::GetIcon(wxIcon
*icon
) const
422 return m_impl
->GetIcon(icon
);
425 bool wxFileType::GetDescription(wxString
*desc
) const
427 return m_impl
->GetDescription(desc
);
431 wxFileType::GetOpenCommand(wxString
*openCmd
,
432 const wxFileType::MessageParameters
& params
) const
434 return m_impl
->GetOpenCommand(openCmd
, params
);
438 wxFileType::GetPrintCommand(wxString
*printCmd
,
439 const wxFileType::MessageParameters
& params
) const
441 return m_impl
->GetPrintCommand(printCmd
, params
);
444 // ----------------------------------------------------------------------------
445 // wxMimeTypesManager
446 // ----------------------------------------------------------------------------
448 wxMimeTypesManager::wxMimeTypesManager()
450 m_impl
= new wxMimeTypesManagerImpl
;
453 wxMimeTypesManager::~wxMimeTypesManager()
459 wxMimeTypesManager::GetFileTypeFromExtension(const wxString
& ext
)
461 return m_impl
->GetFileTypeFromExtension(ext
);
465 wxMimeTypesManager::GetFileTypeFromMimeType(const wxString
& mimeType
)
467 return m_impl
->GetFileTypeFromMimeType(mimeType
);
470 // ============================================================================
471 // real (OS specific) implementation
472 // ============================================================================
476 bool wxFileTypeImpl::GetCommand(wxString
*command
, const char *verb
) const
478 // suppress possible error messages
481 strKey
<< m_strFileType
<< "\\shell\\" << verb
<< "\\command";
482 wxRegKey
key(wxRegKey::HKCR
, strKey
);
485 // it's the default value of the key
486 if ( key
.QueryValue("", *command
) ) {
487 // transform it from '%1' to '%s' style format string
488 // @@ we don't make any attempt to verify that the string is valid,
489 // i.e. doesn't contain %2, or second %1 or .... But we do make
490 // sure that we return a string with _exactly_ one '%s'!
491 size_t len
= command
->Len();
492 for ( size_t n
= 0; n
< len
; n
++ ) {
493 if ( command
->GetChar(n
) == '%' &&
494 (n
+ 1 < len
) && command
->GetChar(n
+ 1) == '1' ) {
495 // replace it with '%s'
496 command
->SetChar(n
+ 1, 's');
502 // we didn't find any '%1'!
503 // @@@ hack: append the filename at the end, hope that it will do
510 // no such file type or no value
514 // @@ this function is half implemented
515 bool wxFileTypeImpl::GetExtensions(wxArrayString
& extensions
)
517 if ( m_ext
.IsEmpty() ) {
518 // the only way to get the list of extensions from the file type is to
519 // scan through all extensions in the registry - too slow...
524 extensions
.Add(m_ext
);
526 // it's a lie too, we don't return _all_ extensions...
531 bool wxFileTypeImpl::GetMimeType(wxString
*mimeType
) const
533 // suppress possible error messages
535 wxRegKey
key(wxRegKey::HKCR
, m_strFileType
);
536 if ( key
.Open() && key
.QueryValue("Content Type", *mimeType
) ) {
544 bool wxFileTypeImpl::GetIcon(wxIcon
*icon
) const
547 strIconKey
<< m_strFileType
<< "\\DefaultIcon";
549 // suppress possible error messages
551 wxRegKey
key(wxRegKey::HKCR
, strIconKey
);
555 // it's the default value of the key
556 if ( key
.QueryValue("", strIcon
) ) {
557 // the format is the following: <full path to file>, <icon index>
558 // NB: icon index may be negative as well as positive and the full
559 // path may contain the environment variables inside '%'
560 wxString strFullPath
= strIcon
.Before(','),
561 strIndex
= strIcon
.Right(',');
563 // index may be omitted, in which case Before(',') is empty and
564 // Right(',') is the whole string
565 if ( strFullPath
.IsEmpty() ) {
566 strFullPath
= strIndex
;
570 wxString strExpPath
= wxExpandEnvVars(strFullPath
);
571 int nIndex
= atoi(strIndex
);
573 HICON hIcon
= ExtractIcon(GetModuleHandle(NULL
), strExpPath
, nIndex
);
574 switch ( (int)hIcon
) {
575 case 0: // means no icons were found
576 case 1: // means no such file or it wasn't a DLL/EXE/OCX/ICO/...
577 wxLogDebug("incorrect registry entry '%s': no such icon.",
578 key
.GetName().c_str());
582 icon
->SetHICON((WXHICON
)hIcon
);
588 // no such file type or no value or incorrect icon entry
592 bool wxFileTypeImpl::GetDescription(wxString
*desc
) const
594 // suppress possible error messages
596 wxRegKey
key(wxRegKey::HKCR
, m_strFileType
);
599 // it's the default value of the key
600 if ( key
.QueryValue("", *desc
) ) {
608 // extension -> file type
610 wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString
& ext
)
612 // add the leading point if necessary
614 if ( ext
[0u] != '.' ) {
619 // suppress possible error messages
622 wxString strFileType
;
623 wxRegKey
key(wxRegKey::HKCR
, str
);
625 // it's the default value of the key
626 if ( key
.QueryValue("", strFileType
) ) {
627 // create the new wxFileType object
628 wxFileType
*fileType
= new wxFileType
;
629 fileType
->m_impl
->SetFileType(strFileType
);
630 fileType
->m_impl
->SetExt(ext
);
640 // MIME type -> extension -> file type
642 wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString
& mimeType
)
644 // @@@ I don't know of any official documentation which mentions this
645 // location, but as a matter of fact IE uses it, so why not we?
646 static const char *szMimeDbase
= "MIME\\Database\\Content Type\\";
648 wxString strKey
= szMimeDbase
;
651 // suppress possible error messages
655 wxRegKey
key(wxRegKey::HKCR
, strKey
);
657 if ( key
.QueryValue("Extension", ext
) ) {
658 return GetFileTypeFromExtension(ext
);
669 wxFileTypeImpl::GetEntry(const wxFileType::MessageParameters
& params
) const
672 MailCapEntry
*entry
= m_manager
->m_aEntries
[m_index
];
673 while ( entry
!= NULL
) {
674 // notice that an empty command would always succeed (@@ is it ok?)
675 command
= wxFileType::ExpandCommand(entry
->GetTestCmd(), params
);
677 if ( command
.IsEmpty() || (system(command
) == 0) ) {
679 wxLogTrace("Test '%s' for mime type '%s' succeeded.",
680 command
.c_str(), params
.GetMimeType().c_str());
684 wxLogTrace("Test '%s' for mime type '%s' failed.",
685 command
.c_str(), params
.GetMimeType().c_str());
688 entry
= entry
->GetNext();
695 wxFileTypeImpl::GetExpandedCommand(wxString
*expandedCmd
,
696 const wxFileType::MessageParameters
& params
,
699 MailCapEntry
*entry
= GetEntry(params
);
700 if ( entry
== NULL
) {
701 // all tests failed...
705 wxString cmd
= open
? entry
->GetOpenCmd() : entry
->GetPrintCmd();
706 if ( cmd
.IsEmpty() ) {
707 // may happen, especially for "print"
711 *expandedCmd
= wxFileType::ExpandCommand(cmd
, params
);
715 bool wxFileTypeImpl::GetExtensions(wxArrayString
& extensions
)
717 wxString strExtensions
= m_manager
->GetExtension(m_index
);
720 // one extension in the space or comma delimitid list
722 for ( const char *p
= strExtensions
; ; p
++ ) {
723 if ( *p
== ' ' || *p
== ',' || *p
== '\0' ) {
724 if ( !strExt
.IsEmpty() ) {
725 extensions
.Add(strExt
);
728 //else: repeated spaces (shouldn't happen, but it's not that
729 // important if it does happen)
734 else if ( *p
== '.' ) {
735 // remove the dot from extension (but only if it's the first char)
736 if ( !strExt
.IsEmpty() ) {
739 //else: no, don't append it
749 // read system and user mailcaps (TODO implement mime.types support)
750 wxMimeTypesManagerImpl::wxMimeTypesManagerImpl()
752 // directories where we look for mailcap and mime.types by default
753 // (taken from metamail(1) sources)
754 static const char *aStandardLocations
[] =
763 // first read the system wide file(s)
764 for ( size_t n
= 0; n
< WXSIZEOF(aStandardLocations
); n
++ ) {
765 wxString dir
= aStandardLocations
[n
];
767 wxString file
= dir
+ "/mailcap";
768 if ( wxFile::Exists(file
) ) {
772 file
= dir
+ "/mime.types";
773 if ( wxFile::Exists(file
) ) {
778 wxString strHome
= getenv("HOME");
780 // and now the users mailcap
781 wxString strUserMailcap
= strHome
+ "/.mailcap";
782 if ( wxFile::Exists(strUserMailcap
) ) {
783 ReadMailcap(strUserMailcap
);
786 // read the users mime.types
787 wxString strUserMimeTypes
= strHome
+ "/.mime.types";
788 if ( wxFile::Exists(strUserMimeTypes
) ) {
789 ReadMimeTypes(strUserMimeTypes
);
794 wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString
& ext
)
796 wxFAIL_MSG("not implemented (must parse mime.types)");
802 wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString
& mimeType
)
804 // mime types are not case-sensitive
805 wxString
mimetype(mimeType
);
806 mimetype
.MakeLower();
808 // first look for an exact match
809 int index
= m_aTypes
.Index(mimetype
);
810 if ( index
== NOT_FOUND
) {
811 // then try to find "text/*" as match for "text/plain" (for example)
812 // NB: if mimeType doesn't contain '/' at all, Left() will return the
813 // whole string - ok.
814 wxString strCategory
= mimetype
.Left('/');
816 size_t nCount
= m_aTypes
.Count();
817 for ( size_t n
= 0; n
< nCount
; n
++ ) {
818 if ( (m_aTypes
[n
].Before('/') == strCategory
) &&
819 m_aTypes
[n
].Right('/') == "*" ) {
826 if ( index
!= NOT_FOUND
) {
827 wxFileType
*fileType
= new wxFileType
;
828 fileType
->m_impl
->Init(this, index
);
838 void wxMimeTypesManagerImpl::ReadMimeTypes(const wxString
& strFileName
)
840 wxLogTrace("--- Parsing mime.types file '%s' ---", strFileName
.c_str());
842 wxTextFile
file(strFileName
);
846 // the information we extract
847 wxString strMimeType
, strDesc
, strExtensions
;
849 size_t nLineCount
= file
.GetLineCount();
850 for ( size_t nLine
= 0; nLine
< nLineCount
; nLine
++ ) {
851 // now we're at the start of the line
852 const char *pc
= file
[nLine
].c_str();
855 while ( isspace(*pc
) )
862 // detect file format
863 const char *pEqualSign
= strchr(pc
, '=');
864 if ( pEqualSign
== NULL
) {
868 // first field is mime type
869 for ( strMimeType
.Empty(); !isspace(*pc
) && *pc
!= '\0'; pc
++ ) {
874 while ( isspace(*pc
) )
877 // take all the rest of the string
887 // the string on the left of '=' is the field name
888 wxString
strLHS(pc
, pEqualSign
- pc
);
891 for ( pc
= pEqualSign
+ 1; isspace(*pc
); pc
++ )
896 // the string is quoted and ends at the matching quote
897 pEnd
= strchr(++pc
, '"');
898 if ( pEnd
== NULL
) {
899 wxLogWarning(_("Mime.types file %s, line %d: unterminated "
901 strFileName
.c_str(), nLine
+ 1);
905 // unquoted stringends at the first space
906 for ( pEnd
= pc
; !isspace(*pEnd
); pEnd
++ )
910 // now we have the RHS (field value)
911 wxString
strRHS(pc
, pEnd
- pc
);
913 // check that it's more or less what we're waiting for, i.e. that
914 // only '\' is left on the line
915 if ( *pEnd
== '"' ) {
920 for ( pc
= pEnd
; isspace(*pc
); pc
++ )
923 // only '\\' may be left on the line normally
924 bool entryEnded
= *pc
== '\0';
925 if ( !entryEnded
&& ((*pc
!= '\\') || (*++pc
!= '\0')) ) {
926 wxLogWarning(_("Mime.types file %s, line %d: extra characters "
927 "after the field value ignored."),
928 strFileName
.c_str(), nLine
+ 1);
930 // if there is a trailing backslash entryEnded = FALSE
932 // now see what we got
933 if ( strLHS
== "type" ) {
934 strMimeType
= strRHS
;
936 else if ( strLHS
== "desc" ) {
939 else if ( strLHS
== "exts" ) {
940 strExtensions
= strRHS
;
943 wxLogWarning(_("Unknown field in file %s, line %d: '%s'."),
944 strFileName
.c_str(), nLine
+ 1, strLHS
.c_str());
948 // as we don't reset strMimeType, the next line in this entry
949 // will be interpreted correctly.
954 int index
= m_aTypes
.Index(strMimeType
);
955 if ( index
== NOT_FOUND
) {
957 m_aTypes
.Add(strMimeType
);
958 m_aEntries
.Add(NULL
);
959 m_aExtensions
.Add(strExtensions
);
960 m_aDescriptions
.Add(strDesc
);
963 // modify an existing one
964 if ( !strDesc
.IsEmpty() ) {
965 m_aDescriptions
[index
] = strDesc
; // replace old value
967 m_aExtensions
[index
] += strExtensions
;
971 // check our data integriry
972 wxASSERT( m_aTypes
.Count() == m_aEntries
.Count() &&
973 m_aTypes
.Count() == m_aExtensions
.Count() &&
974 m_aTypes
.Count() == m_aDescriptions
.Count() );
977 void wxMimeTypesManagerImpl::ReadMailcap(const wxString
& strFileName
)
979 wxLogTrace("--- Parsing mailcap file '%s' ---", strFileName
.c_str());
981 wxTextFile
file(strFileName
);
985 // see the comments near the end of function for the reason we need this
986 wxArrayInt aEntryIndices
;
988 size_t nLineCount
= file
.GetLineCount();
989 for ( size_t nLine
= 0; nLine
< nLineCount
; nLine
++ ) {
990 // now we're at the start of the line
991 const char *pc
= file
[nLine
].c_str();
994 while ( isspace(*pc
) )
997 // comment or empty string?
998 if ( *pc
== '#' || *pc
== '\0' )
1003 // what field are we currently in? The first 2 are fixed and there may
1004 // be an arbitrary number of other fields -- currently, we are not
1005 // interested in any of them, but we should parse them as well...
1011 } currentToken
= Field_Type
;
1013 // the flags and field values on the current line
1014 bool needsterminal
= false,
1015 copiousoutput
= false;
1021 curField
; // accumulator
1022 for ( bool cont
= TRUE
; cont
; pc
++ ) {
1025 // interpret the next character literally (notice that
1026 // backslash can be used for line continuation)
1027 if ( *++pc
== '\0' ) {
1028 // fetch the next line.
1030 // pc currently points to nowhere, but after the next
1031 // pc++ in the for line it will point to the beginning
1032 // of the next line in the file
1033 pc
= file
[++nLine
].c_str() - 1;
1036 // just a normal character
1042 cont
= FALSE
; // end of line reached, exit the loop
1047 // store this field and start looking for the next one
1049 // trim whitespaces from both sides
1050 curField
.Trim(TRUE
).Trim(FALSE
);
1052 switch ( currentToken
) {
1055 if ( strType
.Find('/') == NOT_FOUND
) {
1056 // we interpret "type" as "type/*"
1060 currentToken
= Field_OpenCmd
;
1064 strOpenCmd
= curField
;
1066 currentToken
= Field_Other
;
1071 // "good" mailcap entry?
1074 // is this something of the form foo=bar?
1075 const char *pEq
= strchr(curField
, '=');
1076 if ( pEq
!= NULL
) {
1077 wxString lhs
= curField
.Left('='),
1078 rhs
= curField
.After('=');
1080 lhs
.Trim(TRUE
); // from right
1081 rhs
.Trim(FALSE
); // from left
1083 if ( lhs
== "print" )
1085 else if ( lhs
== "test" )
1087 else if ( lhs
== "description" ) {
1088 // it might be quoted
1089 if ( rhs
[0u] == '"' &&
1090 rhs
.Last() == '"' ) {
1091 strDesc
= wxString(rhs
.c_str() + 1,
1098 else if ( lhs
== "compose" ||
1099 lhs
== "composetyped" ||
1108 // no, it's a simple flag
1109 // TODO support the flags:
1110 // 1. create an xterm for 'needsterminal'
1111 // 2. append "| $PAGER" for 'copiousoutput'
1112 if ( curField
== "needsterminal" )
1113 needsterminal
= TRUE
;
1114 else if ( curField
== "copiousoutput" )
1115 copiousoutput
= TRUE
;
1116 else if ( curField
== "textualnewlines" )
1124 // don't flood the user with error messages
1125 // if we don't understand something in his
1129 _("Mailcap file %s, line %d: unknown "
1130 "field '%s' for the MIME type "
1132 strFileName
.c_str(),
1140 // it already has this value
1141 //currentToken = Field_Other;
1145 wxFAIL_MSG("unknown field type in mailcap");
1148 // next token starts immediately after ';'
1157 // check that we really read something reasonable
1158 if ( currentToken
== Field_Type
|| currentToken
== Field_OpenCmd
) {
1159 wxLogWarning(_("Mailcap file %s, line %d: incomplete entry "
1161 strFileName
.c_str(), nLine
+ 1);
1164 MailCapEntry
*entry
= new MailCapEntry(strOpenCmd
,
1168 strType
.MakeLower();
1169 int nIndex
= m_aTypes
.Index(strType
);
1170 if ( nIndex
== NOT_FOUND
) {
1172 m_aTypes
.Add(strType
);
1174 m_aEntries
.Add(entry
);
1175 m_aExtensions
.Add("");
1176 m_aDescriptions
.Add(strDesc
);
1179 // modify the existing entry: the entry in one and the same file
1180 // are read in top-to-bottom order, i.e. the entries read first
1181 // should be tried before the entries below. However, the files
1182 // read later should override the settings in the files read
1183 // before, thus we Append() the new entry to the list if it has
1184 // already occured in _this_ file, but Prepend() it if it
1185 // occured in some of the previous ones.
1186 if ( aEntryIndices
.Index(nIndex
) == NOT_FOUND
) {
1187 // first time in this file
1188 aEntryIndices
.Add(nIndex
);
1189 entry
->Prepend(m_aEntries
[nIndex
]);
1190 m_aEntries
[nIndex
] = entry
;
1193 // not the first time in _this_ file
1194 entry
->Append(m_aEntries
[nIndex
]);
1197 if ( !strDesc
.IsEmpty() ) {
1198 // @@ replace the old one - what else can we do??
1199 m_aDescriptions
[nIndex
] = strDesc
;
1204 // check our data integriry
1205 wxASSERT( m_aTypes
.Count() == m_aEntries
.Count() &&
1206 m_aTypes
.Count() == m_aExtensions
.Count() &&
1207 m_aTypes
.Count() == m_aDescriptions
.Count() );
1213 /* vi: set cin tw=80 ts=4 sw=4: */