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