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