xref: /haiku/src/kits/translation/TranslatorRoster.cpp (revision 302f62604763c95777d6d04cca456e876f471c4f)
1 /*
2  * Copyright 2002-2006, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Michael Wilber
7  *		Axel Dörfler, axeld@pinc-software.de
8  */
9 
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.
14 */
15 
16 
17 #include "FuncTranslator.h"
18 #include "TranslatorRosterPrivate.h"
19 
20 #include <Application.h>
21 #include <Autolock.h>
22 #include <Directory.h>
23 #include <FindDirectory.h>
24 #include <driver_settings.h>
25 #include <image.h>
26 #include <NodeMonitor.h>
27 #include <Path.h>
28 #include <String.h>
29 #include <TranslatorRoster.h>
30 
31 #include <new>
32 #include <string.h>
33 
34 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
35 extern "C" status_t _kern_get_safemode_option(const char *parameter,
36         char *buffer, size_t *_bufferSize);
37 #else
38 extern "C" status_t _kget_safemode_option_(const char *parameter,
39         char *buffer, size_t *_bufferSize);
40 #endif
41 
42 
43 #if !defined(HAIKU_TARGET_PLATFORM_HAIKU) && !defined(HAIKU_TARGET_PLATFORM_LIBBE_TEST)
44 // building under R5 or Dano/Zeta
45 enum {
46 	B_TRANSLATOR_ADDED			= '_ART',
47 	B_TRANSLATOR_REMOVED		= '_RRT',
48 };
49 #endif
50 
51 namespace BPrivate {
52 
53 class QuarantineTranslatorImage {
54 	public:
55 		QuarantineTranslatorImage(BTranslatorRoster::Private& privateRoster);
56 		~QuarantineTranslatorImage();
57 
58 		void Put(const entry_ref& ref);
59 		void Remove();
60 
61 	private:
62 		BTranslatorRoster::Private& fRoster;
63 		entry_ref	fRef;
64 		bool		fRemove;
65 };
66 
67 }	// namespace BPrivate
68 
69 // Extensions used in the extension BMessage, defined in TranslatorFormats.h
70 char B_TRANSLATOR_EXT_HEADER_ONLY[]			= "/headerOnly";
71 char B_TRANSLATOR_EXT_DATA_ONLY[]			= "/dataOnly";
72 char B_TRANSLATOR_EXT_COMMENT[]				= "/comment";
73 char B_TRANSLATOR_EXT_TIME[]				= "/time";
74 char B_TRANSLATOR_EXT_FRAME[]				= "/frame";
75 char B_TRANSLATOR_EXT_BITMAP_RECT[]			= "bits/Rect";
76 char B_TRANSLATOR_EXT_BITMAP_COLOR_SPACE[]	= "bits/space";
77 char B_TRANSLATOR_EXT_BITMAP_PALETTE[]		= "bits/palette";
78 char B_TRANSLATOR_EXT_SOUND_CHANNEL[]		= "nois/channel";
79 char B_TRANSLATOR_EXT_SOUND_MONO[]			= "nois/mono";
80 char B_TRANSLATOR_EXT_SOUND_MARKER[]		= "nois/marker";
81 char B_TRANSLATOR_EXT_SOUND_LOOP[]			= "nois/loop";
82 
83 BTranslatorRoster* BTranslatorRoster::sDefaultRoster = NULL;
84 
85 
86 namespace BPrivate {
87 
88 /*!
89 	The purpose of this class is to put a translator entry_ref into - and remove
90 	it from the list of translators on destruction (if Remove() was called before).
91 
92 	This is used in Private::CreateTranslators() in case a translator hides a
93 	previous one (ie. if you install a translator in the user's translators directory
94 	that has the same name as one in the system's directory, it will hide this
95 	entry).
96 */
97 QuarantineTranslatorImage::QuarantineTranslatorImage(BTranslatorRoster::Private& privateRoster)
98 	:
99 	fRoster(privateRoster),
100 	fRemove(false)
101 {
102 }
103 
104 
105 QuarantineTranslatorImage::~QuarantineTranslatorImage()
106 {
107 	if (fRef.device == -1 || !fRemove)
108 		return;
109 
110 	fRoster.RemoveTranslators(fRef);
111 }
112 
113 
114 void
115 QuarantineTranslatorImage::Put(const entry_ref& ref)
116 {
117 	fRef = ref;
118 }
119 
120 
121 void
122 QuarantineTranslatorImage::Remove()
123 {
124 	fRemove = true;
125 }
126 
127 }	// namespace BPrivate
128 
129 
130 //	#pragma mark -
131 
132 
133 BTranslatorRoster::Private::Private()
134 	: BHandler("translator roster"), BLocker("translator list"),
135 	fNextID(1),
136 	fLazyScanning(true),
137 	fSafeMode(false)
138 {
139 
140 	char parameter[32];
141 	size_t parameterLength = sizeof(parameter);
142 
143 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
144 	if (_kern_get_safemode_option(B_SAFEMODE_SAFE_MODE, parameter, &parameterLength) == B_OK)
145 #else
146 	if (_kget_safemode_option_(B_SAFEMODE_SAFE_MODE, parameter, &parameterLength) == B_OK)
147 #endif
148 	{
149 		if (!strcasecmp(parameter, "enabled") || !strcasecmp(parameter, "on")
150 			|| !strcasecmp(parameter, "true") || !strcasecmp(parameter, "yes")
151 			|| !strcasecmp(parameter, "enable") || !strcmp(parameter, "1"))
152 			fSafeMode = true;
153 	}
154 
155 	// we're sneaking us into the BApplication
156 	if (be_app != NULL && be_app->Lock()) {
157 		be_app->AddHandler(this);
158 		be_app->Unlock();
159 	}
160 }
161 
162 
163 BTranslatorRoster::Private::~Private()
164 {
165 	stop_watching(this);
166 
167 	if (Looper() && LockLooper()) {
168 		BLooper* looper = Looper();
169 		Looper()->RemoveHandler(this);
170 		looper->Unlock();
171 	}
172 
173 	// Release all translators, so that they can delete themselves
174 
175 	TranslatorMap::iterator iterator = fTranslators.begin();
176 	std::set<image_id> images;
177 
178 	while (iterator != fTranslators.end()) {
179 		BTranslator* translator = iterator->second.translator;
180 
181 		translator->fOwningRoster = NULL;
182 			// we don't want to be notified about this anymore
183 
184 		images.insert(iterator->second.image);
185 		translator->Release();
186 
187 		iterator++;
188 	}
189 
190 	// Unload all images
191 
192 	std::set<image_id>::const_iterator imageIterator = images.begin();
193 
194 	while (imageIterator != images.end()) {
195 		unload_add_on(*imageIterator);
196 		imageIterator++;
197 	}
198 }
199 
200 
201 void
202 BTranslatorRoster::Private::MessageReceived(BMessage* message)
203 {
204 	switch (message->what) {
205 		case B_NODE_MONITOR:
206 		{
207 			BAutolock locker(this);
208 
209 			printf("translator roster node monitor: ");
210 			message->PrintToStream();
211 
212 			int32 opcode;
213 			if (message->FindInt32("opcode", &opcode) != B_OK)
214 				return;
215 
216 			switch (opcode) {
217 				case B_ENTRY_CREATED:
218 				{
219 					const char* name;
220 					node_ref nodeRef;
221 					if (message->FindInt32("device", &nodeRef.device) != B_OK
222 						|| message->FindInt64("directory", &nodeRef.node) != B_OK
223 						|| message->FindString("name", &name) != B_OK)
224 						break;
225 
226 					// TODO: make this better (possible under Haiku)
227 					snooze(100000);
228 						// let the font be written completely before trying to open it
229 
230 					_EntryAdded(nodeRef, name);
231 					break;
232 				}
233 
234 				case B_ENTRY_MOVED:
235 				{
236 					// has the entry been moved into a monitored directory or has
237 					// it been removed from one?
238 					const char* name;
239 					node_ref toNodeRef;
240 					node_ref fromNodeRef;
241 					node_ref nodeRef;
242 
243 					if (message->FindInt32("device", &nodeRef.device) != B_OK
244 						|| message->FindInt64("to directory", &toNodeRef.node) != B_OK
245 						|| message->FindInt64("from directory", (int64 *)&fromNodeRef.node) != B_OK
246 						|| message->FindInt64("node", (int64 *)&nodeRef.node) != B_OK
247 						|| message->FindString("name", &name) != B_OK)
248 						break;
249 
250 					fromNodeRef.device = nodeRef.device;
251 					toNodeRef.device = nodeRef.device;
252 
253 					// Do we know this one yet?
254 					translator_item* item = _FindTranslator(nodeRef);
255 					if (item == NULL) {
256 						// it's a new one!
257 						if (_IsKnownDirectory(toNodeRef))
258 							_EntryAdded(toNodeRef, name);
259 						break;
260 					}
261 
262 					if (!_IsKnownDirectory(toNodeRef)) {
263 						// translator got removed
264 						_RemoveTranslators(&nodeRef);
265 						break;
266 					}
267 
268 					// the name may have changed
269 					item->ref.set_name(name);
270 					item->ref.directory = toNodeRef.node;
271 
272 					if (_IsKnownDirectory(fromNodeRef)
273 						&& _IsKnownDirectory(toNodeRef)) {
274 						// TODO: we should rescan for the name, there might be name
275 						//	clashes with translators in other directories
276 						//	(as well as old ones revealed)
277 						break;
278 					}
279 					break;
280 				}
281 
282 				case B_ENTRY_REMOVED:
283 				{
284 					node_ref nodeRef;
285 					uint64 directoryNode;
286 					if (message->FindInt32("device", &nodeRef.device) != B_OK
287 						|| message->FindInt64("directory", (int64 *)&directoryNode) != B_OK
288 						|| message->FindInt64("node", &nodeRef.node) != B_OK)
289 						break;
290 
291 					translator_item* item = _FindTranslator(nodeRef);
292 					if (item != NULL)
293 						_RemoveTranslators(&nodeRef);
294 					break;
295 				}
296 			}
297 			break;
298 		}
299 
300 		default:
301 			BHandler::MessageReceived(message);
302 			break;
303 	}
304 }
305 
306 
307 void
308 BTranslatorRoster::Private::AddDefaultPaths()
309 {
310 	// add user directories first, so that they can override system translators
311 	const directory_which paths[] = {
312 		B_USER_ADDONS_DIRECTORY,
313 		B_COMMON_ADDONS_DIRECTORY,
314 		B_BEOS_ADDONS_DIRECTORY,
315 	};
316 
317 	for (uint32 i = fSafeMode ? 1 : 0; i < sizeof(paths) / sizeof(paths[0]); i++) {
318 		BPath path;
319 		status_t status = find_directory(paths[i], &path, true);
320 		if (status == B_OK && path.Append("Translators") == B_OK) {
321 			mkdir(path.Path(), 0755);
322 				// make sure the directory exists before we add it
323 			AddPath(path.Path());
324 		}
325 	}
326 }
327 
328 
329 /*!
330 	Adds the colon separated list of directories to the roster.
331 
332 	Note, the order in which these directories are added to actually matters,
333 	translators with the same name will be taken from the earlier directory
334 	first. See _CompareTranslatorDirectoryPriority().
335 */
336 status_t
337 BTranslatorRoster::Private::AddPaths(const char* paths)
338 {
339 	if (paths == NULL)
340 		return B_BAD_VALUE;
341 
342 	status_t status = B_OK;
343 	int32 added = 0;
344 
345 	while (paths != NULL) {
346 		const char* end = strchr(paths, ':');
347 		BString path;
348 
349 		if (end != NULL) {
350 			path.SetTo(paths, end - 1 - paths);
351 			paths = end + 1;
352 		} else {
353 			path.SetTo(paths);
354 			paths = NULL;
355 		}
356 
357 		// Keep the last error that occured, and return it
358 		// but don't overwrite it, if the last path was
359 		// added successfully.
360 		int32 count;
361 		status_t error = AddPath(path.String(), &count);
362 		if (error != B_NO_ERROR)
363 			status = error;
364 
365 		added += count;
366 	}
367 
368 	if (added == 0)
369 		return status;
370 
371 	return B_OK;
372 }
373 
374 
375 /*!
376 	Adds a new directory to the roster.
377 
378 	Note, the order in which these directories are added to actually matters,
379 	see AddPaths().
380 */
381 status_t
382 BTranslatorRoster::Private::AddPath(const char* path, int32* _added)
383 {
384 	BDirectory directory(path);
385 	status_t status = directory.InitCheck();
386 	if (status < B_OK)
387 		return status;
388 
389 	node_ref nodeRef;
390 	status = directory.GetNodeRef(&nodeRef);
391 	if (status < B_OK)
392 		return status;
393 
394 	// do we know this directory already?
395 	if (_IsKnownDirectory(nodeRef))
396 		return B_OK;
397 
398 	if (Looper() != NULL) {
399 		// watch that directory
400 		watch_node(&nodeRef, B_WATCH_DIRECTORY, this);
401 		fDirectories.push_back(nodeRef);
402 	}
403 
404 	int32 count = 0;
405 	int32 files = 0;
406 
407 	entry_ref ref;
408 	while (directory.GetNextRef(&ref) == B_OK) {
409 		if (CreateTranslators(ref, count) == B_OK)
410 			count++;
411 
412 		files++;
413 	}
414 
415 	if (_added)
416 		*_added = count;
417 
418 	if (files != 0 && count == 0)
419 		return B_BAD_VALUE;
420 
421 	return B_OK;
422 }
423 
424 
425 status_t
426 BTranslatorRoster::Private::AddTranslator(BTranslator* translator,
427 	image_id image, const entry_ref* ref, ino_t node)
428 {
429 	BAutolock locker(this);
430 
431 	translator_item item;
432 	item.translator = translator;
433 	item.image = image;
434 	item.node = node;
435 	if (ref != NULL)
436 		item.ref = *ref;
437 
438 	try {
439 		fTranslators[fNextID] = item;
440 	} catch (...) {
441 		return B_NO_MEMORY;
442 	}
443 
444 	translator->fOwningRoster = this;
445 	translator->fID = fNextID++;
446 	return B_OK;
447 }
448 
449 
450 void
451 BTranslatorRoster::Private::RemoveTranslators(entry_ref& ref)
452 {
453 	_RemoveTranslators(NULL, &ref);
454 }
455 
456 
457 BTranslator*
458 BTranslatorRoster::Private::FindTranslator(translator_id id)
459 {
460 	if (!IsLocked()) {
461 		debugger("translator must be locked!");
462 		return NULL;
463 	}
464 
465 	const translator_item* item = _FindTranslator(id);
466 	if (item != NULL)
467 		return item->translator;
468 
469 	return NULL;
470 }
471 
472 
473 status_t
474 BTranslatorRoster::Private::GetTranslatorData(image_id image, translator_data& data)
475 {
476 	// If this is a translator add-on, it is in the C format
477 	memset(&data, 0, sizeof(translator_data));
478 
479 	// find all the symbols
480 
481 	int32* version;
482 	if (get_image_symbol(image, "translatorName", B_SYMBOL_TYPE_DATA, (void**)&data.name) < B_OK
483 		|| get_image_symbol(image, "translatorInfo", B_SYMBOL_TYPE_DATA, (void**)&data.info) < B_OK
484 		|| get_image_symbol(image, "translatorVersion", B_SYMBOL_TYPE_DATA, (void**)&version) < B_OK || version == NULL
485 		|| get_image_symbol(image, "inputFormats", B_SYMBOL_TYPE_DATA, (void**)&data.input_formats) < B_OK
486 		|| get_image_symbol(image, "outputFormats", B_SYMBOL_TYPE_DATA, (void**)&data.output_formats) < B_OK
487 		|| get_image_symbol(image, "Identify", B_SYMBOL_TYPE_TEXT, (void**)&data.identify_hook) < B_OK
488 		|| get_image_symbol(image, "Translate", B_SYMBOL_TYPE_TEXT, (void**)&data.translate_hook) < B_OK)
489 		return B_BAD_TYPE;
490 
491 	data.version = *version;
492 
493 	// those calls are optional
494 	get_image_symbol(image, "MakeConfig", B_SYMBOL_TYPE_TEXT, (void**)&data.make_config_hook);
495 	get_image_symbol(image, "GetConfigMessage", B_SYMBOL_TYPE_TEXT, (void**)&data.get_config_message_hook);
496 
497 	return B_OK;
498 }
499 
500 
501 status_t
502 BTranslatorRoster::Private::CreateTranslators(const entry_ref& ref, int32& count,
503 	BMessage* update)
504 {
505 	BAutolock locker(this);
506 
507 	BPrivate::QuarantineTranslatorImage quarantine(*this);
508 
509 	const translator_item* item = _FindTranslator(ref.name);
510 	if (item != NULL) {
511 		// check if the known translator has a higher priority
512 		if (_CompareTranslatorDirectoryPriority(item->ref, ref) <= 0) {
513 			// keep the existing add-on
514 			return B_OK;
515 		}
516 
517 		// replace existing translator(s) if the new translator succeeds
518 		quarantine.Put(item->ref);
519 	}
520 
521 	BEntry entry(&ref);
522 	node_ref nodeRef;
523 	status_t status = entry.GetNodeRef(&nodeRef);
524 	if (status < B_OK)
525 		return status;
526 
527 	BPath path(&ref);
528 	image_id image = load_add_on(path.Path());
529 	if (image < B_OK)
530 		return image;
531 
532 	// Function pointer used to create post R4.5 style translators
533 	BTranslator *(*makeNthTranslator)(int32 n, image_id you, uint32 flags, ...);
534 
535 	status = get_image_symbol(image, "make_nth_translator",
536 		B_SYMBOL_TYPE_TEXT, (void**)&makeNthTranslator);
537 	if (status == B_OK) {
538 		// If the translator add-on supports the post R4.5
539 		// translator creation mechanism, keep loading translators
540 		// until MakeNthTranslator stops returning them.
541 		BTranslator* translator = NULL;
542 		int32 created = 0;
543 		for (int32 n = 0; (translator = makeNthTranslator(n, image, 0)) != NULL; n++) {
544 			if (AddTranslator(translator, image, &ref, nodeRef.node) == B_OK) {
545 				if (update)
546 					update->AddInt32("translator_id", translator->fID);
547 				count++;
548 				created++;
549 			} else {
550 				translator->Release();
551 					// this will delete the translator
552 			}
553 		}
554 
555 		if (created == 0)
556 			unload_add_on(image);
557 
558 		quarantine.Remove();
559 		return B_OK;
560 	}
561 
562 	// If this is a translator add-on, it is in the C format
563 	translator_data translatorData;
564 	status = GetTranslatorData(image, translatorData);
565 
566 	// add this translator to the list
567 	BPrivate::BFuncTranslator* translator = NULL;
568 	if (status == B_OK) {
569 		translator = new (std::nothrow) BPrivate::BFuncTranslator(translatorData);
570 		if (translator == NULL)
571 			status = B_NO_MEMORY;
572 	}
573 
574 	if (status == B_OK)
575 		status = AddTranslator(translator, image, &ref, nodeRef.node);
576 
577 	if (status == B_OK) {
578 		if (update)
579 			update->AddInt32("translator_id", translator->fID);
580 		quarantine.Remove();
581 		count++;
582 	} else
583 		unload_add_on(image);
584 
585 	return status;
586 }
587 
588 
589 status_t
590 BTranslatorRoster::Private::StartWatching(BMessenger target)
591 {
592 	try {
593 		fMessengers.push_back(target);
594 	} catch (...) {
595 		return B_NO_MEMORY;
596 	}
597 
598 	if (fLazyScanning) {
599 		fLazyScanning = false;
600 			// Since we now have someone to report to, we cannot lazily
601 			// adopt changes to the translator any longer
602 
603 		_RescanChanged();
604 	}
605 
606 	return B_OK;
607 }
608 
609 
610 status_t
611 BTranslatorRoster::Private::StopWatching(BMessenger target)
612 {
613 	MessengerList::iterator iterator = fMessengers.begin();
614 
615 	while (iterator != fMessengers.end()) {
616 		if (*iterator == target) {
617 			fMessengers.erase(iterator);
618 			if (fMessengers.empty())
619 				fLazyScanning = true;
620 
621 			return B_OK;
622 		}
623 
624 		iterator++;
625 	}
626 
627 	return B_BAD_VALUE;
628 }
629 
630 
631 status_t
632 BTranslatorRoster::Private::StoreTranslators(BMessage& archive)
633 {
634 	BAutolock locker(this);
635 
636 	TranslatorMap::const_iterator iterator = fTranslators.begin();
637 
638 	while (iterator != fTranslators.end()) {
639 		const translator_item& item = iterator->second;
640 		BPath path(&item.ref);
641 		if (path.InitCheck() == B_OK)
642 			archive.AddString("be:translator_path", path.Path());
643 
644 		iterator++;
645 	}
646 
647 	return B_OK;
648 }
649 
650 
651 status_t
652 BTranslatorRoster::Private::Identify(BPositionIO* source,
653 	BMessage* ioExtension, uint32 hintType, const char* hintMIME,
654 	uint32 wantType, translator_info* _info)
655 {
656 	BAutolock locker(this);
657 
658 	_RescanChanged();
659 
660 	TranslatorMap::const_iterator iterator = fTranslators.begin();
661 	float bestWeight = 0.0f;
662 
663 	while (iterator != fTranslators.end()) {
664 		BTranslator& translator = *iterator->second.translator;
665 
666 		status_t status = source->Seek(0, SEEK_SET);
667 		if (status != B_OK)
668 			return status;
669 
670 		int32 formatsCount = 0;
671 		const translation_format* formats = translator.InputFormats(&formatsCount);
672 		const translation_format* format = _CheckHints(formats, formatsCount, hintType,
673 			hintMIME);
674 
675 		translator_info info;
676 		if (translator.Identify(source, format, ioExtension, &info, wantType) == B_OK) {
677 			float weight = info.quality * info.capability;
678 			if (weight > bestWeight) {
679 				bestWeight = weight;
680 
681 				info.translator = iterator->first;
682 				memcpy(_info, &info, sizeof(translator_info));
683 			}
684 		}
685 
686 		iterator++;
687 	}
688 
689 	if (bestWeight > 0.0f)
690 		return B_OK;
691 
692 	return B_NO_TRANSLATOR;
693 }
694 
695 
696 status_t
697 BTranslatorRoster::Private::GetTranslators(BPositionIO* source,
698 	BMessage* ioExtension, uint32 hintType, const char* hintMIME,
699 	uint32 wantType, translator_info** _info, int32* _numInfo)
700 {
701 	BAutolock locker(this);
702 
703 	_RescanChanged();
704 
705 	int32 arraySize = fTranslators.size();
706 	translator_info* array = new (std::nothrow) translator_info[arraySize];
707 	if (array == NULL)
708 		return B_NO_MEMORY;
709 
710 	TranslatorMap::const_iterator iterator = fTranslators.begin();
711 	int32 count = 0;
712 
713 	while (iterator != fTranslators.end()) {
714 		BTranslator& translator = *iterator->second.translator;
715 
716 		status_t status = source->Seek(0, SEEK_SET);
717 		if (status < B_OK) {
718 			delete[] array;
719 			return status;
720 		}
721 
722 		int32 formatsCount = 0;
723 		const translation_format* formats = translator.InputFormats(&formatsCount);
724 		const translation_format* format = _CheckHints(formats, formatsCount, hintType,
725 			hintMIME);
726 
727 		translator_info info;
728 		if (translator.Identify(source, format, ioExtension, &info, wantType) == B_OK) {
729 			info.translator = iterator->first;
730 			array[count++] = info;
731 		}
732 
733 		iterator++;
734 	}
735 
736 	*_info = array;
737 	*_numInfo = count;
738 	qsort(array, count, sizeof(translator_info), BTranslatorRoster::Private::_CompareSupport);
739 		// translators are sorted by best support
740 
741 	return B_OK;
742 }
743 
744 
745 status_t
746 BTranslatorRoster::Private::GetAllTranslators(translator_id** _ids, int32* _count)
747 {
748 	BAutolock locker(this);
749 
750 	_RescanChanged();
751 
752 	int32 arraySize = fTranslators.size();
753 	translator_id* array = new (std::nothrow) translator_id[arraySize];
754 	if (array == NULL)
755 		return B_NO_MEMORY;
756 
757 	TranslatorMap::const_iterator iterator = fTranslators.begin();
758 	int32 count = 0;
759 
760 	while (iterator != fTranslators.end()) {
761 		array[count++] = iterator->first;
762 		iterator++;
763 	}
764 
765 	*_ids = array;
766 	*_count = count;
767 	return B_OK;
768 }
769 
770 
771 status_t
772 BTranslatorRoster::Private::GetRefFor(translator_id id, entry_ref& ref)
773 {
774 	BAutolock locker(this);
775 
776 	const translator_item* item = _FindTranslator(id);
777 	if (item == NULL)
778 		return B_NO_TRANSLATOR;
779 
780 	BEntry entry(&item->ref);
781 	if (entry.InitCheck() == B_OK && entry.Exists() && entry.IsFile()) {
782 		ref = item->ref;
783 		return B_OK;
784 	}
785 
786 	return B_ERROR;
787 }
788 
789 
790 void
791 BTranslatorRoster::Private::TranslatorDeleted(translator_id id)
792 {
793 	BAutolock locker(this);
794 
795 	TranslatorMap::iterator iterator = fTranslators.find(id);
796 	if (iterator == fTranslators.end())
797 		return;
798 
799 	fTranslators.erase(iterator);
800 }
801 
802 
803 /*static*/ int
804 BTranslatorRoster::Private::_CompareSupport(const void* _a, const void* _b)
805 {
806 	const translator_info* infoA = (const translator_info*)_a;
807 	const translator_info* infoB = (const translator_info*)_b;
808 
809 	float weightA = infoA->quality * infoA->capability;
810 	float weightB = infoB->quality * infoB->capability;
811 
812 	if (weightA == weightB)
813 		return 0;
814 	if (weightA > weightB)
815 		return -1;
816 
817 	return 1;
818 }
819 
820 
821 /*!
822 	In lazy mode, freshly installed translator are not scanned immediately
823 	when they become available. Instead, they are put into a set.
824 
825 	When a method is called that may be interested in these new translators,
826 	they are scanned on the fly. Since lazy mode also means that this roster
827 	does not have any listeners, we don't need to notify anyone about those
828 	changes.
829 */
830 void
831 BTranslatorRoster::Private::_RescanChanged()
832 {
833 	EntryRefSet::iterator iterator = fRescanEntries.begin();
834 
835 	while (iterator != fRescanEntries.end()) {
836 		int32 count;
837 		CreateTranslators(*iterator, count);
838 
839 		fRescanEntries.erase(iterator);
840 		iterator++;
841 	}
842 }
843 
844 
845 /*!
846 	Tests if the hints provided for a source stream are compatible to
847 	the formats the translator exports.
848 */
849 const translation_format*
850 BTranslatorRoster::Private::_CheckHints(const translation_format* formats,
851 	int32 formatsCount, uint32 hintType, const char* hintMIME)
852 {
853 	if (formats == NULL || formatsCount <= 0 || (!hintType && hintMIME == NULL))
854 		return NULL;
855 
856 	// The provided MIME type hint may be a super type
857 	int32 super = 0;
858 	if (hintMIME && !strchr(hintMIME, '/'))
859 		super = strlen(hintMIME);
860 
861 	// scan for suitable format
862 	for (int32 i = 0; i < formatsCount && formats[i].type; i++) {
863 		if (formats[i].type == hintType
864 			|| hintMIME && ((super && !strncmp(formats[i].MIME, hintMIME, super))
865 				|| !strcmp(formats[i].MIME, hintMIME)))
866 			return &formats[i];
867 	}
868 
869 	return NULL;
870 }
871 
872 
873 const translator_item*
874 BTranslatorRoster::Private::_FindTranslator(translator_id id) const
875 {
876 	TranslatorMap::const_iterator iterator = fTranslators.find(id);
877 	if (iterator == fTranslators.end())
878 		return NULL;
879 
880 	return &iterator->second;
881 }
882 
883 
884 const translator_item*
885 BTranslatorRoster::Private::_FindTranslator(const char* name) const
886 {
887 	if (name == NULL)
888 		return NULL;
889 
890 	TranslatorMap::const_iterator iterator = fTranslators.begin();
891 
892 	while (iterator != fTranslators.end()) {
893 		const translator_item& item = iterator->second;
894 		if (item.ref.name != NULL && !strcmp(item.ref.name, name))
895 			return &item;
896 
897 		iterator++;
898 	}
899 
900 	return NULL;
901 }
902 
903 
904 const translator_item*
905 BTranslatorRoster::Private::_FindTranslator(entry_ref& ref) const
906 {
907 	if (ref.name == NULL)
908 		return NULL;
909 
910 	TranslatorMap::const_iterator iterator = fTranslators.begin();
911 
912 	while (iterator != fTranslators.end()) {
913 		const translator_item& item = iterator->second;
914 		if (item.ref == ref)
915 			return &item;
916 
917 		iterator++;
918 	}
919 
920 	return NULL;
921 }
922 
923 
924 translator_item*
925 BTranslatorRoster::Private::_FindTranslator(node_ref& nodeRef)
926 {
927 	if (nodeRef.device < 0)
928 		return NULL;
929 
930 	TranslatorMap::iterator iterator = fTranslators.begin();
931 
932 	while (iterator != fTranslators.end()) {
933 		translator_item& item = iterator->second;
934 		if (item.ref.device == nodeRef.device
935 			&& item.node == nodeRef.node)
936 			return &item;
937 
938 		iterator++;
939 	}
940 
941 	return NULL;
942 }
943 
944 
945 /*!
946 	Directories added to the roster have a certain priority - the first entry
947 	to be added has the highest priority; if a translator with the same name
948 	is to be found in two directories, the one with the higher priority is
949 	chosen.
950 */
951 int32
952 BTranslatorRoster::Private::_CompareTranslatorDirectoryPriority(const entry_ref& a,
953 	const entry_ref& b) const
954 {
955 	// priority is determined by the order in the list
956 
957 	node_ref nodeRefA;
958 	nodeRefA.device = a.device;
959 	nodeRefA.node = a.directory;
960 
961 	node_ref nodeRefB;
962 	nodeRefB.device = b.device;
963 	nodeRefB.node = b.directory;
964 
965 	NodeRefList::const_iterator iterator = fDirectories.begin();
966 
967 	while (iterator != fDirectories.end()) {
968 		if (*iterator == nodeRefA)
969 			return -1;
970 		if (*iterator == nodeRefB)
971 			return 1;
972 
973 		iterator++;
974 	}
975 
976 	return 0;
977 }
978 
979 
980 bool
981 BTranslatorRoster::Private::_IsKnownDirectory(const node_ref& nodeRef) const
982 {
983 	NodeRefList::const_iterator iterator = fDirectories.begin();
984 
985 	while (iterator != fDirectories.end()) {
986 		if (*iterator == nodeRef)
987 			return true;
988 
989 		iterator++;
990 	}
991 
992 	return false;
993 }
994 
995 
996 void
997 BTranslatorRoster::Private::_RemoveTranslators(const node_ref* nodeRef, const entry_ref* ref)
998 {
999 	if (ref == NULL && nodeRef == NULL)
1000 		return;
1001 
1002 	TranslatorMap::iterator iterator = fTranslators.begin();
1003 	BMessage update(B_TRANSLATOR_REMOVED);
1004 	image_id image = -1;
1005 
1006 	while (iterator != fTranslators.end()) {
1007 		const translator_item& item = iterator->second;
1008 		if ((ref != NULL && item.ref == *ref)
1009 			|| (nodeRef != NULL && item.ref.device == nodeRef->device
1010 				&& item.node == nodeRef->node)) {
1011 			item.translator->fOwningRoster = NULL;
1012 				// if the translator is busy, we don't want to be notified
1013 				// about the removal later on
1014 			item.translator->Release();
1015 			image = item.image;
1016 			update.AddInt32("translator_id", iterator->first);
1017 
1018 			fTranslators.erase(iterator);
1019 		}
1020 
1021 		iterator++;
1022 	}
1023 
1024 	// Unload image from the removed translator
1025 
1026 	if (image >= B_OK)
1027 		unload_add_on(image);
1028 
1029 	_NotifyListeners(update);
1030 }
1031 
1032 
1033 void
1034 BTranslatorRoster::Private::_EntryAdded(const node_ref& nodeRef, const char* name)
1035 {
1036 	entry_ref ref;
1037 	ref.device = nodeRef.device;
1038 	ref.directory = nodeRef.node;
1039 	ref.set_name(name);
1040 
1041 	_EntryAdded(ref);
1042 }
1043 
1044 
1045 /*!
1046 	In lazy mode, the entry is marked to be rescanned on next use of any
1047 	translation method (that could make use of it).
1048 	In non-lazy mode, the translators for this entry are created directly
1049 	and listeners notified.
1050 
1051 	Called by the node monitor handling.
1052 */
1053 void
1054 BTranslatorRoster::Private::_EntryAdded(const entry_ref& ref)
1055 {
1056 	BEntry entry;
1057 	if (entry.SetTo(&ref) != B_OK || !entry.IsFile())
1058 		return;
1059 
1060 	if (fLazyScanning) {
1061 		fRescanEntries.insert(ref);
1062 		return;
1063 	}
1064 
1065 	BMessage update(B_TRANSLATOR_ADDED);
1066 	int32 count = 0;
1067 	CreateTranslators(ref, count, &update);
1068 
1069 	_NotifyListeners(update);
1070 }
1071 
1072 
1073 void
1074 BTranslatorRoster::Private::_NotifyListeners(BMessage& update) const
1075 {
1076 	MessengerList::const_iterator iterator = fMessengers.begin();
1077 
1078 	while (iterator != fMessengers.end()) {
1079 		(*iterator).SendMessage(&update);
1080 		iterator++;
1081 	}
1082 }
1083 
1084 
1085 //	#pragma mark -
1086 
1087 
1088 BTranslatorRoster::BTranslatorRoster()
1089 {
1090 	_Initialize();
1091 }
1092 
1093 
1094 BTranslatorRoster::BTranslatorRoster(BMessage* model)
1095 {
1096 	_Initialize();
1097 
1098 	if (model) {
1099 		const char* path;
1100 		for (int32 i = 0; model->FindString("be:translator_path", i, &path) == B_OK; i++) {
1101 			BEntry entry(path);
1102 			entry_ref ref;
1103 			if (entry.GetRef(&ref) == B_OK) {
1104 				int32 count = 0;
1105 				fPrivate->CreateTranslators(ref, count);
1106 			}
1107 		}
1108 	}
1109 }
1110 
1111 
1112 BTranslatorRoster::~BTranslatorRoster()
1113 {
1114 	// If the default BTranslatorRoster is being
1115 	// deleted, set the pointer to the default
1116 	// BTranslatorRoster to NULL
1117 	if (sDefaultRoster == this)
1118 		sDefaultRoster = NULL;
1119 
1120 	delete fPrivate;
1121 }
1122 
1123 
1124 void
1125 BTranslatorRoster::_Initialize()
1126 {
1127 	fPrivate = new BTranslatorRoster::Private();
1128 }
1129 
1130 
1131 status_t
1132 BTranslatorRoster::Archive(BMessage* into, bool deep) const
1133 {
1134 	status_t status = BArchivable::Archive(into, deep);
1135 	if (status != B_OK)
1136 		return status;
1137 
1138 	return fPrivate->StoreTranslators(*into);
1139 }
1140 
1141 
1142 BArchivable *
1143 BTranslatorRoster::Instantiate(BMessage* from)
1144 {
1145 	if (!from || !validate_instantiation(from, "BTranslatorRoster"))
1146 		return NULL;
1147 
1148 	return new BTranslatorRoster(from);
1149 }
1150 
1151 
1152 BTranslatorRoster *
1153 BTranslatorRoster::Default()
1154 {
1155 	static int32 lock = 0;
1156 
1157 	if (sDefaultRoster != NULL)
1158 		return sDefaultRoster;
1159 
1160 	if (atomic_add(&lock, 1) != 0) {
1161 		// Just wait for the default translator to be instantiated
1162 		while (sDefaultRoster == NULL)
1163 			snooze(10000);
1164 
1165 		atomic_add(&lock, -1);
1166 		return sDefaultRoster;
1167 	}
1168 
1169 	// If the default translators have not been loaded,
1170 	// create a new BTranslatorRoster for them, and load them.
1171 	if (sDefaultRoster == NULL) {
1172 		BTranslatorRoster* roster = new BTranslatorRoster();
1173 		roster->AddTranslators(NULL);
1174 
1175 		sDefaultRoster = roster;
1176 			// this will unlock any other threads waiting for
1177 			// the default roster to become available
1178 	}
1179 
1180 	atomic_add(&lock, -1);
1181 	return sDefaultRoster;
1182 }
1183 
1184 
1185 /*!
1186 	This function takes a string of colon delimited paths, and adds
1187 	the translators from those paths to this BTranslatorRoster.
1188 
1189 	If load_path is NULL, it parses the environment variable
1190 	TRANSLATORS. If that does not exist, it uses the system paths:
1191 		/boot/home/config/add-ons/Translators,
1192 		/system/add-ons/Translators.
1193 */
1194 status_t
1195 BTranslatorRoster::AddTranslators(const char* path)
1196 {
1197 	if (path == NULL)
1198 		path = getenv("TRANSLATORS");
1199 	if (path == NULL) {
1200 		fPrivate->AddDefaultPaths();
1201 		return B_OK;
1202 	}
1203 
1204 	return fPrivate->AddPaths(path);
1205 }
1206 
1207 
1208 /*!
1209 	Adds a BTranslator based object to the BTranslatorRoster.
1210 	When you add a BTranslator roster, it is Acquire()'d by
1211 	BTranslatorRoster; it is Release()'d when the
1212 	BTranslatorRoster is deleted.
1213 
1214 	\param translator the translator to be added to the
1215 		BTranslatorRoster
1216 
1217 	\return B_BAD_VALUE, if translator is NULL,
1218 		B_OK if all went well
1219 */
1220 status_t
1221 BTranslatorRoster::AddTranslator(BTranslator* translator)
1222 {
1223 	if (!translator)
1224 		return B_BAD_VALUE;
1225 
1226 	return fPrivate->AddTranslator(translator);
1227 }
1228 
1229 
1230 bool
1231 BTranslatorRoster::IsTranslator(entry_ref* ref)
1232 {
1233 	if (ref == NULL)
1234 		return false;
1235 
1236 	BPath path(ref);
1237 	image_id image = load_add_on(path.Path());
1238 	if (image < B_OK)
1239 		return false;
1240 
1241 	// Function pointer used to create post R4.5 style translators
1242 	BTranslator *(*makeNthTranslator)(int32 n, image_id you, uint32 flags, ...);
1243 
1244 	status_t status = get_image_symbol(image, "make_nth_translator",
1245 		B_SYMBOL_TYPE_TEXT, (void**)&makeNthTranslator);
1246 	if (status < B_OK) {
1247 		// If this is a translator add-on, it is in the C format
1248 		translator_data translatorData;
1249 		status = fPrivate->GetTranslatorData(image, translatorData);
1250 	}
1251 
1252 	unload_add_on(image);
1253 	return status == B_OK;
1254 }
1255 
1256 
1257 /*!
1258 	This function determines which translator is best suited
1259 	to convert the data from \a source.
1260 
1261 	\param source the data to be identified
1262 	\param ioExtension the configuration data for the translator
1263 	\param _info the information about the chosen translator is put here
1264 	\param hintType a hint about the type of data that is in \a source, set
1265 		it to zero if the type is not known
1266 	\param hintMIME a hint about the MIME type of \a source, set it to NULL
1267 		if the type is not known.
1268 	\param wantType the desired output type - if zero, any type is okay.
1269 
1270 	\return B_OK, identification of \a source was successful,
1271 		B_NO_TRANSLATOR, no appropriate translator found,
1272 		and other errors from accessing the source stream
1273 */
1274 status_t
1275 BTranslatorRoster::Identify(BPositionIO* source, BMessage* ioExtension,
1276 	translator_info* _info, uint32 hintType, const char* hintMIME,
1277 	uint32 wantType)
1278 {
1279 	if (source == NULL || _info == NULL)
1280 		return B_BAD_VALUE;
1281 
1282 	return fPrivate->Identify(source, ioExtension, hintType, hintMIME, wantType, _info);
1283 }
1284 
1285 
1286 /*!
1287 	Finds all translators capable of handling the data in \a source
1288 	and puts them into the outInfo array (which you must delete
1289 	yourself when you are done with it). Specifying a value for
1290 	\a hintType, \a hintMIME and/or \a wantType causes only the
1291 	translators that satisfy them to be included in the outInfo.
1292 
1293 	\param source the data to be translated
1294 	\param ioExtension the configuration data for the translator
1295 	\param _info, the array of acceptable translators is stored here if
1296 		the function succeeds. It's the caller's responsibility to free
1297 		the array using delete[].
1298 	\param _numInfo, number of entries in the \a _info array
1299 	\param hintType a hint about the type of data that is in \a source, set
1300 		it to zero if the type is not known
1301 	\param hintMIME a hint about the MIME type of \a source, set it to NULL
1302 		if the type is not known.
1303 	\param wantType the desired output type - if zero, any type is okay.
1304 
1305 	\return B_OK, successfully indentified the data in \a source
1306 		B_NO_TRANSLATOR, no translator could handle \a source
1307 		other errors, problems using \a source
1308 */
1309 status_t
1310 BTranslatorRoster::GetTranslators(BPositionIO* source, BMessage* ioExtension,
1311 	translator_info** _info, int32* _numInfo, uint32 hintType,
1312 	const char* hintMIME, uint32 wantType)
1313 {
1314 	if (source == NULL || _info == NULL || _numInfo == NULL)
1315 		return B_BAD_VALUE;
1316 
1317 	return fPrivate->GetTranslators(source, ioExtension, hintType, hintMIME,
1318 		wantType, _info, _numInfo);
1319 }
1320 
1321 
1322 /*!
1323 	Returns an array in \a _ids of all of the translators stored by this
1324 	object.
1325 	You must free the array using delete[] when you are done with it.
1326 
1327 	\param _ids the array is stored there (you own the array).
1328 	\param _count number of IDs in the array.
1329 */
1330 status_t
1331 BTranslatorRoster::GetAllTranslators(translator_id** _ids, int32* _count)
1332 {
1333 	if (_ids == NULL || _count == NULL)
1334 		return B_BAD_VALUE;
1335 
1336 	return fPrivate->GetAllTranslators(_ids, _count);
1337 }
1338 
1339 
1340 /*!
1341 	Returns information about the translator with the specified
1342 	translator \a id.
1343 	You must not free any of the data you get back.
1344 
1345 	\param id identifies which translator you want info for
1346 	\param _name the translator name is put here
1347 	\param _info the translator description is put here
1348 	\param _version the translation version is put here
1349 
1350 	\return B_OK if successful,
1351 		B_BAD_VALUE, if all parameters are NULL
1352 		B_NO_TRANSLATOR, \id didn't identify an existing translator
1353 */
1354 status_t
1355 BTranslatorRoster::GetTranslatorInfo(translator_id id, const char** _name,
1356 	const char** _info, int32* _version)
1357 {
1358 	if (_name == NULL && _info == NULL && _version == NULL)
1359 		return B_BAD_VALUE;
1360 
1361 	BAutolock locker(fPrivate);
1362 
1363 	BTranslator* translator = fPrivate->FindTranslator(id);
1364 	if (translator == NULL)
1365 		return B_NO_TRANSLATOR;
1366 
1367 	if (_name)
1368 		*_name = translator->TranslatorName();
1369 	if (_info)
1370 		*_info = translator->TranslatorInfo();
1371 	if (_version)
1372 		*_version = translator->TranslatorVersion();
1373 
1374 	return B_OK;
1375 }
1376 
1377 
1378 /*!
1379 	Returns all of the input formats for the translator specified
1380 	by \a id.
1381 	You must not free any of the data you get back.
1382 
1383 	\param id identifies which translator you want the input formats for
1384 	\param _formats array of input formats
1385 	\param _numFormats number of formats in the array
1386 
1387 	\return B_OK if successful,
1388 		B_BAD_VALUE, if any parameter is NULL
1389 		B_NO_TRANSLATOR, \id didn't identify an existing translator
1390 */
1391 status_t
1392 BTranslatorRoster::GetInputFormats(translator_id id,
1393 	const translation_format** _formats, int32* _numFormats)
1394 {
1395 	if (_formats == NULL || _numFormats == NULL)
1396 		return B_BAD_VALUE;
1397 
1398 	BAutolock locker(fPrivate);
1399 
1400 	BTranslator* translator = fPrivate->FindTranslator(id);
1401 	if (translator == NULL)
1402 		return B_NO_TRANSLATOR;
1403 
1404 	*_formats = translator->InputFormats(_numFormats);
1405 	return B_OK;
1406 }
1407 
1408 
1409 /*!
1410 	Returns all of the output formats for the translator specified
1411 	by \a id.
1412 	You must not free any of the data you get back.
1413 
1414 	\param id identifies which translator you want the output formats for
1415 	\param _formats array of output formats
1416 	\param _numFormats number of formats in the array
1417 
1418 	\return B_OK if successful,
1419 		B_BAD_VALUE, if any parameter is NULL
1420 		B_NO_TRANSLATOR, \id didn't identify an existing translator
1421 */
1422 status_t
1423 BTranslatorRoster::GetOutputFormats(translator_id id,
1424 	const translation_format** _formats, int32* _numFormats)
1425 {
1426 	if (_formats == NULL || _numFormats == NULL)
1427 		return B_BAD_VALUE;
1428 
1429 	BAutolock locker(fPrivate);
1430 
1431 	BTranslator* translator = fPrivate->FindTranslator(id);
1432 	if (translator == NULL)
1433 		return B_NO_TRANSLATOR;
1434 
1435 	*_formats = translator->OutputFormats(_numFormats);
1436 	return B_OK;
1437 }
1438 
1439 
1440 /*!
1441 	This function is the whole point of the Translation Kit.
1442 	This is for translating the data in \a source to \a destination
1443 	using the format \a wantOutType.
1444 
1445 	\param source the data to be translated
1446 	\param ioExtension the configuration data for the translator
1447 	\param info information about translator to use (can be NULL, in which
1448 		case the \a source is identified first)
1449 	\param destination where \a source is translated to
1450 	\param hintType a hint about the type of data that is in \a source, set
1451 		it to zero if the type is not known
1452 	\param hintMIME a hint about the MIME type of \a source, set it to NULL
1453 		if the type is not known.
1454 	\param wantType the desired output type - if zero, any type is okay.
1455 
1456 	\return B_OK, translation of \a source was successful,
1457 		B_NO_TRANSLATOR, no appropriate translator found,
1458 		and other errors from accessing the source and destination streams
1459 */
1460 status_t
1461 BTranslatorRoster::Translate(BPositionIO* source, const translator_info* info,
1462 	BMessage* ioExtension, BPositionIO* destination, uint32 wantOutType,
1463 	uint32 hintType, const char* hintMIME)
1464 {
1465 	if (source == NULL || destination == NULL)
1466 		return B_BAD_VALUE;
1467 
1468 	translator_info infoBuffer;
1469 
1470 	if (info == NULL) {
1471 		// look for a suitable translator
1472 		status_t status = fPrivate->Identify(source, ioExtension, hintType,
1473 			hintMIME, wantOutType, &infoBuffer);
1474 		if (status < B_OK)
1475 			return status;
1476 
1477 		info = &infoBuffer;
1478 	}
1479 
1480 	if (!fPrivate->Lock())
1481 		return B_ERROR;
1482 
1483 	BTranslator* translator = fPrivate->FindTranslator(info->translator);
1484 	if (translator != NULL) {
1485 		translator->Acquire();
1486 			// make sure this translator is not removed while we're playing with it;
1487 			// translating shouldn't be serialized!
1488 	}
1489 
1490 	fPrivate->Unlock();
1491 
1492 	if (translator == NULL)
1493 		return B_NO_TRANSLATOR;
1494 
1495 	status_t status = source->Seek(0, SEEK_SET);
1496 	if (status == B_OK) {
1497 		status = translator->Translate(source, info, ioExtension, wantOutType,
1498 			destination);
1499 	}
1500 	translator->Release();
1501 
1502 	return status;
1503 }
1504 
1505 
1506 /*!
1507 	This function is the whole point of the Translation Kit.
1508 	This is for translating the data in \a source to \a destination
1509 	using the format \a wantOutType and the translator identified
1510 	by \a id.
1511 
1512 	\param id the translator to be used
1513 	\param source the data to be translated
1514 	\param ioExtension the configuration data for the translator
1515 	\param destination where \a source is translated to
1516 	\param wantType the desired output type - if zero, any type is okay.
1517 
1518 	\return B_OK, translation of \a source was successful,
1519 		B_NO_TRANSLATOR, no appropriate translator found,
1520 		and other errors from accessing the source and destination streams
1521 */
1522 status_t
1523 BTranslatorRoster::Translate(translator_id id, BPositionIO* source,
1524 	BMessage* ioExtension, BPositionIO* destination, uint32 wantOutType)
1525 {
1526 	if (source == NULL || destination == NULL)
1527 		return B_BAD_VALUE;
1528 
1529 	BTranslator* translator = fPrivate->FindTranslator(id);
1530 	if (translator != NULL) {
1531 		translator->Acquire();
1532 			// make sure this translator is not removed while we're playing with it;
1533 			// translating shouldn't be serialized!
1534 	}
1535 
1536 	fPrivate->Unlock();
1537 
1538 	if (translator == NULL)
1539 		return B_NO_TRANSLATOR;
1540 
1541 	status_t status = source->Seek(0, SEEK_SET);
1542 	if (status == B_OK) {
1543 		translator_info info;
1544 		status = translator->Identify(source, NULL, ioExtension, &info, wantOutType);
1545 		if (status >= B_OK) {
1546 			status = translator->Translate(source, &info, ioExtension, wantOutType,
1547 				destination);
1548 		}
1549 	}
1550 	translator->Release();
1551 
1552 	return status;
1553 }
1554 
1555 
1556 /*!
1557 	Creates a BView in \a _view for configuring the translator specified
1558 	by \a id. Not all translators support this, though.
1559 
1560 	\param id identifies which translator you want the input formats for
1561 	\param ioExtension the configuration data for the translator
1562 	\param _view the view for configuring the translator
1563 	\param _extent the bounds for the (resizable) view
1564 
1565 	\return B_OK if successful,
1566 		B_BAD_VALUE, if any parameter is NULL
1567 		B_NO_TRANSLATOR, \id didn't identify an existing translator
1568 */
1569 status_t
1570 BTranslatorRoster::MakeConfigurationView(translator_id id, BMessage* ioExtension,
1571 	BView** _view, BRect* _extent)
1572 {
1573 	if (_view == NULL || _extent == NULL)
1574 		return B_BAD_VALUE;
1575 
1576 	BAutolock locker(fPrivate);
1577 
1578 	BTranslator* translator = fPrivate->FindTranslator(id);
1579 	if (translator == NULL)
1580 		return B_NO_TRANSLATOR;
1581 
1582 	return translator->MakeConfigurationView(ioExtension, _view, _extent);
1583 }
1584 
1585 
1586 /*!
1587 	Gets the configuration setttings for the translator
1588 	specified by \a id and puts them into \a ioExtension.
1589 
1590 	\param id identifies which translator you want the input formats for
1591 	\param ioExtension the configuration data for the translator
1592 
1593 	\return B_OK if successful,
1594 		B_BAD_VALUE, if \a ioExtension is NULL
1595 		B_NO_TRANSLATOR, \id didn't identify an existing translator
1596 */
1597 status_t
1598 BTranslatorRoster::GetConfigurationMessage(translator_id id, BMessage* ioExtension)
1599 {
1600 	if (!ioExtension)
1601 		return B_BAD_VALUE;
1602 
1603 	BAutolock locker(fPrivate);
1604 
1605 	BTranslator* translator = fPrivate->FindTranslator(id);
1606 	if (translator == NULL)
1607 		return B_NO_TRANSLATOR;
1608 
1609 	return translator->GetConfigurationMessage(ioExtension);
1610 }
1611 
1612 
1613 /*!
1614 	Gets the entry_ref for the given translator (of course, this works only
1615 	for disk based translators).
1616 
1617 	\param id identifies which translator you want the input formats for
1618 	\param ref the entry ref is stored there
1619 
1620 	\return B_OK if successful,
1621 		B_ERROR, if this is not a disk based translator
1622 		B_BAD_VALUE, if \a ref is NULL
1623 		B_NO_TRANSLATOR, \id didn't identify an existing translator
1624 */
1625 status_t
1626 BTranslatorRoster::GetRefFor(translator_id id, entry_ref* ref)
1627 {
1628 	if (ref == NULL)
1629 		return B_BAD_VALUE;
1630 
1631 	return fPrivate->GetRefFor(id, *ref);
1632 }
1633 
1634 
1635 status_t
1636 BTranslatorRoster::StartWatching(BMessenger target)
1637 {
1638 	return fPrivate->StartWatching(target);
1639 }
1640 
1641 
1642 status_t
1643 BTranslatorRoster::StopWatching(BMessenger target)
1644 {
1645 	return fPrivate->StopWatching(target);
1646 }
1647 
1648 
1649 //	#pragma mark - private
1650 
1651 
1652 BTranslatorRoster::BTranslatorRoster(const BTranslatorRoster &other)
1653 {
1654 }
1655 
1656 
1657 BTranslatorRoster &
1658 BTranslatorRoster::operator=(const BTranslatorRoster &tr)
1659 {
1660 	return *this;
1661 }
1662 
1663 
1664 const char *
1665 Version__17BTranslatorRosterPlT1l(int32 *outCurVersion, int32 *outMinVersion,
1666 	int32 inAppVersion)
1667 {
1668 	if (!outCurVersion || !outMinVersion)
1669 		return "";
1670 
1671 	static char vString[50];
1672 	static char vDate[] = __DATE__;
1673 	if (!vString[0]) {
1674 		sprintf(vString, "Translation Kit v%d.%d.%d %s\n",
1675 			static_cast<int>(B_TRANSLATION_MAJOR_VERSION(B_TRANSLATION_CURRENT_VERSION)),
1676 			static_cast<int>(B_TRANSLATION_MINOR_VERSION(B_TRANSLATION_CURRENT_VERSION)),
1677 			static_cast<int>(B_TRANSLATION_REVISION_VERSION(B_TRANSLATION_CURRENT_VERSION)),
1678 			vDate);
1679 	}
1680 	*outCurVersion = B_TRANSLATION_CURRENT_VERSION;
1681 	*outMinVersion = B_TRANSLATION_MIN_VERSION;
1682 	return vString;
1683 }
1684 
1685 
1686 void BTranslatorRoster::ReservedTranslatorRoster1() {}
1687 void BTranslatorRoster::ReservedTranslatorRoster2() {}
1688 void BTranslatorRoster::ReservedTranslatorRoster3() {}
1689 void BTranslatorRoster::ReservedTranslatorRoster4() {}
1690 void BTranslatorRoster::ReservedTranslatorRoster5() {}
1691 void BTranslatorRoster::ReservedTranslatorRoster6() {}
1692