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 = 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
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	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
690TypeItem*
691TResourceSet::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
706TypeItem*
707TResourceSet::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
722TypeItem*
723TResourceSet::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
800BBitmap*
801TResourceSet::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
846TStringBlock*
847TResourceSet::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
885namespace TResourcePrivate {
886	TResourceSet* gResources = NULL;
887	BLocker gResourceLocker;
888}
889
890
891TResourceSet*
892AppResSet()
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