2 * Copyright 2002-2015, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
6 * Axel Dörfler, axeld@pinc-software.de
7 * Markus Himmel, markus@himmel-villmar.de
12 This class is the guts of the translation kit, it makes the
13 whole thing happen. It bridges the applications using this
14 object with the translators that the apps need to access.
17 #include <TranslatorRoster.h>
24 #include <Application.h>
26 #include <Directory.h>
27 #include <FindDirectory.h>
28 #include <NodeMonitor.h>
32 #include <driver_settings.h>
34 #include <safemode_defs.h>
37 #include "FuncTranslator.h"
38 #include "TranslatorRosterPrivate.h"
43 class QuarantineTranslatorImage
{
45 QuarantineTranslatorImage(
46 BTranslatorRoster::Private
& privateRoster
);
47 ~QuarantineTranslatorImage();
49 void Put(const entry_ref
& ref
);
53 BTranslatorRoster::Private
& fRoster
;
58 } // namespace BPrivate
60 // Extensions used in the extension BMessage, defined in TranslatorFormats.h
61 char B_TRANSLATOR_EXT_HEADER_ONLY
[] = "/headerOnly";
62 char B_TRANSLATOR_EXT_DATA_ONLY
[] = "/dataOnly";
63 char B_TRANSLATOR_EXT_COMMENT
[] = "/comment";
64 char B_TRANSLATOR_EXT_TIME
[] = "/time";
65 char B_TRANSLATOR_EXT_FRAME
[] = "/frame";
66 char B_TRANSLATOR_EXT_BITMAP_RECT
[] = "bits/Rect";
67 char B_TRANSLATOR_EXT_BITMAP_COLOR_SPACE
[] = "bits/space";
68 char B_TRANSLATOR_EXT_BITMAP_PALETTE
[] = "bits/palette";
69 char B_TRANSLATOR_EXT_SOUND_CHANNEL
[] = "nois/channel";
70 char B_TRANSLATOR_EXT_SOUND_MONO
[] = "nois/mono";
71 char B_TRANSLATOR_EXT_SOUND_MARKER
[] = "nois/marker";
72 char B_TRANSLATOR_EXT_SOUND_LOOP
[] = "nois/loop";
74 BTranslatorRoster
* BTranslatorRoster::sDefaultRoster
= NULL
;
80 The purpose of this class is to put a translator entry_ref into - and remove
81 it from the list of translators on destruction (if Remove() was called
84 This is used in Private::CreateTranslators() in case a translator hides a
85 previous one (ie. if you install a translator in the user's translators
86 directory that has the same name as one in the system's directory, it will
89 QuarantineTranslatorImage::QuarantineTranslatorImage(
90 BTranslatorRoster::Private
& privateRoster
)
92 fRoster(privateRoster
),
98 QuarantineTranslatorImage::~QuarantineTranslatorImage()
100 if (fRef
.device
== -1 || !fRemove
)
103 fRoster
.RemoveTranslators(fRef
);
108 QuarantineTranslatorImage::Put(const entry_ref
& ref
)
115 QuarantineTranslatorImage::Remove()
120 } // namespace BPrivate
126 BTranslatorRoster::Private::Private()
128 BHandler("translator roster"),
129 BLocker("translator list"),
130 fABISubDirectory(NULL
),
136 size_t parameterLength
= sizeof(parameter
);
138 if (_kern_get_safemode_option(B_SAFEMODE_SAFE_MODE
, parameter
,
139 ¶meterLength
) == B_OK
) {
140 if (!strcasecmp(parameter
, "enabled") || !strcasecmp(parameter
, "on")
141 || !strcasecmp(parameter
, "true") || !strcasecmp(parameter
, "yes")
142 || !strcasecmp(parameter
, "enable") || !strcmp(parameter
, "1"))
146 if (_kern_get_safemode_option(B_SAFEMODE_DISABLE_USER_ADD_ONS
, parameter
,
147 ¶meterLength
) == B_OK
) {
148 if (!strcasecmp(parameter
, "enabled") || !strcasecmp(parameter
, "on")
149 || !strcasecmp(parameter
, "true") || !strcasecmp(parameter
, "yes")
150 || !strcasecmp(parameter
, "enable") || !strcmp(parameter
, "1"))
154 // We might run in compatibility mode on a system with a different ABI. The
155 // translators matching our ABI can usually be found in respective
156 // subdirectories of the translator directories.
158 if (get_system_info(&info
) == B_OK
159 && (info
.abi
& B_HAIKU_ABI_MAJOR
)
160 != (B_HAIKU_ABI
& B_HAIKU_ABI_MAJOR
)) {
161 switch (B_HAIKU_ABI
& B_HAIKU_ABI_MAJOR
) {
162 case B_HAIKU_ABI_GCC_2
:
163 fABISubDirectory
= "gcc2";
165 case B_HAIKU_ABI_GCC_4
:
166 fABISubDirectory
= "gcc4";
171 // we're sneaking us into the BApplication
172 if (be_app
!= NULL
&& be_app
->Lock()) {
173 be_app
->AddHandler(this);
179 BTranslatorRoster::Private::~Private()
183 if (Looper() && LockLooper()) {
184 BLooper
* looper
= Looper();
185 Looper()->RemoveHandler(this);
189 // Release all translators, so that they can delete themselves
191 TranslatorMap::iterator iterator
= fTranslators
.begin();
192 std::set
<image_id
> images
;
194 while (iterator
!= fTranslators
.end()) {
195 BTranslator
* translator
= iterator
->second
.translator
;
197 images
.insert(iterator
->second
.image
);
198 translator
->Release();
205 std::set
<image_id
>::const_iterator imageIterator
= images
.begin();
207 while (imageIterator
!= images
.end()) {
208 unload_add_on(*imageIterator
);
215 BTranslatorRoster::Private::MessageReceived(BMessage
* message
)
217 switch (message
->what
) {
220 BAutolock
locker(this);
222 printf("translator roster node monitor: ");
223 message
->PrintToStream();
226 if (message
->FindInt32("opcode", &opcode
) != B_OK
)
230 case B_ENTRY_CREATED
:
234 if (message
->FindInt32("device", &nodeRef
.device
) != B_OK
235 || message
->FindInt64("directory", &nodeRef
.node
)
237 || message
->FindString("name", &name
) != B_OK
)
240 // TODO: make this better (possible under Haiku)
242 // let the font be written completely before trying to
245 _EntryAdded(nodeRef
, name
);
251 // has the entry been moved into a monitored directory or
252 // has it been removed from one?
255 node_ref fromNodeRef
;
258 if (message
->FindInt32("device", &nodeRef
.device
) != B_OK
259 || message
->FindInt64("to directory", &toNodeRef
.node
)
261 || message
->FindInt64("from directory",
262 (int64
*)&fromNodeRef
.node
) != B_OK
263 || message
->FindInt64("node", (int64
*)&nodeRef
.node
)
265 || message
->FindString("name", &name
) != B_OK
)
268 fromNodeRef
.device
= nodeRef
.device
;
269 toNodeRef
.device
= nodeRef
.device
;
271 // Do we know this one yet?
272 translator_item
* item
= _FindTranslator(nodeRef
);
275 if (_IsKnownDirectory(toNodeRef
))
276 _EntryAdded(toNodeRef
, name
);
280 if (!_IsKnownDirectory(toNodeRef
)) {
281 // translator got removed
282 _RemoveTranslators(&nodeRef
);
286 // the name may have changed
287 item
->ref
.set_name(name
);
288 item
->ref
.directory
= toNodeRef
.node
;
290 if (_IsKnownDirectory(fromNodeRef
)
291 && _IsKnownDirectory(toNodeRef
)) {
292 // TODO: we should rescan for the name, there might be
293 // name clashes with translators in other directories
294 // (as well as old ones revealed)
300 case B_ENTRY_REMOVED
:
303 uint64 directoryNode
;
304 if (message
->FindInt32("device", &nodeRef
.device
) != B_OK
305 || message
->FindInt64("directory",
306 (int64
*)&directoryNode
) != B_OK
307 || message
->FindInt64("node", &nodeRef
.node
) != B_OK
)
310 translator_item
* item
= _FindTranslator(nodeRef
);
312 _RemoveTranslators(&nodeRef
);
319 case B_DELETE_TRANSLATOR
:
321 // A translator's refcount has been reduced to zero and it wants
325 if (message
->FindInt32("id", &id
) == B_OK
326 && message
->FindPointer("ptr", &self
) == B_OK
) {
327 _TranslatorDeleted(id
, (BTranslator
*)self
);
333 BHandler::MessageReceived(message
);
340 BTranslatorRoster::Private::AddDefaultPaths()
342 // add user directories first, so that they can override system translators
343 const directory_which paths
[] = {
344 B_USER_NONPACKAGED_ADDONS_DIRECTORY
,
345 B_USER_ADDONS_DIRECTORY
,
346 B_SYSTEM_NONPACKAGED_ADDONS_DIRECTORY
,
347 B_SYSTEM_ADDONS_DIRECTORY
,
350 for (uint32 i
= fSafeMode
? 4 : 0; i
< sizeof(paths
) / sizeof(paths
[0]);
353 status_t status
= find_directory(paths
[i
], &path
, true);
354 if (status
== B_OK
&& path
.Append("Translators") == B_OK
) {
355 mkdir(path
.Path(), 0755);
356 // make sure the directory exists before we add it
357 AddPath(path
.Path());
364 Adds the colon separated list of directories to the roster.
366 Note, the order in which these directories are added to actually matters,
367 translators with the same name will be taken from the earlier directory
368 first. See _CompareTranslatorDirectoryPriority().
371 BTranslatorRoster::Private::AddPaths(const char* paths
)
376 status_t status
= B_OK
;
379 while (paths
!= NULL
) {
380 const char* end
= strchr(paths
, ':');
384 path
.SetTo(paths
, end
- 1 - paths
);
391 // Keep the last error that occured, and return it
392 // but don't overwrite it, if the last path was
393 // added successfully.
395 status_t error
= AddPath(path
.String(), &count
);
396 if (error
!= B_NO_ERROR
)
410 Adds a new directory to the roster.
412 Note, the order in which these directories are added to actually matters,
416 BTranslatorRoster::Private::AddPath(const char* path
, int32
* _added
)
418 BDirectory
directory(path
);
419 status_t status
= directory
.InitCheck();
423 // if a subdirectory for our ABI exists, use that instead
424 if (fABISubDirectory
!= NULL
) {
425 BEntry
entry(&directory
, fABISubDirectory
);
426 if (entry
.IsDirectory()) {
427 status
= directory
.SetTo(&entry
);
434 status
= directory
.GetNodeRef(&nodeRef
);
438 // do we know this directory already?
439 if (_IsKnownDirectory(nodeRef
))
442 if (Looper() != NULL
) {
443 // watch that directory
444 watch_node(&nodeRef
, B_WATCH_DIRECTORY
, this);
445 fDirectories
.push_back(nodeRef
);
452 while (directory
.GetNextRef(&ref
) == B_OK
) {
454 if (entry
.IsDirectory())
456 if (CreateTranslators(ref
, count
) == B_OK
)
465 if (files
!= 0 && count
== 0)
473 BTranslatorRoster::Private::AddTranslator(BTranslator
* translator
,
474 image_id image
, const entry_ref
* ref
, ino_t node
)
476 BAutolock
locker(this);
478 translator_item item
;
479 item
.translator
= translator
;
486 fTranslators
[fNextID
] = item
;
491 translator
->fOwningRoster
= this;
492 translator
->fID
= fNextID
++;
498 BTranslatorRoster::Private::RemoveTranslators(entry_ref
& ref
)
500 _RemoveTranslators(NULL
, &ref
);
505 BTranslatorRoster::Private::FindTranslator(translator_id id
)
508 debugger("translator must be locked!");
512 const translator_item
* item
= _FindTranslator(id
);
514 return item
->translator
;
521 BTranslatorRoster::Private::GetTranslatorData(image_id image
,
522 translator_data
& data
)
524 // If this is a translator add-on, it is in the C format
525 memset(&data
, 0, sizeof(translator_data
));
527 // find all the symbols
530 if (get_image_symbol(image
, "translatorName", B_SYMBOL_TYPE_DATA
,
531 (void**)&data
.name
) < B_OK
532 || get_image_symbol(image
, "translatorInfo", B_SYMBOL_TYPE_DATA
,
533 (void**)&data
.info
) < B_OK
534 || get_image_symbol(image
, "translatorVersion", B_SYMBOL_TYPE_DATA
,
535 (void**)&version
) < B_OK
|| version
== NULL
536 || get_image_symbol(image
, "inputFormats", B_SYMBOL_TYPE_DATA
,
537 (void**)&data
.input_formats
) < B_OK
538 || get_image_symbol(image
, "outputFormats", B_SYMBOL_TYPE_DATA
,
539 (void**)&data
.output_formats
) < B_OK
540 || get_image_symbol(image
, "Identify", B_SYMBOL_TYPE_TEXT
,
541 (void**)&data
.identify_hook
) < B_OK
542 || get_image_symbol(image
, "Translate", B_SYMBOL_TYPE_TEXT
,
543 (void**)&data
.translate_hook
) < B_OK
) {
547 data
.version
= *version
;
549 // those calls are optional
550 get_image_symbol(image
, "MakeConfig", B_SYMBOL_TYPE_TEXT
,
551 (void**)&data
.make_config_hook
);
552 get_image_symbol(image
, "GetConfigMessage", B_SYMBOL_TYPE_TEXT
,
553 (void**)&data
.get_config_message_hook
);
560 BTranslatorRoster::Private::CreateTranslators(const entry_ref
& ref
,
561 int32
& count
, BMessage
* update
)
563 BAutolock
locker(this);
565 BPrivate::QuarantineTranslatorImage
quarantine(*this);
567 const translator_item
* item
= _FindTranslator(ref
.name
);
569 // check if the known translator has a higher priority
570 if (_CompareTranslatorDirectoryPriority(item
->ref
, ref
) <= 0) {
571 // keep the existing add-on
575 // replace existing translator(s) if the new translator succeeds
576 quarantine
.Put(item
->ref
);
581 status_t status
= entry
.GetNodeRef(&nodeRef
);
586 image_id image
= load_add_on(path
.Path());
590 // Function pointer used to create post R4.5 style translators
591 BTranslator
*(*makeNthTranslator
)(int32 n
, image_id you
, uint32 flags
, ...);
593 status
= get_image_symbol(image
, "make_nth_translator",
594 B_SYMBOL_TYPE_TEXT
, (void**)&makeNthTranslator
);
595 if (status
== B_OK
) {
596 // If the translator add-on supports the post R4.5
597 // translator creation mechanism, keep loading translators
598 // until MakeNthTranslator stops returning them.
599 BTranslator
* translator
= NULL
;
601 for (int32 n
= 0; (translator
= makeNthTranslator(n
, image
, 0)) != NULL
;
603 if (AddTranslator(translator
, image
, &ref
, nodeRef
.node
) == B_OK
) {
605 update
->AddInt32("translator_id", translator
->fID
);
606 fImageOrigins
.insert(std::make_pair(translator
, image
));
610 translator
->Release();
611 // this will delete the translator
616 unload_add_on(image
);
618 // Initial refcount for the image that was just loaded
619 fKnownImages
.insert(std::make_pair(image
, created
));
626 // If this is a translator add-on, it is in the C format
627 translator_data translatorData
;
628 status
= GetTranslatorData(image
, translatorData
);
630 // add this translator to the list
631 BPrivate::BFuncTranslator
* translator
= NULL
;
632 if (status
== B_OK
) {
633 translator
= new (std::nothrow
) BPrivate::BFuncTranslator(
635 if (translator
== NULL
)
636 status
= B_NO_MEMORY
;
640 status
= AddTranslator(translator
, image
, &ref
, nodeRef
.node
);
642 if (status
== B_OK
) {
644 update
->AddInt32("translator_id", translator
->fID
);
648 unload_add_on(image
);
655 BTranslatorRoster::Private::StartWatching(BMessenger target
)
658 fMessengers
.push_back(target
);
664 fLazyScanning
= false;
665 // Since we now have someone to report to, we cannot lazily
666 // adopt changes to the translator any longer
676 BTranslatorRoster::Private::StopWatching(BMessenger target
)
678 MessengerList::iterator iterator
= fMessengers
.begin();
680 while (iterator
!= fMessengers
.end()) {
681 if (*iterator
== target
) {
682 fMessengers
.erase(iterator
);
683 if (fMessengers
.empty())
684 fLazyScanning
= true;
697 BTranslatorRoster::Private::StoreTranslators(BMessage
& archive
)
699 BAutolock
locker(this);
701 TranslatorMap::const_iterator iterator
= fTranslators
.begin();
703 while (iterator
!= fTranslators
.end()) {
704 const translator_item
& item
= iterator
->second
;
705 BPath
path(&item
.ref
);
706 if (path
.InitCheck() == B_OK
)
707 archive
.AddString("be:translator_path", path
.Path());
717 BTranslatorRoster::Private::Identify(BPositionIO
* source
,
718 BMessage
* ioExtension
, uint32 hintType
, const char* hintMIME
,
719 uint32 wantType
, translator_info
* _info
)
721 BAutolock
locker(this);
725 TranslatorMap::const_iterator iterator
= fTranslators
.begin();
726 BMessage baseExtension
;
727 if (ioExtension
!= NULL
)
728 baseExtension
= *ioExtension
;
730 float bestWeight
= 0.0f
;
732 while (iterator
!= fTranslators
.end()) {
733 BTranslator
& translator
= *iterator
->second
.translator
;
735 off_t pos
= source
->Seek(0, SEEK_SET
);
737 return pos
< 0 ? (status_t
)pos
: B_IO_ERROR
;
739 int32 formatsCount
= 0;
740 const translation_format
* formats
= translator
.InputFormats(
742 const translation_format
* format
= _CheckHints(formats
, formatsCount
,
745 BMessage
extension(baseExtension
);
746 translator_info info
;
747 if (translator
.Identify(source
, format
, &extension
, &info
, wantType
)
749 float weight
= info
.quality
* info
.capability
;
750 if (weight
> bestWeight
) {
751 if (ioExtension
!= NULL
)
752 *ioExtension
= extension
;
755 info
.translator
= iterator
->first
;
756 memcpy(_info
, &info
, sizeof(translator_info
));
763 if (bestWeight
> 0.0f
)
766 return B_NO_TRANSLATOR
;
771 BTranslatorRoster::Private::GetTranslators(BPositionIO
* source
,
772 BMessage
* ioExtension
, uint32 hintType
, const char* hintMIME
,
773 uint32 wantType
, translator_info
** _info
, int32
* _numInfo
)
775 BAutolock
locker(this);
779 int32 arraySize
= fTranslators
.size();
780 translator_info
* array
= new (std::nothrow
) translator_info
[arraySize
];
784 TranslatorMap::const_iterator iterator
= fTranslators
.begin();
787 while (iterator
!= fTranslators
.end()) {
788 BTranslator
& translator
= *iterator
->second
.translator
;
790 off_t pos
= source
->Seek(0, SEEK_SET
);
793 return pos
< 0 ? status_t(pos
) : B_IO_ERROR
;
796 int32 formatsCount
= 0;
797 const translation_format
* formats
= translator
.InputFormats(
799 const translation_format
* format
= _CheckHints(formats
, formatsCount
,
802 translator_info info
;
803 if (translator
.Identify(source
, format
, ioExtension
, &info
, wantType
)
805 info
.translator
= iterator
->first
;
806 array
[count
++] = info
;
814 qsort(array
, count
, sizeof(translator_info
),
815 BTranslatorRoster::Private::_CompareSupport
);
816 // translators are sorted by best support
823 BTranslatorRoster::Private::GetAllTranslators(translator_id
** _ids
,
826 BAutolock
locker(this);
830 int32 arraySize
= fTranslators
.size();
831 translator_id
* array
= new (std::nothrow
) translator_id
[arraySize
];
835 TranslatorMap::const_iterator iterator
= fTranslators
.begin();
838 while (iterator
!= fTranslators
.end()) {
839 array
[count
++] = iterator
->first
;
850 BTranslatorRoster::Private::GetRefFor(translator_id id
, entry_ref
& ref
)
852 BAutolock
locker(this);
854 const translator_item
* item
= _FindTranslator(id
);
856 return B_NO_TRANSLATOR
;
858 BEntry
entry(&item
->ref
);
859 if (entry
.InitCheck() == B_OK
&& entry
.Exists() && entry
.IsFile()) {
869 BTranslatorRoster::Private::_TranslatorDeleted(translator_id id
, BTranslator
* self
)
871 BAutolock
locker(this);
873 TranslatorMap::iterator iterator
= fTranslators
.find(id
);
874 if (iterator
!= fTranslators
.end())
875 fTranslators
.erase(iterator
);
877 image_id image
= fImageOrigins
[self
];
881 int32 former
= atomic_add(&fKnownImages
[image
], -1);
883 unload_add_on(image
);
884 fImageOrigins
.erase(self
);
885 fKnownImages
.erase(image
);
891 BTranslatorRoster::Private::_CompareSupport(const void* _a
, const void* _b
)
893 const translator_info
* infoA
= (const translator_info
*)_a
;
894 const translator_info
* infoB
= (const translator_info
*)_b
;
896 float weightA
= infoA
->quality
* infoA
->capability
;
897 float weightB
= infoB
->quality
* infoB
->capability
;
899 if (weightA
== weightB
)
901 if (weightA
> weightB
)
909 In lazy mode, freshly installed translator are not scanned immediately
910 when they become available. Instead, they are put into a set.
912 When a method is called that may be interested in these new translators,
913 they are scanned on the fly. Since lazy mode also means that this roster
914 does not have any listeners, we don't need to notify anyone about those
918 BTranslatorRoster::Private::_RescanChanged()
920 while (!fRescanEntries
.empty()) {
921 EntryRefSet::iterator iterator
= fRescanEntries
.begin();
923 CreateTranslators(*iterator
, count
);
925 fRescanEntries
.erase(iterator
);
931 Tests if the hints provided for a source stream are compatible to
932 the formats the translator exports.
934 const translation_format
*
935 BTranslatorRoster::Private::_CheckHints(const translation_format
* formats
,
936 int32 formatsCount
, uint32 hintType
, const char* hintMIME
)
938 if (formats
== NULL
|| formatsCount
<= 0 || (!hintType
&& hintMIME
== NULL
))
941 // The provided MIME type hint may be a super type
943 if (hintMIME
&& !strchr(hintMIME
, '/'))
944 super
= strlen(hintMIME
);
946 // scan for suitable format
947 for (int32 i
= 0; i
< formatsCount
&& formats
[i
].type
; i
++) {
948 if (formats
[i
].type
== hintType
950 && ((super
&& !strncmp(formats
[i
].MIME
, hintMIME
, super
))
951 || !strcmp(formats
[i
].MIME
, hintMIME
))))
959 const translator_item
*
960 BTranslatorRoster::Private::_FindTranslator(translator_id id
) const
962 TranslatorMap::const_iterator iterator
= fTranslators
.find(id
);
963 if (iterator
== fTranslators
.end())
966 return &iterator
->second
;
970 const translator_item
*
971 BTranslatorRoster::Private::_FindTranslator(const char* name
) const
976 TranslatorMap::const_iterator iterator
= fTranslators
.begin();
978 while (iterator
!= fTranslators
.end()) {
979 const translator_item
& item
= iterator
->second
;
980 if (item
.ref
.name
!= NULL
&& !strcmp(item
.ref
.name
, name
))
990 const translator_item
*
991 BTranslatorRoster::Private::_FindTranslator(entry_ref
& ref
) const
993 if (ref
.name
== NULL
)
996 TranslatorMap::const_iterator iterator
= fTranslators
.begin();
998 while (iterator
!= fTranslators
.end()) {
999 const translator_item
& item
= iterator
->second
;
1000 if (item
.ref
== ref
)
1011 BTranslatorRoster::Private::_FindTranslator(node_ref
& nodeRef
)
1013 if (nodeRef
.device
< 0)
1016 TranslatorMap::iterator iterator
= fTranslators
.begin();
1018 while (iterator
!= fTranslators
.end()) {
1019 translator_item
& item
= iterator
->second
;
1020 if (item
.ref
.device
== nodeRef
.device
1021 && item
.node
== nodeRef
.node
)
1032 Directories added to the roster have a certain priority - the first entry
1033 to be added has the highest priority; if a translator with the same name
1034 is to be found in two directories, the one with the higher priority is
1038 BTranslatorRoster::Private::_CompareTranslatorDirectoryPriority(
1039 const entry_ref
& a
, const entry_ref
& b
) const
1041 // priority is determined by the order in the list
1044 nodeRefA
.device
= a
.device
;
1045 nodeRefA
.node
= a
.directory
;
1048 nodeRefB
.device
= b
.device
;
1049 nodeRefB
.node
= b
.directory
;
1051 NodeRefList::const_iterator iterator
= fDirectories
.begin();
1053 while (iterator
!= fDirectories
.end()) {
1054 if (*iterator
== nodeRefA
)
1056 if (*iterator
== nodeRefB
)
1067 BTranslatorRoster::Private::_IsKnownDirectory(const node_ref
& nodeRef
) const
1069 NodeRefList::const_iterator iterator
= fDirectories
.begin();
1071 while (iterator
!= fDirectories
.end()) {
1072 if (*iterator
== nodeRef
)
1083 BTranslatorRoster::Private::_RemoveTranslators(const node_ref
* nodeRef
,
1084 const entry_ref
* ref
)
1086 if (ref
== NULL
&& nodeRef
== NULL
)
1089 TranslatorMap::iterator iterator
= fTranslators
.begin();
1090 BMessage
update(B_TRANSLATOR_REMOVED
);
1091 image_id image
= -1;
1093 while (iterator
!= fTranslators
.end()) {
1094 TranslatorMap::iterator next
= iterator
;
1097 const translator_item
& item
= iterator
->second
;
1098 if ((ref
!= NULL
&& item
.ref
== *ref
)
1099 || (nodeRef
!= NULL
&& item
.ref
.device
== nodeRef
->device
1100 && item
.node
== nodeRef
->node
)) {
1101 item
.translator
->Release();
1103 update
.AddInt32("translator_id", iterator
->first
);
1105 fTranslators
.erase(iterator
);
1111 _NotifyListeners(update
);
1116 BTranslatorRoster::Private::_EntryAdded(const node_ref
& nodeRef
,
1120 ref
.device
= nodeRef
.device
;
1121 ref
.directory
= nodeRef
.node
;
1129 In lazy mode, the entry is marked to be rescanned on next use of any
1130 translation method (that could make use of it).
1131 In non-lazy mode, the translators for this entry are created directly
1132 and listeners notified.
1134 Called by the node monitor handling.
1137 BTranslatorRoster::Private::_EntryAdded(const entry_ref
& ref
)
1140 if (entry
.SetTo(&ref
) != B_OK
|| !entry
.IsFile())
1143 if (fLazyScanning
) {
1144 fRescanEntries
.insert(ref
);
1148 BMessage
update(B_TRANSLATOR_ADDED
);
1150 CreateTranslators(ref
, count
, &update
);
1152 _NotifyListeners(update
);
1157 BTranslatorRoster::Private::_NotifyListeners(BMessage
& update
) const
1159 MessengerList::const_iterator iterator
= fMessengers
.begin();
1161 while (iterator
!= fMessengers
.end()) {
1162 (*iterator
).SendMessage(&update
);
1171 BTranslatorReleaseDelegate::BTranslatorReleaseDelegate(BTranslator
* translator
)
1173 fUnderlying(translator
)
1179 BTranslatorReleaseDelegate::Release()
1181 fUnderlying
->Release();
1182 // ReleaseDelegate is only allowed to release a translator once.
1190 BTranslatorRoster::BTranslatorRoster()
1196 BTranslatorRoster::BTranslatorRoster(BMessage
* model
)
1203 model
->FindString("be:translator_path", i
, &path
) == B_OK
; i
++) {
1206 if (entry
.GetRef(&ref
) == B_OK
) {
1208 fPrivate
->CreateTranslators(ref
, count
);
1215 BTranslatorRoster::~BTranslatorRoster()
1217 // If the default BTranslatorRoster is being
1218 // deleted, set the pointer to the default
1219 // BTranslatorRoster to NULL
1220 if (sDefaultRoster
== this)
1221 sDefaultRoster
= NULL
;
1228 BTranslatorRoster::_Initialize()
1230 fPrivate
= new BTranslatorRoster::Private();
1235 BTranslatorRoster::Archive(BMessage
* into
, bool deep
) const
1237 status_t status
= BArchivable::Archive(into
, deep
);
1241 return fPrivate
->StoreTranslators(*into
);
1246 BTranslatorRoster::Instantiate(BMessage
* from
)
1248 if (!from
|| !validate_instantiation(from
, "BTranslatorRoster"))
1251 return new BTranslatorRoster(from
);
1256 BTranslatorRoster::Default()
1258 static int32 lock
= 0;
1260 if (sDefaultRoster
!= NULL
)
1261 return sDefaultRoster
;
1263 if (atomic_add(&lock
, 1) != 0) {
1264 // Just wait for the default translator to be instantiated
1265 while (sDefaultRoster
== NULL
)
1268 atomic_add(&lock
, -1);
1269 return sDefaultRoster
;
1272 // If the default translators have not been loaded,
1273 // create a new BTranslatorRoster for them, and load them.
1274 if (sDefaultRoster
== NULL
) {
1275 BTranslatorRoster
* roster
= new BTranslatorRoster();
1276 roster
->AddTranslators(NULL
);
1278 sDefaultRoster
= roster
;
1279 // this will unlock any other threads waiting for
1280 // the default roster to become available
1283 atomic_add(&lock
, -1);
1284 return sDefaultRoster
;
1289 This function takes a string of colon delimited paths, and adds
1290 the translators from those paths to this BTranslatorRoster.
1292 If load_path is NULL, it parses the environment variable
1293 TRANSLATORS. If that does not exist, it uses the system paths:
1294 /boot/home/config/add-ons/Translators,
1295 /system/add-ons/Translators.
1298 BTranslatorRoster::AddTranslators(const char* path
)
1301 path
= getenv("TRANSLATORS");
1303 fPrivate
->AddDefaultPaths();
1307 return fPrivate
->AddPaths(path
);
1312 Adds a BTranslator based object to the BTranslatorRoster.
1313 When you add a BTranslator roster, it is Acquire()'d by
1314 BTranslatorRoster; it is Release()'d when the
1315 BTranslatorRoster is deleted.
1317 \param translator the translator to be added to the
1320 \return B_BAD_VALUE, if translator is NULL,
1321 B_OK if all went well
1324 BTranslatorRoster::AddTranslator(BTranslator
* translator
)
1329 return fPrivate
->AddTranslator(translator
);
1334 BTranslatorRoster::IsTranslator(entry_ref
* ref
)
1340 image_id image
= load_add_on(path
.Path());
1344 // Function pointer used to create post R4.5 style translators
1345 BTranslator
* (*makeNthTranslator
)(int32 n
, image_id you
, uint32 flags
, ...);
1347 status_t status
= get_image_symbol(image
, "make_nth_translator",
1348 B_SYMBOL_TYPE_TEXT
, (void**)&makeNthTranslator
);
1349 if (status
< B_OK
) {
1350 // If this is a translator add-on, it is in the C format
1351 translator_data translatorData
;
1352 status
= fPrivate
->GetTranslatorData(image
, translatorData
);
1355 unload_add_on(image
);
1356 return status
== B_OK
;
1361 This function determines which translator is best suited
1362 to convert the data from \a source.
1364 \param source the data to be identified
1365 \param ioExtension the configuration data for the translator
1366 \param _info the information about the chosen translator is put here
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, identification of \a source was successful,
1374 B_NO_TRANSLATOR, no appropriate translator found,
1375 and other errors from accessing the source stream
1378 BTranslatorRoster::Identify(BPositionIO
* source
, BMessage
* ioExtension
,
1379 translator_info
* _info
, uint32 hintType
, const char* hintMIME
,
1382 if (source
== NULL
|| _info
== NULL
)
1385 return fPrivate
->Identify(source
, ioExtension
, hintType
, hintMIME
, wantType
,
1391 Finds all translators capable of handling the data in \a source
1392 and puts them into the outInfo array (which you must delete
1393 yourself when you are done with it). Specifying a value for
1394 \a hintType, \a hintMIME and/or \a wantType causes only the
1395 translators that satisfy them to be included in the outInfo.
1397 \param source the data to be translated
1398 \param ioExtension the configuration data for the translator
1399 \param _info, the array of acceptable translators is stored here if
1400 the function succeeds. It's the caller's responsibility to free
1401 the array using delete[].
1402 \param _numInfo, number of entries in the \a _info array
1403 \param hintType a hint about the type of data that is in \a source, set
1404 it to zero if the type is not known
1405 \param hintMIME a hint about the MIME type of \a source, set it to NULL
1406 if the type is not known.
1407 \param wantType the desired output type - if zero, any type is okay.
1409 \return B_OK, successfully indentified the data in \a source
1410 B_NO_TRANSLATOR, no translator could handle \a source
1411 other errors, problems using \a source
1414 BTranslatorRoster::GetTranslators(BPositionIO
* source
, BMessage
* ioExtension
,
1415 translator_info
** _info
, int32
* _numInfo
, uint32 hintType
,
1416 const char* hintMIME
, uint32 wantType
)
1418 if (source
== NULL
|| _info
== NULL
|| _numInfo
== NULL
)
1421 return fPrivate
->GetTranslators(source
, ioExtension
, hintType
, hintMIME
,
1422 wantType
, _info
, _numInfo
);
1427 Returns an array in \a _ids of all of the translators stored by this
1429 You must free the array using delete[] when you are done with it.
1431 \param _ids the array is stored there (you own the array).
1432 \param _count number of IDs in the array.
1435 BTranslatorRoster::GetAllTranslators(translator_id
** _ids
, int32
* _count
)
1437 if (_ids
== NULL
|| _count
== NULL
)
1440 return fPrivate
->GetAllTranslators(_ids
, _count
);
1445 Returns information about the translator with the specified
1447 You must not free any of the data you get back.
1449 \param id identifies which translator you want info for
1450 \param _name the translator name is put here
1451 \param _info the translator description is put here
1452 \param _version the translation version is put here
1454 \return B_OK if successful,
1455 B_BAD_VALUE, if all parameters are NULL
1456 B_NO_TRANSLATOR, \id didn't identify an existing translator
1459 BTranslatorRoster::GetTranslatorInfo(translator_id id
, const char** _name
,
1460 const char** _info
, int32
* _version
)
1462 if (_name
== NULL
&& _info
== NULL
&& _version
== NULL
)
1465 BAutolock
locker(fPrivate
);
1467 BTranslator
* translator
= fPrivate
->FindTranslator(id
);
1468 if (translator
== NULL
)
1469 return B_NO_TRANSLATOR
;
1472 *_name
= translator
->TranslatorName();
1474 *_info
= translator
->TranslatorInfo();
1476 *_version
= translator
->TranslatorVersion();
1483 Returns all of the input formats for the translator specified
1485 You must not free any of the data you get back.
1487 \param id identifies which translator you want the input formats for
1488 \param _formats array of input formats
1489 \param _numFormats number of formats in the array
1491 \return B_OK if successful,
1492 B_BAD_VALUE, if any parameter is NULL
1493 B_NO_TRANSLATOR, \id didn't identify an existing translator
1496 BTranslatorRoster::GetInputFormats(translator_id id
,
1497 const translation_format
** _formats
, int32
* _numFormats
)
1499 if (_formats
== NULL
|| _numFormats
== NULL
)
1502 BAutolock
locker(fPrivate
);
1504 BTranslator
* translator
= fPrivate
->FindTranslator(id
);
1505 if (translator
== NULL
)
1506 return B_NO_TRANSLATOR
;
1508 *_formats
= translator
->InputFormats(_numFormats
);
1514 Returns all of the output formats for the translator specified
1516 You must not free any of the data you get back.
1518 \param id identifies which translator you want the output formats for
1519 \param _formats array of output formats
1520 \param _numFormats number of formats in the array
1522 \return B_OK if successful,
1523 B_BAD_VALUE, if any parameter is NULL
1524 B_NO_TRANSLATOR, \id didn't identify an existing translator
1527 BTranslatorRoster::GetOutputFormats(translator_id id
,
1528 const translation_format
** _formats
, int32
* _numFormats
)
1530 if (_formats
== NULL
|| _numFormats
== NULL
)
1533 BAutolock
locker(fPrivate
);
1535 BTranslator
* translator
= fPrivate
->FindTranslator(id
);
1536 if (translator
== NULL
)
1537 return B_NO_TRANSLATOR
;
1539 *_formats
= translator
->OutputFormats(_numFormats
);
1545 This function is the whole point of the Translation Kit.
1546 This is for translating the data in \a source to \a destination
1547 using the format \a wantOutType.
1549 \param source the data to be translated
1550 \param ioExtension the configuration data for the translator
1551 \param info information about translator to use (can be NULL, in which
1552 case the \a source is identified first)
1553 \param destination where \a source is translated to
1554 \param hintType a hint about the type of data that is in \a source, set
1555 it to zero if the type is not known
1556 \param hintMIME a hint about the MIME type of \a source, set it to NULL
1557 if the type is not known.
1558 \param wantType the desired output type - if zero, any type is okay.
1560 \return B_OK, translation of \a source was successful,
1561 B_NO_TRANSLATOR, no appropriate translator found,
1562 and other errors from accessing the source and destination streams
1565 BTranslatorRoster::Translate(BPositionIO
* source
, const translator_info
* info
,
1566 BMessage
* ioExtension
, BPositionIO
* destination
, uint32 wantOutType
,
1567 uint32 hintType
, const char* hintMIME
)
1569 if (source
== NULL
|| destination
== NULL
)
1572 translator_info infoBuffer
;
1575 // look for a suitable translator
1576 status_t status
= fPrivate
->Identify(source
, ioExtension
, hintType
,
1577 hintMIME
, wantOutType
, &infoBuffer
);
1584 if (!fPrivate
->Lock())
1587 BTranslator
* translator
= fPrivate
->FindTranslator(info
->translator
);
1588 if (translator
!= NULL
) {
1589 translator
->Acquire();
1590 // make sure this translator is not removed while we're playing with
1591 // it; translating shouldn't be serialized!
1596 if (translator
== NULL
)
1597 return B_NO_TRANSLATOR
;
1599 status_t status
= B_OK
;
1600 off_t pos
= source
->Seek(0, SEEK_SET
);
1602 status
= pos
< 0 ? (status_t
)pos
: B_IO_ERROR
;
1603 if (status
== B_OK
) {
1604 status
= translator
->Translate(source
, info
, ioExtension
, wantOutType
,
1607 translator
->Release();
1614 This function is the whole point of the Translation Kit.
1615 This is for translating the data in \a source to \a destination
1616 using the format \a wantOutType and the translator identified
1619 \param id the translator to be used
1620 \param source the data to be translated
1621 \param ioExtension the configuration data for the translator
1622 \param destination where \a source is translated to
1623 \param wantType the desired output type - if zero, any type is okay.
1625 \return B_OK, translation of \a source was successful,
1626 B_NO_TRANSLATOR, no appropriate translator found,
1627 and other errors from accessing the source and destination streams
1630 BTranslatorRoster::Translate(translator_id id
, BPositionIO
* source
,
1631 BMessage
* ioExtension
, BPositionIO
* destination
, uint32 wantOutType
)
1633 if (source
== NULL
|| destination
== NULL
)
1636 if (!fPrivate
->Lock())
1639 BTranslator
* translator
= fPrivate
->FindTranslator(id
);
1640 if (translator
!= NULL
) {
1641 translator
->Acquire();
1642 // make sure this translator is not removed while we're playing with
1643 // it; translating shouldn't be serialized!
1648 if (translator
== NULL
)
1649 return B_NO_TRANSLATOR
;
1652 off_t pos
= source
->Seek(0, SEEK_SET
);
1654 translator_info info
;
1655 status
= translator
->Identify(source
, NULL
, ioExtension
, &info
,
1657 if (status
>= B_OK
) {
1658 off_t pos
= source
->Seek(0, SEEK_SET
);
1660 status
= pos
< 0 ? (status_t
)pos
: B_IO_ERROR
;
1662 status
= translator
->Translate(source
, &info
, ioExtension
,
1663 wantOutType
, destination
);
1667 status
= pos
< 0 ? (status_t
)pos
: B_IO_ERROR
;
1668 translator
->Release();
1675 Creates a BView in \a _view for configuring the translator specified
1676 by \a id. Not all translators support this, though.
1678 \param id identifies which translator you want the input formats for
1679 \param ioExtension the configuration data for the translator
1680 \param _view the view for configuring the translator
1681 \param _extent the bounds for the (resizable) view
1683 \return B_OK if successful,
1684 B_BAD_VALUE, if any parameter is NULL
1685 B_NO_TRANSLATOR, \id didn't identify an existing translator
1688 BTranslatorRoster::MakeConfigurationView(translator_id id
,
1689 BMessage
* ioExtension
, BView
** _view
, BRect
* _extent
)
1691 if (_view
== NULL
|| _extent
== NULL
)
1694 BAutolock
locker(fPrivate
);
1696 BTranslator
* translator
= fPrivate
->FindTranslator(id
);
1697 if (translator
== NULL
)
1698 return B_NO_TRANSLATOR
;
1700 return translator
->MakeConfigurationView(ioExtension
, _view
, _extent
);
1704 BTranslatorReleaseDelegate
*
1705 BTranslatorRoster::AcquireTranslator(int32 id
)
1707 BAutolock
locker(fPrivate
);
1709 BTranslator
* translator
= fPrivate
->FindTranslator(id
);
1710 if (translator
== NULL
)
1713 translator
->Acquire();
1714 return new BTranslatorReleaseDelegate(translator
);
1719 Gets the configuration setttings for the translator
1720 specified by \a id and puts them into \a ioExtension.
1722 \param id identifies which translator you want the input formats for
1723 \param ioExtension the configuration data for the translator
1725 \return B_OK if successful,
1726 B_BAD_VALUE, if \a ioExtension is NULL
1727 B_NO_TRANSLATOR, \id didn't identify an existing translator
1730 BTranslatorRoster::GetConfigurationMessage(translator_id id
,
1731 BMessage
* ioExtension
)
1736 BAutolock
locker(fPrivate
);
1738 BTranslator
* translator
= fPrivate
->FindTranslator(id
);
1739 if (translator
== NULL
)
1740 return B_NO_TRANSLATOR
;
1742 return translator
->GetConfigurationMessage(ioExtension
);
1747 Gets the entry_ref for the given translator (of course, this works only
1748 for disk based translators).
1750 \param id identifies which translator you want the input formats for
1751 \param ref the entry ref is stored there
1753 \return B_OK if successful,
1754 B_ERROR, if this is not a disk based translator
1755 B_BAD_VALUE, if \a ref is NULL
1756 B_NO_TRANSLATOR, \id didn't identify an existing translator
1759 BTranslatorRoster::GetRefFor(translator_id id
, entry_ref
* ref
)
1764 return fPrivate
->GetRefFor(id
, *ref
);
1769 BTranslatorRoster::StartWatching(BMessenger target
)
1771 return fPrivate
->StartWatching(target
);
1776 BTranslatorRoster::StopWatching(BMessenger target
)
1778 return fPrivate
->StopWatching(target
);
1782 // #pragma mark - private
1785 BTranslatorRoster::BTranslatorRoster(const BTranslatorRoster
&other
)
1791 BTranslatorRoster::operator=(const BTranslatorRoster
&tr
)
1797 #if __GNUC__ == 2 // gcc 2
1799 /*static*/ const char*
1800 BTranslatorRoster::Version(int32
* outCurVersion
, int32
* outMinVersion
,
1803 if (!outCurVersion
|| !outMinVersion
)
1806 static char vString
[50];
1807 static char vDate
[] = __DATE__
;
1809 sprintf(vString
, "Translation Kit v%d.%d.%d %s\n",
1810 int(B_TRANSLATION_MAJOR_VERSION(B_TRANSLATION_CURRENT_VERSION
)),
1811 int(B_TRANSLATION_MINOR_VERSION(B_TRANSLATION_CURRENT_VERSION
)),
1812 int(B_TRANSLATION_REVISION_VERSION(B_TRANSLATION_CURRENT_VERSION
)),
1815 *outCurVersion
= B_TRANSLATION_CURRENT_VERSION
;
1816 *outMinVersion
= B_TRANSLATION_MIN_VERSION
;
1823 void BTranslatorRoster::ReservedTranslatorRoster1() {}
1824 void BTranslatorRoster::ReservedTranslatorRoster2() {}
1825 void BTranslatorRoster::ReservedTranslatorRoster3() {}
1826 void BTranslatorRoster::ReservedTranslatorRoster4() {}
1827 void BTranslatorRoster::ReservedTranslatorRoster5() {}
1828 void BTranslatorRoster::ReservedTranslatorRoster6() {}