]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/mimetype.cpp
Right-o, wxConv_local does cause some weird crashes, but things
[wxWidgets.git] / src / common / mimetype.cpp
index 8e068c6f0f7eb3926b1128e6d16fcbce1c61f269..901d85ffb1f4021004df8eab718adda15d468a95 100644 (file)
@@ -124,8 +124,10 @@ public:
     wxFileType *GetFileTypeFromMimeType(const wxString& mimeType);
 
     // this are NOPs under Windows
-    void ReadMailcap(const wxString& filename) { }
-    void ReadMimeTypes(const wxString& filename) { }
+    bool ReadMailcap(const wxString& filename, bool fallback = TRUE)
+        { return TRUE; }
+    bool ReadMimeTypes(const wxString& filename)
+        { return TRUE; }
 };
 
 #else  // Unix
@@ -220,9 +222,27 @@ public:
     // operations
         // prepend this element to the list
     void Prepend(MailCapEntry *next) { m_next = next; }
-        // append to the list
+        // insert into the list at given position
+    void Insert(MailCapEntry *next, size_t pos)
+    {
+        // FIXME slooow...
+        MailCapEntry *cur;
+        size_t n = 0;
+        for ( cur = next; cur != NULL; cur = cur->m_next, n++ ) {
+            if ( n == pos )
+                break;
+        }
+
+        wxASSERT_MSG( n == pos, _T("invalid position in MailCapEntry::Insert") );
+
+        m_next = cur->m_next;
+        cur->m_next = this;
+    }
+        // append this element to the list
     void Append(MailCapEntry *next)
     {
+        wxCHECK_RET( next != NULL, _T("Append()ing to what?") );
+
         // FIXME slooow...
         MailCapEntry *cur;
         for ( cur = next; cur->m_next != NULL; cur = cur->m_next )
@@ -230,9 +250,7 @@ public:
 
         cur->m_next = this;
 
-        // we initialize it in the ctor and there is no reason to both Prepend()
-        // and Append() one and the same entry
-        wxASSERT( m_next == NULL );
+        wxASSERT_MSG( !m_next, _T("Append()ing element already in the list?") );
     }
 
 private:
@@ -252,15 +270,15 @@ friend class wxFileTypeImpl; // give it access to m_aXXX variables
 
 public:
     // ctor loads all info into memory for quicker access later on
-    // @@ it would be nice to load them all, but parse on demand only...
+    // TODO it would be nice to load them all, but parse on demand only...
     wxMimeTypesManagerImpl();
 
     // implement containing class functions
     wxFileType *GetFileTypeFromExtension(const wxString& ext);
     wxFileType *GetFileTypeFromMimeType(const wxString& mimeType);
 
-    void ReadMailcap(const wxString& filename);
-    void ReadMimeTypes(const wxString& filename);
+    bool ReadMailcap(const wxString& filename, bool fallback = FALSE);
+    bool ReadMimeTypes(const wxString& filename);
 
     // accessors
         // get the string containing space separated extensions for the given
@@ -286,7 +304,7 @@ public:
     bool GetMimeType(wxString *mimeType) const
         { *mimeType = m_manager->m_aTypes[m_index]; return TRUE; }
     bool GetIcon(wxIcon * WXUNUSED(icon)) const
-        { return FALSE; }   // @@ maybe with Gnome/KDE integration...
+        { return FALSE; }   // TODO maybe with Gnome/KDE integration...
     bool GetDescription(wxString *desc) const
         { *desc = m_manager->m_aDescriptions[m_index]; return TRUE; }
 
@@ -331,57 +349,57 @@ wxString wxFileType::ExpandCommand(const wxString& command,
     bool hasFilename = FALSE;
 
     wxString str;
-    for ( const char *pc = command.c_str(); *pc != '\0'; pc++ ) {
-        if ( *pc == '%' ) {
+    for ( const wxChar *pc = command.c_str(); *pc != _T('\0'); pc++ ) {
+        if ( *pc == _T('%') ) {
             switch ( *++pc ) {
-                case 's':
+                case _T('s'):
                     // '%s' expands into file name (quoted because it might
                     // contain spaces) - except if there are already quotes
                     // there because otherwise some programs may get confused
                     // by double double quotes
 #if 0
-                    if ( *(pc - 2) == '"' )
+                    if ( *(pc - 2) == _T('"') )
                         str << params.GetFileName();
                     else
-                        str << '"' << params.GetFileName() << '"';
+                        str << _T('"') << params.GetFileName() << _T('"');
 #endif
                     str << params.GetFileName();
                     hasFilename = TRUE;
                     break;
 
-                case 't':
+                case _T('t'):
                     // '%t' expands into MIME type (quote it too just to be
                     // consistent)
-                    str << '\'' << params.GetMimeType() << '\'';
+                    str << _T('\'') << params.GetMimeType() << _T('\'');
                     break;
 
-                case '{':
+                case _T('{'):
                     {
-                        const char *pEnd = strchr(pc, '}');
+                        const wxChar *pEnd = wxStrchr(pc, _T('}'));
                         if ( pEnd == NULL ) {
                             wxString mimetype;
                             wxLogWarning(_("Unmatched '{' in an entry for "
                                            "mime type %s."),
                                          params.GetMimeType().c_str());
-                            str << "%{";
+                            str << _T("%{");
                         }
                         else {
                             wxString param(pc + 1, pEnd - pc - 1);
-                            str << '\'' << params.GetParamValue(param) << '\'';
+                            str << _T('\'') << params.GetParamValue(param) << _T('\'');
                             pc = pEnd;
                         }
                     }
                     break;
 
-                case 'n':
-                case 'F':
+                case _T('n'):
+                case _T('F'):
                     // TODO %n is the number of parts, %F is an array containing
                     //      the names of temp files these parts were written to
                     //      and their mime types.
                     break;
 
                 default:
-                    wxLogDebug("Unknown field %%%c in command '%s'.",
+                    wxLogDebug(_T("Unknown field %%%c in command '%s'."),
                                *pc, command.c_str());
                     str << *pc;
             }
@@ -394,7 +412,7 @@ wxString wxFileType::ExpandCommand(const wxString& command,
     // metamail(1) man page states that if the mailcap entry doesn't have '%s'
     // the program will accept the data on stdin: so give it to it!
     if ( !hasFilename && !str.IsEmpty() ) {
-        str << " < '" << params.GetFileName() << '\'';
+        str << _T(" < '") << params.GetFileName() << _T('\'');
     }
 
     return str;
@@ -451,16 +469,16 @@ wxFileType::GetPrintCommand(wxString *printCmd,
 bool wxMimeTypesManager::IsOfType(const wxString& mimeType,
                                   const wxString& wildcard)
 {
-    wxASSERT_MSG( mimeType.Find('*') == wxNOT_FOUND,
-                  "first MIME type can't contain wildcards" );
+    wxASSERT_MSG( mimeType.Find(_T('*')) == wxNOT_FOUND,
+                  _T("first MIME type can't contain wildcards") );
 
     // all comparaisons are case insensitive (2nd arg of IsSameAs() is FALSE)
-    if ( wildcard.BeforeFirst('/').IsSameAs(mimeType.BeforeFirst('/'), FALSE) )
+    if ( wildcard.BeforeFirst(_T('/')).IsSameAs(mimeType.BeforeFirst(_T('/')), FALSE) )
     {
-        wxString strSubtype = wildcard.AfterFirst('/');
+        wxString strSubtype = wildcard.AfterFirst(_T('/'));
 
-        if ( strSubtype == '*' ||
-             strSubtype.IsSameAs(mimeType.AfterFirst('/'), FALSE) )
+        if ( strSubtype == _T('*') ||
+             strSubtype.IsSameAs(mimeType.AfterFirst(_T('/')), FALSE) )
         {
             // matches (either exactly or it's a wildcard)
             return TRUE;
@@ -492,41 +510,51 @@ wxMimeTypesManager::GetFileTypeFromMimeType(const wxString& mimeType)
     return m_impl->GetFileTypeFromMimeType(mimeType);
 }
 
+bool wxMimeTypesManager::ReadMailcap(const wxString& filename, bool fallback)
+{
+    return m_impl->ReadMailcap(filename, fallback);
+}
+
+bool wxMimeTypesManager::ReadMimeTypes(const wxString& filename)
+{
+    return m_impl->ReadMimeTypes(filename);
+}
+
 // ============================================================================
 // real (OS specific) implementation
 // ============================================================================
 
 #ifdef __WXMSW__
 
-bool wxFileTypeImpl::GetCommand(wxString *command, const char *verb) const
+bool wxFileTypeImpl::GetCommand(wxString *command, const wxChar *verb) const
 {
     // suppress possible error messages
     wxLogNull nolog;
     wxString strKey;
-    strKey << m_strFileType << "\\shell\\" << verb << "\\command";
+    strKey << m_strFileType << _T("\\shell\\") << verb << _T("\\command");
     wxRegKey key(wxRegKey::HKCR, strKey);
 
     if ( key.Open() ) {
         // it's the default value of the key
-        if ( key.QueryValue("", *command) ) {
+        if ( key.QueryValue(_T(""), *command) ) {
             // transform it from '%1' to '%s' style format string
-            // @@ we don't make any attempt to verify that the string is valid,
-            //    i.e. doesn't contain %2, or second %1 or .... But we do make
-            //    sure that we return a string with _exactly_ one '%s'!
+            // NB: we don't make any attempt to verify that the string is valid,
+            //     i.e. doesn't contain %2, or second %1 or .... But we do make
+            //     sure that we return a string with _exactly_ one '%s'!
             size_t len = command->Len();
             for ( size_t n = 0; n < len; n++ ) {
-                if ( command->GetChar(n) == '%' &&
-                     (n + 1 < len) && command->GetChar(n + 1) == '1' ) {
+                if ( command->GetChar(n) == _T('%') &&
+                     (n + 1 < len) && command->GetChar(n + 1) == _T('1') ) {
                     // replace it with '%s'
-                    command->SetChar(n + 1, 's');
+                    command->SetChar(n + 1, _T('s'));
 
                     return TRUE;
                 }
             }
 
             // we didn't find any '%1'!
-            // @@@ hack: append the filename at the end, hope that it will do
-            *command << " %s";
+            // HACK: append the filename at the end, hope that it will do
+            *command << _T(" %s");
 
             return TRUE;
         }
@@ -536,7 +564,7 @@ bool wxFileTypeImpl::GetCommand(wxString *command, const char *verb) const
     return FALSE;
 }
 
-// @@ this function is half implemented
+// TODO this function is half implemented
 bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
 {
     if ( m_ext.IsEmpty() ) {
@@ -557,8 +585,8 @@ bool wxFileTypeImpl::GetMimeType(wxString *mimeType) const
 {
     // suppress possible error messages
     wxLogNull nolog;
-    wxRegKey key(wxRegKey::HKCR, m_strFileType);
-    if ( key.Open() && key.QueryValue("Content Type", *mimeType) ) {
+    wxRegKey key(wxRegKey::HKCR, /*m_strFileType*/ _T(".") + m_ext);
+    if ( key.Open() && key.QueryValue(_T("Content Type"), *mimeType) ) {
         return TRUE;
     }
     else {
@@ -569,7 +597,7 @@ bool wxFileTypeImpl::GetMimeType(wxString *mimeType) const
 bool wxFileTypeImpl::GetIcon(wxIcon *icon) const
 {
     wxString strIconKey;
-    strIconKey << m_strFileType << "\\DefaultIcon";
+    strIconKey << m_strFileType << _T("\\DefaultIcon");
 
     // suppress possible error messages
     wxLogNull nolog;
@@ -578,18 +606,18 @@ bool wxFileTypeImpl::GetIcon(wxIcon *icon) const
     if ( key.Open() ) {
         wxString strIcon;
         // it's the default value of the key
-        if ( key.QueryValue("", strIcon) ) {
+        if ( key.QueryValue(_T(""), strIcon) ) {
             // the format is the following: <full path to file>, <icon index>
             // NB: icon index may be negative as well as positive and the full
             //     path may contain the environment variables inside '%'
-            wxString strFullPath = strIcon.BeforeLast(','),
-            strIndex = strIcon.AfterLast(',');
+            wxString strFullPath = strIcon.BeforeLast(_T(',')),
+            strIndex = strIcon.AfterLast(_T(','));
 
             // index may be omitted, in which case BeforeLast(',') is empty and
             // AfterLast(',') is the whole string
             if ( strFullPath.IsEmpty() ) {
                 strFullPath = strIndex;
-                strIndex = "0";
+                strIndex = _T("0");
             }
 
             wxString strExpPath = wxExpandEnvVars(strFullPath);
@@ -599,7 +627,7 @@ bool wxFileTypeImpl::GetIcon(wxIcon *icon) const
             switch ( (int)hIcon ) {
                 case 0: // means no icons were found
                 case 1: // means no such file or it wasn't a DLL/EXE/OCX/ICO/...
-                    wxLogDebug("incorrect registry entry '%s': no such icon.",
+                    wxLogDebug(_T("incorrect registry entry '%s': no such icon."),
                                key.GetName().c_str());
                     break;
 
@@ -622,7 +650,7 @@ bool wxFileTypeImpl::GetDescription(wxString *desc) const
 
     if ( key.Open() ) {
         // it's the default value of the key
-        if ( key.QueryValue("", *desc) ) {
+        if ( key.QueryValue(_T(""), *desc) ) {
             return TRUE;
         }
     }
@@ -636,8 +664,8 @@ wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& ext)
 {
     // add the leading point if necessary
     wxString str;
-    if ( ext[0u] != '.' ) {
-        str = '.';
+    if ( ext[0u] != _T('.') ) {
+        str = _T('.');
     }
     str << ext;
 
@@ -648,7 +676,7 @@ wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& ext)
     wxRegKey key(wxRegKey::HKCR, str);
     if ( key.Open() ) {
         // it's the default value of the key
-        if ( key.QueryValue("", strFileType) ) {
+        if ( key.QueryValue(_T(""), strFileType) ) {
             // create the new wxFileType object
             wxFileType *fileType = new wxFileType;
             fileType->m_impl->SetFileType(strFileType);
@@ -666,9 +694,9 @@ wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& ext)
 wxFileType *
 wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
 {
-    // @@@ I don't know of any official documentation which mentions this
-    //     location, but as a matter of fact IE uses it, so why not we?
-    static const char *szMimeDbase = "MIME\\Database\\Content Type\\";
+    // HACK I don't know of any official documentation which mentions this
+    //      location, but as a matter of fact IE uses it, so why not we?
+    static const wxChar *szMimeDbase = _T("MIME\\Database\\Content Type\\");
 
     wxString strKey = szMimeDbase;
     strKey << mimeType;
@@ -679,7 +707,7 @@ wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
     wxString ext;
     wxRegKey key(wxRegKey::HKCR, strKey);
     if ( key.Open() ) {
-        if ( key.QueryValue("Extension", ext) ) {
+        if ( key.QueryValue(_T("Extension"), ext) ) {
             return GetFileTypeFromExtension(ext);
         }
     }
@@ -696,17 +724,17 @@ wxFileTypeImpl::GetEntry(const wxFileType::MessageParameters& params) const
     wxString command;
     MailCapEntry *entry = m_manager->m_aEntries[m_index];
     while ( entry != NULL ) {
-        // notice that an empty command would always succeed (@@ is it ok?)
+        // notice that an empty command would always succeed (it's ok)
         command = wxFileType::ExpandCommand(entry->GetTestCmd(), params);
 
-        if ( command.IsEmpty() || (system(command) == 0) ) {
+        if ( command.IsEmpty() || (wxSystem(command) == 0) ) {
             // ok, passed
-            wxLogTrace("Test '%s' for mime type '%s' succeeded.",
+            wxLogTrace(_T("Test '%s' for mime type '%s' succeeded."),
                        command.c_str(), params.GetMimeType().c_str());
             break;
         }
         else {
-            wxLogTrace("Test '%s' for mime type '%s' failed.",
+            wxLogTrace(_T("Test '%s' for mime type '%s' failed."),
                        command.c_str(), params.GetMimeType().c_str());
         }
 
@@ -744,8 +772,8 @@ bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
 
     // one extension in the space or comma delimitid list
     wxString strExt;
-    for ( const char *p = strExtensions; ; p++ ) {
-        if ( *p == ' ' || *p == ',' || *p == '\0' ) {
+    for ( const wxChar *p = strExtensions; ; p++ ) {
+        if ( *p == _T(' ') || *p == _T(',') || *p == _T('\0') ) {
             if ( !strExt.IsEmpty() ) {
                 extensions.Add(strExt);
                 strExt.Empty();
@@ -753,13 +781,13 @@ bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
             //else: repeated spaces (shouldn't happen, but it's not that
             //      important if it does happen)
 
-            if ( *p == '\0' )
+            if ( *p == _T('\0') )
                 break;
         }
-        else if ( *p == '.' ) {
+        else if ( *p == _T('.') ) {
             // remove the dot from extension (but only if it's the first char)
             if ( !strExt.IsEmpty() ) {
-                strExt += '.';
+                strExt += _T('.');
             }
             //else: no, don't append it
         }
@@ -776,40 +804,40 @@ wxMimeTypesManagerImpl::wxMimeTypesManagerImpl()
 {
     // directories where we look for mailcap and mime.types by default
     // (taken from metamail(1) sources)
-    static const char *aStandardLocations[] =
+    static const wxChar *aStandardLocations[] =
     {
-        "/etc",
-        "/usr/etc",
-        "/usr/local/etc",
-        "/etc/mail",
-        "/usr/public/lib"
+        _T("/etc"),
+        _T("/usr/etc"),
+        _T("/usr/local/etc"),
+        _T("/etc/mail"),
+        _T("/usr/public/lib")
     };
 
     // first read the system wide file(s)
     for ( size_t n = 0; n < WXSIZEOF(aStandardLocations); n++ ) {
         wxString dir = aStandardLocations[n];
 
-        wxString file = dir + "/mailcap";
+        wxString file = dir + _T("/mailcap");
         if ( wxFile::Exists(file) ) {
             ReadMailcap(file);
         }
 
-        file = dir + "/mime.types";
+        file = dir + _T("/mime.types");
         if ( wxFile::Exists(file) ) {
             ReadMimeTypes(file);
         }
     }
 
-    wxString strHome = getenv("HOME");
+    wxString strHome = wxGetenv(_T("HOME"));
 
     // and now the users mailcap
-    wxString strUserMailcap = strHome + "/.mailcap";
+    wxString strUserMailcap = strHome + _T("/.mailcap");
     if ( wxFile::Exists(strUserMailcap) ) {
         ReadMailcap(strUserMailcap);
     }
 
     // read the users mime.types
-    wxString strUserMimeTypes = strHome + "/.mime.types";
+    wxString strUserMimeTypes = strHome + _T("/.mime.types");
     if ( wxFile::Exists(strUserMimeTypes) ) {
         ReadMimeTypes(strUserMimeTypes);
     }
@@ -822,8 +850,8 @@ wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& ext)
     for ( size_t n = 0; n < count; n++ ) {
         wxString extensions = m_aExtensions[n];
         while ( !extensions.IsEmpty() ) {
-            wxString field = extensions.BeforeFirst(' ');
-            extensions = extensions.AfterFirst(' ');
+            wxString field = extensions.BeforeFirst(_T(' '));
+            extensions = extensions.AfterFirst(_T(' '));
 
             // consider extensions as not being case-sensitive
             if ( field.IsSameAs(ext, FALSE /* no case */) ) {
@@ -853,12 +881,12 @@ wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
         // then try to find "text/*" as match for "text/plain" (for example)
         // NB: if mimeType doesn't contain '/' at all, BeforeFirst() will return
         //     the whole string - ok.
-        wxString strCategory = mimetype.BeforeFirst('/');
+        wxString strCategory = mimetype.BeforeFirst(_T('/'));
 
         size_t nCount = m_aTypes.Count();
         for ( size_t n = 0; n < nCount; n++ ) {
-            if ( (m_aTypes[n].BeforeFirst('/') == strCategory ) &&
-                 m_aTypes[n].AfterFirst('/') == "*" ) {
+            if ( (m_aTypes[n].BeforeFirst(_T('/')) == strCategory ) &&
+                 m_aTypes[n].AfterFirst(_T('/')) == _T("*") ) {
                     index = n;
                     break;
             }
@@ -877,43 +905,53 @@ wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
     }
 }
 
-void wxMimeTypesManagerImpl::ReadMimeTypes(const wxString& strFileName)
+bool wxMimeTypesManagerImpl::ReadMimeTypes(const wxString& strFileName)
 {
-    wxLogTrace("--- Parsing mime.types file '%s' ---", strFileName.c_str());
+    wxLogTrace(_T("--- Parsing mime.types file '%s' ---"), strFileName.c_str());
 
     wxTextFile file(strFileName);
     if ( !file.Open() )
-        return;
+        return FALSE;
 
     // the information we extract
     wxString strMimeType, strDesc, strExtensions;
 
     size_t nLineCount = file.GetLineCount();
+    const wxChar *pc = NULL;
     for ( size_t nLine = 0; nLine < nLineCount; nLine++ ) {
-        // now we're at the start of the line
-        const char *pc = file[nLine].c_str();
+        if ( pc == NULL ) {
+            // now we're at the start of the line
+            pc = file[nLine].c_str();
+        }
+        else {
+            // we didn't finish with the previous line yet
+            nLine--;
+        }
 
         // skip whitespace
-        while ( isspace(*pc) )
+        while ( wxIsspace(*pc) )
             pc++;
 
         // comment?
-        if ( *pc == '#' )
+        if ( *pc == _T('#') ) {
+            // skip the whole line
+            pc = NULL;
             continue;
+        }
 
         // detect file format
-        const char *pEqualSign = strchr(pc, '=');
+        const wxChar *pEqualSign = wxStrchr(pc, _T('='));
         if ( pEqualSign == NULL ) {
             // brief format
             // ------------
 
             // first field is mime type
-            for ( strMimeType.Empty(); !isspace(*pc) && *pc != '\0'; pc++ ) {
+            for ( strMimeType.Empty(); !wxIsspace(*pc) && *pc != _T('\0'); pc++ ) {
                 strMimeType += *pc;
             }
 
             // skip whitespace
-            while ( isspace(*pc) )
+            while ( wxIsspace(*pc) )
                 pc++;
 
             // take all the rest of the string
@@ -930,13 +968,13 @@ void wxMimeTypesManagerImpl::ReadMimeTypes(const wxString& strFileName)
             wxString strLHS(pc, pEqualSign - pc);
 
             // eat whitespace
-            for ( pc = pEqualSign + 1; isspace(*pc); pc++ )
+            for ( pc = pEqualSign + 1; wxIsspace(*pc); pc++ )
                 ;
 
-            const char *pEnd;
-            if ( *pc == '"' ) {
+            const wxChar *pEnd;
+            if ( *pc == _T('"') ) {
                 // the string is quoted and ends at the matching quote
-                pEnd = strchr(++pc, '"');
+                pEnd = wxStrchr(++pc, _T('"'));
                 if ( pEnd == NULL ) {
                     wxLogWarning(_("Mime.types file %s, line %d: unterminated "
                                    "quoted string."),
@@ -944,41 +982,39 @@ void wxMimeTypesManagerImpl::ReadMimeTypes(const wxString& strFileName)
                 }
             }
             else {
-                // unquoted stringends at the first space
-                for ( pEnd = pc; !isspace(*pEnd); pEnd++ )
+                // unquoted string ends at the first space
+                for ( pEnd = pc; !wxIsspace(*pEnd); pEnd++ )
                     ;
             }
 
             // now we have the RHS (field value)
             wxString strRHS(pc, pEnd - pc);
 
-            // check that it's more or less what we're waiting for, i.e. that
-            // only '\' is left on the line
-            if ( *pEnd == '"' ) {
+            // check what follows this entry
+            if ( *pEnd == _T('"') ) {
                 // skip this quote
                 pEnd++;
             }
 
-            for ( pc = pEnd; isspace(*pc); pc++ )
+            for ( pc = pEnd; wxIsspace(*pc); pc++ )
                 ;
 
-            // only '\\' may be left on the line normally
-            bool entryEnded = *pc == '\0';
-            if ( !entryEnded && ((*pc != '\\') || (*++pc != '\0')) ) {
-                wxLogWarning(_("Mime.types file %s, line %d: extra characters "
-                               "after the field value ignored."),
-                             strFileName.c_str(), nLine + 1);
+            // if there is something left, it may be either a '\\' to continue
+            // the line or the next field of the same entry
+            bool entryEnded = *pc == _T('\0'),
+                 nextFieldOnSameLine = FALSE;
+            if ( !entryEnded ) {
+                nextFieldOnSameLine = ((*pc != _T('\\')) || (pc[1] != _T('\0')));
             }
-            // if there is a trailing backslash entryEnded = FALSE
 
             // now see what we got
-            if ( strLHS == "type" ) {
+            if ( strLHS == _T("type") ) {
                 strMimeType = strRHS;
             }
-            else if ( strLHS == "desc" ) {
+            else if ( strLHS == _T("desc") ) {
                 strDesc = strRHS;
             }
-            else if ( strLHS == "exts" ) {
+            else if ( strLHS == _T("exts") ) {
                 strExtensions = strRHS;
             }
             else {
@@ -987,8 +1023,13 @@ void wxMimeTypesManagerImpl::ReadMimeTypes(const wxString& strFileName)
             }
 
             if ( !entryEnded ) {
-                // as we don't reset strMimeType, the next line in this entry
+                if ( !nextFieldOnSameLine )
+                    pc = NULL;
+                //else: don't reset it
+
+                // as we don't reset strMimeType, the next field in this entry
                 // will be interpreted correctly.
+
                 continue;
             }
         }
@@ -996,10 +1037,10 @@ void wxMimeTypesManagerImpl::ReadMimeTypes(const wxString& strFileName)
         // although it doesn't seem to be covered by RFCs, some programs
         // (notably Netscape) create their entries with several comma
         // separated extensions (RFC mention the spaces only)
-        strExtensions.Replace(",", " ");
+        strExtensions.Replace(_T(","), _T(" "));
 
         // also deal with the leading dot
-        if ( !strExtensions.IsEmpty() && strExtensions[0] == '.' ) {
+        if ( !strExtensions.IsEmpty() && strExtensions[0] == _T('.') ) {
             strExtensions.erase(0, 1);
         }
 
@@ -1018,36 +1059,47 @@ void wxMimeTypesManagerImpl::ReadMimeTypes(const wxString& strFileName)
             }
             m_aExtensions[index] += strExtensions;
         }
+
+        // finished with this line
+        pc = NULL;
     }
 
     // check our data integriry
     wxASSERT( m_aTypes.Count() == m_aEntries.Count() &&
               m_aTypes.Count() == m_aExtensions.Count() &&
               m_aTypes.Count() == m_aDescriptions.Count() );
+
+    return TRUE;
 }
 
-void wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName)
+bool wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName,
+                                         bool fallback)
 {
-    wxLogTrace("--- Parsing mailcap file '%s' ---", strFileName.c_str());
+    wxLogTrace(_T("--- Parsing mailcap file '%s' ---"), strFileName.c_str());
 
     wxTextFile file(strFileName);
     if ( !file.Open() )
-        return;
+        return FALSE;
 
-    // see the comments near the end of function for the reason we need this
+    // see the comments near the end of function for the reason we need these
+    // variables (search for the next occurence of them)
+        // indices of MIME types (in m_aTypes) we already found in this file
     wxArrayInt aEntryIndices;
+        // aLastIndices[n] is the index of last element in
+        // m_aEntries[aEntryIndices[n]] from this file
+    wxArrayInt aLastIndices;
 
     size_t nLineCount = file.GetLineCount();
     for ( size_t nLine = 0; nLine < nLineCount; nLine++ ) {
         // now we're at the start of the line
-        const char *pc = file[nLine].c_str();
+        const wxChar *pc = file[nLine].c_str();
 
         // skip whitespace
-        while ( isspace(*pc) )
+        while ( wxIsspace(*pc) )
             pc++;
 
         // comment or empty string?
-        if ( *pc == '#' || *pc == '\0' )
+        if ( *pc == _T('#') || *pc == _T('\0') )
             continue;
 
         // no, do parse
@@ -1073,10 +1125,10 @@ void wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName)
                  curField; // accumulator
         for ( bool cont = TRUE; cont; pc++ ) {
             switch ( *pc ) {
-                case '\\':
+                case _T('\\'):
                     // interpret the next character literally (notice that
                     // backslash can be used for line continuation)
-                    if ( *++pc == '\0' ) {
+                    if ( *++pc == _T('\0') ) {
                         // fetch the next line.
 
                         // pc currently points to nowhere, but after the next
@@ -1090,12 +1142,12 @@ void wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName)
                     }
                     break;
 
-                case '\0':
+                case _T('\0'):
                     cont = FALSE;   // end of line reached, exit the loop
 
                     // fall through
 
-                case ';':
+                case _T(';'):
                     // store this field and start looking for the next one
 
                     // trim whitespaces from both sides
@@ -1104,9 +1156,9 @@ void wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName)
                     switch ( currentToken ) {
                         case Field_Type:
                             strType = curField;
-                            if ( strType.Find('/') == wxNOT_FOUND ) {
+                            if ( strType.Find(_T('/')) == wxNOT_FOUND ) {
                                 // we interpret "type" as "type/*"
-                                strType += "/*";
+                                strType += _T("/*");
                             }
 
                             currentToken = Field_OpenCmd;
@@ -1124,22 +1176,22 @@ void wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName)
                                 bool ok = TRUE;
 
                                 // is this something of the form foo=bar?
-                                const char *pEq = strchr(curField, '=');
+                                const wxChar *pEq = wxStrchr(curField, _T('='));
                                 if ( pEq != NULL ) {
-                                    wxString lhs = curField.BeforeFirst('='),
-                                             rhs = curField.AfterFirst('=');
+                                    wxString lhs = curField.BeforeFirst(_T('=')),
+                                             rhs = curField.AfterFirst(_T('='));
 
                                     lhs.Trim(TRUE);     // from right
                                     rhs.Trim(FALSE);    // from left
 
-                                    if ( lhs == "print" )
+                                    if ( lhs == _T("print") )
                                         strPrintCmd = rhs;
-                                    else if ( lhs == "test" )
+                                    else if ( lhs == _T("test") )
                                         strTest = rhs;
-                                    else if ( lhs == "description" ) {
+                                    else if ( lhs == _T("description") ) {
                                         // it might be quoted
-                                        if ( rhs[0u] == '"' &&
-                                             rhs.Last() == '"' ) {
+                                        if ( rhs[0u] == _T('"') &&
+                                             rhs.Last() == _T('"') ) {
                                             strDesc = wxString(rhs.c_str() + 1,
                                                                rhs.Len() - 2);
                                         }
@@ -1147,10 +1199,10 @@ void wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName)
                                             strDesc = rhs;
                                         }
                                     }
-                                    else if ( lhs == "compose" ||
-                                              lhs == "composetyped" ||
-                                              lhs == "notes" ||
-                                              lhs == "edit" )
+                                    else if ( lhs == _T("compose") ||
+                                              lhs == _T("composetyped") ||
+                                              lhs == _T("notes") ||
+                                              lhs == _T("edit") )
                                         ;   // ignore
                                     else
                                         ok = FALSE;
@@ -1161,11 +1213,11 @@ void wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName)
                                     // TODO support the flags:
                                     //  1. create an xterm for 'needsterminal'
                                     //  2. append "| $PAGER" for 'copiousoutput'
-                                    if ( curField == "needsterminal" )
+                                    if ( curField == _T("needsterminal") )
                                         needsterminal = TRUE;
-                                    else if ( curField == "copiousoutput" )
+                                    else if ( curField == _T("copiousoutput") )
                                         copiousoutput = TRUE;
-                                    else if ( curField == "textualnewlines" )
+                                    else if ( curField == _T("textualnewlines") )
                                         ;   // ignore
                                     else
                                         ok = FALSE;
@@ -1180,9 +1232,9 @@ void wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName)
                                     // programmer
                                     wxLogDebug
                                     (
-                                      "Mailcap file %s, line %d: unknown "
+                                      _T("Mailcap file %s, line %d: unknown "
                                       "field '%s' for the MIME type "
-                                      "'%s' ignored.",
+                                      "'%s' ignored."),
                                       strFileName.c_str(),
                                       nLine + 1,
                                       curField.c_str(),
@@ -1196,7 +1248,7 @@ void wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName)
                             break;
 
                         default:
-                            wxFAIL_MSG("unknown field type in mailcap");
+                            wxFAIL_MSG(_T("unknown field type in mailcap"));
                     }
 
                     // next token starts immediately after ';'
@@ -1226,30 +1278,55 @@ void wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName)
                 m_aTypes.Add(strType);
 
                 m_aEntries.Add(entry);
-                m_aExtensions.Add("");
+                m_aExtensions.Add(_T(""));
                 m_aDescriptions.Add(strDesc);
             }
             else {
-                // modify the existing entry: the entry in one and the same file
-                // are read in top-to-bottom order, i.e. the entries read first
-                // should be tried before the entries below. However, the files
-                // read later should override the settings in the files read
-                // before, thus we Append() the new entry to the list if it has
-                // already occured in _this_ file, but Prepend() it if it
-                // occured in some of the previous ones.
-                if ( aEntryIndices.Index(nIndex) == wxNOT_FOUND ) {
-                    // first time in this file
-                    aEntryIndices.Add(nIndex);
-                    entry->Prepend(m_aEntries[nIndex]);
-                    m_aEntries[nIndex] = entry;
+                // modify the existing entry: the entries in one and the same
+                // file are read in top-to-bottom order, i.e. the entries read
+                // first should be tried before the entries below. However,
+                // the files read later should override the settings in the
+                // files read before (except if fallback is TRUE), thus we
+                // Insert() the new entry to the list if it has already
+                // occured in _this_ file, but Prepend() it if it occured in
+                // some of the previous ones and Append() to it in the
+                // fallback case
+
+                if ( fallback ) {
+                    // 'fallback' parameter prevents the entries from this
+                    // file from overriding the other ones - always append
+                    MailCapEntry *entryOld = m_aEntries[nIndex];
+                    if ( entryOld )
+                        entry->Append(entryOld);
+                    else
+                        m_aEntries[nIndex] = entry;
                 }
                 else {
-                    // not the first time in _this_ file
-                    entry->Append(m_aEntries[nIndex]);
+                    int entryIndex = aEntryIndices.Index(nIndex);
+                    if ( entryIndex == wxNOT_FOUND ) {
+                        // first time in this file
+                        aEntryIndices.Add(nIndex);
+                        aLastIndices.Add(0);
+
+                        entry->Prepend(m_aEntries[nIndex]);
+                        m_aEntries[nIndex] = entry;
+                    }
+                    else {
+                        // not the first time in _this_ file
+                        size_t nEntryIndex = (size_t)entryIndex;
+                        MailCapEntry *entryOld = m_aEntries[nIndex];
+                        if ( entryOld )
+                            entry->Insert(entryOld, aLastIndices[nEntryIndex]);
+                        else
+                            m_aEntries[nIndex] = entry;
+
+                        // the indices were shifted by 1
+                        aLastIndices[nEntryIndex]++;
+                    }
                 }
 
                 if ( !strDesc.IsEmpty() ) {
-                    // @@ replace the old one - what else can we do??
+                    // replace the old one - what else can we do??
                     m_aDescriptions[nIndex] = strDesc;
                 }
             }
@@ -1260,6 +1337,8 @@ void wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName)
                   m_aTypes.Count() == m_aExtensions.Count() &&
                   m_aTypes.Count() == m_aDescriptions.Count() );
     }
+
+    return TRUE;
 }
 
 #endif // OS type