tcp: Fix 64 bit build with debugging features enabled.
[haiku.git] / src / kits / translation / TranslatorRoster.cpp
bloba0b54af4afc866c2bcddb966b46592316a0068ca
1 /*
2 * Copyright 2002-2009, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Michael Wilber
7 * Axel Dörfler, axeld@pinc-software.de
8 */
10 /*!
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>
18 #include <new>
19 #include <strings.h>
20 #include <stdio.h>
21 #include <stdlib.h>
23 #include <Application.h>
24 #include <Autolock.h>
25 #include <Directory.h>
26 #include <FindDirectory.h>
27 #include <NodeMonitor.h>
28 #include <Path.h>
29 #include <String.h>
31 #include <driver_settings.h>
32 #include <image.h>
33 #include <safemode_defs.h>
34 #include <syscalls.h>
36 #include "FuncTranslator.h"
37 #include "TranslatorRosterPrivate.h"
40 namespace BPrivate {
42 class QuarantineTranslatorImage {
43 public:
44 QuarantineTranslatorImage(
45 BTranslatorRoster::Private& privateRoster);
46 ~QuarantineTranslatorImage();
48 void Put(const entry_ref& ref);
49 void Remove();
51 private:
52 BTranslatorRoster::Private& fRoster;
53 entry_ref fRef;
54 bool fRemove;
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;
76 namespace BPrivate {
78 /*!
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
81 before).
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
86 hide this entry).
88 QuarantineTranslatorImage::QuarantineTranslatorImage(
89 BTranslatorRoster::Private& privateRoster)
91 fRoster(privateRoster),
92 fRemove(false)
97 QuarantineTranslatorImage::~QuarantineTranslatorImage()
99 if (fRef.device == -1 || !fRemove)
100 return;
102 fRoster.RemoveTranslators(fRef);
106 void
107 QuarantineTranslatorImage::Put(const entry_ref& ref)
109 fRef = ref;
113 void
114 QuarantineTranslatorImage::Remove()
116 fRemove = true;
119 } // namespace BPrivate
122 // #pragma mark -
125 BTranslatorRoster::Private::Private()
127 BHandler("translator roster"),
128 BLocker("translator list"),
129 fABISubDirectory(NULL),
130 fNextID(1),
131 fLazyScanning(true),
132 fSafeMode(false)
134 char parameter[32];
135 size_t parameterLength = sizeof(parameter);
137 if (_kern_get_safemode_option(B_SAFEMODE_SAFE_MODE, parameter,
138 &parameterLength) == B_OK) {
139 if (!strcasecmp(parameter, "enabled") || !strcasecmp(parameter, "on")
140 || !strcasecmp(parameter, "true") || !strcasecmp(parameter, "yes")
141 || !strcasecmp(parameter, "enable") || !strcmp(parameter, "1"))
142 fSafeMode = true;
145 if (_kern_get_safemode_option(B_SAFEMODE_DISABLE_USER_ADD_ONS, parameter,
146 &parameterLength) == B_OK) {
147 if (!strcasecmp(parameter, "enabled") || !strcasecmp(parameter, "on")
148 || !strcasecmp(parameter, "true") || !strcasecmp(parameter, "yes")
149 || !strcasecmp(parameter, "enable") || !strcmp(parameter, "1"))
150 fSafeMode = true;
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.
156 system_info info;
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";
163 break;
164 case B_HAIKU_ABI_GCC_4:
165 fABISubDirectory = "gcc4";
166 break;
170 // we're sneaking us into the BApplication
171 if (be_app != NULL && be_app->Lock()) {
172 be_app->AddHandler(this);
173 be_app->Unlock();
178 BTranslatorRoster::Private::~Private()
180 stop_watching(this);
182 if (Looper() && LockLooper()) {
183 BLooper* looper = Looper();
184 Looper()->RemoveHandler(this);
185 looper->Unlock();
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();
202 iterator++;
205 // Unload all images
207 std::set<image_id>::const_iterator imageIterator = images.begin();
209 while (imageIterator != images.end()) {
210 unload_add_on(*imageIterator);
211 imageIterator++;
216 void
217 BTranslatorRoster::Private::MessageReceived(BMessage* message)
219 switch (message->what) {
220 case B_NODE_MONITOR:
222 BAutolock locker(this);
224 printf("translator roster node monitor: ");
225 message->PrintToStream();
227 int32 opcode;
228 if (message->FindInt32("opcode", &opcode) != B_OK)
229 return;
231 switch (opcode) {
232 case B_ENTRY_CREATED:
234 const char* name;
235 node_ref nodeRef;
236 if (message->FindInt32("device", &nodeRef.device) != B_OK
237 || message->FindInt64("directory", &nodeRef.node)
238 != B_OK
239 || message->FindString("name", &name) != B_OK)
240 break;
242 // TODO: make this better (possible under Haiku)
243 snooze(100000);
244 // let the font be written completely before trying to
245 // open it
247 _EntryAdded(nodeRef, name);
248 break;
251 case B_ENTRY_MOVED:
253 // has the entry been moved into a monitored directory or
254 // has it been removed from one?
255 const char* name;
256 node_ref toNodeRef;
257 node_ref fromNodeRef;
258 node_ref nodeRef;
260 if (message->FindInt32("device", &nodeRef.device) != B_OK
261 || message->FindInt64("to directory", &toNodeRef.node)
262 != B_OK
263 || message->FindInt64("from directory",
264 (int64*)&fromNodeRef.node) != B_OK
265 || message->FindInt64("node", (int64*)&nodeRef.node)
266 != B_OK
267 || message->FindString("name", &name) != B_OK)
268 break;
270 fromNodeRef.device = nodeRef.device;
271 toNodeRef.device = nodeRef.device;
273 // Do we know this one yet?
274 translator_item* item = _FindTranslator(nodeRef);
275 if (item == NULL) {
276 // it's a new one!
277 if (_IsKnownDirectory(toNodeRef))
278 _EntryAdded(toNodeRef, name);
279 break;
282 if (!_IsKnownDirectory(toNodeRef)) {
283 // translator got removed
284 _RemoveTranslators(&nodeRef);
285 break;
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)
297 break;
299 break;
302 case B_ENTRY_REMOVED:
304 node_ref nodeRef;
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)
310 break;
312 translator_item* item = _FindTranslator(nodeRef);
313 if (item != NULL)
314 _RemoveTranslators(&nodeRef);
315 break;
318 break;
321 default:
322 BHandler::MessageReceived(message);
323 break;
328 void
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]);
340 i++) {
341 BPath path;
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().
359 status_t
360 BTranslatorRoster::Private::AddPaths(const char* paths)
362 if (paths == NULL)
363 return B_BAD_VALUE;
365 status_t status = B_OK;
366 int32 added = 0;
368 while (paths != NULL) {
369 const char* end = strchr(paths, ':');
370 BString path;
372 if (end != NULL) {
373 path.SetTo(paths, end - 1 - paths);
374 paths = end + 1;
375 } else {
376 path.SetTo(paths);
377 paths = NULL;
380 // Keep the last error that occured, and return it
381 // but don't overwrite it, if the last path was
382 // added successfully.
383 int32 count;
384 status_t error = AddPath(path.String(), &count);
385 if (error != B_NO_ERROR)
386 status = error;
388 added += count;
391 if (added == 0)
392 return status;
394 return B_OK;
399 Adds a new directory to the roster.
401 Note, the order in which these directories are added to actually matters,
402 see AddPaths().
404 status_t
405 BTranslatorRoster::Private::AddPath(const char* path, int32* _added)
407 BDirectory directory(path);
408 status_t status = directory.InitCheck();
409 if (status != B_OK)
410 return status;
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);
417 if (status != B_OK)
418 return status;
422 node_ref nodeRef;
423 status = directory.GetNodeRef(&nodeRef);
424 if (status < B_OK)
425 return status;
427 // do we know this directory already?
428 if (_IsKnownDirectory(nodeRef))
429 return B_OK;
431 if (Looper() != NULL) {
432 // watch that directory
433 watch_node(&nodeRef, B_WATCH_DIRECTORY, this);
434 fDirectories.push_back(nodeRef);
437 int32 count = 0;
438 int32 files = 0;
440 entry_ref ref;
441 while (directory.GetNextRef(&ref) == B_OK) {
442 BEntry entry(&ref);
443 if (entry.IsDirectory())
444 continue;
445 if (CreateTranslators(ref, count) == B_OK)
446 count++;
448 files++;
451 if (_added)
452 *_added = count;
454 if (files != 0 && count == 0)
455 return B_BAD_VALUE;
457 return B_OK;
461 status_t
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;
469 item.image = image;
470 item.node = node;
471 if (ref != NULL)
472 item.ref = *ref;
474 try {
475 fTranslators[fNextID] = item;
476 } catch (...) {
477 return B_NO_MEMORY;
480 translator->fOwningRoster = this;
481 translator->fID = fNextID++;
482 return B_OK;
486 void
487 BTranslatorRoster::Private::RemoveTranslators(entry_ref& ref)
489 _RemoveTranslators(NULL, &ref);
493 BTranslator*
494 BTranslatorRoster::Private::FindTranslator(translator_id id)
496 if (!IsLocked()) {
497 debugger("translator must be locked!");
498 return NULL;
501 const translator_item* item = _FindTranslator(id);
502 if (item != NULL)
503 return item->translator;
505 return NULL;
509 status_t
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
518 int32* version;
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) {
533 return B_BAD_TYPE;
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);
544 return B_OK;
548 status_t
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);
557 if (item != NULL) {
558 // check if the known translator has a higher priority
559 if (_CompareTranslatorDirectoryPriority(item->ref, ref) <= 0) {
560 // keep the existing add-on
561 return B_OK;
564 // replace existing translator(s) if the new translator succeeds
565 quarantine.Put(item->ref);
568 BEntry entry(&ref);
569 node_ref nodeRef;
570 status_t status = entry.GetNodeRef(&nodeRef);
571 if (status < B_OK)
572 return status;
574 BPath path(&ref);
575 image_id image = load_add_on(path.Path());
576 if (image < B_OK)
577 return image;
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;
589 int32 created = 0;
590 for (int32 n = 0; (translator = makeNthTranslator(n, image, 0)) != NULL;
591 n++) {
592 if (AddTranslator(translator, image, &ref, nodeRef.node) == B_OK) {
593 if (update)
594 update->AddInt32("translator_id", translator->fID);
595 count++;
596 created++;
597 } else {
598 translator->Release();
599 // this will delete the translator
603 if (created == 0)
604 unload_add_on(image);
606 quarantine.Remove();
607 return B_OK;
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(
618 translatorData);
619 if (translator == NULL)
620 status = B_NO_MEMORY;
623 if (status == B_OK)
624 status = AddTranslator(translator, image, &ref, nodeRef.node);
626 if (status == B_OK) {
627 if (update)
628 update->AddInt32("translator_id", translator->fID);
629 quarantine.Remove();
630 count++;
631 } else
632 unload_add_on(image);
634 return status;
638 status_t
639 BTranslatorRoster::Private::StartWatching(BMessenger target)
641 try {
642 fMessengers.push_back(target);
643 } catch (...) {
644 return B_NO_MEMORY;
647 if (fLazyScanning) {
648 fLazyScanning = false;
649 // Since we now have someone to report to, we cannot lazily
650 // adopt changes to the translator any longer
652 _RescanChanged();
655 return B_OK;
659 status_t
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;
670 return B_OK;
673 iterator++;
676 return B_BAD_VALUE;
680 status_t
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());
693 iterator++;
696 return B_OK;
700 status_t
701 BTranslatorRoster::Private::Identify(BPositionIO* source,
702 BMessage* ioExtension, uint32 hintType, const char* hintMIME,
703 uint32 wantType, translator_info* _info)
705 BAutolock locker(this);
707 _RescanChanged();
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);
720 if (pos != 0)
721 return pos < 0 ? (status_t)pos : B_IO_ERROR;
723 int32 formatsCount = 0;
724 const translation_format* formats = translator.InputFormats(
725 &formatsCount);
726 const translation_format* format = _CheckHints(formats, formatsCount,
727 hintType, hintMIME);
729 BMessage extension(baseExtension);
730 translator_info info;
731 if (translator.Identify(source, format, &extension, &info, wantType)
732 == B_OK) {
733 float weight = info.quality * info.capability;
734 if (weight > bestWeight) {
735 if (ioExtension != NULL)
736 *ioExtension = extension;
737 bestWeight = weight;
739 info.translator = iterator->first;
740 memcpy(_info, &info, sizeof(translator_info));
744 iterator++;
747 if (bestWeight > 0.0f)
748 return B_OK;
750 return B_NO_TRANSLATOR;
754 status_t
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);
761 _RescanChanged();
763 int32 arraySize = fTranslators.size();
764 translator_info* array = new (std::nothrow) translator_info[arraySize];
765 if (array == NULL)
766 return B_NO_MEMORY;
768 TranslatorMap::const_iterator iterator = fTranslators.begin();
769 int32 count = 0;
771 while (iterator != fTranslators.end()) {
772 BTranslator& translator = *iterator->second.translator;
774 off_t pos = source->Seek(0, SEEK_SET);
775 if (pos != 0) {
776 delete[] array;
777 return pos < 0 ? status_t(pos) : B_IO_ERROR;
780 int32 formatsCount = 0;
781 const translation_format* formats = translator.InputFormats(
782 &formatsCount);
783 const translation_format* format = _CheckHints(formats, formatsCount,
784 hintType, hintMIME);
786 translator_info info;
787 if (translator.Identify(source, format, ioExtension, &info, wantType)
788 == B_OK) {
789 info.translator = iterator->first;
790 array[count++] = info;
793 iterator++;
796 *_info = array;
797 *_numInfo = count;
798 qsort(array, count, sizeof(translator_info),
799 BTranslatorRoster::Private::_CompareSupport);
800 // translators are sorted by best support
802 return B_OK;
806 status_t
807 BTranslatorRoster::Private::GetAllTranslators(translator_id** _ids,
808 int32* _count)
810 BAutolock locker(this);
812 _RescanChanged();
814 int32 arraySize = fTranslators.size();
815 translator_id* array = new (std::nothrow) translator_id[arraySize];
816 if (array == NULL)
817 return B_NO_MEMORY;
819 TranslatorMap::const_iterator iterator = fTranslators.begin();
820 int32 count = 0;
822 while (iterator != fTranslators.end()) {
823 array[count++] = iterator->first;
824 iterator++;
827 *_ids = array;
828 *_count = count;
829 return B_OK;
833 status_t
834 BTranslatorRoster::Private::GetRefFor(translator_id id, entry_ref& ref)
836 BAutolock locker(this);
838 const translator_item* item = _FindTranslator(id);
839 if (item == NULL)
840 return B_NO_TRANSLATOR;
842 BEntry entry(&item->ref);
843 if (entry.InitCheck() == B_OK && entry.Exists() && entry.IsFile()) {
844 ref = item->ref;
845 return B_OK;
848 return B_ERROR;
852 void
853 BTranslatorRoster::Private::TranslatorDeleted(translator_id id)
855 BAutolock locker(this);
857 TranslatorMap::iterator iterator = fTranslators.find(id);
858 if (iterator == fTranslators.end())
859 return;
861 fTranslators.erase(iterator);
865 /*static*/ int
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)
875 return 0;
876 if (weightA > weightB)
877 return -1;
879 return 1;
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
890 changes.
892 void
893 BTranslatorRoster::Private::_RescanChanged()
895 while (!fRescanEntries.empty()) {
896 EntryRefSet::iterator iterator = fRescanEntries.begin();
897 int32 count;
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))
914 return NULL;
916 // The provided MIME type hint may be a super type
917 int32 super = 0;
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
924 || (hintMIME
925 && ((super && !strncmp(formats[i].MIME, hintMIME, super))
926 || !strcmp(formats[i].MIME, hintMIME))))
927 return &formats[i];
930 return NULL;
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())
939 return NULL;
941 return &iterator->second;
945 const translator_item*
946 BTranslatorRoster::Private::_FindTranslator(const char* name) const
948 if (name == NULL)
949 return NULL;
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))
956 return &item;
958 iterator++;
961 return NULL;
965 const translator_item*
966 BTranslatorRoster::Private::_FindTranslator(entry_ref& ref) const
968 if (ref.name == NULL)
969 return NULL;
971 TranslatorMap::const_iterator iterator = fTranslators.begin();
973 while (iterator != fTranslators.end()) {
974 const translator_item& item = iterator->second;
975 if (item.ref == ref)
976 return &item;
978 iterator++;
981 return NULL;
985 translator_item*
986 BTranslatorRoster::Private::_FindTranslator(node_ref& nodeRef)
988 if (nodeRef.device < 0)
989 return NULL;
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)
997 return &item;
999 iterator++;
1002 return NULL;
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
1010 chosen.
1012 int32
1013 BTranslatorRoster::Private::_CompareTranslatorDirectoryPriority(
1014 const entry_ref& a, const entry_ref& b) const
1016 // priority is determined by the order in the list
1018 node_ref nodeRefA;
1019 nodeRefA.device = a.device;
1020 nodeRefA.node = a.directory;
1022 node_ref nodeRefB;
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)
1030 return -1;
1031 if (*iterator == nodeRefB)
1032 return 1;
1034 iterator++;
1037 return 0;
1041 bool
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)
1048 return true;
1050 iterator++;
1053 return false;
1057 void
1058 BTranslatorRoster::Private::_RemoveTranslators(const node_ref* nodeRef,
1059 const entry_ref* ref)
1061 if (ref == NULL && nodeRef == NULL)
1062 return;
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;
1070 next++;
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();
1080 image = item.image;
1081 update.AddInt32("translator_id", iterator->first);
1083 fTranslators.erase(iterator);
1086 iterator = next;
1089 // Unload image from the removed translator
1091 if (image >= B_OK)
1092 unload_add_on(image);
1094 _NotifyListeners(update);
1098 void
1099 BTranslatorRoster::Private::_EntryAdded(const node_ref& nodeRef,
1100 const char* name)
1102 entry_ref ref;
1103 ref.device = nodeRef.device;
1104 ref.directory = nodeRef.node;
1105 ref.set_name(name);
1107 _EntryAdded(ref);
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.
1119 void
1120 BTranslatorRoster::Private::_EntryAdded(const entry_ref& ref)
1122 BEntry entry;
1123 if (entry.SetTo(&ref) != B_OK || !entry.IsFile())
1124 return;
1126 if (fLazyScanning) {
1127 fRescanEntries.insert(ref);
1128 return;
1131 BMessage update(B_TRANSLATOR_ADDED);
1132 int32 count = 0;
1133 CreateTranslators(ref, count, &update);
1135 _NotifyListeners(update);
1139 void
1140 BTranslatorRoster::Private::_NotifyListeners(BMessage& update) const
1142 MessengerList::const_iterator iterator = fMessengers.begin();
1144 while (iterator != fMessengers.end()) {
1145 (*iterator).SendMessage(&update);
1146 iterator++;
1151 // #pragma mark -
1154 BTranslatorRoster::BTranslatorRoster()
1156 _Initialize();
1160 BTranslatorRoster::BTranslatorRoster(BMessage* model)
1162 _Initialize();
1164 if (model) {
1165 const char* path;
1166 for (int32 i = 0;
1167 model->FindString("be:translator_path", i, &path) == B_OK; i++) {
1168 BEntry entry(path);
1169 entry_ref ref;
1170 if (entry.GetRef(&ref) == B_OK) {
1171 int32 count = 0;
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;
1187 delete fPrivate;
1191 void
1192 BTranslatorRoster::_Initialize()
1194 fPrivate = new BTranslatorRoster::Private();
1198 status_t
1199 BTranslatorRoster::Archive(BMessage* into, bool deep) const
1201 status_t status = BArchivable::Archive(into, deep);
1202 if (status != B_OK)
1203 return status;
1205 return fPrivate->StoreTranslators(*into);
1209 BArchivable*
1210 BTranslatorRoster::Instantiate(BMessage* from)
1212 if (!from || !validate_instantiation(from, "BTranslatorRoster"))
1213 return NULL;
1215 return new BTranslatorRoster(from);
1219 BTranslatorRoster*
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)
1230 snooze(10000);
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.
1261 status_t
1262 BTranslatorRoster::AddTranslators(const char* path)
1264 if (path == NULL)
1265 path = getenv("TRANSLATORS");
1266 if (path == NULL) {
1267 fPrivate->AddDefaultPaths();
1268 return B_OK;
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
1282 BTranslatorRoster
1284 \return B_BAD_VALUE, if translator is NULL,
1285 B_OK if all went well
1287 status_t
1288 BTranslatorRoster::AddTranslator(BTranslator* translator)
1290 if (!translator)
1291 return B_BAD_VALUE;
1293 return fPrivate->AddTranslator(translator);
1297 bool
1298 BTranslatorRoster::IsTranslator(entry_ref* ref)
1300 if (ref == NULL)
1301 return false;
1303 BPath path(ref);
1304 image_id image = load_add_on(path.Path());
1305 if (image < B_OK)
1306 return false;
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
1341 status_t
1342 BTranslatorRoster::Identify(BPositionIO* source, BMessage* ioExtension,
1343 translator_info* _info, uint32 hintType, const char* hintMIME,
1344 uint32 wantType)
1346 if (source == NULL || _info == NULL)
1347 return B_BAD_VALUE;
1349 return fPrivate->Identify(source, ioExtension, hintType, hintMIME, wantType,
1350 _info);
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
1377 status_t
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)
1383 return B_BAD_VALUE;
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
1392 object.
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.
1398 status_t
1399 BTranslatorRoster::GetAllTranslators(translator_id** _ids, int32* _count)
1401 if (_ids == NULL || _count == NULL)
1402 return B_BAD_VALUE;
1404 return fPrivate->GetAllTranslators(_ids, _count);
1409 Returns information about the translator with the specified
1410 translator \a id.
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
1422 status_t
1423 BTranslatorRoster::GetTranslatorInfo(translator_id id, const char** _name,
1424 const char** _info, int32* _version)
1426 if (_name == NULL && _info == NULL && _version == NULL)
1427 return B_BAD_VALUE;
1429 BAutolock locker(fPrivate);
1431 BTranslator* translator = fPrivate->FindTranslator(id);
1432 if (translator == NULL)
1433 return B_NO_TRANSLATOR;
1435 if (_name)
1436 *_name = translator->TranslatorName();
1437 if (_info)
1438 *_info = translator->TranslatorInfo();
1439 if (_version)
1440 *_version = translator->TranslatorVersion();
1442 return B_OK;
1447 Returns all of the input formats for the translator specified
1448 by \a id.
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
1459 status_t
1460 BTranslatorRoster::GetInputFormats(translator_id id,
1461 const translation_format** _formats, int32* _numFormats)
1463 if (_formats == NULL || _numFormats == NULL)
1464 return B_BAD_VALUE;
1466 BAutolock locker(fPrivate);
1468 BTranslator* translator = fPrivate->FindTranslator(id);
1469 if (translator == NULL)
1470 return B_NO_TRANSLATOR;
1472 *_formats = translator->InputFormats(_numFormats);
1473 return B_OK;
1478 Returns all of the output formats for the translator specified
1479 by \a id.
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
1490 status_t
1491 BTranslatorRoster::GetOutputFormats(translator_id id,
1492 const translation_format** _formats, int32* _numFormats)
1494 if (_formats == NULL || _numFormats == NULL)
1495 return B_BAD_VALUE;
1497 BAutolock locker(fPrivate);
1499 BTranslator* translator = fPrivate->FindTranslator(id);
1500 if (translator == NULL)
1501 return B_NO_TRANSLATOR;
1503 *_formats = translator->OutputFormats(_numFormats);
1504 return B_OK;
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
1528 status_t
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)
1534 return B_BAD_VALUE;
1536 translator_info infoBuffer;
1538 if (info == NULL) {
1539 // look for a suitable translator
1540 status_t status = fPrivate->Identify(source, ioExtension, hintType,
1541 hintMIME, wantOutType, &infoBuffer);
1542 if (status < B_OK)
1543 return status;
1545 info = &infoBuffer;
1548 if (!fPrivate->Lock())
1549 return B_ERROR;
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!
1558 fPrivate->Unlock();
1560 if (translator == NULL)
1561 return B_NO_TRANSLATOR;
1563 status_t status = B_OK;
1564 off_t pos = source->Seek(0, SEEK_SET);
1565 if (pos != 0)
1566 status = pos < 0 ? (status_t)pos : B_IO_ERROR;
1567 if (status == B_OK) {
1568 status = translator->Translate(source, info, ioExtension, wantOutType,
1569 destination);
1571 translator->Release();
1573 return status;
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
1581 by \a id.
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
1593 status_t
1594 BTranslatorRoster::Translate(translator_id id, BPositionIO* source,
1595 BMessage* ioExtension, BPositionIO* destination, uint32 wantOutType)
1597 if (source == NULL || destination == NULL)
1598 return B_BAD_VALUE;
1600 if (!fPrivate->Lock())
1601 return B_ERROR;
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!
1610 fPrivate->Unlock();
1612 if (translator == NULL)
1613 return B_NO_TRANSLATOR;
1615 status_t status;
1616 off_t pos = source->Seek(0, SEEK_SET);
1617 if (pos == 0) {
1618 translator_info info;
1619 status = translator->Identify(source, NULL, ioExtension, &info,
1620 wantOutType);
1621 if (status >= B_OK) {
1622 off_t pos = source->Seek(0, SEEK_SET);
1623 if (pos != 0)
1624 status = pos < 0 ? (status_t)pos : B_IO_ERROR;
1625 else {
1626 status = translator->Translate(source, &info, ioExtension,
1627 wantOutType, destination);
1630 } else
1631 status = pos < 0 ? (status_t)pos : B_IO_ERROR;
1632 translator->Release();
1634 return status;
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
1651 status_t
1652 BTranslatorRoster::MakeConfigurationView(translator_id id,
1653 BMessage* ioExtension, BView** _view, BRect* _extent)
1655 if (_view == NULL || _extent == NULL)
1656 return B_BAD_VALUE;
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
1679 status_t
1680 BTranslatorRoster::GetConfigurationMessage(translator_id id,
1681 BMessage* ioExtension)
1683 if (!ioExtension)
1684 return B_BAD_VALUE;
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
1708 status_t
1709 BTranslatorRoster::GetRefFor(translator_id id, entry_ref* ref)
1711 if (ref == NULL)
1712 return B_BAD_VALUE;
1714 return fPrivate->GetRefFor(id, *ref);
1718 status_t
1719 BTranslatorRoster::StartWatching(BMessenger target)
1721 return fPrivate->StartWatching(target);
1725 status_t
1726 BTranslatorRoster::StopWatching(BMessenger target)
1728 return fPrivate->StopWatching(target);
1732 // #pragma mark - private
1735 BTranslatorRoster::BTranslatorRoster(const BTranslatorRoster &other)
1740 BTranslatorRoster &
1741 BTranslatorRoster::operator=(const BTranslatorRoster &tr)
1743 return *this;
1747 #if __GNUC__ == 2 // gcc 2
1749 /*static*/ const char*
1750 BTranslatorRoster::Version(int32* outCurVersion, int32* outMinVersion,
1751 int32 inAppVersion)
1753 if (!outCurVersion || !outMinVersion)
1754 return "";
1756 static char vString[50];
1757 static char vDate[] = __DATE__;
1758 if (!vString[0]) {
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)),
1763 vDate);
1765 *outCurVersion = B_TRANSLATION_CURRENT_VERSION;
1766 *outMinVersion = B_TRANSLATION_MIN_VERSION;
1767 return vString;
1770 #endif // gcc 2
1773 void BTranslatorRoster::ReservedTranslatorRoster1() {}
1774 void BTranslatorRoster::ReservedTranslatorRoster2() {}
1775 void BTranslatorRoster::ReservedTranslatorRoster3() {}
1776 void BTranslatorRoster::ReservedTranslatorRoster4() {}
1777 void BTranslatorRoster::ReservedTranslatorRoster5() {}
1778 void BTranslatorRoster::ReservedTranslatorRoster6() {}