-void wxMimeTypesManagerImpl::AddMailcapInfo(const wxString& strType,
- const wxString& strOpenCmd,
- const wxString& strPrintCmd,
- const wxString& strTest,
- const wxString& strDesc)
-{
- InitIfNeeded();
-
- wxMimeTypeCommands *entry = new wxMimeTypeCommands;
- entry->Add(wxT("open=") + strOpenCmd);
- entry->Add(wxT("print=") + strPrintCmd);
- entry->Add(wxT("test=") + strTest);
-
- wxString strIcon;
- wxArrayString strExtensions;
-
- AddToMimeData(strType, strIcon, entry, strExtensions, strDesc, true);
-}
-
-bool wxMimeTypesManagerImpl::ReadMimeTypes(const wxString& strFileName)
-{
- wxLogTrace(TRACE_MIME, wxT("--- Parsing mime.types file '%s' ---"),
- strFileName.c_str());
-
- wxTextFile file(strFileName);
-#if defined(__WXGTK20__) && wxUSE_UNICODE
- if ( !file.Open(wxConvUTF8) )
-#else
- if ( !file.Open() )
-#endif
- return false;
-
- // the information we extract
- wxString strMimeType, strDesc, strExtensions;
-
- size_t nLineCount = file.GetLineCount();
- const wxChar *pc = NULL;
- for ( size_t nLine = 0; nLine < nLineCount; nLine++ )
- {
- if ( pc == NULL )
- {
- // now we're at the start of the line
- pc = file[nLine].c_str();
- }
- else
- {
- // we didn't finish with the previous line yet
- nLine--;
- }
-
- // skip whitespace
- while ( wxIsspace(*pc) )
- pc++;
-
- // comment or blank line?
- if ( *pc == wxT('#') || !*pc )
- {
- // skip the whole line
- pc = NULL;
- continue;
- }
-
- // detect file format
- const wxChar *pEqualSign = wxStrchr(pc, wxT('='));
- if ( pEqualSign == NULL )
- {
- // brief format
- // ------------
-
- // first field is mime type
- for ( strMimeType.Empty(); !wxIsspace(*pc) && *pc != wxT('\0'); pc++ )
- {
- strMimeType += *pc;
- }
-
- // skip whitespace
- while ( wxIsspace(*pc) )
- pc++;
-
- // take all the rest of the string
- strExtensions = pc;
-
- // no description...
- strDesc.Empty();
- }
- else
- {
- // expanded format
- // ---------------
-
- // the string on the left of '=' is the field name
- wxString strLHS(pc, pEqualSign - pc);
-
- // eat whitespace
- for ( pc = pEqualSign + 1; wxIsspace(*pc); pc++ )
- ;
-
- const wxChar *pEnd;
- if ( *pc == wxT('"') )
- {
- // the string is quoted and ends at the matching quote
- pEnd = wxStrchr(++pc, wxT('"'));
- if ( pEnd == NULL )
- {
- wxLogWarning(wxT("Mime.types file %s, line %lu: unterminated quoted string."),
- strFileName.c_str(), nLine + 1L);
- }
- }
- else
- {
- // unquoted string ends at the first space or at the end of
- // line
- for ( pEnd = pc; *pEnd && !wxIsspace(*pEnd); pEnd++ )
- ;
- }
-
- // now we have the RHS (field value)
- wxString strRHS(pc, pEnd - pc);
-
- // check what follows this entry
- if ( *pEnd == wxT('"') )
- {
- // skip this quote
- pEnd++;
- }
-
- for ( pc = pEnd; wxIsspace(*pc); pc++ )
- ;
-
- // if there is something left, it may be either a '\\' to continue
- // the line or the next field of the same entry
- bool entryEnded = *pc == wxT('\0');
- bool nextFieldOnSameLine = false;
- if ( !entryEnded )
- {
- nextFieldOnSameLine = ((*pc != wxT('\\')) || (pc[1] != wxT('\0')));
- }
-
- // now see what we got
- if ( strLHS == wxT("type") )
- {
- strMimeType = strRHS;
- }
- else if ( strLHS.StartsWith(wxT("desc")) )
- {
- strDesc = strRHS;
- }
- else if ( strLHS == wxT("exts") )
- {
- strExtensions = strRHS;
- }
- else if ( strLHS == wxT("icon") )
- {
- // this one is simply ignored: it usually refers to Netscape
- // built in icons which are useless for us anyhow
- }
- else if ( !strLHS.StartsWith(wxT("x-")) )
- {
- // we suppose that all fields starting with "X-" are
- // unregistered extensions according to the standard practice,
- // but it may be worth telling the user about other junk in
- // his mime.types file
- wxLogWarning(wxT("Unknown field in file %s, line %lu: '%s'."),
- strFileName.c_str(), nLine + 1L, strLHS.c_str());
- }
-
- if ( !entryEnded )
- {
- if ( !nextFieldOnSameLine )
- pc = NULL;
- //else: don't reset it
-
- // as we don't reset strMimeType, the next field in this entry
- // will be interpreted correctly.
-
- continue;
- }
- }
-
- // depending on the format (Mosaic or Netscape) either space or comma
- // is used to separate the extensions
- strExtensions.Replace(wxT(","), wxT(" "));
-
- // also deal with the leading dot
- if ( !strExtensions.empty() && strExtensions[0u] == wxT('.') )
- {
- strExtensions.erase(0, 1);
- }
-
- wxLogTrace(TRACE_MIME, wxT("mime.types: '%s' => '%s' (%s)"),
- strExtensions.c_str(),
- strMimeType.c_str(),
- strDesc.c_str());
-
- AddMimeTypeInfo(strMimeType, strExtensions, strDesc);
-
- // finished with this line
- pc = NULL;
- }
-
- return true;
-}
-
-// ----------------------------------------------------------------------------
-// UNIX mailcap files parsing
-// ----------------------------------------------------------------------------
-
-// the data for a single MIME type
-struct MailcapLineData
-{
- // field values
- wxString type,
- cmdOpen,
- test,
- icon,
- desc;
-
- wxArrayString verbs,
- commands;
-
- // flags
- bool testfailed,
- needsterminal,
- copiousoutput;
-
- MailcapLineData() { testfailed = needsterminal = copiousoutput = false; }
-};
-
-// process a non-standard (i.e. not the first or second one) mailcap field
-bool
-wxMimeTypesManagerImpl::ProcessOtherMailcapField(MailcapLineData& data,
- const wxString& curField)
-{
- if ( curField.empty() )
- {
- // we don't care
- return true;
- }
-
- // is this something of the form foo=bar?
- const wxChar *pEq = wxStrchr(curField, wxT('='));
- if ( pEq != NULL )
- {
- // split "LHS = RHS" in 2
- wxString lhs = curField.BeforeFirst(wxT('=')),
- rhs = curField.AfterFirst(wxT('='));
-
- lhs.Trim(true); // from right
- rhs.Trim(false); // from left
-
- // it might be quoted
- if ( !rhs.empty() && rhs[0u] == wxT('"') && rhs.Last() == wxT('"') )
- {
- rhs = rhs.Mid(1, rhs.length() - 2);
- }
-
- // is it a command verb or something else?
- if ( lhs == wxT("test") )
- {
- if ( wxSystem(rhs) == 0 )
- {
- // ok, test passed
- wxLogTrace(TRACE_MIME_TEST,
- wxT("Test '%s' for mime type '%s' succeeded."),
- rhs.c_str(), data.type.c_str());
- }
- else
- {
- wxLogTrace(TRACE_MIME_TEST,
- wxT("Test '%s' for mime type '%s' failed, skipping."),
- rhs.c_str(), data.type.c_str());
-
- data.testfailed = true;
- }
- }
- else if ( lhs == wxT("desc") )
- {
- data.desc = rhs;
- }
- else if ( lhs == wxT("x11-bitmap") )
- {
- data.icon = rhs;
- }
- else if ( lhs == wxT("notes") )
- {
- // ignore
- }
- else // not a (recognized) special case, must be a verb (e.g. "print")
- {
- data.verbs.Add(lhs);
- data.commands.Add(rhs);
- }
- }
- else // '=' not found
- {
- // so it must be a simple flag
- if ( curField == wxT("needsterminal") )
- {
- data.needsterminal = true;
- }
- else if ( curField == wxT("copiousoutput"))
- {
- // copiousoutput impies that the viewer is a console program
- data.needsterminal =
- data.copiousoutput = true;
- }
- else if ( !IsKnownUnimportantField(curField) )
- {
- return false;
- }
- }
-
- return true;
-}
-
-bool wxMimeTypesManagerImpl::ReadMailcap(const wxString& strFileName,
- bool fallback)
-{
- wxLogTrace(TRACE_MIME, wxT("--- Parsing mailcap file '%s' ---"),
- strFileName.c_str());
-
- wxTextFile file(strFileName);
-#if defined(__WXGTK20__) && wxUSE_UNICODE
- if ( !file.Open(wxConvUTF8) )
-#else
- if ( !file.Open() )
-#endif
- return false;
-
- // indices of MIME types (in m_aTypes) we already found in this file
- //
- // (see the comments near the end of function for the reason we need this)
- wxArrayInt aIndicesSeenHere;
-
- // accumulator for the current field
- wxString curField;
- curField.reserve(1024);
-
- size_t nLineCount = file.GetLineCount();
- for ( size_t nLine = 0; nLine < nLineCount; nLine++ )
- {
- // now we're at the start of the line
- const wxChar *pc = file[nLine].c_str();
-
- // skip whitespace
- while ( wxIsspace(*pc) )
- pc++;
-
- // comment or empty string?
- if ( *pc == wxT('#') || *pc == wxT('\0') )
- continue;
-
- // no, do parse
- // ------------
-
- // what field are we currently in? The first 2 are fixed and there may
- // be an arbitrary number of other fields parsed by
- // ProcessOtherMailcapField()
- //
- // the first field is the MIME type
- enum
- {
- Field_Type,
- Field_OpenCmd,
- Field_Other
- }
- currentToken = Field_Type;
-
- // the flags and field values on the current line
- MailcapLineData data;
-
- bool cont = true;
- while ( cont )
- {
- switch ( *pc )
- {
- case wxT('\\'):
- // interpret the next character literally (notice that
- // backslash can be used for line continuation)
- if ( *++pc == wxT('\0') )
- {
- // fetch the next line if there is one
- if ( nLine == nLineCount - 1 )
- {
- // something is wrong, bail out
- cont = false;
-
- wxLogDebug(wxT("Mailcap file %s, line %lu: '\\' on the end of the last line ignored."),
- strFileName.c_str(),
- nLine + 1L);
- }
- else
- {
- // pass to the beginning of the next line
- pc = file[++nLine].c_str();
-
- // skip pc++ at the end of the loop
- continue;
- }
- }
- else
- {
- // just a normal character
- curField += *pc;
- }
- break;
-
- case wxT('\0'):
- cont = false; // end of line reached, exit the loop
-
- // fall through to still process this field
-
- case wxT(';'):
- // trim whitespaces from both sides
- curField.Trim(true).Trim(false);
-
- switch ( currentToken )
- {
- case Field_Type:
- data.type = curField.Lower();
- if ( data.type.empty() )
- {
- // I don't think that this is a valid mailcap
- // entry, but try to interpret it somehow
- data.type = wxT('*');
- }
-
- if ( data.type.Find(wxT('/')) == wxNOT_FOUND )
- {
- // we interpret "type" as "type/*"
- data.type += wxT("/*");
- }
-
- currentToken = Field_OpenCmd;
- break;
-
- case Field_OpenCmd:
- data.cmdOpen = curField;
-
- currentToken = Field_Other;
- break;
-
- case Field_Other:
- if ( !ProcessOtherMailcapField(data, curField) )
- {
- // don't flood the user with error messages if
- // we don't understand something in his
- // mailcap, but give them in debug mode because
- // this might be useful for the programmer
- wxLogDebug
- (
- wxT("Mailcap file %s, line %lu: unknown field '%s' for the MIME type '%s' ignored."),
- strFileName.c_str(),
- nLine + 1L,
- curField.c_str(),
- data.type.c_str()
- );
- }
- else if ( data.testfailed )
- {
- // skip this entry entirely
- cont = false;
- }
-
- // it already has this value
- //currentToken = Field_Other;
- break;
-
- default:
- wxFAIL_MSG(wxT("unknown field type in mailcap"));
- }
-
- // next token starts immediately after ';'
- curField.Empty();
- break;
-
- default:
- curField += *pc;
- }
-
- // continue in the same line
- pc++;
- }
-
- // we read the entire entry, check what have we got
- // ------------------------------------------------
-
- // check that we really read something reasonable
- if ( currentToken < Field_Other )
- {
- wxLogWarning(wxT("Mailcap file %s, line %lu: incomplete entry ignored."),
- strFileName.c_str(), nLine + 1L);
-
- continue;
- }
-
- // if the test command failed, it's as if the entry were not there at all
- if ( data.testfailed )
- {
- continue;
- }
-
- // support for flags:
- // 1. create an xterm for 'needsterminal'
- // 2. append "| $PAGER" for 'copiousoutput'
- //
- // Note that the RFC says that having both needsterminal and
- // copiousoutput is probably a mistake, so it seems that running
- // programs with copiousoutput inside an xterm as it is done now
- // is a bad idea (FIXME)
- if ( data.copiousoutput )
- {
- const wxChar *p = wxGetenv(wxT("PAGER"));
- data.cmdOpen << wxT(" | ") << (p ? p : wxT("more"));
- }
-
- if ( data.needsterminal )
- {
- data.cmdOpen = wxString::Format(wxT("xterm -e sh -c '%s'"),
- data.cmdOpen.c_str());
- }
-
- if ( !data.cmdOpen.empty() )
- {
- data.verbs.Insert(wxT("open"), 0);
- data.commands.Insert(data.cmdOpen, 0);
- }
-
- // we have to decide whether the new entry should replace any entries
- // for the same MIME type we had previously found or not
- bool overwrite;
-
- // the fall back entries have the lowest priority, by definition
- if ( fallback )
- {
- overwrite = false;
- }
- else
- {
- // have we seen this one before?
- int nIndex = m_aTypes.Index(data.type);
-
- // and if we have, was it in this file? if not, we should
- // overwrite the previously seen one
- overwrite = nIndex == wxNOT_FOUND ||
- aIndicesSeenHere.Index(nIndex) == wxNOT_FOUND;
- }
-
- wxLogTrace(TRACE_MIME, wxT("mailcap %s: %s [%s]"),
- data.type.c_str(), data.cmdOpen.c_str(),
- overwrite ? wxT("replace") : wxT("add"));
-
- int n = AddToMimeData
- (
- data.type,
- data.icon,
- new wxMimeTypeCommands(data.verbs, data.commands),
- wxArrayString() /* extensions */,
- data.desc,
- overwrite
- );
-
- if ( overwrite )
- {
- aIndicesSeenHere.Add(n);
- }
- }
-
- return true;
-}
-