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 us into the BApplication 172 if (be_app != NULL && be_app->Lock()) { 173 be_app->AddHandler(this); 174 be_app->Unlock(); 175 } 176 } 177 178 179 BTranslatorRoster::Private::~Private() 180 { 181 stop_watching(this); 182 183 if (Looper() && LockLooper()) { 184 BLooper* looper = Looper(); 185 Looper()->RemoveHandler(this); 186 looper->Unlock(); 187 } 188 189 // Release all translators, so that they can delete themselves 190 191 TranslatorMap::iterator iterator = fTranslators.begin(); 192 std::set<image_id> images; 193 194 while (iterator != fTranslators.end()) { 195 BTranslator* translator = iterator->second.translator; 196 197 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 image_id image = -1; 1092 1093 while (iterator != fTranslators.end()) { 1094 TranslatorMap::iterator next = iterator; 1095 next++; 1096 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); 1104 1105 fTranslators.erase(iterator); 1106 } 1107 1108 iterator = next; 1109 } 1110 1111 _NotifyListeners(update); 1112 } 1113 1114 1115 void 1116 BTranslatorRoster::Private::_EntryAdded(const node_ref& nodeRef, 1117 const char* name) 1118 { 1119 entry_ref ref; 1120 ref.device = nodeRef.device; 1121 ref.directory = nodeRef.node; 1122 ref.set_name(name); 1123 1124 _EntryAdded(ref); 1125 } 1126 1127 1128 /*! 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. 1133 1134 Called by the node monitor handling. 1135 */ 1136 void 1137 BTranslatorRoster::Private::_EntryAdded(const entry_ref& ref) 1138 { 1139 BEntry entry; 1140 if (entry.SetTo(&ref) != B_OK || !entry.IsFile()) 1141 return; 1142 1143 if (fLazyScanning) { 1144 fRescanEntries.insert(ref); 1145 return; 1146 } 1147 1148 BMessage update(B_TRANSLATOR_ADDED); 1149 int32 count = 0; 1150 CreateTranslators(ref, count, &update); 1151 1152 _NotifyListeners(update); 1153 } 1154 1155 1156 void 1157 BTranslatorRoster::Private::_NotifyListeners(BMessage& update) const 1158 { 1159 MessengerList::const_iterator iterator = fMessengers.begin(); 1160 1161 while (iterator != fMessengers.end()) { 1162 (*iterator).SendMessage(&update); 1163 iterator++; 1164 } 1165 } 1166 1167 1168 // #pragma mark - 1169 1170 1171 BTranslatorReleaseDelegate::BTranslatorReleaseDelegate(BTranslator* translator) 1172 : 1173 fUnderlying(translator) 1174 { 1175 } 1176 1177 1178 void 1179 BTranslatorReleaseDelegate::Release() 1180 { 1181 fUnderlying->Release(); 1182 // ReleaseDelegate is only allowed to release a translator once. 1183 delete this; 1184 } 1185 1186 1187 // #pragma mark - 1188 1189 1190 BTranslatorRoster::BTranslatorRoster() 1191 { 1192 _Initialize(); 1193 } 1194 1195 1196 BTranslatorRoster::BTranslatorRoster(BMessage* model) 1197 { 1198 _Initialize(); 1199 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); 1209 } 1210 } 1211 } 1212 } 1213 1214 1215 BTranslatorRoster::~BTranslatorRoster() 1216 { 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; 1222 1223 delete fPrivate; 1224 } 1225 1226 1227 void 1228 BTranslatorRoster::_Initialize() 1229 { 1230 fPrivate = new BTranslatorRoster::Private(); 1231 } 1232 1233 1234 status_t 1235 BTranslatorRoster::Archive(BMessage* into, bool deep) const 1236 { 1237 status_t status = BArchivable::Archive(into, deep); 1238 if (status != B_OK) 1239 return status; 1240 1241 return fPrivate->StoreTranslators(*into); 1242 } 1243 1244 1245 BArchivable* 1246 BTranslatorRoster::Instantiate(BMessage* from) 1247 { 1248 if (!from || !validate_instantiation(from, "BTranslatorRoster")) 1249 return NULL; 1250 1251 return new BTranslatorRoster(from); 1252 } 1253 1254 1255 BTranslatorRoster* 1256 BTranslatorRoster::Default() 1257 { 1258 static int32 lock = 0; 1259 1260 if (sDefaultRoster != NULL) 1261 return sDefaultRoster; 1262 1263 if (atomic_add(&lock, 1) != 0) { 1264 // Just wait for the default translator to be instantiated 1265 while (sDefaultRoster == NULL) 1266 snooze(10000); 1267 1268 atomic_add(&lock, -1); 1269 return sDefaultRoster; 1270 } 1271 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); 1277 1278 sDefaultRoster = roster; 1279 // this will unlock any other threads waiting for 1280 // the default roster to become available 1281 } 1282 1283 atomic_add(&lock, -1); 1284 return sDefaultRoster; 1285 } 1286 1287 1288 /*! 1289 This function takes a string of colon delimited paths, and adds 1290 the translators from those paths to this BTranslatorRoster. 1291 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. 1296 */ 1297 status_t 1298 BTranslatorRoster::AddTranslators(const char* path) 1299 { 1300 if (path == NULL) 1301 path = getenv("TRANSLATORS"); 1302 if (path == NULL) { 1303 fPrivate->AddDefaultPaths(); 1304 return B_OK; 1305 } 1306 1307 return fPrivate->AddPaths(path); 1308 } 1309 1310 1311 /*! 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. 1316 1317 \param translator the translator to be added to the 1318 BTranslatorRoster 1319 1320 \return B_BAD_VALUE, if translator is NULL, 1321 B_OK if all went well 1322 */ 1323 status_t 1324 BTranslatorRoster::AddTranslator(BTranslator* translator) 1325 { 1326 if (!translator) 1327 return B_BAD_VALUE; 1328 1329 return fPrivate->AddTranslator(translator); 1330 } 1331 1332 1333 bool 1334 BTranslatorRoster::IsTranslator(entry_ref* ref) 1335 { 1336 if (ref == NULL) 1337 return false; 1338 1339 BPath path(ref); 1340 image_id image = load_add_on(path.Path()); 1341 if (image < B_OK) 1342 return false; 1343 1344 // Function pointer used to create post R4.5 style translators 1345 BTranslator* (*makeNthTranslator)(int32 n, image_id you, uint32 flags, ...); 1346 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); 1353 } 1354 1355 unload_add_on(image); 1356 return status == B_OK; 1357 } 1358 1359 1360 /*! 1361 This function determines which translator is best suited 1362 to convert the data from \a source. 1363 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. 1372 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 1376 */ 1377 status_t 1378 BTranslatorRoster::Identify(BPositionIO* source, BMessage* ioExtension, 1379 translator_info* _info, uint32 hintType, const char* hintMIME, 1380 uint32 wantType) 1381 { 1382 if (source == NULL || _info == NULL) 1383 return B_BAD_VALUE; 1384 1385 return fPrivate->Identify(source, ioExtension, hintType, hintMIME, wantType, 1386 _info); 1387 } 1388 1389 1390 /*! 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. 1396 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. 1408 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 1412 */ 1413 status_t 1414 BTranslatorRoster::GetTranslators(BPositionIO* source, BMessage* ioExtension, 1415 translator_info** _info, int32* _numInfo, uint32 hintType, 1416 const char* hintMIME, uint32 wantType) 1417 { 1418 if (source == NULL || _info == NULL || _numInfo == NULL) 1419 return B_BAD_VALUE; 1420 1421 return fPrivate->GetTranslators(source, ioExtension, hintType, hintMIME, 1422 wantType, _info, _numInfo); 1423 } 1424 1425 1426 /*! 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. 1430 1431 \param _ids the array is stored there (you own the array). 1432 \param _count number of IDs in the array. 1433 */ 1434 status_t 1435 BTranslatorRoster::GetAllTranslators(translator_id** _ids, int32* _count) 1436 { 1437 if (_ids == NULL || _count == NULL) 1438 return B_BAD_VALUE; 1439 1440 return fPrivate->GetAllTranslators(_ids, _count); 1441 } 1442 1443 1444 /*! 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. 1448 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 1453 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 1457 */ 1458 status_t 1459 BTranslatorRoster::GetTranslatorInfo(translator_id id, const char** _name, 1460 const char** _info, int32* _version) 1461 { 1462 if (_name == NULL && _info == NULL && _version == NULL) 1463 return B_BAD_VALUE; 1464 1465 BAutolock locker(fPrivate); 1466 1467 BTranslator* translator = fPrivate->FindTranslator(id); 1468 if (translator == NULL) 1469 return B_NO_TRANSLATOR; 1470 1471 if (_name) 1472 *_name = translator->TranslatorName(); 1473 if (_info) 1474 *_info = translator->TranslatorInfo(); 1475 if (_version) 1476 *_version = translator->TranslatorVersion(); 1477 1478 return B_OK; 1479 } 1480 1481 1482 /*! 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. 1486 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 1490 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 1494 */ 1495 status_t 1496 BTranslatorRoster::GetInputFormats(translator_id id, 1497 const translation_format** _formats, int32* _numFormats) 1498 { 1499 if (_formats == NULL || _numFormats == NULL) 1500 return B_BAD_VALUE; 1501 1502 BAutolock locker(fPrivate); 1503 1504 BTranslator* translator = fPrivate->FindTranslator(id); 1505 if (translator == NULL) 1506 return B_NO_TRANSLATOR; 1507 1508 *_formats = translator->InputFormats(_numFormats); 1509 return B_OK; 1510 } 1511 1512 1513 /*! 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. 1517 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 1521 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 1525 */ 1526 status_t 1527 BTranslatorRoster::GetOutputFormats(translator_id id, 1528 const translation_format** _formats, int32* _numFormats) 1529 { 1530 if (_formats == NULL || _numFormats == NULL) 1531 return B_BAD_VALUE; 1532 1533 BAutolock locker(fPrivate); 1534 1535 BTranslator* translator = fPrivate->FindTranslator(id); 1536 if (translator == NULL) 1537 return B_NO_TRANSLATOR; 1538 1539 *_formats = translator->OutputFormats(_numFormats); 1540 return B_OK; 1541 } 1542 1543 1544 /*! 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. 1548 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. 1559 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 1563 */ 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) 1568 { 1569 if (source == NULL || destination == NULL) 1570 return B_BAD_VALUE; 1571 1572 translator_info infoBuffer; 1573 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; 1580 1581 info = &infoBuffer; 1582 } 1583 1584 if (!fPrivate->Lock()) 1585 return B_ERROR; 1586 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! 1592 } 1593 1594 fPrivate->Unlock(); 1595 1596 if (translator == NULL) 1597 return B_NO_TRANSLATOR; 1598 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); 1606 } 1607 translator->Release(); 1608 1609 return status; 1610 } 1611 1612 1613 /*! 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. 1618 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. 1624 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 1628 */ 1629 status_t 1630 BTranslatorRoster::Translate(translator_id id, BPositionIO* source, 1631 BMessage* ioExtension, BPositionIO* destination, uint32 wantOutType) 1632 { 1633 if (source == NULL || destination == NULL) 1634 return B_BAD_VALUE; 1635 1636 if (!fPrivate->Lock()) 1637 return B_ERROR; 1638 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! 1644 } 1645 1646 fPrivate->Unlock(); 1647 1648 if (translator == NULL) 1649 return B_NO_TRANSLATOR; 1650 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); 1664 } 1665 } 1666 } else 1667 status = pos < 0 ? (status_t)pos : B_IO_ERROR; 1668 translator->Release(); 1669 1670 return status; 1671 } 1672 1673 1674 /*! 1675 Creates a BView in \a _view for configuring the translator specified 1676 by \a id. Not all translators support this, though. 1677 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 1682 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 1686 */ 1687 status_t 1688 BTranslatorRoster::MakeConfigurationView(translator_id id, 1689 BMessage* ioExtension, BView** _view, BRect* _extent) 1690 { 1691 if (_view == NULL || _extent == NULL) 1692 return B_BAD_VALUE; 1693 1694 BAutolock locker(fPrivate); 1695 1696 BTranslator* translator = fPrivate->FindTranslator(id); 1697 if (translator == NULL) 1698 return B_NO_TRANSLATOR; 1699 1700 return translator->MakeConfigurationView(ioExtension, _view, _extent); 1701 } 1702 1703 1704 BTranslatorReleaseDelegate* 1705 BTranslatorRoster::AcquireTranslator(int32 id) 1706 { 1707 BAutolock locker(fPrivate); 1708 1709 BTranslator* translator = fPrivate->FindTranslator(id); 1710 if (translator == NULL) 1711 return NULL; 1712 1713 translator->Acquire(); 1714 return new BTranslatorReleaseDelegate(translator); 1715 } 1716 1717 1718 /*! 1719 Gets the configuration setttings for the translator 1720 specified by \a id and puts them into \a ioExtension. 1721 1722 \param id identifies which translator you want the input formats for 1723 \param ioExtension the configuration data for the translator 1724 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 1728 */ 1729 status_t 1730 BTranslatorRoster::GetConfigurationMessage(translator_id id, 1731 BMessage* ioExtension) 1732 { 1733 if (!ioExtension) 1734 return B_BAD_VALUE; 1735 1736 BAutolock locker(fPrivate); 1737 1738 BTranslator* translator = fPrivate->FindTranslator(id); 1739 if (translator == NULL) 1740 return B_NO_TRANSLATOR; 1741 1742 return translator->GetConfigurationMessage(ioExtension); 1743 } 1744 1745 1746 /*! 1747 Gets the entry_ref for the given translator (of course, this works only 1748 for disk based translators). 1749 1750 \param id identifies which translator you want the input formats for 1751 \param ref the entry ref is stored there 1752 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 1757 */ 1758 status_t 1759 BTranslatorRoster::GetRefFor(translator_id id, entry_ref* ref) 1760 { 1761 if (ref == NULL) 1762 return B_BAD_VALUE; 1763 1764 return fPrivate->GetRefFor(id, *ref); 1765 } 1766 1767 1768 status_t 1769 BTranslatorRoster::StartWatching(BMessenger target) 1770 { 1771 return fPrivate->StartWatching(target); 1772 } 1773 1774 1775 status_t 1776 BTranslatorRoster::StopWatching(BMessenger target) 1777 { 1778 return fPrivate->StopWatching(target); 1779 } 1780 1781 1782 // #pragma mark - private 1783 1784 1785 BTranslatorRoster::BTranslatorRoster(const BTranslatorRoster &other) 1786 { 1787 } 1788 1789 1790 BTranslatorRoster & 1791 BTranslatorRoster::operator=(const BTranslatorRoster &tr) 1792 { 1793 return *this; 1794 } 1795 1796 1797 #if __GNUC__ == 2 // gcc 2 1798 1799 /*static*/ const char* 1800 BTranslatorRoster::Version(int32* outCurVersion, int32* outMinVersion, 1801 int32 inAppVersion) 1802 { 1803 if (!outCurVersion || !outMinVersion) 1804 return ""; 1805 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); 1814 } 1815 *outCurVersion = B_TRANSLATION_CURRENT_VERSION; 1816 *outMinVersion = B_TRANSLATION_MIN_VERSION; 1817 return vString; 1818 } 1819 1820 #endif // gcc 2 1821 1822 1823 void BTranslatorRoster::ReservedTranslatorRoster1() {} 1824 void BTranslatorRoster::ReservedTranslatorRoster2() {} 1825 void BTranslatorRoster::ReservedTranslatorRoster3() {} 1826 void BTranslatorRoster::ReservedTranslatorRoster4() {} 1827 void BTranslatorRoster::ReservedTranslatorRoster5() {} 1828 void BTranslatorRoster::ReservedTranslatorRoster6() {} 1829