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 = fItems.CountItems() - 1; i >= 0; 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 = fItems.CountItems() - 1; i >= 0; 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 for (int32 i = fTypes.CountItems() - 1; i >= 0; i--) { 681 TypeList* list = (TypeList*)fTypes.ItemAt(i); 682 if (list && list->Type() == type) 683 return list; 684 } 685 686 return NULL; 687 } 688 689 690 TypeItem* 691 TResourceSet::FindItemID(type_code type, int32 id) 692 { 693 TypeList* list = FindTypeList(type); 694 TypeItem* item = NULL; 695 696 if (list) 697 item = list->FindItemByID(id); 698 699 if (!item) 700 item = LoadResource(type, id, 0, &list); 701 702 return item; 703 } 704 705 706 TypeItem* 707 TResourceSet::FindItemName(type_code type, const char* name) 708 { 709 TypeList* list = FindTypeList(type); 710 TypeItem* item = NULL; 711 712 if (list) 713 item = list->FindItemByName(name); 714 715 if (!item) 716 item = LoadResource(type, -1, name, &list); 717 718 return item; 719 } 720 721 722 TypeItem* 723 TResourceSet::LoadResource(type_code type, int32 id, const char* name, 724 TypeList** inOutList) 725 { 726 TypeItem* item = NULL; 727 728 if (name) { 729 BEntry entry; 730 731 // If a named resource, first look in directories. 732 fLock.Lock(); 733 for (int32 i = fDirectories.CountItems() - 1; i >= 0; i--) { 734 BPath* dir = (BPath*)fDirectories.ItemAt(i); 735 if (dir) { 736 fLock.Unlock(); 737 BPath path(dir->Path(), name); 738 if (entry.SetTo(path.Path(), true) == B_OK ) { 739 BFile file(&entry, B_READ_ONLY); 740 if (file.InitCheck() == B_OK ) { 741 item = new TypeItem(id, name, &file); 742 item->SetSourceIsFile(true); 743 } 744 } 745 fLock.Lock(); 746 } 747 } 748 fLock.Unlock(); 749 } 750 751 #if USE_RESOURCES 752 if (!item) { 753 // Look through resource objects for data. 754 fLock.Lock(); 755 for (int32 i = fResources.CountItems() - 1; i >= 0; i--) { 756 BResources* resource = (BResources*)fResources.ItemAt(i); 757 if (resource) { 758 const void* data = NULL; 759 size_t size = 0; 760 if (id >= 0) 761 data = resource->LoadResource(type, id, &size); 762 else if (name != NULL) 763 data = resource->LoadResource(type, name, &size); 764 765 if (data && size) { 766 item = new TypeItem(id, name, data, size); 767 item->SetSourceIsFile(false); 768 } 769 } 770 } 771 fLock.Unlock(); 772 } 773 #endif 774 775 if (item) { 776 TypeList* list = inOutList ? *inOutList : NULL; 777 if (!list) { 778 // Don't currently have a list for this type -- check if there is 779 // already one. 780 list = FindTypeList(type); 781 } 782 783 BAutolock lock(&fLock); 784 785 if (!list) { 786 // Need to make a new list for this type. 787 list = new TypeList(type); 788 fTypes.AddItem(list); 789 } 790 if (inOutList) 791 *inOutList = list; 792 793 list->AddItem(item); 794 } 795 796 return item; 797 } 798 799 800 BBitmap* 801 TResourceSet::ReturnBitmapItem(type_code, TypeItem* from) 802 { 803 if (!from) 804 return NULL; 805 806 TypeObject* obj = from->Object(); 807 BitmapTypeItem* bitmap = dynamic_cast<BitmapTypeItem*>(obj); 808 if (bitmap) 809 return bitmap; 810 811 // Can't change an existing object. 812 if (obj) 813 return NULL; 814 815 // Don't have a bitmap in the item -- we'll try to create one. 816 BMemoryIO stream(from->Data(), from->Size()); 817 818 // Try to read as an archived bitmap. 819 stream.Seek(0, SEEK_SET); 820 BMessage archive; 821 if (archive.Unflatten(&stream) == B_OK) { 822 bitmap = new BitmapTypeItem(&archive); 823 if (bitmap && bitmap->InitCheck() != B_OK) { 824 bitmap->Delete(); 825 // allows us to delete this bitmap... 826 delete bitmap; 827 bitmap = NULL; 828 } 829 } 830 831 if (bitmap) { 832 BAutolock lock(&fLock); 833 if (from->Object() != NULL) { 834 // Whoops! Someone snuck in under us. 835 bitmap->Delete(); 836 delete bitmap; 837 bitmap = dynamic_cast<BitmapTypeItem*>(from->Object()); 838 } else 839 from->SetObject(bitmap); 840 } 841 842 return bitmap; 843 } 844 845 846 TStringBlock* 847 TResourceSet::ReturnStringBlockItem(TypeItem* from) 848 { 849 if (!from) 850 return NULL; 851 852 TypeObject* obj = from->Object(); 853 StringBlockTypeItem* stringBlock = dynamic_cast<StringBlockTypeItem*>(obj); 854 if (stringBlock) 855 return stringBlock; 856 857 // Can't change an existing object. 858 if (obj) 859 return NULL; 860 861 // Don't have a string block in the item -- we'll create one. 862 if (from->SourceIsFile() ) { 863 BMemoryIO stream(from->Data(), from->Size()); 864 stringBlock = new StringBlockTypeItem(&stream); 865 } else 866 stringBlock = new StringBlockTypeItem(from->Data(), from->Size()); 867 868 if (stringBlock) { 869 BAutolock lock(&fLock); 870 if (from->Object() != NULL) { 871 // Whoops! Someone snuck in under us. 872 delete stringBlock; 873 stringBlock = dynamic_cast<StringBlockTypeItem*>(from->Object()); 874 } else 875 from->SetObject(stringBlock); 876 } 877 878 return stringBlock; 879 } 880 881 882 // #pragma mark - 883 884 885 namespace TResourcePrivate { 886 TResourceSet* gResources = NULL; 887 BLocker gResourceLocker; 888 } 889 890 891 TResourceSet* 892 AppResSet() 893 { 894 // If already have it, return immediately. 895 if (gResources) 896 return gResources; 897 898 // Don't have 'em, lock access to make 'em. 899 if (!gResourceLocker.Lock()) 900 return NULL; 901 if (gResources) { 902 // Whoops, somebody else already did. Oh well. 903 gResourceLocker.Unlock(); 904 return gResources; 905 } 906 907 // Make 'em. 908 gResources = new TResourceSet; 909 gResources->AddResources(BApplication::AppResources()); 910 gResourceLocker.Unlock(); 911 return gResources; 912 } 913