haiku/src/bin/setmime.cpp

1174 lines
30 KiB
C++

/*
* Copyright 2011 Aleksas Pantechovskis, <alexp.frl@gmail.com>
* Copyright 2011 Siarzhuk Zharski, <imker@gmx.li>
* All rights reserved. Distributed under the terms of the MIT License.
*/
#include <iomanip>
#include <iostream>
#include <map>
#include <math.h>
#include <set>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <vector>
#include <Application.h>
#include <Bitmap.h>
#include <Entry.h>
#include <InterfaceDefs.h>
#include <Message.h>
#include <Mime.h>
#include <Path.h>
#include <String.h>
using namespace std;
const char* kUsageMessage = "# setmime:\n"
"# usage: setmime ((-dump | -dumpSniffRule | -dumpIcon | -dumpAll) "
"[ <signatureString> ] )\n"
"# | (-remove <signatureString> )\n"
"# | ( (-set | -force | -add) <signatureString>\n"
"# [ -short <short description> ] [ -long <long description> ]\n"
"# [ -preferredApp <preferred app path> ]\n"
"# [ -preferredAppSig <preferred app signature> ]\n"
"# [ -sniffRule <sniffRule> ]\n"
"# [ -extension <file suffix> ]\n"
"# [ -attribute <internal name>\n"
"# [ -attrName <public name> ] [ -attrType <type code> ]\n"
"# [ -attrWidth <display width> ][ -attrAlignment <position>]\n"
"# [ -attrViewable <bool flag> ][ -attrEditable <bool flag> ]\n"
"# [ -attrExtra <bool flag> ] ]\n"
"# [ -miniIcon <256 hex bytes> ]\n"
"# [ -largeIcon <1024 hex bytes> ]\n"
"# [ -vectorIcon <icon hex bytes> ] ... )\n"
"# | (-checkSniffRule <sniffRule>\n"
"# | -includeApps)\n";
const char* kHelpMessage = "# -dump prints a specified metamime\n"
"# -remove removes specified metamime\n"
"# -add adds specified metamime and specified metamime attributes\n"
"# that have not yet been defined\n"
"# -set adds specified metamime and specified metamime attributes, \n"
"# overwrites the existing values of specified metamime attributes\n"
"# -force adds specified metamime and specified metamime attributes\n"
"# after first erasing all the existing attributes\n"
"# -dumpSniffRule prints just the MIME sniffer rule of a "
"specified metamime\n"
"# -dumpIcon prints just the icon information of a specified metamime\n"
"# -dumpAll prints all the information, including icons of a "
"specified metamime\n"
"# -checkSniffRule parses a MIME sniffer rule and reports any errors\n"
"# -includeApps will include applications\n";
const char* kNeedArgMessage = "you have to specify any of "
"-dump[All|Icon|SnifferRule], -add, -set, "
"-force or -remove";
const char* kWrongModeMessage = "can only specify one of -dump, -dumpAll, "
"-dumpIcon, -dumpSnifferRule, -remove, "
"-add, -set, -force or -checkSnifferRule";
const char* kHelpReq = "--help";
const char* kDump = "-dump";
const char* kDumpSniffRule = "-dumpSniffRule";
const char* kDumpIcon = "-dumpIcon";
const char* kDumpAll = "-dumpAll";
const char* kAdd = "-add";
const char* kSet = "-set";
const char* kForce = "-force";
const char* kRemove = "-remove";
const char* kCheckSniffRule = "-checkSniffRule";
const char* kShort = "-short";
const char* kLong = "-long";
const char* kPreferredApp = "-preferredApp";
const char* kPreferredAppSig = "-preferredAppSig";
const char* kSniffRule = "-sniffRule";
const char* kMiniIcon = "-miniIcon";
const char* kLargeIcon = "-largeIcon";
const char* kVectorIcon = "-vectorIcon";
const char* kIncludeApps = "-includeApps";
const char* kExtension = "-extension";
const char* kAttribute = "-attribute";
const char* kAttrName = "-attrName";
const char* kAttrType = "-attrType";
const char* kAttrWidth = "-attrWidth";
const char* kAttrAlignment = "-attrAlignment";
const char* kAttrViewable = "-attrViewable";
const char* kAttrEditable = "-attrEditable";
const char* kAttrExtra = "-attrExtra";
const uint32 hash_function(const char* str)
{
uint32 h = 0;
uint32 g = 0;
for (const char* p = str; *p; p++) {
h = (h << 4) + (*p & 0xFF);
g = h & 0xF0000000;
if (g != 0) {
h ^= g >> 24;
h ^= g;
}
}
return h;
}
// the list of all acceptable command-line options
struct CmdOption {
enum Type {
kMode,
kOption,
kAttrRoot,
kAttrib,
kHelp
};
const char* fName;
Type fType;
bool fNeedArg;
bool fNonExclusive;
} gCmdOptions[] = {
{ kHelpReq, CmdOption::kHelp },
{ kDump, CmdOption::kMode },
{ kDumpSniffRule, CmdOption::kMode },
{ kDumpIcon, CmdOption::kMode },
{ kDumpAll, CmdOption::kMode },
{ kAdd, CmdOption::kMode },
{ kSet, CmdOption::kMode },
{ kForce, CmdOption::kMode },
{ kRemove, CmdOption::kMode },
{ kCheckSniffRule, CmdOption::kMode, true },
{ kShort, CmdOption::kOption, true },
{ kLong, CmdOption::kOption, true },
{ kPreferredApp, CmdOption::kOption, true },
{ kPreferredAppSig, CmdOption::kOption, true },
{ kSniffRule, CmdOption::kOption, true },
{ kMiniIcon , CmdOption::kOption, true },
{ kLargeIcon, CmdOption::kOption, true },
{ kVectorIcon, CmdOption::kOption, true },
{ kIncludeApps, CmdOption::kOption, false },
{ kExtension, CmdOption::kOption, true, true },
{ kAttribute, CmdOption::kAttrRoot, true, true },
{ kAttrName, CmdOption::kAttrib, true },
{ kAttrType, CmdOption::kAttrib, true },
{ kAttrWidth, CmdOption::kAttrib, true },
{ kAttrAlignment, CmdOption::kAttrib, true },
{ kAttrViewable, CmdOption::kAttrib, true },
{ kAttrEditable, CmdOption::kAttrib, true },
{ kAttrExtra, CmdOption::kAttrib, true }
};
// the 'hash -> value' map of arguments provided by user
typedef multimap<uint32, const char*> TUserArgs;
typedef multimap<uint32, const char*>::iterator TUserArgsI;
// user provided attributes are grouped separately in vector
typedef vector<TUserArgs> TUserAttrs;
typedef vector<TUserArgs>::iterator TUserAttrsI;
const uint32 kOpModeUndefined = 0;
// #pragma mark -
class Error : public std::exception
{
BString fWhat;
public:
Error(const char* what, ...);
virtual ~Error() throw() {}
virtual const char* what() const throw() { return fWhat.String(); }
};
Error::Error(const char* what, ...)
{
const int size = 1024;
va_list args;
va_start(args, what);
vsnprintf(fWhat.LockBuffer(size), size, what, args);
fWhat.UnlockBuffer();
va_end(args);
}
// #pragma mark -
// encapsulate the single attribute params
//
struct MimeAttribute
{
status_t fStatus;
BString fName;
BString fPublicName;
int32 fType;
bool fViewable;
bool fEditable;
bool fExtra;
int32 fWidth;
int32 fAlignment;
MimeAttribute(BMessage& msg, int32 index);
MimeAttribute(TUserArgs& args);
MimeAttribute(const MimeAttribute& src);
status_t InitCheck() { return fStatus; }
MimeAttribute& operator=(const MimeAttribute& src);
void Dump();
void SyncWith(TUserArgs& args);
void StoreInto(BMessage* target);
const char* UserArgValue(TUserArgs& map, const char* name);
bool IsPrintableChar(char c)
{ return c >= ' ' && c < 127 && c != '\'' && c != '\\'; }
};
MimeAttribute::MimeAttribute(BMessage& msg, int32 index)
:
fStatus(B_NO_INIT),
fType('CSTR'),
fViewable(true),
fEditable(false),
fExtra(false),
fWidth(0),
fAlignment(0)
{
BString rawPublicName;
struct attrEntry {
const char* name;
type_code type;
bool required;
void* data;
} attrEntries[] = {
{ "attr:name", B_STRING_TYPE, true, &fName },
{ "attr:public_name", B_STRING_TYPE, true, &rawPublicName },
{ "attr:type", B_INT32_TYPE, true, &fType },
{ "attr:viewable", B_BOOL_TYPE, false, &fViewable },
{ "attr:editable", B_BOOL_TYPE, false, &fEditable },
{ "attr:extra", B_BOOL_TYPE, false, &fExtra },
{ "attr:width", B_INT32_TYPE, false, &fWidth },
{ "attr:alignment", B_INT32_TYPE, false, &fAlignment }
};
for (size_t i = 0; i < sizeof(attrEntries) / sizeof(attrEntries[0]); i++) {
switch (attrEntries[i].type) {
case B_STRING_TYPE:
fStatus = msg.FindString(attrEntries[i].name, index,
(BString*)attrEntries[i].data);
break;
case B_BOOL_TYPE:
fStatus = msg.FindBool(attrEntries[i].name, index,
(bool*)attrEntries[i].data);
break;
case B_INT32_TYPE:
fStatus = msg.FindInt32(attrEntries[i].name, index,
(int32*)attrEntries[i].data);
break;
}
if (attrEntries[i].required && fStatus != B_OK)
return;
}
fPublicName.CharacterEscape(rawPublicName, "\'", '\\');
fStatus = B_OK;
}
MimeAttribute::MimeAttribute(TUserArgs& args)
:
fStatus(B_NO_INIT),
fType('CSTR'),
fViewable(true),
fEditable(false),
fExtra(false),
fWidth(0),
fAlignment(0)
{
SyncWith(args);
fStatus = B_OK;
}
MimeAttribute::MimeAttribute(const MimeAttribute& src)
{
*this = src;
}
MimeAttribute&
MimeAttribute::operator=(const MimeAttribute& src)
{
fStatus = src.fStatus;
fName = src.fName;
fPublicName = src.fPublicName;
fType = src.fType;
fViewable = src.fViewable;
fEditable = src.fEditable;
fExtra = src.fExtra;
fWidth = src.fWidth;
fAlignment = src.fAlignment;
return *this;
}
void
MimeAttribute::SyncWith(TUserArgs& args)
{
const char* value = UserArgValue(args, kAttribute);
if (value != NULL)
fName.SetTo(value, B_MIME_TYPE_LENGTH);
value = UserArgValue(args, kAttrName);
if (value != NULL)
fPublicName.SetTo(value, B_MIME_TYPE_LENGTH);
value = UserArgValue(args, kAttrType);
if (value != NULL) {
fType = 0;
if (strlen(value) > 2 && value[0] == '0' && value[1] == 'x') {
stringstream ss;
ss << setbase(16) << value + 2;
ss >> fType;
} else if (strlen(value) == 4) {
for (int i = 0; i < 4 && value[i] != '\0'; i++) {
fType <<= 8;
fType |= (value[i] != '\0' ? value[i] : ' ');
}
} else
throw Error("Invalid data for %s", kAttrType);
fType = B_LENDIAN_TO_HOST_INT32(fType);
}
value = UserArgValue(args, kAttrWidth);
if (value != NULL)
fWidth = atoi(value);
value = UserArgValue(args, kAttrAlignment);
if (value != NULL) {
if (strcasecmp(value, "right") == 0) {
fAlignment = B_ALIGN_RIGHT;
} else if (strcasecmp(value, "left") == 0) {
fAlignment = B_ALIGN_LEFT;
} else if (strcasecmp(value, "center") == 0) {
fAlignment = B_ALIGN_CENTER;
} else
fAlignment = atoi(value);
}
value = UserArgValue(args, kAttrViewable);
if (value != NULL)
fViewable = atoi(value) != 0;
value = UserArgValue(args, kAttrEditable);
if (value != NULL)
fEditable = atoi(value) != 0;
value = UserArgValue(args, kAttrExtra);
if (value != NULL)
fExtra = atoi(value) != 0;
}
void
MimeAttribute::Dump()
{
uint32 type = B_HOST_TO_LENDIAN_INT32(fType);
const char* alignment = fAlignment == B_ALIGN_RIGHT ? "right"
: (fAlignment == B_ALIGN_LEFT ? "left" : "center");
cout << " \\" << endl << "\t" << kAttribute << " \"" << fName << "\" "
<< kAttrName << " \"" << fPublicName << "\"";
char c1 = (char)((type >> 24) & 0xFF);
char c2 = (char)((type >> 16) & 0xFF);
char c3 = (char)((type >> 8) & 0xFF);
char c4 = (char)(type & 0xFF);
ios::fmtflags flags = cout.flags();
cout << " \\" << endl << "\t\t" << kAttrType;
if (IsPrintableChar(c1) && IsPrintableChar(c2) &&
IsPrintableChar(c3) && IsPrintableChar(c4))
cout << " '" << c1 << c2 << c3 << c4 << "' ";
else
cout << "0x" << hex << type;
cout << " " << kAttrWidth << " " << fWidth
<< " " << kAttrAlignment << " " << alignment;
cout << " \\" << endl << "\t\t" << kAttrViewable << " " << fViewable
<< " " << kAttrEditable << " " << fEditable
<< " " << kAttrExtra << " " << fExtra;
cout.flags(flags);
}
void
MimeAttribute::StoreInto(BMessage* target)
{
struct attrEntry {
const char* name;
type_code type;
const void* data;
} attrEntries[] = {
{ "attr:name", B_STRING_TYPE, fName.String() },
{ "attr:public_name", B_STRING_TYPE, fPublicName.String() },
{ "attr:type", B_INT32_TYPE, &fType },
{ "attr:viewable", B_BOOL_TYPE, &fViewable },
{ "attr:editable", B_BOOL_TYPE, &fEditable },
{ "attr:extra", B_BOOL_TYPE, &fExtra },
{ "attr:width", B_INT32_TYPE, &fWidth },
{ "attr:alignment", B_INT32_TYPE, &fAlignment }
};
for (size_t i = 0; i < sizeof(attrEntries) / sizeof(attrEntries[0]); i++) {
switch (attrEntries[i].type) {
case B_STRING_TYPE:
fStatus = target->AddString(attrEntries[i].name,
(const char*)attrEntries[i].data);
break;
case B_BOOL_TYPE:
fStatus = target->AddBool(attrEntries[i].name,
(bool*)attrEntries[i].data);
break;
case B_INT32_TYPE:
fStatus = target->AddInt32(attrEntries[i].name,
*(int32*)attrEntries[i].data);
break;
}
if (fStatus != B_OK)
return;
}
}
const char*
MimeAttribute::UserArgValue(TUserArgs& map, const char* name)
{
TUserArgsI i = map.find(hash_function(name));
if (i == map.end())
return NULL;
return i->second != NULL ? i->second : "";
}
// #pragma mark -
// the work-horse of the app - the class encapsulates extended info readed
// from the mime type and do all edit and dump operations
//
class MimeType : public BMimeType {
public:
MimeType(char** argv);
~MimeType();
void Process();
private:
status_t _InitCheck();
void _SetTo(const char* mimetype);
void _PurgeProperties();
void _Init(char** argv);
void _DumpIcon(uint8 *iconData, size_t iconSize);
void _Dump(const char* mimetype);
void _DoEdit();
void _SetIcon(const char* iconData, int32 iconSize);
const char* _UserArgValue(const char* name);
status_t fStatus;
const char* fToolName;
// configurable MimeType properties
BString fShort;
BString fLong;
BString fPrefApp;
BString fPrefAppSig;
BString fSniffRule;
BBitmap* fSmallIcon;
BBitmap* fBigIcon;
uint8* fVectorIcon;
size_t fVectorIconSize;
map<uint32, BString> fExtensions;
map<uint32, MimeAttribute> fAttributes;
// user provided arguments
TUserArgs fUserArguments;
TUserAttrs fUserAttributes;
// operation mode switches and flags
uint32 fOpMode;
bool fDumpNormal;
bool fDumpRule;
bool fDumpIcon;
bool fDumpAll;
bool fDoAdd;
bool fDoSet;
bool fDoForce;
bool fDoRemove;
bool fCheckSniffRule;
};
MimeType::MimeType(char** argv)
:
fStatus(B_NO_INIT),
fToolName(argv[0]),
fSmallIcon(NULL),
fBigIcon(NULL),
fVectorIcon(NULL),
fVectorIconSize(0),
fOpMode(kOpModeUndefined),
fDumpNormal(false),
fDumpRule(false),
fDumpIcon(false),
fDumpAll(false),
fDoAdd(false),
fDoSet(false),
fDoForce(false),
fDoRemove(false),
fCheckSniffRule(false)
{
fToolName = strrchr(argv[0], '/');
fToolName = fToolName == NULL ? argv[0] : fToolName + 1;
_Init(++argv);
}
MimeType::~MimeType()
{
delete fSmallIcon;
delete fBigIcon;
free(fVectorIcon);
}
void
MimeType::_Init(char** argv)
{
// fill the helper map of options - for quick lookup of arguments
map<uint32, const CmdOption*> cmdOptionsMap;
for (size_t i = 0; i < sizeof(gCmdOptions) / sizeof(gCmdOptions[0]); i++)
cmdOptionsMap.insert(pair<uint32, CmdOption*>(
hash_function(gCmdOptions[i].fName), &gCmdOptions[i]));
// parse the command line arguments
for (char** arg = argv; *arg; arg++) {
// non-option arguments are assumed as signature
if (**arg != '-') {
if (Type() != NULL)
throw Error("mime signature already specified: '%s'", Type());
SetTo(*arg);
continue;
}
// check op.modes, options and attribs
uint32 key = hash_function(*arg);
map<uint32, const CmdOption*>::iterator I = cmdOptionsMap.find(key);
if (I == cmdOptionsMap.end())
throw Error("unknown option '%s'", *arg);
switch (I->second->fType) {
case CmdOption::kHelp:
cerr << kUsageMessage;
throw Error(kHelpMessage);
case CmdOption::kMode:
// op.modes are exclusive - no simultaneous possible
if (fOpMode != kOpModeUndefined)
throw Error(kWrongModeMessage);
fOpMode = key;
if (hash_function(I->second->fName) != hash_function(kCheckSniffRule))
break;
// else -> fallthrough, CheckRule works both as mode and Option
case CmdOption::kOption:
{
const char* name = *arg;
const char* param = NULL;
if (I->second->fNeedArg) {
if (!*++arg)
throw Error("argument required for '%s'", name);
param = *arg;
}
TUserArgsI A = fUserArguments.find(key);
if (A != fUserArguments.end() && !I->second->fNonExclusive)
throw Error("option '%s' already specified", name);
fUserArguments.insert(
pair<uint32, const char*>(key, param));
}
break;
case CmdOption::kAttrRoot:
if (!*++arg)
throw Error("attribute name should be specified");
fUserAttributes.resize(fUserAttributes.size() + 1);
fUserAttributes.back().insert(
pair<uint32, const char*>(key, *arg));
break;
case CmdOption::kAttrib:
{
const char* name = *arg;
if (fUserAttributes.size() <= 0)
throw Error("'%s' allowed only after the '%s' <name>",
name, kAttribute);
if (!*++arg || **arg == '-')
throw Error("'%s', argument should be specified", name);
TUserArgsI A = fUserAttributes.back().find(key);
if (A != fUserAttributes.back().end())
throw Error("'%s' for attribute '%s' already specified",
name, A->second);
fUserAttributes.back().insert(
pair<uint32, const char*>(key, *arg));
}
break;
default:
throw Error("internal error. wrong mode: %d", I->second->fType);
}
}
// check some mutual exclusive conditions
if (fOpMode == kOpModeUndefined)
throw Error(kNeedArgMessage);
if (Type() != NULL && InitCheck() != B_OK)
throw Error("error instantiating mime for '%s': %s",
Type(), strerror(InitCheck()));
fDoAdd = fOpMode == hash_function(kAdd);
fDoSet = fOpMode == hash_function(kSet);
fDoForce = fOpMode == hash_function(kForce);
fDoRemove = fOpMode == hash_function(kRemove);
fDumpNormal = fOpMode == hash_function(kDump);
fDumpRule = fOpMode == hash_function(kDumpSniffRule);
fDumpIcon = fOpMode == hash_function(kDumpIcon);
fDumpAll = fOpMode == hash_function(kDumpAll);
fCheckSniffRule = fOpMode == hash_function(kCheckSniffRule);
if (fDoAdd || fDoSet || fDoForce || fDoRemove) {
if (Type() == NULL)
throw Error("signature should be specified");
if (!IsValid())
throw Error("mime for '%s' is not valid", Type());
} else if (fDumpNormal || fDumpRule || fDumpIcon || fDumpAll) {
if (Type() != NULL) {
if (!IsValid())
throw Error("mime for '%s' is not valid", Type());
if (!IsInstalled())
throw Error("mime for '%s' is not installed", Type());
}
}
// finally force to load mime-specific fileds
_SetTo(Type());
}
status_t
MimeType::_InitCheck()
{
return fStatus != B_OK ? fStatus : BMimeType::InitCheck();
}
void
MimeType::_PurgeProperties()
{
fShort.Truncate(0);
fLong.Truncate(0);
fPrefApp.Truncate(0);
fPrefAppSig.Truncate(0);
fSniffRule.Truncate(0);
delete fSmallIcon;
fSmallIcon = NULL;
delete fBigIcon;
fBigIcon = NULL;
free(fVectorIcon);
fVectorIcon = NULL;
fExtensions.clear();
fAttributes.clear();
}
void
MimeType::_DumpIcon(uint8 *iconData, size_t iconSize)
{
// bitmap icons ASCII art :)
int lineLimit = iconSize == B_MINI_ICON * B_MINI_ICON
? B_MINI_ICON : B_LARGE_ICON;
ios::fmtflags flags = cout.flags();
for (size_t i = 0; i < iconSize; i++) {
if (i % lineLimit == 0 && i != iconSize - 1)
cout << "\\" << endl;
cout << hex << setfill('0') << setw(2) << (uint16) iconData[i];
}
cout.flags(flags);
}
void
MimeType::_SetIcon(const char* iconData, int32 iconSize)
{
uint8* bits = NULL;
BRect rect(0, 0, iconSize - 1, iconSize - 1);
switch (iconSize) {
case B_MINI_ICON:
if (fSmallIcon == NULL)
fSmallIcon = new BBitmap(rect, B_COLOR_8_BIT);
bits = (uint8*) fSmallIcon->Bits();
break;
case B_LARGE_ICON:
if (fBigIcon == NULL)
fBigIcon = new BBitmap(rect, B_COLOR_8_BIT);
bits = (uint8*) fBigIcon->Bits();
break;
default:
if (iconSize >= 0)
break;
free(fVectorIcon);
fVectorIconSize = -iconSize;
bits = fVectorIcon = (uint8*) malloc(fVectorIconSize);
break;
}
if (bits == NULL)
throw Error("cannot create icon of size %d", iconSize);
size_t dataSize = iconSize < 0 ? -iconSize / 2 : iconSize * iconSize;
for (size_t i = 0; i < dataSize; i++) {
stringstream ss;
uint16 val;
ss << setbase(16) << iconData[i * 2] << iconData[i * 2 + 1];
ss >> val;
bits[i] = uint8(val & 0xff);
}
if (iconSize < 0)
SetIcon(fVectorIcon, dataSize);
else
SetIcon(iconSize == B_MINI_ICON ? fSmallIcon : fBigIcon,
(icon_size) iconSize);
}
void
MimeType::_SetTo(const char* mimetype)
{
if (mimetype == NULL)
return; // iterate all types - nothing to load ATM
if (BMimeType::SetTo(mimetype) != B_OK)
throw Error("failed to set mimetype to '%s'", mimetype);
_PurgeProperties();
char buffer[B_MIME_TYPE_LENGTH] = { 0 };
if (GetShortDescription(buffer) == B_OK)
fShort.SetTo(buffer, B_MIME_TYPE_LENGTH);
if (GetLongDescription(buffer) == B_OK)
fLong.SetTo(buffer, B_MIME_TYPE_LENGTH);
entry_ref ref;
if (GetAppHint(&ref) == B_OK) {
BPath path(&ref);
fPrefApp.SetTo(path.Path(), B_MIME_TYPE_LENGTH);
}
if (GetPreferredApp(buffer, B_OPEN) == B_OK)
fPrefAppSig.SetTo(buffer, B_MIME_TYPE_LENGTH);
BString rule;
if (GetSnifferRule(&rule) == B_OK)
fSniffRule.CharacterEscape(rule.String(), "\'", '\\');
BMessage exts;
fExtensions.clear();
if (GetFileExtensions(&exts) == B_OK) {
uint32 i = 0;
const char* ext = NULL;
while (exts.FindString("extensions", i++, &ext) == B_OK)
fExtensions.insert(pair<uint32, BString>(hash_function(ext), ext));
}
BMessage attrs;
fAttributes.clear();
if (GetAttrInfo(&attrs) == B_OK) {
for (int index = 0; ; index++) {
MimeAttribute attr(attrs, index);
if (attr.InitCheck() != B_OK)
break;
fAttributes.insert(
pair<uint32, MimeAttribute>(hash_function(attr.fName), attr));
}
}
fSmallIcon = new BBitmap(BRect(0, 0, 15, 15), B_COLOR_8_BIT);
if (GetIcon(fSmallIcon, B_MINI_ICON) != B_OK) {
delete fSmallIcon;
fSmallIcon = NULL;
}
fBigIcon = new BBitmap(BRect(0, 0, 31, 31), B_COLOR_8_BIT);
if (GetIcon(fBigIcon, B_LARGE_ICON) != B_OK) {
delete fBigIcon;
fBigIcon = NULL;
}
if (GetIcon(&fVectorIcon, &fVectorIconSize) != B_OK)
fVectorIcon = NULL;
}
const char*
MimeType::_UserArgValue(const char* name)
{
TUserArgsI i = fUserArguments.find(hash_function(name));
if (i == fUserArguments.end())
return NULL;
return i->second != NULL ? i->second : "";
}
void
MimeType::_Dump(const char* mimetype)
{
// _Dump can be called as part of all types iteration - so set to required
if (Type() == NULL || strcasecmp(Type(), mimetype) != 0)
_SetTo(mimetype);
// apps have themself as preferred app - use it to handle
// -includeApps option - do not dump applications info
if (!fPrefApp.IsEmpty()
&& fPrefApp.ICompare(mimetype) == 0
&& _UserArgValue(kIncludeApps) == NULL)
return;
if (fDumpIcon && fSmallIcon == NULL && fBigIcon == NULL)
return;
if (fDumpRule && fSniffRule.IsEmpty())
return;
cout << fToolName << " -set " << mimetype;
if (fDumpNormal || fDumpAll) {
if (!fShort.IsEmpty())
cout << " " << kShort << " \"" << fShort << "\"";
if (!fLong.IsEmpty())
cout << " " << kLong << " \"" << fLong << "\"";
if (!fPrefApp.IsEmpty())
cout << " " << kPreferredApp << " " << fPrefApp;
if (!fPrefAppSig.IsEmpty())
cout << " " << kPreferredAppSig << " " << fPrefAppSig;
}
if (!fDumpIcon && !fSniffRule.IsEmpty())
cout << " " << kSniffRule << " '" << fSniffRule << "'";
if (fDumpNormal || fDumpAll)
for (map<uint32, BString>::iterator i = fExtensions.begin();
i != fExtensions.end(); i++)
cout << " " << kExtension << " " << i->second;
if (fDumpAll)
for (map<uint32, MimeAttribute>::iterator i = fAttributes.begin();
i != fAttributes.end(); i++)
i->second.Dump();
if (fDumpIcon || fDumpAll) {
if (fSmallIcon != NULL && fSmallIcon->Bits() != NULL) {
cout << " \\" << endl << "\t" << kMiniIcon << " ";
_DumpIcon((uint8*) fSmallIcon->Bits(), fSmallIcon->BitsLength());
}
if (fBigIcon != NULL && fBigIcon->Bits() != NULL) {
cout << " \\" << endl << "\t" << kLargeIcon << " ";
_DumpIcon((uint8*) fBigIcon->Bits(), fBigIcon->BitsLength());
}
if (fVectorIcon != NULL && fVectorIconSize != 0) {
cout << " \\" << endl << "\t" << kVectorIcon << " ";
_DumpIcon((uint8*) fVectorIcon, fVectorIconSize);
}
}
cout << endl;
}
void
MimeType::_DoEdit()
{
if (fDoRemove || fDoForce) {
status_t result = Delete();
if (result != B_OK)
throw Error(strerror(result), result);
if (fDoRemove)
return;
_PurgeProperties();
}
if (!IsInstalled() && Install() != B_OK)
throw Error("could not install mimetype '%s'", Type());
const char* value = _UserArgValue(kShort);
if (value != NULL && (!fDoAdd || fShort.IsEmpty()))
if (SetShortDescription(value) != B_OK)
throw Error("cannot set %s to %s for '%s'", kShort, value, Type());
value = _UserArgValue(kLong);
if (value != NULL && (!fDoAdd || fLong.IsEmpty()))
if (SetLongDescription(value) != B_OK)
throw Error("cannot set %s to %s for '%s'", kLong, value, Type());
value = _UserArgValue(kPreferredApp);
if (value != NULL && (!fDoAdd || fPrefApp.IsEmpty())) {
entry_ref appHint;
if (get_ref_for_path(value, &appHint) != B_OK)
throw Error("%s ref_entry for '%s' couldn't be found for '%s'",
kPreferredApp, value, Type());
if (SetAppHint(&appHint) != B_OK)
throw Error("cannot set %s to %s for '%s'",
kPreferredApp, value, Type());
}
value = _UserArgValue(kPreferredAppSig);
if (value != NULL && (!fDoAdd || fPrefAppSig.IsEmpty()))
if (SetPreferredApp(value) != B_OK)
throw Error("cannot set %s to %s for '%s'",
kPreferredAppSig, value, Type());
value = _UserArgValue(kSniffRule);
if (value != NULL && (!fDoAdd || fSniffRule.IsEmpty()))
if (SetSnifferRule(value) != B_OK)
throw Error("cannot set %s to %s for '%s'",
kSniffRule, value, Type());
value = _UserArgValue(kMiniIcon);
if (value != NULL && (!fDoAdd || fSmallIcon == NULL)) {
int32 iconSize = strlen(value);
if (iconSize / 2 != B_MINI_ICON * B_MINI_ICON)
throw Error("cannot set %s for '%s'. Hex data size %d is invalid",
kMiniIcon, Type(), iconSize);
_SetIcon(value, B_MINI_ICON);
}
value = _UserArgValue(kLargeIcon);
if (value != NULL && (!fDoAdd || fBigIcon == NULL)) {
int32 iconSize = strlen(value);
if (iconSize / 2 != B_LARGE_ICON * B_LARGE_ICON)
throw Error("cannot set %s for '%s'. Hex data size %d is invalid",
kLargeIcon, Type(), iconSize);
_SetIcon(value, B_LARGE_ICON);
}
value = _UserArgValue(kVectorIcon);
if (value != NULL && (!fDoAdd || fVectorIcon == NULL)) {
int32 iconSize = strlen(value);
if ((iconSize % 2) != 0)
throw Error("cannot set %s for '%s'. Hex data size %d is invalid",
kVectorIcon, Type(), iconSize);
// vector icon size is negative intended
_SetIcon(value, -iconSize);
}
// handle extensions update
pair<TUserArgsI, TUserArgsI> exts
= fUserArguments.equal_range(hash_function(kExtension));
for (TUserArgsI i = exts.first; i != exts.second; i++) {
uint32 key = hash_function(i->second);
if (fExtensions.find(key) == fExtensions.end())
fExtensions.insert(pair<uint32, BString>(key, i->second));
}
if (exts.first != exts.second) {
BMessage msg;
for (map<uint32, BString>::iterator i = fExtensions.begin();
i != fExtensions.end(); i++)
if (msg.AddString("extensions", i->second.String()) != B_OK)
throw Error("extension '%s' couldn't be added",
i->second.String());
if (SetFileExtensions(&msg) != B_OK)
throw Error("set file extensions failed");
}
// take care about attribute trees
for (TUserAttrsI userAttr = fUserAttributes.begin();
userAttr != fUserAttributes.end(); userAttr++ )
{
// search for -attribute "name" in args map
TUserArgsI attrArgs = userAttr->find(hash_function(kAttribute));
if (attrArgs == userAttr->end())
throw Error("internal error: %s arg not found", kAttribute);
// check if we already have this attribute cached
map<uint32, MimeAttribute>::iterator
attr = fAttributes.find(hash_function(attrArgs->second));
if (attr == fAttributes.end()) {
// add new one
MimeAttribute mimeAttr(*userAttr);
fAttributes.insert(
pair<uint32, MimeAttribute>(hash_function(mimeAttr.fName), mimeAttr));
} else if (!fDoAdd)
attr->second.SyncWith(*userAttr);
}
if (fAttributes.size() > 0) {
BMessage msg;
for (map<uint32, MimeAttribute>::iterator i = fAttributes.begin();
i != fAttributes.end(); i++)
{
i->second.StoreInto(&msg);
if (i->second.InitCheck() != B_OK)
throw Error("storing attributes in message failed");
}
if (SetAttrInfo(&msg) != B_OK)
throw Error("set mimetype attributes failed");
}
}
void
MimeType::Process()
{
if (fCheckSniffRule) {
TUserArgsI I = fUserArguments.find(fOpMode);
if (I == fUserArguments.end())
throw Error("Sniffer rule is empty");
BString error;
status_t result = BMimeType::CheckSnifferRule(I->second, &error);
if (result == B_OK)
cerr << I->second << endl << "Sniffer rule is correct" << endl;
else
cerr << error.String() << endl;
return;
}
if (fDoAdd || fDoSet || fDoForce || fDoRemove) {
_DoEdit();
return;
}
if (fDumpNormal || fDumpRule || fDumpIcon || fDumpAll) {
if (Type() != NULL) {
_Dump(Type());
return;
}
BMessage superTypes;
int32 superCount = 0;
type_code type = B_INT32_TYPE;
if (BMimeType::GetInstalledSupertypes(&superTypes) != B_OK
|| superTypes.GetInfo("super_types", &type, &superCount) != B_OK)
throw Error("super types enumeration failed");
for (int32 si = 0; si < superCount; si++) {
const char* superName = NULL;
if (superTypes.FindString("super_types", si, &superName) != B_OK)
throw Error("name for supertype #%d not found", si);
BMessage types;
if (BMimeType::GetInstalledTypes(superName, &types) != B_OK)
throw Error("mimetypes of supertype '%s' not found", superName);
int32 count = 0;
if (types.GetInfo("types", &type, &count) != B_OK)
continue; // no sub-types?
for (int32 i = 0; i < count; i++) {
const char* name = NULL;
if (types.FindString("types", i, &name) != B_OK)
throw Error("name for type %s/#%d not found", superName, i);
_Dump(name);
}
}
}
}
int
main(int argc, char** argv)
{
// AppServer link is required to work with bitmaps
BApplication app("application/x-vnd.haiku.setmime");
try {
if (argc < 2)
throw Error(kNeedArgMessage);
MimeType mimetype(argv);
mimetype.Process();
} catch(exception& exc) {
cerr << argv[0] << " : " << exc.what() << endl;
cerr << kUsageMessage;
return B_ERROR;
}
return B_OK;
}