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:
TypeObject()69 TypeObject()
70 : fDeleteOK(false)
71 {
72 }
73
~TypeObject()74 virtual ~TypeObject()
75 {
76 if (!fDeleteOK)
77 debugger("deleting object owned by BResourceSet");
78 }
79
Delete()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:
BitmapTypeItem(BRect bounds,uint32 flags,color_space depth,int32 bytesPerRow=B_ANY_BYTES_PER_ROW,screen_id screenID=B_MAIN_SCREEN_ID)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
BitmapTypeItem(const BBitmap * source,bool accepts_views=false,bool need_contiguous=false)103 BitmapTypeItem(const BBitmap* source, bool accepts_views = false,
104 bool need_contiguous = false)
105 : BBitmap(source, accepts_views, need_contiguous)
106 {
107 }
108
BitmapTypeItem(BMessage * data)109 BitmapTypeItem(BMessage* data)
110 : BBitmap(data)
111 {
112 }
113
~BitmapTypeItem()114 virtual ~BitmapTypeItem()
115 {
116 }
117 };
118
119 class StringBlockTypeItem : public TStringBlock, public TypeObject {
120 public:
StringBlockTypeItem(BDataIO * data)121 StringBlockTypeItem(BDataIO* data)
122 : TStringBlock(data)
123 {
124 }
125
StringBlockTypeItem(const void * block,size_t size)126 StringBlockTypeItem(const void* block, size_t size)
127 : TStringBlock(block, size)
128 {
129 }
130
~StringBlockTypeItem()131 virtual ~StringBlockTypeItem()
132 {
133 }
134 };
135
136 class TypeItem {
137 public:
TypeItem(int32 id,const char * name,const void * data,size_t size)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
TypeItem(int32 id,const char * name,BFile * file)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
~TypeItem()166 virtual ~TypeItem()
167 {
168 if (fOwnData) {
169 free(fData);
170 fData = NULL;
171 fSize = 0;
172 }
173 SetObject(NULL);
174 }
175
ID() const176 int32 ID() const
177 {
178 return fID;
179 }
180
Name() const181 const char* Name() const
182 {
183 return fName.String();
184 }
185
Data() const186 const void* Data() const
187 {
188 return fData;
189 }
190
Size() const191 size_t Size() const
192 {
193 return fSize;
194 }
195
SetObject(TypeObject * object)196 void SetObject(TypeObject* object)
197 {
198 if (object == fObject)
199 return;
200 if (fObject)
201 fObject->Delete();
202 fObject = object;
203 }
204
Object() const205 TypeObject* Object() const
206 {
207 return fObject;
208 }
209
SetSourceIsFile(bool state)210 void SetSourceIsFile(bool state)
211 {
212 fSourceIsFile = state;
213 }
214
SourceIsFile() const215 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
FreeTypeItemFunc(void * item)230 static bool FreeTypeItemFunc(void* item)
231 {
232 delete reinterpret_cast<TypeItem*>(item);
233 return false;
234 }
235
236 class TypeList {
237 public:
TypeList(type_code type)238 TypeList(type_code type)
239 : fType(type)
240 {
241 }
242
~TypeList()243 virtual ~TypeList()
244 {
245 fItems.DoForEach(FreeTypeItemFunc);
246 fItems.MakeEmpty();
247 }
248
Type() const249 type_code Type() const
250 {
251 return fType;
252 }
253
FindItemByID(int32 id)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
FindItemByName(const char * name)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
AddItem(TypeItem * item)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
TStringBlock(BDataIO * data)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
TStringBlock(const void * block,size_t size)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
~TStringBlock()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*
String(size_t index) const344 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
PreIndex(char * strings,ssize_t len)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
MakeIndex(const char * strings,ssize_t len,size_t indexLength,size_t * resultingIndex)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
FreeResourcesFunc(void * item)439 FreeResourcesFunc(void* item)
440 {
441 delete reinterpret_cast<BResources*>(item);
442 return false;
443 }
444 #endif
445
446
447 static bool
FreePathFunc(void * item)448 FreePathFunc(void* item)
449 {
450 delete reinterpret_cast<BPath*>(item);
451 return false;
452 }
453
454
455 static bool
FreeTypeFunc(void * item)456 FreeTypeFunc(void* item)
457 {
458 delete reinterpret_cast<TypeList*>(item);
459 return false;
460 }
461
462
463 // #pragma mark -
464 // ----------------------------- TResourceSet -----------------------------
465
466
TResourceSet()467 TResourceSet::TResourceSet()
468 {
469 }
470
471
~TResourceSet()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
AddResources(BResources * RESOURCES_ONLY (resources))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
AddDirectory(const char * fullPath)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
AddEnvDirectory(const char * in,const char * defaultValue)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
ExpandString(BString * out,const char * in)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*
FindResource(type_code type,int32 id,size_t * outSize)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*
FindResource(type_code type,const char * name,size_t * outSize)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*
FindBitmap(type_code type,int32 id)624 TResourceSet::FindBitmap(type_code type, int32 id)
625 {
626 return ReturnBitmapItem(type, FindItemID(type, id));
627 }
628
629
630 const BBitmap*
FindBitmap(type_code type,const char * name)631 TResourceSet::FindBitmap(type_code type, const char* name)
632 {
633 return ReturnBitmapItem(type, FindItemName(type, name));
634 }
635
636
637 const TStringBlock*
FindStringBlock(type_code type,int32 id)638 TResourceSet::FindStringBlock(type_code type, int32 id)
639 {
640 return ReturnStringBlockItem(FindItemID(type, id));
641 }
642
643
644 const TStringBlock*
FindStringBlock(type_code type,const char * name)645 TResourceSet::FindStringBlock(type_code type, const char* name)
646 {
647 return ReturnStringBlockItem(FindItemName(type, name));
648 }
649
650
651 const char*
FindString(type_code type,int32 id,uint32 index)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*
FindString(type_code type,const char * name,uint32 index)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*
FindTypeList(type_code type)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*
FindItemID(type_code type,int32 id)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*
FindItemName(type_code type,const char * name)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*
LoadResource(type_code type,int32 id,const char * name,TypeList ** inOutList)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*
ReturnBitmapItem(type_code,TypeItem * from)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*
ReturnStringBlockItem(TypeItem * from)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*
AppResSet()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