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