]> git.saurik.com Git - wxWidgets.git/blob - src/common/mimetype.cpp
VTK wrapper of vtkRenderWindow for wxPython. Tested on MSW so far.
[wxWidgets.git] / src / common / mimetype.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: common/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$
8 // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr>
9 // Licence: wxWindows license (part of wxExtra library)
10 /////////////////////////////////////////////////////////////////////////////
11
12 #ifdef __GNUG__
13 #pragma implementation "mimetype.h"
14 #endif
15
16 // for compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #ifdef __BORLANDC__
20 #pragma hdrstop
21 #endif
22
23 #ifndef WX_PRECOMP
24 #include "wx/defs.h"
25 #endif
26
27 #if (wxUSE_FILE && wxUSE_TEXTFILE) || defined(__WXMSW__)
28
29 #ifndef WX_PRECOMP
30 #include "wx/string.h"
31 #include "wx/icon.h"
32 #endif //WX_PRECOMP
33
34 // Doesn't compile in WIN16 mode
35 #ifndef __WIN16__
36
37 #include "wx/log.h"
38 #include "wx/file.h"
39 #include "wx/intl.h"
40 #include "wx/dynarray.h"
41 #include "wx/confbase.h"
42
43 #ifdef __WXMSW__
44 #include "wx/msw/registry.h"
45 #include "windows.h"
46 #else // Unix
47 #include "wx/textfile.h"
48 #endif // OS
49
50 #include "wx/mimetype.h"
51
52 // other standard headers
53 #include <ctype.h>
54
55 // ----------------------------------------------------------------------------
56 // private classes
57 // ----------------------------------------------------------------------------
58
59 // implementation classes, platform dependent
60 #ifdef __WXMSW__
61
62 // These classes use Windows registry to retrieve the required information.
63 //
64 // Keys used (not all of them are documented, so it might actually stop working
65 // in futur versions of Windows...):
66 // 1. "HKCR\MIME\Database\Content Type" contains subkeys for all known MIME
67 // types, each key has a string value "Extension" which gives (dot preceded)
68 // extension for the files of this MIME type.
69 //
70 // 2. "HKCR\.ext" contains
71 // a) unnamed value containing the "filetype"
72 // b) value "Content Type" containing the MIME type
73 //
74 // 3. "HKCR\filetype" contains
75 // a) unnamed value containing the description
76 // b) subkey "DefaultIcon" with single unnamed value giving the icon index in
77 // an icon file
78 // c) shell\open\command and shell\open\print subkeys containing the commands
79 // to open/print the file (the positional parameters are introduced by %1,
80 // %2, ... in these strings, we change them to %s ourselves)
81
82 class wxFileTypeImpl
83 {
84 public:
85 // ctor
86 wxFileTypeImpl() { m_info = NULL; }
87
88 // one of these Init() function must be called (ctor can't take any
89 // arguments because it's common)
90
91 // initialize us with our file type name and extension - in this case
92 // we will read all other data from the registry
93 void Init(const wxString& strFileType, const wxString& ext)
94 { m_strFileType = strFileType; m_ext = ext; }
95
96 // initialize us with a wxFileTypeInfo object - it contains all the
97 // data
98 void Init(const wxFileTypeInfo& info)
99 { m_info = &info; }
100
101 // implement accessor functions
102 bool GetExtensions(wxArrayString& extensions);
103 bool GetMimeType(wxString *mimeType) const;
104 bool GetIcon(wxIcon *icon) const;
105 bool GetDescription(wxString *desc) const;
106 bool GetOpenCommand(wxString *openCmd,
107 const wxFileType::MessageParameters& params) const;
108 bool GetPrintCommand(wxString *printCmd,
109 const wxFileType::MessageParameters& params) const;
110
111 private:
112 // helper function: reads the command corresponding to the specified verb
113 // from the registry (returns an empty string if not found)
114 wxString GetCommand(const wxChar *verb) const;
115
116 // we use either m_info or read the data from the registry if m_info == NULL
117 const wxFileTypeInfo *m_info;
118 wxString m_strFileType,
119 m_ext;
120 };
121
122 WX_DECLARE_EXPORTED_OBJARRAY(wxFileTypeInfo, wxArrayFileTypeInfo);
123 #include "wx/arrimpl.cpp"
124 WX_DEFINE_OBJARRAY(wxArrayFileTypeInfo);
125
126 class wxMimeTypesManagerImpl
127 {
128 public:
129 // nothing to do here, we don't load any data but just go and fetch it from
130 // the registry when asked for
131 wxMimeTypesManagerImpl() { }
132
133 // implement containing class functions
134 wxFileType *GetFileTypeFromExtension(const wxString& ext);
135 wxFileType *GetFileTypeFromMimeType(const wxString& mimeType);
136
137 // this are NOPs under Windows
138 bool ReadMailcap(const wxString& filename, bool fallback = TRUE)
139 { return TRUE; }
140 bool ReadMimeTypes(const wxString& filename)
141 { return TRUE; }
142
143 void AddFallback(const wxFileTypeInfo& ft) { m_fallbacks.Add(ft); }
144
145 private:
146 wxArrayFileTypeInfo m_fallbacks;
147 };
148
149 #elif defined( __WXMAC__ )
150
151 WX_DECLARE_EXPORTED_OBJARRAY(wxFileTypeInfo, wxArrayFileTypeInfo);
152 #include "wx/arrimpl.cpp"
153 WX_DEFINE_OBJARRAY(wxArrayFileTypeInfo);
154
155 class wxMimeTypesManagerImpl
156 {
157 public :
158 wxMimeTypesManagerImpl() { }
159
160 // implement containing class functions
161 wxFileType *GetFileTypeFromExtension(const wxString& ext);
162 wxFileType *GetFileTypeFromMimeType(const wxString& mimeType);
163
164 // this are NOPs under MacOS
165 bool ReadMailcap(const wxString& filename, bool fallback = TRUE) { return TRUE; }
166 bool ReadMimeTypes(const wxString& filename) { return TRUE; }
167
168 void AddFallback(const wxFileTypeInfo& ft) { m_fallbacks.Add(ft); }
169
170 private:
171 wxArrayFileTypeInfo m_fallbacks;
172 };
173
174 class wxFileTypeImpl
175 {
176 public:
177 // initialize us with our file type name
178 void SetFileType(const wxString& strFileType)
179 { m_strFileType = strFileType; }
180 void SetExt(const wxString& ext)
181 { m_ext = ext; }
182
183 // implement accessor functions
184 bool GetExtensions(wxArrayString& extensions);
185 bool GetMimeType(wxString *mimeType) const;
186 bool GetIcon(wxIcon *icon) const;
187 bool GetDescription(wxString *desc) const;
188 bool GetOpenCommand(wxString *openCmd,
189 const wxFileType::MessageParameters&) const
190 { return GetCommand(openCmd, "open"); }
191 bool GetPrintCommand(wxString *printCmd,
192 const wxFileType::MessageParameters&) const
193 { return GetCommand(printCmd, "print"); }
194
195 private:
196 // helper function
197 bool GetCommand(wxString *command, const char *verb) const;
198
199 wxString m_strFileType, m_ext;
200 };
201
202 #else // Unix
203
204 // this class uses both mailcap and mime.types to gather information about file
205 // types.
206 //
207 // The information about mailcap file was extracted from metamail(1) sources and
208 // documentation.
209 //
210 // Format of mailcap file: spaces are ignored, each line is either a comment
211 // (starts with '#') or a line of the form <field1>;<field2>;...;<fieldN>.
212 // A backslash can be used to quote semicolons and newlines (and, in fact,
213 // anything else including itself).
214 //
215 // The first field is always the MIME type in the form of type/subtype (see RFC
216 // 822) where subtype may be '*' meaning "any". Following metamail, we accept
217 // "type" which means the same as "type/*", although I'm not sure whether this
218 // is standard.
219 //
220 // The second field is always the command to run. It is subject to
221 // parameter/filename expansion described below.
222 //
223 // All the following fields are optional and may not be present at all. If
224 // they're present they may appear in any order, although each of them should
225 // appear only once. The optional fields are the following:
226 // * notes=xxx is an uninterpreted string which is silently ignored
227 // * test=xxx is the command to be used to determine whether this mailcap line
228 // applies to our data or not. The RHS of this field goes through the
229 // parameter/filename expansion (as the 2nd field) and the resulting string
230 // is executed. The line applies only if the command succeeds, i.e. returns 0
231 // exit code.
232 // * print=xxx is the command to be used to print (and not view) the data of
233 // this type (parameter/filename expansion is done here too)
234 // * edit=xxx is the command to open/edit the data of this type
235 // * needsterminal means that a new console must be created for the viewer
236 // * copiousoutput means that the viewer doesn't interact with the user but
237 // produces (possibly) a lof of lines of output on stdout (i.e. "cat" is a
238 // good example), thus it might be a good idea to use some kind of paging
239 // mechanism.
240 // * textualnewlines means not to perform CR/LF translation (not honored)
241 // * compose and composetyped fields are used to determine the program to be
242 // called to create a new message pert in the specified format (unused).
243 //
244 // Parameter/filename xpansion:
245 // * %s is replaced with the (full) file name
246 // * %t is replaced with MIME type/subtype of the entry
247 // * for multipart type only %n is replaced with the nnumber of parts and %F is
248 // replaced by an array of (content-type, temporary file name) pairs for all
249 // message parts (TODO)
250 // * %{parameter} is replaced with the value of parameter taken from
251 // Content-type header line of the message.
252 //
253 // FIXME any docs with real descriptions of these files??
254 //
255 // There are 2 possible formats for mime.types file, one entry per line (used
256 // for global mime.types) and "expanded" format where an entry takes multiple
257 // lines (used for users mime.types).
258 //
259 // For both formats spaces are ignored and lines starting with a '#' are
260 // comments. Each record has one of two following forms:
261 // a) for "brief" format:
262 // <mime type> <space separated list of extensions>
263 // b) for "expanded" format:
264 // type=<mime type> \ desc="<description>" \ exts="ext"
265 //
266 // We try to autodetect the format of mime.types: if a non-comment line starts
267 // with "type=" we assume the second format, otherwise the first one.
268
269 // there may be more than one entry for one and the same mime type, to
270 // choose the right one we have to run the command specified in the test
271 // field on our data.
272 class MailCapEntry
273 {
274 public:
275 // ctor
276 MailCapEntry(const wxString& openCmd,
277 const wxString& printCmd,
278 const wxString& testCmd)
279 : m_openCmd(openCmd), m_printCmd(printCmd), m_testCmd(testCmd)
280 {
281 m_next = NULL;
282 }
283
284 // accessors
285 const wxString& GetOpenCmd() const { return m_openCmd; }
286 const wxString& GetPrintCmd() const { return m_printCmd; }
287 const wxString& GetTestCmd() const { return m_testCmd; }
288
289 MailCapEntry *GetNext() const { return m_next; }
290
291 // operations
292 // prepend this element to the list
293 void Prepend(MailCapEntry *next) { m_next = next; }
294 // insert into the list at given position
295 void Insert(MailCapEntry *next, size_t pos)
296 {
297 // FIXME slooow...
298 MailCapEntry *cur;
299 size_t n = 0;
300 for ( cur = next; cur != NULL; cur = cur->m_next, n++ ) {
301 if ( n == pos )
302 break;
303 }
304
305 wxASSERT_MSG( n == pos, wxT("invalid position in MailCapEntry::Insert") );
306
307 m_next = cur->m_next;
308 cur->m_next = this;
309 }
310 // append this element to the list
311 void Append(MailCapEntry *next)
312 {
313 wxCHECK_RET( next != NULL, wxT("Append()ing to what?") );
314
315 // FIXME slooow...
316 MailCapEntry *cur;
317 for ( cur = next; cur->m_next != NULL; cur = cur->m_next )
318 ;
319
320 cur->m_next = this;
321
322 wxASSERT_MSG( !m_next, wxT("Append()ing element already in the list?") );
323 }
324
325 private:
326 wxString m_openCmd, // command to use to open/view the file
327 m_printCmd, // print
328 m_testCmd; // only apply this entry if test yields
329 // true (i.e. the command returns 0)
330
331 MailCapEntry *m_next; // in the linked list
332 };
333
334 WX_DEFINE_ARRAY(MailCapEntry *, ArrayTypeEntries);
335
336 class wxMimeTypesManagerImpl
337 {
338 friend class wxFileTypeImpl; // give it access to m_aXXX variables
339
340 public:
341 // ctor loads all info into memory for quicker access later on
342 // TODO it would be nice to load them all, but parse on demand only...
343 wxMimeTypesManagerImpl();
344
345 // implement containing class functions
346 wxFileType *GetFileTypeFromExtension(const wxString& ext);
347 wxFileType *GetFileTypeFromMimeType(const wxString& mimeType);
348
349 bool ReadMailcap(const wxString& filename, bool fallback = FALSE);
350 bool ReadMimeTypes(const wxString& filename);
351
352 void AddFallback(const wxFileTypeInfo& filetype);
353
354 // add information about the given mimetype
355 void AddMimeTypeInfo(const wxString& mimetype,
356 const wxString& extensions,
357 const wxString& description);
358 void AddMailcapInfo(const wxString& strType,
359 const wxString& strOpenCmd,
360 const wxString& strPrintCmd,
361 const wxString& strTest,
362 const wxString& strDesc);
363
364 // accessors
365 // get the string containing space separated extensions for the given
366 // file type
367 wxString GetExtension(size_t index) { return m_aExtensions[index]; }
368
369 private:
370 wxArrayString m_aTypes, // MIME types
371 m_aDescriptions, // descriptions (just some text)
372 m_aExtensions; // space separated list of extensions
373 ArrayTypeEntries m_aEntries; // commands and tests for this file type
374 };
375
376 class wxFileTypeImpl
377 {
378 public:
379 // initialization functions
380 void Init(wxMimeTypesManagerImpl *manager, size_t index)
381 { m_manager = manager; m_index = index; }
382
383 // accessors
384 bool GetExtensions(wxArrayString& extensions);
385 bool GetMimeType(wxString *mimeType) const
386 { *mimeType = m_manager->m_aTypes[m_index]; return TRUE; }
387 bool GetIcon(wxIcon * WXUNUSED(icon)) const
388 { return FALSE; } // TODO maybe with Gnome/KDE integration...
389 bool GetDescription(wxString *desc) const
390 { *desc = m_manager->m_aDescriptions[m_index]; return TRUE; }
391
392 bool GetOpenCommand(wxString *openCmd,
393 const wxFileType::MessageParameters& params) const
394 {
395 return GetExpandedCommand(openCmd, params, TRUE);
396 }
397
398 bool GetPrintCommand(wxString *printCmd,
399 const wxFileType::MessageParameters& params) const
400 {
401 return GetExpandedCommand(printCmd, params, FALSE);
402 }
403
404 private:
405 // get the entry which passes the test (may return NULL)
406 MailCapEntry *GetEntry(const wxFileType::MessageParameters& params) const;
407
408 // choose the correct entry to use and expand the command
409 bool GetExpandedCommand(wxString *expandedCmd,
410 const wxFileType::MessageParameters& params,
411 bool open) const;
412
413 wxMimeTypesManagerImpl *m_manager;
414 size_t m_index; // in the wxMimeTypesManagerImpl arrays
415 };
416
417 #endif // OS type
418
419 // ============================================================================
420 // common classes
421 // ============================================================================
422
423 // ----------------------------------------------------------------------------
424 // wxFileTypeInfo
425 // ----------------------------------------------------------------------------
426
427 wxFileTypeInfo::wxFileTypeInfo(const char *mimeType,
428 const char *openCmd,
429 const char *printCmd,
430 const char *desc,
431 ...)
432 : m_mimeType(mimeType),
433 m_openCmd(openCmd),
434 m_printCmd(printCmd),
435 m_desc(desc)
436 {
437 va_list argptr;
438 va_start(argptr, desc);
439
440 for ( ;; )
441 {
442 const char *ext = va_arg(argptr, const char *);
443 if ( !ext )
444 {
445 // NULL terminates the list
446 break;
447 }
448
449 m_exts.Add(ext);
450 }
451
452 va_end(argptr);
453 }
454
455 // ============================================================================
456 // implementation of the wrapper classes
457 // ============================================================================
458
459 // ----------------------------------------------------------------------------
460 // wxFileType
461 // ----------------------------------------------------------------------------
462
463 wxString wxFileType::ExpandCommand(const wxString& command,
464 const wxFileType::MessageParameters& params)
465 {
466 bool hasFilename = FALSE;
467
468 wxString str;
469 for ( const wxChar *pc = command.c_str(); *pc != wxT('\0'); pc++ ) {
470 if ( *pc == wxT('%') ) {
471 switch ( *++pc ) {
472 case wxT('s'):
473 // '%s' expands into file name (quoted because it might
474 // contain spaces) - except if there are already quotes
475 // there because otherwise some programs may get confused
476 // by double double quotes
477 #if 0
478 if ( *(pc - 2) == wxT('"') )
479 str << params.GetFileName();
480 else
481 str << wxT('"') << params.GetFileName() << wxT('"');
482 #endif
483 str << params.GetFileName();
484 hasFilename = TRUE;
485 break;
486
487 case wxT('t'):
488 // '%t' expands into MIME type (quote it too just to be
489 // consistent)
490 str << wxT('\'') << params.GetMimeType() << wxT('\'');
491 break;
492
493 case wxT('{'):
494 {
495 const wxChar *pEnd = wxStrchr(pc, wxT('}'));
496 if ( pEnd == NULL ) {
497 wxString mimetype;
498 wxLogWarning(_("Unmatched '{' in an entry for "
499 "mime type %s."),
500 params.GetMimeType().c_str());
501 str << wxT("%{");
502 }
503 else {
504 wxString param(pc + 1, pEnd - pc - 1);
505 str << wxT('\'') << params.GetParamValue(param) << wxT('\'');
506 pc = pEnd;
507 }
508 }
509 break;
510
511 case wxT('n'):
512 case wxT('F'):
513 // TODO %n is the number of parts, %F is an array containing
514 // the names of temp files these parts were written to
515 // and their mime types.
516 break;
517
518 default:
519 wxLogDebug(wxT("Unknown field %%%c in command '%s'."),
520 *pc, command.c_str());
521 str << *pc;
522 }
523 }
524 else {
525 str << *pc;
526 }
527 }
528
529 // metamail(1) man page states that if the mailcap entry doesn't have '%s'
530 // the program will accept the data on stdin: so give it to it!
531 if ( !hasFilename && !str.IsEmpty() ) {
532 str << wxT(" < '") << params.GetFileName() << wxT('\'');
533 }
534
535 return str;
536 }
537
538 wxFileType::wxFileType()
539 {
540 m_impl = new wxFileTypeImpl;
541 }
542
543 wxFileType::~wxFileType()
544 {
545 delete m_impl;
546 }
547
548 bool wxFileType::GetExtensions(wxArrayString& extensions)
549 {
550 return m_impl->GetExtensions(extensions);
551 }
552
553 bool wxFileType::GetMimeType(wxString *mimeType) const
554 {
555 return m_impl->GetMimeType(mimeType);
556 }
557
558 bool wxFileType::GetIcon(wxIcon *icon) const
559 {
560 return m_impl->GetIcon(icon);
561 }
562
563 bool wxFileType::GetDescription(wxString *desc) const
564 {
565 return m_impl->GetDescription(desc);
566 }
567
568 bool
569 wxFileType::GetOpenCommand(wxString *openCmd,
570 const wxFileType::MessageParameters& params) const
571 {
572 return m_impl->GetOpenCommand(openCmd, params);
573 }
574
575 bool
576 wxFileType::GetPrintCommand(wxString *printCmd,
577 const wxFileType::MessageParameters& params) const
578 {
579 return m_impl->GetPrintCommand(printCmd, params);
580 }
581
582 // ----------------------------------------------------------------------------
583 // wxMimeTypesManager
584 // ----------------------------------------------------------------------------
585
586 bool wxMimeTypesManager::IsOfType(const wxString& mimeType,
587 const wxString& wildcard)
588 {
589 wxASSERT_MSG( mimeType.Find(wxT('*')) == wxNOT_FOUND,
590 wxT("first MIME type can't contain wildcards") );
591
592 // all comparaisons are case insensitive (2nd arg of IsSameAs() is FALSE)
593 if ( wildcard.BeforeFirst(wxT('/')).IsSameAs(mimeType.BeforeFirst(wxT('/')), FALSE) )
594 {
595 wxString strSubtype = wildcard.AfterFirst(wxT('/'));
596
597 if ( strSubtype == wxT("*") ||
598 strSubtype.IsSameAs(mimeType.AfterFirst(wxT('/')), FALSE) )
599 {
600 // matches (either exactly or it's a wildcard)
601 return TRUE;
602 }
603 }
604
605 return FALSE;
606 }
607
608 wxMimeTypesManager::wxMimeTypesManager()
609 {
610 m_impl = new wxMimeTypesManagerImpl;
611 }
612
613 wxMimeTypesManager::~wxMimeTypesManager()
614 {
615 delete m_impl;
616 }
617
618 wxFileType *
619 wxMimeTypesManager::GetFileTypeFromExtension(const wxString& ext)
620 {
621 return m_impl->GetFileTypeFromExtension(ext);
622 }
623
624 wxFileType *
625 wxMimeTypesManager::GetFileTypeFromMimeType(const wxString& mimeType)
626 {
627 return m_impl->GetFileTypeFromMimeType(mimeType);
628 }
629
630 bool wxMimeTypesManager::ReadMailcap(const wxString& filename, bool fallback)
631 {
632 return m_impl->ReadMailcap(filename, fallback);
633 }
634
635 bool wxMimeTypesManager::ReadMimeTypes(const wxString& filename)
636 {
637 return m_impl->ReadMimeTypes(filename);
638 }
639
640 void wxMimeTypesManager::AddFallbacks(const wxFileTypeInfo *filetypes)
641 {
642 for ( const wxFileTypeInfo *ft = filetypes; ft->IsValid(); ft++ ) {
643 m_impl->AddFallback(*ft);
644 }
645 }
646
647 // ============================================================================
648 // real (OS specific) implementation
649 // ============================================================================
650
651 #ifdef __WXMSW__
652
653 wxString wxFileTypeImpl::GetCommand(const wxChar *verb) const
654 {
655 // suppress possible error messages
656 wxLogNull nolog;
657 wxString strKey;
658 strKey << m_strFileType << wxT("\\shell\\") << verb << wxT("\\command");
659 wxRegKey key(wxRegKey::HKCR, strKey);
660
661 wxString command;
662 if ( key.Open() ) {
663 // it's the default value of the key
664 if ( key.QueryValue(wxT(""), command) ) {
665 // transform it from '%1' to '%s' style format string
666
667 // NB: we don't make any attempt to verify that the string is valid,
668 // i.e. doesn't contain %2, or second %1 or .... But we do make
669 // sure that we return a string with _exactly_ one '%s'!
670 bool foundFilename = FALSE;
671 size_t len = command.Len();
672 for ( size_t n = 0; (n < len) && !foundFilename; n++ ) {
673 if ( command[n] == wxT('%') &&
674 (n + 1 < len) && command[n + 1] == wxT('1') ) {
675 // replace it with '%s'
676 command[n + 1] = wxT('s');
677
678 foundFilename = TRUE;
679 }
680 }
681
682 if ( !foundFilename ) {
683 // we didn't find any '%1'!
684 // HACK: append the filename at the end, hope that it will do
685 command << wxT(" %s");
686 }
687 }
688 }
689
690 // no such file type or no value
691 return command;
692 }
693
694 bool
695 wxFileTypeImpl::GetOpenCommand(wxString *openCmd,
696 const wxFileType::MessageParameters& params)
697 const
698 {
699 wxString cmd;
700 if ( m_info ) {
701 cmd = m_info->GetOpenCommand();
702 }
703 else {
704 cmd = GetCommand(wxT("open"));
705 }
706
707 *openCmd = wxFileType::ExpandCommand(cmd, params);
708
709 return !openCmd->IsEmpty();
710 }
711
712 bool
713 wxFileTypeImpl::GetPrintCommand(wxString *printCmd,
714 const wxFileType::MessageParameters& params)
715 const
716 {
717 wxString cmd;
718 if ( m_info ) {
719 cmd = m_info->GetPrintCommand();
720 }
721 else {
722 cmd = GetCommand(wxT("print"));
723 }
724
725 *printCmd = wxFileType::ExpandCommand(cmd, params);
726
727 return !printCmd->IsEmpty();
728 }
729
730 // TODO this function is half implemented
731 bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
732 {
733 if ( m_info ) {
734 extensions = m_info->GetExtensions();
735
736 return TRUE;
737 }
738 else if ( m_ext.IsEmpty() ) {
739 // the only way to get the list of extensions from the file type is to
740 // scan through all extensions in the registry - too slow...
741 return FALSE;
742 }
743 else {
744 extensions.Empty();
745 extensions.Add(m_ext);
746
747 // it's a lie too, we don't return _all_ extensions...
748 return TRUE;
749 }
750 }
751
752 bool wxFileTypeImpl::GetMimeType(wxString *mimeType) const
753 {
754 if ( m_info ) {
755 // we already have it
756 *mimeType = m_info->GetMimeType();
757
758 return TRUE;
759 }
760
761 // suppress possible error messages
762 wxLogNull nolog;
763 wxRegKey key(wxRegKey::HKCR, /*m_strFileType*/ wxT(".") + m_ext);
764 if ( key.Open() && key.QueryValue(wxT("Content Type"), *mimeType) ) {
765 return TRUE;
766 }
767 else {
768 return FALSE;
769 }
770 }
771
772 bool wxFileTypeImpl::GetIcon(wxIcon *icon) const
773 {
774 if ( m_info ) {
775 // we don't have icons in the fallback resources
776 return FALSE;
777 }
778
779 wxString strIconKey;
780 strIconKey << m_strFileType << wxT("\\DefaultIcon");
781
782 // suppress possible error messages
783 wxLogNull nolog;
784 wxRegKey key(wxRegKey::HKCR, strIconKey);
785
786 if ( key.Open() ) {
787 wxString strIcon;
788 // it's the default value of the key
789 if ( key.QueryValue(wxT(""), strIcon) ) {
790 // the format is the following: <full path to file>, <icon index>
791 // NB: icon index may be negative as well as positive and the full
792 // path may contain the environment variables inside '%'
793 wxString strFullPath = strIcon.BeforeLast(wxT(',')),
794 strIndex = strIcon.AfterLast(wxT(','));
795
796 // index may be omitted, in which case BeforeLast(',') is empty and
797 // AfterLast(',') is the whole string
798 if ( strFullPath.IsEmpty() ) {
799 strFullPath = strIndex;
800 strIndex = wxT("0");
801 }
802
803 wxString strExpPath = wxExpandEnvVars(strFullPath);
804 int nIndex = wxAtoi(strIndex);
805
806 HICON hIcon = ExtractIcon(GetModuleHandle(NULL), strExpPath, nIndex);
807 switch ( (int)hIcon ) {
808 case 0: // means no icons were found
809 case 1: // means no such file or it wasn't a DLL/EXE/OCX/ICO/...
810 wxLogDebug(wxT("incorrect registry entry '%s': no such icon."),
811 key.GetName().c_str());
812 break;
813
814 default:
815 icon->SetHICON((WXHICON)hIcon);
816 return TRUE;
817 }
818 }
819 }
820
821 // no such file type or no value or incorrect icon entry
822 return FALSE;
823 }
824
825 bool wxFileTypeImpl::GetDescription(wxString *desc) const
826 {
827 if ( m_info ) {
828 // we already have it
829 *desc = m_info->GetDescription();
830
831 return TRUE;
832 }
833
834 // suppress possible error messages
835 wxLogNull nolog;
836 wxRegKey key(wxRegKey::HKCR, m_strFileType);
837
838 if ( key.Open() ) {
839 // it's the default value of the key
840 if ( key.QueryValue(wxT(""), *desc) ) {
841 return TRUE;
842 }
843 }
844
845 return FALSE;
846 }
847
848 // extension -> file type
849 wxFileType *
850 wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& ext)
851 {
852 // add the leading point if necessary
853 wxString str;
854 if ( ext[0u] != wxT('.') ) {
855 str = wxT('.');
856 }
857 str << ext;
858
859 // suppress possible error messages
860 wxLogNull nolog;
861
862 wxString strFileType;
863 wxRegKey key(wxRegKey::HKCR, str);
864 if ( key.Open() ) {
865 // it's the default value of the key
866 if ( key.QueryValue(wxT(""), strFileType) ) {
867 // create the new wxFileType object
868 wxFileType *fileType = new wxFileType;
869 fileType->m_impl->Init(strFileType, ext);
870
871 return fileType;
872 }
873 }
874
875 // check the fallbacks
876 // TODO linear search is potentially slow, perhaps we should use a sorted
877 // array?
878 size_t count = m_fallbacks.GetCount();
879 for ( size_t n = 0; n < count; n++ ) {
880 if ( m_fallbacks[n].GetExtensions().Index(ext) != wxNOT_FOUND ) {
881 wxFileType *fileType = new wxFileType;
882 fileType->m_impl->Init(m_fallbacks[n]);
883
884 return fileType;
885 }
886 }
887
888 // unknown extension
889 return NULL;
890 }
891
892 // MIME type -> extension -> file type
893 wxFileType *
894 wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
895 {
896 // HACK I don't know of any official documentation which mentions this
897 // location, but as a matter of fact IE uses it, so why not we?
898 static const wxChar *szMimeDbase = wxT("MIME\\Database\\Content Type\\");
899
900 wxString strKey = szMimeDbase;
901 strKey << mimeType;
902
903 // suppress possible error messages
904 wxLogNull nolog;
905
906 wxString ext;
907 wxRegKey key(wxRegKey::HKCR, strKey);
908 if ( key.Open() ) {
909 if ( key.QueryValue(wxT("Extension"), ext) ) {
910 return GetFileTypeFromExtension(ext);
911 }
912 }
913
914 // check the fallbacks
915 // TODO linear search is potentially slow, perhaps we should use a sorted
916 // array?
917 size_t count = m_fallbacks.GetCount();
918 for ( size_t n = 0; n < count; n++ ) {
919 if ( wxMimeTypesManager::IsOfType(mimeType,
920 m_fallbacks[n].GetMimeType()) ) {
921 wxFileType *fileType = new wxFileType;
922 fileType->m_impl->Init(m_fallbacks[n]);
923
924 return fileType;
925 }
926 }
927
928 // unknown MIME type
929 return NULL;
930 }
931
932 #elif defined ( __WXMAC__ )
933
934
935 bool wxFileTypeImpl::GetCommand(wxString *command, const char *verb) const
936 {
937 return FALSE;
938 }
939
940 // @@ this function is half implemented
941 bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
942 {
943 return FALSE;
944 }
945
946 bool wxFileTypeImpl::GetMimeType(wxString *mimeType) const
947 {
948 if ( m_strFileType.Length() > 0 )
949 {
950 *mimeType = m_strFileType ;
951 return TRUE ;
952 }
953 else
954 return FALSE;
955 }
956
957 bool wxFileTypeImpl::GetIcon(wxIcon *icon) const
958 {
959 // no such file type or no value or incorrect icon entry
960 return FALSE;
961 }
962
963 bool wxFileTypeImpl::GetDescription(wxString *desc) const
964 {
965 return FALSE;
966 }
967
968 // extension -> file type
969 wxFileType *
970 wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& e)
971 {
972 wxString ext = e ;
973 ext = ext.Lower() ;
974 if ( ext == "txt" )
975 {
976 wxFileType *fileType = new wxFileType;
977 fileType->m_impl->SetFileType("text/text");
978 fileType->m_impl->SetExt(ext);
979 return fileType;
980 }
981 else if ( ext == "htm" || ext == "html" )
982 {
983 wxFileType *fileType = new wxFileType;
984 fileType->m_impl->SetFileType("text/html");
985 fileType->m_impl->SetExt(ext);
986 return fileType;
987 }
988 else if ( ext == "gif" )
989 {
990 wxFileType *fileType = new wxFileType;
991 fileType->m_impl->SetFileType("image/gif");
992 fileType->m_impl->SetExt(ext);
993 return fileType;
994 }
995 else if ( ext == "png" )
996 {
997 wxFileType *fileType = new wxFileType;
998 fileType->m_impl->SetFileType("image/png");
999 fileType->m_impl->SetExt(ext);
1000 return fileType;
1001 }
1002 else if ( ext == "jpg" || ext == "jpeg" )
1003 {
1004 wxFileType *fileType = new wxFileType;
1005 fileType->m_impl->SetFileType("image/jpeg");
1006 fileType->m_impl->SetExt(ext);
1007 return fileType;
1008 }
1009 else if ( ext == "bmp" )
1010 {
1011 wxFileType *fileType = new wxFileType;
1012 fileType->m_impl->SetFileType("image/bmp");
1013 fileType->m_impl->SetExt(ext);
1014 return fileType;
1015 }
1016 else if ( ext == "tif" || ext == "tiff" )
1017 {
1018 wxFileType *fileType = new wxFileType;
1019 fileType->m_impl->SetFileType("image/tiff");
1020 fileType->m_impl->SetExt(ext);
1021 return fileType;
1022 }
1023 else if ( ext == "xpm" )
1024 {
1025 wxFileType *fileType = new wxFileType;
1026 fileType->m_impl->SetFileType("image/xpm");
1027 fileType->m_impl->SetExt(ext);
1028 return fileType;
1029 }
1030 else if ( ext == "xbm" )
1031 {
1032 wxFileType *fileType = new wxFileType;
1033 fileType->m_impl->SetFileType("image/xbm");
1034 fileType->m_impl->SetExt(ext);
1035 return fileType;
1036 }
1037 // unknown extension
1038 return NULL;
1039 }
1040
1041 // MIME type -> extension -> file type
1042 wxFileType *
1043 wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
1044 {
1045 return NULL;
1046 }
1047 #else // Unix
1048
1049 MailCapEntry *
1050 wxFileTypeImpl::GetEntry(const wxFileType::MessageParameters& params) const
1051 {
1052 wxString command;
1053 MailCapEntry *entry = m_manager->m_aEntries[m_index];
1054 while ( entry != NULL ) {
1055 // notice that an empty command would always succeed (it's ok)
1056 command = wxFileType::ExpandCommand(entry->GetTestCmd(), params);
1057
1058 if ( command.IsEmpty() || (wxSystem(command) == 0) ) {
1059 // ok, passed
1060 wxLogTrace(wxT("Test '%s' for mime type '%s' succeeded."),
1061 command.c_str(), params.GetMimeType().c_str());
1062 break;
1063 }
1064 else {
1065 wxLogTrace(wxT("Test '%s' for mime type '%s' failed."),
1066 command.c_str(), params.GetMimeType().c_str());
1067 }
1068
1069 entry = entry->GetNext();
1070 }
1071
1072 return entry;
1073 }
1074
1075 bool
1076 wxFileTypeImpl::GetExpandedCommand(wxString *expandedCmd,
1077 const wxFileType::MessageParameters& params,
1078 bool open) const
1079 {
1080 MailCapEntry *entry = GetEntry(params);
1081 if ( entry == NULL ) {
1082 // all tests failed...
1083 return FALSE;
1084 }
1085
1086 wxString cmd = open ? entry->GetOpenCmd() : entry->GetPrintCmd();
1087 if ( cmd.IsEmpty() ) {
1088 // may happen, especially for "print"
1089 return FALSE;
1090 }
1091
1092 *expandedCmd = wxFileType::ExpandCommand(cmd, params);
1093 return TRUE;
1094 }
1095
1096 bool wxFileTypeImpl::GetExtensions(wxArrayString& extensions)
1097 {
1098 wxString strExtensions = m_manager->GetExtension(m_index);
1099 extensions.Empty();
1100
1101 // one extension in the space or comma delimitid list
1102 wxString strExt;
1103 for ( const wxChar *p = strExtensions; ; p++ ) {
1104 if ( *p == wxT(' ') || *p == wxT(',') || *p == wxT('\0') ) {
1105 if ( !strExt.IsEmpty() ) {
1106 extensions.Add(strExt);
1107 strExt.Empty();
1108 }
1109 //else: repeated spaces (shouldn't happen, but it's not that
1110 // important if it does happen)
1111
1112 if ( *p == wxT('\0') )
1113 break;
1114 }
1115 else if ( *p == wxT('.') ) {
1116 // remove the dot from extension (but only if it's the first char)
1117 if ( !strExt.IsEmpty() ) {
1118 strExt += wxT('.');
1119 }
1120 //else: no, don't append it
1121 }
1122 else {
1123 strExt += *p;
1124 }
1125 }
1126
1127 return TRUE;
1128 }
1129
1130 // read system and user mailcaps (TODO implement mime.types support)
1131 wxMimeTypesManagerImpl::wxMimeTypesManagerImpl()
1132 {
1133 // directories where we look for mailcap and mime.types by default
1134 // (taken from metamail(1) sources)
1135 static const wxChar *aStandardLocations[] =
1136 {
1137 wxT("/etc"),
1138 wxT("/usr/etc"),
1139 wxT("/usr/local/etc"),
1140 wxT("/etc/mail"),
1141 wxT("/usr/public/lib")
1142 };
1143
1144 // first read the system wide file(s)
1145 for ( size_t n = 0; n < WXSIZEOF(aStandardLocations); n++ ) {
1146 wxString dir = aStandardLocations[n];
1147
1148 wxString file = dir + wxT("/mailcap");
1149 if ( wxFile::Exists(file) ) {
1150 ReadMailcap(file);
1151 }
1152
1153 file = dir + wxT("/mime.types");
1154 if ( wxFile::Exists(file) ) {
1155 ReadMimeTypes(file);
1156 }
1157 }
1158
1159 wxString strHome = wxGetenv(wxT("HOME"));
1160
1161 // and now the users mailcap
1162 wxString strUserMailcap = strHome + wxT("/.mailcap");
1163 if ( wxFile::Exists(strUserMailcap) ) {
1164 ReadMailcap(strUserMailcap);
1165 }
1166
1167 // read the users mime.types
1168 wxString strUserMimeTypes = strHome + wxT("/.mime.types");
1169 if ( wxFile::Exists(strUserMimeTypes) ) {
1170 ReadMimeTypes(strUserMimeTypes);
1171 }
1172 }
1173
1174 wxFileType *
1175 wxMimeTypesManagerImpl::GetFileTypeFromExtension(const wxString& ext)
1176 {
1177 size_t count = m_aExtensions.GetCount();
1178 for ( size_t n = 0; n < count; n++ ) {
1179 wxString extensions = m_aExtensions[n];
1180 while ( !extensions.IsEmpty() ) {
1181 wxString field = extensions.BeforeFirst(wxT(' '));
1182 extensions = extensions.AfterFirst(wxT(' '));
1183
1184 // consider extensions as not being case-sensitive
1185 if ( field.IsSameAs(ext, FALSE /* no case */) ) {
1186 // found
1187 wxFileType *fileType = new wxFileType;
1188 fileType->m_impl->Init(this, n);
1189
1190 return fileType;
1191 }
1192 }
1193 }
1194
1195 // not found
1196 return NULL;
1197 }
1198
1199 wxFileType *
1200 wxMimeTypesManagerImpl::GetFileTypeFromMimeType(const wxString& mimeType)
1201 {
1202 // mime types are not case-sensitive
1203 wxString mimetype(mimeType);
1204 mimetype.MakeLower();
1205
1206 // first look for an exact match
1207 int index = m_aTypes.Index(mimetype);
1208 if ( index == wxNOT_FOUND ) {
1209 // then try to find "text/*" as match for "text/plain" (for example)
1210 // NB: if mimeType doesn't contain '/' at all, BeforeFirst() will return
1211 // the whole string - ok.
1212 wxString strCategory = mimetype.BeforeFirst(wxT('/'));
1213
1214 size_t nCount = m_aTypes.Count();
1215 for ( size_t n = 0; n < nCount; n++ ) {
1216 if ( (m_aTypes[n].BeforeFirst(wxT('/')) == strCategory ) &&
1217 m_aTypes[n].AfterFirst(wxT('/')) == wxT("*") ) {
1218 index = n;
1219 break;
1220 }
1221 }
1222 }
1223
1224 if ( index != wxNOT_FOUND ) {
1225 wxFileType *fileType = new wxFileType;
1226 fileType->m_impl->Init(this, index);
1227
1228 return fileType;
1229 }
1230 else {
1231 // not found...
1232 return NULL;
1233 }
1234 }
1235
1236 void wxMimeTypesManagerImpl::AddFallback(const wxFileTypeInfo& filetype)
1237 {
1238 wxString extensions;
1239 const wxArrayString& exts = filetype.GetExtensions();
1240 size_t nExts = exts.GetCount();
1241 for ( size_t nExt = 0; nExt < nExts; nExt++ ) {
1242 if ( nExt > 0 ) {
1243 extensions += wxT(' ');
1244 }
1245 extensions += exts[nExt];
1246 }
1247
1248 AddMimeTypeInfo(filetype.GetMimeType(),
1249 extensions,
1250 filetype.GetDescription());
1251
1252 AddMailcapInfo(filetype.GetMimeType(),
1253 filetype.GetOpenCommand(),
1254 filetype.GetPrintCommand(),
1255 wxT(""),
1256 filetype.GetDescription());
1257 }
1258
1259 void wxMimeTypesManagerImpl::AddMimeTypeInfo(const wxString& strMimeType,
1260 const wxString& strExtensions,
1261 const wxString& strDesc)
1262 {
1263 int index = m_aTypes.Index(strMimeType);
1264 if ( index == wxNOT_FOUND ) {
1265 // add a new entry
1266 m_aTypes.Add(strMimeType);
1267 m_aEntries.Add(NULL);
1268 m_aExtensions.Add(strExtensions);
1269 m_aDescriptions.Add(strDesc);
1270 }
1271 else {
1272 // modify an existing one
1273 if ( !strDesc.IsEmpty() ) {
1274 m_aDescriptions[index] = strDesc; // replace old value
1275 }
1276 m_aExtensions[index] += ' ' + strExtensions;
1277 }
1278 }
1279
1280 void wxMimeTypesManagerImpl::AddMailcapInfo(const wxString& strType,
1281 const wxString& strOpenCmd,
1282 const wxString& strPrintCmd,
1283 const wxString& strTest,
1284 const wxString& strDesc)
1285 {
1286 MailCapEntry *entry = new MailCapEntry(strOpenCmd, strPrintCmd, strTest);
1287
1288 int nIndex = m_aTypes.Index(strType);
1289 if ( nIndex == wxNOT_FOUND ) {
1290 // new file type
1291 m_aTypes.Add(strType);
1292
1293 m_aEntries.Add(entry);
1294 m_aExtensions.Add(wxT(""));
1295 m_aDescriptions.Add(strDesc);
1296 }
1297 else {
1298 // always append the entry in the tail of the list - info added with
1299 // this function can only come from AddFallbacks()
1300 MailCapEntry *entryOld = m_aEntries[nIndex];
1301 if ( entryOld )
1302 entry->Append(entryOld);
1303 else
1304 m_aEntries[nIndex] = entry;
1305 }
1306 }
1307
1308 bool wxMimeTypesManagerImpl::ReadMimeTypes(const wxString& strFileName)
1309 {
1310 wxLogTrace(wxT("--- Parsing mime.types file '%s' ---"), strFileName.c_str());
1311
1312 wxTextFile file(strFileName);
1313 if ( !file.Open() )
1314 return FALSE;
1315
1316 // the information we extract
1317 wxString strMimeType, strDesc, strExtensions;
1318
1319 size_t nLineCount = file.GetLineCount();
1320 const wxChar *pc = NULL;
1321 for ( size_t nLine = 0; nLine < nLineCount; nLine++ ) {
1322 if ( pc == NULL ) {
1323 // now we're at the start of the line
1324 pc = file[nLine].c_str();
1325 }
1326 else {
1327 // we didn't finish with the previous line yet
1328 nLine--;
1329 }
1330
1331 // skip whitespace
1332 while ( wxIsspace(*pc) )
1333 pc++;
1334
1335 // comment?
1336 if ( *pc == wxT('#') ) {
1337 // skip the whole line
1338 pc = NULL;
1339 continue;
1340 }
1341
1342 // detect file format
1343 const wxChar *pEqualSign = wxStrchr(pc, wxT('='));
1344 if ( pEqualSign == NULL ) {
1345 // brief format
1346 // ------------
1347
1348 // first field is mime type
1349 for ( strMimeType.Empty(); !wxIsspace(*pc) && *pc != wxT('\0'); pc++ ) {
1350 strMimeType += *pc;
1351 }
1352
1353 // skip whitespace
1354 while ( wxIsspace(*pc) )
1355 pc++;
1356
1357 // take all the rest of the string
1358 strExtensions = pc;
1359
1360 // no description...
1361 strDesc.Empty();
1362 }
1363 else {
1364 // expanded format
1365 // ---------------
1366
1367 // the string on the left of '=' is the field name
1368 wxString strLHS(pc, pEqualSign - pc);
1369
1370 // eat whitespace
1371 for ( pc = pEqualSign + 1; wxIsspace(*pc); pc++ )
1372 ;
1373
1374 const wxChar *pEnd;
1375 if ( *pc == wxT('"') ) {
1376 // the string is quoted and ends at the matching quote
1377 pEnd = wxStrchr(++pc, wxT('"'));
1378 if ( pEnd == NULL ) {
1379 wxLogWarning(_("Mime.types file %s, line %d: unterminated "
1380 "quoted string."),
1381 strFileName.c_str(), nLine + 1);
1382 }
1383 }
1384 else {
1385 // unquoted string ends at the first space
1386 for ( pEnd = pc; !wxIsspace(*pEnd); pEnd++ )
1387 ;
1388 }
1389
1390 // now we have the RHS (field value)
1391 wxString strRHS(pc, pEnd - pc);
1392
1393 // check what follows this entry
1394 if ( *pEnd == wxT('"') ) {
1395 // skip this quote
1396 pEnd++;
1397 }
1398
1399 for ( pc = pEnd; wxIsspace(*pc); pc++ )
1400 ;
1401
1402 // if there is something left, it may be either a '\\' to continue
1403 // the line or the next field of the same entry
1404 bool entryEnded = *pc == wxT('\0'),
1405 nextFieldOnSameLine = FALSE;
1406 if ( !entryEnded ) {
1407 nextFieldOnSameLine = ((*pc != wxT('\\')) || (pc[1] != wxT('\0')));
1408 }
1409
1410 // now see what we got
1411 if ( strLHS == wxT("type") ) {
1412 strMimeType = strRHS;
1413 }
1414 else if ( strLHS == wxT("desc") ) {
1415 strDesc = strRHS;
1416 }
1417 else if ( strLHS == wxT("exts") ) {
1418 strExtensions = strRHS;
1419 }
1420 else {
1421 wxLogWarning(_("Unknown field in file %s, line %d: '%s'."),
1422 strFileName.c_str(), nLine + 1, strLHS.c_str());
1423 }
1424
1425 if ( !entryEnded ) {
1426 if ( !nextFieldOnSameLine )
1427 pc = NULL;
1428 //else: don't reset it
1429
1430 // as we don't reset strMimeType, the next field in this entry
1431 // will be interpreted correctly.
1432
1433 continue;
1434 }
1435 }
1436
1437 // although it doesn't seem to be covered by RFCs, some programs
1438 // (notably Netscape) create their entries with several comma
1439 // separated extensions (RFC mention the spaces only)
1440 strExtensions.Replace(wxT(","), wxT(" "));
1441
1442 // also deal with the leading dot
1443 #if defined(__VISAGECPP__) && __IBMCPP__ >= 400
1444 if ( !strExtensions.IsEmpty() && strExtensions[size_t(0)] == wxT('.') ) {
1445 #else
1446 if ( !strExtensions.IsEmpty() && strExtensions[0] == wxT('.') ) {
1447 #endif
1448 strExtensions.erase(0, 1);
1449 }
1450
1451 AddMimeTypeInfo(strMimeType, strExtensions, strDesc);
1452
1453 // finished with this line
1454 pc = NULL;
1455 }
1456
1457 // check our data integriry
1458 wxASSERT( m_aTypes.Count() == m_aEntries.Count() &&
1459 m_aTypes.Count() == m_aExtensions.Count() &&
1460 m_aTypes.Count() == m_aDescriptions.Count() );
1461
1462 return TRUE;
1463 }
1464
1465 bool wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName,
1466 bool fallback)
1467 {
1468 wxLogTrace(wxT("--- Parsing mailcap file '%s' ---"), strFileName.c_str());
1469
1470 wxTextFile file(strFileName);
1471 if ( !file.Open() )
1472 return FALSE;
1473
1474 // see the comments near the end of function for the reason we need these
1475 // variables (search for the next occurence of them)
1476 // indices of MIME types (in m_aTypes) we already found in this file
1477 wxArrayInt aEntryIndices;
1478 // aLastIndices[n] is the index of last element in
1479 // m_aEntries[aEntryIndices[n]] from this file
1480 wxArrayInt aLastIndices;
1481
1482 size_t nLineCount = file.GetLineCount();
1483 for ( size_t nLine = 0; nLine < nLineCount; nLine++ ) {
1484 // now we're at the start of the line
1485 const wxChar *pc = file[nLine].c_str();
1486
1487 // skip whitespace
1488 while ( wxIsspace(*pc) )
1489 pc++;
1490
1491 // comment or empty string?
1492 if ( *pc == wxT('#') || *pc == wxT('\0') )
1493 continue;
1494
1495 // no, do parse
1496
1497 // what field are we currently in? The first 2 are fixed and there may
1498 // be an arbitrary number of other fields -- currently, we are not
1499 // interested in any of them, but we should parse them as well...
1500 enum
1501 {
1502 Field_Type,
1503 Field_OpenCmd,
1504 Field_Other
1505 } currentToken = Field_Type;
1506
1507 // the flags and field values on the current line
1508 bool needsterminal = FALSE,
1509 copiousoutput = FALSE;
1510 wxString strType,
1511 strOpenCmd,
1512 strPrintCmd,
1513 strTest,
1514 strDesc,
1515 curField; // accumulator
1516 for ( bool cont = TRUE; cont; pc++ ) {
1517 switch ( *pc ) {
1518 case wxT('\\'):
1519 // interpret the next character literally (notice that
1520 // backslash can be used for line continuation)
1521 if ( *++pc == wxT('\0') ) {
1522 // fetch the next line.
1523
1524 // pc currently points to nowhere, but after the next
1525 // pc++ in the for line it will point to the beginning
1526 // of the next line in the file
1527 pc = file[++nLine].c_str() - 1;
1528 }
1529 else {
1530 // just a normal character
1531 curField += *pc;
1532 }
1533 break;
1534
1535 case wxT('\0'):
1536 cont = FALSE; // end of line reached, exit the loop
1537
1538 // fall through
1539
1540 case wxT(';'):
1541 // store this field and start looking for the next one
1542
1543 // trim whitespaces from both sides
1544 curField.Trim(TRUE).Trim(FALSE);
1545
1546 switch ( currentToken ) {
1547 case Field_Type:
1548 strType = curField;
1549 if ( strType.Find(wxT('/')) == wxNOT_FOUND ) {
1550 // we interpret "type" as "type/*"
1551 strType += wxT("/*");
1552 }
1553
1554 currentToken = Field_OpenCmd;
1555 break;
1556
1557 case Field_OpenCmd:
1558 strOpenCmd = curField;
1559
1560 currentToken = Field_Other;
1561 break;
1562
1563 case Field_Other:
1564 {
1565 // "good" mailcap entry?
1566 bool ok = TRUE;
1567
1568 // is this something of the form foo=bar?
1569 const wxChar *pEq = wxStrchr(curField, wxT('='));
1570 if ( pEq != NULL ) {
1571 wxString lhs = curField.BeforeFirst(wxT('=')),
1572 rhs = curField.AfterFirst(wxT('='));
1573
1574 lhs.Trim(TRUE); // from right
1575 rhs.Trim(FALSE); // from left
1576
1577 if ( lhs == wxT("print") )
1578 strPrintCmd = rhs;
1579 else if ( lhs == wxT("test") )
1580 strTest = rhs;
1581 else if ( lhs == wxT("description") ) {
1582 // it might be quoted
1583 if ( rhs[0u] == wxT('"') &&
1584 rhs.Last() == wxT('"') ) {
1585 strDesc = wxString(rhs.c_str() + 1,
1586 rhs.Len() - 2);
1587 }
1588 else {
1589 strDesc = rhs;
1590 }
1591 }
1592 else if ( lhs == wxT("compose") ||
1593 lhs == wxT("composetyped") ||
1594 lhs == wxT("notes") ||
1595 lhs == wxT("edit") )
1596 ; // ignore
1597 else
1598 ok = FALSE;
1599
1600 }
1601 else {
1602 // no, it's a simple flag
1603 // TODO support the flags:
1604 // 1. create an xterm for 'needsterminal'
1605 // 2. append "| $PAGER" for 'copiousoutput'
1606 if ( curField == wxT("needsterminal") )
1607 needsterminal = TRUE;
1608 else if ( curField == wxT("copiousoutput") )
1609 copiousoutput = TRUE;
1610 else if ( curField == wxT("textualnewlines") )
1611 ; // ignore
1612 else
1613 ok = FALSE;
1614 }
1615
1616 if ( !ok )
1617 {
1618 // don't flood the user with error messages
1619 // if we don't understand something in his
1620 // mailcap, but give them in debug mode
1621 // because this might be useful for the
1622 // programmer
1623 wxLogDebug
1624 (
1625 wxT("Mailcap file %s, line %d: unknown "
1626 "field '%s' for the MIME type "
1627 "'%s' ignored."),
1628 strFileName.c_str(),
1629 nLine + 1,
1630 curField.c_str(),
1631 strType.c_str()
1632 );
1633 }
1634 }
1635
1636 // it already has this value
1637 //currentToken = Field_Other;
1638 break;
1639
1640 default:
1641 wxFAIL_MSG(wxT("unknown field type in mailcap"));
1642 }
1643
1644 // next token starts immediately after ';'
1645 curField.Empty();
1646 break;
1647
1648 default:
1649 curField += *pc;
1650 }
1651 }
1652
1653 // check that we really read something reasonable
1654 if ( currentToken == Field_Type || currentToken == Field_OpenCmd ) {
1655 wxLogWarning(_("Mailcap file %s, line %d: incomplete entry "
1656 "ignored."),
1657 strFileName.c_str(), nLine + 1);
1658 }
1659 else {
1660 MailCapEntry *entry = new MailCapEntry(strOpenCmd,
1661 strPrintCmd,
1662 strTest);
1663
1664 // NB: because of complications below (we must get entries priority
1665 // right), we can't use AddMailcapInfo() here, unfortunately.
1666 strType.MakeLower();
1667 int nIndex = m_aTypes.Index(strType);
1668 if ( nIndex == wxNOT_FOUND ) {
1669 // new file type
1670 m_aTypes.Add(strType);
1671
1672 m_aEntries.Add(entry);
1673 m_aExtensions.Add(wxT(""));
1674 m_aDescriptions.Add(strDesc);
1675 }
1676 else {
1677 // modify the existing entry: the entries in one and the same
1678 // file are read in top-to-bottom order, i.e. the entries read
1679 // first should be tried before the entries below. However,
1680 // the files read later should override the settings in the
1681 // files read before (except if fallback is TRUE), thus we
1682 // Insert() the new entry to the list if it has already
1683 // occured in _this_ file, but Prepend() it if it occured in
1684 // some of the previous ones and Append() to it in the
1685 // fallback case
1686
1687 if ( fallback ) {
1688 // 'fallback' parameter prevents the entries from this
1689 // file from overriding the other ones - always append
1690 MailCapEntry *entryOld = m_aEntries[nIndex];
1691 if ( entryOld )
1692 entry->Append(entryOld);
1693 else
1694 m_aEntries[nIndex] = entry;
1695 }
1696 else {
1697 int entryIndex = aEntryIndices.Index(nIndex);
1698 if ( entryIndex == wxNOT_FOUND ) {
1699 // first time in this file
1700 aEntryIndices.Add(nIndex);
1701 aLastIndices.Add(0);
1702
1703 entry->Prepend(m_aEntries[nIndex]);
1704 m_aEntries[nIndex] = entry;
1705 }
1706 else {
1707 // not the first time in _this_ file
1708 size_t nEntryIndex = (size_t)entryIndex;
1709 MailCapEntry *entryOld = m_aEntries[nIndex];
1710 if ( entryOld )
1711 entry->Insert(entryOld, aLastIndices[nEntryIndex]);
1712 else
1713 m_aEntries[nIndex] = entry;
1714
1715 // the indices were shifted by 1
1716 aLastIndices[nEntryIndex]++;
1717 }
1718 }
1719
1720 if ( !strDesc.IsEmpty() ) {
1721 // replace the old one - what else can we do??
1722 m_aDescriptions[nIndex] = strDesc;
1723 }
1724 }
1725 }
1726
1727 // check our data integriry
1728 wxASSERT( m_aTypes.Count() == m_aEntries.Count() &&
1729 m_aTypes.Count() == m_aExtensions.Count() &&
1730 m_aTypes.Count() == m_aDescriptions.Count() );
1731 }
1732
1733 return TRUE;
1734 }
1735
1736 #endif
1737 // OS type
1738
1739 #endif
1740 // wxUSE_FILE && wxUSE_TEXTFILE
1741
1742 #endif
1743 // __WIN16__