Assorted whitespace cleanup and typo fixes.
[haiku.git] / src / bin / setmime.cpp
blob8078c6099e380ba07e75833ae36bc7127ca65337
1 /*
2 * Copyright 2011 Aleksas Pantechovskis, <alexp.frl@gmail.com>
3 * Copyright 2011 Siarzhuk Zharski, <imker@gmx.li>
4 * All rights reserved. Distributed under the terms of the MIT License.
5 */
7 #include <iomanip>
8 #include <iostream>
9 #include <map>
10 #include <math.h>
11 #include <set>
12 #include <sstream>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <strings.h>
16 #include <vector>
18 #include <Application.h>
19 #include <Bitmap.h>
20 #include <Entry.h>
21 #include <InterfaceDefs.h>
22 #include <Message.h>
23 #include <Mime.h>
24 #include <Path.h>
25 #include <String.h>
28 using namespace std;
31 const char* kUsageMessage = "# setmime:\n"
32 "# usage: setmime ((-dump | -dumpSniffRule | -dumpIcon | -dumpAll) "
33 "[ <signatureString> ] )\n"
34 "# | (-remove <signatureString> )\n"
35 "# | ( (-set | -force | -add) <signatureString>\n"
36 "# [ -short <short description> ] [ -long <long description> ]\n"
37 "# [ -preferredApp <preferred app path> ]\n"
38 "# [ -preferredAppSig <preferred app signature> ]\n"
39 "# [ -sniffRule <sniffRule> ]\n"
40 "# [ -extension <file suffix> ]\n"
41 "# [ -attribute <internal name>\n"
42 "# [ -attrName <public name> ] [ -attrType <type code> ]\n"
43 "# [ -attrWidth <display width> ][ -attrAlignment <position>]\n"
44 "# [ -attrViewable <bool flag> ][ -attrEditable <bool flag> ]\n"
45 "# [ -attrExtra <bool flag> ] ]\n"
46 "# [ -miniIcon <256 hex bytes> ]\n"
47 "# [ -largeIcon <1024 hex bytes> ]\n"
48 "# [ -vectorIcon <icon hex bytes> ] ... )\n"
49 "# | (-checkSniffRule <sniffRule>\n"
50 "# | -includeApps)\n";
52 const char* kHelpMessage = "# -dump prints a specified metamime\n"
53 "# -remove removes specified metamime\n"
54 "# -add adds specified metamime and specified metamime attributes\n"
55 "# that have not yet been defined\n"
56 "# -set adds specified metamime and specified metamime attributes, \n"
57 "# overwrites the existing values of specified metamime attributes\n"
58 "# -force adds specified metamime and specified metamime attributes\n"
59 "# after first erasing all the existing attributes\n"
60 "# -dumpSniffRule prints just the MIME sniffer rule of a "
61 "specified metamime\n"
62 "# -dumpIcon prints just the icon information of a specified metamime\n"
63 "# -dumpAll prints all the information, including icons of a "
64 "specified metamime\n"
65 "# -checkSniffRule parses a MIME sniffer rule and reports any errors\n"
66 "# -includeApps will include applications\n";
68 const char* kNeedArgMessage = "you have to specify any of "
69 "-dump[All|Icon|SnifferRule], -add, -set, "
70 "-force or -remove";
72 const char* kWrongModeMessage = "can only specify one of -dump, -dumpAll, "
73 "-dumpIcon, -dumpSnifferRule, -remove, "
74 "-add, -set, -force or -checkSnifferRule";
76 const char* kHelpReq = "--help";
77 const char* kDump = "-dump";
78 const char* kDumpSniffRule = "-dumpSniffRule";
79 const char* kDumpIcon = "-dumpIcon";
80 const char* kDumpAll = "-dumpAll";
81 const char* kAdd = "-add";
82 const char* kSet = "-set";
83 const char* kForce = "-force";
84 const char* kRemove = "-remove";
85 const char* kCheckSniffRule = "-checkSniffRule";
86 const char* kShort = "-short";
87 const char* kLong = "-long";
88 const char* kPreferredApp = "-preferredApp";
89 const char* kPreferredAppSig = "-preferredAppSig";
90 const char* kSniffRule = "-sniffRule";
91 const char* kMiniIcon = "-miniIcon";
92 const char* kLargeIcon = "-largeIcon";
93 const char* kVectorIcon = "-vectorIcon";
94 const char* kIncludeApps = "-includeApps";
95 const char* kExtension = "-extension";
96 const char* kAttribute = "-attribute";
97 const char* kAttrName = "-attrName";
98 const char* kAttrType = "-attrType";
99 const char* kAttrWidth = "-attrWidth";
100 const char* kAttrAlignment = "-attrAlignment";
101 const char* kAttrViewable = "-attrViewable";
102 const char* kAttrEditable = "-attrEditable";
103 const char* kAttrExtra = "-attrExtra";
106 const uint32 hash_function(const char* str)
108 uint32 h = 0;
109 uint32 g = 0;
110 for (const char* p = str; *p; p++) {
111 h = (h << 4) + (*p & 0xFF);
112 g = h & 0xF0000000;
113 if (g != 0) {
114 h ^= g >> 24;
115 h ^= g;
118 return h;
122 // the list of all acceptable command-line options
123 struct CmdOption {
125 enum Type {
126 kMode,
127 kOption,
128 kAttrRoot,
129 kAttrib,
130 kHelp
133 const char* fName;
134 Type fType;
135 bool fNeedArg;
136 bool fNonExclusive;
138 } gCmdOptions[] = {
140 { kHelpReq, CmdOption::kHelp },
142 { kDump, CmdOption::kMode },
143 { kDumpSniffRule, CmdOption::kMode },
144 { kDumpIcon, CmdOption::kMode },
145 { kDumpAll, CmdOption::kMode },
146 { kAdd, CmdOption::kMode },
147 { kSet, CmdOption::kMode },
148 { kForce, CmdOption::kMode },
149 { kRemove, CmdOption::kMode },
150 { kCheckSniffRule, CmdOption::kMode, true },
152 { kShort, CmdOption::kOption, true },
153 { kLong, CmdOption::kOption, true },
154 { kPreferredApp, CmdOption::kOption, true },
155 { kPreferredAppSig, CmdOption::kOption, true },
156 { kSniffRule, CmdOption::kOption, true },
157 { kMiniIcon , CmdOption::kOption, true },
158 { kLargeIcon, CmdOption::kOption, true },
159 { kVectorIcon, CmdOption::kOption, true },
160 { kIncludeApps, CmdOption::kOption, false },
161 { kExtension, CmdOption::kOption, true, true },
162 { kAttribute, CmdOption::kAttrRoot, true, true },
164 { kAttrName, CmdOption::kAttrib, true },
165 { kAttrType, CmdOption::kAttrib, true },
166 { kAttrWidth, CmdOption::kAttrib, true },
167 { kAttrAlignment, CmdOption::kAttrib, true },
168 { kAttrViewable, CmdOption::kAttrib, true },
169 { kAttrEditable, CmdOption::kAttrib, true },
170 { kAttrExtra, CmdOption::kAttrib, true }
173 // the 'hash -> value' map of arguments provided by user
174 typedef multimap<uint32, const char*> TUserArgs;
175 typedef multimap<uint32, const char*>::iterator TUserArgsI;
177 // user provided attributes are grouped separately in vector
178 typedef vector<TUserArgs> TUserAttrs;
179 typedef vector<TUserArgs>::iterator TUserAttrsI;
181 const uint32 kOpModeUndefined = 0;
184 // #pragma mark -
186 class Error : public std::exception
188 BString fWhat;
189 public:
190 Error(const char* what, ...);
191 virtual ~Error() throw() {}
192 virtual const char* what() const throw() { return fWhat.String(); }
196 Error::Error(const char* what, ...)
198 const int size = 1024;
199 va_list args;
200 va_start(args, what);
201 vsnprintf(fWhat.LockBuffer(size), size, what, args);
202 fWhat.UnlockBuffer();
203 va_end(args);
207 // #pragma mark -
209 // encapsulate the single attribute params
211 struct MimeAttribute
213 status_t fStatus;
214 BString fName;
215 BString fPublicName;
216 int32 fType;
217 bool fViewable;
218 bool fEditable;
219 bool fExtra;
220 int32 fWidth;
221 int32 fAlignment;
223 MimeAttribute(BMessage& msg, int32 index);
224 MimeAttribute(TUserArgs& args);
225 MimeAttribute(const MimeAttribute& src);
227 status_t InitCheck() { return fStatus; }
229 MimeAttribute& operator=(const MimeAttribute& src);
231 void Dump();
232 void SyncWith(TUserArgs& args) throw(Error);
233 void StoreInto(BMessage* target);
234 const char* UserArgValue(TUserArgs& map, const char* name);
236 bool IsPrintableChar(char c)
237 { return c >= ' ' && c < 127 && c != '\'' && c != '\\'; }
241 MimeAttribute::MimeAttribute(BMessage& msg, int32 index)
243 fStatus(B_NO_INIT),
244 fType('CSTR'),
245 fViewable(true),
246 fEditable(false),
247 fExtra(false),
248 fWidth(0),
249 fAlignment(0)
251 BString rawPublicName;
252 struct attrEntry {
253 const char* name;
254 type_code type;
255 bool required;
256 void* data;
257 } attrEntries[] = {
258 { "attr:name", B_STRING_TYPE, true, &fName },
259 { "attr:public_name", B_STRING_TYPE, true, &rawPublicName },
260 { "attr:type", B_INT32_TYPE, true, &fType },
261 { "attr:viewable", B_BOOL_TYPE, false, &fViewable },
262 { "attr:editable", B_BOOL_TYPE, false, &fEditable },
263 { "attr:extra", B_BOOL_TYPE, false, &fExtra },
264 { "attr:width", B_INT32_TYPE, false, &fWidth },
265 { "attr:alignment", B_INT32_TYPE, false, &fAlignment }
268 for (size_t i = 0; i < sizeof(attrEntries) / sizeof(attrEntries[0]); i++) {
269 switch (attrEntries[i].type) {
270 case B_STRING_TYPE:
271 fStatus = msg.FindString(attrEntries[i].name, index,
272 (BString*)attrEntries[i].data);
273 break;
274 case B_BOOL_TYPE:
275 fStatus = msg.FindBool(attrEntries[i].name, index,
276 (bool*)attrEntries[i].data);
277 break;
278 case B_INT32_TYPE:
279 fStatus = msg.FindInt32(attrEntries[i].name, index,
280 (int32*)attrEntries[i].data);
281 break;
284 if (attrEntries[i].required && fStatus != B_OK)
285 return;
288 fPublicName.CharacterEscape(rawPublicName, "\'", '\\');
289 fStatus = B_OK;
293 MimeAttribute::MimeAttribute(TUserArgs& args)
295 fStatus(B_NO_INIT),
296 fType('CSTR'),
297 fViewable(true),
298 fEditable(false),
299 fExtra(false),
300 fWidth(0),
301 fAlignment(0)
303 SyncWith(args);
304 fStatus = B_OK;
308 MimeAttribute::MimeAttribute(const MimeAttribute& src)
310 *this = src;
314 MimeAttribute&
315 MimeAttribute::operator=(const MimeAttribute& src)
317 fStatus = src.fStatus;
318 fName = src.fName;
319 fPublicName = src.fPublicName;
320 fType = src.fType;
321 fViewable = src.fViewable;
322 fEditable = src.fEditable;
323 fExtra = src.fExtra;
324 fWidth = src.fWidth;
325 fAlignment = src.fAlignment;
327 return *this;
331 void
332 MimeAttribute::SyncWith(TUserArgs& args) throw(Error)
334 const char* value = UserArgValue(args, kAttribute);
335 if (value != NULL)
336 fName.SetTo(value, B_MIME_TYPE_LENGTH);
338 value = UserArgValue(args, kAttrName);
339 if (value != NULL)
340 fPublicName.SetTo(value, B_MIME_TYPE_LENGTH);
342 value = UserArgValue(args, kAttrType);
343 if (value != NULL) {
344 fType = 0;
345 if (strlen(value) > 2 && value[0] == '0' && value[1] == 'x') {
346 stringstream ss;
347 ss << setbase(16) << value + 2;
348 ss >> fType;
349 } else if (strlen(value) == 4) {
350 for (int i = 0; i < 4 && value[i] != '\0'; i++) {
351 fType <<= 8;
352 fType |= (value[i] != '\0' ? value[i] : ' ');
355 } else
356 throw Error("Invalid data for %s", kAttrType);
358 fType = B_LENDIAN_TO_HOST_INT32(fType);
361 value = UserArgValue(args, kAttrWidth);
362 if (value != NULL)
363 fWidth = atoi(value);
365 value = UserArgValue(args, kAttrAlignment);
366 if (value != NULL) {
367 if (strcasecmp(value, "right") == 0) {
368 fAlignment = B_ALIGN_RIGHT;
369 } else if (strcasecmp(value, "left") == 0) {
370 fAlignment = B_ALIGN_LEFT;
371 } else if (strcasecmp(value, "center") == 0) {
372 fAlignment = B_ALIGN_CENTER;
373 } else
374 fAlignment = atoi(value);
377 value = UserArgValue(args, kAttrViewable);
378 if (value != NULL)
379 fViewable = atoi(value) != 0;
381 value = UserArgValue(args, kAttrEditable);
382 if (value != NULL)
383 fEditable = atoi(value) != 0;
385 value = UserArgValue(args, kAttrExtra);
386 if (value != NULL)
387 fExtra = atoi(value) != 0;
391 void
392 MimeAttribute::Dump()
394 uint32 type = B_HOST_TO_LENDIAN_INT32(fType);
395 const char* alignment = fAlignment == B_ALIGN_RIGHT ? "right"
396 : (fAlignment == B_ALIGN_LEFT ? "left" : "center");
398 cout << " \\" << endl << "\t" << kAttribute << " \"" << fName << "\" "
399 << kAttrName << " \"" << fPublicName << "\"";
401 char c1 = (char)((type >> 24) & 0xFF);
402 char c2 = (char)((type >> 16) & 0xFF);
403 char c3 = (char)((type >> 8) & 0xFF);
404 char c4 = (char)(type & 0xFF);
406 ios::fmtflags flags = cout.flags();
408 cout << " \\" << endl << "\t\t" << kAttrType;
409 if (IsPrintableChar(c1) && IsPrintableChar(c2) &&
410 IsPrintableChar(c3) && IsPrintableChar(c4))
411 cout << " '" << c1 << c2 << c3 << c4 << "' ";
412 else
413 cout << "0x" << hex << type;
415 cout << " " << kAttrWidth << " " << fWidth
416 << " " << kAttrAlignment << " " << alignment;
418 cout << " \\" << endl << "\t\t" << kAttrViewable << " " << fViewable
419 << " " << kAttrEditable << " " << fEditable
420 << " " << kAttrExtra << " " << fExtra;
422 cout.flags(flags);
426 void
427 MimeAttribute::StoreInto(BMessage* target)
429 struct attrEntry {
430 const char* name;
431 type_code type;
432 const void* data;
433 } attrEntries[] = {
434 { "attr:name", B_STRING_TYPE, fName.String() },
435 { "attr:public_name", B_STRING_TYPE, fPublicName.String() },
436 { "attr:type", B_INT32_TYPE, &fType },
437 { "attr:viewable", B_BOOL_TYPE, &fViewable },
438 { "attr:editable", B_BOOL_TYPE, &fEditable },
439 { "attr:extra", B_BOOL_TYPE, &fExtra },
440 { "attr:width", B_INT32_TYPE, &fWidth },
441 { "attr:alignment", B_INT32_TYPE, &fAlignment }
444 for (size_t i = 0; i < sizeof(attrEntries) / sizeof(attrEntries[0]); i++) {
445 switch (attrEntries[i].type) {
446 case B_STRING_TYPE:
447 fStatus = target->AddString(attrEntries[i].name,
448 (const char*)attrEntries[i].data);
449 break;
450 case B_BOOL_TYPE:
451 fStatus = target->AddBool(attrEntries[i].name,
452 (bool*)attrEntries[i].data);
453 break;
454 case B_INT32_TYPE:
455 fStatus = target->AddInt32(attrEntries[i].name,
456 *(int32*)attrEntries[i].data);
457 break;
460 if (fStatus != B_OK)
461 return;
466 const char*
467 MimeAttribute::UserArgValue(TUserArgs& map, const char* name)
469 TUserArgsI i = map.find(hash_function(name));
470 if (i == map.end())
471 return NULL;
472 return i->second != NULL ? i->second : "";
476 // #pragma mark -
478 // the work-horse of the app - the class encapsulates extended info readed
479 // from the mime type and do all edit and dump operations
481 class MimeType : public BMimeType {
483 public:
484 MimeType(char** argv) throw (Error);
485 ~MimeType();
487 void Process() throw (Error);
489 private:
490 status_t _InitCheck();
491 void _SetTo(const char* mimetype) throw (Error);
492 void _PurgeProperties();
493 void _Init(char** argv) throw (Error);
494 void _DumpIcon(uint8 *iconData, size_t iconSize);
495 void _Dump(const char* mimetype) throw (Error);
496 void _DoEdit() throw (Error);
497 void _SetIcon(const char* iconData, int32 iconSize);
499 const char* _UserArgValue(const char* name);
501 status_t fStatus;
502 const char* fToolName;
504 // configurable MimeType properties
505 BString fShort;
506 BString fLong;
507 BString fPrefApp;
508 BString fPrefAppSig;
509 BString fSniffRule;
510 BBitmap* fSmallIcon;
511 BBitmap* fBigIcon;
512 uint8* fVectorIcon;
513 size_t fVectorIconSize;
515 map<uint32, BString> fExtensions;
516 map<uint32, MimeAttribute> fAttributes;
518 // user provided arguments
519 TUserArgs fUserArguments;
520 TUserAttrs fUserAttributes;
522 // operation mode switches and flags
523 uint32 fOpMode;
524 bool fDumpNormal;
525 bool fDumpRule;
526 bool fDumpIcon;
527 bool fDumpAll;
528 bool fDoAdd;
529 bool fDoSet;
530 bool fDoForce;
531 bool fDoRemove;
532 bool fCheckSniffRule;
536 MimeType::MimeType(char** argv) throw (Error)
538 fStatus(B_NO_INIT),
539 fToolName(argv[0]),
540 fSmallIcon(NULL),
541 fBigIcon(NULL),
542 fVectorIcon(NULL),
543 fVectorIconSize(0),
544 fOpMode(kOpModeUndefined),
545 fDumpNormal(false),
546 fDumpRule(false),
547 fDumpIcon(false),
548 fDumpAll(false),
549 fDoAdd(false),
550 fDoSet(false),
551 fDoForce(false),
552 fDoRemove(false),
553 fCheckSniffRule(false)
555 fToolName = strrchr(argv[0], '/');
556 fToolName = fToolName == NULL ? argv[0] : fToolName + 1;
558 _Init(++argv);
562 MimeType::~MimeType()
564 delete fSmallIcon;
565 delete fBigIcon;
566 free(fVectorIcon);
570 void
571 MimeType::_Init(char** argv) throw (Error)
573 // fill the helper map of options - for quick lookup of arguments
574 map<uint32, const CmdOption*> cmdOptionsMap;
575 for (size_t i = 0; i < sizeof(gCmdOptions) / sizeof(gCmdOptions[0]); i++)
576 cmdOptionsMap.insert(pair<uint32, CmdOption*>(
577 hash_function(gCmdOptions[i].fName), &gCmdOptions[i]));
579 // parse the command line arguments
580 for (char** arg = argv; *arg; arg++) {
581 // non-option arguments are assumed as signature
582 if (**arg != '-') {
583 if (Type() != NULL)
584 throw Error("mime signature already specified: '%s'", Type());
586 SetTo(*arg);
587 continue;
590 // check op.modes, options and attribs
591 uint32 key = hash_function(*arg);
593 map<uint32, const CmdOption*>::iterator I = cmdOptionsMap.find(key);
594 if (I == cmdOptionsMap.end())
595 throw Error("unknown option '%s'", *arg);
597 switch (I->second->fType) {
598 case CmdOption::kHelp:
599 cerr << kUsageMessage;
600 throw Error(kHelpMessage);
602 case CmdOption::kMode:
603 // op.modes are exclusive - no simultaneous possible
604 if (fOpMode != kOpModeUndefined)
605 throw Error(kWrongModeMessage);
606 fOpMode = key;
608 if (hash_function(I->second->fName) != hash_function(kCheckSniffRule))
609 break;
610 // else -> fallthrough, CheckRule works both as mode and Option
611 case CmdOption::kOption:
613 const char* name = *arg;
614 const char* param = NULL;
615 if (I->second->fNeedArg) {
616 if (!*++arg)
617 throw Error("argument required for '%s'", name);
618 param = *arg;
621 TUserArgsI A = fUserArguments.find(key);
622 if (A != fUserArguments.end() && !I->second->fNonExclusive)
623 throw Error("option '%s' already specified", name);
625 fUserArguments.insert(
626 pair<uint32, const char*>(key, param));
628 break;
630 case CmdOption::kAttrRoot:
631 if (!*++arg)
632 throw Error("attribute name should be specified");
634 fUserAttributes.resize(fUserAttributes.size() + 1);
635 fUserAttributes.back().insert(
636 pair<uint32, const char*>(key, *arg));
637 break;
639 case CmdOption::kAttrib:
641 const char* name = *arg;
642 if (fUserAttributes.size() <= 0)
643 throw Error("'%s' allowed only after the '%s' <name>",
644 name, kAttribute);
646 if (!*++arg || **arg == '-')
647 throw Error("'%s', argument should be specified", name);
649 TUserArgsI A = fUserAttributes.back().find(key);
650 if (A != fUserAttributes.back().end())
651 throw Error("'%s' for attribute '%s' already specified",
652 name, A->second);
654 fUserAttributes.back().insert(
655 pair<uint32, const char*>(key, *arg));
657 break;
659 default:
660 throw Error("internal error. wrong mode: %d", I->second->fType);
664 // check some mutual exclusive conditions
665 if (fOpMode == kOpModeUndefined)
666 throw Error(kNeedArgMessage);
668 if (Type() != NULL && InitCheck() != B_OK)
669 throw Error("error instantiating mime for '%s': %s",
670 Type(), strerror(InitCheck()));
672 fDoAdd = fOpMode == hash_function(kAdd);
673 fDoSet = fOpMode == hash_function(kSet);
674 fDoForce = fOpMode == hash_function(kForce);
675 fDoRemove = fOpMode == hash_function(kRemove);
676 fDumpNormal = fOpMode == hash_function(kDump);
677 fDumpRule = fOpMode == hash_function(kDumpSniffRule);
678 fDumpIcon = fOpMode == hash_function(kDumpIcon);
679 fDumpAll = fOpMode == hash_function(kDumpAll);
680 fCheckSniffRule = fOpMode == hash_function(kCheckSniffRule);
682 if (fDoAdd || fDoSet || fDoForce || fDoRemove) {
683 if (Type() == NULL)
684 throw Error("signature should be specified");
686 if (!IsValid())
687 throw Error("mime for '%s' is not valid", Type());
689 } else if (fDumpNormal || fDumpRule || fDumpIcon || fDumpAll) {
690 if (Type() != NULL) {
691 if (!IsValid())
692 throw Error("mime for '%s' is not valid", Type());
694 if (!IsInstalled())
695 throw Error("mime for '%s' is not installed", Type());
699 // finally force to load mime-specific fileds
700 _SetTo(Type());
704 status_t
705 MimeType::_InitCheck()
707 return fStatus != B_OK ? fStatus : BMimeType::InitCheck();
711 void
712 MimeType::_PurgeProperties()
714 fShort.Truncate(0);
715 fLong.Truncate(0);
716 fPrefApp.Truncate(0);
717 fPrefAppSig.Truncate(0);
718 fSniffRule.Truncate(0);
720 delete fSmallIcon;
721 fSmallIcon = NULL;
723 delete fBigIcon;
724 fBigIcon = NULL;
726 fVectorIcon = NULL;
727 free(fVectorIcon);
729 fExtensions.clear();
730 fAttributes.clear();
734 void
735 MimeType::_DumpIcon(uint8 *iconData, size_t iconSize)
737 // bitmap icons ASCII art :)
738 int lineLimit = iconSize == B_MINI_ICON * B_MINI_ICON
739 ? B_MINI_ICON : B_LARGE_ICON;
741 ios::fmtflags flags = cout.flags();
743 for (size_t i = 0; i < iconSize; i++) {
744 if (i % lineLimit == 0 && i != iconSize - 1)
745 cout << "\\" << endl;
747 cout << hex << setfill('0') << setw(2) << (uint16) iconData[i];
750 cout.flags(flags);
754 void
755 MimeType::_SetIcon(const char* iconData, int32 iconSize)
757 uint8* bits = NULL;
758 BRect rect(0, 0, iconSize - 1, iconSize - 1);
760 switch (iconSize) {
761 case B_MINI_ICON:
762 if (fSmallIcon == NULL)
763 fSmallIcon = new BBitmap(rect, B_COLOR_8_BIT);
764 bits = (uint8*) fSmallIcon->Bits();
765 break;
766 case B_LARGE_ICON:
767 if (fBigIcon == NULL)
768 fBigIcon = new BBitmap(rect, B_COLOR_8_BIT);
769 bits = (uint8*) fBigIcon->Bits();
770 break;
771 default:
772 if (iconSize >= 0)
773 break;
774 free(fVectorIcon);
775 fVectorIconSize = -iconSize;
776 bits = fVectorIcon = (uint8*) malloc(fVectorIconSize);
777 break;
780 if (bits == NULL)
781 throw Error("cannot create icon of size %d", iconSize);
783 size_t dataSize = iconSize < 0 ? -iconSize / 2 : iconSize * iconSize;
785 for (size_t i = 0; i < dataSize; i++) {
786 stringstream ss;
787 uint16 val;
788 ss << setbase(16) << iconData[i * 2] << iconData[i * 2 + 1];
789 ss >> val;
790 bits[i] = uint8(val & 0xff);
793 if (iconSize < 0)
794 SetIcon(fVectorIcon, dataSize);
795 else
796 SetIcon(iconSize == B_MINI_ICON ? fSmallIcon : fBigIcon,
797 (icon_size) iconSize);
801 void
802 MimeType::_SetTo(const char* mimetype) throw (Error)
804 if (mimetype == NULL)
805 return; // iterate all types - nothing to load ATM
807 if (BMimeType::SetTo(mimetype) != B_OK)
808 throw Error("failed to set mimetype to '%s'", mimetype);
810 _PurgeProperties();
812 char buffer[B_MIME_TYPE_LENGTH] = { 0 };
813 if (GetShortDescription(buffer) == B_OK)
814 fShort.SetTo(buffer, B_MIME_TYPE_LENGTH);
816 if (GetLongDescription(buffer) == B_OK)
817 fLong.SetTo(buffer, B_MIME_TYPE_LENGTH);
819 entry_ref ref;
820 if (GetAppHint(&ref) == B_OK) {
821 BPath path(&ref);
822 fPrefApp.SetTo(path.Path(), B_MIME_TYPE_LENGTH);
825 if (GetPreferredApp(buffer, B_OPEN) == B_OK)
826 fPrefAppSig.SetTo(buffer, B_MIME_TYPE_LENGTH);
828 BString rule;
829 if (GetSnifferRule(&rule) == B_OK)
830 fSniffRule.CharacterEscape(rule.String(), "\'", '\\');
832 BMessage exts;
833 fExtensions.clear();
834 if (GetFileExtensions(&exts) == B_OK) {
835 uint32 i = 0;
836 const char* ext = NULL;
837 while (exts.FindString("extensions", i++, &ext) == B_OK)
838 fExtensions.insert(pair<uint32, BString>(hash_function(ext), ext));
841 BMessage attrs;
842 fAttributes.clear();
843 if (GetAttrInfo(&attrs) == B_OK) {
844 for (int index = 0; ; index++) {
845 MimeAttribute attr(attrs, index);
846 if (attr.InitCheck() != B_OK)
847 break;
849 fAttributes.insert(
850 pair<uint32, MimeAttribute>(hash_function(attr.fName), attr));
854 fSmallIcon = new BBitmap(BRect(0, 0, 15, 15), B_COLOR_8_BIT);
855 if (GetIcon(fSmallIcon, B_MINI_ICON) != B_OK) {
856 delete fSmallIcon;
857 fSmallIcon = NULL;
860 fBigIcon = new BBitmap(BRect(0, 0, 31, 31), B_COLOR_8_BIT);
861 if (GetIcon(fBigIcon, B_LARGE_ICON) != B_OK) {
862 delete fBigIcon;
863 fBigIcon = NULL;
866 if (GetIcon(&fVectorIcon, &fVectorIconSize) != B_OK)
867 fVectorIcon = NULL;
871 const char*
872 MimeType::_UserArgValue(const char* name)
874 TUserArgsI i = fUserArguments.find(hash_function(name));
875 if (i == fUserArguments.end())
876 return NULL;
878 return i->second != NULL ? i->second : "";
882 void
883 MimeType::_Dump(const char* mimetype) throw (Error)
885 // _Dump can be called as part of all types iteration - so set to required
886 if (Type() == NULL || strcasecmp(Type(), mimetype) != 0)
887 _SetTo(mimetype);
889 // apps have themself as preferred app - use it to handle
890 // -includeApps option - do not dump applications info
891 if (!fPrefApp.IsEmpty()
892 && fPrefApp.ICompare(mimetype) == 0
893 && _UserArgValue(kIncludeApps) == NULL)
894 return;
896 if (fDumpIcon && fSmallIcon == NULL && fBigIcon == NULL)
897 return;
899 if (fDumpRule && fSniffRule.IsEmpty())
900 return;
902 cout << fToolName << " -set " << mimetype;
904 if (fDumpNormal || fDumpAll) {
905 if (!fShort.IsEmpty())
906 cout << " " << kShort << " \"" << fShort << "\"";
907 if (!fLong.IsEmpty())
908 cout << " " << kLong << " \"" << fLong << "\"";
909 if (!fPrefApp.IsEmpty())
910 cout << " " << kPreferredApp << " " << fPrefApp;
911 if (!fPrefAppSig.IsEmpty())
912 cout << " " << kPreferredAppSig << " " << fPrefAppSig;
915 if (!fDumpIcon && !fSniffRule.IsEmpty())
916 cout << " " << kSniffRule << " '" << fSniffRule << "'";
918 if (fDumpNormal || fDumpAll)
919 for (map<uint32, BString>::iterator i = fExtensions.begin();
920 i != fExtensions.end(); i++)
921 cout << " " << kExtension << " " << i->second;
923 if (fDumpAll)
924 for (map<uint32, MimeAttribute>::iterator i = fAttributes.begin();
925 i != fAttributes.end(); i++)
926 i->second.Dump();
928 if (fDumpIcon || fDumpAll) {
929 if (fSmallIcon != NULL && fSmallIcon->Bits() != NULL) {
930 cout << " \\" << endl << "\t" << kMiniIcon << " ";
931 _DumpIcon((uint8*) fSmallIcon->Bits(), fSmallIcon->BitsLength());
934 if (fBigIcon != NULL && fBigIcon->Bits() != NULL) {
935 cout << " \\" << endl << "\t" << kLargeIcon << " ";
936 _DumpIcon((uint8*) fBigIcon->Bits(), fBigIcon->BitsLength());
939 if (fVectorIcon != NULL && fVectorIconSize != 0) {
940 cout << " \\" << endl << "\t" << kVectorIcon << " ";
941 _DumpIcon((uint8*) fVectorIcon, fVectorIconSize);
945 cout << endl;
949 void
950 MimeType::_DoEdit() throw (Error)
952 if (fDoRemove || fDoForce) {
953 status_t result = Delete();
954 if (result != B_OK)
955 throw Error(strerror(result), result);
957 if (fDoRemove)
958 return;
960 _PurgeProperties();
963 if (!IsInstalled() && Install() != B_OK)
964 throw Error("could not install mimetype '%s'", Type());
966 const char* value = _UserArgValue(kShort);
967 if (value != NULL && (!fDoAdd || fShort.IsEmpty()))
968 if (SetShortDescription(value) != B_OK)
969 throw Error("cannot set %s to %s for '%s'", kShort, value, Type());
971 value = _UserArgValue(kLong);
972 if (value != NULL && (!fDoAdd || fLong.IsEmpty()))
973 if (SetLongDescription(value) != B_OK)
974 throw Error("cannot set %s to %s for '%s'", kLong, value, Type());
976 value = _UserArgValue(kPreferredApp);
977 if (value != NULL && (!fDoAdd || fPrefApp.IsEmpty())) {
978 entry_ref appHint;
979 if (get_ref_for_path(value, &appHint) != B_OK)
980 throw Error("%s ref_entry for '%s' couldn't be found for '%s'",
981 kPreferredApp, value, Type());
983 if (SetAppHint(&appHint) != B_OK)
984 throw Error("cannot set %s to %s for '%s'",
985 kPreferredApp, value, Type());
988 value = _UserArgValue(kPreferredAppSig);
989 if (value != NULL && (!fDoAdd || fPrefAppSig.IsEmpty()))
990 if (SetPreferredApp(value) != B_OK)
991 throw Error("cannot set %s to %s for '%s'",
992 kPreferredAppSig, value, Type());
994 value = _UserArgValue(kSniffRule);
995 if (value != NULL && (!fDoAdd || fSniffRule.IsEmpty()))
996 if (SetSnifferRule(value) != B_OK)
997 throw Error("cannot set %s to %s for '%s'",
998 kSniffRule, value, Type());
1000 value = _UserArgValue(kMiniIcon);
1001 if (value != NULL && (!fDoAdd || fSmallIcon == NULL)) {
1002 int32 iconSize = strlen(value);
1003 if (iconSize / 2 != B_MINI_ICON * B_MINI_ICON)
1004 throw Error("cannot set %s for '%s'. Hex data size %d is invalid",
1005 kMiniIcon, Type(), iconSize);
1007 _SetIcon(value, B_MINI_ICON);
1010 value = _UserArgValue(kLargeIcon);
1011 if (value != NULL && (!fDoAdd || fBigIcon == NULL)) {
1012 int32 iconSize = strlen(value);
1013 if (iconSize / 2 != B_LARGE_ICON * B_LARGE_ICON)
1014 throw Error("cannot set %s for '%s'. Hex data size %d is invalid",
1015 kLargeIcon, Type(), iconSize);
1017 _SetIcon(value, B_LARGE_ICON);
1020 value = _UserArgValue(kVectorIcon);
1021 if (value != NULL && (!fDoAdd || fVectorIcon == NULL)) {
1022 int32 iconSize = strlen(value);
1023 if ((iconSize % 2) != 0)
1024 throw Error("cannot set %s for '%s'. Hex data size %d is invalid",
1025 kVectorIcon, Type(), iconSize);
1027 // vector icon size is negative intended
1028 _SetIcon(value, -iconSize);
1031 // handle extensions update
1032 pair<TUserArgsI, TUserArgsI> exts
1033 = fUserArguments.equal_range(hash_function(kExtension));
1034 for (TUserArgsI i = exts.first; i != exts.second; i++) {
1035 uint32 key = hash_function(i->second);
1036 if (fExtensions.find(key) == fExtensions.end())
1037 fExtensions.insert(pair<uint32, BString>(key, i->second));
1040 if (exts.first != exts.second) {
1041 BMessage msg;
1042 for (map<uint32, BString>::iterator i = fExtensions.begin();
1043 i != fExtensions.end(); i++)
1044 if (msg.AddString("extensions", i->second.String()) != B_OK)
1045 throw Error("extension '%s' couldn't be added",
1046 i->second.String());
1048 if (SetFileExtensions(&msg) != B_OK)
1049 throw Error("set file extensions failed");
1052 // take care about attribute trees
1053 for (TUserAttrsI userAttr = fUserAttributes.begin();
1054 userAttr != fUserAttributes.end(); userAttr++ )
1056 // search for -attribute "name" in args map
1057 TUserArgsI attrArgs = userAttr->find(hash_function(kAttribute));
1058 if (attrArgs == userAttr->end())
1059 throw Error("internal error: %s arg not found", kAttribute);
1061 // check if we already have this attribute cached
1062 map<uint32, MimeAttribute>::iterator
1063 attr = fAttributes.find(hash_function(attrArgs->second));
1064 if (attr == fAttributes.end()) {
1065 // add new one
1066 MimeAttribute mimeAttr(*userAttr);
1067 fAttributes.insert(
1068 pair<uint32, MimeAttribute>(hash_function(mimeAttr.fName), mimeAttr));
1069 } else if (!fDoAdd)
1070 attr->second.SyncWith(*userAttr);
1073 if (fAttributes.size() > 0) {
1074 BMessage msg;
1075 for (map<uint32, MimeAttribute>::iterator i = fAttributes.begin();
1076 i != fAttributes.end(); i++)
1078 i->second.StoreInto(&msg);
1079 if (i->second.InitCheck() != B_OK)
1080 throw Error("storing attributes in message failed");
1083 if (SetAttrInfo(&msg) != B_OK)
1084 throw Error("set mimetype attributes failed");
1089 void
1090 MimeType::Process() throw (Error)
1092 if (fCheckSniffRule) {
1093 TUserArgsI I = fUserArguments.find(fOpMode);
1094 if (I == fUserArguments.end())
1095 throw Error("Sniffer rule is empty");
1097 BString error;
1098 status_t result = BMimeType::CheckSnifferRule(I->second, &error);
1099 if (result == B_OK)
1100 cerr << I->second << endl << "Sniffer rule is correct" << endl;
1101 else
1102 cerr << error.String() << endl;
1104 return;
1107 if (fDoAdd || fDoSet || fDoForce || fDoRemove) {
1108 _DoEdit();
1109 return;
1112 if (fDumpNormal || fDumpRule || fDumpIcon || fDumpAll) {
1113 if (Type() != NULL) {
1114 _Dump(Type());
1115 return;
1118 BMessage superTypes;
1119 int32 superCount = 0;
1120 type_code type = B_INT32_TYPE;
1121 if (BMimeType::GetInstalledSupertypes(&superTypes) != B_OK
1122 || superTypes.GetInfo("super_types", &type, &superCount) != B_OK)
1123 throw Error("super types enumeration failed");
1125 for (int32 si = 0; si < superCount; si++) {
1126 const char* superName = NULL;
1127 if (superTypes.FindString("super_types", si, &superName) != B_OK)
1128 throw Error("name for supertype #%d not found", si);
1130 BMessage types;
1131 if (BMimeType::GetInstalledTypes(superName, &types) != B_OK)
1132 throw Error("mimetypes of supertype '%s' not found", superName);
1134 int32 count = 0;
1135 if (types.GetInfo("types", &type, &count) != B_OK)
1136 continue; // no sub-types?
1138 for (int32 i = 0; i < count; i++) {
1139 const char* name = NULL;
1140 if (types.FindString("types", i, &name) != B_OK)
1141 throw Error("name for type %s/#%d not found", superName, i);
1143 _Dump(name);
1151 main(int argc, char** argv)
1153 // AppServer link is required to work with bitmaps
1154 BApplication app("application/x-vnd.haiku.setmime");
1156 try {
1158 if (argc < 2)
1159 throw Error(kNeedArgMessage);
1161 MimeType mimetype(argv);
1163 mimetype.Process();
1165 } catch(exception& exc) {
1166 cerr << argv[0] << " : " << exc.what() << endl;
1167 cerr << kUsageMessage;
1168 return B_ERROR;
1171 return B_OK;