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