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