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
41namespace BPrivate {
42
43class QuarantineTranslatorImage {
44public:
45								QuarantineTranslatorImage(
46									BTranslatorRoster::Private& privateRoster);
47								~QuarantineTranslatorImage();
48
49			void				Put(const entry_ref& ref);
50			void				Remove();
51
52private:
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
61char B_TRANSLATOR_EXT_HEADER_ONLY[]			= "/headerOnly";
62char B_TRANSLATOR_EXT_DATA_ONLY[]			= "/dataOnly";
63char B_TRANSLATOR_EXT_COMMENT[]				= "/comment";
64char B_TRANSLATOR_EXT_TIME[]				= "/time";
65char B_TRANSLATOR_EXT_FRAME[]				= "/frame";
66char B_TRANSLATOR_EXT_BITMAP_RECT[]			= "bits/Rect";
67char B_TRANSLATOR_EXT_BITMAP_COLOR_SPACE[]	= "bits/space";
68char B_TRANSLATOR_EXT_BITMAP_PALETTE[]		= "bits/palette";
69char B_TRANSLATOR_EXT_SOUND_CHANNEL[]		= "nois/channel";
70char B_TRANSLATOR_EXT_SOUND_MONO[]			= "nois/mono";
71char B_TRANSLATOR_EXT_SOUND_MARKER[]		= "nois/marker";
72char B_TRANSLATOR_EXT_SOUND_LOOP[]			= "nois/loop";
73
74BTranslatorRoster* BTranslatorRoster::sDefaultRoster = NULL;
75
76
77namespace 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*/
89QuarantineTranslatorImage::QuarantineTranslatorImage(
90	BTranslatorRoster::Private& privateRoster)
91	:
92	fRoster(privateRoster),
93	fRemove(false)
94{
95}
96
97
98QuarantineTranslatorImage::~QuarantineTranslatorImage()
99{
100	if (fRef.device == -1 || !fRemove)
101		return;
102
103	fRoster.RemoveTranslators(fRef);
104}
105
106
107void
108QuarantineTranslatorImage::Put(const entry_ref& ref)
109{
110	fRef = ref;
111}
112
113
114void
115QuarantineTranslatorImage::Remove()
116{
117	fRemove = true;
118}
119
120}	// namespace BPrivate
121
122
123//	#pragma mark -
124
125
126BTranslatorRoster::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
179BTranslatorRoster::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
214void
215BTranslatorRoster::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
339void
340BTranslatorRoster::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*/
370status_t
371BTranslatorRoster::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*/
415status_t
416BTranslatorRoster::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
472status_t
473BTranslatorRoster::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
497void
498BTranslatorRoster::Private::RemoveTranslators(entry_ref& ref)
499{
500	_RemoveTranslators(NULL, &ref);
501}
502
503
504BTranslator*
505BTranslatorRoster::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
520status_t
521BTranslatorRoster::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
559status_t
560BTranslatorRoster::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
654status_t
655BTranslatorRoster::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
675status_t
676BTranslatorRoster::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
696status_t
697BTranslatorRoster::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
716status_t
717BTranslatorRoster::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
770status_t
771BTranslatorRoster::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
822status_t
823BTranslatorRoster::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
849status_t
850BTranslatorRoster::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
868void
869BTranslatorRoster::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
891BTranslatorRoster::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*/
917void
918BTranslatorRoster::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*/
934const translation_format*
935BTranslatorRoster::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
959const translator_item*
960BTranslatorRoster::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
970const translator_item*
971BTranslatorRoster::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
990const translator_item*
991BTranslatorRoster::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
1010translator_item*
1011BTranslatorRoster::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*/
1037int32
1038BTranslatorRoster::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
1066bool
1067BTranslatorRoster::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
1082void
1083BTranslatorRoster::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
1092	while (iterator != fTranslators.end()) {
1093		TranslatorMap::iterator next = iterator;
1094		next++;
1095
1096		const translator_item& item = iterator->second;
1097		if ((ref != NULL && item.ref == *ref)
1098			|| (nodeRef != NULL && item.ref.device == nodeRef->device
1099				&& item.node == nodeRef->node)) {
1100			item.translator->Release();
1101			update.AddInt32("translator_id", iterator->first);
1102
1103			fTranslators.erase(iterator);
1104		}
1105
1106		iterator = next;
1107	}
1108
1109	_NotifyListeners(update);
1110}
1111
1112
1113void
1114BTranslatorRoster::Private::_EntryAdded(const node_ref& nodeRef,
1115	const char* name)
1116{
1117	entry_ref ref;
1118	ref.device = nodeRef.device;
1119	ref.directory = nodeRef.node;
1120	ref.set_name(name);
1121
1122	_EntryAdded(ref);
1123}
1124
1125
1126/*!
1127	In lazy mode, the entry is marked to be rescanned on next use of any
1128	translation method (that could make use of it).
1129	In non-lazy mode, the translators for this entry are created directly
1130	and listeners notified.
1131
1132	Called by the node monitor handling.
1133*/
1134void
1135BTranslatorRoster::Private::_EntryAdded(const entry_ref& ref)
1136{
1137	BEntry entry;
1138	if (entry.SetTo(&ref) != B_OK || !entry.IsFile())
1139		return;
1140
1141	if (fLazyScanning) {
1142		fRescanEntries.insert(ref);
1143		return;
1144	}
1145
1146	BMessage update(B_TRANSLATOR_ADDED);
1147	int32 count = 0;
1148	CreateTranslators(ref, count, &update);
1149
1150	_NotifyListeners(update);
1151}
1152
1153
1154void
1155BTranslatorRoster::Private::_NotifyListeners(BMessage& update) const
1156{
1157	MessengerList::const_iterator iterator = fMessengers.begin();
1158
1159	while (iterator != fMessengers.end()) {
1160		(*iterator).SendMessage(&update);
1161		iterator++;
1162	}
1163}
1164
1165
1166//	#pragma mark -
1167
1168
1169BTranslatorReleaseDelegate::BTranslatorReleaseDelegate(BTranslator* translator)
1170	:
1171	fUnderlying(translator)
1172{
1173}
1174
1175
1176void
1177BTranslatorReleaseDelegate::Release()
1178{
1179	fUnderlying->Release();
1180	// ReleaseDelegate is only allowed to release a translator once.
1181	delete this;
1182}
1183
1184
1185//	#pragma mark -
1186
1187
1188BTranslatorRoster::BTranslatorRoster()
1189{
1190	_Initialize();
1191}
1192
1193
1194BTranslatorRoster::BTranslatorRoster(BMessage* model)
1195{
1196	_Initialize();
1197
1198	if (model) {
1199		const char* path;
1200		for (int32 i = 0;
1201			model->FindString("be:translator_path", i, &path) == B_OK; i++) {
1202			BEntry entry(path);
1203			entry_ref ref;
1204			if (entry.GetRef(&ref) == B_OK) {
1205				int32 count = 0;
1206				fPrivate->CreateTranslators(ref, count);
1207			}
1208		}
1209	}
1210}
1211
1212
1213BTranslatorRoster::~BTranslatorRoster()
1214{
1215	// If the default BTranslatorRoster is being
1216	// deleted, set the pointer to the default
1217	// BTranslatorRoster to NULL
1218	if (sDefaultRoster == this)
1219		sDefaultRoster = NULL;
1220
1221	delete fPrivate;
1222}
1223
1224
1225void
1226BTranslatorRoster::_Initialize()
1227{
1228	fPrivate = new BTranslatorRoster::Private();
1229}
1230
1231
1232status_t
1233BTranslatorRoster::Archive(BMessage* into, bool deep) const
1234{
1235	status_t status = BArchivable::Archive(into, deep);
1236	if (status != B_OK)
1237		return status;
1238
1239	return fPrivate->StoreTranslators(*into);
1240}
1241
1242
1243BArchivable*
1244BTranslatorRoster::Instantiate(BMessage* from)
1245{
1246	if (!from || !validate_instantiation(from, "BTranslatorRoster"))
1247		return NULL;
1248
1249	return new BTranslatorRoster(from);
1250}
1251
1252
1253BTranslatorRoster*
1254BTranslatorRoster::Default()
1255{
1256	static int32 lock = 0;
1257
1258	if (sDefaultRoster != NULL)
1259		return sDefaultRoster;
1260
1261	if (atomic_add(&lock, 1) != 0) {
1262		// Just wait for the default translator to be instantiated
1263		while (sDefaultRoster == NULL)
1264			snooze(10000);
1265
1266		atomic_add(&lock, -1);
1267		return sDefaultRoster;
1268	}
1269
1270	// If the default translators have not been loaded,
1271	// create a new BTranslatorRoster for them, and load them.
1272	if (sDefaultRoster == NULL) {
1273		BTranslatorRoster* roster = new BTranslatorRoster();
1274		roster->AddTranslators(NULL);
1275
1276		sDefaultRoster = roster;
1277			// this will unlock any other threads waiting for
1278			// the default roster to become available
1279	}
1280
1281	atomic_add(&lock, -1);
1282	return sDefaultRoster;
1283}
1284
1285
1286/*!
1287	This function takes a string of colon delimited paths, and adds
1288	the translators from those paths to this BTranslatorRoster.
1289
1290	If load_path is NULL, it parses the environment variable
1291	TRANSLATORS. If that does not exist, it uses the system paths:
1292		/boot/home/config/add-ons/Translators,
1293		/system/add-ons/Translators.
1294*/
1295status_t
1296BTranslatorRoster::AddTranslators(const char* path)
1297{
1298	if (path == NULL)
1299		path = getenv("TRANSLATORS");
1300	if (path == NULL) {
1301		fPrivate->AddDefaultPaths();
1302		return B_OK;
1303	}
1304
1305	return fPrivate->AddPaths(path);
1306}
1307
1308
1309/*!
1310	Adds a BTranslator based object to the BTranslatorRoster.
1311	When you add a BTranslator roster, it is Acquire()'d by
1312	BTranslatorRoster; it is Release()'d when the
1313	BTranslatorRoster is deleted.
1314
1315	\param translator the translator to be added to the
1316		BTranslatorRoster
1317
1318	\return B_BAD_VALUE, if translator is NULL,
1319		B_OK if all went well
1320*/
1321status_t
1322BTranslatorRoster::AddTranslator(BTranslator* translator)
1323{
1324	if (!translator)
1325		return B_BAD_VALUE;
1326
1327	return fPrivate->AddTranslator(translator);
1328}
1329
1330
1331bool
1332BTranslatorRoster::IsTranslator(entry_ref* ref)
1333{
1334	if (ref == NULL)
1335		return false;
1336
1337	BPath path(ref);
1338	image_id image = load_add_on(path.Path());
1339	if (image < B_OK)
1340		return false;
1341
1342	// Function pointer used to create post R4.5 style translators
1343	BTranslator* (*makeNthTranslator)(int32 n, image_id you, uint32 flags, ...);
1344
1345	status_t status = get_image_symbol(image, "make_nth_translator",
1346		B_SYMBOL_TYPE_TEXT, (void**)&makeNthTranslator);
1347	if (status < B_OK) {
1348		// If this is a translator add-on, it is in the C format
1349		translator_data translatorData;
1350		status = fPrivate->GetTranslatorData(image, translatorData);
1351	}
1352
1353	unload_add_on(image);
1354	return status == B_OK;
1355}
1356
1357
1358/*!
1359	This function determines which translator is best suited
1360	to convert the data from \a source.
1361
1362	\param source the data to be identified
1363	\param ioExtension the configuration data for the translator
1364	\param _info the information about the chosen translator is put here
1365	\param hintType a hint about the type of data that is in \a source, set
1366		it to zero if the type is not known
1367	\param hintMIME a hint about the MIME type of \a source, set it to NULL
1368		if the type is not known.
1369	\param wantType the desired output type - if zero, any type is okay.
1370
1371	\return B_OK, identification of \a source was successful,
1372		B_NO_TRANSLATOR, no appropriate translator found,
1373		and other errors from accessing the source stream
1374*/
1375status_t
1376BTranslatorRoster::Identify(BPositionIO* source, BMessage* ioExtension,
1377	translator_info* _info, uint32 hintType, const char* hintMIME,
1378	uint32 wantType)
1379{
1380	if (source == NULL || _info == NULL)
1381		return B_BAD_VALUE;
1382
1383	return fPrivate->Identify(source, ioExtension, hintType, hintMIME, wantType,
1384		_info);
1385}
1386
1387
1388/*!
1389	Finds all translators capable of handling the data in \a source
1390	and puts them into the outInfo array (which you must delete
1391	yourself when you are done with it). Specifying a value for
1392	\a hintType, \a hintMIME and/or \a wantType causes only the
1393	translators that satisfy them to be included in the outInfo.
1394
1395	\param source the data to be translated
1396	\param ioExtension the configuration data for the translator
1397	\param _info, the array of acceptable translators is stored here if
1398		the function succeeds. It's the caller's responsibility to free
1399		the array using delete[].
1400	\param _numInfo, number of entries in the \a _info array
1401	\param hintType a hint about the type of data that is in \a source, set
1402		it to zero if the type is not known
1403	\param hintMIME a hint about the MIME type of \a source, set it to NULL
1404		if the type is not known.
1405	\param wantType the desired output type - if zero, any type is okay.
1406
1407	\return B_OK, successfully indentified the data in \a source
1408		B_NO_TRANSLATOR, no translator could handle \a source
1409		other errors, problems using \a source
1410*/
1411status_t
1412BTranslatorRoster::GetTranslators(BPositionIO* source, BMessage* ioExtension,
1413	translator_info** _info, int32* _numInfo, uint32 hintType,
1414	const char* hintMIME, uint32 wantType)
1415{
1416	if (source == NULL || _info == NULL || _numInfo == NULL)
1417		return B_BAD_VALUE;
1418
1419	return fPrivate->GetTranslators(source, ioExtension, hintType, hintMIME,
1420		wantType, _info, _numInfo);
1421}
1422
1423
1424/*!
1425	Returns an array in \a _ids of all of the translators stored by this
1426	object.
1427	You must free the array using delete[] when you are done with it.
1428
1429	\param _ids the array is stored there (you own the array).
1430	\param _count number of IDs in the array.
1431*/
1432status_t
1433BTranslatorRoster::GetAllTranslators(translator_id** _ids, int32* _count)
1434{
1435	if (_ids == NULL || _count == NULL)
1436		return B_BAD_VALUE;
1437
1438	return fPrivate->GetAllTranslators(_ids, _count);
1439}
1440
1441
1442/*!
1443	Returns information about the translator with the specified
1444	translator \a id.
1445	You must not free any of the data you get back.
1446
1447	\param id identifies which translator you want info for
1448	\param _name the translator name is put here
1449	\param _info the translator description is put here
1450	\param _version the translation version is put here
1451
1452	\return B_OK if successful,
1453		B_BAD_VALUE, if all parameters are NULL
1454		B_NO_TRANSLATOR, \id didn't identify an existing translator
1455*/
1456status_t
1457BTranslatorRoster::GetTranslatorInfo(translator_id id, const char** _name,
1458	const char** _info, int32* _version)
1459{
1460	if (_name == NULL && _info == NULL && _version == NULL)
1461		return B_BAD_VALUE;
1462
1463	BAutolock locker(fPrivate);
1464
1465	BTranslator* translator = fPrivate->FindTranslator(id);
1466	if (translator == NULL)
1467		return B_NO_TRANSLATOR;
1468
1469	if (_name)
1470		*_name = translator->TranslatorName();
1471	if (_info)
1472		*_info = translator->TranslatorInfo();
1473	if (_version)
1474		*_version = translator->TranslatorVersion();
1475
1476	return B_OK;
1477}
1478
1479
1480/*!
1481	Returns all of the input formats for the translator specified
1482	by \a id.
1483	You must not free any of the data you get back.
1484
1485	\param id identifies which translator you want the input formats for
1486	\param _formats array of input formats
1487	\param _numFormats number of formats in the array
1488
1489	\return B_OK if successful,
1490		B_BAD_VALUE, if any parameter is NULL
1491		B_NO_TRANSLATOR, \id didn't identify an existing translator
1492*/
1493status_t
1494BTranslatorRoster::GetInputFormats(translator_id id,
1495	const translation_format** _formats, int32* _numFormats)
1496{
1497	if (_formats == NULL || _numFormats == NULL)
1498		return B_BAD_VALUE;
1499
1500	BAutolock locker(fPrivate);
1501
1502	BTranslator* translator = fPrivate->FindTranslator(id);
1503	if (translator == NULL)
1504		return B_NO_TRANSLATOR;
1505
1506	*_formats = translator->InputFormats(_numFormats);
1507	return B_OK;
1508}
1509
1510
1511/*!
1512	Returns all of the output formats for the translator specified
1513	by \a id.
1514	You must not free any of the data you get back.
1515
1516	\param id identifies which translator you want the output formats for
1517	\param _formats array of output formats
1518	\param _numFormats number of formats in the array
1519
1520	\return B_OK if successful,
1521		B_BAD_VALUE, if any parameter is NULL
1522		B_NO_TRANSLATOR, \id didn't identify an existing translator
1523*/
1524status_t
1525BTranslatorRoster::GetOutputFormats(translator_id id,
1526	const translation_format** _formats, int32* _numFormats)
1527{
1528	if (_formats == NULL || _numFormats == NULL)
1529		return B_BAD_VALUE;
1530
1531	BAutolock locker(fPrivate);
1532
1533	BTranslator* translator = fPrivate->FindTranslator(id);
1534	if (translator == NULL)
1535		return B_NO_TRANSLATOR;
1536
1537	*_formats = translator->OutputFormats(_numFormats);
1538	return B_OK;
1539}
1540
1541
1542/*!
1543	This function is the whole point of the Translation Kit.
1544	This is for translating the data in \a source to \a destination
1545	using the format \a wantOutType.
1546
1547	\param source the data to be translated
1548	\param ioExtension the configuration data for the translator
1549	\param info information about translator to use (can be NULL, in which
1550		case the \a source is identified first)
1551	\param destination where \a source is translated to
1552	\param hintType a hint about the type of data that is in \a source, set
1553		it to zero if the type is not known
1554	\param hintMIME a hint about the MIME type of \a source, set it to NULL
1555		if the type is not known.
1556	\param wantType the desired output type - if zero, any type is okay.
1557
1558	\return B_OK, translation of \a source was successful,
1559		B_NO_TRANSLATOR, no appropriate translator found,
1560		and other errors from accessing the source and destination streams
1561*/
1562status_t
1563BTranslatorRoster::Translate(BPositionIO* source, const translator_info* info,
1564	BMessage* ioExtension, BPositionIO* destination, uint32 wantOutType,
1565	uint32 hintType, const char* hintMIME)
1566{
1567	if (source == NULL || destination == NULL)
1568		return B_BAD_VALUE;
1569
1570	translator_info infoBuffer;
1571
1572	if (info == NULL) {
1573		// look for a suitable translator
1574		status_t status = fPrivate->Identify(source, ioExtension, hintType,
1575			hintMIME, wantOutType, &infoBuffer);
1576		if (status < B_OK)
1577			return status;
1578
1579		info = &infoBuffer;
1580	}
1581
1582	if (!fPrivate->Lock())
1583		return B_ERROR;
1584
1585	BTranslator* translator = fPrivate->FindTranslator(info->translator);
1586	if (translator != NULL) {
1587		translator->Acquire();
1588			// make sure this translator is not removed while we're playing with
1589			// it; translating shouldn't be serialized!
1590	}
1591
1592	fPrivate->Unlock();
1593
1594	if (translator == NULL)
1595		return B_NO_TRANSLATOR;
1596
1597	status_t status = B_OK;
1598	off_t pos = source->Seek(0, SEEK_SET);
1599	if (pos != 0)
1600		status = pos < 0 ? (status_t)pos : B_IO_ERROR;
1601	if (status == B_OK) {
1602		status = translator->Translate(source, info, ioExtension, wantOutType,
1603			destination);
1604	}
1605	translator->Release();
1606
1607	return status;
1608}
1609
1610
1611/*!
1612	This function is the whole point of the Translation Kit.
1613	This is for translating the data in \a source to \a destination
1614	using the format \a wantOutType and the translator identified
1615	by \a id.
1616
1617	\param id the translator to be used
1618	\param source the data to be translated
1619	\param ioExtension the configuration data for the translator
1620	\param destination where \a source is translated to
1621	\param wantType the desired output type - if zero, any type is okay.
1622
1623	\return B_OK, translation of \a source was successful,
1624		B_NO_TRANSLATOR, no appropriate translator found,
1625		and other errors from accessing the source and destination streams
1626*/
1627status_t
1628BTranslatorRoster::Translate(translator_id id, BPositionIO* source,
1629	BMessage* ioExtension, BPositionIO* destination, uint32 wantOutType)
1630{
1631	if (source == NULL || destination == NULL)
1632		return B_BAD_VALUE;
1633
1634	if (!fPrivate->Lock())
1635		return B_ERROR;
1636
1637	BTranslator* translator = fPrivate->FindTranslator(id);
1638	if (translator != NULL) {
1639		translator->Acquire();
1640			// make sure this translator is not removed while we're playing with
1641			// it; translating shouldn't be serialized!
1642	}
1643
1644	fPrivate->Unlock();
1645
1646	if (translator == NULL)
1647		return B_NO_TRANSLATOR;
1648
1649	status_t status;
1650	off_t pos = source->Seek(0, SEEK_SET);
1651	if (pos == 0) {
1652		translator_info info;
1653		status = translator->Identify(source, NULL, ioExtension, &info,
1654			wantOutType);
1655		if (status >= B_OK) {
1656			off_t pos = source->Seek(0, SEEK_SET);
1657			if (pos != 0)
1658				status = pos < 0 ? (status_t)pos : B_IO_ERROR;
1659			else {
1660				status = translator->Translate(source, &info, ioExtension,
1661					wantOutType, destination);
1662			}
1663		}
1664	} else
1665		status = pos < 0 ? (status_t)pos : B_IO_ERROR;
1666	translator->Release();
1667
1668	return status;
1669}
1670
1671
1672/*!
1673	Creates a BView in \a _view for configuring the translator specified
1674	by \a id. Not all translators support this, though.
1675
1676	\param id identifies which translator you want the input formats for
1677	\param ioExtension the configuration data for the translator
1678	\param _view the view for configuring the translator
1679	\param _extent the bounds for the (resizable) view
1680
1681	\return B_OK if successful,
1682		B_BAD_VALUE, if any parameter is NULL
1683		B_NO_TRANSLATOR, \id didn't identify an existing translator
1684*/
1685status_t
1686BTranslatorRoster::MakeConfigurationView(translator_id id,
1687	BMessage* ioExtension, BView** _view, BRect* _extent)
1688{
1689	if (_view == NULL || _extent == NULL)
1690		return B_BAD_VALUE;
1691
1692	BAutolock locker(fPrivate);
1693
1694	BTranslator* translator = fPrivate->FindTranslator(id);
1695	if (translator == NULL)
1696		return B_NO_TRANSLATOR;
1697
1698	return translator->MakeConfigurationView(ioExtension, _view, _extent);
1699}
1700
1701
1702BTranslatorReleaseDelegate*
1703BTranslatorRoster::AcquireTranslator(int32 id)
1704{
1705	BAutolock locker(fPrivate);
1706
1707	BTranslator* translator = fPrivate->FindTranslator(id);
1708	if (translator == NULL)
1709		return NULL;
1710
1711	translator->Acquire();
1712	return new BTranslatorReleaseDelegate(translator);
1713}
1714
1715
1716/*!
1717	Gets the configuration setttings for the translator
1718	specified by \a id and puts them into \a ioExtension.
1719
1720	\param id identifies which translator you want the input formats for
1721	\param ioExtension the configuration data for the translator
1722
1723	\return B_OK if successful,
1724		B_BAD_VALUE, if \a ioExtension is NULL
1725		B_NO_TRANSLATOR, \id didn't identify an existing translator
1726*/
1727status_t
1728BTranslatorRoster::GetConfigurationMessage(translator_id id,
1729	BMessage* ioExtension)
1730{
1731	if (!ioExtension)
1732		return B_BAD_VALUE;
1733
1734	BAutolock locker(fPrivate);
1735
1736	BTranslator* translator = fPrivate->FindTranslator(id);
1737	if (translator == NULL)
1738		return B_NO_TRANSLATOR;
1739
1740	return translator->GetConfigurationMessage(ioExtension);
1741}
1742
1743
1744/*!
1745	Gets the entry_ref for the given translator (of course, this works only
1746	for disk based translators).
1747
1748	\param id identifies which translator you want the input formats for
1749	\param ref the entry ref is stored there
1750
1751	\return B_OK if successful,
1752		B_ERROR, if this is not a disk based translator
1753		B_BAD_VALUE, if \a ref is NULL
1754		B_NO_TRANSLATOR, \id didn't identify an existing translator
1755*/
1756status_t
1757BTranslatorRoster::GetRefFor(translator_id id, entry_ref* ref)
1758{
1759	if (ref == NULL)
1760		return B_BAD_VALUE;
1761
1762	return fPrivate->GetRefFor(id, *ref);
1763}
1764
1765
1766status_t
1767BTranslatorRoster::StartWatching(BMessenger target)
1768{
1769	return fPrivate->StartWatching(target);
1770}
1771
1772
1773status_t
1774BTranslatorRoster::StopWatching(BMessenger target)
1775{
1776	return fPrivate->StopWatching(target);
1777}
1778
1779
1780//	#pragma mark - private
1781
1782
1783BTranslatorRoster::BTranslatorRoster(const BTranslatorRoster &other)
1784{
1785}
1786
1787
1788BTranslatorRoster &
1789BTranslatorRoster::operator=(const BTranslatorRoster &tr)
1790{
1791	return *this;
1792}
1793
1794
1795#if __GNUC__ == 2	// gcc 2
1796
1797/*static*/ const char*
1798BTranslatorRoster::Version(int32* outCurVersion, int32* outMinVersion,
1799	int32 inAppVersion)
1800{
1801	if (!outCurVersion || !outMinVersion)
1802		return "";
1803
1804	static char vString[50];
1805	static char vDate[] = __DATE__;
1806	if (!vString[0]) {
1807		sprintf(vString, "Translation Kit v%d.%d.%d %s\n",
1808			int(B_TRANSLATION_MAJOR_VERSION(B_TRANSLATION_CURRENT_VERSION)),
1809			int(B_TRANSLATION_MINOR_VERSION(B_TRANSLATION_CURRENT_VERSION)),
1810			int(B_TRANSLATION_REVISION_VERSION(B_TRANSLATION_CURRENT_VERSION)),
1811			vDate);
1812	}
1813	*outCurVersion = B_TRANSLATION_CURRENT_VERSION;
1814	*outMinVersion = B_TRANSLATION_MIN_VERSION;
1815	return vString;
1816}
1817
1818#endif	// gcc 2
1819
1820
1821void BTranslatorRoster::ReservedTranslatorRoster1() {}
1822void BTranslatorRoster::ReservedTranslatorRoster2() {}
1823void BTranslatorRoster::ReservedTranslatorRoster3() {}
1824void BTranslatorRoster::ReservedTranslatorRoster4() {}
1825void BTranslatorRoster::ReservedTranslatorRoster5() {}
1826void BTranslatorRoster::ReservedTranslatorRoster6() {}
1827