diff --git a/src/bin/Jamfile b/src/bin/Jamfile index 3c07b31b48..61bbdd29c6 100644 --- a/src/bin/Jamfile +++ b/src/bin/Jamfile @@ -23,6 +23,7 @@ AddResources hey : hey.rdef ; AddResources mimeset : mimeset.rdef ; AddResources mountvolume : mountvolume.rdef ; AddResources notify : notify.rdef ; +AddResources setmime : setmime.rdef ; AddResources urlwrapper : urlwrapper.rdef ; # standard commands that don't need any additional library @@ -102,6 +103,7 @@ StdBinCommands play.cpp quit.cpp roster.cpp + setmime.cpp setversion.cpp trash.cpp version.cpp diff --git a/src/bin/setmime.cpp b/src/bin/setmime.cpp new file mode 100644 index 0000000000..5f5bc4ea68 --- /dev/null +++ b/src/bin/setmime.cpp @@ -0,0 +1,964 @@ +/* + * Copyright 2011 Aleksas Pantechovskis, + * Copyright 2011 Siarzhuk Zharski, + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace std; + + +const char* kUsageMessage = "# setmime:\n" + "# usage: setmime ((-dump | -dumpSniffRule | -dumpIcon | -dumpAll) " + "[ ] )\n" + "# | (-remove )\n" + "# | ( (-set | -force | -add) \n" + "# [ -short ] [ -long ]\n" + "# [ -preferredApp ]\n" + "# [ -preferredAppSig ]\n" + "# [ -sniffRule ]\n" + "# [ -extension ]\n" + "# [ -attribute \n" + "# [ -attrName ] [ -attrType ]\n" + "# [ -attrWidth ][ -attrAlignment ]\n" + "# [ -attrViewable ][ -attrEditable ]\n" + "# [ -attrExtra ] ]\n" + "# [ -miniIcon <256 hex bytes> ]\n" + "# [ -largeIcon <1024 hex bytes> ] ... )\n" + "# | (-checkSniffRule \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* 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(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 }, + + { 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 }, + { 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 TUserArgs; +typedef multimap::iterator TUserArgsI; + +// user provided attributes are grouped separately in vector +typedef vector TUserAttrs; +typedef vector::iterator TUserAttrsI; + +const uint32 kOpModeUndefined = 0; + + +// #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); + + status_t InitCheck() { return fStatus; } + void Dump(); + void SyncWith(TUserArgs& args); + void StoreInto(BMessage* target); + const char* UserArgValue(TUserArgs& map, const char* name); +}; + + +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; + void* data; + } attrEntries[] = { + { "attr:name", B_STRING_TYPE, &fName }, + { "attr:public_name", B_STRING_TYPE, &rawPublicName }, + { "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 (int 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 (fStatus != B_OK) + return; + } + + fPublicName.CharacterEscape(rawPublicName, "\'", '\\'); +} + + +MimeAttribute::MimeAttribute(TUserArgs& args) +{ + SyncWith(args); +} + + +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; + for (int i = 0; i < 4 && value[i] != '\0'; i++) { + fType <<= 8; + fType |= (value[i] != '\0' ? value[i] : ' '); + } + + 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 << "\""; + + cout << " \\" << endl << "\t\t" << kAttrType << " '" + << (char)((type >> 24) & 0xFF) << (char)((type >> 16) & 0xFF) + << (char)((type >> 8) & 0xFF) << (char)(type & 0xFF) << "' " + << " " << kAttrWidth << " " << fWidth + << " " << kAttrAlignment << " " << alignment; + + cout << " \\" << endl << "\t\t" << kAttrViewable << " " << fViewable + << " " << kAttrEditable << " " << fEditable + << " " << kAttrExtra << " " << fExtra; +} + + +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 (int 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(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: + class Error : public std::exception + { + BString fWhat; + public: + Error(const char* what, ...); + virtual ~Error() throw() {} + virtual const char* what() const throw() { return fWhat.String(); } + }; + + MimeType(char** argv) throw (Error); + ~MimeType(); + + void Process() throw (Error); + +private: + status_t _InitCheck(); + void _SetTo(const char* mimetype) throw (Error); + void _PurgeProperties(); + void _Init(char** argv) throw (Error); + void _Dump(const char* mimetype) throw (Error); + void _DoEdit() throw (Error); + 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; + + map fExtensions; + map 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::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); +} + + +MimeType::MimeType(char** argv) throw (Error) + : + fStatus(B_NO_INIT), + fToolName(argv[0]), + fSmallIcon(NULL), + fBigIcon(NULL), + fOpMode(kOpModeUndefined), + fDumpNormal(false), + fDumpRule(false), + fDumpIcon(false), + fDumpAll(false), + fDoAdd(false), + fDoSet(false), + fDoForce(false), + fDoRemove(false), + fCheckSniffRule(false) +{ + _Init(++argv); +} + + +MimeType::~MimeType() +{ + delete fSmallIcon; + delete fBigIcon; +} + + +void +MimeType::_Init(char** argv) throw (Error) +{ + // fill the helper map of options - for quick lookup of arguments + map cmdOptionsMap; + for (size_t i = 0; i < sizeof(gCmdOptions) / sizeof(gCmdOptions[0]); i++) + cmdOptionsMap.insert(pair( + hash(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()); + + status_t s = SetTo(*arg); + continue; + } + + // check op.modes, options and attribs + uint32 key = hash(*arg); + + map::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 (I->second->fType != hash(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(key, param)); + } + break; + + case CmdOption::kAttrRoot: + if (!*++arg) + throw Error("attribute name should be specified"); + + fUserAttributes.resize(fUserAttributes.size() + 1); + fUserAttributes.back().insert( + pair(key, *arg)); + break; + + case CmdOption::kAttrib: + { + const char* name = *arg; + if (fUserAttributes.size() <= 0) + throw Error("'%s' allowed only after the '%s' ", + name, kAttribute); + + if (!*++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(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(kAdd); + fDoSet = fOpMode == hash(kSet); + fDoForce = fOpMode == hash(kForce); + fDoRemove = fOpMode == hash(kRemove); + fDumpNormal = fOpMode == hash(kDump); + fDumpRule = fOpMode == hash(kDumpSniffRule); + fDumpIcon = fOpMode == hash(kDumpIcon); + fDumpAll = fOpMode == hash(kDumpAll); + fCheckSniffRule = fOpMode == hash(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; + + fExtensions.clear(); + fAttributes.clear(); +} + + +void +MimeType::_SetTo(const char* mimetype) throw (Error) +{ + 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(hash(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(hash(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; + } +} + + +const char* +MimeType::_UserArgValue(const char* name) +{ + TUserArgsI i = fUserArguments.find(hash(name)); + if (i == fUserArguments.end()) + return NULL; + + return i->second != NULL ? i->second : ""; +} + + +void +MimeType::_Dump(const char* mimetype) throw (Error) +{ + // _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; + + 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::iterator i = fExtensions.begin(); + i != fExtensions.end(); i++) + cout << " " << kExtension << " " << i->second; + + if (fDumpAll) + for (map::iterator i = fAttributes.begin(); + i != fAttributes.end(); i++) + i->second.Dump(); + + if (fDumpIcon || fDumpAll) + cout << " \\" << endl << "Icon dumps are not yet implemented!"; + + cout << endl; +} + + +void +MimeType::_DoEdit() throw (Error) +{ + 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()); + + // handle extensions update + pair exts + = fUserArguments.equal_range(hash(kExtension)); + for (TUserArgsI i = exts.first; i != exts.second; i++) { + uint32 key = hash(i->second); + if (fExtensions.find(key) == fExtensions.end()) + fExtensions.insert(pair(key, i->second)); + } + + if (exts.first != exts.second) { + BMessage msg; + for (map::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(kAttribute)); + if (attrArgs == userAttr->end()) + throw Error("internal error: %s arg not found", kAttribute); + + // check if we already have this attribute cached + map::iterator + attr = fAttributes.find(hash(attrArgs->second)); + if (attr == fAttributes.end()) { + // add new one + MimeAttribute mimeAttr(*userAttr); + fAttributes.insert( + pair(hash(mimeAttr.fName), mimeAttr)); + } else if (!fDoAdd) + attr->second.SyncWith(*userAttr); + } + + if (fAttributes.size() > 0) { + BMessage msg; + for (map::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() throw (Error) +{ + 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); + cerr << (result == B_OK ? + "sniffer rule is correct" : 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 MimeType::Error(kNeedArgMessage); + + MimeType mimetype(argv); + + mimetype.Process(); + + } catch(exception& exc) { + cerr << argv[0] << " : " << exc.what() << endl; + cerr << kUsageMessage; + return B_ERROR; + } + + return B_OK; +} + diff --git a/src/bin/setmime.rdef b/src/bin/setmime.rdef new file mode 100644 index 0000000000..0cb6a84fa3 --- /dev/null +++ b/src/bin/setmime.rdef @@ -0,0 +1,3 @@ +resource app_signature "application/x-vnd.haiku.setmime"; + +resource app_flags B_MULTIPLE_LAUNCH | B_BACKGROUND_APP;