Initial checkin. BPrivate::Storage::Mime::AssociatedTypes class, which

is responsible for managing associations between mime types and
file extensions.


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@1146 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Tyler Dauwalder 2002-09-24 05:20:30 +00:00
parent 0af8a6afe0
commit 09b4d458da
2 changed files with 509 additions and 0 deletions

View File

@ -0,0 +1,58 @@
//----------------------------------------------------------------------
// This software is part of the OpenBeOS distribution and is covered
// by the OpenBeOS license.
//---------------------------------------------------------------------
/*!
\file AssociatedTypes.h
AssociatedTypes class declarations
*/
#ifndef _MIME_ASSOCIATED_TYPES_H
#define _MIME_ASSOCIATED_TYPES_H
#include <SupportDefs.h>
#include <map>
#include <set>
#include <string>
class BMessage;
class BString;
namespace BPrivate {
namespace Storage {
namespace Mime {
class AssociatedTypes {
public:
AssociatedTypes();
~AssociatedTypes();
status_t GetAssociatedTypes(const char *extension, BMessage *types);
status_t GuessMimeType(const char *filename, BString *result);
status_t GuessMimeType(const entry_ref *ref, BString *result);
status_t SetFileExtensions(const char *type, const BMessage *extensions);
status_t DeleteFileExtensions(const char *type);
void PrintToStream() const;
private:
status_t AddAssociatedType(const char *extension, const char *type);
status_t RemoveAssociatedType(const char *extension, const char *type);
status_t BuildAssociatedTypesTable();
status_t ProcessType(const char *type);
std::string PrepExtension(const char *extension) const;
std::map<std::string, std::set<std::string> > fFileExtensions; // mime type => set of associated file extensions
std::map<std::string, std::set<std::string> > fAssociatedTypes; // file extension => set of associated mime types
bool fHaveDoneFullBuild;
};
} // namespace Mime
} // namespace Storage
} // namespace BPrivate
#endif // _MIME_ASSOCIATED_TYPES_H

View File

@ -0,0 +1,451 @@
//----------------------------------------------------------------------
// This software is part of the OpenBeOS distribution and is covered
// by the OpenBeOS license.
//---------------------------------------------------------------------
/*!
\file AssociatedTypes.cpp
AssociatedTypes class implementation
*/
#include "mime/AssociatedTypes.h"
#include <Directory.h>
#include <Entry.h>
#include <Message.h>
#include <MimeType.h>
#include <Path.h>
#include <String.h>
#include <kernel_interface.h>
#include <mime/database_support.h>
#include <storage_support.h>
#include <new>
#include <stdio.h>
#define DBG(x) x
//#define DBG(x)
#define OUT printf
namespace BPrivate {
namespace Storage {
namespace Mime {
/*!
\class AssociatedTypes
\brief Information about file extensions and their associated types
*/
// Constructor
//! Constructs a new AssociatedTypes object
AssociatedTypes::AssociatedTypes()
: fHaveDoneFullBuild(false)
{
}
// Destructor
//! Destroys the AssociatedTypes object
AssociatedTypes::~AssociatedTypes()
{
}
// GetAssociatedTypes
/*! \brief Returns a list of mime types associated with the given file
extension in the pre-allocated \c BMessage pointed to by \c types.
See \c BMimeType::GetAssociatedTypes() for more information.
*/
status_t
AssociatedTypes::GetAssociatedTypes(const char *extension, BMessage *types)
{
status_t err = extension && types ? B_OK : B_BAD_VALUE;
std::string extStr;
// See if we need to do our initial build still
if (!err && !fHaveDoneFullBuild) {
err = BuildAssociatedTypesTable();
}
// Format the extension
if (!err) {
extStr = PrepExtension(extension);
err = extStr.length() > 0 ? B_OK : B_BAD_VALUE;
}
// Build the message
if (!err) {
// Clear the message, as we're just going to add to it
types->MakeEmpty();
// Add the types associated with this extension
std::set<std::string> &assTypes = fAssociatedTypes[extStr];
std::set<std::string>::const_iterator i;
for (i = assTypes.begin(); i != assTypes.end() && !err; i++) {
err = types->AddString(kTypesField, i->c_str());
}
}
return err;
}
// GuessMimeType
/*! \brief Guesses a MIME type for the given filename based on its extension
\param filename The filename of interest
\param result Pointer to a pre-allocated \c BString object into which
the result is stored. If the function returns a value other
than \c B_OK, \a result will not be modified.
\return
- \c B_OK: success
- \c other error code: failure
*/
status_t
AssociatedTypes::GuessMimeType(const char *filename, BString *result)
{
status_t err = filename && result ? B_OK : B_BAD_VALUE;
if (!err && !fHaveDoneFullBuild)
err = BuildAssociatedTypesTable();
if (!err) {
// Extract the extension from the file
uint i = strlen(filename);
while (i-1 >= 0 && filename[i-1] != '.')
i--;
// If there was an extension, grab it and look up its associated
// type(s). Otherwise, the best guess we can offer is
// "application/octect-stream"
if (i > 0) {
std::string extension = PrepExtension(&(filename[i]));
/*! \todo I'm just grabbing the first item in the set here. Should we perhaps
do something different?
*/
std::set<std::string> &types = fAssociatedTypes[extension];
std::set<std::string>::const_iterator i = types.begin();
if (i != types.end())
result->SetTo(i->c_str());
else
err = kMimeGuessFailureError;
} else {
err = kMimeGuessFailureError;
}
}
return err;
}
// GuessMimeType
/*! \brief Guesses a MIME type for the given \c entry_ref based on its filename extension
\param filename The entry_ref of interest
\param result Pointer to a pre-allocated \c BString object into which
the result is stored. If the function returns a value other
than \c B_OK, \a result will not be modified.
\return
- \c B_OK: success
- \c other error code: failure
*/
status_t
AssociatedTypes::GuessMimeType(const entry_ref *ref, BString *result)
{
// Convert the entry_ref to a filename and then do the check
/*! \todo If the ref is invalid, */
char path[B_PATH_NAME_LENGTH];
status_t err = entry_ref_to_path(ref, path, B_PATH_NAME_LENGTH);
if (!err)
err = GuessMimeType(path, result);
return err;
}
// SetFileExtensions
/*! \brief Sets the list of file extensions for the given type and
updates the associated types mappings.
All listed extensions will including the given mime type in
their list of associated types following this call.
All extensions previously but no longer associated with this
mime type will no longer list this mime type as an associated
type.
\param app The mime type whose associated file extensions you are setting
\param types Pointer to a \c BMessage containing an array of associated
file extensions in its \c Mime::kExtensionsField field.
*/
status_t
AssociatedTypes::SetFileExtensions(const char *type, const BMessage *extensions)
{
status_t err = type && extensions ? B_OK : B_BAD_VALUE;
if (!fHaveDoneFullBuild)
return err;
std::set<std::string> oldExtensions;
std::set<std::string> &newExtensions = fFileExtensions[type];
// Make a copy of the previous extensions
if (!err)
oldExtensions = newExtensions;
if (!err) {
// Read through the list of new extensions, creating the new
// file extensions list and adding the type as an associated type
// for each extension
newExtensions.clear();
const char *extension;
for (int32 i = 0;
extensions->FindString(kTypesField, i, &extension) == B_OK;
i++)
{
newExtensions.insert(extension);
AddAssociatedType(extension, type);
}
// Remove any extensions that are still associated from the list
// of previously associated extensions
for (std::set<std::string>::const_iterator i = newExtensions.begin();
i != newExtensions.end();
i++)
{
oldExtensions.erase(*i);
}
// Now remove the type as an associated type for any of its previously
// but no longer associated extensions
for (std::set<std::string>::const_iterator i = oldExtensions.begin();
i != oldExtensions.end();
i++)
{
RemoveAssociatedType(i->c_str(), type);
}
}
return err;
}
// DeleteFileExtensions
/*! \brief Clears the given types's file extensions list and removes the
types from each of said extensions' associated types list.
\param app The mime type whose file extensions you are clearing
*/
status_t
AssociatedTypes::DeleteFileExtensions(const char *type)
{
BMessage extensions;
return SetFileExtensions(type, &extensions);
}
// PrintToStream
//! Dumps the associated types mapping to standard output
void
AssociatedTypes::PrintToStream() const
{
printf("\n");
printf("-----------------\n");
printf("Associated Types:\n");
printf("-----------------\n");
for (std::map<std::string, std::set<std::string> >::const_iterator i = fAssociatedTypes.begin();
i != fAssociatedTypes.end();
i++)
{
printf("%s: ", i->first.c_str());
fflush(stdout);
bool first = true;
for (std::set<std::string>::const_iterator type = i->second.begin();
type != i->second.end();
type++)
{
if (first)
first = false;
else
printf(", ");
printf("%s", type->c_str());
fflush(stdout);
}
printf("\n");
}
}
// AddAssociatedType
/*! \brief Adds the given mime type to the set of associated types
for the given extension.
\param extension The file extension
\param type The associated mime type
\return
- B_OK: success, even if the type was already in the associated types list
- "error code": failure
*/
status_t
AssociatedTypes::AddAssociatedType(const char *extension, const char *type)
{
status_t err = extension && type ? B_OK : B_BAD_VALUE;
std::string extStr;
if (!err) {
extStr = PrepExtension(extension);
err = extStr.length() > 0 ? B_OK : B_BAD_VALUE;
}
if (!err)
fAssociatedTypes[extStr].insert(type);
return err;
}
// RemoveAssociatedType
/*! \brief Removes the given mime type from the set of associated types
for the given extension.
\param extension The file extension
\param type The associated mime type
\return
- B_OK: success, even if the type was not found in the associated types list
- "error code": failure
*/
status_t
AssociatedTypes::RemoveAssociatedType(const char *extension, const char *type)
{
status_t err = extension && type ? B_OK : B_BAD_VALUE;
std::string extStr;
if (!err) {
extStr = PrepExtension(extension);
err = extStr.length() > 0 ? B_OK : B_BAD_VALUE;
}
if (!err)
fAssociatedTypes[extension].erase(type);
return err;
}
// BuildAssociatedTypesTable
/*! \brief Crawls the mime database and builds a list of associated types
for every associated file extension.
*/
status_t
AssociatedTypes::BuildAssociatedTypesTable()
{
fFileExtensions.clear();
fAssociatedTypes.clear();
BDirectory root;
status_t err = root.SetTo(kDatabaseDir.c_str());
if (!err) {
root.Rewind();
while (true) {
BEntry entry;
err = root.GetNextEntry(&entry);
if (err) {
// If we've come to the end of list, it's not an error
if (err == B_ENTRY_NOT_FOUND)
err = B_OK;
break;
} else {
// Check that this entry is both a directory and a valid MIME string
char supertype[B_PATH_NAME_LENGTH];
if (entry.IsDirectory()
&& entry.GetName(supertype) == B_OK
&& BMimeType::IsValid(supertype))
{
// Make sure the supertype string is all lowercase
BPrivate::Storage::to_lower(supertype);
// First, iterate through this supertype directory and process
// all of its subtypes
BDirectory dir;
if (dir.SetTo(&entry) == B_OK) {
dir.Rewind();
while (true) {
BEntry subEntry;
err = dir.GetNextEntry(&subEntry);
if (err) {
// If we've come to the end of list, it's not an error
if (err == B_ENTRY_NOT_FOUND)
err = B_OK;
break;
} else {
// Get the subtype's name
char subtype[B_PATH_NAME_LENGTH];
if (subEntry.GetName(subtype) == B_OK) {
BPrivate::Storage::to_lower(subtype);
char fulltype[B_PATH_NAME_LENGTH];
sprintf(fulltype, "%s/%s", supertype, subtype);
// Process the subtype
ProcessType(fulltype);
}
}
}
} else {
DBG(OUT("Mime::AssociatedTypes::BuildAssociatedTypesTable(): "
"Failed opening supertype directory '%s'\n",
supertype));
}
// Second, process the supertype
ProcessType(supertype);
}
}
}
} else {
DBG(OUT("Mime::AssociatedTypes::BuildAssociatedTypesTable(): "
"Failed opening mime database directory '%s'\n",
kDatabaseDir.c_str()));
}
if (!err) {
fHaveDoneFullBuild = true;
// PrintToStream();
}
else
DBG(OUT("Mime::AssociatedTypes::BuildAssociatedTypesTable() failed, error code == 0x%lx\n", err));
return err;
}
// ProcessType
/*! \brief Handles a portion of the initial associated types table construction for
the given mime type.
\note To be called by BuildAssociatedTypesTable() *ONLY*. :-)
\param type The mime type of interest. The mime string is expected to be valid
and lowercase. Both "supertype" and "supertype/subtype" mime types
are allowed.
*/
status_t
AssociatedTypes::ProcessType(const char *type)
{
status_t err = type ? B_OK : B_BAD_VALUE;
if (!err) {
// Read in the list of file extension types
BMessage msg;
if (read_mime_attr_message(type, kFileExtensionsAttr, &msg) == B_OK) {
// Iterate through the file extesions, adding them to the list of
// file extensions for the mime type and adding the mime type
// to the list of associated types for each file extension
const char *extension;
std::set<std::string> &fileExtensions = fFileExtensions[type];
for (int i = 0; msg.FindString(kExtensionsField, i, &extension) == B_OK; i++) {
std::string extStr = PrepExtension(extension);
if (extStr.length() > 0) {
fileExtensions.insert(extStr);
AddAssociatedType(extStr.c_str(), type);
}
}
}
}
return err;
}
// PrepExtension
/*! \brief Strips any leading '.' chars from the given extension and
forces all chars to lowercase.
*/
std::string
AssociatedTypes::PrepExtension(const char *extension) const
{
if (extension) {
uint i = 0;
while (extension[i] == '.')
i++;
return BPrivate::Storage::to_lower(&(extension[i]));
} else {
return ""; // This shouldn't ever happen, but if it does, an
// empty string is considered an invalid extension
}
}
} // namespace Mime
} // namespace Storage
} // namespace BPrivate