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:
@@ -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
|
||||||
@@ -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
|
||||||
|
|
||||||
Reference in New Issue
Block a user