1 /* 2 Open Tracker License 3 4 Terms and Conditions 5 6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved. 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy of 9 this software and associated documentation files (the "Software"), to deal in 10 the Software without restriction, including without limitation the rights to 11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 of the Software, and to permit persons to whom the Software is furnished to do 13 so, subject to the following conditions: 14 15 The above copyright notice and this permission notice applies to all licensees 16 and shall be included in all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION 23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 25 Except as contained in this notice, the name of Be Incorporated shall not be 26 used in advertising or otherwise to promote the sale, use or other dealings in 27 this Software without prior written authorization from Be Incorporated. 28 29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks 30 of Be Incorporated in the United States and other countries. Other brand product 31 names are registered trademarks or trademarks of their respective holders. 32 All rights reserved. 33 */ 34 35 #define USE_RESOURCES 1 36 37 #include "ResourceSet.h" 38 39 #include <Application.h> 40 #include <Autolock.h> 41 #include <Bitmap.h> 42 #include <DataIO.h> 43 #include <Debug.h> 44 #include <Entry.h> 45 #include <File.h> 46 #include <Path.h> 47 #include <String.h> 48 49 50 #if USE_RESOURCES 51 #include <Resources.h> 52 #endif 53 54 55 #include <stdlib.h> 56 #include <unistd.h> 57 #include <ctype.h> 58 59 #if USE_RESOURCES 60 #define RESOURCES_ONLY(x) x 61 #else 62 #define RESOURCES_ONLY(x) 63 #endif 64 65 namespace TResourcePrivate { 66 67 class TypeObject { 68 public: 69 TypeObject() 70 : fDeleteOK(false) 71 { 72 } 73 74 virtual ~TypeObject() 75 { 76 if (!fDeleteOK) 77 debugger("deleting object owned by BResourceSet"); 78 } 79 80 void Delete() 81 { 82 fDeleteOK = true; 83 } 84 85 private: 86 TypeObject(const TypeObject &); 87 TypeObject &operator=(const TypeObject &); 88 bool operator==(const TypeObject &); 89 bool operator!=(const TypeObject &); 90 91 bool fDeleteOK; 92 }; 93 94 class BitmapTypeItem : public BBitmap, public TypeObject { 95 public: 96 BitmapTypeItem(BRect bounds, uint32 flags, color_space depth, 97 int32 bytesPerRow=B_ANY_BYTES_PER_ROW, screen_id screenID 98 = B_MAIN_SCREEN_ID) 99 : BBitmap(bounds, flags, depth, bytesPerRow, screenID) 100 { 101 } 102 103 BitmapTypeItem(const BBitmap* source, bool accepts_views = false, 104 bool need_contiguous = false) 105 : BBitmap(source, accepts_views, need_contiguous) 106 { 107 } 108 109 BitmapTypeItem(BMessage* data) 110 : BBitmap(data) 111 { 112 } 113 114 virtual ~BitmapTypeItem() 115 { 116 } 117 }; 118 119 class StringBlockTypeItem : public TStringBlock, public TypeObject { 120 public: 121 StringBlockTypeItem(BDataIO* data) 122 : TStringBlock(data) 123 { 124 } 125 126 StringBlockTypeItem(const void* block, size_t size) 127 : TStringBlock(block, size) 128 { 129 } 130 131 virtual ~StringBlockTypeItem() 132 { 133 } 134 }; 135 136 class TypeItem { 137 public: 138 TypeItem(int32 id, const char* name, const void* data, size_t size) 139 : fID(id), fName(name), 140 fData(const_cast<void*>(data)), fSize(size), fObject(0), 141 fOwnData(false), fSourceIsFile(false) 142 { 143 } 144 145 TypeItem(int32 id, const char* name, BFile* file) 146 : fID(id), 147 fName(name), 148 fData(NULL), 149 fSize(0), 150 fObject(NULL), 151 fOwnData(true), 152 fSourceIsFile(false) 153 { 154 off_t size; 155 if (file->GetSize(&size) == B_OK) { 156 fSize = (size_t)size; 157 fData = malloc(fSize); 158 if (file->ReadAt(0, fData, fSize) < B_OK ) { 159 free(fData); 160 fData = NULL; 161 fSize = 0; 162 } 163 } 164 } 165 166 virtual ~TypeItem() 167 { 168 if (fOwnData) { 169 free(fData); 170 fData = NULL; 171 fSize = 0; 172 } 173 SetObject(NULL); 174 } 175 176 int32 ID() const 177 { return fID; } 178 179 const char* Name() const 180 { return fName.String(); } 181 182 const void* Data() const 183 { return fData; } 184 185 size_t Size() const 186 { return fSize; } 187 188 void SetObject(TypeObject* object) 189 { 190 if (object == fObject) 191 return; 192 if (fObject) 193 fObject->Delete(); 194 fObject = object; 195 } 196 197 TypeObject* Object() const 198 { return fObject; } 199 200 void SetSourceIsFile(bool state) 201 { fSourceIsFile = state; } 202 203 bool SourceIsFile() const 204 { return fSourceIsFile; } 205 206 private: 207 int32 fID; 208 BString fName; 209 void* fData; 210 size_t fSize; 211 TypeObject* fObject; 212 bool fOwnData; 213 bool fSourceIsFile; 214 }; 215 216 static bool FreeTypeItemFunc(void* item) 217 { 218 delete reinterpret_cast<TypeItem*>(item); 219 return false; 220 } 221 222 class TypeList { 223 public: 224 TypeList(type_code type) 225 : fType(type) 226 { 227 } 228 229 virtual ~TypeList() 230 { 231 fItems.DoForEach(FreeTypeItemFunc); 232 fItems.MakeEmpty(); 233 } 234 235 type_code Type() const 236 { return fType; } 237 238 TypeItem* FindItemByID(int32 id) 239 { 240 for (int32 i = 0; i < fItems.CountItems(); i++ ) { 241 TypeItem* it = (TypeItem*)fItems.ItemAt(i); 242 if (it->ID() == id) 243 return it; 244 } 245 return NULL; 246 } 247 248 TypeItem* FindItemByName(const char* name) 249 { 250 for (int32 i = 0; i < fItems.CountItems(); i++ ) { 251 TypeItem* it = (TypeItem*)fItems.ItemAt(i); 252 if (strcmp(it->Name(), name) == 0) 253 return it; 254 } 255 return NULL; 256 } 257 258 void AddItem(TypeItem* item) 259 { 260 fItems.AddItem(item); 261 } 262 263 private: 264 type_code fType; 265 BList fItems; 266 }; 267 268 } 269 270 using namespace TResourcePrivate; 271 272 // #pragma mark - 273 // ----------------------------- TStringBlock ----------------------------- 274 275 276 TStringBlock::TStringBlock(BDataIO* data) 277 : fNumEntries(0), 278 fIndex(0), 279 fStrings(NULL), 280 fOwnData(true) 281 { 282 fStrings = (char*)malloc(1024); 283 size_t pos = 0; 284 ssize_t amount; 285 while ((amount=data->Read(fStrings + pos, 1024)) == 1024) { 286 pos += amount; 287 fStrings = (char*)realloc(fStrings, pos + 1024); 288 } 289 if (amount > 0) 290 pos += amount; 291 292 fNumEntries = PreIndex(fStrings, amount); 293 fIndex = (size_t*)malloc(sizeof(size_t) * fNumEntries); 294 MakeIndex(fStrings, amount, fNumEntries, fIndex); 295 } 296 297 298 TStringBlock::TStringBlock(const void* block, size_t size) 299 : 300 fNumEntries(0), 301 fIndex(0), 302 fStrings(NULL), 303 fOwnData(false) 304 { 305 fIndex = (size_t*)const_cast<void*>(block); 306 fStrings = (char*)const_cast<void*>(block); 307 308 // Figure out how many entries there are. 309 size_t last_off = 0; 310 while (fIndex[fNumEntries] > last_off && fIndex[fNumEntries] < size ) { 311 last_off = fIndex[fNumEntries]; 312 fNumEntries++; 313 } 314 } 315 316 317 TStringBlock::~TStringBlock() 318 { 319 if (fOwnData) { 320 free(fIndex); 321 free(fStrings); 322 } 323 fIndex = 0; 324 fStrings = NULL; 325 } 326 327 328 const char* 329 TStringBlock::String(size_t index) const 330 { 331 if (index >= fNumEntries) 332 return NULL; 333 334 return fStrings + fIndex[index]; 335 } 336 337 338 size_t 339 TStringBlock::PreIndex(char* strings, ssize_t len) 340 { 341 size_t count = 0; 342 343 char* orig = strings; 344 char* end = strings + len; 345 bool in_cr = false; 346 bool first = true; 347 bool skipping = false; 348 while (orig < end) { 349 if (*orig == '\n' || *orig == '\r' || *orig == 0) { 350 if (!in_cr && *orig == '\r') 351 in_cr = true; 352 if (in_cr && *orig == '\n') { 353 orig++; 354 continue; 355 } 356 first = true; 357 skipping = false; 358 *strings = 0; 359 count++; 360 } else if (first && *orig == '#') { 361 skipping = true; 362 first = false; 363 orig++; 364 continue; 365 } else if (skipping) { 366 orig++; 367 continue; 368 } else if (*orig == '\\' && (orig + 1) < end) { 369 orig++; 370 switch (*orig) { 371 case '\\': 372 *strings = '\\'; 373 break; 374 375 case '\n': 376 *strings = '\n'; 377 break; 378 379 case '\r': 380 *strings = '\r'; 381 break; 382 383 case '\t': 384 *strings = '\t'; 385 break; 386 387 default: 388 *strings = *orig; 389 break; 390 } 391 } else 392 *strings = *orig; 393 394 orig++; 395 strings++; 396 } 397 398 return count; 399 } 400 401 402 void 403 TStringBlock::MakeIndex(const char* strings, ssize_t len, 404 size_t indexLength, size_t* resultingIndex) 405 { 406 *resultingIndex++ = 0; 407 indexLength--; 408 409 ssize_t pos = 0; 410 while (pos < len && indexLength > 0) { 411 if (strings[pos] == 0 ) { 412 *resultingIndex++ = (size_t)pos + 1; 413 indexLength--; 414 } 415 pos++; 416 } 417 } 418 419 420 // #pragma mark - 421 422 423 #if USE_RESOURCES 424 static bool 425 FreeResourcesFunc(void* item) 426 { 427 delete reinterpret_cast<BResources*>(item); 428 return false; 429 } 430 #endif 431 432 433 static bool 434 FreePathFunc(void* item) 435 { 436 delete reinterpret_cast<BPath*>(item); 437 return false; 438 } 439 440 441 static bool 442 FreeTypeFunc(void* item) 443 { 444 delete reinterpret_cast<TypeList*>(item); 445 return false; 446 } 447 448 449 // #pragma mark - 450 // ----------------------------- TResourceSet ----------------------------- 451 452 453 TResourceSet::TResourceSet() 454 { 455 } 456 457 458 TResourceSet::~TResourceSet() 459 { 460 BAutolock lock(&fLock); 461 #if USE_RESOURCES 462 fResources.DoForEach(FreeResourcesFunc); 463 fResources.MakeEmpty(); 464 #endif 465 fDirectories.DoForEach(FreePathFunc); 466 fDirectories.MakeEmpty(); 467 fTypes.DoForEach(FreeTypeFunc); 468 fTypes.MakeEmpty(); 469 } 470 471 472 status_t 473 TResourceSet::AddResources(BResources* RESOURCES_ONLY(resources)) 474 { 475 #if USE_RESOURCES 476 if (!resources) 477 return B_BAD_VALUE; 478 479 BAutolock lock(&fLock); 480 status_t err = fResources.AddItem(resources) ? B_OK : B_ERROR; 481 if (err != B_OK) 482 delete resources; 483 return err; 484 #else 485 return B_ERROR; 486 #endif 487 } 488 489 490 status_t 491 TResourceSet::AddDirectory(const char* fullPath) 492 { 493 if (!fullPath) 494 return B_BAD_VALUE; 495 496 BPath* path = new BPath(fullPath); 497 status_t err = path->InitCheck(); 498 if (err != B_OK) { 499 delete path; 500 return err; 501 } 502 503 BAutolock lock(&fLock); 504 err = fDirectories.AddItem(path) ? B_OK : B_ERROR; 505 if (err != B_OK) 506 delete path; 507 508 return err; 509 } 510 511 512 status_t 513 TResourceSet::AddEnvDirectory(const char* in, const char* defaultValue) 514 { 515 BString buf; 516 status_t err = ExpandString(&buf, in); 517 518 if (err != B_OK) { 519 if (defaultValue) 520 return AddDirectory(defaultValue); 521 return err; 522 } 523 524 return AddDirectory(buf.String()); 525 } 526 527 528 status_t 529 TResourceSet::ExpandString(BString* out, const char* in) 530 { 531 const char* start = in; 532 while (*in) { 533 if (*in == '$') { 534 if (start < in) 535 out->Append(start, (int32)(in - start)); 536 537 in++; 538 char variableName[1024]; 539 size_t i = 0; 540 if (*in == '{') { 541 in++; 542 while (*in && *in != '}' && i < sizeof(variableName) - 1) 543 variableName[i++] = *in++; 544 545 if (*in) 546 in++; 547 548 } else 549 while ((isalnum(*in) || *in == '_') && i 550 < sizeof(variableName) - 1) 551 variableName[i++] = *in++; 552 553 start = in; 554 555 variableName[i] = '\0'; 556 557 const char* val = getenv(variableName); 558 if (!val) { 559 PRINT(("Error: env var %s not found.\n", &variableName[0])); 560 return B_NAME_NOT_FOUND; 561 } 562 563 status_t err = ExpandString(out, val); 564 if (err != B_OK) 565 return err; 566 567 } else if (*in == '\\') { 568 if (start < in) 569 out->Append(start, (int32)(in - start)); 570 in++; 571 start = in; 572 in++; 573 } else 574 in++; 575 } 576 577 if (start < in) 578 out->Append(start, (int32)(in - start)); 579 580 return B_OK; 581 } 582 583 584 const void* 585 TResourceSet::FindResource(type_code type, int32 id, size_t* outSize) 586 { 587 TypeItem* item = FindItemID(type, id); 588 589 if (outSize) 590 *outSize = item ? item->Size() : 0; 591 592 return item ? item->Data() : NULL; 593 } 594 595 596 const void* 597 TResourceSet::FindResource(type_code type, const char* name, size_t* outSize) 598 { 599 TypeItem* item = FindItemName(type, name); 600 601 if (outSize) 602 *outSize = item ? item->Size() : 0; 603 604 return item ? item->Data() : NULL; 605 } 606 607 608 const BBitmap* 609 TResourceSet::FindBitmap(type_code type, int32 id) 610 { 611 return ReturnBitmapItem(type, FindItemID(type, id)); 612 } 613 614 615 const BBitmap* 616 TResourceSet::FindBitmap(type_code type, const char* name) 617 { 618 return ReturnBitmapItem(type, FindItemName(type, name)); 619 } 620 621 622 const TStringBlock* 623 TResourceSet::FindStringBlock(type_code type, int32 id) 624 { 625 return ReturnStringBlockItem(FindItemID(type, id)); 626 } 627 628 629 const TStringBlock* 630 TResourceSet::FindStringBlock(type_code type, const char* name) 631 { 632 return ReturnStringBlockItem(FindItemName(type, name)); 633 } 634 635 636 const char* 637 TResourceSet::FindString(type_code type, int32 id, uint32 index) 638 { 639 const TStringBlock* stringBlock = FindStringBlock(type, id); 640 if (!stringBlock) 641 return NULL; 642 643 return stringBlock->String(index); 644 } 645 646 647 const char* 648 TResourceSet::FindString(type_code type, const char* name, uint32 index) 649 { 650 const TStringBlock* stringBlock = FindStringBlock(type, name); 651 if (!stringBlock) 652 return NULL; 653 654 return stringBlock->String(index); 655 } 656 657 658 TypeList* 659 TResourceSet::FindTypeList(type_code type) 660 { 661 BAutolock lock(&fLock); 662 663 int32 count = fTypes.CountItems(); 664 for (int32 i = 0; i < count; i++ ) { 665 TypeList* list = (TypeList*)fTypes.ItemAt(i); 666 if (list && list->Type() == type) 667 return list; 668 } 669 670 return NULL; 671 } 672 673 674 TypeItem* 675 TResourceSet::FindItemID(type_code type, int32 id) 676 { 677 TypeList* list = FindTypeList(type); 678 TypeItem* item = NULL; 679 680 if (list) item = list->FindItemByID(id); 681 682 if (!item) 683 item = LoadResource(type, id, 0, &list); 684 685 return item; 686 } 687 688 689 TypeItem* 690 TResourceSet::FindItemName(type_code type, const char* name) 691 { 692 TypeList* list = FindTypeList(type); 693 TypeItem* item = NULL; 694 695 if (list) 696 item = list->FindItemByName(name); 697 698 if (!item) 699 item = LoadResource(type, -1, name, &list); 700 701 return item; 702 } 703 704 705 TypeItem* 706 TResourceSet::LoadResource(type_code type, int32 id, const char* name, 707 TypeList** inOutList) 708 { 709 TypeItem* item = NULL; 710 711 if (name) { 712 BEntry entry; 713 714 // If a named resource, first look in directories. 715 fLock.Lock(); 716 int32 count = fDirectories.CountItems(); 717 for (int32 i = 0; item == 0 && i < count; i++) { 718 BPath* dir = (BPath*)fDirectories.ItemAt(i); 719 if (dir) { 720 fLock.Unlock(); 721 BPath path(dir->Path(), name); 722 if (entry.SetTo(path.Path(), true) == B_OK ) { 723 BFile file(&entry, B_READ_ONLY); 724 if (file.InitCheck() == B_OK ) { 725 item = new TypeItem(id, name, &file); 726 item->SetSourceIsFile(true); 727 } 728 } 729 fLock.Lock(); 730 } 731 } 732 fLock.Unlock(); 733 } 734 735 #if USE_RESOURCES 736 if (!item) { 737 // Look through resource objects for data. 738 fLock.Lock(); 739 int32 count = fResources.CountItems(); 740 for (int32 i = 0; item == 0 && i < count; i++ ) { 741 BResources* resource = (BResources*)fResources.ItemAt(i); 742 if (resource) { 743 const void* data = NULL; 744 size_t size = 0; 745 if (id >= 0) 746 data = resource->LoadResource(type, id, &size); 747 else if (name != NULL) 748 data = resource->LoadResource(type, name, &size); 749 750 if (data && size) { 751 item = new TypeItem(id, name, data, size); 752 item->SetSourceIsFile(false); 753 } 754 } 755 } 756 fLock.Unlock(); 757 } 758 #endif 759 760 if (item) { 761 TypeList* list = inOutList ? *inOutList : NULL; 762 if (!list) { 763 // Don't currently have a list for this type -- check if there is 764 // already one. 765 list = FindTypeList(type); 766 } 767 768 BAutolock lock(&fLock); 769 770 if (!list) { 771 // Need to make a new list for this type. 772 list = new TypeList(type); 773 fTypes.AddItem(list); 774 } 775 if (inOutList) 776 *inOutList = list; 777 778 list->AddItem(item); 779 } 780 781 return item; 782 } 783 784 785 BBitmap* 786 TResourceSet::ReturnBitmapItem(type_code, TypeItem* from) 787 { 788 if (!from) 789 return NULL; 790 791 TypeObject* obj = from->Object(); 792 BitmapTypeItem* bitmap = dynamic_cast<BitmapTypeItem*>(obj); 793 if (bitmap) 794 return bitmap; 795 796 // Can't change an existing object. 797 if (obj) 798 return NULL; 799 800 // Don't have a bitmap in the item -- we'll try to create one. 801 BMemoryIO stream(from->Data(), from->Size()); 802 803 // Try to read as an archived bitmap. 804 stream.Seek(0, SEEK_SET); 805 BMessage archive; 806 if (archive.Unflatten(&stream) == B_OK ) { 807 bitmap = new BitmapTypeItem(&archive); 808 if (bitmap && bitmap->InitCheck() != B_OK) { 809 bitmap->Delete(); // allows us to delete this bitmap... 810 delete bitmap; 811 bitmap = NULL; 812 } 813 } 814 815 if (bitmap) { 816 BAutolock lock(&fLock); 817 if (from->Object() != NULL) { 818 // Whoops! Someone snuck in under us. 819 bitmap->Delete(); 820 delete bitmap; 821 bitmap = dynamic_cast<BitmapTypeItem*>(from->Object()); 822 } else 823 from->SetObject(bitmap); 824 } 825 826 return bitmap; 827 } 828 829 830 TStringBlock* 831 TResourceSet::ReturnStringBlockItem(TypeItem* from) 832 { 833 if (!from) 834 return NULL; 835 836 TypeObject* obj = from->Object(); 837 StringBlockTypeItem* stringBlock = dynamic_cast<StringBlockTypeItem*>(obj); 838 if (stringBlock) 839 return stringBlock; 840 841 // Can't change an existing object. 842 if (obj) 843 return NULL; 844 845 // Don't have a string block in the item -- we'll create one. 846 if (from->SourceIsFile() ) { 847 BMemoryIO stream(from->Data(), from->Size()); 848 stringBlock = new StringBlockTypeItem(&stream); 849 } else 850 stringBlock = new StringBlockTypeItem(from->Data(), from->Size()); 851 852 if (stringBlock) { 853 BAutolock lock(&fLock); 854 if (from->Object() != NULL) { 855 // Whoops! Someone snuck in under us. 856 delete stringBlock; 857 stringBlock = dynamic_cast<StringBlockTypeItem*>(from->Object()); 858 } else 859 from->SetObject(stringBlock); 860 } 861 862 return stringBlock; 863 } 864 865 866 // #pragma mark - 867 868 869 namespace TResourcePrivate { 870 871 TResourceSet* gResources = NULL; 872 BLocker gResourceLocker; 873 874 } 875 876 877 TResourceSet* 878 AppResSet() 879 { 880 // If already have it, return immediately. 881 if (gResources) 882 return gResources; 883 884 // Don't have 'em, lock access to make 'em. 885 if (!gResourceLocker.Lock()) 886 return NULL; 887 if (gResources) { 888 // Whoops, somebody else already did. Oh well. 889 gResourceLocker.Unlock(); 890 return gResources; 891 } 892 893 // Make 'em. 894 gResources = new TResourceSet; 895 gResources->AddResources(BApplication::AppResources()); 896 gResourceLocker.Unlock(); 897 return gResources; 898 } 899 900