1 /*
2 * Copyright 2002-2015, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Axel Dörfler, axeld@pinc-software.de
7 * Markus Himmel, markus@himmel-villmar.de
8 * Michael Wilber
9 */
10
11 /*!
12 This class is the guts of the translation kit, it makes the
13 whole thing happen. It bridges the applications using this
14 object with the translators that the apps need to access.
15 */
16
17 #include <TranslatorRoster.h>
18
19 #include <new>
20 #include <strings.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23
24 #include <Application.h>
25 #include <Autolock.h>
26 #include <Directory.h>
27 #include <FindDirectory.h>
28 #include <NodeMonitor.h>
29 #include <Path.h>
30 #include <String.h>
31
32 #include <driver_settings.h>
33 #include <image.h>
34 #include <safemode_defs.h>
35 #include <syscalls.h>
36
37 #include "FuncTranslator.h"
38 #include "TranslatorRosterPrivate.h"
39
40
41 namespace BPrivate {
42
43 class QuarantineTranslatorImage {
44 public:
45 QuarantineTranslatorImage(
46 BTranslatorRoster::Private& privateRoster);
47 ~QuarantineTranslatorImage();
48
49 void Put(const entry_ref& ref);
50 void Remove();
51
52 private:
53 BTranslatorRoster::Private& fRoster;
54 entry_ref fRef;
55 bool fRemove;
56 };
57
58 } // namespace BPrivate
59
60 // Extensions used in the extension BMessage, defined in TranslatorFormats.h
61 char B_TRANSLATOR_EXT_HEADER_ONLY[] = "/headerOnly";
62 char B_TRANSLATOR_EXT_DATA_ONLY[] = "/dataOnly";
63 char B_TRANSLATOR_EXT_COMMENT[] = "/comment";
64 char B_TRANSLATOR_EXT_TIME[] = "/time";
65 char B_TRANSLATOR_EXT_FRAME[] = "/frame";
66 char B_TRANSLATOR_EXT_BITMAP_RECT[] = "bits/Rect";
67 char B_TRANSLATOR_EXT_BITMAP_COLOR_SPACE[] = "bits/space";
68 char B_TRANSLATOR_EXT_BITMAP_PALETTE[] = "bits/palette";
69 char B_TRANSLATOR_EXT_SOUND_CHANNEL[] = "nois/channel";
70 char B_TRANSLATOR_EXT_SOUND_MONO[] = "nois/mono";
71 char B_TRANSLATOR_EXT_SOUND_MARKER[] = "nois/marker";
72 char B_TRANSLATOR_EXT_SOUND_LOOP[] = "nois/loop";
73
74 BTranslatorRoster* BTranslatorRoster::sDefaultRoster = NULL;
75
76
77 namespace BPrivate {
78
79 /*!
80 The purpose of this class is to put a translator entry_ref into - and remove
81 it from the list of translators on destruction (if Remove() was called
82 before).
83
84 This is used in Private::CreateTranslators() in case a translator hides a
85 previous one (ie. if you install a translator in the user's translators
86 directory that has the same name as one in the system's directory, it will
87 hide this entry).
88 */
QuarantineTranslatorImage(BTranslatorRoster::Private & privateRoster)89 QuarantineTranslatorImage::QuarantineTranslatorImage(
90 BTranslatorRoster::Private& privateRoster)
91 :
92 fRoster(privateRoster),
93 fRemove(false)
94 {
95 }
96
97
~QuarantineTranslatorImage()98 QuarantineTranslatorImage::~QuarantineTranslatorImage()
99 {
100 if (fRef.device == -1 || !fRemove)
101 return;
102
103 fRoster.RemoveTranslators(fRef);
104 }
105
106
107 void
Put(const entry_ref & ref)108 QuarantineTranslatorImage::Put(const entry_ref& ref)
109 {
110 fRef = ref;
111 }
112
113
114 void
Remove()115 QuarantineTranslatorImage::Remove()
116 {
117 fRemove = true;
118 }
119
120 } // namespace BPrivate
121
122
123 // #pragma mark -
124
125
Private()126 BTranslatorRoster::Private::Private()
127 :
128 BHandler("translator roster"),
129 BLocker("translator list"),
130 fABISubDirectory(NULL),
131 fNextID(1),
132 fLazyScanning(true),
133 fSafeMode(false)
134 {
135 char parameter[32];
136 size_t parameterLength = sizeof(parameter);
137
138 if (_kern_get_safemode_option(B_SAFEMODE_SAFE_MODE, parameter,
139 ¶meterLength) == 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 ¶meterLength) == 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 ourselves into the BApplication, if it's running
172 if (be_app != NULL && !be_app->IsLaunching() && be_app->Lock()) {
173 be_app->AddHandler(this);
174 be_app->Unlock();
175 }
176 }
177
178
~Private()179 BTranslatorRoster::Private::~Private()
180 {
181 stop_watching(this);
182
183 if (Looper() && LockLooper()) {
184 BLooper* looper = Looper();
185 Looper()->RemoveHandler(this);
186 looper->Unlock();
187 }
188
189 // Release all translators, so that they can delete themselves
190
191 TranslatorMap::iterator iterator = fTranslators.begin();
192 std::set<image_id> images;
193
194 while (iterator != fTranslators.end()) {
195 BTranslator* translator = iterator->second.translator;
196
197 images.insert(iterator->second.image);
198 translator->Release();
199
200 iterator++;
201 }
202
203 // Unload all images
204
205 std::set<image_id>::const_iterator imageIterator = images.begin();
206
207 while (imageIterator != images.end()) {
208 unload_add_on(*imageIterator);
209 imageIterator++;
210 }
211 }
212
213
214 void
MessageReceived(BMessage * message)215 BTranslatorRoster::Private::MessageReceived(BMessage* message)
216 {
217 switch (message->what) {
218 case B_NODE_MONITOR:
219 {
220 BAutolock locker(this);
221
222 printf("translator roster node monitor: ");
223 message->PrintToStream();
224
225 int32 opcode;
226 if (message->FindInt32("opcode", &opcode) != B_OK)
227 return;
228
229 switch (opcode) {
230 case B_ENTRY_CREATED:
231 {
232 const char* name;
233 node_ref nodeRef;
234 if (message->FindInt32("device", &nodeRef.device) != B_OK
235 || message->FindInt64("directory", &nodeRef.node)
236 != B_OK
237 || message->FindString("name", &name) != B_OK)
238 break;
239
240 // TODO: make this better (possible under Haiku)
241 snooze(100000);
242 // let the font be written completely before trying to
243 // open it
244
245 _EntryAdded(nodeRef, name);
246 break;
247 }
248
249 case B_ENTRY_MOVED:
250 {
251 // has the entry been moved into a monitored directory or
252 // has it been removed from one?
253 const char* name;
254 node_ref toNodeRef;
255 node_ref fromNodeRef;
256 node_ref nodeRef;
257
258 if (message->FindInt32("device", &nodeRef.device) != B_OK
259 || message->FindInt64("to directory", &toNodeRef.node)
260 != B_OK
261 || message->FindInt64("from directory",
262 (int64*)&fromNodeRef.node) != B_OK
263 || message->FindInt64("node", (int64*)&nodeRef.node)
264 != B_OK
265 || message->FindString("name", &name) != B_OK)
266 break;
267
268 fromNodeRef.device = nodeRef.device;
269 toNodeRef.device = nodeRef.device;
270
271 // Do we know this one yet?
272 translator_item* item = _FindTranslator(nodeRef);
273 if (item == NULL) {
274 // it's a new one!
275 if (_IsKnownDirectory(toNodeRef))
276 _EntryAdded(toNodeRef, name);
277 break;
278 }
279
280 if (!_IsKnownDirectory(toNodeRef)) {
281 // translator got removed
282 _RemoveTranslators(&nodeRef);
283 break;
284 }
285
286 // the name may have changed
287 item->ref.set_name(name);
288 item->ref.directory = toNodeRef.node;
289
290 if (_IsKnownDirectory(fromNodeRef)
291 && _IsKnownDirectory(toNodeRef)) {
292 // TODO: we should rescan for the name, there might be
293 // name clashes with translators in other directories
294 // (as well as old ones revealed)
295 break;
296 }
297 break;
298 }
299
300 case B_ENTRY_REMOVED:
301 {
302 node_ref nodeRef;
303 uint64 directoryNode;
304 if (message->FindInt32("device", &nodeRef.device) != B_OK
305 || message->FindInt64("directory",
306 (int64*)&directoryNode) != B_OK
307 || message->FindInt64("node", &nodeRef.node) != B_OK)
308 break;
309
310 translator_item* item = _FindTranslator(nodeRef);
311 if (item != NULL)
312 _RemoveTranslators(&nodeRef);
313 break;
314 }
315 }
316 break;
317 }
318
319 case B_DELETE_TRANSLATOR:
320 {
321 // A translator's refcount has been reduced to zero and it wants
322 // us to delete it.
323 int32 id;
324 void* self;
325 if (message->FindInt32("id", &id) == B_OK
326 && message->FindPointer("ptr", &self) == B_OK) {
327 _TranslatorDeleted(id, (BTranslator*)self);
328 }
329 break;
330 }
331
332 default:
333 BHandler::MessageReceived(message);
334 break;
335 }
336 }
337
338
339 void
AddDefaultPaths()340 BTranslatorRoster::Private::AddDefaultPaths()
341 {
342 // add user directories first, so that they can override system translators
343 const directory_which paths[] = {
344 B_USER_NONPACKAGED_ADDONS_DIRECTORY,
345 B_USER_ADDONS_DIRECTORY,
346 B_SYSTEM_NONPACKAGED_ADDONS_DIRECTORY,
347 B_SYSTEM_ADDONS_DIRECTORY,
348 };
349
350 for (uint32 i = fSafeMode ? 4 : 0; i < sizeof(paths) / sizeof(paths[0]);
351 i++) {
352 BPath path;
353 status_t status = find_directory(paths[i], &path, true);
354 if (status == B_OK && path.Append("Translators") == B_OK) {
355 mkdir(path.Path(), 0755);
356 // make sure the directory exists before we add it
357 AddPath(path.Path());
358 }
359 }
360 }
361
362
363 /*!
364 Adds the colon separated list of directories to the roster.
365
366 Note, the order in which these directories are added to actually matters,
367 translators with the same name will be taken from the earlier directory
368 first. See _CompareTranslatorDirectoryPriority().
369 */
370 status_t
AddPaths(const char * paths)371 BTranslatorRoster::Private::AddPaths(const char* paths)
372 {
373 if (paths == NULL)
374 return B_BAD_VALUE;
375
376 status_t status = B_OK;
377 int32 added = 0;
378
379 while (paths != NULL) {
380 const char* end = strchr(paths, ':');
381 BString path;
382
383 if (end != NULL) {
384 path.SetTo(paths, end - 1 - paths);
385 paths = end + 1;
386 } else {
387 path.SetTo(paths);
388 paths = NULL;
389 }
390
391 // Keep the last error that occured, and return it
392 // but don't overwrite it, if the last path was
393 // added successfully.
394 int32 count;
395 status_t error = AddPath(path.String(), &count);
396 if (error != B_NO_ERROR)
397 status = error;
398
399 added += count;
400 }
401
402 if (added == 0)
403 return status;
404
405 return B_OK;
406 }
407
408
409 /*!
410 Adds a new directory to the roster.
411
412 Note, the order in which these directories are added to actually matters,
413 see AddPaths().
414 */
415 status_t
AddPath(const char * path,int32 * _added)416 BTranslatorRoster::Private::AddPath(const char* path, int32* _added)
417 {
418 BDirectory directory(path);
419 status_t status = directory.InitCheck();
420 if (status != B_OK)
421 return status;
422
423 // if a subdirectory for our ABI exists, use that instead
424 if (fABISubDirectory != NULL) {
425 BEntry entry(&directory, fABISubDirectory);
426 if (entry.IsDirectory()) {
427 status = directory.SetTo(&entry);
428 if (status != B_OK)
429 return status;
430 }
431 }
432
433 node_ref nodeRef;
434 status = directory.GetNodeRef(&nodeRef);
435 if (status < B_OK)
436 return status;
437
438 // do we know this directory already?
439 if (_IsKnownDirectory(nodeRef))
440 return B_OK;
441
442 if (Looper() != NULL) {
443 // watch that directory
444 watch_node(&nodeRef, B_WATCH_DIRECTORY, this);
445 fDirectories.push_back(nodeRef);
446 }
447
448 int32 count = 0;
449 int32 files = 0;
450
451 entry_ref ref;
452 while (directory.GetNextRef(&ref) == B_OK) {
453 BEntry entry(&ref);
454 if (entry.IsDirectory())
455 continue;
456 if (CreateTranslators(ref, count) == B_OK)
457 count++;
458
459 files++;
460 }
461
462 if (_added)
463 *_added = count;
464
465 if (files != 0 && count == 0)
466 return B_BAD_VALUE;
467
468 return B_OK;
469 }
470
471
472 status_t
AddTranslator(BTranslator * translator,image_id image,const entry_ref * ref,ino_t node)473 BTranslatorRoster::Private::AddTranslator(BTranslator* translator,
474 image_id image, const entry_ref* ref, ino_t node)
475 {
476 BAutolock locker(this);
477
478 translator_item item;
479 item.translator = translator;
480 item.image = image;
481 item.node = node;
482 if (ref != NULL)
483 item.ref = *ref;
484
485 try {
486 fTranslators[fNextID] = item;
487 } catch (...) {
488 return B_NO_MEMORY;
489 }
490
491 translator->fOwningRoster = this;
492 translator->fID = fNextID++;
493 return B_OK;
494 }
495
496
497 void
RemoveTranslators(entry_ref & ref)498 BTranslatorRoster::Private::RemoveTranslators(entry_ref& ref)
499 {
500 _RemoveTranslators(NULL, &ref);
501 }
502
503
504 BTranslator*
FindTranslator(translator_id id)505 BTranslatorRoster::Private::FindTranslator(translator_id id)
506 {
507 if (!IsLocked()) {
508 debugger("translator must be locked!");
509 return NULL;
510 }
511
512 const translator_item* item = _FindTranslator(id);
513 if (item != NULL)
514 return item->translator;
515
516 return NULL;
517 }
518
519
520 status_t
GetTranslatorData(image_id image,translator_data & data)521 BTranslatorRoster::Private::GetTranslatorData(image_id image,
522 translator_data& data)
523 {
524 // If this is a translator add-on, it is in the C format
525 memset(&data, 0, sizeof(translator_data));
526
527 // find all the symbols
528
529 int32* version;
530 if (get_image_symbol(image, "translatorName", B_SYMBOL_TYPE_DATA,
531 (void**)&data.name) < B_OK
532 || get_image_symbol(image, "translatorInfo", B_SYMBOL_TYPE_DATA,
533 (void**)&data.info) < B_OK
534 || get_image_symbol(image, "translatorVersion", B_SYMBOL_TYPE_DATA,
535 (void**)&version) < B_OK || version == NULL
536 || get_image_symbol(image, "inputFormats", B_SYMBOL_TYPE_DATA,
537 (void**)&data.input_formats) < B_OK
538 || get_image_symbol(image, "outputFormats", B_SYMBOL_TYPE_DATA,
539 (void**)&data.output_formats) < B_OK
540 || get_image_symbol(image, "Identify", B_SYMBOL_TYPE_TEXT,
541 (void**)&data.identify_hook) < B_OK
542 || get_image_symbol(image, "Translate", B_SYMBOL_TYPE_TEXT,
543 (void**)&data.translate_hook) < B_OK) {
544 return B_BAD_TYPE;
545 }
546
547 data.version = *version;
548
549 // those calls are optional
550 get_image_symbol(image, "MakeConfig", B_SYMBOL_TYPE_TEXT,
551 (void**)&data.make_config_hook);
552 get_image_symbol(image, "GetConfigMessage", B_SYMBOL_TYPE_TEXT,
553 (void**)&data.get_config_message_hook);
554
555 return B_OK;
556 }
557
558
559 status_t
CreateTranslators(const entry_ref & ref,int32 & count,BMessage * update)560 BTranslatorRoster::Private::CreateTranslators(const entry_ref& ref,
561 int32& count, BMessage* update)
562 {
563 BAutolock locker(this);
564
565 BPrivate::QuarantineTranslatorImage quarantine(*this);
566
567 const translator_item* item = _FindTranslator(ref.name);
568 if (item != NULL) {
569 // check if the known translator has a higher priority
570 if (_CompareTranslatorDirectoryPriority(item->ref, ref) <= 0) {
571 // keep the existing add-on
572 return B_OK;
573 }
574
575 // replace existing translator(s) if the new translator succeeds
576 quarantine.Put(item->ref);
577 }
578
579 BEntry entry(&ref);
580 node_ref nodeRef;
581 status_t status = entry.GetNodeRef(&nodeRef);
582 if (status < B_OK)
583 return status;
584
585 BPath path(&ref);
586 image_id image = load_add_on(path.Path());
587 if (image < B_OK)
588 return image;
589
590 // Function pointer used to create post R4.5 style translators
591 BTranslator *(*makeNthTranslator)(int32 n, image_id you, uint32 flags, ...);
592
593 status = get_image_symbol(image, "make_nth_translator",
594 B_SYMBOL_TYPE_TEXT, (void**)&makeNthTranslator);
595 if (status == B_OK) {
596 // If the translator add-on supports the post R4.5
597 // translator creation mechanism, keep loading translators
598 // until MakeNthTranslator stops returning them.
599 BTranslator* translator = NULL;
600 int32 created = 0;
601 for (int32 n = 0; (translator = makeNthTranslator(n, image, 0)) != NULL;
602 n++) {
603 if (AddTranslator(translator, image, &ref, nodeRef.node) == B_OK) {
604 if (update)
605 update->AddInt32("translator_id", translator->fID);
606 fImageOrigins.insert(std::make_pair(translator, image));
607 count++;
608 created++;
609 } else {
610 translator->Release();
611 // this will delete the translator
612 }
613 }
614
615 if (created == 0) {
616 unload_add_on(image);
617 } else {
618 // Initial refcount for the image that was just loaded
619 fKnownImages.insert(std::make_pair(image, created));
620 }
621
622 quarantine.Remove();
623 return B_OK;
624 }
625
626 // If this is a translator add-on, it is in the C format
627 translator_data translatorData;
628 status = GetTranslatorData(image, translatorData);
629
630 // add this translator to the list
631 BPrivate::BFuncTranslator* translator = NULL;
632 if (status == B_OK) {
633 translator = new (std::nothrow) BPrivate::BFuncTranslator(
634 translatorData);
635 if (translator == NULL)
636 status = B_NO_MEMORY;
637 }
638
639 if (status == B_OK)
640 status = AddTranslator(translator, image, &ref, nodeRef.node);
641
642 if (status == B_OK) {
643 if (update)
644 update->AddInt32("translator_id", translator->fID);
645 quarantine.Remove();
646 count++;
647 } else
648 unload_add_on(image);
649
650 return status;
651 }
652
653
654 status_t
StartWatching(BMessenger target)655 BTranslatorRoster::Private::StartWatching(BMessenger target)
656 {
657 try {
658 fMessengers.push_back(target);
659 } catch (...) {
660 return B_NO_MEMORY;
661 }
662
663 if (fLazyScanning) {
664 fLazyScanning = false;
665 // Since we now have someone to report to, we cannot lazily
666 // adopt changes to the translator any longer
667
668 _RescanChanged();
669 }
670
671 return B_OK;
672 }
673
674
675 status_t
StopWatching(BMessenger target)676 BTranslatorRoster::Private::StopWatching(BMessenger target)
677 {
678 MessengerList::iterator iterator = fMessengers.begin();
679
680 while (iterator != fMessengers.end()) {
681 if (*iterator == target) {
682 fMessengers.erase(iterator);
683 if (fMessengers.empty())
684 fLazyScanning = true;
685
686 return B_OK;
687 }
688
689 iterator++;
690 }
691
692 return B_BAD_VALUE;
693 }
694
695
696 status_t
StoreTranslators(BMessage & archive)697 BTranslatorRoster::Private::StoreTranslators(BMessage& archive)
698 {
699 BAutolock locker(this);
700
701 TranslatorMap::const_iterator iterator = fTranslators.begin();
702
703 while (iterator != fTranslators.end()) {
704 const translator_item& item = iterator->second;
705 BPath path(&item.ref);
706 if (path.InitCheck() == B_OK)
707 archive.AddString("be:translator_path", path.Path());
708
709 iterator++;
710 }
711
712 return B_OK;
713 }
714
715
716 status_t
Identify(BPositionIO * source,BMessage * ioExtension,uint32 hintType,const char * hintMIME,uint32 wantType,translator_info * _info)717 BTranslatorRoster::Private::Identify(BPositionIO* source,
718 BMessage* ioExtension, uint32 hintType, const char* hintMIME,
719 uint32 wantType, translator_info* _info)
720 {
721 BAutolock locker(this);
722
723 _RescanChanged();
724
725 TranslatorMap::const_iterator iterator = fTranslators.begin();
726 BMessage baseExtension;
727 if (ioExtension != NULL)
728 baseExtension = *ioExtension;
729
730 float bestWeight = 0.0f;
731
732 while (iterator != fTranslators.end()) {
733 BTranslator& translator = *iterator->second.translator;
734
735 off_t pos = source->Seek(0, SEEK_SET);
736 if (pos != 0)
737 return pos < 0 ? (status_t)pos : B_IO_ERROR;
738
739 int32 formatsCount = 0;
740 const translation_format* formats = translator.InputFormats(
741 &formatsCount);
742 const translation_format* format = _CheckHints(formats, formatsCount,
743 hintType, hintMIME);
744
745 BMessage extension(baseExtension);
746 translator_info info;
747 if (translator.Identify(source, format, &extension, &info, wantType)
748 == B_OK) {
749 float weight = info.quality * info.capability;
750 if (weight > bestWeight) {
751 if (ioExtension != NULL)
752 *ioExtension = extension;
753 bestWeight = weight;
754
755 info.translator = iterator->first;
756 memcpy(_info, &info, sizeof(translator_info));
757 }
758 }
759
760 iterator++;
761 }
762
763 if (bestWeight > 0.0f)
764 return B_OK;
765
766 return B_NO_TRANSLATOR;
767 }
768
769
770 status_t
GetTranslators(BPositionIO * source,BMessage * ioExtension,uint32 hintType,const char * hintMIME,uint32 wantType,translator_info ** _info,int32 * _numInfo)771 BTranslatorRoster::Private::GetTranslators(BPositionIO* source,
772 BMessage* ioExtension, uint32 hintType, const char* hintMIME,
773 uint32 wantType, translator_info** _info, int32* _numInfo)
774 {
775 BAutolock locker(this);
776
777 _RescanChanged();
778
779 int32 arraySize = fTranslators.size();
780 translator_info* array = new (std::nothrow) translator_info[arraySize];
781 if (array == NULL)
782 return B_NO_MEMORY;
783
784 TranslatorMap::const_iterator iterator = fTranslators.begin();
785 int32 count = 0;
786
787 while (iterator != fTranslators.end()) {
788 BTranslator& translator = *iterator->second.translator;
789
790 off_t pos = source->Seek(0, SEEK_SET);
791 if (pos != 0) {
792 delete[] array;
793 return pos < 0 ? status_t(pos) : B_IO_ERROR;
794 }
795
796 int32 formatsCount = 0;
797 const translation_format* formats = translator.InputFormats(
798 &formatsCount);
799 const translation_format* format = _CheckHints(formats, formatsCount,
800 hintType, hintMIME);
801
802 translator_info info;
803 if (translator.Identify(source, format, ioExtension, &info, wantType)
804 == B_OK) {
805 info.translator = iterator->first;
806 array[count++] = info;
807 }
808
809 iterator++;
810 }
811
812 *_info = array;
813 *_numInfo = count;
814 qsort(array, count, sizeof(translator_info),
815 BTranslatorRoster::Private::_CompareSupport);
816 // translators are sorted by best support
817
818 return B_OK;
819 }
820
821
822 status_t
GetAllTranslators(translator_id ** _ids,int32 * _count)823 BTranslatorRoster::Private::GetAllTranslators(translator_id** _ids,
824 int32* _count)
825 {
826 BAutolock locker(this);
827
828 _RescanChanged();
829
830 int32 arraySize = fTranslators.size();
831 translator_id* array = new (std::nothrow) translator_id[arraySize];
832 if (array == NULL)
833 return B_NO_MEMORY;
834
835 TranslatorMap::const_iterator iterator = fTranslators.begin();
836 int32 count = 0;
837
838 while (iterator != fTranslators.end()) {
839 array[count++] = iterator->first;
840 iterator++;
841 }
842
843 *_ids = array;
844 *_count = count;
845 return B_OK;
846 }
847
848
849 status_t
GetRefFor(translator_id id,entry_ref & ref)850 BTranslatorRoster::Private::GetRefFor(translator_id id, entry_ref& ref)
851 {
852 BAutolock locker(this);
853
854 const translator_item* item = _FindTranslator(id);
855 if (item == NULL)
856 return B_NO_TRANSLATOR;
857
858 BEntry entry(&item->ref);
859 if (entry.InitCheck() == B_OK && entry.Exists() && entry.IsFile()) {
860 ref = item->ref;
861 return B_OK;
862 }
863
864 return B_ERROR;
865 }
866
867
868 void
_TranslatorDeleted(translator_id id,BTranslator * self)869 BTranslatorRoster::Private::_TranslatorDeleted(translator_id id, BTranslator* self)
870 {
871 BAutolock locker(this);
872
873 TranslatorMap::iterator iterator = fTranslators.find(id);
874 if (iterator != fTranslators.end())
875 fTranslators.erase(iterator);
876
877 image_id image = fImageOrigins[self];
878
879 delete self;
880
881 int32 former = atomic_add(&fKnownImages[image], -1);
882 if (former == 1) {
883 unload_add_on(image);
884 fImageOrigins.erase(self);
885 fKnownImages.erase(image);
886 }
887 }
888
889
890 /*static*/ int
_CompareSupport(const void * _a,const void * _b)891 BTranslatorRoster::Private::_CompareSupport(const void* _a, const void* _b)
892 {
893 const translator_info* infoA = (const translator_info*)_a;
894 const translator_info* infoB = (const translator_info*)_b;
895
896 float weightA = infoA->quality * infoA->capability;
897 float weightB = infoB->quality * infoB->capability;
898
899 if (weightA == weightB)
900 return 0;
901 if (weightA > weightB)
902 return -1;
903
904 return 1;
905 }
906
907
908 /*!
909 In lazy mode, freshly installed translator are not scanned immediately
910 when they become available. Instead, they are put into a set.
911
912 When a method is called that may be interested in these new translators,
913 they are scanned on the fly. Since lazy mode also means that this roster
914 does not have any listeners, we don't need to notify anyone about those
915 changes.
916 */
917 void
_RescanChanged()918 BTranslatorRoster::Private::_RescanChanged()
919 {
920 while (!fRescanEntries.empty()) {
921 EntryRefSet::iterator iterator = fRescanEntries.begin();
922 int32 count;
923 CreateTranslators(*iterator, count);
924
925 fRescanEntries.erase(iterator);
926 }
927 }
928
929
930 /*!
931 Tests if the hints provided for a source stream are compatible to
932 the formats the translator exports.
933 */
934 const translation_format*
_CheckHints(const translation_format * formats,int32 formatsCount,uint32 hintType,const char * hintMIME)935 BTranslatorRoster::Private::_CheckHints(const translation_format* formats,
936 int32 formatsCount, uint32 hintType, const char* hintMIME)
937 {
938 if (formats == NULL || formatsCount <= 0 || (!hintType && hintMIME == NULL))
939 return NULL;
940
941 // The provided MIME type hint may be a super type
942 int32 super = 0;
943 if (hintMIME && !strchr(hintMIME, '/'))
944 super = strlen(hintMIME);
945
946 // scan for suitable format
947 for (int32 i = 0; i < formatsCount && formats[i].type; i++) {
948 if (formats[i].type == hintType
949 || (hintMIME
950 && ((super && !strncmp(formats[i].MIME, hintMIME, super))
951 || !strcmp(formats[i].MIME, hintMIME))))
952 return &formats[i];
953 }
954
955 return NULL;
956 }
957
958
959 const translator_item*
_FindTranslator(translator_id id) const960 BTranslatorRoster::Private::_FindTranslator(translator_id id) const
961 {
962 TranslatorMap::const_iterator iterator = fTranslators.find(id);
963 if (iterator == fTranslators.end())
964 return NULL;
965
966 return &iterator->second;
967 }
968
969
970 const translator_item*
_FindTranslator(const char * name) const971 BTranslatorRoster::Private::_FindTranslator(const char* name) const
972 {
973 if (name == NULL)
974 return NULL;
975
976 TranslatorMap::const_iterator iterator = fTranslators.begin();
977
978 while (iterator != fTranslators.end()) {
979 const translator_item& item = iterator->second;
980 if (item.ref.name != NULL && !strcmp(item.ref.name, name))
981 return &item;
982
983 iterator++;
984 }
985
986 return NULL;
987 }
988
989
990 const translator_item*
_FindTranslator(entry_ref & ref) const991 BTranslatorRoster::Private::_FindTranslator(entry_ref& ref) const
992 {
993 if (ref.name == NULL)
994 return NULL;
995
996 TranslatorMap::const_iterator iterator = fTranslators.begin();
997
998 while (iterator != fTranslators.end()) {
999 const translator_item& item = iterator->second;
1000 if (item.ref == ref)
1001 return &item;
1002
1003 iterator++;
1004 }
1005
1006 return NULL;
1007 }
1008
1009
1010 translator_item*
_FindTranslator(node_ref & nodeRef)1011 BTranslatorRoster::Private::_FindTranslator(node_ref& nodeRef)
1012 {
1013 if (nodeRef.device < 0)
1014 return NULL;
1015
1016 TranslatorMap::iterator iterator = fTranslators.begin();
1017
1018 while (iterator != fTranslators.end()) {
1019 translator_item& item = iterator->second;
1020 if (item.ref.device == nodeRef.device
1021 && item.node == nodeRef.node)
1022 return &item;
1023
1024 iterator++;
1025 }
1026
1027 return NULL;
1028 }
1029
1030
1031 /*!
1032 Directories added to the roster have a certain priority - the first entry
1033 to be added has the highest priority; if a translator with the same name
1034 is to be found in two directories, the one with the higher priority is
1035 chosen.
1036 */
1037 int32
_CompareTranslatorDirectoryPriority(const entry_ref & a,const entry_ref & b) const1038 BTranslatorRoster::Private::_CompareTranslatorDirectoryPriority(
1039 const entry_ref& a, const entry_ref& b) const
1040 {
1041 // priority is determined by the order in the list
1042
1043 node_ref nodeRefA;
1044 nodeRefA.device = a.device;
1045 nodeRefA.node = a.directory;
1046
1047 node_ref nodeRefB;
1048 nodeRefB.device = b.device;
1049 nodeRefB.node = b.directory;
1050
1051 NodeRefList::const_iterator iterator = fDirectories.begin();
1052
1053 while (iterator != fDirectories.end()) {
1054 if (*iterator == nodeRefA)
1055 return -1;
1056 if (*iterator == nodeRefB)
1057 return 1;
1058
1059 iterator++;
1060 }
1061
1062 return 0;
1063 }
1064
1065
1066 bool
_IsKnownDirectory(const node_ref & nodeRef) const1067 BTranslatorRoster::Private::_IsKnownDirectory(const node_ref& nodeRef) const
1068 {
1069 NodeRefList::const_iterator iterator = fDirectories.begin();
1070
1071 while (iterator != fDirectories.end()) {
1072 if (*iterator == nodeRef)
1073 return true;
1074
1075 iterator++;
1076 }
1077
1078 return false;
1079 }
1080
1081
1082 void
_RemoveTranslators(const node_ref * nodeRef,const entry_ref * ref)1083 BTranslatorRoster::Private::_RemoveTranslators(const node_ref* nodeRef,
1084 const entry_ref* ref)
1085 {
1086 if (ref == NULL && nodeRef == NULL)
1087 return;
1088
1089 TranslatorMap::iterator iterator = fTranslators.begin();
1090 BMessage update(B_TRANSLATOR_REMOVED);
1091
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
1113 void
_EntryAdded(const node_ref & nodeRef,const char * name)1114 BTranslatorRoster::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 */
1134 void
_EntryAdded(const entry_ref & ref)1135 BTranslatorRoster::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
1154 void
_NotifyListeners(BMessage & update) const1155 BTranslatorRoster::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
BTranslatorReleaseDelegate(BTranslator * translator)1169 BTranslatorReleaseDelegate::BTranslatorReleaseDelegate(BTranslator* translator)
1170 :
1171 fUnderlying(translator)
1172 {
1173 }
1174
1175
1176 void
Release()1177 BTranslatorReleaseDelegate::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
BTranslatorRoster()1188 BTranslatorRoster::BTranslatorRoster()
1189 {
1190 _Initialize();
1191 }
1192
1193
BTranslatorRoster(BMessage * model)1194 BTranslatorRoster::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
~BTranslatorRoster()1213 BTranslatorRoster::~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
1225 void
_Initialize()1226 BTranslatorRoster::_Initialize()
1227 {
1228 fPrivate = new BTranslatorRoster::Private();
1229 }
1230
1231
1232 status_t
Archive(BMessage * into,bool deep) const1233 BTranslatorRoster::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
1243 BArchivable*
Instantiate(BMessage * from)1244 BTranslatorRoster::Instantiate(BMessage* from)
1245 {
1246 if (!from || !validate_instantiation(from, "BTranslatorRoster"))
1247 return NULL;
1248
1249 return new BTranslatorRoster(from);
1250 }
1251
1252
1253 BTranslatorRoster*
Default()1254 BTranslatorRoster::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 */
1295 status_t
AddTranslators(const char * path)1296 BTranslatorRoster::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 */
1321 status_t
AddTranslator(BTranslator * translator)1322 BTranslatorRoster::AddTranslator(BTranslator* translator)
1323 {
1324 if (!translator)
1325 return B_BAD_VALUE;
1326
1327 return fPrivate->AddTranslator(translator);
1328 }
1329
1330
1331 bool
IsTranslator(entry_ref * ref)1332 BTranslatorRoster::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 */
1375 status_t
Identify(BPositionIO * source,BMessage * ioExtension,translator_info * _info,uint32 hintType,const char * hintMIME,uint32 wantType)1376 BTranslatorRoster::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 */
1411 status_t
GetTranslators(BPositionIO * source,BMessage * ioExtension,translator_info ** _info,int32 * _numInfo,uint32 hintType,const char * hintMIME,uint32 wantType)1412 BTranslatorRoster::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 */
1432 status_t
GetAllTranslators(translator_id ** _ids,int32 * _count)1433 BTranslatorRoster::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 */
1456 status_t
GetTranslatorInfo(translator_id id,const char ** _name,const char ** _info,int32 * _version)1457 BTranslatorRoster::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 */
1493 status_t
GetInputFormats(translator_id id,const translation_format ** _formats,int32 * _numFormats)1494 BTranslatorRoster::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 */
1524 status_t
GetOutputFormats(translator_id id,const translation_format ** _formats,int32 * _numFormats)1525 BTranslatorRoster::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 */
1562 status_t
Translate(BPositionIO * source,const translator_info * info,BMessage * ioExtension,BPositionIO * destination,uint32 wantOutType,uint32 hintType,const char * hintMIME)1563 BTranslatorRoster::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 */
1627 status_t
Translate(translator_id id,BPositionIO * source,BMessage * ioExtension,BPositionIO * destination,uint32 wantOutType)1628 BTranslatorRoster::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 */
1685 status_t
MakeConfigurationView(translator_id id,BMessage * ioExtension,BView ** _view,BRect * _extent)1686 BTranslatorRoster::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
1702 BTranslatorReleaseDelegate*
AcquireTranslator(int32 id)1703 BTranslatorRoster::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 */
1727 status_t
GetConfigurationMessage(translator_id id,BMessage * ioExtension)1728 BTranslatorRoster::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 */
1756 status_t
GetRefFor(translator_id id,entry_ref * ref)1757 BTranslatorRoster::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
1766 status_t
StartWatching(BMessenger target)1767 BTranslatorRoster::StartWatching(BMessenger target)
1768 {
1769 return fPrivate->StartWatching(target);
1770 }
1771
1772
1773 status_t
StopWatching(BMessenger target)1774 BTranslatorRoster::StopWatching(BMessenger target)
1775 {
1776 return fPrivate->StopWatching(target);
1777 }
1778
1779
1780 // #pragma mark - private
1781
1782
BTranslatorRoster(const BTranslatorRoster & other)1783 BTranslatorRoster::BTranslatorRoster(const BTranslatorRoster &other)
1784 {
1785 }
1786
1787
1788 BTranslatorRoster &
operator =(const BTranslatorRoster & tr)1789 BTranslatorRoster::operator=(const BTranslatorRoster &tr)
1790 {
1791 return *this;
1792 }
1793
1794
1795 #if __GNUC__ == 2 // gcc 2
1796
1797 /*static*/ const char*
Version(int32 * outCurVersion,int32 * outMinVersion,int32 inAppVersion)1798 BTranslatorRoster::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
ReservedTranslatorRoster1()1821 void BTranslatorRoster::ReservedTranslatorRoster1() {}
ReservedTranslatorRoster2()1822 void BTranslatorRoster::ReservedTranslatorRoster2() {}
ReservedTranslatorRoster3()1823 void BTranslatorRoster::ReservedTranslatorRoster3() {}
ReservedTranslatorRoster4()1824 void BTranslatorRoster::ReservedTranslatorRoster4() {}
ReservedTranslatorRoster5()1825 void BTranslatorRoster::ReservedTranslatorRoster5() {}
ReservedTranslatorRoster6()1826 void BTranslatorRoster::ReservedTranslatorRoster6() {}
1827