home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / wxos2240.zip / wxWindows-2.4.0 / src / unix / mimetype.cpp < prev    next >
C/C++ Source or Header  |  2002-11-09  |  85KB  |  2,665 lines

  1. /////////////////////////////////////////////////////////////////////////////
  2. // Name:        unix/mimetype.cpp
  3. // Purpose:     classes and functions to manage MIME types
  4. // Author:      Vadim Zeitlin
  5. // Modified by:
  6. // Created:     23.09.98
  7. // RCS-ID:      $Id: mimetype.cpp,v 1.27.2.6 2002/11/07 12:03:29 VS Exp $
  8. // Copyright:   (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
  9. // Licence:     wxWindows license (part of wxExtra library)
  10. /////////////////////////////////////////////////////////////////////////////
  11.  
  12. // known bugs; there may be others!! chris elliott, biol75@york.ac.uk 27 Mar 01
  13.  
  14. // 1) .mailcap and .mimetypes can be either in a netscape or metamail format
  15. //    and entries may get confused during writing (I've tried to fix this; please let me know
  16. //    any files that fail)
  17. // 2) KDE and Gnome do not yet fully support international read/write
  18. // 3) Gnome key lines like open.latex."LaTeX this file"=latex %f will have odd results
  19. // 4) writing to files comments out the existing data; I hope this avoids losing
  20. //    any data which we could not read, and data which we did not store like test=
  21. // 5) results from reading files with multiple entries (especially matches with type/* )
  22. //    may (or may not) work for getXXX commands
  23. // 6) Loading the png icons in Gnome doesn't work for me...
  24. // 7) In Gnome, if keys.mime exists but keys.users does not, there is
  25. //    an error message in debug mode, but the file is still written OK
  26. // 8) Deleting entries is only allowed from the user file; sytem wide entries
  27. //    will be preserved during unassociate
  28. // 9) KDE does not yet handle multiple actions; Netscape mode never will
  29.  
  30. /*
  31.     TODO: this file is a mess, we need to split it and reformet/review
  32.           everything (VZ)
  33.  */
  34.  
  35. // ============================================================================
  36. // declarations
  37. // ============================================================================
  38.  
  39. // ----------------------------------------------------------------------------
  40. // headers
  41. // ----------------------------------------------------------------------------
  42.  
  43. #ifdef __GNUG__
  44.     #pragma implementation "mimetype.h"
  45. #endif
  46.  
  47. // for compilers that support precompilation, includes "wx.h".
  48. #include "wx/wxprec.h"
  49.  
  50. #ifdef __BORLANDC__
  51.   #pragma hdrstop
  52. #endif
  53.  
  54. #ifndef WX_PRECOMP
  55.   #include "wx/defs.h"
  56. #endif
  57.  
  58. #if wxUSE_MIMETYPE && wxUSE_FILE && wxUSE_TEXTFILE
  59.  
  60. #ifndef WX_PRECOMP
  61.   #include "wx/string.h"
  62.   #if wxUSE_GUI
  63.     #include "wx/icon.h"
  64.  
  65.   #endif
  66. #endif //WX_PRECOMP
  67.  
  68.  
  69. #include "wx/log.h"
  70. #include "wx/file.h"
  71. #include "wx/intl.h"
  72. #include "wx/dynarray.h"
  73. #include "wx/confbase.h"
  74.  
  75. #include "wx/ffile.h"
  76. #include "wx/textfile.h"
  77. #include "wx/dir.h"
  78. #include "wx/utils.h"
  79. #include "wx/tokenzr.h"
  80.  
  81. #include "wx/unix/mimetype.h"
  82.  
  83. // other standard headers
  84. #include <ctype.h>
  85.  
  86. #ifdef __VMS
  87. /* silence warnings for comparing unsigned int's <0 */
  88. # pragma message disable unscomzer
  89. #endif
  90.  
  91. // wxMimeTypeCommands stores the verbs defined for the given MIME type with
  92. // their values
  93. class wxMimeTypeCommands
  94. {
  95. public:
  96.     wxMimeTypeCommands() { }
  97.  
  98.     wxMimeTypeCommands(const wxArrayString& verbs,
  99.                        const wxArrayString& commands)
  100.         : m_verbs(verbs),
  101.           m_commands(commands)
  102.     {
  103.     }
  104.  
  105.     // add a new verb with the command or replace the old value
  106.     void AddOrReplaceVerb(const wxString& verb, const wxString& cmd)
  107.     {
  108.         int n = m_verbs.Index(verb, FALSE /* ignore case */);
  109.         if ( n == wxNOT_FOUND )
  110.         {
  111.             m_verbs.Add(verb);
  112.             m_commands.Add(cmd);
  113.         }
  114.         else
  115.         {
  116.             m_commands[n] = cmd;
  117.         }
  118.     }
  119.  
  120.     void Add(const wxString& s)
  121.     {
  122.         m_verbs.Add(s.BeforeFirst(_T('=')));
  123.         m_commands.Add(s.AfterFirst(_T('=')));
  124.     }
  125.  
  126.     // access the commands
  127.     size_t GetCount() const { return m_verbs.GetCount(); }
  128.     const wxString& GetVerb(size_t n) const { return m_verbs[n]; }
  129.     const wxString& GetCmd(size_t n) const { return m_commands[n]; }
  130.  
  131.     bool HasVerb(const wxString& verb) const
  132.         { return m_verbs.Index(verb) != wxNOT_FOUND; }
  133.  
  134.     wxString GetCommandForVerb(const wxString& verb, size_t *idx = NULL) const
  135.     {
  136.         wxString s;
  137.  
  138.         int n = m_verbs.Index(verb);
  139.         if ( n != wxNOT_FOUND )
  140.         {
  141.             s = m_commands[(size_t)n];
  142.             if ( idx )
  143.                 *idx = n;
  144.         }
  145.  
  146.         return s;
  147.     }
  148.  
  149.     // get a "verb=command" string
  150.     wxString GetVerbCmd(size_t n) const
  151.     {
  152.         return m_verbs[n] + _T('=') + m_commands[n];
  153.     }
  154.  
  155. private:
  156.     wxArrayString m_verbs,
  157.                   m_commands;
  158. };
  159.  
  160. // this class extends wxTextFile
  161. //
  162. // VZ: ???
  163. class wxMimeTextFile : public wxTextFile
  164. {
  165. public:
  166.     // constructors
  167.     wxMimeTextFile () : wxTextFile () {};
  168.     wxMimeTextFile (const wxString& strFile) : wxTextFile (strFile)  {  };
  169.  
  170.     int pIndexOf(const wxString & sSearch, bool bIncludeComments = FALSE, int iStart = 0)
  171.     {
  172.         size_t i = iStart;
  173.         int nResult = wxNOT_FOUND;
  174.         if (i>=GetLineCount()) return wxNOT_FOUND;
  175.  
  176.         wxString sTest = sSearch;
  177.         sTest.MakeLower();
  178.         wxString sLine;
  179.  
  180.         if (bIncludeComments)
  181.         {
  182.             while ( (i < GetLineCount())   )
  183.             {
  184.                 sLine = GetLine (i);
  185.                 sLine.MakeLower();
  186.                 if (sLine.Contains(sTest)) nResult = (int) i;
  187.                 i++;
  188.             }
  189.         }
  190.         else
  191.         {
  192.             while ( (i < GetLineCount()) )
  193.             {
  194.                 sLine = GetLine (i);
  195.                 sLine.MakeLower();
  196.                 if ( ! sLine.StartsWith(wxT("#")))
  197.                 {
  198.                     if (sLine.Contains(sTest)) nResult = (int) i;
  199.                 }
  200.                 i++;
  201.             }
  202.         }
  203.         return  nResult;
  204.     }
  205.  
  206.     bool CommentLine(int nIndex)
  207.     {
  208.         if (nIndex <0) return FALSE;
  209.         if (nIndex >= (int)GetLineCount() ) return FALSE;
  210.         GetLine(nIndex) = GetLine(nIndex).Prepend(wxT("#"));
  211.         return TRUE;
  212.     }
  213.  
  214.     bool CommentLine(const wxString & sTest)
  215.     {
  216.         int nIndex = pIndexOf(sTest);
  217.         if (nIndex <0) return FALSE;
  218.         if (nIndex >= (int)GetLineCount() ) return FALSE;
  219.         GetLine(nIndex) = GetLine(nIndex).Prepend(wxT("#"));
  220.         return TRUE;
  221.     }
  222.  
  223.     wxString GetVerb (size_t i)
  224.     {
  225.         if (i > GetLineCount() ) return wxEmptyString;
  226.         wxString sTmp = GetLine(i).BeforeFirst(wxT('='));
  227.         return sTmp;
  228.     }
  229.  
  230.     wxString GetCmd (size_t i)
  231.     {
  232.         if (i > GetLineCount() ) return wxEmptyString;
  233.         wxString sTmp = GetLine(i).AfterFirst(wxT('='));
  234.         return sTmp;
  235.     }
  236. };
  237.  
  238. // in case we're compiling in non-GUI mode
  239. class WXDLLEXPORT wxIcon;
  240.  
  241. // ----------------------------------------------------------------------------
  242. // constants
  243. // ----------------------------------------------------------------------------
  244.  
  245. // MIME code tracing mask
  246. #define TRACE_MIME _T("mime")
  247.  
  248. // give trace messages about the results of mailcap tests
  249. #define TRACE_MIME_TEST _T("mimetest")
  250.  
  251. // ----------------------------------------------------------------------------
  252. // private functions
  253. // ----------------------------------------------------------------------------
  254.  
  255. // there are some fields which we don't understand but for which we don't give
  256. // warnings as we know that they're not important - this function is used to
  257. // test for them
  258. static bool IsKnownUnimportantField(const wxString& field);
  259.  
  260. // ----------------------------------------------------------------------------
  261. // private classes
  262. // ----------------------------------------------------------------------------
  263.  
  264.  
  265. // This class uses both mailcap and mime.types to gather information about file
  266. // types.
  267. //
  268. // The information about mailcap file was extracted from metamail(1) sources
  269. // and documentation and subsequently revised when I found the RFC 1524
  270. // describing it.
  271. //
  272. // Format of mailcap file: spaces are ignored, each line is either a comment
  273. // (starts with '#') or a line of the form <field1>;<field2>;...;<fieldN>.
  274. // A backslash can be used to quote semicolons and newlines (and, in fact,
  275. // anything else including itself).
  276. //
  277. // The first field is always the MIME type in the form of type/subtype (see RFC
  278. // 822) where subtype may be '*' meaning "any". Following metamail, we accept
  279. // "type" which means the same as "type/*", although I'm not sure whether this
  280. // is standard.
  281. //
  282. // The second field is always the command to run. It is subject to
  283. // parameter/filename expansion described below.
  284. //
  285. // All the following fields are optional and may not be present at all. If
  286. // they're present they may appear in any order, although each of them should
  287. // appear only once. The optional fields are the following:
  288. //  * notes=xxx is an uninterpreted string which is silently ignored
  289. //  * test=xxx is the command to be used to determine whether this mailcap line
  290. //    applies to our data or not. The RHS of this field goes through the
  291. //    parameter/filename expansion (as the 2nd field) and the resulting string
  292. //    is executed. The line applies only if the command succeeds, i.e. returns 0
  293. //    exit code.
  294. //  * print=xxx is the command to be used to print (and not view) the data of
  295. //    this type (parameter/filename expansion is done here too)
  296. //  * edit=xxx is the command to open/edit the data of this type
  297. //  * needsterminal means that a new interactive console must be created for
  298. //    the viewer
  299. //  * copiousoutput means that the viewer doesn't interact with the user but
  300. //    produces (possibly) a lof of lines of output on stdout (i.e. "cat" is a
  301. //    good example), thus it might be a good idea to use some kind of paging
  302. //    mechanism.
  303. //  * textualnewlines means not to perform CR/LF translation (not honored)
  304. //  * compose and composetyped fields are used to determine the program to be
  305. //    called to create a new message pert in the specified format (unused).
  306. //
  307. // Parameter/filename expansion:
  308. //  * %s is replaced with the (full) file name
  309. //  * %t is replaced with MIME type/subtype of the entry
  310. //  * for multipart type only %n is replaced with the nnumber of parts and %F is
  311. //    replaced by an array of (content-type, temporary file name) pairs for all
  312. //    message parts (TODO)
  313. //  * %{parameter} is replaced with the value of parameter taken from
  314. //    Content-type header line of the message.
  315. //
  316. //
  317. // There are 2 possible formats for mime.types file, one entry per line (used
  318. // for global mime.types and called Mosaic format) and "expanded" format where
  319. // an entry takes multiple lines (used for users mime.types and called
  320. // Netscape format).
  321. //
  322. // For both formats spaces are ignored and lines starting with a '#' are
  323. // comments. Each record has one of two following forms:
  324. //  a) for "brief" format:
  325. //      <mime type>  <space separated list of extensions>
  326. //  b) for "expanded" format:
  327. //      type=<mime type> BACKSLASH
  328. //      desc="<description>" BACKSLASH
  329. //      exts="<comma separated list of extensions>"
  330. //
  331. // (where BACKSLASH is a literal '\\' which we can't put here because cpp
  332. // misinterprets it)
  333. //
  334. // We try to autodetect the format of mime.types: if a non-comment line starts
  335. // with "type=" we assume the second format, otherwise the first one.
  336.  
  337. // there may be more than one entry for one and the same mime type, to
  338. // choose the right one we have to run the command specified in the test
  339. // field on our data.
  340.  
  341. // ----------------------------------------------------------------------------
  342. // wxGNOME
  343. // ----------------------------------------------------------------------------
  344.  
  345. // GNOME stores the info we're interested in in several locations:
  346. //  1. xxx.keys files under /usr/share/mime-info
  347. //  2. xxx.keys files under ~/.gnome/mime-info
  348. //
  349. // The format of xxx.keys file is the following:
  350. //
  351. // mimetype/subtype:
  352. //      field=value
  353. //
  354. // with blank lines separating the entries and indented lines starting with
  355. // TABs. We're interested in the field icon-filename whose value is the path
  356. // containing the icon.
  357. //
  358. // Update (Chris Elliott): apparently there may be an optional "[lang]" prefix
  359. // just before the field name.
  360.  
  361.  
  362. bool wxMimeTypesManagerImpl::CheckGnomeDirsExist ()
  363. {
  364.     wxString gnomedir;
  365.     wxGetHomeDir( &gnomedir );
  366.     wxString sTmp = gnomedir;
  367.     sTmp = sTmp + wxT("/.gnome");
  368.     if (! wxDir::Exists ( sTmp ) )
  369.     {
  370.         if (!wxMkdir ( sTmp ))
  371.         {
  372.             wxLogError(_("Failed to create directory %s/.gnome."), sTmp.c_str());
  373.             return FALSE;
  374.         }
  375.     }
  376.     sTmp = sTmp + wxT("/mime-info");
  377.     if (! wxDir::Exists ( sTmp ) )
  378.     {
  379.         if (!wxMkdir ( sTmp ))
  380.         {
  381.             wxLogError(_("Failed to create directory %s/mime-info."), sTmp.c_str());
  382.             return FALSE;
  383.         }
  384.     }
  385.     return TRUE;
  386.  
  387. }
  388.  
  389.  
  390.  
  391. bool wxMimeTypesManagerImpl::WriteGnomeKeyFile(int index, bool delete_index)
  392. {
  393.     wxString gnomedir;
  394.     wxGetHomeDir( &gnomedir );
  395.  
  396.     wxMimeTextFile outfile ( gnomedir + wxT("/.gnome/mime-info/user.keys"));
  397.     // if this fails probably Gnome is not installed ??
  398.     // create it anyway as a private mime store
  399.  
  400. #if defined(__WXGTK20__) && wxUSE_UNICODE
  401.     if (! outfile.Open ( wxConvUTF8) )
  402. #else
  403.     if (! outfile.Open () )
  404. #endif
  405.     {
  406.         if (delete_index) return FALSE;
  407.         if (!CheckGnomeDirsExist() ) return FALSE;
  408.         outfile.Create ();
  409.     }
  410.  
  411.     wxString sTmp, strType = m_aTypes[index];
  412.     int nIndex = outfile.pIndexOf(strType);
  413.     if ( nIndex == wxNOT_FOUND )
  414.     {
  415.         outfile.AddLine ( strType + wxT(':') );
  416.         // see file:/usr/doc/gnome-libs-devel-1.0.40/devel-docs/mime-type-handling.txt
  417.         // as this does not deal with internationalisation
  418.         //        wxT( "\t[en_US]") + verb + wxT ('=') + cmd + wxT(" %f");
  419.         wxMimeTypeCommands * entries = m_aEntries[index];
  420.         size_t count = entries->GetCount();
  421.         for ( size_t i = 0; i < count; i++ )
  422.         {
  423.             sTmp = entries->GetVerbCmd(i);
  424.             sTmp.Replace( wxT("%s"), wxT("%f") );
  425.             sTmp = wxT ( "\t") + sTmp;
  426.             outfile.AddLine ( sTmp );
  427.         }
  428.         //for international use do something like this
  429.         //outfile.AddLine ( wxString( "\t[en_US]icon-filename=") + cmd );
  430.         outfile.AddLine ( wxT( "\ticon-filename=") + m_aIcons[index] );
  431.     }
  432.     else
  433.     {
  434.         if (delete_index)
  435.             outfile.CommentLine(nIndex);
  436.  
  437.         wxMimeTypeCommands sOld;
  438.         size_t nOld = nIndex + 1;
  439.         bool oldEntryEnd = FALSE;
  440.         while ( (nOld < outfile.GetLineCount() )&& (oldEntryEnd == FALSE ))
  441.         {
  442.             sTmp = outfile.GetLine(nOld);
  443.             if ( (sTmp[0u] == wxT('\t')) || (sTmp[0u] == wxT('#')) )
  444.             {
  445.                 // we have another line to deal with
  446.                 outfile.CommentLine(nOld);
  447.                 nOld ++;
  448.                 // add the line to our store
  449.                 if ((!delete_index) && (sTmp[0u] == wxT('\t')))
  450.                     sOld.Add(sTmp);
  451.             }
  452.             // next mimetpye ??or blank line
  453.             else
  454.                 oldEntryEnd = TRUE;
  455.         }
  456.         // list of entries in our data; these should all be in sOld,
  457.         // though sOld may also contain other entries , eg flags
  458.         if (!delete_index)
  459.         {
  460.             wxMimeTypeCommands * entries = m_aEntries[index];
  461.             size_t i;
  462.             for (i=0; i < entries->GetCount(); i++)
  463.             {
  464.                 // replace any entries in sold that match verbs we know
  465.                 sOld.AddOrReplaceVerb ( entries->GetVerb(i), entries->GetCmd (i) );
  466.             }
  467.             //sOld should also contain the icon
  468.             if ( !m_aIcons[index].empty() )
  469.                 sOld.AddOrReplaceVerb ( wxT("icon-filename"), m_aIcons[index] );
  470.  
  471.             for (i=0; i < sOld.GetCount(); i++)
  472.             {
  473.                 sTmp = sOld.GetVerbCmd(i);
  474.                 sTmp.Replace( wxT("%s"), wxT("%f") );
  475.                 sTmp = wxT("\t") + sTmp;
  476.                 nIndex ++;
  477.                 outfile.InsertLine ( sTmp, nIndex );
  478.             }
  479.         }
  480.     }
  481.     bool bTmp = outfile.Write ();
  482.     return bTmp;
  483. }
  484.  
  485.  
  486. bool wxMimeTypesManagerImpl::WriteGnomeMimeFile(int index, bool delete_index)
  487. {
  488.     wxString gnomedir;
  489.     wxGetHomeDir( &gnomedir );
  490.  
  491.     wxMimeTextFile outfile ( gnomedir + wxT("/.gnome/mime-info/user.mime"));
  492.     // if this fails probably Gnome is not installed ??
  493.     // create it anyway as a private mime store
  494.     if (! outfile.Open () )
  495.     {
  496.         if (delete_index) return FALSE;
  497.         if (!CheckGnomeDirsExist() ) return FALSE;
  498.         outfile.Create ();
  499.     }
  500.     wxString strType = m_aTypes[index];
  501.     int nIndex = outfile.pIndexOf(strType);
  502.     if ( nIndex == wxNOT_FOUND )
  503.     {
  504.         outfile.AddLine ( strType );
  505.         outfile.AddLine ( wxT("\text:") + m_aExtensions.Item(index) );
  506.     }
  507.     else
  508.     {
  509.         if (delete_index)
  510.         {
  511.             outfile.CommentLine(nIndex);
  512.             outfile.CommentLine(nIndex+1);
  513.         }
  514.         else
  515.         {// check for next line being the right one to replace ??
  516.             wxString sOld = outfile.GetLine(nIndex+1);
  517.             if (sOld.Contains( wxT("\text: ")))
  518.             {
  519.                 outfile.GetLine(nIndex+1) = wxT("\text: ") + m_aExtensions.Item(index);
  520.             }
  521.             else
  522.             {
  523.                 outfile.InsertLine( wxT("\text: ") + m_aExtensions.Item(index), nIndex + 1 );
  524.             }
  525.         }
  526.     }
  527.     bool bTmp = outfile.Write ();
  528.     return bTmp;
  529. }
  530.  
  531.  
  532. void wxMimeTypesManagerImpl::LoadGnomeDataFromKeyFile(const wxString& filename)
  533. {
  534.     wxTextFile textfile(filename);
  535. #if defined(__WXGTK20__) && wxUSE_UNICODE
  536.     if ( !textfile.Open( wxConvUTF8) )
  537. #else
  538.     if ( !textfile.Open() )
  539. #endif
  540.         return;
  541.     wxLogTrace(TRACE_MIME, wxT("--- Opened Gnome file %s  ---"),
  542.             filename.c_str());
  543.  
  544.     // values for the entry being parsed
  545.     wxString curMimeType, curIconFile;
  546.     wxMimeTypeCommands * entry = new wxMimeTypeCommands;
  547.  
  548.     // these are always empty in this file
  549.     wxArrayString strExtensions;
  550.     wxString strDesc;
  551.  
  552.     const wxChar *pc;
  553.     size_t nLineCount = textfile.GetLineCount();
  554.     size_t nLine = 0;
  555.     while ( nLine < nLineCount)
  556.     {
  557.         pc = textfile[nLine].c_str();
  558.         if ( *pc != _T('#') )
  559.         {
  560.  
  561.             wxLogTrace(TRACE_MIME, wxT("--- Reading from Gnome file %s '%s' ---"),
  562.                     filename.c_str(),pc);
  563.  
  564.             wxString sTmp(pc);
  565.             if (sTmp.Contains(wxT("=")) )
  566.             {
  567.                 if (sTmp.Contains( wxT("icon-filename=") ) )
  568.                 {
  569.                     curIconFile = sTmp.AfterFirst(wxT('='));
  570.                 }
  571.                 else //: some other field,
  572.                 {
  573.                     //may contain lines like this (RH7)
  574.                     // \t[lang]open.tex."TeX this file"=tex %f
  575.                     // \tflags.tex.flags=needsterminal
  576.                     // \topen.latex."LaTeX this file"=latex %f
  577.                     // \tflags.latex.flags=needsterminal
  578.  
  579.                     // \topen=xdvi %f
  580.                     // \tview=xdvi %f
  581.                     // \topen.convert.Convert file to Postscript=dvips %f -o `basename %f .dvi`.ps
  582.  
  583.                     // for now ignore lines with flags in...FIX
  584.                     sTmp = sTmp.AfterLast(wxT(']'));
  585.                     sTmp = sTmp.AfterLast(wxT('\t'));
  586.                     sTmp.Trim(FALSE).Trim();
  587.                     if (0 == sTmp.Replace ( wxT("%f"), wxT("%s") )) sTmp = sTmp + wxT(" %s");
  588.                     entry->Add(sTmp);
  589.  
  590.                 }
  591.  
  592.             } // emd of has an equals sign
  593.             else
  594.             {
  595.                 // not a comment and not an equals sign
  596.                 if (sTmp.Contains(wxT('/')))
  597.                 {
  598.                     // this is the start of the new mimetype
  599.                     // overwrite any existing data
  600.                     if (! curMimeType.empty())
  601.                     {
  602.                         AddToMimeData ( curMimeType, curIconFile, entry, strExtensions, strDesc);
  603.  
  604.                         // now get ready for next bit
  605.                         entry = new wxMimeTypeCommands;
  606.                     }
  607.                     curMimeType = sTmp.BeforeFirst(wxT(':'));
  608.                 }
  609.             }
  610.         } // end of not a comment
  611.         // ignore blank lines
  612.         nLine ++;
  613.     } // end of while, save any data
  614.     if (! curMimeType.empty())
  615.     {
  616.         AddToMimeData ( curMimeType, curIconFile, entry, strExtensions, strDesc);
  617.     }
  618.  
  619. }
  620.  
  621.  
  622.  
  623. void wxMimeTypesManagerImpl::LoadGnomeMimeTypesFromMimeFile(const wxString& filename)
  624. {
  625.     wxTextFile textfile(filename);
  626. #if defined(__WXGTK20__) && wxUSE_UNICODE
  627.     if ( !textfile.Open( wxConvUTF8) )
  628. #else
  629.     if ( !textfile.Open() )
  630. #endif
  631.         return;
  632.     wxLogTrace(TRACE_MIME, wxT("--- Opened Gnome file %s  ---"),
  633.                  filename.c_str());
  634.  
  635.     // values for the entry being parsed
  636.     wxString curMimeType, curExtList;
  637.  
  638.     const wxChar *pc;
  639.     size_t nLineCount = textfile.GetLineCount();
  640.     for ( size_t nLine = 0;; nLine++ )
  641.     {
  642.         if ( nLine < nLineCount )
  643.         {
  644.             pc = textfile[nLine].c_str();
  645.             if ( *pc == wxT('#') )
  646.             {
  647.                 // skip comments
  648.                 continue;
  649.             }
  650.         }
  651.         else
  652.         {
  653.             // so that we will fall into the "if" below
  654.             pc = NULL;
  655.         }
  656.  
  657.         if ( !pc || !*pc )
  658.         {
  659.             // end of the entry
  660.             if ( !!curMimeType && !!curExtList )
  661.             {
  662.                  wxLogTrace(TRACE_MIME, wxT("--- At end of Gnome file  finding mimetype %s  ---"),
  663.                  curMimeType.c_str());
  664.  
  665.                  AddMimeTypeInfo(curMimeType, curExtList, wxEmptyString);
  666.             }
  667.  
  668.             if ( !pc )
  669.             {
  670.                 // the end - this can only happen if nLine == nLineCount
  671.                 break;
  672.             }
  673.  
  674.             curExtList.Empty();
  675.  
  676.             continue;
  677.         }
  678.  
  679.         // what do we have here?
  680.         if ( *pc == wxT('\t') )
  681.         {
  682.             // this is a field=value ling
  683.             pc++; // skip leading TAB
  684.  
  685.             static const int lenField = 4; // strlen("ext:")
  686.             if ( wxStrncmp(pc, wxT("ext:"), lenField) == 0 )
  687.             {
  688.                 // skip ' ' which follows and take everything left until the end
  689.                 // of line
  690.                 curExtList = pc + lenField + 1;
  691.             }
  692.             //else: some other field, we don't care
  693.         }
  694.         else
  695.         {
  696.             // this is the start of the new section
  697.             wxLogTrace(TRACE_MIME, wxT("--- In Gnome file  finding mimetype %s  ---"),
  698.                  curMimeType.c_str());
  699.  
  700.             if (! curMimeType.empty())
  701.                 AddMimeTypeInfo(curMimeType, curExtList, wxEmptyString);
  702.  
  703.             curMimeType.Empty();
  704.  
  705.             while ( *pc != wxT(':') && *pc != wxT('\0') )
  706.             {
  707.                 curMimeType += *pc++;
  708.             }
  709.         }
  710.     }
  711. }
  712.  
  713.  
  714. void wxMimeTypesManagerImpl::LoadGnomeMimeFilesFromDir(const wxString& dirbase)
  715. {
  716.     wxASSERT_MSG( !!dirbase && !wxEndsWithPathSeparator(dirbase),
  717.                   _T("base directory shouldn't end with a slash") );
  718.  
  719.     wxString dirname = dirbase;
  720.     dirname << wxT("/mime-info");
  721.  
  722.     if ( !wxDir::Exists(dirname) )
  723.         return;
  724.  
  725.     wxDir dir(dirname);
  726.     if ( !dir.IsOpened() )
  727.         return;
  728.  
  729.     // we will concatenate it with filename to get the full path below
  730.     dirname += wxT('/');
  731.  
  732.     wxString filename;
  733.     bool cont = dir.GetFirst(&filename, _T("*.mime"), wxDIR_FILES);
  734.     while ( cont )
  735.     {
  736.         LoadGnomeMimeTypesFromMimeFile(dirname + filename);
  737.  
  738.         cont = dir.GetNext(&filename);
  739.     }
  740.  
  741.     cont = dir.GetFirst(&filename, _T("*.keys"), wxDIR_FILES);
  742.     while ( cont )
  743.     {
  744.         LoadGnomeDataFromKeyFile(dirname + filename);
  745.  
  746.         cont = dir.GetNext(&filename);
  747.     }
  748. }
  749.  
  750.  
  751.  
  752.  
  753. void wxMimeTypesManagerImpl::GetGnomeMimeInfo(const wxString& sExtraDir)
  754. {
  755.  
  756.     wxArrayString dirs;
  757.     dirs.Add(wxT("/usr/share"));
  758.     dirs.Add(wxT("/usr/local/share"));
  759.  
  760.     wxString gnomedir;
  761.     wxGetHomeDir( &gnomedir );
  762.     gnomedir += wxT("/.gnome");
  763.     dirs.Add( gnomedir );
  764.     if (!sExtraDir.empty()) dirs.Add( sExtraDir );
  765.  
  766.     size_t nDirs = dirs.GetCount();
  767.     for ( size_t nDir = 0; nDir < nDirs; nDir++ )
  768.     {
  769.         LoadGnomeMimeFilesFromDir(dirs[nDir]);
  770.     }
  771. }
  772.  
  773. // ----------------------------------------------------------------------------
  774. // KDE
  775. // ----------------------------------------------------------------------------
  776.  
  777.  
  778. // KDE stores the icon info in its .kdelnk files. The file for mimetype/subtype
  779. // may be found in either of the following locations
  780. //
  781. //  1. $KDEDIR/share/mimelnk/mimetype/subtype.kdelnk
  782. //  2. ~/.kde/share/mimelnk/mimetype/subtype.kdelnk
  783. //
  784. // The format of a .kdelnk file is almost the same as the one used by
  785. // wxFileConfig, i.e. there are groups, comments and entries. The icon is the
  786. // value for the entry "Type"
  787.  
  788. // kde writing; see http://webcvs.kde.org/cgi-bin/cvsweb.cgi/~checkout~/kdelibs/kio/DESKTOP_ENTRY_STANDARD
  789. // for now write to .kdelnk but should eventually do .desktop instead (in preference??)
  790.  
  791. bool wxMimeTypesManagerImpl::CheckKDEDirsExist ( const wxString & sOK, const wxString & sTest )
  792.  
  793.     {
  794.     if (sTest.empty())
  795.         {
  796.             if (wxDir::Exists(sOK)) return TRUE;
  797.             else return FALSE;
  798.         }
  799.     else
  800.         {
  801.             wxString sStart = sOK + wxT("/") + sTest.BeforeFirst(wxT('/'));
  802.             if (!wxDir::Exists(sStart))  wxMkdir(sStart);
  803.             wxString sEnd = sTest.AfterFirst(wxT('/'));
  804.             return CheckKDEDirsExist(sStart, sEnd);
  805.     }
  806. }
  807.  
  808. bool wxMimeTypesManagerImpl::WriteKDEMimeFile(int index, bool delete_index)
  809. {
  810.     wxMimeTextFile appoutfile, mimeoutfile;
  811.     wxString sHome = wxGetHomeDir();
  812.     wxString sTmp = wxT(".kde/share/mimelnk/");
  813.     wxString sMime = m_aTypes[index];
  814.     CheckKDEDirsExist (sHome, sTmp + sMime.BeforeFirst(wxT('/')) );
  815.     sTmp = sHome + wxT('/') + sTmp + sMime + wxT(".kdelnk");
  816.  
  817.     bool bTemp;
  818.     bool bMimeExists = mimeoutfile.Open (sTmp);
  819.     if (!bMimeExists)
  820.     {
  821.         bTemp = mimeoutfile.Create (sTmp);
  822.         // some unknown error eg out of disk space
  823.         if (!bTemp) return FALSE;
  824.     }
  825.  
  826.     sTmp = wxT(".kde/share/applnk/");
  827.     CheckKDEDirsExist (sHome, sTmp + sMime.AfterFirst(wxT('/')) );
  828.     sTmp = sHome + wxT('/') + sTmp + sMime.AfterFirst(wxT('/')) + wxT(".kdelnk");
  829.  
  830.     bool bAppExists;
  831.     bAppExists = appoutfile.Open (sTmp);
  832.     if (!bAppExists)
  833.     {
  834.         bTemp = appoutfile.Create (sTmp);
  835.         // some unknown error eg out of disk space
  836.         if (!bTemp) return FALSE;
  837.     }
  838.  
  839.     // fixed data; write if new file
  840.     if (!bMimeExists)
  841.     {
  842.         mimeoutfile.AddLine(wxT("#KDE Config File"));
  843.         mimeoutfile.AddLine(wxT("[KDE Desktop Entry]"));
  844.         mimeoutfile.AddLine(wxT("Version=1.0"));
  845.         mimeoutfile.AddLine(wxT("Type=MimeType"));
  846.         mimeoutfile.AddLine(wxT("MimeType=") + sMime);
  847.     }
  848.  
  849.     if (!bAppExists)
  850.     {
  851.         mimeoutfile.AddLine(wxT("#KDE Config File"));
  852.         mimeoutfile.AddLine(wxT("[KDE Desktop Entry]"));
  853.         appoutfile.AddLine(wxT("Version=1.0"));
  854.         appoutfile.AddLine(wxT("Type=Application"));
  855.         appoutfile.AddLine(wxT("MimeType=") + sMime + wxT(';'));
  856.     }
  857.  
  858.     // variable data
  859.     // ignore locale
  860.     mimeoutfile.CommentLine(wxT("Comment="));
  861.     if (!delete_index)
  862.         mimeoutfile.AddLine(wxT("Comment=") + m_aDescriptions[index]);
  863.     appoutfile.CommentLine(wxT("Name="));
  864.     if (!delete_index)
  865.         appoutfile.AddLine(wxT("Comment=") + m_aDescriptions[index]);
  866.  
  867.     sTmp = m_aIcons[index];
  868.     // we can either give the full path, or the shortfilename if its in
  869.     // one of the directories we search
  870.     mimeoutfile.CommentLine(wxT("Icon=") );
  871.     if (!delete_index) mimeoutfile.AddLine(wxT("Icon=") + sTmp );
  872.     appoutfile.CommentLine(wxT("Icon=")  );
  873.     if (!delete_index) appoutfile.AddLine(wxT("Icon=") + sTmp );
  874.  
  875.     sTmp = wxT(" ") + m_aExtensions[index];
  876.  
  877.     wxStringTokenizer tokenizer(sTmp, _T(" "));
  878.     sTmp = wxT("Patterns=");
  879.     mimeoutfile.CommentLine(sTmp);
  880.     while ( tokenizer.HasMoreTokens() )
  881.     {
  882.         // holds an extension; need to change it to *.ext;
  883.         wxString e = wxT("*.") + tokenizer.GetNextToken() + wxT(";");
  884.         sTmp = sTmp + e;
  885.     }
  886.     if (!delete_index) mimeoutfile.AddLine(sTmp);
  887.  
  888.     wxMimeTypeCommands * entries = m_aEntries[index];
  889.     // if we don't find open just have an empty string ... FIX this
  890.     sTmp = entries->GetCommandForVerb(_T("open"));
  891.     sTmp.Replace( wxT("%s"), wxT("%f") );
  892.  
  893.     mimeoutfile.CommentLine(wxT("DefaultApp=") );
  894.     if (!delete_index) mimeoutfile.AddLine(wxT("DefaultApp=") + sTmp);
  895.  
  896.     sTmp.Replace( wxT("%f"), wxT("") );
  897.     appoutfile.CommentLine(wxT("Exec="));
  898.     if (!delete_index) appoutfile.AddLine(wxT("Exec=") + sTmp);
  899.  
  900.     if (entries->GetCount() > 1)
  901.     {
  902.         //other actions as well as open
  903.  
  904.     }
  905.     bTemp = FALSE;
  906.     if (mimeoutfile.Write ()) bTemp = TRUE;
  907.     mimeoutfile.Close ();
  908.     if (appoutfile.Write ()) bTemp = TRUE;
  909.     appoutfile.Close ();
  910.  
  911.     return bTemp;
  912.  
  913.  
  914. }
  915.  
  916. void wxMimeTypesManagerImpl::LoadKDELinksForMimeSubtype(const wxString& dirbase,
  917.                                                const wxString& subdir,
  918.                                                const wxString& filename,
  919.                                                const wxArrayString& icondirs)
  920. {
  921.     wxMimeTextFile file;
  922.     if ( !file.Open(dirbase + filename) ) return;
  923.  
  924.     wxMimeTypeCommands * entry = new wxMimeTypeCommands;
  925.     wxArrayString sExts;
  926.     wxString mimetype, mime_desc, strIcon;
  927.  
  928.     int nIndex = file.pIndexOf( wxT("MimeType=") );
  929.     if (nIndex == wxNOT_FOUND)
  930.     {
  931.         // construct mimetype from the directory name and the basename of the
  932.         // file (it always has .kdelnk extension)
  933.         mimetype << subdir << wxT('/') << filename.BeforeLast( wxT('.') );
  934.     }
  935.     else mimetype = file.GetCmd (nIndex);
  936.  
  937.     // first find the description string: it is the value in either "Comment="
  938.     // line or "Comment[<locale_name>]=" one
  939.     nIndex = wxNOT_FOUND;
  940.  
  941.     wxString comment;
  942. #if wxUSE_INTL
  943.     wxLocale *locale = wxGetLocale();
  944.     if ( locale )
  945.     {
  946.         // try "Comment[locale name]" first
  947.         comment << _T("Comment[") + locale->GetName() + _T("]=");
  948.         nIndex = file.pIndexOf(comment);
  949.     }
  950. #endif // wxUSE_INTL
  951.  
  952.     if ( nIndex == wxNOT_FOUND )
  953.     {
  954.         comment = _T("Comment=");
  955.         nIndex = file.pIndexOf(comment);
  956.     }
  957.  
  958.     if ( nIndex != wxNOT_FOUND ) mime_desc = file.GetCmd(nIndex);
  959.     //else: no description
  960.  
  961.     // next find the extensions
  962.     wxString mime_extension;
  963.  
  964.     nIndex = file.pIndexOf(_T("Patterns="));
  965.     if ( nIndex != wxNOT_FOUND )
  966.     {
  967.         wxString exts = file.GetCmd (nIndex);;
  968.  
  969.         wxStringTokenizer tokenizer(exts, _T(";"));
  970.         while ( tokenizer.HasMoreTokens() )
  971.         {
  972.             wxString e = tokenizer.GetNextToken();
  973.             if ( e.Left(2) != _T("*.") )
  974.                 continue; // don't support too difficult patterns
  975.  
  976.             if ( !mime_extension.empty() )
  977.             {
  978.                 // separate from the previous ext
  979.                 mime_extension << _T(' ');
  980.             }
  981.  
  982.             mime_extension << e.Mid(2);
  983.         }
  984.     }
  985.     sExts.Add(mime_extension);
  986.  
  987.  
  988.     // ok, now we can take care of icon:
  989.  
  990.     nIndex = file.pIndexOf(_T("Icon="));
  991.     if ( nIndex != wxNOT_FOUND )
  992.     {
  993.         strIcon = file.GetCmd(nIndex);
  994.         //it could be the real path, but more often a short name
  995.         if (!wxFileExists(strIcon))
  996.         {
  997.             // icon is just the short name
  998.             if ( !strIcon.empty() )
  999.             {
  1000.                 // we must check if the file exists because it may be stored
  1001.                 // in many locations, at least ~/.kde and $KDEDIR
  1002.                 size_t nDir, nDirs = icondirs.GetCount();
  1003.                 for ( nDir = 0; nDir < nDirs; nDir++ )
  1004.                     if (wxFileExists(icondirs[nDir] + strIcon))
  1005.                     {
  1006.                         strIcon.Prepend(icondirs[nDir]);
  1007.                         break;
  1008.                     }
  1009.             }
  1010.         }
  1011.     }
  1012.     // now look for lines which know about the application
  1013.     // exec= or DefaultApp=
  1014.  
  1015.     nIndex = file.pIndexOf(wxT("DefaultApp"));
  1016.  
  1017.     if ( nIndex == wxNOT_FOUND )
  1018.     {
  1019.         // no entry try exec
  1020.         nIndex = file.pIndexOf(wxT("Exec"));
  1021.     }
  1022.  
  1023.     if ( nIndex != wxNOT_FOUND )
  1024.     {
  1025.         wxString sTmp = file.GetCmd(nIndex);
  1026.         // we expect %f; others including  %F and %U and %u are possible
  1027.         if (0 == sTmp.Replace ( wxT("%f"), wxT("%s") ))
  1028.             sTmp = sTmp + wxT(" %s");
  1029.         entry->AddOrReplaceVerb (wxString(wxT("open")), sTmp );
  1030.     }
  1031.  
  1032.     AddToMimeData (mimetype, strIcon, entry, sExts, mime_desc);
  1033. }
  1034.  
  1035. void wxMimeTypesManagerImpl::LoadKDELinksForMimeType(const wxString& dirbase,
  1036.                                             const wxString& subdir,
  1037.                                             const wxArrayString& icondirs)
  1038. {
  1039.     wxString dirname = dirbase;
  1040.     dirname += subdir;
  1041.     wxDir dir(dirname);
  1042.     if ( !dir.IsOpened() )
  1043.         return;
  1044.  
  1045.     dirname += _T('/');
  1046.  
  1047.     wxString filename;
  1048.     bool cont = dir.GetFirst(&filename, _T("*.kdelnk"), wxDIR_FILES);
  1049.     while ( cont )
  1050.     {
  1051.         LoadKDELinksForMimeSubtype(dirname, subdir, filename, icondirs);
  1052.  
  1053.         cont = dir.GetNext(&filename);
  1054.     }
  1055.     // new standard for Gnome and KDE
  1056.     cont = dir.GetFirst(&filename, _T("*.desktop"), wxDIR_FILES);
  1057.     while ( cont )
  1058.     {
  1059.         LoadKDELinksForMimeSubtype(dirname, subdir, filename, icondirs);
  1060.  
  1061.         cont = dir.GetNext(&filename);
  1062.     }
  1063. }
  1064.  
  1065. void wxMimeTypesManagerImpl::LoadKDELinkFilesFromDir(const wxString& dirbase,
  1066.                                             const wxArrayString& icondirs)
  1067. {
  1068.     wxASSERT_MSG( !!dirbase && !wxEndsWithPathSeparator(dirbase),
  1069.                   _T("base directory shouldn't end with a slash") );
  1070.  
  1071.     wxString dirname = dirbase;
  1072.     dirname << _T("/mimelnk");
  1073.  
  1074.     if ( !wxDir::Exists(dirname) )
  1075.         return;
  1076.  
  1077.     wxDir dir(dirname);
  1078.     if ( !dir.IsOpened() )
  1079.         return;
  1080.  
  1081.     // we will concatenate it with dir name to get the full path below
  1082.     dirname += _T('/');
  1083.  
  1084.     wxString subdir;
  1085.     bool cont = dir.GetFirst(&subdir, wxEmptyString, wxDIR_DIRS);
  1086.     while ( cont )
  1087.     {
  1088.         LoadKDELinksForMimeType(dirname, subdir, icondirs);
  1089.  
  1090.         cont = dir.GetNext(&subdir);
  1091.     }
  1092. }
  1093.  
  1094. void wxMimeTypesManagerImpl::GetKDEMimeInfo(const wxString& sExtraDir)
  1095. {
  1096.     wxArrayString dirs;
  1097.     wxArrayString icondirs;
  1098.  
  1099.     // settings in ~/.kde have maximal priority
  1100.     dirs.Add(wxGetHomeDir() + wxT("/.kde/share"));
  1101.     icondirs.Add(wxGetHomeDir() + wxT("/.kde/share/icons/"));
  1102.  
  1103.     // the variable KDEDIR is set when KDE is running
  1104.     const wxChar *kdedir = wxGetenv( wxT("KDEDIR") );
  1105.     if ( kdedir )
  1106.     {
  1107.         dirs.Add( wxString(kdedir) + wxT("/share") );
  1108.         icondirs.Add( wxString(kdedir) + wxT("/share/icons/") );
  1109.     }
  1110.     else
  1111.     {
  1112.         // try to guess KDEDIR
  1113.         dirs.Add(_T("/usr/share"));
  1114.         dirs.Add(_T("/opt/kde/share"));
  1115.         icondirs.Add(_T("/usr/share/icons/"));
  1116.         icondirs.Add(_T("/usr/X11R6/share/icons/")); // Debian/Corel linux
  1117.         icondirs.Add(_T("/opt/kde/share/icons/"));
  1118.     }
  1119.  
  1120.     if (!sExtraDir.empty()) dirs.Add (sExtraDir);
  1121.     icondirs.Add(sExtraDir + wxT("/icons"));
  1122.  
  1123.     size_t nDirs = dirs.GetCount();
  1124.     for ( size_t nDir = 0; nDir < nDirs; nDir++ )
  1125.     {
  1126.         LoadKDELinkFilesFromDir(dirs[nDir], icondirs);
  1127.     }
  1128. }
  1129.  
  1130. // ----------------------------------------------------------------------------
  1131. // wxFileTypeImpl (Unix)
  1132. // ----------------------------------------------------------------------------
  1133.  
  1134. wxString wxFileTypeImpl::GetExpandedCommand(const wxString & verb, const wxFileType::MessageParameters& params) const
  1135. {
  1136.     wxString sTmp;
  1137.     size_t i = 0;
  1138.     while ( (i < m_index.GetCount() ) && sTmp.empty() )
  1139.     {
  1140.             sTmp = m_manager->GetCommand ( verb, m_index[i] );
  1141.             i ++;
  1142.     }
  1143.  
  1144.     return wxFileType::ExpandCommand(sTmp, params);
  1145. }
  1146.  
  1147. bool wxFileTypeImpl::GetIcon(wxIcon *icon,
  1148.                              wxString *iconFile /*= NULL */,
  1149.                              int *iconIndex /*= NULL*/) const
  1150.  
  1151. {
  1152. #if wxUSE_GUI
  1153.     wxString sTmp;
  1154.     size_t i = 0;
  1155.     while ( (i < m_index.GetCount() ) && sTmp.empty() )
  1156.     {
  1157.         sTmp = m_manager->m_aIcons[m_index[i]];
  1158.         i ++;
  1159.     }
  1160.     if ( sTmp.empty () ) return FALSE;
  1161.  
  1162.     wxIcon icn;
  1163.  
  1164.     if (sTmp.Right(4).MakeUpper() == _T(".XPM"))
  1165.         icn = wxIcon(sTmp);
  1166.     else
  1167.         icn = wxIcon(sTmp, wxBITMAP_TYPE_ANY);
  1168.  
  1169.     if ( icn.Ok() )
  1170.     {
  1171.         *icon = icn;
  1172.         if (iconFile) *iconFile = sTmp;
  1173.         if (iconIndex) *iconIndex = 0;
  1174.         return TRUE;
  1175.     }
  1176. #endif // wxUSE_GUI
  1177.  
  1178.     return FALSE;
  1179. }
  1180.  
  1181.  
  1182. bool
  1183. wxFileTypeImpl::GetMimeTypes(wxArrayString& mimeTypes) const
  1184. {
  1185.     mimeTypes.Clear();
  1186.     for (size_t i = 0; i < m_index.GetCount(); i++)
  1187.         mimeTypes.Add(m_manager->m_aTypes[m_index[i]]);
  1188.     return TRUE;
  1189. }
  1190.  
  1191.  
  1192. size_t wxFileTypeImpl::GetAllCommands(wxArrayString *verbs,
  1193.                                   wxArrayString *commands,
  1194.                                   const wxFileType::MessageParameters& params) const
  1195. {
  1196.  
  1197.     wxString vrb, cmd, sTmp;
  1198.     size_t count = 0;
  1199.     wxMimeTypeCommands * sPairs;
  1200.  
  1201.     // verbs and commands have been cleared already in mimecmn.cpp...
  1202.     // if we find no entries in the exact match, try the inexact match
  1203.     for (size_t n = 0; ((count ==0) && (n < m_index.GetCount())); n++)
  1204.     {
  1205.         // list of verb = command pairs for this mimetype
  1206.         sPairs = m_manager->m_aEntries [m_index[n]];
  1207.         size_t i;
  1208.         for ( i = 0; i < sPairs->GetCount (); i++ )
  1209.             {
  1210.                 vrb = sPairs->GetVerb(i);
  1211.                 // some gnome entries have . inside
  1212.                 vrb = vrb.AfterLast(wxT('.'));
  1213.                 cmd = sPairs->GetCmd (i);
  1214.                 if (! cmd.empty() )
  1215.                      {
  1216.                      cmd = wxFileType::ExpandCommand(cmd, params);
  1217.                      count ++;
  1218.                      if ( vrb.IsSameAs (wxT("open")))
  1219.                          {
  1220.                          verbs->Insert(vrb,0u);
  1221.                          commands ->Insert(cmd,0u);
  1222.                          }
  1223.                      else
  1224.                          {
  1225.                          verbs->Add (vrb);
  1226.                          commands->Add (cmd);
  1227.                          }
  1228.                      }
  1229.  
  1230.         }
  1231.  
  1232.     }
  1233.     return count;
  1234.  
  1235. }
  1236.  
  1237. bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
  1238. {
  1239.     wxString strExtensions = m_manager->GetExtension(m_index[0]);
  1240.     extensions.Empty();
  1241.  
  1242.     // one extension in the space or comma delimitid list
  1243.     wxString strExt;
  1244.     for ( const wxChar *p = strExtensions;; p++ ) {
  1245.         if ( *p == wxT(' ') || *p == wxT(',') || *p == wxT('\0') ) {
  1246.             if ( !strExt.empty() ) {
  1247.                 extensions.Add(strExt);
  1248.                 strExt.Empty();
  1249.             }
  1250.             //else: repeated spaces (shouldn't happen, but it's not that
  1251.             //      important if it does happen)
  1252.  
  1253.             if ( *p == wxT('\0') )
  1254.                 break;
  1255.         }
  1256.         else if ( *p == wxT('.') ) {
  1257.             // remove the dot from extension (but only if it's the first char)
  1258.             if ( !strExt.empty() ) {
  1259.                 strExt += wxT('.');
  1260.             }
  1261.             //else: no, don't append it
  1262.         }
  1263.         else {
  1264.             strExt += *p;
  1265.         }
  1266.     }
  1267.  
  1268.     return TRUE;
  1269. }
  1270.  
  1271. // set an arbitrary command,
  1272. // could adjust the code to ask confirmation if it already exists and
  1273. // overwriteprompt is TRUE, but this is currently ignored as *Associate* has
  1274. // no overwrite prompt
  1275. bool wxFileTypeImpl::SetCommand(const wxString& cmd, const wxString& verb, bool overwriteprompt /*= TRUE*/)
  1276. {
  1277.     wxArrayString strExtensions;
  1278.     wxString strDesc, strIcon;
  1279.  
  1280.     wxMimeTypeCommands *entry = new wxMimeTypeCommands ();
  1281.     entry->Add(verb + wxT("=")  + cmd + wxT(" %s "));
  1282.  
  1283.     wxArrayString strTypes;
  1284.     GetMimeTypes (strTypes);
  1285.     if (strTypes.GetCount() < 1) return FALSE;
  1286.  
  1287.     size_t i;
  1288.     bool Ok = TRUE;
  1289.     for (i = 0; i < strTypes.GetCount(); i++)
  1290.     {
  1291.         if (!m_manager->DoAssociation (strTypes[i], strIcon, entry, strExtensions, strDesc))
  1292.             Ok = FALSE;
  1293.     }
  1294.  
  1295.     return Ok;
  1296. }
  1297.  
  1298. // ignore index on the grouds that we only have one icon in a Unix file
  1299. bool wxFileTypeImpl::SetDefaultIcon(const wxString& strIcon /*= wxEmptyString*/, int /*index = 0*/)
  1300. {
  1301.     if (strIcon.empty()) return FALSE;
  1302.     wxArrayString strExtensions;
  1303.     wxString strDesc;
  1304.  
  1305.     wxMimeTypeCommands *entry = new wxMimeTypeCommands ();
  1306.  
  1307.     wxArrayString strTypes;
  1308.     GetMimeTypes (strTypes);
  1309.     if (strTypes.GetCount() < 1) return FALSE;
  1310.  
  1311.     size_t i;
  1312.     bool Ok = TRUE;
  1313.     for (i = 0; i < strTypes.GetCount(); i++)
  1314.     {
  1315.         if (!m_manager->DoAssociation (strTypes[i], strIcon, entry, strExtensions, strDesc))
  1316.             Ok = FALSE;
  1317.     }
  1318.  
  1319.     return Ok;
  1320. }
  1321.  
  1322. // ----------------------------------------------------------------------------
  1323. // wxMimeTypesManagerImpl (Unix)
  1324. // ----------------------------------------------------------------------------
  1325.  
  1326.  
  1327. wxMimeTypesManagerImpl::wxMimeTypesManagerImpl()
  1328. {
  1329.     m_initialized = FALSE;
  1330.     m_mailcapStylesInited = 0;
  1331. }
  1332.  
  1333. // read system and user mailcaps and other files
  1334. void wxMimeTypesManagerImpl::Initialize(int mailcapStyles,
  1335.                                         const wxString& sExtraDir)
  1336. {
  1337.     // read mimecap amd mime.types
  1338.     if ( (mailcapStyles & wxMAILCAP_NETSCAPE) ||
  1339.          (mailcapStyles & wxMAILCAP_STANDARD) )
  1340.         GetMimeInfo(sExtraDir);
  1341.  
  1342.     // read GNOME tables
  1343.     if ( mailcapStyles & wxMAILCAP_GNOME)
  1344.         GetGnomeMimeInfo(sExtraDir);
  1345.  
  1346.     // read KDE tables
  1347.     if ( mailcapStyles & wxMAILCAP_KDE)
  1348.         GetKDEMimeInfo(sExtraDir);
  1349.  
  1350.     m_mailcapStylesInited |= mailcapStyles;
  1351. }
  1352.  
  1353. // clear data so you can read another group of WM files
  1354. void wxMimeTypesManagerImpl::ClearData()
  1355. {
  1356.     m_aTypes.Clear ();
  1357.     m_aIcons.Clear ();
  1358.     m_aExtensions.Clear ();
  1359.     m_aDescriptions.Clear ();
  1360.  
  1361.     WX_CLEAR_ARRAY(m_aEntries);
  1362.     m_aEntries.Empty();
  1363.  
  1364.     m_mailcapStylesInited = 0;
  1365. }
  1366.  
  1367. wxMimeTypesManagerImpl::~wxMimeTypesManagerImpl()
  1368. {
  1369.     ClearData();
  1370. }
  1371.  
  1372.  
  1373. void wxMimeTypesManagerImpl::GetMimeInfo (const wxString& sExtraDir)
  1374. {
  1375.     // read this for netscape or Metamail formats
  1376.  
  1377.     // directories where we look for mailcap and mime.types by default
  1378.     // used by netscape and pine and other mailers, using 2 different formats!
  1379.  
  1380.     // (taken from metamail(1) sources)
  1381.     //
  1382.     // although RFC 1524 specifies the search path of
  1383.     // /etc/:/usr/etc:/usr/local/etc only, it doesn't hurt to search in more
  1384.     // places - OTOH, the RFC also says that this path can be changed with
  1385.     // MAILCAPS environment variable (containing the colon separated full
  1386.     // filenames to try) which is not done yet (TODO?)
  1387.  
  1388.     wxString strHome = wxGetenv(wxT("HOME"));
  1389.  
  1390.     wxArrayString dirs;
  1391.     dirs.Add ( strHome + wxT("/.") );
  1392.     dirs.Add ( wxT("/etc/") );
  1393.     dirs.Add ( wxT("/usr/etc/") );
  1394.     dirs.Add ( wxT("/usr/local/etc/") );
  1395.     dirs.Add ( wxT("/etc/mail/") );
  1396.     dirs.Add ( wxT("/usr/public/lib/") );
  1397.     if (!sExtraDir.empty()) dirs.Add ( sExtraDir + wxT("/") );
  1398.  
  1399.     size_t nDirs = dirs.GetCount();
  1400.     for ( size_t nDir = 0; nDir < nDirs; nDir++ )
  1401.     {
  1402.         wxString file = dirs[nDir] + wxT("mailcap");
  1403.         if ( wxFile::Exists(file) ) {
  1404.             ReadMailcap(file);
  1405.         }
  1406.  
  1407.         file = dirs[nDir] + wxT("mime.types");
  1408.         if ( wxFile::Exists(file) ) {
  1409.             ReadMimeTypes(file);
  1410.         }
  1411.     }
  1412.  
  1413. }
  1414.  
  1415. bool wxMimeTypesManagerImpl::WriteToMimeTypes (int index, bool delete_index)
  1416. {
  1417.     // check we have the right manager
  1418.     if (! ( m_mailcapStylesInited & wxMAILCAP_STANDARD) )
  1419.         return FALSE;
  1420.  
  1421.     bool bTemp;
  1422.     wxString strHome = wxGetenv(wxT("HOME"));
  1423.  
  1424.     // and now the users mailcap
  1425.     wxString strUserMailcap = strHome + wxT("/.mime.types");
  1426.  
  1427.     wxMimeTextFile file;
  1428.     if ( wxFile::Exists(strUserMailcap) )
  1429.     {
  1430.         bTemp =  file.Open(strUserMailcap);
  1431.     }
  1432.     else
  1433.     {
  1434.         if (delete_index) return FALSE;
  1435.         bTemp = file.Create(strUserMailcap);
  1436.     }
  1437.     if (bTemp)
  1438.     {
  1439.         int nIndex;
  1440.         // test for netscape's header and return FALSE if its found
  1441.         nIndex = file.pIndexOf (wxT("#--Netscape"));
  1442.         if (nIndex != wxNOT_FOUND)
  1443.         {
  1444.             wxASSERT_MSG(FALSE,wxT("Error in .mime.types \nTrying to mix Netscape and Metamail formats\nFile not modiifed"));
  1445.             return FALSE;
  1446.         }
  1447.         // write it in alternative format
  1448.         // get rid of unwanted entries
  1449.         wxString strType = m_aTypes[index];
  1450.         nIndex = file.pIndexOf (strType);
  1451.         // get rid of all the unwanted entries...
  1452.         if (nIndex != wxNOT_FOUND) file.CommentLine (nIndex);
  1453.  
  1454.         if (!delete_index)
  1455.         {
  1456.             // add the new entries in
  1457.             wxString sTmp = strType.Append (wxT(' '), 40-strType.Len() );
  1458.             sTmp = sTmp + m_aExtensions[index];
  1459.             file.AddLine (sTmp);
  1460.         }
  1461.  
  1462.  
  1463.         bTemp = file.Write ();
  1464.         file.Close ();
  1465.     }
  1466.     return bTemp;
  1467. }
  1468.  
  1469. bool wxMimeTypesManagerImpl::WriteToNSMimeTypes (int index, bool delete_index)
  1470. {
  1471.     //check we have the right managers
  1472.     if (! ( m_mailcapStylesInited & wxMAILCAP_NETSCAPE) )
  1473.         return FALSE;
  1474.  
  1475.     bool bTemp;
  1476.     wxString strHome = wxGetenv(wxT("HOME"));
  1477.  
  1478.     // and now the users mailcap
  1479.     wxString strUserMailcap = strHome + wxT("/.mime.types");
  1480.  
  1481.     wxMimeTextFile file;
  1482.     if ( wxFile::Exists(strUserMailcap) )
  1483.     {
  1484.         bTemp =  file.Open(strUserMailcap);
  1485.     }
  1486.     else
  1487.     {
  1488.         if (delete_index) return FALSE;
  1489.         bTemp = file.Create(strUserMailcap);
  1490.     }
  1491.     if (bTemp)
  1492.     {
  1493.  
  1494.         // write it in the format that Netscape uses
  1495.         int nIndex;
  1496.         // test for netscape's header and insert if required...
  1497.         // this is a comment so use TRUE
  1498.         nIndex = file.pIndexOf (wxT("#--Netscape"), TRUE);
  1499.         if (nIndex == wxNOT_FOUND)
  1500.         {
  1501.             // either empty file or metamail format
  1502.             // at present we can't cope with mixed formats, so exit to preseve
  1503.             // metamail entreies
  1504.             if (file.GetLineCount () > 0)
  1505.             {
  1506.                 wxASSERT_MSG(FALSE, wxT(".mime.types File not in Netscape format\nNo entries written to\n.mime.types or to .mailcap"));
  1507.                 return FALSE;
  1508.             }
  1509.             file.InsertLine (wxT( "#--Netscape Communications Corporation MIME Information" ), 0);
  1510.             nIndex = 0;
  1511.         }
  1512.  
  1513.         wxString strType = wxT("type=") + m_aTypes[index];
  1514.         nIndex = file.pIndexOf (strType);
  1515.         // get rid of all the unwanted entries...
  1516.         if (nIndex != wxNOT_FOUND)
  1517.         {
  1518.             wxString sOld = file[nIndex];
  1519.             while ( (sOld.Contains(wxT("\\"))) && (nIndex < (int) file.GetLineCount()) )
  1520.             {
  1521.                 file.CommentLine(nIndex);
  1522.                 sOld = file[nIndex];
  1523.                 wxLogTrace(TRACE_MIME, wxT("--- Deleting from mime.types line '%d %s' ---"), nIndex, sOld.c_str());
  1524.                 nIndex ++;
  1525.             }
  1526.             if (nIndex < (int) file.GetLineCount()) file.CommentLine (nIndex);
  1527.         }
  1528.         else nIndex = (int) file.GetLineCount();
  1529.  
  1530.         wxString sTmp = strType + wxT(" \\");
  1531.         if (!delete_index) file.InsertLine (sTmp, nIndex);
  1532.         if ( ! m_aDescriptions.Item(index).empty() )
  1533.         {
  1534.             sTmp =     wxT("desc=\"") + m_aDescriptions[index]+ wxT("\" \\"); //.trim ??
  1535.             if (!delete_index)
  1536.             {
  1537.                 nIndex ++;
  1538.                 file.InsertLine (sTmp, nIndex);
  1539.             }
  1540.         }
  1541.         wxString sExts =  m_aExtensions.Item(index);
  1542.         sTmp =     wxT("exts=\"") + sExts.Trim(FALSE).Trim() + wxT("\"");
  1543.         if (!delete_index)
  1544.         {
  1545.             nIndex ++;
  1546.             file.InsertLine (sTmp, nIndex);
  1547.         }
  1548.  
  1549.         bTemp = file.Write ();
  1550.         file.Close ();
  1551.     }
  1552.     return bTemp;
  1553. }
  1554.  
  1555.  
  1556. bool wxMimeTypesManagerImpl::WriteToMailCap (int index, bool delete_index)
  1557. {
  1558.     //check we have the right managers
  1559.     if ( !( ( m_mailcapStylesInited & wxMAILCAP_NETSCAPE) ||
  1560.             ( m_mailcapStylesInited & wxMAILCAP_STANDARD) ) )
  1561.         return FALSE;
  1562.  
  1563.     bool bTemp;
  1564.     wxString strHome = wxGetenv(wxT("HOME"));
  1565.  
  1566.     // and now the users mailcap
  1567.     wxString strUserMailcap = strHome + wxT("/.mailcap");
  1568.  
  1569.     wxMimeTextFile file;
  1570.     if ( wxFile::Exists(strUserMailcap) )
  1571.     {
  1572.         bTemp =  file.Open(strUserMailcap);
  1573.     }
  1574.     else
  1575.     {
  1576.         if (delete_index) return FALSE;
  1577.         bTemp = file.Create(strUserMailcap);
  1578.     }
  1579.     if (bTemp)
  1580.     {
  1581.         // now got a file we can write to ....
  1582.         wxMimeTypeCommands * entries = m_aEntries[index];
  1583.         size_t iOpen;
  1584.         wxString sCmd = entries->GetCommandForVerb(_T("open"), &iOpen);
  1585.         wxString sTmp;
  1586.  
  1587.         sTmp = m_aTypes[index];
  1588.         wxString sOld;
  1589.         int nIndex = file.pIndexOf(sTmp);
  1590.         // get rid of all the unwanted entries...
  1591.         if (nIndex == wxNOT_FOUND)
  1592.         {
  1593.             nIndex = (int) file.GetLineCount();
  1594.         }
  1595.         else
  1596.         {
  1597.             sOld = file[nIndex];
  1598.             wxLogTrace(TRACE_MIME, wxT("--- Deleting from mailcap line '%d' ---"), nIndex);
  1599.  
  1600.             while ( (sOld.Contains(wxT("\\"))) && (nIndex < (int) file.GetLineCount()) )
  1601.             {
  1602.                 file.CommentLine(nIndex);
  1603.                 if (nIndex < (int) file.GetLineCount()) sOld = sOld + file[nIndex];
  1604.             }
  1605.             if (nIndex < (int) file.GetLineCount()) file.CommentLine (nIndex);
  1606.         }
  1607.  
  1608.         sTmp = sTmp + wxT(";") + sCmd; //includes wxT(" %s ");
  1609.  
  1610.         // write it in the format that Netscape uses (default)
  1611.         if (! ( m_mailcapStylesInited & wxMAILCAP_STANDARD  ) )
  1612.         {
  1613.             if (! delete_index) file.InsertLine (sTmp, nIndex);
  1614.             nIndex ++;
  1615.         }
  1616.  
  1617.         // write extended format
  1618.         else
  1619.         {
  1620.             // todo FIX this code;
  1621.             // ii) lost entries
  1622.             // sOld holds all the entries, but our data store only has some
  1623.             // eg test= is not stored
  1624.  
  1625.             // so far we have written the mimetype and command out
  1626.             wxStringTokenizer sT (sOld, wxT(";\\"));
  1627.             if (sT.CountTokens () > 2)
  1628.             {
  1629.                 // first one mimetype; second one command, rest unknown...
  1630.                 wxString s;
  1631.                 s = sT.GetNextToken();
  1632.                 s = sT.GetNextToken();
  1633.  
  1634.                 // first unknown
  1635.                 s = sT.GetNextToken();
  1636.                 while ( ! s.empty() )
  1637.                 {
  1638.                     bool bKnownToken = FALSE;
  1639.                     if (s.Contains(wxT("description="))) bKnownToken = TRUE;
  1640.                     if (s.Contains(wxT("x11-bitmap="))) bKnownToken = TRUE;
  1641.                     size_t i;
  1642.                     for (i=0; i < entries->GetCount(); i++)
  1643.                     {
  1644.                         if (s.Contains(entries->GetVerb(i))) bKnownToken = TRUE;
  1645.                     }
  1646.                     if (!bKnownToken)
  1647.                     {
  1648.                         sTmp = sTmp + wxT("; \\");
  1649.                         file.InsertLine (sTmp, nIndex);
  1650.                         sTmp = s;
  1651.                     }
  1652.                     s = sT.GetNextToken ();
  1653.                 }
  1654.  
  1655.             }
  1656.  
  1657.             if (! m_aDescriptions[index].empty() )
  1658.             {
  1659.                 sTmp = sTmp + wxT("; \\");
  1660.                 file.InsertLine (sTmp, nIndex);
  1661.                 nIndex ++;
  1662.                 sTmp = wxT("       description=\"") + m_aDescriptions[index] + wxT("\"");
  1663.             }
  1664.  
  1665.             if (! m_aIcons[index].empty() )
  1666.             {
  1667.                 sTmp = sTmp + wxT("; \\");
  1668.                 file.InsertLine (sTmp, nIndex);
  1669.                 nIndex ++;
  1670.                 sTmp = wxT("       x11-bitmap=\"") + m_aIcons[index] + wxT("\"");
  1671.             }
  1672.             if ( entries->GetCount() > 1 )
  1673.  
  1674.             {
  1675.                 size_t i;
  1676.                 for (i=0; i < entries->GetCount(); i++)
  1677.                     if ( i != iOpen )
  1678.                     {
  1679.                         sTmp = sTmp + wxT("; \\");
  1680.                         file.InsertLine (sTmp, nIndex);
  1681.                         nIndex ++;
  1682.                         sTmp = wxT("       ") + entries->GetVerbCmd(i);
  1683.                     }
  1684.             }
  1685.  
  1686.             file.InsertLine (sTmp, nIndex);
  1687.             nIndex ++;
  1688.  
  1689.         }
  1690.         bTemp = file.Write ();
  1691.         file.Close ();
  1692.     }
  1693.     return bTemp;
  1694. }
  1695.  
  1696. wxFileType *
  1697. wxMimeTypesManagerImpl::Associate(const wxFileTypeInfo& ftInfo)
  1698. {
  1699.     InitIfNeeded();
  1700.  
  1701.     wxString strType = ftInfo.GetMimeType ();
  1702.     wxString strDesc = ftInfo.GetDescription ();
  1703.     wxString strIcon = ftInfo.GetIconFile ();
  1704.  
  1705.     wxMimeTypeCommands *entry = new wxMimeTypeCommands ();
  1706.  
  1707.     if ( ! ftInfo.GetOpenCommand().empty())
  1708.         entry->Add(wxT("open=")  + ftInfo.GetOpenCommand  () + wxT(" %s "));
  1709.     if ( ! ftInfo.GetPrintCommand  ().empty())
  1710.         entry->Add(wxT("print=") + ftInfo.GetPrintCommand () + wxT(" %s "));
  1711.  
  1712.     // now find where these extensions are in the data store and remove them
  1713.     wxArrayString sA_Exts = ftInfo.GetExtensions ();
  1714.     wxString sExt, sExtStore;
  1715.     size_t i, nIndex;
  1716.     for (i=0; i < sA_Exts.GetCount(); i++)
  1717.         {
  1718.         sExt = sA_Exts.Item(i);
  1719.         //clean up to just a space before and after
  1720.         sExt.Trim().Trim(FALSE);
  1721.         sExt = wxT(' ') + sExt + wxT(' ');
  1722.         for (nIndex = 0; nIndex < m_aExtensions.GetCount(); nIndex ++)
  1723.             {
  1724.             sExtStore = m_aExtensions.Item(nIndex);
  1725.             if (sExtStore.Replace(sExt, wxT(" ") ) > 0) m_aExtensions.Item(nIndex) = sExtStore;
  1726.     }
  1727.  
  1728.     }
  1729.  
  1730.     if ( !DoAssociation (strType, strIcon, entry, sA_Exts, strDesc) )
  1731.         return NULL;
  1732.  
  1733.     return GetFileTypeFromMimeType(strType);
  1734. }
  1735.  
  1736.  
  1737. bool wxMimeTypesManagerImpl::DoAssociation(const wxString& strType,
  1738.                                            const wxString& strIcon,
  1739.                                            wxMimeTypeCommands *entry,
  1740.                                            const wxArrayString& strExtensions,
  1741.                                            const wxString& strDesc)
  1742. {
  1743.     int nIndex = AddToMimeData(strType, strIcon, entry, strExtensions, strDesc, TRUE);
  1744.  
  1745.     if ( nIndex == wxNOT_FOUND )
  1746.         return FALSE;
  1747.  
  1748.     return WriteMimeInfo (nIndex, FALSE);
  1749. }
  1750.  
  1751. bool wxMimeTypesManagerImpl::WriteMimeInfo(int nIndex, bool delete_mime )
  1752. {
  1753.     bool ok = TRUE;
  1754.  
  1755.     if ( m_mailcapStylesInited & wxMAILCAP_STANDARD )
  1756.     {
  1757.         // write in metamail  format;
  1758.         if (WriteToMimeTypes (nIndex, delete_mime) )
  1759.             if ( WriteToMailCap   (nIndex, delete_mime) )
  1760.                 ok = FALSE;
  1761.     }
  1762.     if ( m_mailcapStylesInited & wxMAILCAP_NETSCAPE )
  1763.     {
  1764.         // write in netsacpe format;
  1765.         if (WriteToNSMimeTypes (nIndex, delete_mime) )
  1766.             if ( WriteToMailCap   (nIndex, delete_mime) )
  1767.                 ok = FALSE;
  1768.     }
  1769.     if (m_mailcapStylesInited & wxMAILCAP_GNOME)
  1770.     {
  1771.         // write in Gnome format;
  1772.         if (WriteGnomeMimeFile (nIndex, delete_mime) )
  1773.             if (WriteGnomeKeyFile   (nIndex, delete_mime) )
  1774.                 ok = FALSE;
  1775.     }
  1776.     if (m_mailcapStylesInited & wxMAILCAP_KDE)
  1777.     {
  1778.         // write in KDE format;
  1779.         if (WriteKDEMimeFile (nIndex, delete_mime) )
  1780.             ok = FALSE;
  1781.     }
  1782.  
  1783.     return ok;
  1784. }
  1785.  
  1786. int wxMimeTypesManagerImpl::AddToMimeData(const wxString& strType,
  1787.                                           const wxString& strIcon,
  1788.                                           wxMimeTypeCommands *entry,
  1789.                                           const wxArrayString& strExtensions,
  1790.                                           const wxString& strDesc,
  1791.                                           bool replaceExisting)
  1792. {
  1793.     InitIfNeeded();
  1794.  
  1795.     // ensure mimetype is always lower case
  1796.     wxString mimeType = strType.Lower();
  1797.  
  1798.     // is this a known MIME type?
  1799.     int nIndex = m_aTypes.Index(mimeType);
  1800.     if ( nIndex == wxNOT_FOUND )
  1801.     {
  1802.         // new file type
  1803.         m_aTypes.Add(mimeType);
  1804.         m_aIcons.Add(strIcon);
  1805.         m_aEntries.Add(entry ? entry : new wxMimeTypeCommands);
  1806.  
  1807.         // change nIndex so we can use it below to add the extensions
  1808.         nIndex = m_aExtensions.Add(wxEmptyString);
  1809.  
  1810.         m_aDescriptions.Add(strDesc);
  1811.     }
  1812.     else // yes, we already have it
  1813.     {
  1814.         if ( replaceExisting )
  1815.         {
  1816.             // if new description change it
  1817.             if ( !strDesc.empty())
  1818.                 m_aDescriptions[nIndex] = strDesc;
  1819.  
  1820.             // if new icon change it
  1821.             if ( !strIcon.empty())
  1822.                 m_aIcons[nIndex] = strIcon;
  1823.  
  1824.             if ( entry )
  1825.             {
  1826.                 delete m_aEntries[nIndex];
  1827.                 m_aEntries[nIndex] = entry;
  1828.             }
  1829.         }
  1830.         else // add data we don't already have ...
  1831.         {
  1832.             // if new description add only if none
  1833.             if ( m_aDescriptions[nIndex].empty() )
  1834.                 m_aDescriptions[nIndex] = strDesc;
  1835.  
  1836.             // if new icon and no existing icon
  1837.             if ( m_aIcons[nIndex].empty () )
  1838.                 m_aIcons[nIndex] = strIcon;
  1839.  
  1840.             // add any new entries...
  1841.             if ( entry )
  1842.             {
  1843.                 wxMimeTypeCommands *entryOld  =  m_aEntries[nIndex];
  1844.  
  1845.                 size_t count = entry->GetCount();
  1846.                 for ( size_t i = 0; i < count; i++ )
  1847.                 {
  1848.                     const wxString& verb = entry->GetVerb(i);
  1849.                     if ( !entryOld->HasVerb(verb) )
  1850.                     {
  1851.                         entryOld->AddOrReplaceVerb(verb, entry->GetCmd(i));
  1852.                     }
  1853.                 }
  1854.             }
  1855.         }
  1856.     }
  1857.  
  1858.     // always add the extensions to this mimetype
  1859.     wxString& exts = m_aExtensions[nIndex];
  1860.  
  1861.     // add all extensions we don't have yet
  1862.     size_t count = strExtensions.GetCount();
  1863.     for ( size_t i = 0; i < count; i++ )
  1864.     {
  1865.         wxString ext = strExtensions[i] + _T(' ');
  1866.  
  1867.         if ( exts.Find(ext) == wxNOT_FOUND )
  1868.         {
  1869.             exts += ext;
  1870.         }
  1871.     }
  1872.  
  1873.     // check data integrity
  1874.     wxASSERT( m_aTypes.Count() == m_aEntries.Count() &&
  1875.               m_aTypes.Count() == m_aExtensions.Count() &&
  1876.               m_aTypes.Count() == m_aIcons.Count() &&
  1877.               m_aTypes.Count() == m_aDescriptions.Count() );
  1878.  
  1879.     return nIndex;
  1880. }
  1881.  
  1882.  
  1883. wxFileType *
  1884. wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& ext)
  1885. {
  1886.     if (ext.empty() )
  1887.         return NULL;
  1888.  
  1889.     InitIfNeeded();
  1890.  
  1891.     size_t count = m_aExtensions.GetCount();
  1892.     for ( size_t n = 0; n < count; n++ )
  1893.     {
  1894.         wxStringTokenizer tk(m_aExtensions[n], _T(' '));
  1895.  
  1896.         while ( tk.HasMoreTokens() )
  1897.         {
  1898.             // consider extensions as not being case-sensitive
  1899.             if ( tk.GetNextToken().IsSameAs(ext, FALSE /* no case */) )
  1900.             {
  1901.                 // found
  1902.                 wxFileType *fileType = new wxFileType;
  1903.                 fileType->m_impl->Init(this, n);
  1904.  
  1905.                 return fileType;
  1906.             }
  1907.         }
  1908.     }
  1909.  
  1910.     return NULL;
  1911. }
  1912.  
  1913. wxFileType *
  1914. wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
  1915. {
  1916.     InitIfNeeded();
  1917.  
  1918.     wxFileType * fileType = NULL;
  1919.     // mime types are not case-sensitive
  1920.     wxString mimetype(mimeType);
  1921.     mimetype.MakeLower();
  1922.  
  1923.     // first look for an exact match
  1924.     int index = m_aTypes.Index(mimetype);
  1925.     if ( index != wxNOT_FOUND )
  1926.     {
  1927.         fileType = new wxFileType;
  1928.         fileType->m_impl->Init(this, index);
  1929.     }
  1930.  
  1931.     // then try to find "text/*" as match for "text/plain" (for example)
  1932.     // NB: if mimeType doesn't contain '/' at all, BeforeFirst() will return
  1933.     //     the whole string - ok.
  1934.  
  1935.     index = wxNOT_FOUND;
  1936.     wxString strCategory = mimetype.BeforeFirst(wxT('/'));
  1937.  
  1938.     size_t nCount = m_aTypes.Count();
  1939.     for ( size_t n = 0; n < nCount; n++ ) {
  1940.         if ( (m_aTypes[n].BeforeFirst(wxT('/')) == strCategory ) &&
  1941.                 m_aTypes[n].AfterFirst(wxT('/')) == wxT("*") ) {
  1942.             index = n;
  1943.             break;
  1944.         }
  1945.  
  1946.     }
  1947.  
  1948.     if ( index != wxNOT_FOUND )
  1949.     {
  1950.         fileType = new wxFileType;
  1951.         fileType->m_impl->Init(this, index);
  1952.     }
  1953.     return fileType;
  1954. }
  1955.  
  1956.  
  1957. wxString wxMimeTypesManagerImpl::GetCommand(const wxString & verb, size_t nIndex) const
  1958. {
  1959.     wxString command, testcmd, sV, sTmp;
  1960.     sV = verb + wxT("=");
  1961.     // list of verb = command pairs for this mimetype
  1962.     wxMimeTypeCommands * sPairs = m_aEntries [nIndex];
  1963.  
  1964.     size_t i;
  1965.     for ( i = 0; i < sPairs->GetCount (); i++ )
  1966.     {
  1967.         sTmp = sPairs->GetVerbCmd (i);
  1968.         if ( sTmp.Contains(sV) )
  1969.             command = sTmp.AfterFirst(wxT('='));
  1970.     }
  1971.     return command;
  1972. }
  1973.  
  1974. void wxMimeTypesManagerImpl::AddFallback(const wxFileTypeInfo& filetype)
  1975. {
  1976.     InitIfNeeded();
  1977.  
  1978.     wxString extensions;
  1979.     const wxArrayString& exts = filetype.GetExtensions();
  1980.     size_t nExts = exts.GetCount();
  1981.     for ( size_t nExt = 0; nExt < nExts; nExt++ ) {
  1982.         if ( nExt > 0 ) {
  1983.             extensions += wxT(' ');
  1984.         }
  1985.         extensions += exts[nExt];
  1986.     }
  1987.  
  1988.     AddMimeTypeInfo(filetype.GetMimeType(),
  1989.                     extensions,
  1990.                     filetype.GetDescription());
  1991.  
  1992.     AddMailcapInfo(filetype.GetMimeType(),
  1993.                    filetype.GetOpenCommand(),
  1994.                    filetype.GetPrintCommand(),
  1995.                    wxT(""),
  1996.                    filetype.GetDescription());
  1997. }
  1998.  
  1999. void wxMimeTypesManagerImpl::AddMimeTypeInfo(const wxString& strMimeType,
  2000.                                              const wxString& strExtensions,
  2001.                                              const wxString& strDesc)
  2002. {
  2003.     // reading mailcap may find image/* , while
  2004.     // reading mime.types finds image/gif and no match is made
  2005.     // this means all the get functions don't work  fix this
  2006.     wxString strIcon;
  2007.     wxString sTmp = strExtensions;
  2008.  
  2009.     wxArrayString sExts;
  2010.     sTmp.Trim().Trim(FALSE);
  2011.  
  2012.     while (!sTmp.empty())
  2013.     {
  2014.         sExts.Add (sTmp.AfterLast(wxT(' ')));
  2015.         sTmp = sTmp.BeforeLast(wxT(' '));
  2016.     }
  2017.  
  2018.     AddToMimeData (strMimeType, strIcon, NULL, sExts, strDesc, TRUE);
  2019. }
  2020.  
  2021. void wxMimeTypesManagerImpl::AddMailcapInfo(const wxString& strType,
  2022.                                             const wxString& strOpenCmd,
  2023.                                             const wxString& strPrintCmd,
  2024.                                             const wxString& strTest,
  2025.                                             const wxString& strDesc)
  2026. {
  2027.     InitIfNeeded();
  2028.  
  2029.     wxMimeTypeCommands *entry = new wxMimeTypeCommands;
  2030.     entry->Add(wxT("open=")  + strOpenCmd);
  2031.     entry->Add(wxT("print=") + strPrintCmd);
  2032.     entry->Add(wxT("test=")  + strTest);
  2033.  
  2034.     wxString strIcon;
  2035.     wxArrayString strExtensions;
  2036.  
  2037.     AddToMimeData (strType, strIcon, entry, strExtensions, strDesc, TRUE);
  2038.  
  2039. }
  2040.  
  2041. bool wxMimeTypesManagerImpl::ReadMimeTypes(const wxString& strFileName)
  2042. {
  2043.     wxLogTrace(TRACE_MIME, wxT("--- Parsing mime.types file '%s' ---"),
  2044.                strFileName.c_str());
  2045.  
  2046.     wxTextFile file(strFileName);
  2047. #if defined(__WXGTK20__) && wxUSE_UNICODE
  2048.     if ( !file.Open( wxConvUTF8) )
  2049. #else
  2050.     if ( !file.Open() )
  2051. #endif
  2052.         return FALSE;
  2053.  
  2054.     // the information we extract
  2055.     wxString strMimeType, strDesc, strExtensions;
  2056.  
  2057.     size_t nLineCount = file.GetLineCount();
  2058.     const wxChar *pc = NULL;
  2059.     for ( size_t nLine = 0; nLine < nLineCount; nLine++ )
  2060.     {
  2061.         if ( pc == NULL ) {
  2062.             // now we're at the start of the line
  2063.             pc = file[nLine].c_str();
  2064.         }
  2065.         else {
  2066.             // we didn't finish with the previous line yet
  2067.             nLine--;
  2068.         }
  2069.  
  2070.         // skip whitespace
  2071.         while ( wxIsspace(*pc) )
  2072.             pc++;
  2073.  
  2074.         // comment or blank line?
  2075.         if ( *pc == wxT('#') || !*pc ) {
  2076.             // skip the whole line
  2077.             pc = NULL;
  2078.             continue;
  2079.         }
  2080.  
  2081.         // detect file format
  2082.         const wxChar *pEqualSign = wxStrchr(pc, wxT('='));
  2083.         if ( pEqualSign == NULL ) {
  2084.             // brief format
  2085.             // ------------
  2086.  
  2087.             // first field is mime type
  2088.             for ( strMimeType.Empty(); !wxIsspace(*pc) && *pc != wxT('\0'); pc++ ) {
  2089.                 strMimeType += *pc;
  2090.             }
  2091.  
  2092.             // skip whitespace
  2093.             while ( wxIsspace(*pc) )
  2094.                 pc++;
  2095.  
  2096.             // take all the rest of the string
  2097.             strExtensions = pc;
  2098.  
  2099.             // no description...
  2100.             strDesc.Empty();
  2101.         }
  2102.         else {
  2103.             // expanded format
  2104.             // ---------------
  2105.  
  2106.             // the string on the left of '=' is the field name
  2107.             wxString strLHS(pc, pEqualSign - pc);
  2108.  
  2109.             // eat whitespace
  2110.             for ( pc = pEqualSign + 1; wxIsspace(*pc); pc++ )
  2111.               ;
  2112.  
  2113.             const wxChar *pEnd;
  2114.             if ( *pc == wxT('"') ) {
  2115.                 // the string is quoted and ends at the matching quote
  2116.                 pEnd = wxStrchr(++pc, wxT('"'));
  2117.                 if ( pEnd == NULL ) {
  2118.                     wxLogWarning(_("Mime.types file %s, line %d: unterminated "
  2119.                                    "quoted string."),
  2120.                                  strFileName.c_str(), nLine + 1);
  2121.                 }
  2122.             }
  2123.             else {
  2124.                 // unquoted string ends at the first space or at the end of
  2125.                 // line
  2126.                 for ( pEnd = pc; *pEnd && !wxIsspace(*pEnd); pEnd++ )
  2127.                   ;
  2128.             }
  2129.  
  2130.             // now we have the RHS (field value)
  2131.             wxString strRHS(pc, pEnd - pc);
  2132.  
  2133.             // check what follows this entry
  2134.             if ( *pEnd == wxT('"') ) {
  2135.                 // skip this quote
  2136.                 pEnd++;
  2137.             }
  2138.  
  2139.             for ( pc = pEnd; wxIsspace(*pc); pc++ )
  2140.               ;
  2141.  
  2142.             // if there is something left, it may be either a '\\' to continue
  2143.             // the line or the next field of the same entry
  2144.             bool entryEnded = *pc == wxT('\0'),
  2145.                  nextFieldOnSameLine = FALSE;
  2146.             if ( !entryEnded ) {
  2147.                 nextFieldOnSameLine = ((*pc != wxT('\\')) || (pc[1] != wxT('\0')));
  2148.             }
  2149.  
  2150.             // now see what we got
  2151.             if ( strLHS == wxT("type") ) {
  2152.                 strMimeType = strRHS;
  2153.             }
  2154.             else if ( strLHS.StartsWith(wxT("desc")) ) {
  2155.                 strDesc = strRHS;
  2156.             }
  2157.             else if ( strLHS == wxT("exts") ) {
  2158.                 strExtensions = strRHS;
  2159.             }
  2160.             else {
  2161.                 // this one is simply ignored: it usually refers to Netscape
  2162.                 // built in icons which are useless for us anyhow
  2163.                 if ( strLHS != _T("icon") )
  2164.                 {
  2165.                     wxLogWarning(_("Unknown field in file %s, line %d: '%s'."),
  2166.                                  strFileName.c_str(), nLine + 1, strLHS.c_str());
  2167.                 }
  2168.             }
  2169.  
  2170.             if ( !entryEnded ) {
  2171.                 if ( !nextFieldOnSameLine )
  2172.                     pc = NULL;
  2173.                 //else: don't reset it
  2174.  
  2175.                 // as we don't reset strMimeType, the next field in this entry
  2176.                 // will be interpreted correctly.
  2177.  
  2178.                 continue;
  2179.             }
  2180.         }
  2181.  
  2182.         // depending on the format (Mosaic or Netscape) either space or comma
  2183.         // is used to separate the extensions
  2184.         strExtensions.Replace(wxT(","), wxT(" "));
  2185.  
  2186.         // also deal with the leading dot
  2187.         if ( !strExtensions.empty() && strExtensions[0u] == wxT('.') )
  2188.         {
  2189.             strExtensions.erase(0, 1);
  2190.         }
  2191.  
  2192.         wxLogTrace(TRACE_MIME, wxT("mime.types: '%s' => '%s' (%s)"),
  2193.                    strExtensions.c_str(),
  2194.                    strMimeType.c_str(),
  2195.                    strDesc.c_str());
  2196.  
  2197.         AddMimeTypeInfo(strMimeType, strExtensions, strDesc);
  2198.  
  2199.         // finished with this line
  2200.         pc = NULL;
  2201.     }
  2202.  
  2203.     return TRUE;
  2204. }
  2205.  
  2206. // ----------------------------------------------------------------------------
  2207. // UNIX mailcap files parsing
  2208. // ----------------------------------------------------------------------------
  2209.  
  2210. // the data for a single MIME type
  2211. struct MailcapLineData
  2212. {
  2213.     // field values
  2214.     wxString type,
  2215.              cmdOpen,
  2216.              test,
  2217.              icon,
  2218.              desc;
  2219.  
  2220.     wxArrayString verbs,
  2221.                   commands;
  2222.  
  2223.     // flags
  2224.     bool testfailed,
  2225.          needsterminal,
  2226.          copiousoutput;
  2227.  
  2228.     MailcapLineData() { testfailed = needsterminal = copiousoutput = FALSE; }
  2229. };
  2230.  
  2231. // process a non-standard (i.e. not the first or second one) mailcap field
  2232. bool
  2233. wxMimeTypesManagerImpl::ProcessOtherMailcapField(MailcapLineData& data,
  2234.                                                  const wxString& curField)
  2235. {
  2236.     if ( curField.empty() )
  2237.     {
  2238.         // we don't care
  2239.         return TRUE;
  2240.     }
  2241.  
  2242.     // is this something of the form foo=bar?
  2243.     const wxChar *pEq = wxStrchr(curField, wxT('='));
  2244.     if ( pEq != NULL )
  2245.     {
  2246.         // split "LHS = RHS" in 2
  2247.         wxString lhs = curField.BeforeFirst(wxT('=')),
  2248.                  rhs = curField.AfterFirst(wxT('='));
  2249.  
  2250.         lhs.Trim(TRUE);     // from right
  2251.         rhs.Trim(FALSE);    // from left
  2252.  
  2253.         // it might be quoted
  2254.         if ( !rhs.empty() && rhs[0u] == wxT('"') && rhs.Last() == wxT('"') )
  2255.         {
  2256.             rhs = rhs.Mid(1, rhs.length() - 2);
  2257.         }
  2258.  
  2259.         // is it a command verb or something else?
  2260.         if ( lhs == wxT("test") )
  2261.         {
  2262.             if ( wxSystem(rhs) == 0 )
  2263.             {
  2264.                 // ok, test passed
  2265.                 wxLogTrace(TRACE_MIME_TEST,
  2266.                            wxT("Test '%s' for mime type '%s' succeeded."),
  2267.                            rhs.c_str(), data.type.c_str());
  2268.  
  2269.             }
  2270.             else
  2271.             {
  2272.                 wxLogTrace(TRACE_MIME_TEST,
  2273.                            wxT("Test '%s' for mime type '%s' failed, skipping."),
  2274.                            rhs.c_str(), data.type.c_str());
  2275.  
  2276.                 data.testfailed = TRUE;
  2277.             }
  2278.         }
  2279.         else if ( lhs == wxT("desc") )
  2280.         {
  2281.             data.desc = rhs;
  2282.         }
  2283.         else if ( lhs == wxT("x11-bitmap") )
  2284.         {
  2285.             data.icon = rhs;
  2286.         }
  2287.         else if ( lhs == wxT("notes") )
  2288.         {
  2289.             // ignore
  2290.         }
  2291.         else // not a (recognized) special case, must be a verb (e.g. "print")
  2292.         {
  2293.             data.verbs.Add(lhs);
  2294.             data.commands.Add(rhs);
  2295.         }
  2296.     }
  2297.     else // '=' not found
  2298.     {
  2299.         // so it must be a simple flag
  2300.         if ( curField == wxT("needsterminal") )
  2301.         {
  2302.             data.needsterminal = TRUE;
  2303.         }
  2304.         else if ( curField == wxT("copiousoutput"))
  2305.         {
  2306.             // copiousoutput impies that the viewer is a console program
  2307.             data.needsterminal =
  2308.             data.copiousoutput = TRUE;
  2309.         }
  2310.         else if ( !IsKnownUnimportantField(curField) )
  2311.         {
  2312.             return FALSE;
  2313.         }
  2314.     }
  2315.  
  2316.     return TRUE;
  2317. }
  2318.  
  2319. bool wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName,
  2320.                                          bool fallback)
  2321. {
  2322.     wxLogTrace(TRACE_MIME, wxT("--- Parsing mailcap file '%s' ---"),
  2323.                strFileName.c_str());
  2324.  
  2325.     wxTextFile file(strFileName);
  2326. #if defined(__WXGTK20__) && wxUSE_UNICODE
  2327.     if ( !file.Open( wxConvUTF8) )
  2328. #else
  2329.     if ( !file.Open() )
  2330. #endif
  2331.         return FALSE;
  2332.  
  2333.     // indices of MIME types (in m_aTypes) we already found in this file
  2334.     //
  2335.     // (see the comments near the end of function for the reason we need this)
  2336.     wxArrayInt aIndicesSeenHere;
  2337.  
  2338.     // accumulator for the current field
  2339.     wxString curField;
  2340.     curField.reserve(1024);
  2341.  
  2342.     size_t nLineCount = file.GetLineCount();
  2343.     for ( size_t nLine = 0; nLine < nLineCount; nLine++ )
  2344.     {
  2345.         // now we're at the start of the line
  2346.         const wxChar *pc = file[nLine].c_str();
  2347.  
  2348.         // skip whitespace
  2349.         while ( wxIsspace(*pc) )
  2350.             pc++;
  2351.  
  2352.         // comment or empty string?
  2353.         if ( *pc == wxT('#') || *pc == wxT('\0') )
  2354.             continue;
  2355.  
  2356.         // no, do parse
  2357.         // ------------
  2358.  
  2359.         // what field are we currently in? The first 2 are fixed and there may
  2360.         // be an arbitrary number of other fields parsed by
  2361.         // ProcessOtherMailcapField()
  2362.         //
  2363.         // the first field is the MIME type
  2364.         enum
  2365.         {
  2366.             Field_Type,
  2367.             Field_OpenCmd,
  2368.             Field_Other
  2369.         } currentToken = Field_Type;
  2370.  
  2371.         // the flags and field values on the current line
  2372.         MailcapLineData data;
  2373.  
  2374.         bool cont = TRUE;
  2375.         while ( cont )
  2376.         {
  2377.             switch ( *pc )
  2378.             {
  2379.                 case wxT('\\'):
  2380.                     // interpret the next character literally (notice that
  2381.                     // backslash can be used for line continuation)
  2382.                     if ( *++pc == wxT('\0') )
  2383.                     {
  2384.                         // fetch the next line if there is one
  2385.                         if ( nLine == nLineCount - 1 )
  2386.                         {
  2387.                             // something is wrong, bail out
  2388.                             cont = FALSE;
  2389.  
  2390.                             wxLogDebug(wxT("Mailcap file %s, line %lu: "
  2391.                                            "'\\' on the end of the last line "
  2392.                                            "ignored."),
  2393.                                        strFileName.c_str(),
  2394.                                        (unsigned long)nLine + 1);
  2395.                         }
  2396.                         else
  2397.                         {
  2398.                             // pass to the beginning of the next line
  2399.                             pc = file[++nLine].c_str();
  2400.  
  2401.                             // skip pc++ at the end of the loop
  2402.                             continue;
  2403.                         }
  2404.                     }
  2405.                     else
  2406.                     {
  2407.                         // just a normal character
  2408.                         curField += *pc;
  2409.                     }
  2410.                     break;
  2411.  
  2412.                 case wxT('\0'):
  2413.                     cont = FALSE;   // end of line reached, exit the loop
  2414.  
  2415.                     // fall through to still process this field
  2416.  
  2417.                 case wxT(';'):
  2418.                     // trim whitespaces from both sides
  2419.                     curField.Trim(TRUE).Trim(FALSE);
  2420.  
  2421.                     switch ( currentToken )
  2422.                     {
  2423.                         case Field_Type:
  2424.                             data.type = curField.Lower();
  2425.                             if ( data.type.empty() )
  2426.                             {
  2427.                                 // I don't think that this is a valid mailcap
  2428.                                 // entry, but try to interpret it somehow
  2429.                                 data.type = _T('*');
  2430.                             }
  2431.  
  2432.                             if ( data.type.Find(wxT('/')) == wxNOT_FOUND )
  2433.                             {
  2434.                                 // we interpret "type" as "type/*"
  2435.                                 data.type += wxT("/*");
  2436.                             }
  2437.  
  2438.                             currentToken = Field_OpenCmd;
  2439.                             break;
  2440.  
  2441.                         case Field_OpenCmd:
  2442.                             data.cmdOpen = curField;
  2443.  
  2444.                             currentToken = Field_Other;
  2445.                             break;
  2446.  
  2447.                         case Field_Other:
  2448.                             if ( !ProcessOtherMailcapField(data, curField) )
  2449.                             {
  2450.                                 // don't flood the user with error messages if
  2451.                                 // we don't understand something in his
  2452.                                 // mailcap, but give them in debug mode because
  2453.                                 // this might be useful for the programmer
  2454.                                 wxLogDebug
  2455.                                 (
  2456.                                     wxT("Mailcap file %s, line %lu: "
  2457.                                         "unknown field '%s' for the "
  2458.                                         "MIME type '%s' ignored."),
  2459.                                     strFileName.c_str(),
  2460.                                     (unsigned long)nLine + 1,
  2461.                                     curField.c_str(),
  2462.                                     data.type.c_str()
  2463.                                 );
  2464.                             }
  2465.                             else if ( data.testfailed )
  2466.                             {
  2467.                                 // skip this entry entirely
  2468.                                 cont = FALSE;
  2469.                             }
  2470.  
  2471.                             // it already has this value
  2472.                             //currentToken = Field_Other;
  2473.                             break;
  2474.  
  2475.                         default:
  2476.                             wxFAIL_MSG(wxT("unknown field type in mailcap"));
  2477.                     }
  2478.  
  2479.                     // next token starts immediately after ';'
  2480.                     curField.Empty();
  2481.                     break;
  2482.  
  2483.                 default:
  2484.                     curField += *pc;
  2485.             }
  2486.  
  2487.             // continue in the same line
  2488.             pc++;
  2489.         }
  2490.  
  2491.         // we read the entire entry, check what have we got
  2492.         // ------------------------------------------------
  2493.  
  2494.         // check that we really read something reasonable
  2495.         if ( currentToken < Field_Other )
  2496.         {
  2497.             wxLogWarning(_("Mailcap file %s, line %d: incomplete entry "
  2498.                            "ignored."),
  2499.                          strFileName.c_str(), nLine + 1);
  2500.  
  2501.             continue;
  2502.         }
  2503.  
  2504.         // if the test command failed, it's as if the entry were not there at
  2505.         // all
  2506.         if ( data.testfailed )
  2507.         {
  2508.             continue;
  2509.         }
  2510.  
  2511.         // support for flags:
  2512.         //  1. create an xterm for 'needsterminal'
  2513.         //  2. append "| $PAGER" for 'copiousoutput'
  2514.         //
  2515.         // Note that the RFC says that having both needsterminal and
  2516.         // copiousoutput is probably a mistake, so it seems that running
  2517.         // programs with copiousoutput inside an xterm as it is done now
  2518.         // is a bad idea (FIXME)
  2519.         if ( data.copiousoutput )
  2520.         {
  2521.             const wxChar *p = wxGetenv(_T("PAGER"));
  2522.             data.cmdOpen << _T(" | ") << (p ? p : _T("more"));
  2523.         }
  2524.  
  2525.         if ( data.needsterminal )
  2526.         {
  2527.             data.cmdOpen = wxString::Format(_T("xterm -e sh -c '%s'"),
  2528.                                             data.cmdOpen.c_str());
  2529.         }
  2530.  
  2531.         if ( !data.cmdOpen.empty() )
  2532.         {
  2533.             data.verbs.Insert(_T("open"), 0);
  2534.             data.commands.Insert(data.cmdOpen, 0);
  2535.         }
  2536.  
  2537.         // we have to decide whether the new entry should replace any entries
  2538.         // for the same MIME type we had previously found or not
  2539.         bool overwrite;
  2540.  
  2541.         // the fall back entries have the lowest priority, by definition
  2542.         if ( fallback )
  2543.         {
  2544.             overwrite = FALSE;
  2545.         }
  2546.         else
  2547.         {
  2548.             // have we seen this one before?
  2549.             int nIndex = m_aTypes.Index(data.type);
  2550.  
  2551.             // and if we have, was it in this file?
  2552.             overwrite = nIndex == wxNOT_FOUND ||
  2553.                             aIndicesSeenHere.Index(nIndex) != wxNOT_FOUND;
  2554.         }
  2555.  
  2556.         wxLogTrace(TRACE_MIME, _T("mailcap %s: %s [%s]"),
  2557.                    data.type.c_str(), data.cmdOpen.c_str(),
  2558.                    overwrite ? _T("replace") : _T("add"));
  2559.  
  2560.         int n = AddToMimeData
  2561.                 (
  2562.                     data.type,
  2563.                     data.icon,
  2564.                     new wxMimeTypeCommands(data.verbs, data.commands),
  2565.                     wxArrayString() /* extensions */,
  2566.                     data.desc,
  2567.                     overwrite
  2568.                 );
  2569.  
  2570.         if ( overwrite )
  2571.         {
  2572.             aIndicesSeenHere.Add(n);
  2573.         }
  2574.     }
  2575.  
  2576.     return TRUE;
  2577. }
  2578.  
  2579. size_t wxMimeTypesManagerImpl::EnumAllFileTypes(wxArrayString& mimetypes)
  2580. {
  2581.     InitIfNeeded();
  2582.  
  2583.     mimetypes.Empty();
  2584.  
  2585.     wxString type;
  2586.     size_t count = m_aTypes.GetCount();
  2587.     for ( size_t n = 0; n < count; n++ )
  2588.     {
  2589.         // don't return template types from here (i.e. anything containg '*')
  2590.         type = m_aTypes[n];
  2591.         if ( type.Find(_T('*')) == wxNOT_FOUND )
  2592.         {
  2593.             mimetypes.Add(type);
  2594.         }
  2595.     }
  2596.  
  2597.     return mimetypes.GetCount();
  2598. }
  2599.  
  2600. // ----------------------------------------------------------------------------
  2601. // writing to MIME type files
  2602. // ----------------------------------------------------------------------------
  2603.  
  2604. bool wxMimeTypesManagerImpl::Unassociate(wxFileType *ft)
  2605. {
  2606.     wxArrayString sMimeTypes;
  2607.     ft->GetMimeTypes (sMimeTypes);
  2608.  
  2609.     wxString sMime;
  2610.     size_t i;
  2611.     for (i = 0; i < sMimeTypes.GetCount(); i ++)
  2612.     {
  2613.         sMime = sMimeTypes.Item(i);
  2614.         int nIndex = m_aTypes.Index (sMime);
  2615.         if ( nIndex == wxNOT_FOUND)
  2616.         {
  2617.             // error if we get here ??
  2618.             return FALSE;
  2619.         }
  2620.         else
  2621.         {
  2622.             WriteMimeInfo(nIndex, TRUE );
  2623.             m_aTypes.RemoveAt(nIndex);
  2624.             m_aEntries.RemoveAt(nIndex);
  2625.             m_aExtensions.RemoveAt(nIndex);
  2626.             m_aDescriptions.RemoveAt(nIndex);
  2627.             m_aIcons.RemoveAt(nIndex);
  2628.         }
  2629.     }
  2630.     // check data integrity
  2631.     wxASSERT( m_aTypes.Count() == m_aEntries.Count() &&
  2632.             m_aTypes.Count() == m_aExtensions.Count() &&
  2633.             m_aTypes.Count() == m_aIcons.Count() &&
  2634.             m_aTypes.Count() == m_aDescriptions.Count() );
  2635.  
  2636.     return TRUE;
  2637. }
  2638.  
  2639. // ----------------------------------------------------------------------------
  2640. // private functions
  2641. // ----------------------------------------------------------------------------
  2642.  
  2643. static bool IsKnownUnimportantField(const wxString& fieldAll)
  2644. {
  2645.     static const wxChar *knownFields[] =
  2646.     {
  2647.         _T("x-mozilla-flags"),
  2648.         _T("nametemplate"),
  2649.         _T("textualnewlines"),
  2650.     };
  2651.  
  2652.     wxString field = fieldAll.BeforeFirst(_T('='));
  2653.     for ( size_t n = 0; n < WXSIZEOF(knownFields); n++ )
  2654.     {
  2655.         if ( field.CmpNoCase(knownFields[n]) == 0 )
  2656.             return TRUE;
  2657.     }
  2658.  
  2659.     return FALSE;
  2660. }
  2661.  
  2662. #endif
  2663.   // wxUSE_MIMETYPE && wxUSE_FILE && wxUSE_TEXTFILE
  2664.  
  2665.