2 * Copyright 2002-2009, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
7 * Axel Dörfler, axeld@pinc-software.de
11 This class is the guts of the translation kit, it makes the
12 whole thing happen. It bridges the applications using this
13 object with the translators that the apps need to access.
16 #include <TranslatorRoster.h>
23 #include <Application.h>
25 #include <Directory.h>
26 #include <FindDirectory.h>
27 #include <NodeMonitor.h>
31 #include <driver_settings.h>
33 #include <safemode_defs.h>
36 #include "FuncTranslator.h"
37 #include "TranslatorRosterPrivate.h"
42 class QuarantineTranslatorImage
{
44 QuarantineTranslatorImage(
45 BTranslatorRoster::Private
& privateRoster
);
46 ~QuarantineTranslatorImage();
48 void Put(const entry_ref
& ref
);
52 BTranslatorRoster::Private
& fRoster
;
57 } // namespace BPrivate
59 // Extensions used in the extension BMessage, defined in TranslatorFormats.h
60 char B_TRANSLATOR_EXT_HEADER_ONLY
[] = "/headerOnly";
61 char B_TRANSLATOR_EXT_DATA_ONLY
[] = "/dataOnly";
62 char B_TRANSLATOR_EXT_COMMENT
[] = "/comment";
63 char B_TRANSLATOR_EXT_TIME
[] = "/time";
64 char B_TRANSLATOR_EXT_FRAME
[] = "/frame";
65 char B_TRANSLATOR_EXT_BITMAP_RECT
[] = "bits/Rect";
66 char B_TRANSLATOR_EXT_BITMAP_COLOR_SPACE
[] = "bits/space";
67 char B_TRANSLATOR_EXT_BITMAP_PALETTE
[] = "bits/palette";
68 char B_TRANSLATOR_EXT_SOUND_CHANNEL
[] = "nois/channel";
69 char B_TRANSLATOR_EXT_SOUND_MONO
[] = "nois/mono";
70 char B_TRANSLATOR_EXT_SOUND_MARKER
[] = "nois/marker";
71 char B_TRANSLATOR_EXT_SOUND_LOOP
[] = "nois/loop";
73 BTranslatorRoster
* BTranslatorRoster::sDefaultRoster
= NULL
;
79 The purpose of this class is to put a translator entry_ref into - and remove
80 it from the list of translators on destruction (if Remove() was called
83 This is used in Private::CreateTranslators() in case a translator hides a
84 previous one (ie. if you install a translator in the user's translators
85 directory that has the same name as one in the system's directory, it will
88 QuarantineTranslatorImage::QuarantineTranslatorImage(
89 BTranslatorRoster::Private
& privateRoster
)
91 fRoster(privateRoster
),
97 QuarantineTranslatorImage::~QuarantineTranslatorImage()
99 if (fRef
.device
== -1 || !fRemove
)
102 fRoster
.RemoveTranslators(fRef
);
107 QuarantineTranslatorImage::Put(const entry_ref
& ref
)
114 QuarantineTranslatorImage::Remove()
119 } // namespace BPrivate
125 BTranslatorRoster::Private::Private()
127 BHandler("translator roster"),
128 BLocker("translator list"),
129 fABISubDirectory(NULL
),
135 size_t parameterLength
= sizeof(parameter
);
137 if (_kern_get_safemode_option(B_SAFEMODE_SAFE_MODE
, parameter
,
138 ¶meterLength
) == B_OK
) {
139 if (!strcasecmp(parameter
, "enabled") || !strcasecmp(parameter
, "on")
140 || !strcasecmp(parameter
, "true") || !strcasecmp(parameter
, "yes")
141 || !strcasecmp(parameter
, "enable") || !strcmp(parameter
, "1"))
145 if (_kern_get_safemode_option(B_SAFEMODE_DISABLE_USER_ADD_ONS
, parameter
,
146 ¶meterLength
) == B_OK
) {
147 if (!strcasecmp(parameter
, "enabled") || !strcasecmp(parameter
, "on")
148 || !strcasecmp(parameter
, "true") || !strcasecmp(parameter
, "yes")
149 || !strcasecmp(parameter
, "enable") || !strcmp(parameter
, "1"))
153 // We might run in compatibility mode on a system with a different ABI. The
154 // translators matching our ABI can usually be found in respective
155 // subdirectories of the translator directories.
157 if (get_system_info(&info
) == B_OK
158 && (info
.abi
& B_HAIKU_ABI_MAJOR
)
159 != (B_HAIKU_ABI
& B_HAIKU_ABI_MAJOR
)) {
160 switch (B_HAIKU_ABI
& B_HAIKU_ABI_MAJOR
) {
161 case B_HAIKU_ABI_GCC_2
:
162 fABISubDirectory
= "gcc2";
164 case B_HAIKU_ABI_GCC_4
:
165 fABISubDirectory
= "gcc4";
170 // we're sneaking us into the BApplication
171 if (be_app
!= NULL
&& be_app
->Lock()) {
172 be_app
->AddHandler(this);
178 BTranslatorRoster::Private::~Private()
182 if (Looper() && LockLooper()) {
183 BLooper
* looper
= Looper();
184 Looper()->RemoveHandler(this);
188 // Release all translators, so that they can delete themselves
190 TranslatorMap::iterator iterator
= fTranslators
.begin();
191 std::set
<image_id
> images
;
193 while (iterator
!= fTranslators
.end()) {
194 BTranslator
* translator
= iterator
->second
.translator
;
196 translator
->fOwningRoster
= NULL
;
197 // we don't want to be notified about this anymore
199 images
.insert(iterator
->second
.image
);
200 translator
->Release();
207 std::set
<image_id
>::const_iterator imageIterator
= images
.begin();
209 while (imageIterator
!= images
.end()) {
210 unload_add_on(*imageIterator
);
217 BTranslatorRoster::Private::MessageReceived(BMessage
* message
)
219 switch (message
->what
) {
222 BAutolock
locker(this);
224 printf("translator roster node monitor: ");
225 message
->PrintToStream();
228 if (message
->FindInt32("opcode", &opcode
) != B_OK
)
232 case B_ENTRY_CREATED
:
236 if (message
->FindInt32("device", &nodeRef
.device
) != B_OK
237 || message
->FindInt64("directory", &nodeRef
.node
)
239 || message
->FindString("name", &name
) != B_OK
)
242 // TODO: make this better (possible under Haiku)
244 // let the font be written completely before trying to
247 _EntryAdded(nodeRef
, name
);
253 // has the entry been moved into a monitored directory or
254 // has it been removed from one?
257 node_ref fromNodeRef
;
260 if (message
->FindInt32("device", &nodeRef
.device
) != B_OK
261 || message
->FindInt64("to directory", &toNodeRef
.node
)
263 || message
->FindInt64("from directory",
264 (int64
*)&fromNodeRef
.node
) != B_OK
265 || message
->FindInt64("node", (int64
*)&nodeRef
.node
)
267 || message
->FindString("name", &name
) != B_OK
)
270 fromNodeRef
.device
= nodeRef
.device
;
271 toNodeRef
.device
= nodeRef
.device
;
273 // Do we know this one yet?
274 translator_item
* item
= _FindTranslator(nodeRef
);
277 if (_IsKnownDirectory(toNodeRef
))
278 _EntryAdded(toNodeRef
, name
);
282 if (!_IsKnownDirectory(toNodeRef
)) {
283 // translator got removed
284 _RemoveTranslators(&nodeRef
);
288 // the name may have changed
289 item
->ref
.set_name(name
);
290 item
->ref
.directory
= toNodeRef
.node
;
292 if (_IsKnownDirectory(fromNodeRef
)
293 && _IsKnownDirectory(toNodeRef
)) {
294 // TODO: we should rescan for the name, there might be
295 // name clashes with translators in other directories
296 // (as well as old ones revealed)
302 case B_ENTRY_REMOVED
:
305 uint64 directoryNode
;
306 if (message
->FindInt32("device", &nodeRef
.device
) != B_OK
307 || message
->FindInt64("directory",
308 (int64
*)&directoryNode
) != B_OK
309 || message
->FindInt64("node", &nodeRef
.node
) != B_OK
)
312 translator_item
* item
= _FindTranslator(nodeRef
);
314 _RemoveTranslators(&nodeRef
);
322 BHandler::MessageReceived(message
);
329 BTranslatorRoster::Private::AddDefaultPaths()
331 // add user directories first, so that they can override system translators
332 const directory_which paths
[] = {
333 B_USER_NONPACKAGED_ADDONS_DIRECTORY
,
334 B_USER_ADDONS_DIRECTORY
,
335 B_SYSTEM_NONPACKAGED_ADDONS_DIRECTORY
,
336 B_SYSTEM_ADDONS_DIRECTORY
,
339 for (uint32 i
= fSafeMode
? 4 : 0; i
< sizeof(paths
) / sizeof(paths
[0]);
342 status_t status
= find_directory(paths
[i
], &path
, true);
343 if (status
== B_OK
&& path
.Append("Translators") == B_OK
) {
344 mkdir(path
.Path(), 0755);
345 // make sure the directory exists before we add it
346 AddPath(path
.Path());
353 Adds the colon separated list of directories to the roster.
355 Note, the order in which these directories are added to actually matters,
356 translators with the same name will be taken from the earlier directory
357 first. See _CompareTranslatorDirectoryPriority().
360 BTranslatorRoster::Private::AddPaths(const char* paths
)
365 status_t status
= B_OK
;
368 while (paths
!= NULL
) {
369 const char* end
= strchr(paths
, ':');
373 path
.SetTo(paths
, end
- 1 - paths
);
380 // Keep the last error that occured, and return it
381 // but don't overwrite it, if the last path was
382 // added successfully.
384 status_t error
= AddPath(path
.String(), &count
);
385 if (error
!= B_NO_ERROR
)
399 Adds a new directory to the roster.
401 Note, the order in which these directories are added to actually matters,
405 BTranslatorRoster::Private::AddPath(const char* path
, int32
* _added
)
407 BDirectory
directory(path
);
408 status_t status
= directory
.InitCheck();
412 // if a subdirectory for our ABI exists, use that instead
413 if (fABISubDirectory
!= NULL
) {
414 BEntry
entry(&directory
, fABISubDirectory
);
415 if (entry
.IsDirectory()) {
416 status
= directory
.SetTo(&entry
);
423 status
= directory
.GetNodeRef(&nodeRef
);
427 // do we know this directory already?
428 if (_IsKnownDirectory(nodeRef
))
431 if (Looper() != NULL
) {
432 // watch that directory
433 watch_node(&nodeRef
, B_WATCH_DIRECTORY
, this);
434 fDirectories
.push_back(nodeRef
);
441 while (directory
.GetNextRef(&ref
) == B_OK
) {
443 if (entry
.IsDirectory())
445 if (CreateTranslators(ref
, count
) == B_OK
)
454 if (files
!= 0 && count
== 0)
462 BTranslatorRoster::Private::AddTranslator(BTranslator
* translator
,
463 image_id image
, const entry_ref
* ref
, ino_t node
)
465 BAutolock
locker(this);
467 translator_item item
;
468 item
.translator
= translator
;
475 fTranslators
[fNextID
] = item
;
480 translator
->fOwningRoster
= this;
481 translator
->fID
= fNextID
++;
487 BTranslatorRoster::Private::RemoveTranslators(entry_ref
& ref
)
489 _RemoveTranslators(NULL
, &ref
);
494 BTranslatorRoster::Private::FindTranslator(translator_id id
)
497 debugger("translator must be locked!");
501 const translator_item
* item
= _FindTranslator(id
);
503 return item
->translator
;
510 BTranslatorRoster::Private::GetTranslatorData(image_id image
,
511 translator_data
& data
)
513 // If this is a translator add-on, it is in the C format
514 memset(&data
, 0, sizeof(translator_data
));
516 // find all the symbols
519 if (get_image_symbol(image
, "translatorName", B_SYMBOL_TYPE_DATA
,
520 (void**)&data
.name
) < B_OK
521 || get_image_symbol(image
, "translatorInfo", B_SYMBOL_TYPE_DATA
,
522 (void**)&data
.info
) < B_OK
523 || get_image_symbol(image
, "translatorVersion", B_SYMBOL_TYPE_DATA
,
524 (void**)&version
) < B_OK
|| version
== NULL
525 || get_image_symbol(image
, "inputFormats", B_SYMBOL_TYPE_DATA
,
526 (void**)&data
.input_formats
) < B_OK
527 || get_image_symbol(image
, "outputFormats", B_SYMBOL_TYPE_DATA
,
528 (void**)&data
.output_formats
) < B_OK
529 || get_image_symbol(image
, "Identify", B_SYMBOL_TYPE_TEXT
,
530 (void**)&data
.identify_hook
) < B_OK
531 || get_image_symbol(image
, "Translate", B_SYMBOL_TYPE_TEXT
,
532 (void**)&data
.translate_hook
) < B_OK
) {
536 data
.version
= *version
;
538 // those calls are optional
539 get_image_symbol(image
, "MakeConfig", B_SYMBOL_TYPE_TEXT
,
540 (void**)&data
.make_config_hook
);
541 get_image_symbol(image
, "GetConfigMessage", B_SYMBOL_TYPE_TEXT
,
542 (void**)&data
.get_config_message_hook
);
549 BTranslatorRoster::Private::CreateTranslators(const entry_ref
& ref
,
550 int32
& count
, BMessage
* update
)
552 BAutolock
locker(this);
554 BPrivate::QuarantineTranslatorImage
quarantine(*this);
556 const translator_item
* item
= _FindTranslator(ref
.name
);
558 // check if the known translator has a higher priority
559 if (_CompareTranslatorDirectoryPriority(item
->ref
, ref
) <= 0) {
560 // keep the existing add-on
564 // replace existing translator(s) if the new translator succeeds
565 quarantine
.Put(item
->ref
);
570 status_t status
= entry
.GetNodeRef(&nodeRef
);
575 image_id image
= load_add_on(path
.Path());
579 // Function pointer used to create post R4.5 style translators
580 BTranslator
*(*makeNthTranslator
)(int32 n
, image_id you
, uint32 flags
, ...);
582 status
= get_image_symbol(image
, "make_nth_translator",
583 B_SYMBOL_TYPE_TEXT
, (void**)&makeNthTranslator
);
584 if (status
== B_OK
) {
585 // If the translator add-on supports the post R4.5
586 // translator creation mechanism, keep loading translators
587 // until MakeNthTranslator stops returning them.
588 BTranslator
* translator
= NULL
;
590 for (int32 n
= 0; (translator
= makeNthTranslator(n
, image
, 0)) != NULL
;
592 if (AddTranslator(translator
, image
, &ref
, nodeRef
.node
) == B_OK
) {
594 update
->AddInt32("translator_id", translator
->fID
);
598 translator
->Release();
599 // this will delete the translator
604 unload_add_on(image
);
610 // If this is a translator add-on, it is in the C format
611 translator_data translatorData
;
612 status
= GetTranslatorData(image
, translatorData
);
614 // add this translator to the list
615 BPrivate::BFuncTranslator
* translator
= NULL
;
616 if (status
== B_OK
) {
617 translator
= new (std::nothrow
) BPrivate::BFuncTranslator(
619 if (translator
== NULL
)
620 status
= B_NO_MEMORY
;
624 status
= AddTranslator(translator
, image
, &ref
, nodeRef
.node
);
626 if (status
== B_OK
) {
628 update
->AddInt32("translator_id", translator
->fID
);
632 unload_add_on(image
);
639 BTranslatorRoster::Private::StartWatching(BMessenger target
)
642 fMessengers
.push_back(target
);
648 fLazyScanning
= false;
649 // Since we now have someone to report to, we cannot lazily
650 // adopt changes to the translator any longer
660 BTranslatorRoster::Private::StopWatching(BMessenger target
)
662 MessengerList::iterator iterator
= fMessengers
.begin();
664 while (iterator
!= fMessengers
.end()) {
665 if (*iterator
== target
) {
666 fMessengers
.erase(iterator
);
667 if (fMessengers
.empty())
668 fLazyScanning
= true;
681 BTranslatorRoster::Private::StoreTranslators(BMessage
& archive
)
683 BAutolock
locker(this);
685 TranslatorMap::const_iterator iterator
= fTranslators
.begin();
687 while (iterator
!= fTranslators
.end()) {
688 const translator_item
& item
= iterator
->second
;
689 BPath
path(&item
.ref
);
690 if (path
.InitCheck() == B_OK
)
691 archive
.AddString("be:translator_path", path
.Path());
701 BTranslatorRoster::Private::Identify(BPositionIO
* source
,
702 BMessage
* ioExtension
, uint32 hintType
, const char* hintMIME
,
703 uint32 wantType
, translator_info
* _info
)
705 BAutolock
locker(this);
709 TranslatorMap::const_iterator iterator
= fTranslators
.begin();
710 BMessage baseExtension
;
711 if (ioExtension
!= NULL
)
712 baseExtension
= *ioExtension
;
714 float bestWeight
= 0.0f
;
716 while (iterator
!= fTranslators
.end()) {
717 BTranslator
& translator
= *iterator
->second
.translator
;
719 off_t pos
= source
->Seek(0, SEEK_SET
);
721 return pos
< 0 ? (status_t
)pos
: B_IO_ERROR
;
723 int32 formatsCount
= 0;
724 const translation_format
* formats
= translator
.InputFormats(
726 const translation_format
* format
= _CheckHints(formats
, formatsCount
,
729 BMessage
extension(baseExtension
);
730 translator_info info
;
731 if (translator
.Identify(source
, format
, &extension
, &info
, wantType
)
733 float weight
= info
.quality
* info
.capability
;
734 if (weight
> bestWeight
) {
735 if (ioExtension
!= NULL
)
736 *ioExtension
= extension
;
739 info
.translator
= iterator
->first
;
740 memcpy(_info
, &info
, sizeof(translator_info
));
747 if (bestWeight
> 0.0f
)
750 return B_NO_TRANSLATOR
;
755 BTranslatorRoster::Private::GetTranslators(BPositionIO
* source
,
756 BMessage
* ioExtension
, uint32 hintType
, const char* hintMIME
,
757 uint32 wantType
, translator_info
** _info
, int32
* _numInfo
)
759 BAutolock
locker(this);
763 int32 arraySize
= fTranslators
.size();
764 translator_info
* array
= new (std::nothrow
) translator_info
[arraySize
];
768 TranslatorMap::const_iterator iterator
= fTranslators
.begin();
771 while (iterator
!= fTranslators
.end()) {
772 BTranslator
& translator
= *iterator
->second
.translator
;
774 off_t pos
= source
->Seek(0, SEEK_SET
);
777 return pos
< 0 ? status_t(pos
) : B_IO_ERROR
;
780 int32 formatsCount
= 0;
781 const translation_format
* formats
= translator
.InputFormats(
783 const translation_format
* format
= _CheckHints(formats
, formatsCount
,
786 translator_info info
;
787 if (translator
.Identify(source
, format
, ioExtension
, &info
, wantType
)
789 info
.translator
= iterator
->first
;
790 array
[count
++] = info
;
798 qsort(array
, count
, sizeof(translator_info
),
799 BTranslatorRoster::Private::_CompareSupport
);
800 // translators are sorted by best support
807 BTranslatorRoster::Private::GetAllTranslators(translator_id
** _ids
,
810 BAutolock
locker(this);
814 int32 arraySize
= fTranslators
.size();
815 translator_id
* array
= new (std::nothrow
) translator_id
[arraySize
];
819 TranslatorMap::const_iterator iterator
= fTranslators
.begin();
822 while (iterator
!= fTranslators
.end()) {
823 array
[count
++] = iterator
->first
;
834 BTranslatorRoster::Private::GetRefFor(translator_id id
, entry_ref
& ref
)
836 BAutolock
locker(this);
838 const translator_item
* item
= _FindTranslator(id
);
840 return B_NO_TRANSLATOR
;
842 BEntry
entry(&item
->ref
);
843 if (entry
.InitCheck() == B_OK
&& entry
.Exists() && entry
.IsFile()) {
853 BTranslatorRoster::Private::TranslatorDeleted(translator_id id
)
855 BAutolock
locker(this);
857 TranslatorMap::iterator iterator
= fTranslators
.find(id
);
858 if (iterator
== fTranslators
.end())
861 fTranslators
.erase(iterator
);
866 BTranslatorRoster::Private::_CompareSupport(const void* _a
, const void* _b
)
868 const translator_info
* infoA
= (const translator_info
*)_a
;
869 const translator_info
* infoB
= (const translator_info
*)_b
;
871 float weightA
= infoA
->quality
* infoA
->capability
;
872 float weightB
= infoB
->quality
* infoB
->capability
;
874 if (weightA
== weightB
)
876 if (weightA
> weightB
)
884 In lazy mode, freshly installed translator are not scanned immediately
885 when they become available. Instead, they are put into a set.
887 When a method is called that may be interested in these new translators,
888 they are scanned on the fly. Since lazy mode also means that this roster
889 does not have any listeners, we don't need to notify anyone about those
893 BTranslatorRoster::Private::_RescanChanged()
895 while (!fRescanEntries
.empty()) {
896 EntryRefSet::iterator iterator
= fRescanEntries
.begin();
898 CreateTranslators(*iterator
, count
);
900 fRescanEntries
.erase(iterator
);
906 Tests if the hints provided for a source stream are compatible to
907 the formats the translator exports.
909 const translation_format
*
910 BTranslatorRoster::Private::_CheckHints(const translation_format
* formats
,
911 int32 formatsCount
, uint32 hintType
, const char* hintMIME
)
913 if (formats
== NULL
|| formatsCount
<= 0 || (!hintType
&& hintMIME
== NULL
))
916 // The provided MIME type hint may be a super type
918 if (hintMIME
&& !strchr(hintMIME
, '/'))
919 super
= strlen(hintMIME
);
921 // scan for suitable format
922 for (int32 i
= 0; i
< formatsCount
&& formats
[i
].type
; i
++) {
923 if (formats
[i
].type
== hintType
925 && ((super
&& !strncmp(formats
[i
].MIME
, hintMIME
, super
))
926 || !strcmp(formats
[i
].MIME
, hintMIME
))))
934 const translator_item
*
935 BTranslatorRoster::Private::_FindTranslator(translator_id id
) const
937 TranslatorMap::const_iterator iterator
= fTranslators
.find(id
);
938 if (iterator
== fTranslators
.end())
941 return &iterator
->second
;
945 const translator_item
*
946 BTranslatorRoster::Private::_FindTranslator(const char* name
) const
951 TranslatorMap::const_iterator iterator
= fTranslators
.begin();
953 while (iterator
!= fTranslators
.end()) {
954 const translator_item
& item
= iterator
->second
;
955 if (item
.ref
.name
!= NULL
&& !strcmp(item
.ref
.name
, name
))
965 const translator_item
*
966 BTranslatorRoster::Private::_FindTranslator(entry_ref
& ref
) const
968 if (ref
.name
== NULL
)
971 TranslatorMap::const_iterator iterator
= fTranslators
.begin();
973 while (iterator
!= fTranslators
.end()) {
974 const translator_item
& item
= iterator
->second
;
986 BTranslatorRoster::Private::_FindTranslator(node_ref
& nodeRef
)
988 if (nodeRef
.device
< 0)
991 TranslatorMap::iterator iterator
= fTranslators
.begin();
993 while (iterator
!= fTranslators
.end()) {
994 translator_item
& item
= iterator
->second
;
995 if (item
.ref
.device
== nodeRef
.device
996 && item
.node
== nodeRef
.node
)
1007 Directories added to the roster have a certain priority - the first entry
1008 to be added has the highest priority; if a translator with the same name
1009 is to be found in two directories, the one with the higher priority is
1013 BTranslatorRoster::Private::_CompareTranslatorDirectoryPriority(
1014 const entry_ref
& a
, const entry_ref
& b
) const
1016 // priority is determined by the order in the list
1019 nodeRefA
.device
= a
.device
;
1020 nodeRefA
.node
= a
.directory
;
1023 nodeRefB
.device
= b
.device
;
1024 nodeRefB
.node
= b
.directory
;
1026 NodeRefList::const_iterator iterator
= fDirectories
.begin();
1028 while (iterator
!= fDirectories
.end()) {
1029 if (*iterator
== nodeRefA
)
1031 if (*iterator
== nodeRefB
)
1042 BTranslatorRoster::Private::_IsKnownDirectory(const node_ref
& nodeRef
) const
1044 NodeRefList::const_iterator iterator
= fDirectories
.begin();
1046 while (iterator
!= fDirectories
.end()) {
1047 if (*iterator
== nodeRef
)
1058 BTranslatorRoster::Private::_RemoveTranslators(const node_ref
* nodeRef
,
1059 const entry_ref
* ref
)
1061 if (ref
== NULL
&& nodeRef
== NULL
)
1064 TranslatorMap::iterator iterator
= fTranslators
.begin();
1065 BMessage
update(B_TRANSLATOR_REMOVED
);
1066 image_id image
= -1;
1068 while (iterator
!= fTranslators
.end()) {
1069 TranslatorMap::iterator next
= iterator
;
1072 const translator_item
& item
= iterator
->second
;
1073 if ((ref
!= NULL
&& item
.ref
== *ref
)
1074 || (nodeRef
!= NULL
&& item
.ref
.device
== nodeRef
->device
1075 && item
.node
== nodeRef
->node
)) {
1076 item
.translator
->fOwningRoster
= NULL
;
1077 // if the translator is busy, we don't want to be notified
1078 // about the removal later on
1079 item
.translator
->Release();
1081 update
.AddInt32("translator_id", iterator
->first
);
1083 fTranslators
.erase(iterator
);
1089 // Unload image from the removed translator
1092 unload_add_on(image
);
1094 _NotifyListeners(update
);
1099 BTranslatorRoster::Private::_EntryAdded(const node_ref
& nodeRef
,
1103 ref
.device
= nodeRef
.device
;
1104 ref
.directory
= nodeRef
.node
;
1112 In lazy mode, the entry is marked to be rescanned on next use of any
1113 translation method (that could make use of it).
1114 In non-lazy mode, the translators for this entry are created directly
1115 and listeners notified.
1117 Called by the node monitor handling.
1120 BTranslatorRoster::Private::_EntryAdded(const entry_ref
& ref
)
1123 if (entry
.SetTo(&ref
) != B_OK
|| !entry
.IsFile())
1126 if (fLazyScanning
) {
1127 fRescanEntries
.insert(ref
);
1131 BMessage
update(B_TRANSLATOR_ADDED
);
1133 CreateTranslators(ref
, count
, &update
);
1135 _NotifyListeners(update
);
1140 BTranslatorRoster::Private::_NotifyListeners(BMessage
& update
) const
1142 MessengerList::const_iterator iterator
= fMessengers
.begin();
1144 while (iterator
!= fMessengers
.end()) {
1145 (*iterator
).SendMessage(&update
);
1154 BTranslatorRoster::BTranslatorRoster()
1160 BTranslatorRoster::BTranslatorRoster(BMessage
* model
)
1167 model
->FindString("be:translator_path", i
, &path
) == B_OK
; i
++) {
1170 if (entry
.GetRef(&ref
) == B_OK
) {
1172 fPrivate
->CreateTranslators(ref
, count
);
1179 BTranslatorRoster::~BTranslatorRoster()
1181 // If the default BTranslatorRoster is being
1182 // deleted, set the pointer to the default
1183 // BTranslatorRoster to NULL
1184 if (sDefaultRoster
== this)
1185 sDefaultRoster
= NULL
;
1192 BTranslatorRoster::_Initialize()
1194 fPrivate
= new BTranslatorRoster::Private();
1199 BTranslatorRoster::Archive(BMessage
* into
, bool deep
) const
1201 status_t status
= BArchivable::Archive(into
, deep
);
1205 return fPrivate
->StoreTranslators(*into
);
1210 BTranslatorRoster::Instantiate(BMessage
* from
)
1212 if (!from
|| !validate_instantiation(from
, "BTranslatorRoster"))
1215 return new BTranslatorRoster(from
);
1220 BTranslatorRoster::Default()
1222 static int32 lock
= 0;
1224 if (sDefaultRoster
!= NULL
)
1225 return sDefaultRoster
;
1227 if (atomic_add(&lock
, 1) != 0) {
1228 // Just wait for the default translator to be instantiated
1229 while (sDefaultRoster
== NULL
)
1232 atomic_add(&lock
, -1);
1233 return sDefaultRoster
;
1236 // If the default translators have not been loaded,
1237 // create a new BTranslatorRoster for them, and load them.
1238 if (sDefaultRoster
== NULL
) {
1239 BTranslatorRoster
* roster
= new BTranslatorRoster();
1240 roster
->AddTranslators(NULL
);
1242 sDefaultRoster
= roster
;
1243 // this will unlock any other threads waiting for
1244 // the default roster to become available
1247 atomic_add(&lock
, -1);
1248 return sDefaultRoster
;
1253 This function takes a string of colon delimited paths, and adds
1254 the translators from those paths to this BTranslatorRoster.
1256 If load_path is NULL, it parses the environment variable
1257 TRANSLATORS. If that does not exist, it uses the system paths:
1258 /boot/home/config/add-ons/Translators,
1259 /system/add-ons/Translators.
1262 BTranslatorRoster::AddTranslators(const char* path
)
1265 path
= getenv("TRANSLATORS");
1267 fPrivate
->AddDefaultPaths();
1271 return fPrivate
->AddPaths(path
);
1276 Adds a BTranslator based object to the BTranslatorRoster.
1277 When you add a BTranslator roster, it is Acquire()'d by
1278 BTranslatorRoster; it is Release()'d when the
1279 BTranslatorRoster is deleted.
1281 \param translator the translator to be added to the
1284 \return B_BAD_VALUE, if translator is NULL,
1285 B_OK if all went well
1288 BTranslatorRoster::AddTranslator(BTranslator
* translator
)
1293 return fPrivate
->AddTranslator(translator
);
1298 BTranslatorRoster::IsTranslator(entry_ref
* ref
)
1304 image_id image
= load_add_on(path
.Path());
1308 // Function pointer used to create post R4.5 style translators
1309 BTranslator
* (*makeNthTranslator
)(int32 n
, image_id you
, uint32 flags
, ...);
1311 status_t status
= get_image_symbol(image
, "make_nth_translator",
1312 B_SYMBOL_TYPE_TEXT
, (void**)&makeNthTranslator
);
1313 if (status
< B_OK
) {
1314 // If this is a translator add-on, it is in the C format
1315 translator_data translatorData
;
1316 status
= fPrivate
->GetTranslatorData(image
, translatorData
);
1319 unload_add_on(image
);
1320 return status
== B_OK
;
1325 This function determines which translator is best suited
1326 to convert the data from \a source.
1328 \param source the data to be identified
1329 \param ioExtension the configuration data for the translator
1330 \param _info the information about the chosen translator is put here
1331 \param hintType a hint about the type of data that is in \a source, set
1332 it to zero if the type is not known
1333 \param hintMIME a hint about the MIME type of \a source, set it to NULL
1334 if the type is not known.
1335 \param wantType the desired output type - if zero, any type is okay.
1337 \return B_OK, identification of \a source was successful,
1338 B_NO_TRANSLATOR, no appropriate translator found,
1339 and other errors from accessing the source stream
1342 BTranslatorRoster::Identify(BPositionIO
* source
, BMessage
* ioExtension
,
1343 translator_info
* _info
, uint32 hintType
, const char* hintMIME
,
1346 if (source
== NULL
|| _info
== NULL
)
1349 return fPrivate
->Identify(source
, ioExtension
, hintType
, hintMIME
, wantType
,
1355 Finds all translators capable of handling the data in \a source
1356 and puts them into the outInfo array (which you must delete
1357 yourself when you are done with it). Specifying a value for
1358 \a hintType, \a hintMIME and/or \a wantType causes only the
1359 translators that satisfy them to be included in the outInfo.
1361 \param source the data to be translated
1362 \param ioExtension the configuration data for the translator
1363 \param _info, the array of acceptable translators is stored here if
1364 the function succeeds. It's the caller's responsibility to free
1365 the array using delete[].
1366 \param _numInfo, number of entries in the \a _info array
1367 \param hintType a hint about the type of data that is in \a source, set
1368 it to zero if the type is not known
1369 \param hintMIME a hint about the MIME type of \a source, set it to NULL
1370 if the type is not known.
1371 \param wantType the desired output type - if zero, any type is okay.
1373 \return B_OK, successfully indentified the data in \a source
1374 B_NO_TRANSLATOR, no translator could handle \a source
1375 other errors, problems using \a source
1378 BTranslatorRoster::GetTranslators(BPositionIO
* source
, BMessage
* ioExtension
,
1379 translator_info
** _info
, int32
* _numInfo
, uint32 hintType
,
1380 const char* hintMIME
, uint32 wantType
)
1382 if (source
== NULL
|| _info
== NULL
|| _numInfo
== NULL
)
1385 return fPrivate
->GetTranslators(source
, ioExtension
, hintType
, hintMIME
,
1386 wantType
, _info
, _numInfo
);
1391 Returns an array in \a _ids of all of the translators stored by this
1393 You must free the array using delete[] when you are done with it.
1395 \param _ids the array is stored there (you own the array).
1396 \param _count number of IDs in the array.
1399 BTranslatorRoster::GetAllTranslators(translator_id
** _ids
, int32
* _count
)
1401 if (_ids
== NULL
|| _count
== NULL
)
1404 return fPrivate
->GetAllTranslators(_ids
, _count
);
1409 Returns information about the translator with the specified
1411 You must not free any of the data you get back.
1413 \param id identifies which translator you want info for
1414 \param _name the translator name is put here
1415 \param _info the translator description is put here
1416 \param _version the translation version is put here
1418 \return B_OK if successful,
1419 B_BAD_VALUE, if all parameters are NULL
1420 B_NO_TRANSLATOR, \id didn't identify an existing translator
1423 BTranslatorRoster::GetTranslatorInfo(translator_id id
, const char** _name
,
1424 const char** _info
, int32
* _version
)
1426 if (_name
== NULL
&& _info
== NULL
&& _version
== NULL
)
1429 BAutolock
locker(fPrivate
);
1431 BTranslator
* translator
= fPrivate
->FindTranslator(id
);
1432 if (translator
== NULL
)
1433 return B_NO_TRANSLATOR
;
1436 *_name
= translator
->TranslatorName();
1438 *_info
= translator
->TranslatorInfo();
1440 *_version
= translator
->TranslatorVersion();
1447 Returns all of the input formats for the translator specified
1449 You must not free any of the data you get back.
1451 \param id identifies which translator you want the input formats for
1452 \param _formats array of input formats
1453 \param _numFormats number of formats in the array
1455 \return B_OK if successful,
1456 B_BAD_VALUE, if any parameter is NULL
1457 B_NO_TRANSLATOR, \id didn't identify an existing translator
1460 BTranslatorRoster::GetInputFormats(translator_id id
,
1461 const translation_format
** _formats
, int32
* _numFormats
)
1463 if (_formats
== NULL
|| _numFormats
== NULL
)
1466 BAutolock
locker(fPrivate
);
1468 BTranslator
* translator
= fPrivate
->FindTranslator(id
);
1469 if (translator
== NULL
)
1470 return B_NO_TRANSLATOR
;
1472 *_formats
= translator
->InputFormats(_numFormats
);
1478 Returns all of the output formats for the translator specified
1480 You must not free any of the data you get back.
1482 \param id identifies which translator you want the output formats for
1483 \param _formats array of output formats
1484 \param _numFormats number of formats in the array
1486 \return B_OK if successful,
1487 B_BAD_VALUE, if any parameter is NULL
1488 B_NO_TRANSLATOR, \id didn't identify an existing translator
1491 BTranslatorRoster::GetOutputFormats(translator_id id
,
1492 const translation_format
** _formats
, int32
* _numFormats
)
1494 if (_formats
== NULL
|| _numFormats
== NULL
)
1497 BAutolock
locker(fPrivate
);
1499 BTranslator
* translator
= fPrivate
->FindTranslator(id
);
1500 if (translator
== NULL
)
1501 return B_NO_TRANSLATOR
;
1503 *_formats
= translator
->OutputFormats(_numFormats
);
1509 This function is the whole point of the Translation Kit.
1510 This is for translating the data in \a source to \a destination
1511 using the format \a wantOutType.
1513 \param source the data to be translated
1514 \param ioExtension the configuration data for the translator
1515 \param info information about translator to use (can be NULL, in which
1516 case the \a source is identified first)
1517 \param destination where \a source is translated to
1518 \param hintType a hint about the type of data that is in \a source, set
1519 it to zero if the type is not known
1520 \param hintMIME a hint about the MIME type of \a source, set it to NULL
1521 if the type is not known.
1522 \param wantType the desired output type - if zero, any type is okay.
1524 \return B_OK, translation of \a source was successful,
1525 B_NO_TRANSLATOR, no appropriate translator found,
1526 and other errors from accessing the source and destination streams
1529 BTranslatorRoster::Translate(BPositionIO
* source
, const translator_info
* info
,
1530 BMessage
* ioExtension
, BPositionIO
* destination
, uint32 wantOutType
,
1531 uint32 hintType
, const char* hintMIME
)
1533 if (source
== NULL
|| destination
== NULL
)
1536 translator_info infoBuffer
;
1539 // look for a suitable translator
1540 status_t status
= fPrivate
->Identify(source
, ioExtension
, hintType
,
1541 hintMIME
, wantOutType
, &infoBuffer
);
1548 if (!fPrivate
->Lock())
1551 BTranslator
* translator
= fPrivate
->FindTranslator(info
->translator
);
1552 if (translator
!= NULL
) {
1553 translator
->Acquire();
1554 // make sure this translator is not removed while we're playing with
1555 // it; translating shouldn't be serialized!
1560 if (translator
== NULL
)
1561 return B_NO_TRANSLATOR
;
1563 status_t status
= B_OK
;
1564 off_t pos
= source
->Seek(0, SEEK_SET
);
1566 status
= pos
< 0 ? (status_t
)pos
: B_IO_ERROR
;
1567 if (status
== B_OK
) {
1568 status
= translator
->Translate(source
, info
, ioExtension
, wantOutType
,
1571 translator
->Release();
1578 This function is the whole point of the Translation Kit.
1579 This is for translating the data in \a source to \a destination
1580 using the format \a wantOutType and the translator identified
1583 \param id the translator to be used
1584 \param source the data to be translated
1585 \param ioExtension the configuration data for the translator
1586 \param destination where \a source is translated to
1587 \param wantType the desired output type - if zero, any type is okay.
1589 \return B_OK, translation of \a source was successful,
1590 B_NO_TRANSLATOR, no appropriate translator found,
1591 and other errors from accessing the source and destination streams
1594 BTranslatorRoster::Translate(translator_id id
, BPositionIO
* source
,
1595 BMessage
* ioExtension
, BPositionIO
* destination
, uint32 wantOutType
)
1597 if (source
== NULL
|| destination
== NULL
)
1600 if (!fPrivate
->Lock())
1603 BTranslator
* translator
= fPrivate
->FindTranslator(id
);
1604 if (translator
!= NULL
) {
1605 translator
->Acquire();
1606 // make sure this translator is not removed while we're playing with
1607 // it; translating shouldn't be serialized!
1612 if (translator
== NULL
)
1613 return B_NO_TRANSLATOR
;
1616 off_t pos
= source
->Seek(0, SEEK_SET
);
1618 translator_info info
;
1619 status
= translator
->Identify(source
, NULL
, ioExtension
, &info
,
1621 if (status
>= B_OK
) {
1622 off_t pos
= source
->Seek(0, SEEK_SET
);
1624 status
= pos
< 0 ? (status_t
)pos
: B_IO_ERROR
;
1626 status
= translator
->Translate(source
, &info
, ioExtension
,
1627 wantOutType
, destination
);
1631 status
= pos
< 0 ? (status_t
)pos
: B_IO_ERROR
;
1632 translator
->Release();
1639 Creates a BView in \a _view for configuring the translator specified
1640 by \a id. Not all translators support this, though.
1642 \param id identifies which translator you want the input formats for
1643 \param ioExtension the configuration data for the translator
1644 \param _view the view for configuring the translator
1645 \param _extent the bounds for the (resizable) view
1647 \return B_OK if successful,
1648 B_BAD_VALUE, if any parameter is NULL
1649 B_NO_TRANSLATOR, \id didn't identify an existing translator
1652 BTranslatorRoster::MakeConfigurationView(translator_id id
,
1653 BMessage
* ioExtension
, BView
** _view
, BRect
* _extent
)
1655 if (_view
== NULL
|| _extent
== NULL
)
1658 BAutolock
locker(fPrivate
);
1660 BTranslator
* translator
= fPrivate
->FindTranslator(id
);
1661 if (translator
== NULL
)
1662 return B_NO_TRANSLATOR
;
1664 return translator
->MakeConfigurationView(ioExtension
, _view
, _extent
);
1669 Gets the configuration setttings for the translator
1670 specified by \a id and puts them into \a ioExtension.
1672 \param id identifies which translator you want the input formats for
1673 \param ioExtension the configuration data for the translator
1675 \return B_OK if successful,
1676 B_BAD_VALUE, if \a ioExtension is NULL
1677 B_NO_TRANSLATOR, \id didn't identify an existing translator
1680 BTranslatorRoster::GetConfigurationMessage(translator_id id
,
1681 BMessage
* ioExtension
)
1686 BAutolock
locker(fPrivate
);
1688 BTranslator
* translator
= fPrivate
->FindTranslator(id
);
1689 if (translator
== NULL
)
1690 return B_NO_TRANSLATOR
;
1692 return translator
->GetConfigurationMessage(ioExtension
);
1697 Gets the entry_ref for the given translator (of course, this works only
1698 for disk based translators).
1700 \param id identifies which translator you want the input formats for
1701 \param ref the entry ref is stored there
1703 \return B_OK if successful,
1704 B_ERROR, if this is not a disk based translator
1705 B_BAD_VALUE, if \a ref is NULL
1706 B_NO_TRANSLATOR, \id didn't identify an existing translator
1709 BTranslatorRoster::GetRefFor(translator_id id
, entry_ref
* ref
)
1714 return fPrivate
->GetRefFor(id
, *ref
);
1719 BTranslatorRoster::StartWatching(BMessenger target
)
1721 return fPrivate
->StartWatching(target
);
1726 BTranslatorRoster::StopWatching(BMessenger target
)
1728 return fPrivate
->StopWatching(target
);
1732 // #pragma mark - private
1735 BTranslatorRoster::BTranslatorRoster(const BTranslatorRoster
&other
)
1741 BTranslatorRoster::operator=(const BTranslatorRoster
&tr
)
1747 #if __GNUC__ == 2 // gcc 2
1749 /*static*/ const char*
1750 BTranslatorRoster::Version(int32
* outCurVersion
, int32
* outMinVersion
,
1753 if (!outCurVersion
|| !outMinVersion
)
1756 static char vString
[50];
1757 static char vDate
[] = __DATE__
;
1759 sprintf(vString
, "Translation Kit v%d.%d.%d %s\n",
1760 int(B_TRANSLATION_MAJOR_VERSION(B_TRANSLATION_CURRENT_VERSION
)),
1761 int(B_TRANSLATION_MINOR_VERSION(B_TRANSLATION_CURRENT_VERSION
)),
1762 int(B_TRANSLATION_REVISION_VERSION(B_TRANSLATION_CURRENT_VERSION
)),
1765 *outCurVersion
= B_TRANSLATION_CURRENT_VERSION
;
1766 *outMinVersion
= B_TRANSLATION_MIN_VERSION
;
1773 void BTranslatorRoster::ReservedTranslatorRoster1() {}
1774 void BTranslatorRoster::ReservedTranslatorRoster2() {}
1775 void BTranslatorRoster::ReservedTranslatorRoster3() {}
1776 void BTranslatorRoster::ReservedTranslatorRoster4() {}
1777 void BTranslatorRoster::ReservedTranslatorRoster5() {}
1778 void BTranslatorRoster::ReservedTranslatorRoster6() {}