From b13d92d1c8840feca54b00e15ad558d77bda78df Mon Sep 17 00:00:00 2001
From: Vadim Zeitlin <vadim@wxwidgets.org>
Date: Tue, 5 Jan 1999 22:40:50 +0000
Subject: [PATCH] MIME classes with docs (not yet added to the makefiles)

git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@1321 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
---
 docs/latex/wx/classes.tex  |    4 +-
 docs/latex/wx/filetype.tex |  194 ++++++
 docs/latex/wx/mimetype.tex |  111 ++++
 include/wx/mimetype.h      |  145 +++++
 src/common/mimetype.cpp    | 1211 ++++++++++++++++++++++++++++++++++++
 5 files changed, 1664 insertions(+), 1 deletion(-)
 create mode 100644 docs/latex/wx/filetype.tex
 create mode 100644 docs/latex/wx/mimetype.tex
 create mode 100644 include/wx/mimetype.h
 create mode 100644 src/common/mimetype.cpp

diff --git a/docs/latex/wx/classes.tex b/docs/latex/wx/classes.tex
index 19ecd75429..e7eded49ff 100644
--- a/docs/latex/wx/classes.tex
+++ b/docs/latex/wx/classes.tex
@@ -81,6 +81,7 @@ $$\image{14cm;0cm}{wxclass.ps}$$
 \input file.tex
 \input filedlg.tex
 \input filehist.tex
+\input filetype.tex
 \input focusevt.tex
 \input font.tex
 \input fontdlg.tex
@@ -113,13 +114,14 @@ $$\image{14cm;0cm}{wxclass.ps}$$
 \input menuitem.tex
 \input menuevt.tex
 \input memorydc.tex
-\input msgdlg.tex
 \input metafile.tex
+\input mimetype.tex
 \input minifram.tex
 \input module.tex
 \input mouseevt.tex
 \input moveevt.tex
 \input mltchdlg.tex
+\input msgdlg.tex
 \input mutex.tex
 \input mutexlck.tex
 \input node.tex
diff --git a/docs/latex/wx/filetype.tex b/docs/latex/wx/filetype.tex
new file mode 100644
index 0000000000..3b56bcdb6b
--- /dev/null
+++ b/docs/latex/wx/filetype.tex
@@ -0,0 +1,194 @@
+\section{\class{wxFileType}}\label{wxfiletype}
+
+This class holds information about a given "file type". File type is the same as
+MIME type under Unix, but under Windows it corresponds more to an extension than
+to MIME type (in fact, several extensions may correspond to a file type). This
+object may be created in several different ways: the program might know the file
+extension and wish to find out the corresponding MIME type or, conversely, it
+might want to find the right extension for the file to which it writes the
+contents of given MIME type. Depending on how it was created some fields may be
+unknown so the return value of all the accessors {\bf must} be checked: FALSE
+will be returned if the corresponding information couldn't be found.
+
+The objects of this class are never created by the application code but are
+returned by \helpref{wxMimeTypesManager::GetFileTypeFromMimeType}{wxmimetypesmanagergetfiletypefrommimetype} and 
+\helpref{wxMimeTypesManager::GetFileTypeFromExtension}{wxmimetypesmanagergetfiletypefromextension} methods.
+But it's your responsability to delete the returned pointer when you're done
+with it!
+
+% TODO describe MIME types better than this...
+A brief remainder about what the MIME types are (see the RFC 1341 for more
+information): basicly, it is just a pair category/type (for example,
+"text/plain") where the category is a basic indication of what a file is
+(examples of categories are "application", "image", "text", "binary"...) and
+type is a precise definition of the document format: "plain" in the example
+above means just ASCII text without any formatting, while "text/html" is the
+HTML document source.
+
+A MIME type may have one or more associated extensions: "text/plain" will
+typically correspond to the extension ".txt", but may as well be associated with
+".ini" or ".conf".
+
+\wxheading{Required headers}
+
+#include <wx/mimetype.h>
+
+\wxheading{Derived from}
+
+No base class.
+
+\wxheading{See also}
+
+\helpref{wxMimeTypesManager}{wxmimetypesmanager}
+
+\latexignore{\rtfignore{\wxheading{Members}}}
+
+\membersection{MessageParameters class}{wxfiletypemessageparameters}
+
+One of the most common usages of MIME is to encode an e-mail message. The MIME
+type of the encoded message is an example of a {\it message parameter}. These
+parameters are found in the message headers ("Content-XXX"). At the very least,
+they must specify the MIME type and the version of MIME used, but almost always
+they provide additional information about the message such as the original file
+name or the charset (for the text documents).
+
+These parameters may be useful to the program used to open, edit, view or print
+the message, so, for example, an e-mail client program will have to pass them to
+this program. Because wxFileType itself can not know about these parameters,
+it uses MessageParameters class to query them. The default implementation only
+requiers the caller to provide the file name (always used by the program to be
+called - it must know which file to open) and the MIME type and supposes that
+there are no other parameters. If you wish to supply additional parameters, you
+must derive your own class from MessageParameters and override GetParamValue()
+function, for example:
+
+\begin{verbatim}
+// provide the message parameters for the MIME type manager
+class MailMessageParamaters : public wxFileType::MessageParameters
+{
+public:
+   MailMessageParamaters(const wxString& filename,
+                         const wxString& mimetype)
+      : wxFileType::MessageParameters(filename, mimetype)
+   {
+   }
+
+   virtual wxString GetParamValue(const wxString& name) const
+   {
+       // parameter names are not case-sensitive
+       if ( name.CmpNoCase("charset") == 0 )
+           return "US-ASCII";
+       else
+           return wxFileType::MessageParameters::GetParamValue(name);
+   }
+};
+\end{verbatim}
+
+Now you only need to create an object of this class and pass it to, for example,
+\helpref{GetOpenCommand}{wxfiletypegetopencommand} like this:
+
+\begin{verbatim}
+wxString command;
+if ( filetype->GetOpenCommand(&command,
+                              MailMessageParamaters("foo.txt", "text/plain")) )
+{
+    // the full command for opening the text documents is in 'command'
+    // (it might be "notepad foo.txt" under Windows or "cat foo.txt" under Unix)
+}
+else
+{
+    // we don't know how to handle such files...
+}
+
+\end{verbatim}
+
+{\bf Windows:} As only the file name is used by the program associated with the
+given extension anyhow (but no other message parameters), there is no need to
+ever derive from MessageParameters class for a Windows-only program.
+
+\membersection{wxFileType::wxFileType}\label{wxfiletypewxfiletype}
+\func{}{wxFileType}{\void}
+
+The default constructor is private because you should never create objects of
+this type: they are only returned by 
+\helpref{wxMimeTypesManager}{wxmimetypesmanager} methods.
+
+\membersection{wxFileType::\destruct{wxFileType}}\label{wxfiletypedtor}
+\func{}{\destruct{wxFileType}{\void}
+
+The destructor of this class is not virtual, so it should not be derived from.
+
+\membersection{wxFileType::GetMimeType}\label{wxfiletypegetmimetype}
+\func{bool}{GetMimeType}{\param{wxString *}{mimeType}}
+
+If the function returns TRUE, the string pointed to by {\it mimeType} is filled
+with full MIME type specification for this file type: for example, "text/plain".
+
+\membersection{wxFileType::GetExtensions}\label{wxfiletypegetextensions}
+\func{bool}{GetExtensions}{\param{wxArrayString \&}{extensions}}
+
+If the function returns TRUE, the array {\it extensions} is filled
+with all extensions associated with this file type: for example, it may
+contain the following two elements for the MIME type "text/html" (notice the
+absence of the leading dot): "html" and "htm".
+
+{\bf Windows:} This function is currently not implemented: there is no
+(efficient) way to retrieve associated extensions from the given MIME type on
+this platform, so it will only return TRUE if the wxFileType object was created
+by \helpref{GetFileTypeFromExtension}{wxmimetypesmanagergetfiletypefromextension} 
+function in the first place.
+
+\membersection{wxFileType::GetIcon}\label{wxfiletypegeticon}
+\func{bool}{GetIcon}{\param{wxIcon *}{icon}}
+
+If the function returns TRUE, the icon associated with this file type will be
+created and assigned to the {\it icon} parameter.
+
+{\bf Unix:} This function always returns FALSE under Unix.
+
+\membersection{wxFileType::GetDescription}\label{wxfiletypegetdescription}
+\func{bool}{GetDescription}{\param{wxString *}{desc}}
+
+If the function returns TRUE, the string pointed to by {\it desc} is filled
+with a brief description for this file type: for example, "text document" for
+the "text/plain" MIME type.
+
+\membersection{wxFileType::GetOpenCommand}\label{wxfiletypegetopencommand}
+\func{bool}{GetOpenCommand}{\param{wxString *}{command},\param{MessageParameters \&}{params}}
+
+If the function returns TRUE, the string pointed to by {\it command} is filled
+with the command which must be executed (see \helpref{wxExecute}{wxexecute}) in
+order to open the file of the given type. The name of the file is
+retrieved from \helpref{MessageParameters}{wxfiletypemessageparameters} class.
+
+\membersection{wxFileType::GetPrintCommand}\label{wxfiletypegetprintcommand}
+\func{bool}{GetPrintCommand}{\param{wxString *}{command},\param{MessageParameters \&}{params}}
+
+If the function returns TRUE, the string pointed to by {\it command} is filled
+with the command which must be executed (see \helpref{wxExecute}{wxexecute}) in
+order to print the file of the given type. The name of the file is
+retrieved from \helpref{MessageParameters}{wxfiletypemessageparameters} class.
+
+\membersection{wxFileType::ExpandCommand}\label{wxfiletypeexpandcommand}
+\func{static wxString}{ExpandCommand}{\param{const wxString \&}{command},\param{MessageParameters \&}{params}}
+
+This function is primarly intended for GetOpenCommand and GetPrintCommand
+usage but may be also used by the application directly if, for example, you want
+to use some non default command to open the file.
+
+The function replaces all occurences of
+\twocolwidtha{7cm}
+\begin{twocollist}\itemsep=0pt
+\twocolitem{format specificator}{with}
+\twocolitem{\%s}{the full file name}
+\twocolitem{\%t}{the MIME type}
+\twocolitem{\%\{param\}}{the value of the parameter {\it param}}
+\end{twocollist}
+using the MessageParameters object you pass to it.
+
+If there is no '\%s' in the command string (and the string is not empty), it is
+assumed that the command reads the data on stdin and so the effect is the same
+as "< \%s" were appended to the string.
+
+Unlike all other functions of this class, there is no error return for this
+function.
diff --git a/docs/latex/wx/mimetype.tex b/docs/latex/wx/mimetype.tex
new file mode 100644
index 0000000000..263e124619
--- /dev/null
+++ b/docs/latex/wx/mimetype.tex
@@ -0,0 +1,111 @@
+\section{\class{wxMimeTypesManager}}\label{wxmimetypesmanager}
+
+This class allows the application to retrieve the information about all known
+MIME types from a system-specific location and the filename extensions to the
+MIME types and vice versa. After initialization the functions
+\helpref{wxMimeTypesManager::GetFileTypeFromMimeType}{wxmimetypesmanagergetfiletypefrommimetype} 
+and \helpref{wxMimeTypesManager::GetFileTypeFromExtension}{wxmimetypesmanagergetfiletypefromextension} 
+may be called: they will return a \helpref{wxFileType}{wxfiletype} object which
+may be further queried for file description, icon and other attributes.
+
+{\bf Windows:} MIME type information is stored in the registry and no additional
+initialization is needed.
+
+{\bf Unix:} MIME type information is stored in the files mailcap and mime.types
+(system-wide) and .mailcap and .mime.types in the current user's home directory:
+all of these files are searched for and loaded if found by default. However,
+additional functions 
+\helpref{wxMimeTypesManager::ReadMailcap}{wxmimetypesmanagerreadmailcap} and
+\helpref{wxMimeTypesManager::ReadMimeTypes}{wxmimetypesmanagerreadmimetypes} are
+provided to load additional files.
+
+NB: Currently, wxMimeTypesManager is limited to reading MIME type information
+but it will support modifying it as well in the future versions.
+
+\wxheading{Required headers}
+
+#include <wx/mimetype.h>
+
+\wxheading{Derived from}
+
+No base class.
+
+\wxheading{See also}
+
+\helpref{wxFileType}{wxfiletype}
+
+\latexignore{\rtfignore{\wxheading{Function groups}}}
+
+\membersection{Constructor and destructor}
+
+NB: You won't normally need to use more than one wxMimeTypesManager object in a
+program.
+
+\helpref{wxMimeTypesManager}{wxmimetypesmanagerctor}\\
+\helpref{\destruct{wxMimeTypesManager}}{wxmimetypesmanagerdtor}
+
+\membersection{Query database}
+
+These functions are the heart of this class: they allow to find a 
+\helpref{file type}{wxfiletype} object from either file extension or MIME type.
+If the function is successful, it returns a pointer to the wxFileType object
+which {\bf must be deleted by the caller}, otherwise NULL will be returned.
+
+\helpref{GetFileTypeFromMimeType}{wxmimetypesmanagergetfiletypefrommimetype}\\
+\helpref{GetFileTypeFromExtension}{wxmimetypesmanagergetfiletypefromextension}
+
+\membersection{Initialization functions}
+
+{\bf Unix:} These functions may be used to load additional (except for the
+default ones which are loaded automatically) files containing MIME
+information in either mailcap(5) or mime.types(5) format.
+
+\helpref{ReadMailcap}{wxmimetypesmanagerreadmailcap}\\
+\helpref{ReadMimeTypes}{wxmimetypesmanagerreadmimetypes}
+
+%%%%% MEMBERS HERE %%%%%
+\helponly{\insertatlevel{2}{
+
+\wxheading{Members}
+
+}}
+
+\membersection{wxMimeTypesManager::wxMimeTypesManager}\label{wxmimetypesmanagerctor}
+\func{}{wxMimeTypesManager}{\void}
+
+Constructor puts the object in the "working" state, no additional initialization
+are needed - but \helpref{ReadXXX}{wxmimetypesmanagerinit} may be used to load
+additional mailcap/mime.types files.
+
+\membersection{wxMimeTypesManager::\destruct{wxMimeTypesManager}}\label{wxmimetypesmanagerdtor}
+\func{}{\destruct{wxMimeTypesManager}{\void}
+
+Destructor is not virtual, so this class should not be derived from.
+
+\membersection{wxMimeTypesManager::GetFileTypeFromExtension}\label{wxmimetypesmanagergetfiletypefromextension}
+\func{wxFileType *}{GetFileTypeFromExtension}{\param{const wxString \&}{extension}}
+
+Gather information about the files with given extension and return the
+corresponding \helpref{wxFileType}{wxfiletype} object or NULL if the extension
+is unknown.
+
+\membersection{wxMimeTypesManager::GetFileTypeFromMimeType}\label{wxmimetypesmanagergetfiletypefrommimetype}
+\func{wxFileType *}{GetFileTypeFromMimeType}{\param{const wxString \&}{mimeType}}
+
+Gather information about the files with given MIME type and return the
+corresponding \helpref{wxFileType}{wxfiletype} object or NULL if the MIME type
+is unknown.
+
+\membersection{wxMimeTypesManager::ReadMailcap}\label{wxmimetypesmanagerreadmailcap}
+\func{\void}{ReadMailcap}{\param{const wxString \&}{filename}}
+
+Load additional file containing information about MIME types and associated
+information in mailcap format. See metamail(1) and mailcap(5) for more
+information.
+
+\membersection{wxMimeTypesManager::ReadMimeTypes}\label{wxmimetypesmanagerreadmimetypes}
+\func{\void}{ReadMimeTypes}{\param{const wxString \&}{filename}}
+
+Load additional file containing information about MIME types and associated
+information in mime.types file format. See metamail(1) and mailcap(5) for more
+information.
diff --git a/include/wx/mimetype.h b/include/wx/mimetype.h
new file mode 100644
index 0000000000..d79caf78af
--- /dev/null
+++ b/include/wx/mimetype.h
@@ -0,0 +1,145 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        wx/mimetype.h
+// Purpose:     classes and functions to manage MIME types
+// Author:      Vadim Zeitlin
+// Modified by:
+// Created:     23.09.98
+// RCS-ID:      $Id$
+// Copyright:   (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
+// Licence:     wxWindows license (part of wxExtra library)
+/////////////////////////////////////////////////////////////////////////////
+
+#ifndef   _MIMETYPE_H
+#define   _MIMETYPE_H
+
+// fwd decls
+class wxIcon;
+class wxFileTypeImpl;
+class wxMimeTypesManagerImpl;
+
+// the things we really need
+#include "wx/string.h"
+
+// This class holds information about a given "file type". File type is the
+// same as MIME type under Unix, but under Windows it corresponds more to an
+// extension than to MIME type (in fact, several extensions may correspond to a
+// file type). This object may be created in many different ways and depending
+// on how it was created some fields may be unknown so the return value of all
+// the accessors *must* be checked!
+class wxFileType
+{
+friend wxMimeTypesManagerImpl;  // it has access to m_impl
+
+public:
+    // An object of this class must be passed to Get{Open|Print}Command. The
+    // default implementation is trivial and doesn't know anything at all about
+    // parameters, only filename and MIME type are used (so it's probably ok for
+    // Windows where %{param} is not used anyhow)
+    class MessageParameters
+    {
+    public:
+        // ctors
+        MessageParameters() { }
+        MessageParameters(const wxString& filename, const wxString& mimetype)
+            : m_filename(filename), m_mimetype(mimetype) { }
+
+        // accessors (called by GetOpenCommand)
+            // filename
+        const wxString& GetFileName() const { return m_filename; }
+            // mime type
+        const wxString& GetMimeType() const { return m_mimetype; }
+
+        // override this function in derived class
+        virtual wxString GetParamValue(const wxString& paramName) const
+            { return ""; }
+
+        // virtual dtor as in any base class
+        virtual ~MessageParameters() { }
+
+    protected:
+        wxString m_filename, m_mimetype;
+    };
+
+    // accessors: all of them return true if the corresponding information
+    // could be retrieved/found, false otherwise (and in this case all [out]
+    // parameters are unchanged)
+        // return the MIME type for this file type
+    bool GetMimeType(wxString *mimeType) const;
+        // fill passed in array with all extensions associated with this file
+        // type
+    bool GetExtensions(wxArrayString& extensions);
+        // get the icon corresponding to this file type
+    bool GetIcon(wxIcon *icon) const;
+        // get a brief file type description ("*.txt" => "text document")
+    bool GetDescription(wxString *desc) const;
+
+    // get the command to be used to open/print the given file.
+        // get the command to execute the file of given type
+    bool GetOpenCommand(wxString *openCmd,
+                        const MessageParameters& params) const;
+        // get the command to print the file of given type
+    bool GetPrintCommand(wxString *printCmd,
+                         const MessageParameters& params) const;
+
+    // operations
+        // expand a string in the format of GetOpenCommand (which may contain
+        // '%s' and '%t' format specificators for the file name and mime type
+        // and %{param} constructions).
+    static wxString ExpandCommand(const wxString& command,
+                                  const MessageParameters& params);
+
+    // dtor (not virtual, shouldn't be derived from)
+    ~wxFileType();
+
+private:
+    // default ctor is private because the user code never creates us
+    wxFileType();
+
+    // no copy ctor/assignment operator
+    wxFileType(const wxFileType&);
+    wxFileType& operator=(const wxFileType&);
+
+    wxFileTypeImpl *m_impl;
+};
+
+// This class accesses the information about all known MIME types and allows
+// the application to retrieve information (including how to handle data of
+// given type) about them.
+//
+// NB: currently it doesn't support modifying MIME database (read-only access).
+class wxMimeTypesManager
+{
+public:
+    // ctor
+    wxMimeTypesManager();
+
+    // Database lookup: all functions return a pointer to wxFileType object
+    // whose methods may be used to query it for the information you're
+    // interested in. If the return value is !NULL, caller is responsible for
+    // deleting it.
+        // get file type from file extension
+    wxFileType *GetFileTypeFromExtension(const wxString& ext);
+        // get file type from MIME type (in format <category>/<format>)
+    wxFileType *GetFileTypeFromMimeType(const wxString& mimeType);
+
+    // other operations
+        // read in additional file (the standard ones are read automatically)
+        // in mailcap format (see mimetype.cpp for description)
+    void ReadMailcap(const wxString& filename);
+        // read in additional file in mime.types format
+    void ReadMimeTypes(const wxString& filename);
+
+    // dtor (not virtual, shouldn't be derived from)
+    ~wxMimeTypesManager();
+
+private:
+    // no copy ctor/assignment operator
+    wxMimeTypesManager(const wxMimeTypesManager&);
+    wxMimeTypesManager& operator=(const wxMimeTypesManager&);
+
+    wxMimeTypesManagerImpl *m_impl;
+};
+
+#endif  //_MIMETYPE_H
+
+/* vi: set cin tw=80 ts=4 sw=4: */
diff --git a/src/common/mimetype.cpp b/src/common/mimetype.cpp
new file mode 100644
index 0000000000..61310e14de
--- /dev/null
+++ b/src/common/mimetype.cpp
@@ -0,0 +1,1211 @@
+/////////////////////////////////////////////////////////////////////////////
+// Name:        common/mimetype.cpp
+// Purpose:     classes and functions to manage MIME types
+// Author:      Vadim Zeitlin
+// Modified by:
+// Created:     23.09.98
+// RCS-ID:      $Id$
+// Copyright:   (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
+// Licence:     wxWindows license (part of wxExtra library)
+/////////////////////////////////////////////////////////////////////////////
+
+#ifdef    __GNUG__
+    #pragma implementation "mimetype.h"
+#endif
+
+// ============================================================================
+// declarations
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// headers
+// ----------------------------------------------------------------------------
+
+// for compilers that support precompilation, includes "wx.h".
+#include "wx/wxprec.h"
+
+#ifdef __BORLANDC__
+    #pragma hdrstop
+#endif
+
+// wxWindows
+#ifndef WX_PRECOMP
+    #include  "wx/string.h"
+#endif //WX_PRECOMP
+
+#include "wx/log.h"
+#include "wx/intl.h"
+#include "wx/dynarray.h"
+
+#ifdef __WXMSW__
+    #include "wx/msw/registry.h"
+#else  // Unix
+    #include "wx/textfile.h"
+#endif // OS
+
+#include "wx/mimetype.h"
+
+// other standard headers
+#include <ctype.h>
+
+// ----------------------------------------------------------------------------
+// private classes
+// ----------------------------------------------------------------------------
+
+// implementation classes, platform dependent
+#ifdef __WXMSW__
+
+// These classes use Windows registry to retrieve the required information.
+//
+// Keys used (not all of them are documented, so it might actually stop working
+// in futur versions of Windows...):
+//  1. "HKCR\MIME\Database\Content Type" contains subkeys for all known MIME
+//     types, each key has a string value "Extension" which gives (dot preceded)
+//     extension for the files of this MIME type.
+//
+//  2. "HKCR\.ext" contains
+//   a) unnamed value containing the "filetype"
+//   b) value "Content Type" containing the MIME type
+//
+// 3. "HKCR\filetype" contains
+//   a) unnamed value containing the description
+//   b) subkey "DefaultIcon" with single unnamed value giving the icon index in
+//      an icon file
+//   c) shell\open\command and shell\open\print subkeys containing the commands
+//      to open/print the file (the positional parameters are introduced by %1,
+//      %2, ... in these strings, we change them to %s ourselves)
+
+class wxFileTypeImpl
+{
+public:
+    // ctor
+    wxFileTypeImpl() { }
+
+    // initialize us with our file type name
+    void SetFileType(const wxString& strFileType)
+        { m_strFileType = strFileType; }
+    void SetExt(const wxString& ext)
+        { m_ext = ext; }
+
+    // implement accessor functions
+    bool GetExtensions(wxArrayString& extensions);
+    bool GetMimeType(wxString *mimeType) const;
+    bool GetIcon(wxIcon *icon) const;
+    bool GetDescription(wxString *desc) const;
+    bool GetOpenCommand(wxString *openCmd,
+                        const wxFileType::MessageParameters&) const
+        { return GetCommand(openCmd, "open"); }
+    bool GetPrintCommand(wxString *printCmd,
+                         const wxFileType::MessageParameters&) const
+        { return GetCommand(printCmd, "print"); }
+
+private:
+    // helper function
+    bool GetCommand(wxString *command, const char *verb) const;
+
+    wxString m_strFileType, m_ext;
+};
+
+class wxMimeTypesManagerImpl
+{
+public:
+    // nothing to do here, we don't load any data but just go and fetch it from
+    // the registry when asked for
+    wxMimeTypesManagerImpl() { }
+
+    // implement containing class functions
+    wxFileType *GetFileTypeFromExtension(const wxString& ext);
+    wxFileType *GetFileTypeFromMimeType(const wxString& mimeType);
+
+    // this are NOPs under Windows
+    void ReadMailcap(const wxString& filename) { }
+    void ReadMimeTypes(const wxString& filename) { }
+};
+
+#else  // Unix
+
+// this class uses both mailcap and mime.types to gather information about file
+// types.
+//
+// The information about mailcap file was extracted from metamail(1) sources and
+// documentation.
+//
+// Format of mailcap file: spaces are ignored, each line is either a comment
+// (starts with '#') or a line of the form <field1>;<field2>;...;<fieldN>.
+// A backslash can be used to quote semicolons and newlines (and, in fact,
+// anything else including itself).
+//
+// The first field is always the MIME type in the form of type/subtype (see RFC
+// 822) where subtype may be '*' meaning "any". Following metamail, we accept
+// "type" which means the same as "type/*", although I'm not sure whether this
+// is standard.
+//
+// The second field is always the command to run. It is subject to
+// parameter/filename expansion described below.
+//
+// All the following fields are optional and may not be present at all. If
+// they're present they may appear in any order, although each of them should
+// appear only once. The optional fields are the following:
+//  * notes=xxx is an uninterpreted string which is silently ignored
+//  * test=xxx is the command to be used to determine whether this mailcap line
+//    applies to our data or not. The RHS of this field goes through the
+//    parameter/filename expansion (as the 2nd field) and the resulting string
+//    is executed. The line applies only if the command succeeds, i.e. returns 0
+//    exit code.
+//  * print=xxx is the command to be used to print (and not view) the data of
+//    this type (parameter/filename expansion is done here too)
+//  * edit=xxx is the command to open/edit the data of this type
+//  * needsterminal means that a new console must be created for the viewer
+//  * copiousoutput means that the viewer doesn't interact with the user but
+//    produces (possibly) a lof of lines of output on stdout (i.e. "cat" is a
+//    good example), thus it might be a good idea to use some kind of paging
+//    mechanism.
+//  * textualnewlines means not to perform CR/LF translation (not honored)
+//  * compose and composetyped fields are used to determine the program to be
+//    called to create a new message pert in the specified format (unused).
+//
+// Parameter/filename xpansion: 
+//  * %s is replaced with the (full) file name
+//  * %t is replaced with MIME type/subtype of the entry
+//  * for multipart type only %n is replaced with the nnumber of parts and %F is
+//    replaced by an array of (content-type, temporary file name) pairs for all
+//    message parts (TODO)
+//  * %{parameter} is replaced with the value of parameter taken from
+//    Content-type header line of the message.
+//
+// FIXME any docs with real descriptions of these files??
+//
+// There are 2 possible formats for mime.types file, one entry per line (used
+// for global mime.types) and "expanded" format where an entry takes multiple
+// lines (used for users mime.types).
+//
+// For both formats spaces are ignored and lines starting with a '#' are
+// comments. Each record has one of two following forms:
+//  a) for "brief" format:
+//      <mime type>  <space separated list of extensions>
+//  b) for "expanded" format: 
+//      type=<mime type> \ desc="<description>" \ exts="ext"
+//
+// We try to autodetect the format of mime.types: if a non-comment line starts
+// with "type=" we assume the second format, otherwise the first one.
+
+// there may be more than one entry for one and the same mime type, to
+// choose the right one we have to run the command specified in the test
+// field on our data.
+class MailCapEntry
+{
+public:
+    // ctor
+    MailCapEntry(const wxString& openCmd,
+                 const wxString& printCmd,
+                 const wxString& testCmd)
+        : m_openCmd(openCmd), m_printCmd(printCmd), m_testCmd(testCmd)
+    {
+        m_next = NULL;
+    }
+
+    // accessors
+    const wxString& GetOpenCmd()  const { return m_openCmd;  }
+    const wxString& GetPrintCmd() const { return m_printCmd; }
+    const wxString& GetTestCmd()  const { return m_testCmd;  }
+
+    MailCapEntry *GetNext() const { return m_next; }
+
+    // operations
+        // prepend this element to the list
+    void Prepend(MailCapEntry *next) { m_next = next; }
+        // append to the list
+    void Append(MailCapEntry *next)
+    {
+        // FIXME slooow...
+        MailCapEntry *cur;
+        for ( cur = next; cur->m_next != NULL; cur = cur->m_next )
+            ;
+
+        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 );
+    }
+
+private:
+    wxString m_openCmd,         // command to use to open/view the file
+             m_printCmd,        //                     print
+             m_testCmd;         // only apply this entry if test yields
+                                // true (i.e. the command returns 0)
+
+    MailCapEntry *m_next;       // in the linked list
+};
+
+WX_DEFINE_ARRAY(MailCapEntry *, ArrayTypeEntries);
+
+class wxMimeTypesManagerImpl
+{
+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...
+    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);
+
+    // accessors
+        // get the string containing space separated extensions for the given
+        // file type
+    wxString GetExtension(size_t index) { return m_aExtensions[index]; }
+
+private:
+    wxArrayString m_aTypes,         // MIME types
+                  m_aDescriptions,  // descriptions (just some text)
+                  m_aExtensions;    // space separated list of extensions
+    ArrayTypeEntries m_aEntries;    // commands and tests for this file type
+};
+
+class wxFileTypeImpl
+{
+public:
+    // initialization functions
+    void Init(wxMimeTypesManagerImpl *manager, size_t index)
+        { m_manager = manager; m_index = index; }
+
+    // accessors
+    bool GetExtensions(wxArrayString& extensions);
+    bool GetMimeType(wxString *mimeType) const
+        { *mimeType = m_manager->m_aTypes[m_index]; return TRUE; }
+    bool GetIcon(wxIcon *icon) const
+        { return FALSE; }   // @@ maybe with Gnome/KDE integration...
+    bool GetDescription(wxString *desc) const
+        { *desc = m_manager->m_aDescriptions[m_index]; return TRUE; }
+
+    bool GetOpenCommand(wxString *openCmd,
+                        const wxFileType::MessageParameters& params) const
+    {
+        return GetExpandedCommand(openCmd, params, TRUE);
+    }
+
+    bool GetPrintCommand(wxString *printCmd,
+                         const wxFileType::MessageParameters& params) const
+    {
+        return GetExpandedCommand(printCmd, params, FALSE);
+    }
+
+private:
+    // get the entry which passes the test (may return NULL)
+    MailCapEntry *GetEntry(const wxFileType::MessageParameters& params) const;
+
+    // choose the correct entry to use and expand the command
+    bool GetExpandedCommand(wxString *expandedCmd,
+                            const wxFileType::MessageParameters& params,
+                            bool open) const;
+
+    wxMimeTypesManagerImpl *m_manager;
+    size_t                  m_index; // in the wxMimeTypesManagerImpl arrays
+};
+
+#endif // OS type
+
+// ============================================================================
+// implementation of the wrapper classes
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// wxFileType
+// ----------------------------------------------------------------------------
+
+wxString wxFileType::ExpandCommand(const wxString& command,
+                                   const wxFileType::MessageParameters& params)
+{
+    bool hasFilename = FALSE;
+
+    wxString str;
+    for ( const char *pc = command.c_str(); *pc != '\0'; pc++ ) {
+        if ( *pc == '%' ) {
+            switch ( *++pc ) {
+                case '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) == '"' )
+                        str << params.GetFileName();
+                    else
+                        str << '"' << params.GetFileName() << '"';
+#endif
+                    str << params.GetFileName();
+                    hasFilename = TRUE;
+                    break;
+
+                case 't':
+                    // '%t' expands into MIME type (quote it too just to be
+                    // consistent)
+                    str << '\'' << params.GetMimeType() << '\'';
+                    break;
+
+                case '{':
+                    {
+                        const char *pEnd = strchr(pc, '}');
+                        if ( pEnd == NULL ) {
+                            wxString mimetype;
+                            wxLogWarning(_("Unmatched '{' in an entry for "
+                                           "mime type %s."),
+                                         params.GetMimeType().c_str());
+                            str << "%{";
+                        }
+                        else {
+                            wxString param(pc + 1, pEnd - pc - 1);
+                            str << '\'' << params.GetParamValue(param) << '\'';
+                            pc = pEnd;
+                        }
+                    }
+                    break;
+
+                case 'n':
+                case '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'.",
+                               *pc, command.c_str());
+                    str << *pc;
+            }
+        }
+        else {
+            str << *pc;
+        }
+    }
+
+    // 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() << '\'';
+    }
+
+    return str;
+}
+
+wxFileType::wxFileType()
+{
+    m_impl = new wxFileTypeImpl;
+}
+
+wxFileType::~wxFileType()
+{
+    delete m_impl;
+}
+
+bool wxFileType::GetExtensions(wxArrayString& extensions)
+{
+    return m_impl->GetExtensions(extensions);
+}
+
+bool wxFileType::GetMimeType(wxString *mimeType) const
+{
+    return m_impl->GetMimeType(mimeType);
+}
+
+bool wxFileType::GetIcon(wxIcon *icon) const
+{
+    return m_impl->GetIcon(icon);
+}
+
+bool wxFileType::GetDescription(wxString *desc) const
+{
+    return m_impl->GetDescription(desc);
+}
+
+bool
+wxFileType::GetOpenCommand(wxString *openCmd,
+                           const wxFileType::MessageParameters& params) const
+{
+    return m_impl->GetOpenCommand(openCmd, params);
+}
+
+bool
+wxFileType::GetPrintCommand(wxString *printCmd,
+                            const wxFileType::MessageParameters& params) const
+{
+    return m_impl->GetPrintCommand(printCmd, params);
+}
+
+// ----------------------------------------------------------------------------
+// wxMimeTypesManager
+// ----------------------------------------------------------------------------
+
+wxMimeTypesManager::wxMimeTypesManager()
+{
+    m_impl = new wxMimeTypesManagerImpl;
+}
+
+wxMimeTypesManager::~wxMimeTypesManager()
+{
+    delete m_impl;
+}
+
+wxFileType *
+wxMimeTypesManager::GetFileTypeFromExtension(const wxString& ext)
+{
+    return m_impl->GetFileTypeFromExtension(ext);
+}
+
+wxFileType *
+wxMimeTypesManager::GetFileTypeFromMimeType(const wxString& mimeType)
+{
+    return m_impl->GetFileTypeFromMimeType(mimeType);
+}
+
+// ============================================================================
+// real (OS specific) implementation
+// ============================================================================
+
+#ifdef __WXMSW__
+
+bool wxFileTypeImpl::GetCommand(wxString *command, const char *verb) const
+{
+    // suppress possible error messages
+    wxLogNull nolog;
+    wxString strKey;
+    strKey << m_strFileType << "\\shell\\" << verb << "\\command";
+    wxRegKey key(wxRegKey::HKCR, strKey);
+
+    if ( key.Open() ) {
+        // it's the default value of the key
+        if ( key.QueryValue("", *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'!
+            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' ) {
+                    // replace it with '%s'
+                    command->SetChar(n + 1, 's');
+
+                    return TRUE;
+                }
+            }
+
+            // we didn't find any '%1'!
+            // @@@ hack: append the filename at the end, hope that it will do
+            *command << " %s";
+
+            return TRUE;
+        }
+    }
+
+    // no such file type or no value
+    return FALSE;
+}
+
+// @@ this function is half implemented
+bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
+{
+    if ( m_ext.IsEmpty() ) {
+        // the only way to get the list of extensions from the file type is to
+        // scan through all extensions in the registry - too slow...
+        return FALSE;
+    }
+    else {
+        extensions.Empty();
+        extensions.Add(m_ext);
+
+        // it's a lie too, we don't return _all_ extensions...
+        return TRUE;
+    }
+}
+
+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) ) {
+        return TRUE;
+    }
+    else {
+        return FALSE;
+    }
+}
+
+bool wxFileTypeImpl::GetIcon(wxIcon *icon) const
+{
+    wxString strIconKey;
+    strIconKey << m_strFileType << "\\DefaultIcon";
+
+    // suppress possible error messages
+    wxLogNull nolog;
+    wxRegKey key(wxRegKey::HKCR, strIconKey);
+
+    if ( key.Open() ) {
+        wxString strIcon;
+        // it's the default value of the key
+        if ( key.QueryValue("", 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.Before(','),
+            strIndex = strIcon.Right(',');
+
+            // index may be omitted, in which case Before(',') is empty and
+            // Right(',') is the whole string
+            if ( strFullPath.IsEmpty() ) {
+                strFullPath = strIndex;
+                strIndex = "0";
+            }
+
+            wxString strExpPath = wxExpandEnvVars(strFullPath);
+            int nIndex = atoi(strIndex);
+
+            HICON hIcon = ExtractIcon(GetModuleHandle(NULL), strExpPath, nIndex);
+            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.",
+                               key.GetName().c_str());
+                    break;
+
+                default:
+                    icon->SetHICON((WXHICON)hIcon);
+                    return TRUE;
+            }
+        }
+    }
+
+    // no such file type or no value or incorrect icon entry
+    return FALSE;
+}
+
+bool wxFileTypeImpl::GetDescription(wxString *desc) const
+{
+    // suppress possible error messages
+    wxLogNull nolog;
+    wxRegKey key(wxRegKey::HKCR, m_strFileType);
+
+    if ( key.Open() ) {
+        // it's the default value of the key
+        if ( key.QueryValue("", *desc) ) {
+            return TRUE;
+        }
+    }
+
+    return FALSE;
+}
+
+// extension -> file type
+wxFileType *
+wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& ext)
+{
+    // add the leading point if necessary
+    wxString str;
+    if ( ext[0u] != '.' ) {
+        str = '.';
+    }
+    str << ext;
+
+    // suppress possible error messages
+    wxLogNull nolog;
+
+    wxString strFileType;
+    wxRegKey key(wxRegKey::HKCR, str);
+    if ( key.Open() ) {
+        // it's the default value of the key
+        if ( key.QueryValue("", strFileType) ) {
+            // create the new wxFileType object
+            wxFileType *fileType = new wxFileType;
+            fileType->m_impl->SetFileType(strFileType);
+            fileType->m_impl->SetExt(ext);
+
+            return fileType;
+        }
+    }
+
+    // unknown extension
+    return NULL;
+}
+
+// MIME type -> extension -> file type
+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\\";
+
+    wxString strKey = szMimeDbase;
+    strKey << mimeType;
+
+    // suppress possible error messages
+    wxLogNull nolog;
+
+    wxString ext;
+    wxRegKey key(wxRegKey::HKCR, strKey);
+    if ( key.Open() ) {
+        if ( key.QueryValue("Extension", ext) ) {
+            return GetFileTypeFromExtension(ext);
+        }
+    }
+
+    // unknown MIME type
+    return NULL;
+}
+
+#else  // Unix
+
+MailCapEntry *
+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?)
+        command = wxFileType::ExpandCommand(entry->GetTestCmd(), params);
+
+        if ( command.IsEmpty() || (system(command) == 0) ) {
+            // ok, passed
+            wxLogTrace("Test '%s' for mime type '%s' succeeded.",
+                       command.c_str(), params.GetMimeType().c_str());
+            break;
+        }
+        else {
+            wxLogTrace("Test '%s' for mime type '%s' failed.",
+                       command.c_str(), params.GetMimeType().c_str());
+        }
+
+        entry = entry->GetNext();
+    }
+
+    return entry;
+}
+
+bool
+wxFileTypeImpl::GetExpandedCommand(wxString *expandedCmd,
+                                   const wxFileType::MessageParameters& params,
+                                   bool open) const
+{
+    MailCapEntry *entry = GetEntry(params);
+    if ( entry == NULL ) {
+        // all tests failed...
+        return FALSE;
+    }
+
+    wxString cmd = open ? entry->GetOpenCmd() : entry->GetPrintCmd();
+    if ( cmd.IsEmpty() ) {
+        // may happen, especially for "print"
+        return FALSE;
+    }
+
+    *expandedCmd = wxFileType::ExpandCommand(cmd, params);
+    return TRUE;
+}
+
+bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
+{
+    wxString strExtensions = m_manager->GetExtension(m_index);
+    extensions.Empty();
+
+    // one extension in the space or comma delimitid list
+    wxString strExt;
+    for ( const char *p = strExtensions; ; p++ ) {
+        if ( *p == ' ' || *p == ',' || *p == '\0' ) {
+            if ( !strExt.IsEmpty() ) {
+                extensions.Add(strExt);
+                strExt.Empty();
+            }
+            //else: repeated spaces (shouldn't happen, but it's not that
+            //      important if it does happen)
+
+            if ( *p == '\0' )
+                break;
+        }
+        else if ( *p == '.' ) {
+            // remove the dot from extension (but only if it's the first char)
+            if ( !strExt.IsEmpty() ) {
+                strExt += '.';
+            }
+            //else: no, don't append it
+        }
+        else {
+            strExt += *p;
+        }
+    }
+
+    return TRUE;
+}
+
+// read system and user mailcaps (TODO implement mime.types support)
+wxMimeTypesManagerImpl::wxMimeTypesManagerImpl()
+{
+    // directories where we look for mailcap and mime.types by default
+    // (taken from metamail(1) sources)
+    static const char *aStandardLocations[] =
+    {
+        "/etc",
+        "/usr/etc",
+        "/usr/local/etc",
+        "/etc/mail",
+        "/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";
+        if ( wxFile::Exists(file) ) {
+            ReadMailcap(file);
+        }
+
+        file = dir + "/mime.types";
+        if ( wxFile::Exists(file) ) {
+            ReadMimeTypes(file);
+        }
+    }
+
+    wxString strHome = getenv("HOME");
+
+    // and now the users mailcap
+    wxString strUserMailcap = strHome + "/.mailcap";
+    if ( wxFile::Exists(strUserMailcap) ) {
+        ReadMailcap(strUserMailcap);
+    }
+
+    // read the users mime.types
+    wxString strUserMimeTypes = strHome + "/.mime.types";
+    if ( wxFile::Exists(strUserMimeTypes) ) {
+        ReadMimeTypes(strUserMimeTypes);
+    }
+}
+
+wxFileType *
+wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& ext)
+{
+    wxFAIL_MSG("not implemented (must parse mime.types)");
+
+    return NULL;
+}
+
+wxFileType *
+wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
+{
+    // mime types are not case-sensitive
+    wxString mimetype(mimeType);
+    mimetype.MakeLower();
+
+    // first look for an exact match
+    int index = m_aTypes.Index(mimetype);
+    if ( index == NOT_FOUND ) {
+        // then try to find "text/*" as match for "text/plain" (for example)
+        // NB: if mimeType doesn't contain '/' at all, Left() will return the
+        //     whole string - ok.
+        wxString strCategory = mimetype.Left('/');
+
+        size_t nCount = m_aTypes.Count();
+        for ( size_t n = 0; n < nCount; n++ ) {
+            if ( (m_aTypes[n].Before('/') == strCategory ) &&
+                 m_aTypes[n].Right('/') == "*" ) {
+                    index = n;
+                    break;
+            }
+        }
+    }
+
+    if ( index != NOT_FOUND ) {
+        wxFileType *fileType = new wxFileType;
+        fileType->m_impl->Init(this, index);
+
+        return fileType;
+    }
+    else {
+        // not found...
+        return NULL;
+    }
+}
+
+void wxMimeTypesManagerImpl::ReadMimeTypes(const wxString& strFileName)
+{
+    wxLogTrace("--- Parsing mime.types file '%s' ---", strFileName.c_str());
+
+    wxTextFile file(strFileName);
+    if ( !file.Open() )
+        return;
+
+    // the information we extract
+    wxString strMimeType, strDesc, strExtensions;
+
+    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();
+
+        // skip whitespace
+        while ( isspace(*pc) )
+            pc++;
+
+        // comment?
+        if ( *pc == '#' )
+            continue;
+
+        // detect file format
+        const char *pEqualSign = strchr(pc, '=');
+        if ( pEqualSign == NULL ) {
+            // brief format
+            // ------------
+
+            // first field is mime type
+            for ( strMimeType.Empty(); !isspace(*pc) && *pc != '\0'; pc++ ) {
+                strMimeType += *pc;
+            }
+
+            // skip whitespace
+            while ( isspace(*pc) )
+                pc++;
+
+            // take all the rest of the string
+            strExtensions = pc;
+
+            // no description...
+            strDesc.Empty();
+        }
+        else {
+            // expanded format
+            // ---------------
+
+            // the string on the left of '=' is the field name
+            wxString strLHS(pc, pEqualSign - pc);
+
+            // eat whitespace
+            for ( pc = pEqualSign + 1; isspace(*pc); pc++ )
+                ;
+
+            const char *pEnd;
+            if ( *pc == '"' ) {
+                // the string is quoted and ends at the matching quote
+                pEnd = strchr(++pc, '"');
+                if ( pEnd == NULL ) {
+                    wxLogWarning(_("Mime.types file %s, line %d: unterminated "
+                                   "quoted string."),
+                                 strFileName.c_str(), nLine + 1);
+                }
+            }
+            else {
+                // unquoted stringends at the first space
+                for ( pEnd = pc; !isspace(*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 == '"' ) {
+                // skip this quote
+                pEnd++;
+            }
+
+            for ( pc = pEnd; isspace(*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 a trailing backslash entryEnded = FALSE
+
+            // now see what we got
+            if ( strLHS == "type" ) {
+                strMimeType = strRHS;
+            }
+            else if ( strLHS == "desc" ) {
+                strDesc = strRHS;
+            }
+            else if ( strLHS == "exts" ) {
+                strExtensions = strRHS;
+            }
+            else {
+                wxLogWarning(_("Unknown field in file %s, line %d: '%s'."),
+                             strFileName.c_str(), nLine + 1, strLHS.c_str());
+            }
+
+            if ( !entryEnded ) {
+                // as we don't reset strMimeType, the next line in this entry
+                // will be interpreted correctly.
+                continue;
+            }
+        }
+
+        int index = m_aTypes.Index(strMimeType);
+        if ( index == NOT_FOUND ) {
+            // add a new entry
+            m_aTypes.Add(strMimeType);
+            m_aEntries.Add(NULL);
+            m_aExtensions.Add(strExtensions);
+            m_aDescriptions.Add(strDesc);
+        }
+        else {
+            // modify an existing one
+            if ( !strDesc.IsEmpty() ) {
+                m_aDescriptions[index] = strDesc;   // replace old value
+            }
+            m_aExtensions[index] += strExtensions;
+        }
+    }
+
+    // check our data integriry
+    wxASSERT( m_aTypes.Count() == m_aEntries.Count() &&
+              m_aTypes.Count() == m_aExtensions.Count() &&
+              m_aTypes.Count() == m_aDescriptions.Count() );
+}
+
+void wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName)
+{
+    wxLogTrace("--- Parsing mailcap file '%s' ---", strFileName.c_str());
+
+    wxTextFile file(strFileName);
+    if ( !file.Open() )
+        return;
+
+    // see the comments near the end of function for the reason we need this
+    wxArrayInt aEntryIndices;
+
+    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();
+
+        // skip whitespace
+        while ( isspace(*pc) )
+            pc++;
+
+        // comment or empty string?
+        if ( *pc == '#' || *pc == '\0' )
+            continue;
+
+        // no, do parse
+
+        // what field are we currently in? The first 2 are fixed and there may
+        // be an arbitrary number of other fields -- currently, we are not
+        // interested in any of them, but we should parse them as well...
+        enum
+        {
+            Field_Type,
+            Field_OpenCmd,
+            Field_Other
+        } currentToken = Field_Type;
+
+        // the flags and field values on the current line
+        bool needsterminal = false,
+             copiousoutput = false;
+        wxString strType,
+                 strOpenCmd,
+                 strPrintCmd,
+                 strTest,
+                 strDesc,
+                 curField; // accumulator
+        for ( bool cont = TRUE; cont; pc++ ) {
+            switch ( *pc ) {
+                case '\\':
+                    // interpret the next character literally (notice that
+                    // backslash can be used for line continuation)
+                    if ( *++pc == '\0' ) {
+                        // fetch the next line.
+
+                        // pc currently points to nowhere, but after the next
+                        // pc++ in the for line it will point to the beginning
+                        // of the next line in the file
+                        pc = file[++nLine].c_str() - 1;
+                    }
+                    else {
+                        // just a normal character
+                        curField += *pc;
+                    }
+                    break;
+
+                case '\0':
+                    cont = FALSE;   // end of line reached, exit the loop
+
+                    // fall through
+
+                case ';':
+                    // store this field and start looking for the next one
+
+                    // trim whitespaces from both sides
+                    curField.Trim(TRUE).Trim(FALSE);
+
+                    switch ( currentToken ) {
+                        case Field_Type:
+                            strType = curField;
+                            if ( strType.Find('/') == NOT_FOUND ) {
+                                // we interpret "type" as "type/*"
+                                strType += "/*";
+                            }
+
+                            currentToken = Field_OpenCmd;
+                            break;
+
+                        case Field_OpenCmd:
+                            strOpenCmd = curField;
+
+                            currentToken = Field_Other;
+                            break;
+
+                        case Field_Other:
+                            {
+                                // "good" mailcap entry?
+                                bool ok = TRUE;
+
+                                // is this something of the form foo=bar?
+                                const char *pEq = strchr(curField, '=');
+                                if ( pEq != NULL ) {
+                                    wxString lhs = curField.Left('='),
+                                             rhs = curField.After('=');
+
+                                    lhs.Trim(TRUE);     // from right
+                                    rhs.Trim(FALSE);    // from left
+
+                                    if ( lhs == "print" )
+                                        strPrintCmd = rhs;
+                                    else if ( lhs == "test" )
+                                        strTest = rhs;
+                                    else if ( lhs == "description" ) {
+                                        // it might be quoted
+                                        if ( rhs[0u] == '"' &&
+                                             rhs.Last() == '"' ) {
+                                            strDesc = wxString(rhs.c_str() + 1,
+                                                               rhs.Len() - 2);
+                                        }
+                                        else {
+                                            strDesc = rhs;
+                                        }
+                                    }
+                                    else if ( lhs == "compose" ||
+                                              lhs == "composetyped" ||
+                                              lhs == "notes" ||
+                                              lhs == "edit" )
+                                        ;   // ignore
+                                    else
+                                        ok = FALSE;
+
+                                }
+                                else {
+                                    // no, it's a simple flag
+                                    // TODO support the flags:
+                                    //  1. create an xterm for 'needsterminal'
+                                    //  2. append "| $PAGER" for 'copiousoutput'
+                                    if ( curField == "needsterminal" )
+                                        needsterminal = TRUE;
+                                    else if ( curField == "copiousoutput" )
+                                        copiousoutput = TRUE;
+                                    else if ( curField == "textualnewlines" )
+                                        ;   // ignore
+                                    else
+                                        ok = FALSE;
+                                }
+
+                                if ( !ok )
+                                {
+                                    // don't flood the user with error messages
+                                    // if we don't understand something in his
+                                    // mailcap
+                                    wxLogDebug
+                                    (
+                                      _("Mailcap file %s, line %d: unknown "
+                                        "field '%s' for the MIME type "
+                                        "'%s' ignored."),
+                                      strFileName.c_str(),
+                                      nLine + 1,
+                                      curField.c_str(),
+                                      strType.c_str()
+                                    );
+                                }
+                            }
+
+                            // it already has this value
+                            //currentToken = Field_Other;
+                            break;
+
+                        default:
+                            wxFAIL_MSG("unknown field type in mailcap");
+                    }
+
+                    // next token starts immediately after ';'
+                    curField.Empty();
+                    break;
+
+                default:
+                    curField += *pc;
+            }
+        }
+
+        // check that we really read something reasonable
+        if ( currentToken == Field_Type || currentToken == Field_OpenCmd ) {
+            wxLogWarning(_("Mailcap file %s, line %d: incomplete entry "
+                           "ignored."),
+                         strFileName.c_str(), nLine + 1);
+        }
+        else {
+            MailCapEntry *entry = new MailCapEntry(strOpenCmd,
+                                                   strPrintCmd,
+                                                   strTest);
+
+            strType.MakeLower();
+            int nIndex = m_aTypes.Index(strType);
+            if ( nIndex == NOT_FOUND ) {
+                // new file type
+                m_aTypes.Add(strType);
+
+                m_aEntries.Add(entry);
+                m_aExtensions.Add("");
+                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) == NOT_FOUND ) {
+                    // first time in this file
+                    aEntryIndices.Add(nIndex);
+                    entry->Prepend(m_aEntries[nIndex]);
+                    m_aEntries[nIndex] = entry;
+                }
+                else {
+                    // not the first time in _this_ file
+                    entry->Append(m_aEntries[nIndex]);
+                }
+
+                if ( !strDesc.IsEmpty() ) {
+                    // @@ replace the old one - what else can we do??
+                    m_aDescriptions[nIndex] = strDesc;
+                }
+            }
+        }
+
+        // check our data integriry
+        wxASSERT( m_aTypes.Count() == m_aEntries.Count() &&
+                  m_aTypes.Count() == m_aExtensions.Count() &&
+                  m_aTypes.Count() == m_aDescriptions.Count() );
+    }
+}
+
+#endif // OS type
+
+/* vi: set cin tw=80 ts=4 sw=4: */
+
-- 
2.50.0

