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