* Copyright 2002-2006, Haiku.
* Distributed under the terms of the MIT License.
*
* Authors:
* Tyler Dauwalder
* Ingo Weinhold, bonefish@users.sf.net
* Axel Dörfler, axeld@pinc-software.de
*/
#include <mime/InstalledTypes.h>
#include <stdio.h>
#include <new>
#include <Directory.h>
#include <Entry.h>
#include <Message.h>
#include <MimeType.h>
#include <String.h>
#include <mime/database_support.h>
#include <mime/DatabaseDirectory.h>
#include <storage_support.h>
#define DBG(x) x
#define OUT printf
namespace BPrivate {
namespace Storage {
namespace Mime {
\class InstalledTypes
\brief Installed types information for the entire database
*/
InstalledTypes::InstalledTypes(DatabaseLocation* databaseLocation)
:
fDatabaseLocation(databaseLocation),
fCachedMessage(NULL),
fCachedSupertypesMessage(NULL),
fHaveDoneFullBuild(false)
{
}
InstalledTypes::~InstalledTypes()
{
delete fCachedSupertypesMessage;
delete fCachedMessage;
}
pre-allocated \c BMessage pointed to by \c types.
See \c BMimeType::GetInstalledTypes(BMessage*) for more information.
*/
status_t
InstalledTypes::GetInstalledTypes(BMessage *types)
{
status_t err = types ? B_OK : B_BAD_VALUE;
if (!err && !fHaveDoneFullBuild)
err = _BuildInstalledTypesList();
if (!err && !fCachedMessage)
err = _CreateMessageWithTypes(&fCachedMessage);
if (!err)
*types = *fCachedMessage;
return err;
}
supertype in the pre-allocated \c BMessage pointed to by \c types.
See \c BMimeType::GetInstalledTypes(const char*, BMessage*) for more
information.
*/
status_t
InstalledTypes::GetInstalledTypes(const char *supertype, BMessage *types)
{
if (supertype == NULL || types == NULL)
return B_BAD_VALUE;
BMimeType mime;
BMimeType super;
status_t err = mime.SetTo(supertype);
if (!err && !mime.IsSupertypeOnly())
err = B_BAD_VALUE;
if (!err && !fHaveDoneFullBuild)
err = _BuildInstalledTypesList();
if (!err) {
std::map<std::string, Supertype>::iterator i = fSupertypes.find(supertype);
if (i != fSupertypes.end())
err = i->second.GetInstalledSubtypes(types);
else
err = B_NAME_NOT_FOUND;
}
return err;
}
pre-allocated \c BMessage pointed to by \c types.
See \c BMimeType::GetInstalledSupertypes() for more information.
*/
status_t
InstalledTypes::GetInstalledSupertypes(BMessage *types)
{
if (types == NULL)
return B_BAD_VALUE;
status_t err = B_OK;
if (!fHaveDoneFullBuild)
err = _BuildInstalledTypesList();
if (!err && !fCachedSupertypesMessage)
err = _CreateMessageWithSupertypes(&fCachedSupertypesMessage);
if (!err)
*types = *fCachedSupertypesMessage;
return err;
}
If cached messages exist, the type is simply appended to the end of
the current type list.
*/
status_t
InstalledTypes::AddType(const char *type)
{
if (!fHaveDoneFullBuild)
return B_OK;
BMimeType mime(type);
if (type == NULL || mime.InitCheck() != B_OK)
return B_BAD_VALUE;
uint i;
size_t len = strlen(type);
for (i = 0; i < len; i++) {
if (type[i] == '/')
break;
}
if (i == len) {
return _AddSupertype(type);
}
char super[B_PATH_NAME_LENGTH];
strncpy(super, type, i);
super[i] = 0;
const char *sub = &(type[i+1]);
return _AddSubtype(super, sub);
}
Any corresponding cached messages are invalidated.
*/
status_t
InstalledTypes::RemoveType(const char *type)
{
if (!fHaveDoneFullBuild)
return B_OK;
BMimeType mime(type);
if (type == NULL || mime.InitCheck() != B_OK)
return B_BAD_VALUE;
uint i;
size_t len = strlen(type);
for (i = 0; i < len; i++) {
if (type[i] == '/')
break;
}
if (i == len) {
return _RemoveSupertype(type);
}
char super[B_PATH_NAME_LENGTH];
strncpy(super, type, i);
super[i] = 0;
const char *sub = &(type[i+1]);
return _RemoveSubtype(super, sub);
}
\return
- B_OK: success, even if the supertype already existed in the map
- "error code": failure
*/
status_t
InstalledTypes::_AddSupertype(const char* super)
{
if (super == NULL)
return B_BAD_VALUE;
status_t err = B_OK;
if (fSupertypes.find(super) == fSupertypes.end()) {
Supertype &supertype = fSupertypes[super];
supertype.SetName(super);
if (fCachedMessage)
err = fCachedMessage->AddString(kTypesField, super);
if (!err && fCachedSupertypesMessage)
err = fCachedSupertypesMessage->AddString(kSupertypesField, super);
}
return err;
}
If the supertype does not yet exist, it is created.
\param super The supertype
\param sub The subtype (subtype only; no "supertype/subtype" types please)
\return
- B_OK: success
- B_NAME_IN_USE: The subtype already exists in the subtype list
- "error code": failure
*/
status_t
InstalledTypes::_AddSubtype(const char *super, const char *sub)
{
if (super == NULL || sub == NULL)
return B_BAD_VALUE;
status_t err = _AddSupertype(super);
if (!err)
err = _AddSubtype(fSupertypes[super], sub);
return err;
}
\param super The supertype object
\param sub The subtype (subtype only; no "supertype/subtype" types please)
\return
- B_OK: success
- B_NAME_IN_USE: The subtype already exists in the subtype list
- "error code": failure
*/
status_t
InstalledTypes::_AddSubtype(Supertype &super, const char *sub)
{
if (sub == NULL)
return B_BAD_VALUE;
status_t err = super.AddSubtype(sub);
if (!err && fCachedMessage) {
char type[B_PATH_NAME_LENGTH];
sprintf(type, "%s/%s", super.GetName(), sub);
err = fCachedMessage->AddString("types", type);
}
return err;
}
*/
status_t
InstalledTypes::_RemoveSupertype(const char *super)
{
if (super == NULL)
return B_BAD_VALUE;
status_t err = fSupertypes.erase(super) == 1 ? B_OK : B_NAME_NOT_FOUND;
if (!err)
_ClearCachedMessages();
return err;
}
*/
status_t
InstalledTypes::_RemoveSubtype(const char *super, const char *sub)
{
if (super == NULL || sub == NULL)
return B_BAD_VALUE;
status_t err = B_NAME_NOT_FOUND;
std::map<std::string, Supertype>::iterator i = fSupertypes.find(super);
if (i != fSupertypes.end()) {
err = i->second.RemoveSubtype(sub);
if (!err)
_ClearCachedMessages();
}
return err;
}
void
InstalledTypes::_Unset()
{
_ClearCachedMessages();
fSupertypes.clear();
}
void
InstalledTypes::_ClearCachedMessages()
{
delete fCachedSupertypesMessage;
delete fCachedMessage;
fCachedSupertypesMessage = NULL;
fCachedMessage = NULL;
}
An initial set of cached messages are also created.
*/
status_t
InstalledTypes::_BuildInstalledTypesList()
{
status_t err = B_OK;
_Unset();
try {
fCachedMessage = new BMessage();
fCachedSupertypesMessage = new BMessage();
} catch (std::bad_alloc&) {
err = B_NO_MEMORY;
}
DatabaseDirectory root;
if (!err)
err = root.Init(fDatabaseLocation);
if (!err) {
root.Rewind();
while (true) {
BEntry entry;
err = root.GetNextEntry(&entry);
if (err) {
if (err == B_ENTRY_NOT_FOUND)
err = B_OK;
break;
} else {
char supertype[B_PATH_NAME_LENGTH];
if (entry.IsDirectory()
&& entry.GetName(supertype) == B_OK
&& BMimeType::IsValid(supertype))
{
BPrivate::Storage::to_lower(supertype);
if (_AddSupertype(supertype) != B_OK)
DBG(OUT("Mime::InstalledTypes::BuildInstalledTypesList()"
" -- Error adding supertype '%s': 0x%" B_PRIx32 "\n",
supertype, err));
Supertype &supertypeRef = fSupertypes[supertype];
DatabaseDirectory dir;
if (dir.Init(fDatabaseLocation, supertype) == B_OK) {
dir.Rewind();
while (true) {
BEntry subEntry;
err = dir.GetNextEntry(&subEntry);
if (err) {
if (err == B_ENTRY_NOT_FOUND)
err = B_OK;
break;
} else {
BString type;
int32 subStart;
BNode node(&subEntry);
if (node.InitCheck() == B_OK
&& node.ReadAttrString(kTypeAttr, &type) >= B_OK
&& (subStart = type.FindFirst('/')) > 0) {
if (_AddSubtype(supertypeRef, type.String()
+ subStart + 1) != B_OK) {
DBG(OUT("Mime::InstalledTypes::BuildInstalledTypesList()"
" -- Error adding subtype '%s/%s': 0x%" B_PRIx32 "\n",
supertype, type.String() + subStart + 1, err));
}
}
}
}
} else {
DBG(OUT("Mime::InstalledTypes::BuildInstalledTypesList(): "
"Failed opening supertype directory '%s'\n",
supertype));
}
}
}
}
} else {
DBG(OUT("Mime::InstalledTypes::BuildInstalledTypesList(): "
"Failed opening mime database directory.\n"));
}
fHaveDoneFullBuild = true;
return err;
}
and fills it with a complete list of installed types.
*/
status_t
InstalledTypes::_CreateMessageWithTypes(BMessage **_result) const
{
if (_result == NULL)
return B_BAD_VALUE;
status_t err = B_OK;
try {
*_result = new BMessage();
} catch (std::bad_alloc&) {
err = B_NO_MEMORY;
}
if (!err) {
BMessage &msg = **_result;
std::map<std::string, Supertype>::const_iterator i;
for (i = fSupertypes.begin(); i != fSupertypes.end() && !err; i++) {
err = msg.AddString(kTypesField, i->first.c_str());
if (!err)
err = i->second.FillMessageWithTypes(msg);
}
}
return err;
}
and fills it with a complete list of installed supertypes.
*/
status_t
InstalledTypes::_CreateMessageWithSupertypes(BMessage **_result) const
{
if (_result == NULL)
return B_BAD_VALUE;
status_t err = B_OK;
try {
*_result = new BMessage();
} catch (std::bad_alloc&) {
err = B_NO_MEMORY;
}
if (!err) {
BMessage &msg = **_result;
std::map<std::string, Supertype>::const_iterator i;
for (i = fSupertypes.begin(); i != fSupertypes.end() && !err; i++) {
err = msg.AddString(kSupertypesField, i->first.c_str());
}
}
return err;
}
}
}
}