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