1/*
2Open Tracker License
3
4Terms and Conditions
5
6Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7
8Permission is hereby granted, free of charge, to any person obtaining a copy of
9this software and associated documentation files (the "Software"), to deal in
10the Software without restriction, including without limitation the rights to
11use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12of the Software, and to permit persons to whom the Software is furnished to do
13so, subject to the following conditions:
14
15The above copyright notice and this permission notice applies to all licensees
16and shall be included in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
25Except as contained in this notice, the name of Be Incorporated shall not be
26used in advertising or otherwise to promote the sale, use or other dealings in
27this Software without prior written authorization from Be Incorporated.
28
29Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered
30trademarks of Be Incorporated in the United States and other countries. Other
31brand product names are registered trademarks or trademarks of their respective
32holders.
33All 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
66namespace 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
285using namespace TResourcePrivate;
286
287//	#pragma mark -
288// ----------------------------- TStringBlock -----------------------------
289
290
291TStringBlock::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
313TStringBlock::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
332TStringBlock::~TStringBlock()
333{
334	if (fOwnData) {
335		free(fIndex);
336		free(fStrings);
337	}
338	fIndex = 0;
339	fStrings = NULL;
340}
341
342
343const char*
344TStringBlock::String(size_t index) const
345{
346	if (index >= fNumEntries)
347		return NULL;
348
349	return fStrings + fIndex[index];
350}
351
352
353size_t
354TStringBlock::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
416void
417TStringBlock::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
438static bool
439FreeResourcesFunc(void* item)
440{
441	delete reinterpret_cast<BResources*>(item);
442	return false;
443}
444#endif
445
446
447static bool
448FreePathFunc(void* item)
449{
450	delete reinterpret_cast<BPath*>(item);
451	return false;
452}
453
454
455static bool
456FreeTypeFunc(void* item)
457{
458	delete reinterpret_cast<TypeList*>(item);
459	return false;
460}
461
462
463//	#pragma mark -
464// ----------------------------- TResourceSet -----------------------------
465
466
467TResourceSet::TResourceSet()
468{
469}
470
471
472TResourceSet::~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
486status_t
487TResourceSet::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
504status_t
505TResourceSet::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
526status_t
527TResourceSet::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
542status_t
543TResourceSet::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
599const void*
600TResourceSet::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
611const void*
612TResourceSet::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
623const BBitmap*
624TResourceSet::FindBitmap(type_code type, int32 id)
625{
626	return ReturnBitmapItem(type, FindItemID(type, id));
627}
628
629
630const BBitmap*
631TResourceSet::FindBitmap(type_code type, const char* name)
632{
633	return ReturnBitmapItem(type, FindItemName(type, name));
634}
635
636
637const TStringBlock*
638TResourceSet::FindStringBlock(type_code type, int32 id)
639{
640	return ReturnStringBlockItem(FindItemID(type, id));
641}
642
643
644const TStringBlock*
645TResourceSet::FindStringBlock(type_code type, const char* name)
646{
647	return ReturnStringBlockItem(FindItemName(type, name));
648}
649
650
651const char*
652TResourceSet::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
663const char*
664TResourceSet::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
675TypeList*
676TResourceSet::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
691TypeItem*
692TResourceSet::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
707TypeItem*
708TResourceSet::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
723TypeItem*
724TResourceSet::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
803BBitmap*
804TResourceSet::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
849TStringBlock*
850TResourceSet::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
888namespace TResourcePrivate {
889	TResourceSet* gResources = NULL;
890	BLocker gResourceLocker;
891}
892
893
894TResourceSet*
895AppResSet()
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