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 30 trademarks of Be Incorporated in the United States and other countries. Other 31 brand product names are registered trademarks or trademarks of their respective 32 holders. 33 All rights reserved. 34 */ 35 36 37 #define USE_RESOURCES 1 38 39 #include "ResourceSet.h" 40 41 #include <stdlib.h> 42 #include <unistd.h> 43 #include <ctype.h> 44 45 #include <Application.h> 46 #include <Autolock.h> 47 #include <Bitmap.h> 48 #include <DataIO.h> 49 #include <Debug.h> 50 #include <Entry.h> 51 #include <File.h> 52 #include <Path.h> 53 #include <String.h> 54 55 #if USE_RESOURCES 56 #include <Resources.h> 57 #endif 58 59 #if USE_RESOURCES 60 #define RESOURCES_ONLY(x) x 61 #else 62 #define RESOURCES_ONLY(x) 63 #endif 64 65 66 namespace TResourcePrivate { 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 { 178 return fID; 179 } 180 181 const char* Name() const 182 { 183 return fName.String(); 184 } 185 186 const void* Data() const 187 { 188 return fData; 189 } 190 191 size_t Size() const 192 { 193 return fSize; 194 } 195 196 void SetObject(TypeObject* object) 197 { 198 if (object == fObject) 199 return; 200 if (fObject) 201 fObject->Delete(); 202 fObject = object; 203 } 204 205 TypeObject* Object() const 206 { 207 return fObject; 208 } 209 210 void SetSourceIsFile(bool state) 211 { 212 fSourceIsFile = state; 213 } 214 215 bool SourceIsFile() const 216 { 217 return fSourceIsFile; 218 } 219 220 private: 221 int32 fID; 222 BString fName; 223 void* fData; 224 size_t fSize; 225 TypeObject* fObject; 226 bool fOwnData; 227 bool fSourceIsFile; 228 }; 229 230 static bool FreeTypeItemFunc(void* item) 231 { 232 delete reinterpret_cast<TypeItem*>(item); 233 return false; 234 } 235 236 class TypeList { 237 public: 238 TypeList(type_code type) 239 : fType(type) 240 { 241 } 242 243 virtual ~TypeList() 244 { 245 fItems.DoForEach(FreeTypeItemFunc); 246 fItems.MakeEmpty(); 247 } 248 249 type_code Type() const 250 { 251 return fType; 252 } 253 254 TypeItem* FindItemByID(int32 id) 255 { 256 for (int32 i = 0; i < fItems.CountItems(); i++ ) { 257 TypeItem* it = (TypeItem*)fItems.ItemAt(i); 258 if (it->ID() == id) 259 return it; 260 } 261 return NULL; 262 } 263 264 TypeItem* FindItemByName(const char* name) 265 { 266 for (int32 i = 0; i < fItems.CountItems(); i++ ) { 267 TypeItem* it = (TypeItem*)fItems.ItemAt(i); 268 if (strcmp(it->Name(), name) == 0) 269 return it; 270 } 271 return NULL; 272 } 273 274 void AddItem(TypeItem* item) 275 { 276 fItems.AddItem(item); 277 } 278 279 private: 280 type_code fType; 281 BList fItems; 282 }; 283 } 284 285 using namespace TResourcePrivate; 286 287 // #pragma mark - 288 // ----------------------------- TStringBlock ----------------------------- 289 290 291 TStringBlock::TStringBlock(BDataIO* data) 292 : fNumEntries(0), 293 fIndex(0), 294 fStrings(NULL), 295 fOwnData(true) 296 { 297 fStrings = (char*)malloc(1024); 298 size_t pos = 0; 299 ssize_t amount; 300 while ((amount = data->Read(fStrings + pos, 1024)) == 1024) { 301 pos += amount; 302 fStrings = (char*)realloc(fStrings, pos + 1024); 303 } 304 if (amount > 0) 305 pos += amount; 306 307 fNumEntries = PreIndex(fStrings, amount); 308 fIndex = (size_t*)malloc(sizeof(size_t) * fNumEntries); 309 MakeIndex(fStrings, amount, fNumEntries, fIndex); 310 } 311 312 313 TStringBlock::TStringBlock(const void* block, size_t size) 314 : 315 fNumEntries(0), 316 fIndex(0), 317 fStrings(NULL), 318 fOwnData(false) 319 { 320 fIndex = (size_t*)const_cast<void*>(block); 321 fStrings = (char*)const_cast<void*>(block); 322 323 // Figure out how many entries there are. 324 size_t last_off = 0; 325 while (fIndex[fNumEntries] > last_off && fIndex[fNumEntries] < size ) { 326 last_off = fIndex[fNumEntries]; 327 fNumEntries++; 328 } 329 } 330 331 332 TStringBlock::~TStringBlock() 333 { 334 if (fOwnData) { 335 free(fIndex); 336 free(fStrings); 337 } 338 fIndex = 0; 339 fStrings = NULL; 340 } 341 342 343 const char* 344 TStringBlock::String(size_t index) const 345 { 346 if (index >= fNumEntries) 347 return NULL; 348 349 return fStrings + fIndex[index]; 350 } 351 352 353 size_t 354 TStringBlock::PreIndex(char* strings, ssize_t len) 355 { 356 size_t count = 0; 357 char* orig = strings; 358 char* end = strings + len; 359 bool in_cr = false; 360 bool first = true; 361 bool skipping = false; 362 363 while (orig < end) { 364 if (*orig == '\n' || *orig == '\r' || *orig == 0) { 365 if (!in_cr && *orig == '\r') 366 in_cr = true; 367 if (in_cr && *orig == '\n') { 368 orig++; 369 continue; 370 } 371 first = true; 372 skipping = false; 373 *strings = 0; 374 count++; 375 } else if (first && *orig == '#') { 376 skipping = true; 377 first = false; 378 orig++; 379 continue; 380 } else if (skipping) { 381 orig++; 382 continue; 383 } else if (*orig == '\\' && (orig + 1) < end) { 384 orig++; 385 switch (*orig) { 386 case '\\': 387 *strings = '\\'; 388 break; 389 390 case '\n': 391 *strings = '\n'; 392 break; 393 394 case '\r': 395 *strings = '\r'; 396 break; 397 398 case '\t': 399 *strings = '\t'; 400 break; 401 402 default: 403 *strings = *orig; 404 break; 405 } 406 } else 407 *strings = *orig; 408 409 orig++; 410 strings++; 411 } 412 return count; 413 } 414 415 416 void 417 TStringBlock::MakeIndex(const char* strings, ssize_t len, 418 size_t indexLength, size_t* resultingIndex) 419 { 420 *resultingIndex++ = 0; 421 indexLength--; 422 423 ssize_t pos = 0; 424 while (pos < len && indexLength > 0) { 425 if (strings[pos] == 0 ) { 426 *resultingIndex++ = (size_t)pos + 1; 427 indexLength--; 428 } 429 pos++; 430 } 431 } 432 433 434 // #pragma mark - 435 436 437 #if USE_RESOURCES 438 static bool 439 FreeResourcesFunc(void* item) 440 { 441 delete reinterpret_cast<BResources*>(item); 442 return false; 443 } 444 #endif 445 446 447 static bool 448 FreePathFunc(void* item) 449 { 450 delete reinterpret_cast<BPath*>(item); 451 return false; 452 } 453 454 455 static bool 456 FreeTypeFunc(void* item) 457 { 458 delete reinterpret_cast<TypeList*>(item); 459 return false; 460 } 461 462 463 // #pragma mark - 464 // ----------------------------- TResourceSet ----------------------------- 465 466 467 TResourceSet::TResourceSet() 468 { 469 } 470 471 472 TResourceSet::~TResourceSet() 473 { 474 BAutolock lock(&fLock); 475 #if USE_RESOURCES 476 fResources.DoForEach(FreeResourcesFunc); 477 fResources.MakeEmpty(); 478 #endif 479 fDirectories.DoForEach(FreePathFunc); 480 fDirectories.MakeEmpty(); 481 fTypes.DoForEach(FreeTypeFunc); 482 fTypes.MakeEmpty(); 483 } 484 485 486 status_t 487 TResourceSet::AddResources(BResources* RESOURCES_ONLY(resources)) 488 { 489 #if USE_RESOURCES 490 if (!resources) 491 return B_BAD_VALUE; 492 493 BAutolock lock(&fLock); 494 status_t err = fResources.AddItem(resources) ? B_OK : B_ERROR; 495 if (err != B_OK) 496 delete resources; 497 return err; 498 #else 499 return B_ERROR; 500 #endif 501 } 502 503 504 status_t 505 TResourceSet::AddDirectory(const char* fullPath) 506 { 507 if (!fullPath) 508 return B_BAD_VALUE; 509 510 BPath* path = new BPath(fullPath); 511 status_t err = path->InitCheck(); 512 if (err != B_OK) { 513 delete path; 514 return err; 515 } 516 517 BAutolock lock(&fLock); 518 err = fDirectories.AddItem(path) ? B_OK : B_ERROR; 519 if (err != B_OK) 520 delete path; 521 522 return err; 523 } 524 525 526 status_t 527 TResourceSet::AddEnvDirectory(const char* in, const char* defaultValue) 528 { 529 BString buf; 530 status_t err = ExpandString(&buf, in); 531 532 if (err != B_OK) { 533 if (defaultValue) 534 return AddDirectory(defaultValue); 535 return err; 536 } 537 538 return AddDirectory(buf.String()); 539 } 540 541 542 status_t 543 TResourceSet::ExpandString(BString* out, const char* in) 544 { 545 const char* start = in; 546 547 while (*in) { 548 if (*in == '$') { 549 if (start < in) 550 out->Append(start, (int32)(in - start)); 551 552 in++; 553 char variableName[1024]; 554 size_t i = 0; 555 if (*in == '{') { 556 in++; 557 while (*in && *in != '}' && i < sizeof(variableName) - 1) 558 variableName[i++] = *in++; 559 560 if (*in) 561 in++; 562 563 } else { 564 while ((isalnum(*in) || *in == '_') 565 && i < sizeof(variableName) - 1) 566 variableName[i++] = *in++; 567 } 568 569 start = in; 570 variableName[i] = '\0'; 571 572 const char* val = getenv(variableName); 573 if (!val) { 574 PRINT(("Error: env var %s not found.\n", &variableName[0])); 575 return B_NAME_NOT_FOUND; 576 } 577 578 status_t err = ExpandString(out, val); 579 if (err != B_OK) 580 return err; 581 582 } else if (*in == '\\') { 583 if (start < in) 584 out->Append(start, (int32)(in - start)); 585 in++; 586 start = in; 587 in++; 588 } else 589 in++; 590 } 591 592 if (start < in) 593 out->Append(start, (int32)(in - start)); 594 595 return B_OK; 596 } 597 598 599 const void* 600 TResourceSet::FindResource(type_code type, int32 id, size_t* outSize) 601 { 602 TypeItem* item = FindItemID(type, id); 603 604 if (outSize) 605 *outSize = item ? item->Size() : 0; 606 607 return item ? item->Data() : NULL; 608 } 609 610 611 const void* 612 TResourceSet::FindResource(type_code type, const char* name, size_t* outSize) 613 { 614 TypeItem* item = FindItemName(type, name); 615 616 if (outSize) 617 *outSize = item ? item->Size() : 0; 618 619 return item ? item->Data() : NULL; 620 } 621 622 623 const BBitmap* 624 TResourceSet::FindBitmap(type_code type, int32 id) 625 { 626 return ReturnBitmapItem(type, FindItemID(type, id)); 627 } 628 629 630 const BBitmap* 631 TResourceSet::FindBitmap(type_code type, const char* name) 632 { 633 return ReturnBitmapItem(type, FindItemName(type, name)); 634 } 635 636 637 const TStringBlock* 638 TResourceSet::FindStringBlock(type_code type, int32 id) 639 { 640 return ReturnStringBlockItem(FindItemID(type, id)); 641 } 642 643 644 const TStringBlock* 645 TResourceSet::FindStringBlock(type_code type, const char* name) 646 { 647 return ReturnStringBlockItem(FindItemName(type, name)); 648 } 649 650 651 const char* 652 TResourceSet::FindString(type_code type, int32 id, uint32 index) 653 { 654 const TStringBlock* stringBlock = FindStringBlock(type, id); 655 656 if (!stringBlock) 657 return NULL; 658 659 return stringBlock->String(index); 660 } 661 662 663 const char* 664 TResourceSet::FindString(type_code type, const char* name, uint32 index) 665 { 666 const TStringBlock* stringBlock = FindStringBlock(type, name); 667 668 if (!stringBlock) 669 return NULL; 670 671 return stringBlock->String(index); 672 } 673 674 675 TypeList* 676 TResourceSet::FindTypeList(type_code type) 677 { 678 BAutolock lock(&fLock); 679 680 int32 count = fTypes.CountItems(); 681 for (int32 i = 0; i < count; i++ ) { 682 TypeList* list = (TypeList*)fTypes.ItemAt(i); 683 if (list && list->Type() == type) 684 return list; 685 } 686 687 return NULL; 688 } 689 690 691 TypeItem* 692 TResourceSet::FindItemID(type_code type, int32 id) 693 { 694 TypeList* list = FindTypeList(type); 695 TypeItem* item = NULL; 696 697 if (list) 698 item = list->FindItemByID(id); 699 700 if (!item) 701 item = LoadResource(type, id, 0, &list); 702 703 return item; 704 } 705 706 707 TypeItem* 708 TResourceSet::FindItemName(type_code type, const char* name) 709 { 710 TypeList* list = FindTypeList(type); 711 TypeItem* item = NULL; 712 713 if (list) 714 item = list->FindItemByName(name); 715 716 if (!item) 717 item = LoadResource(type, -1, name, &list); 718 719 return item; 720 } 721 722 723 TypeItem* 724 TResourceSet::LoadResource(type_code type, int32 id, const char* name, 725 TypeList** inOutList) 726 { 727 TypeItem* item = NULL; 728 729 if (name) { 730 BEntry entry; 731 732 // If a named resource, first look in directories. 733 fLock.Lock(); 734 int32 count = fDirectories.CountItems(); 735 for (int32 i = 0; item == 0 && i < count; i++) { 736 BPath* dir = (BPath*)fDirectories.ItemAt(i); 737 if (dir) { 738 fLock.Unlock(); 739 BPath path(dir->Path(), name); 740 if (entry.SetTo(path.Path(), true) == B_OK ) { 741 BFile file(&entry, B_READ_ONLY); 742 if (file.InitCheck() == B_OK ) { 743 item = new TypeItem(id, name, &file); 744 item->SetSourceIsFile(true); 745 } 746 } 747 fLock.Lock(); 748 } 749 } 750 fLock.Unlock(); 751 } 752 753 #if USE_RESOURCES 754 if (!item) { 755 // Look through resource objects for data. 756 fLock.Lock(); 757 int32 count = fResources.CountItems(); 758 for (int32 i = 0; item == 0 && i < count; i++ ) { 759 BResources* resource = (BResources*)fResources.ItemAt(i); 760 if (resource) { 761 const void* data = NULL; 762 size_t size = 0; 763 if (id >= 0) 764 data = resource->LoadResource(type, id, &size); 765 else if (name != NULL) 766 data = resource->LoadResource(type, name, &size); 767 768 if (data && size) { 769 item = new TypeItem(id, name, data, size); 770 item->SetSourceIsFile(false); 771 } 772 } 773 } 774 fLock.Unlock(); 775 } 776 #endif 777 778 if (item) { 779 TypeList* list = inOutList ? *inOutList : NULL; 780 if (!list) { 781 // Don't currently have a list for this type -- check if there is 782 // already one. 783 list = FindTypeList(type); 784 } 785 786 BAutolock lock(&fLock); 787 788 if (!list) { 789 // Need to make a new list for this type. 790 list = new TypeList(type); 791 fTypes.AddItem(list); 792 } 793 if (inOutList) 794 *inOutList = list; 795 796 list->AddItem(item); 797 } 798 799 return item; 800 } 801 802 803 BBitmap* 804 TResourceSet::ReturnBitmapItem(type_code, TypeItem* from) 805 { 806 if (!from) 807 return NULL; 808 809 TypeObject* obj = from->Object(); 810 BitmapTypeItem* bitmap = dynamic_cast<BitmapTypeItem*>(obj); 811 if (bitmap) 812 return bitmap; 813 814 // Can't change an existing object. 815 if (obj) 816 return NULL; 817 818 // Don't have a bitmap in the item -- we'll try to create one. 819 BMemoryIO stream(from->Data(), from->Size()); 820 821 // Try to read as an archived bitmap. 822 stream.Seek(0, SEEK_SET); 823 BMessage archive; 824 if (archive.Unflatten(&stream) == B_OK) { 825 bitmap = new BitmapTypeItem(&archive); 826 if (bitmap && bitmap->InitCheck() != B_OK) { 827 bitmap->Delete(); 828 // allows us to delete this bitmap... 829 delete bitmap; 830 bitmap = NULL; 831 } 832 } 833 834 if (bitmap) { 835 BAutolock lock(&fLock); 836 if (from->Object() != NULL) { 837 // Whoops! Someone snuck in under us. 838 bitmap->Delete(); 839 delete bitmap; 840 bitmap = dynamic_cast<BitmapTypeItem*>(from->Object()); 841 } else 842 from->SetObject(bitmap); 843 } 844 845 return bitmap; 846 } 847 848 849 TStringBlock* 850 TResourceSet::ReturnStringBlockItem(TypeItem* from) 851 { 852 if (!from) 853 return NULL; 854 855 TypeObject* obj = from->Object(); 856 StringBlockTypeItem* stringBlock = dynamic_cast<StringBlockTypeItem*>(obj); 857 if (stringBlock) 858 return stringBlock; 859 860 // Can't change an existing object. 861 if (obj) 862 return NULL; 863 864 // Don't have a string block in the item -- we'll create one. 865 if (from->SourceIsFile() ) { 866 BMemoryIO stream(from->Data(), from->Size()); 867 stringBlock = new StringBlockTypeItem(&stream); 868 } else 869 stringBlock = new StringBlockTypeItem(from->Data(), from->Size()); 870 871 if (stringBlock) { 872 BAutolock lock(&fLock); 873 if (from->Object() != NULL) { 874 // Whoops! Someone snuck in under us. 875 delete stringBlock; 876 stringBlock = dynamic_cast<StringBlockTypeItem*>(from->Object()); 877 } else 878 from->SetObject(stringBlock); 879 } 880 881 return stringBlock; 882 } 883 884 885 // #pragma mark - 886 887 888 namespace TResourcePrivate { 889 TResourceSet* gResources = NULL; 890 BLocker gResourceLocker; 891 } 892 893 894 TResourceSet* 895 AppResSet() 896 { 897 // If already have it, return immediately. 898 if (gResources) 899 return gResources; 900 901 // Don't have 'em, lock access to make 'em. 902 if (!gResourceLocker.Lock()) 903 return NULL; 904 if (gResources) { 905 // Whoops, somebody else already did. Oh well. 906 gResourceLocker.Unlock(); 907 return gResources; 908 } 909 910 // Make 'em. 911 gResources = new TResourceSet; 912 gResources->AddResources(BApplication::AppResources()); 913 gResourceLocker.Unlock(); 914 return gResources; 915 } 916