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