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