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