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:
parent
0af8a6afe0
commit
09b4d458da
58
headers/private/storage/mime/AssociatedTypes.h
Normal file
58
headers/private/storage/mime/AssociatedTypes.h
Normal 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
|
451
src/kits/storage/mime/AssociatedTypes.cpp
Normal file
451
src/kits/storage/mime/AssociatedTypes.cpp
Normal 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
|
||||
|
Loading…
Reference in New Issue
Block a user