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