vfs: check userland buffers before reading them.
[haiku.git] / src / kits / translation / TranslatorRoster.cpp
blob208ec850e422e014486d8582a78d56223606208d
1 /*
2 * Copyright 2002-2015, Haiku, Inc. All Rights Reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Axel Dörfler, axeld@pinc-software.de
7 * Markus Himmel, markus@himmel-villmar.de
8 * Michael Wilber
9 */
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.
17 #include <TranslatorRoster.h>
19 #include <new>
20 #include <strings.h>
21 #include <stdio.h>
22 #include <stdlib.h>
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>
32 #include <driver_settings.h>
33 #include <image.h>
34 #include <safemode_defs.h>
35 #include <syscalls.h>
37 #include "FuncTranslator.h"
38 #include "TranslatorRosterPrivate.h"
41 namespace BPrivate {
43 class QuarantineTranslatorImage {
44 public:
45 QuarantineTranslatorImage(
46 BTranslatorRoster::Private& privateRoster);
47 ~QuarantineTranslatorImage();
49 void Put(const entry_ref& ref);
50 void Remove();
52 private:
53 BTranslatorRoster::Private& fRoster;
54 entry_ref fRef;
55 bool fRemove;
58 } // namespace BPrivate
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";
74 BTranslatorRoster* BTranslatorRoster::sDefaultRoster = NULL;
77 namespace BPrivate {
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).
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).
89 QuarantineTranslatorImage::QuarantineTranslatorImage(
90 BTranslatorRoster::Private& privateRoster)
92 fRoster(privateRoster),
93 fRemove(false)
98 QuarantineTranslatorImage::~QuarantineTranslatorImage()
100 if (fRef.device == -1 || !fRemove)
101 return;
103 fRoster.RemoveTranslators(fRef);
107 void
108 QuarantineTranslatorImage::Put(const entry_ref& ref)
110 fRef = ref;
114 void
115 QuarantineTranslatorImage::Remove()
117 fRemove = true;
120 } // namespace BPrivate
123 // #pragma mark -
126 BTranslatorRoster::Private::Private()
128 BHandler("translator roster"),
129 BLocker("translator list"),
130 fABISubDirectory(NULL),
131 fNextID(1),
132 fLazyScanning(true),
133 fSafeMode(false)
135 char parameter[32];
136 size_t parameterLength = sizeof(parameter);
138 if (_kern_get_safemode_option(B_SAFEMODE_SAFE_MODE, parameter,
139 &parameterLength) == B_OK) {
140 if (!strcasecmp(parameter, "enabled") || !strcasecmp(parameter, "on")
141 || !strcasecmp(parameter, "true") || !strcasecmp(parameter, "yes")
142 || !strcasecmp(parameter, "enable") || !strcmp(parameter, "1"))
143 fSafeMode = true;
146 if (_kern_get_safemode_option(B_SAFEMODE_DISABLE_USER_ADD_ONS, parameter,
147 &parameterLength) == B_OK) {
148 if (!strcasecmp(parameter, "enabled") || !strcasecmp(parameter, "on")
149 || !strcasecmp(parameter, "true") || !strcasecmp(parameter, "yes")
150 || !strcasecmp(parameter, "enable") || !strcmp(parameter, "1"))
151 fSafeMode = true;
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;
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();
179 BTranslatorRoster::Private::~Private()
181 stop_watching(this);
183 if (Looper() && LockLooper()) {
184 BLooper* looper = Looper();
185 Looper()->RemoveHandler(this);
186 looper->Unlock();
189 // Release all translators, so that they can delete themselves
191 TranslatorMap::iterator iterator = fTranslators.begin();
192 std::set<image_id> images;
194 while (iterator != fTranslators.end()) {
195 BTranslator* translator = iterator->second.translator;
197 images.insert(iterator->second.image);
198 translator->Release();
200 iterator++;
203 // Unload all images
205 std::set<image_id>::const_iterator imageIterator = images.begin();
207 while (imageIterator != images.end()) {
208 unload_add_on(*imageIterator);
209 imageIterator++;
214 void
215 BTranslatorRoster::Private::MessageReceived(BMessage* message)
217 switch (message->what) {
218 case B_NODE_MONITOR:
220 BAutolock locker(this);
222 printf("translator roster node monitor: ");
223 message->PrintToStream();
225 int32 opcode;
226 if (message->FindInt32("opcode", &opcode) != B_OK)
227 return;
229 switch (opcode) {
230 case B_ENTRY_CREATED:
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;
240 // TODO: make this better (possible under Haiku)
241 snooze(100000);
242 // let the font be written completely before trying to
243 // open it
245 _EntryAdded(nodeRef, name);
246 break;
249 case B_ENTRY_MOVED:
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;
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;
268 fromNodeRef.device = nodeRef.device;
269 toNodeRef.device = nodeRef.device;
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;
280 if (!_IsKnownDirectory(toNodeRef)) {
281 // translator got removed
282 _RemoveTranslators(&nodeRef);
283 break;
286 // the name may have changed
287 item->ref.set_name(name);
288 item->ref.directory = toNodeRef.node;
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;
297 break;
300 case B_ENTRY_REMOVED:
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;
310 translator_item* item = _FindTranslator(nodeRef);
311 if (item != NULL)
312 _RemoveTranslators(&nodeRef);
313 break;
316 break;
319 case B_DELETE_TRANSLATOR:
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);
329 break;
332 default:
333 BHandler::MessageReceived(message);
334 break;
339 void
340 BTranslatorRoster::Private::AddDefaultPaths()
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,
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());
364 Adds the colon separated list of directories to the roster.
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().
370 status_t
371 BTranslatorRoster::Private::AddPaths(const char* paths)
373 if (paths == NULL)
374 return B_BAD_VALUE;
376 status_t status = B_OK;
377 int32 added = 0;
379 while (paths != NULL) {
380 const char* end = strchr(paths, ':');
381 BString path;
383 if (end != NULL) {
384 path.SetTo(paths, end - 1 - paths);
385 paths = end + 1;
386 } else {
387 path.SetTo(paths);
388 paths = NULL;
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;
399 added += count;
402 if (added == 0)
403 return status;
405 return B_OK;
410 Adds a new directory to the roster.
412 Note, the order in which these directories are added to actually matters,
413 see AddPaths().
415 status_t
416 BTranslatorRoster::Private::AddPath(const char* path, int32* _added)
418 BDirectory directory(path);
419 status_t status = directory.InitCheck();
420 if (status != B_OK)
421 return status;
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;
433 node_ref nodeRef;
434 status = directory.GetNodeRef(&nodeRef);
435 if (status < B_OK)
436 return status;
438 // do we know this directory already?
439 if (_IsKnownDirectory(nodeRef))
440 return B_OK;
442 if (Looper() != NULL) {
443 // watch that directory
444 watch_node(&nodeRef, B_WATCH_DIRECTORY, this);
445 fDirectories.push_back(nodeRef);
448 int32 count = 0;
449 int32 files = 0;
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++;
459 files++;
462 if (_added)
463 *_added = count;
465 if (files != 0 && count == 0)
466 return B_BAD_VALUE;
468 return B_OK;
472 status_t
473 BTranslatorRoster::Private::AddTranslator(BTranslator* translator,
474 image_id image, const entry_ref* ref, ino_t node)
476 BAutolock locker(this);
478 translator_item item;
479 item.translator = translator;
480 item.image = image;
481 item.node = node;
482 if (ref != NULL)
483 item.ref = *ref;
485 try {
486 fTranslators[fNextID] = item;
487 } catch (...) {
488 return B_NO_MEMORY;
491 translator->fOwningRoster = this;
492 translator->fID = fNextID++;
493 return B_OK;
497 void
498 BTranslatorRoster::Private::RemoveTranslators(entry_ref& ref)
500 _RemoveTranslators(NULL, &ref);
504 BTranslator*
505 BTranslatorRoster::Private::FindTranslator(translator_id id)
507 if (!IsLocked()) {
508 debugger("translator must be locked!");
509 return NULL;
512 const translator_item* item = _FindTranslator(id);
513 if (item != NULL)
514 return item->translator;
516 return NULL;
520 status_t
521 BTranslatorRoster::Private::GetTranslatorData(image_id image,
522 translator_data& data)
524 // If this is a translator add-on, it is in the C format
525 memset(&data, 0, sizeof(translator_data));
527 // find all the symbols
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;
547 data.version = *version;
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);
555 return B_OK;
559 status_t
560 BTranslatorRoster::Private::CreateTranslators(const entry_ref& ref,
561 int32& count, BMessage* update)
563 BAutolock locker(this);
565 BPrivate::QuarantineTranslatorImage quarantine(*this);
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;
575 // replace existing translator(s) if the new translator succeeds
576 quarantine.Put(item->ref);
579 BEntry entry(&ref);
580 node_ref nodeRef;
581 status_t status = entry.GetNodeRef(&nodeRef);
582 if (status < B_OK)
583 return status;
585 BPath path(&ref);
586 image_id image = load_add_on(path.Path());
587 if (image < B_OK)
588 return image;
590 // Function pointer used to create post R4.5 style translators
591 BTranslator *(*makeNthTranslator)(int32 n, image_id you, uint32 flags, ...);
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
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));
622 quarantine.Remove();
623 return B_OK;
626 // If this is a translator add-on, it is in the C format
627 translator_data translatorData;
628 status = GetTranslatorData(image, translatorData);
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;
639 if (status == B_OK)
640 status = AddTranslator(translator, image, &ref, nodeRef.node);
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);
650 return status;
654 status_t
655 BTranslatorRoster::Private::StartWatching(BMessenger target)
657 try {
658 fMessengers.push_back(target);
659 } catch (...) {
660 return B_NO_MEMORY;
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
668 _RescanChanged();
671 return B_OK;
675 status_t
676 BTranslatorRoster::Private::StopWatching(BMessenger target)
678 MessengerList::iterator iterator = fMessengers.begin();
680 while (iterator != fMessengers.end()) {
681 if (*iterator == target) {
682 fMessengers.erase(iterator);
683 if (fMessengers.empty())
684 fLazyScanning = true;
686 return B_OK;
689 iterator++;
692 return B_BAD_VALUE;
696 status_t
697 BTranslatorRoster::Private::StoreTranslators(BMessage& archive)
699 BAutolock locker(this);
701 TranslatorMap::const_iterator iterator = fTranslators.begin();
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());
709 iterator++;
712 return B_OK;
716 status_t
717 BTranslatorRoster::Private::Identify(BPositionIO* source,
718 BMessage* ioExtension, uint32 hintType, const char* hintMIME,
719 uint32 wantType, translator_info* _info)
721 BAutolock locker(this);
723 _RescanChanged();
725 TranslatorMap::const_iterator iterator = fTranslators.begin();
726 BMessage baseExtension;
727 if (ioExtension != NULL)
728 baseExtension = *ioExtension;
730 float bestWeight = 0.0f;
732 while (iterator != fTranslators.end()) {
733 BTranslator& translator = *iterator->second.translator;
735 off_t pos = source->Seek(0, SEEK_SET);
736 if (pos != 0)
737 return pos < 0 ? (status_t)pos : B_IO_ERROR;
739 int32 formatsCount = 0;
740 const translation_format* formats = translator.InputFormats(
741 &formatsCount);
742 const translation_format* format = _CheckHints(formats, formatsCount,
743 hintType, hintMIME);
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;
755 info.translator = iterator->first;
756 memcpy(_info, &info, sizeof(translator_info));
760 iterator++;
763 if (bestWeight > 0.0f)
764 return B_OK;
766 return B_NO_TRANSLATOR;
770 status_t
771 BTranslatorRoster::Private::GetTranslators(BPositionIO* source,
772 BMessage* ioExtension, uint32 hintType, const char* hintMIME,
773 uint32 wantType, translator_info** _info, int32* _numInfo)
775 BAutolock locker(this);
777 _RescanChanged();
779 int32 arraySize = fTranslators.size();
780 translator_info* array = new (std::nothrow) translator_info[arraySize];
781 if (array == NULL)
782 return B_NO_MEMORY;
784 TranslatorMap::const_iterator iterator = fTranslators.begin();
785 int32 count = 0;
787 while (iterator != fTranslators.end()) {
788 BTranslator& translator = *iterator->second.translator;
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;
796 int32 formatsCount = 0;
797 const translation_format* formats = translator.InputFormats(
798 &formatsCount);
799 const translation_format* format = _CheckHints(formats, formatsCount,
800 hintType, hintMIME);
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;
809 iterator++;
812 *_info = array;
813 *_numInfo = count;
814 qsort(array, count, sizeof(translator_info),
815 BTranslatorRoster::Private::_CompareSupport);
816 // translators are sorted by best support
818 return B_OK;
822 status_t
823 BTranslatorRoster::Private::GetAllTranslators(translator_id** _ids,
824 int32* _count)
826 BAutolock locker(this);
828 _RescanChanged();
830 int32 arraySize = fTranslators.size();
831 translator_id* array = new (std::nothrow) translator_id[arraySize];
832 if (array == NULL)
833 return B_NO_MEMORY;
835 TranslatorMap::const_iterator iterator = fTranslators.begin();
836 int32 count = 0;
838 while (iterator != fTranslators.end()) {
839 array[count++] = iterator->first;
840 iterator++;
843 *_ids = array;
844 *_count = count;
845 return B_OK;
849 status_t
850 BTranslatorRoster::Private::GetRefFor(translator_id id, entry_ref& ref)
852 BAutolock locker(this);
854 const translator_item* item = _FindTranslator(id);
855 if (item == NULL)
856 return B_NO_TRANSLATOR;
858 BEntry entry(&item->ref);
859 if (entry.InitCheck() == B_OK && entry.Exists() && entry.IsFile()) {
860 ref = item->ref;
861 return B_OK;
864 return B_ERROR;
868 void
869 BTranslatorRoster::Private::_TranslatorDeleted(translator_id id, BTranslator* self)
871 BAutolock locker(this);
873 TranslatorMap::iterator iterator = fTranslators.find(id);
874 if (iterator != fTranslators.end())
875 fTranslators.erase(iterator);
877 image_id image = fImageOrigins[self];
879 delete self;
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);
890 /*static*/ int
891 BTranslatorRoster::Private::_CompareSupport(const void* _a, const void* _b)
893 const translator_info* infoA = (const translator_info*)_a;
894 const translator_info* infoB = (const translator_info*)_b;
896 float weightA = infoA->quality * infoA->capability;
897 float weightB = infoB->quality * infoB->capability;
899 if (weightA == weightB)
900 return 0;
901 if (weightA > weightB)
902 return -1;
904 return 1;
909 In lazy mode, freshly installed translator are not scanned immediately
910 when they become available. Instead, they are put into a set.
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.
917 void
918 BTranslatorRoster::Private::_RescanChanged()
920 while (!fRescanEntries.empty()) {
921 EntryRefSet::iterator iterator = fRescanEntries.begin();
922 int32 count;
923 CreateTranslators(*iterator, count);
925 fRescanEntries.erase(iterator);
931 Tests if the hints provided for a source stream are compatible to
932 the formats the translator exports.
934 const translation_format*
935 BTranslatorRoster::Private::_CheckHints(const translation_format* formats,
936 int32 formatsCount, uint32 hintType, const char* hintMIME)
938 if (formats == NULL || formatsCount <= 0 || (!hintType && hintMIME == NULL))
939 return NULL;
941 // The provided MIME type hint may be a super type
942 int32 super = 0;
943 if (hintMIME && !strchr(hintMIME, '/'))
944 super = strlen(hintMIME);
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];
955 return NULL;
959 const translator_item*
960 BTranslatorRoster::Private::_FindTranslator(translator_id id) const
962 TranslatorMap::const_iterator iterator = fTranslators.find(id);
963 if (iterator == fTranslators.end())
964 return NULL;
966 return &iterator->second;
970 const translator_item*
971 BTranslatorRoster::Private::_FindTranslator(const char* name) const
973 if (name == NULL)
974 return NULL;
976 TranslatorMap::const_iterator iterator = fTranslators.begin();
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;
983 iterator++;
986 return NULL;
990 const translator_item*
991 BTranslatorRoster::Private::_FindTranslator(entry_ref& ref) const
993 if (ref.name == NULL)
994 return NULL;
996 TranslatorMap::const_iterator iterator = fTranslators.begin();
998 while (iterator != fTranslators.end()) {
999 const translator_item& item = iterator->second;
1000 if (item.ref == ref)
1001 return &item;
1003 iterator++;
1006 return NULL;
1010 translator_item*
1011 BTranslatorRoster::Private::_FindTranslator(node_ref& nodeRef)
1013 if (nodeRef.device < 0)
1014 return NULL;
1016 TranslatorMap::iterator iterator = fTranslators.begin();
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;
1024 iterator++;
1027 return NULL;
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.
1037 int32
1038 BTranslatorRoster::Private::_CompareTranslatorDirectoryPriority(
1039 const entry_ref& a, const entry_ref& b) const
1041 // priority is determined by the order in the list
1043 node_ref nodeRefA;
1044 nodeRefA.device = a.device;
1045 nodeRefA.node = a.directory;
1047 node_ref nodeRefB;
1048 nodeRefB.device = b.device;
1049 nodeRefB.node = b.directory;
1051 NodeRefList::const_iterator iterator = fDirectories.begin();
1053 while (iterator != fDirectories.end()) {
1054 if (*iterator == nodeRefA)
1055 return -1;
1056 if (*iterator == nodeRefB)
1057 return 1;
1059 iterator++;
1062 return 0;
1066 bool
1067 BTranslatorRoster::Private::_IsKnownDirectory(const node_ref& nodeRef) const
1069 NodeRefList::const_iterator iterator = fDirectories.begin();
1071 while (iterator != fDirectories.end()) {
1072 if (*iterator == nodeRef)
1073 return true;
1075 iterator++;
1078 return false;
1082 void
1083 BTranslatorRoster::Private::_RemoveTranslators(const node_ref* nodeRef,
1084 const entry_ref* ref)
1086 if (ref == NULL && nodeRef == NULL)
1087 return;
1089 TranslatorMap::iterator iterator = fTranslators.begin();
1090 BMessage update(B_TRANSLATOR_REMOVED);
1091 image_id image = -1;
1093 while (iterator != fTranslators.end()) {
1094 TranslatorMap::iterator next = iterator;
1095 next++;
1097 const translator_item& item = iterator->second;
1098 if ((ref != NULL && item.ref == *ref)
1099 || (nodeRef != NULL && item.ref.device == nodeRef->device
1100 && item.node == nodeRef->node)) {
1101 item.translator->Release();
1102 image = item.image;
1103 update.AddInt32("translator_id", iterator->first);
1105 fTranslators.erase(iterator);
1108 iterator = next;
1111 _NotifyListeners(update);
1115 void
1116 BTranslatorRoster::Private::_EntryAdded(const node_ref& nodeRef,
1117 const char* name)
1119 entry_ref ref;
1120 ref.device = nodeRef.device;
1121 ref.directory = nodeRef.node;
1122 ref.set_name(name);
1124 _EntryAdded(ref);
1129 In lazy mode, the entry is marked to be rescanned on next use of any
1130 translation method (that could make use of it).
1131 In non-lazy mode, the translators for this entry are created directly
1132 and listeners notified.
1134 Called by the node monitor handling.
1136 void
1137 BTranslatorRoster::Private::_EntryAdded(const entry_ref& ref)
1139 BEntry entry;
1140 if (entry.SetTo(&ref) != B_OK || !entry.IsFile())
1141 return;
1143 if (fLazyScanning) {
1144 fRescanEntries.insert(ref);
1145 return;
1148 BMessage update(B_TRANSLATOR_ADDED);
1149 int32 count = 0;
1150 CreateTranslators(ref, count, &update);
1152 _NotifyListeners(update);
1156 void
1157 BTranslatorRoster::Private::_NotifyListeners(BMessage& update) const
1159 MessengerList::const_iterator iterator = fMessengers.begin();
1161 while (iterator != fMessengers.end()) {
1162 (*iterator).SendMessage(&update);
1163 iterator++;
1168 // #pragma mark -
1171 BTranslatorReleaseDelegate::BTranslatorReleaseDelegate(BTranslator* translator)
1173 fUnderlying(translator)
1178 void
1179 BTranslatorReleaseDelegate::Release()
1181 fUnderlying->Release();
1182 // ReleaseDelegate is only allowed to release a translator once.
1183 delete this;
1187 // #pragma mark -
1190 BTranslatorRoster::BTranslatorRoster()
1192 _Initialize();
1196 BTranslatorRoster::BTranslatorRoster(BMessage* model)
1198 _Initialize();
1200 if (model) {
1201 const char* path;
1202 for (int32 i = 0;
1203 model->FindString("be:translator_path", i, &path) == B_OK; i++) {
1204 BEntry entry(path);
1205 entry_ref ref;
1206 if (entry.GetRef(&ref) == B_OK) {
1207 int32 count = 0;
1208 fPrivate->CreateTranslators(ref, count);
1215 BTranslatorRoster::~BTranslatorRoster()
1217 // If the default BTranslatorRoster is being
1218 // deleted, set the pointer to the default
1219 // BTranslatorRoster to NULL
1220 if (sDefaultRoster == this)
1221 sDefaultRoster = NULL;
1223 delete fPrivate;
1227 void
1228 BTranslatorRoster::_Initialize()
1230 fPrivate = new BTranslatorRoster::Private();
1234 status_t
1235 BTranslatorRoster::Archive(BMessage* into, bool deep) const
1237 status_t status = BArchivable::Archive(into, deep);
1238 if (status != B_OK)
1239 return status;
1241 return fPrivate->StoreTranslators(*into);
1245 BArchivable*
1246 BTranslatorRoster::Instantiate(BMessage* from)
1248 if (!from || !validate_instantiation(from, "BTranslatorRoster"))
1249 return NULL;
1251 return new BTranslatorRoster(from);
1255 BTranslatorRoster*
1256 BTranslatorRoster::Default()
1258 static int32 lock = 0;
1260 if (sDefaultRoster != NULL)
1261 return sDefaultRoster;
1263 if (atomic_add(&lock, 1) != 0) {
1264 // Just wait for the default translator to be instantiated
1265 while (sDefaultRoster == NULL)
1266 snooze(10000);
1268 atomic_add(&lock, -1);
1269 return sDefaultRoster;
1272 // If the default translators have not been loaded,
1273 // create a new BTranslatorRoster for them, and load them.
1274 if (sDefaultRoster == NULL) {
1275 BTranslatorRoster* roster = new BTranslatorRoster();
1276 roster->AddTranslators(NULL);
1278 sDefaultRoster = roster;
1279 // this will unlock any other threads waiting for
1280 // the default roster to become available
1283 atomic_add(&lock, -1);
1284 return sDefaultRoster;
1289 This function takes a string of colon delimited paths, and adds
1290 the translators from those paths to this BTranslatorRoster.
1292 If load_path is NULL, it parses the environment variable
1293 TRANSLATORS. If that does not exist, it uses the system paths:
1294 /boot/home/config/add-ons/Translators,
1295 /system/add-ons/Translators.
1297 status_t
1298 BTranslatorRoster::AddTranslators(const char* path)
1300 if (path == NULL)
1301 path = getenv("TRANSLATORS");
1302 if (path == NULL) {
1303 fPrivate->AddDefaultPaths();
1304 return B_OK;
1307 return fPrivate->AddPaths(path);
1312 Adds a BTranslator based object to the BTranslatorRoster.
1313 When you add a BTranslator roster, it is Acquire()'d by
1314 BTranslatorRoster; it is Release()'d when the
1315 BTranslatorRoster is deleted.
1317 \param translator the translator to be added to the
1318 BTranslatorRoster
1320 \return B_BAD_VALUE, if translator is NULL,
1321 B_OK if all went well
1323 status_t
1324 BTranslatorRoster::AddTranslator(BTranslator* translator)
1326 if (!translator)
1327 return B_BAD_VALUE;
1329 return fPrivate->AddTranslator(translator);
1333 bool
1334 BTranslatorRoster::IsTranslator(entry_ref* ref)
1336 if (ref == NULL)
1337 return false;
1339 BPath path(ref);
1340 image_id image = load_add_on(path.Path());
1341 if (image < B_OK)
1342 return false;
1344 // Function pointer used to create post R4.5 style translators
1345 BTranslator* (*makeNthTranslator)(int32 n, image_id you, uint32 flags, ...);
1347 status_t status = get_image_symbol(image, "make_nth_translator",
1348 B_SYMBOL_TYPE_TEXT, (void**)&makeNthTranslator);
1349 if (status < B_OK) {
1350 // If this is a translator add-on, it is in the C format
1351 translator_data translatorData;
1352 status = fPrivate->GetTranslatorData(image, translatorData);
1355 unload_add_on(image);
1356 return status == B_OK;
1361 This function determines which translator is best suited
1362 to convert the data from \a source.
1364 \param source the data to be identified
1365 \param ioExtension the configuration data for the translator
1366 \param _info the information about the chosen translator is put here
1367 \param hintType a hint about the type of data that is in \a source, set
1368 it to zero if the type is not known
1369 \param hintMIME a hint about the MIME type of \a source, set it to NULL
1370 if the type is not known.
1371 \param wantType the desired output type - if zero, any type is okay.
1373 \return B_OK, identification of \a source was successful,
1374 B_NO_TRANSLATOR, no appropriate translator found,
1375 and other errors from accessing the source stream
1377 status_t
1378 BTranslatorRoster::Identify(BPositionIO* source, BMessage* ioExtension,
1379 translator_info* _info, uint32 hintType, const char* hintMIME,
1380 uint32 wantType)
1382 if (source == NULL || _info == NULL)
1383 return B_BAD_VALUE;
1385 return fPrivate->Identify(source, ioExtension, hintType, hintMIME, wantType,
1386 _info);
1391 Finds all translators capable of handling the data in \a source
1392 and puts them into the outInfo array (which you must delete
1393 yourself when you are done with it). Specifying a value for
1394 \a hintType, \a hintMIME and/or \a wantType causes only the
1395 translators that satisfy them to be included in the outInfo.
1397 \param source the data to be translated
1398 \param ioExtension the configuration data for the translator
1399 \param _info, the array of acceptable translators is stored here if
1400 the function succeeds. It's the caller's responsibility to free
1401 the array using delete[].
1402 \param _numInfo, number of entries in the \a _info array
1403 \param hintType a hint about the type of data that is in \a source, set
1404 it to zero if the type is not known
1405 \param hintMIME a hint about the MIME type of \a source, set it to NULL
1406 if the type is not known.
1407 \param wantType the desired output type - if zero, any type is okay.
1409 \return B_OK, successfully indentified the data in \a source
1410 B_NO_TRANSLATOR, no translator could handle \a source
1411 other errors, problems using \a source
1413 status_t
1414 BTranslatorRoster::GetTranslators(BPositionIO* source, BMessage* ioExtension,
1415 translator_info** _info, int32* _numInfo, uint32 hintType,
1416 const char* hintMIME, uint32 wantType)
1418 if (source == NULL || _info == NULL || _numInfo == NULL)
1419 return B_BAD_VALUE;
1421 return fPrivate->GetTranslators(source, ioExtension, hintType, hintMIME,
1422 wantType, _info, _numInfo);
1427 Returns an array in \a _ids of all of the translators stored by this
1428 object.
1429 You must free the array using delete[] when you are done with it.
1431 \param _ids the array is stored there (you own the array).
1432 \param _count number of IDs in the array.
1434 status_t
1435 BTranslatorRoster::GetAllTranslators(translator_id** _ids, int32* _count)
1437 if (_ids == NULL || _count == NULL)
1438 return B_BAD_VALUE;
1440 return fPrivate->GetAllTranslators(_ids, _count);
1445 Returns information about the translator with the specified
1446 translator \a id.
1447 You must not free any of the data you get back.
1449 \param id identifies which translator you want info for
1450 \param _name the translator name is put here
1451 \param _info the translator description is put here
1452 \param _version the translation version is put here
1454 \return B_OK if successful,
1455 B_BAD_VALUE, if all parameters are NULL
1456 B_NO_TRANSLATOR, \id didn't identify an existing translator
1458 status_t
1459 BTranslatorRoster::GetTranslatorInfo(translator_id id, const char** _name,
1460 const char** _info, int32* _version)
1462 if (_name == NULL && _info == NULL && _version == NULL)
1463 return B_BAD_VALUE;
1465 BAutolock locker(fPrivate);
1467 BTranslator* translator = fPrivate->FindTranslator(id);
1468 if (translator == NULL)
1469 return B_NO_TRANSLATOR;
1471 if (_name)
1472 *_name = translator->TranslatorName();
1473 if (_info)
1474 *_info = translator->TranslatorInfo();
1475 if (_version)
1476 *_version = translator->TranslatorVersion();
1478 return B_OK;
1483 Returns all of the input formats for the translator specified
1484 by \a id.
1485 You must not free any of the data you get back.
1487 \param id identifies which translator you want the input formats for
1488 \param _formats array of input formats
1489 \param _numFormats number of formats in the array
1491 \return B_OK if successful,
1492 B_BAD_VALUE, if any parameter is NULL
1493 B_NO_TRANSLATOR, \id didn't identify an existing translator
1495 status_t
1496 BTranslatorRoster::GetInputFormats(translator_id id,
1497 const translation_format** _formats, int32* _numFormats)
1499 if (_formats == NULL || _numFormats == NULL)
1500 return B_BAD_VALUE;
1502 BAutolock locker(fPrivate);
1504 BTranslator* translator = fPrivate->FindTranslator(id);
1505 if (translator == NULL)
1506 return B_NO_TRANSLATOR;
1508 *_formats = translator->InputFormats(_numFormats);
1509 return B_OK;
1514 Returns all of the output formats for the translator specified
1515 by \a id.
1516 You must not free any of the data you get back.
1518 \param id identifies which translator you want the output formats for
1519 \param _formats array of output formats
1520 \param _numFormats number of formats in the array
1522 \return B_OK if successful,
1523 B_BAD_VALUE, if any parameter is NULL
1524 B_NO_TRANSLATOR, \id didn't identify an existing translator
1526 status_t
1527 BTranslatorRoster::GetOutputFormats(translator_id id,
1528 const translation_format** _formats, int32* _numFormats)
1530 if (_formats == NULL || _numFormats == NULL)
1531 return B_BAD_VALUE;
1533 BAutolock locker(fPrivate);
1535 BTranslator* translator = fPrivate->FindTranslator(id);
1536 if (translator == NULL)
1537 return B_NO_TRANSLATOR;
1539 *_formats = translator->OutputFormats(_numFormats);
1540 return B_OK;
1545 This function is the whole point of the Translation Kit.
1546 This is for translating the data in \a source to \a destination
1547 using the format \a wantOutType.
1549 \param source the data to be translated
1550 \param ioExtension the configuration data for the translator
1551 \param info information about translator to use (can be NULL, in which
1552 case the \a source is identified first)
1553 \param destination where \a source is translated to
1554 \param hintType a hint about the type of data that is in \a source, set
1555 it to zero if the type is not known
1556 \param hintMIME a hint about the MIME type of \a source, set it to NULL
1557 if the type is not known.
1558 \param wantType the desired output type - if zero, any type is okay.
1560 \return B_OK, translation of \a source was successful,
1561 B_NO_TRANSLATOR, no appropriate translator found,
1562 and other errors from accessing the source and destination streams
1564 status_t
1565 BTranslatorRoster::Translate(BPositionIO* source, const translator_info* info,
1566 BMessage* ioExtension, BPositionIO* destination, uint32 wantOutType,
1567 uint32 hintType, const char* hintMIME)
1569 if (source == NULL || destination == NULL)
1570 return B_BAD_VALUE;
1572 translator_info infoBuffer;
1574 if (info == NULL) {
1575 // look for a suitable translator
1576 status_t status = fPrivate->Identify(source, ioExtension, hintType,
1577 hintMIME, wantOutType, &infoBuffer);
1578 if (status < B_OK)
1579 return status;
1581 info = &infoBuffer;
1584 if (!fPrivate->Lock())
1585 return B_ERROR;
1587 BTranslator* translator = fPrivate->FindTranslator(info->translator);
1588 if (translator != NULL) {
1589 translator->Acquire();
1590 // make sure this translator is not removed while we're playing with
1591 // it; translating shouldn't be serialized!
1594 fPrivate->Unlock();
1596 if (translator == NULL)
1597 return B_NO_TRANSLATOR;
1599 status_t status = B_OK;
1600 off_t pos = source->Seek(0, SEEK_SET);
1601 if (pos != 0)
1602 status = pos < 0 ? (status_t)pos : B_IO_ERROR;
1603 if (status == B_OK) {
1604 status = translator->Translate(source, info, ioExtension, wantOutType,
1605 destination);
1607 translator->Release();
1609 return status;
1614 This function is the whole point of the Translation Kit.
1615 This is for translating the data in \a source to \a destination
1616 using the format \a wantOutType and the translator identified
1617 by \a id.
1619 \param id the translator to be used
1620 \param source the data to be translated
1621 \param ioExtension the configuration data for the translator
1622 \param destination where \a source is translated to
1623 \param wantType the desired output type - if zero, any type is okay.
1625 \return B_OK, translation of \a source was successful,
1626 B_NO_TRANSLATOR, no appropriate translator found,
1627 and other errors from accessing the source and destination streams
1629 status_t
1630 BTranslatorRoster::Translate(translator_id id, BPositionIO* source,
1631 BMessage* ioExtension, BPositionIO* destination, uint32 wantOutType)
1633 if (source == NULL || destination == NULL)
1634 return B_BAD_VALUE;
1636 if (!fPrivate->Lock())
1637 return B_ERROR;
1639 BTranslator* translator = fPrivate->FindTranslator(id);
1640 if (translator != NULL) {
1641 translator->Acquire();
1642 // make sure this translator is not removed while we're playing with
1643 // it; translating shouldn't be serialized!
1646 fPrivate->Unlock();
1648 if (translator == NULL)
1649 return B_NO_TRANSLATOR;
1651 status_t status;
1652 off_t pos = source->Seek(0, SEEK_SET);
1653 if (pos == 0) {
1654 translator_info info;
1655 status = translator->Identify(source, NULL, ioExtension, &info,
1656 wantOutType);
1657 if (status >= B_OK) {
1658 off_t pos = source->Seek(0, SEEK_SET);
1659 if (pos != 0)
1660 status = pos < 0 ? (status_t)pos : B_IO_ERROR;
1661 else {
1662 status = translator->Translate(source, &info, ioExtension,
1663 wantOutType, destination);
1666 } else
1667 status = pos < 0 ? (status_t)pos : B_IO_ERROR;
1668 translator->Release();
1670 return status;
1675 Creates a BView in \a _view for configuring the translator specified
1676 by \a id. Not all translators support this, though.
1678 \param id identifies which translator you want the input formats for
1679 \param ioExtension the configuration data for the translator
1680 \param _view the view for configuring the translator
1681 \param _extent the bounds for the (resizable) view
1683 \return B_OK if successful,
1684 B_BAD_VALUE, if any parameter is NULL
1685 B_NO_TRANSLATOR, \id didn't identify an existing translator
1687 status_t
1688 BTranslatorRoster::MakeConfigurationView(translator_id id,
1689 BMessage* ioExtension, BView** _view, BRect* _extent)
1691 if (_view == NULL || _extent == NULL)
1692 return B_BAD_VALUE;
1694 BAutolock locker(fPrivate);
1696 BTranslator* translator = fPrivate->FindTranslator(id);
1697 if (translator == NULL)
1698 return B_NO_TRANSLATOR;
1700 return translator->MakeConfigurationView(ioExtension, _view, _extent);
1704 BTranslatorReleaseDelegate*
1705 BTranslatorRoster::AcquireTranslator(int32 id)
1707 BAutolock locker(fPrivate);
1709 BTranslator* translator = fPrivate->FindTranslator(id);
1710 if (translator == NULL)
1711 return NULL;
1713 translator->Acquire();
1714 return new BTranslatorReleaseDelegate(translator);
1719 Gets the configuration setttings for the translator
1720 specified by \a id and puts them into \a ioExtension.
1722 \param id identifies which translator you want the input formats for
1723 \param ioExtension the configuration data for the translator
1725 \return B_OK if successful,
1726 B_BAD_VALUE, if \a ioExtension is NULL
1727 B_NO_TRANSLATOR, \id didn't identify an existing translator
1729 status_t
1730 BTranslatorRoster::GetConfigurationMessage(translator_id id,
1731 BMessage* ioExtension)
1733 if (!ioExtension)
1734 return B_BAD_VALUE;
1736 BAutolock locker(fPrivate);
1738 BTranslator* translator = fPrivate->FindTranslator(id);
1739 if (translator == NULL)
1740 return B_NO_TRANSLATOR;
1742 return translator->GetConfigurationMessage(ioExtension);
1747 Gets the entry_ref for the given translator (of course, this works only
1748 for disk based translators).
1750 \param id identifies which translator you want the input formats for
1751 \param ref the entry ref is stored there
1753 \return B_OK if successful,
1754 B_ERROR, if this is not a disk based translator
1755 B_BAD_VALUE, if \a ref is NULL
1756 B_NO_TRANSLATOR, \id didn't identify an existing translator
1758 status_t
1759 BTranslatorRoster::GetRefFor(translator_id id, entry_ref* ref)
1761 if (ref == NULL)
1762 return B_BAD_VALUE;
1764 return fPrivate->GetRefFor(id, *ref);
1768 status_t
1769 BTranslatorRoster::StartWatching(BMessenger target)
1771 return fPrivate->StartWatching(target);
1775 status_t
1776 BTranslatorRoster::StopWatching(BMessenger target)
1778 return fPrivate->StopWatching(target);
1782 // #pragma mark - private
1785 BTranslatorRoster::BTranslatorRoster(const BTranslatorRoster &other)
1790 BTranslatorRoster &
1791 BTranslatorRoster::operator=(const BTranslatorRoster &tr)
1793 return *this;
1797 #if __GNUC__ == 2 // gcc 2
1799 /*static*/ const char*
1800 BTranslatorRoster::Version(int32* outCurVersion, int32* outMinVersion,
1801 int32 inAppVersion)
1803 if (!outCurVersion || !outMinVersion)
1804 return "";
1806 static char vString[50];
1807 static char vDate[] = __DATE__;
1808 if (!vString[0]) {
1809 sprintf(vString, "Translation Kit v%d.%d.%d %s\n",
1810 int(B_TRANSLATION_MAJOR_VERSION(B_TRANSLATION_CURRENT_VERSION)),
1811 int(B_TRANSLATION_MINOR_VERSION(B_TRANSLATION_CURRENT_VERSION)),
1812 int(B_TRANSLATION_REVISION_VERSION(B_TRANSLATION_CURRENT_VERSION)),
1813 vDate);
1815 *outCurVersion = B_TRANSLATION_CURRENT_VERSION;
1816 *outMinVersion = B_TRANSLATION_MIN_VERSION;
1817 return vString;
1820 #endif // gcc 2
1823 void BTranslatorRoster::ReservedTranslatorRoster1() {}
1824 void BTranslatorRoster::ReservedTranslatorRoster2() {}
1825 void BTranslatorRoster::ReservedTranslatorRoster3() {}
1826 void BTranslatorRoster::ReservedTranslatorRoster4() {}
1827 void BTranslatorRoster::ReservedTranslatorRoster5() {}
1828 void BTranslatorRoster::ReservedTranslatorRoster6() {}