xref: /haiku/src/apps/deskbar/ResourceSet.cpp (revision 85fb3e7df81f8d5b6e47a9a64a53873ea906ea6e)
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