1//----------------------------------------------------------------------
2//  This software is part of the Haiku distribution and is covered
3//  by the MIT License.
4//---------------------------------------------------------------------
5/*!
6	\file ResourceStrings.cpp
7	BResourceStrings implementation.
8*/
9
10#include <ResourceStrings.h>
11
12#include <new>
13#include <stdlib.h>
14#include <string.h>
15
16#include <Entry.h>
17#include <File.h>
18#include <Resources.h>
19#include <String.h>
20
21#include <AppMisc.h>
22
23using namespace std;
24
25
26// constructor
27/*! \brief Creates an object initialized to the application's string resources.
28*/
29BResourceStrings::BResourceStrings()
30				: _string_lock(),
31				  _init_error(),
32				  fFileRef(),
33				  fResources(NULL),
34				  fHashTable(NULL),
35				  fHashTableSize(0),
36				  fStringCount(0)
37{
38	SetStringFile(NULL);
39}
40
41// constructor
42/*! \brief Creates an object initialized to the string resources of the
43	file referred to by the supplied entry_ref.
44	\param ref the entry_ref referring to the resource file
45*/
46BResourceStrings::BResourceStrings(const entry_ref &ref)
47				: _string_lock(),
48				  _init_error(),
49				  fFileRef(),
50				  fResources(NULL),
51				  fHashTable(NULL),
52				  fHashTableSize(0),
53				  fStringCount(0)
54{
55	SetStringFile(&ref);
56}
57
58// destructor
59/*!	\brief Frees all resources associated with the BResourceStrings object.
60*/
61BResourceStrings::~BResourceStrings()
62{
63	_string_lock.Lock();
64	_Cleanup();
65}
66
67// InitCheck
68/*!	\brief Returns the status of the last initialization via contructor or
69	SetStringFile().
70	\return \c B_OK, if the object is properly initialized, an error code
71			otherwise.
72*/
73status_t
74BResourceStrings::InitCheck()
75{
76	return _init_error;
77}
78
79// NewString
80/*!	\brief Finds and returns a copy of the string identified by the supplied
81	ID.
82	The caller is responsible for deleting the returned BString object.
83	\param id the ID of the requested string
84	\return
85	- A string object containing the requested string,
86	- \c NULL, if the object is not properly initialized or there is no string
87	  with ID \a id.
88*/
89BString *
90BResourceStrings::NewString(int32 id)
91{
92//	_string_lock.Lock();
93	BString *result = NULL;
94	if (const char *str = FindString(id))
95		result = new(nothrow) BString(str);
96//	_string_lock.Unlock();
97	return result;
98}
99
100// FindString
101/*!	\brief Finds and returns the string identified by the supplied ID.
102	The caller must not free the returned string. It belongs to the
103	BResourceStrings object and is valid until the object is destroyed or set
104	to another file.
105	\param id the ID of the requested string
106	\return
107	- The requested string,
108	- \c NULL, if the object is not properly initialized or there is no string
109	  with ID \a id.
110*/
111const char *
112BResourceStrings::FindString(int32 id)
113{
114	_string_lock.Lock();
115	const char *result = NULL;
116	if (InitCheck() == B_OK) {
117		if (_string_id_hash *entry = _FindString(id))
118			result = entry->data;
119	}
120	_string_lock.Unlock();
121	return result;
122}
123
124// SetStringFile
125/*!	\brief Re-initialized the BResourceStrings object to the file referred to
126	by the supplied entry_ref.
127	If the supplied entry_ref is \c NULL, the object is initialized to the
128	application file.
129	\param ref the entry_ref referring to the resource file
130*/
131status_t
132BResourceStrings::SetStringFile(const entry_ref *ref)
133{
134	_string_lock.Lock();
135	// cleanup
136	_Cleanup();
137	// get the ref (if NULL, take the application)
138	status_t error = B_OK;
139	entry_ref fileRef;
140	if (ref) {
141		fileRef = *ref;
142		fFileRef = *ref;
143	} else
144		error = BPrivate::get_app_ref(&fileRef);
145	// get the BResources
146	if (error == B_OK) {
147		BFile file(&fileRef, B_READ_ONLY);
148		error = file.InitCheck();
149		if (error == B_OK) {
150			fResources = new(nothrow) BResources;
151			if (fResources)
152				error = fResources->SetTo(&file);
153			else
154				error = B_NO_MEMORY;
155		}
156	}
157	// read the strings
158	if (error == B_OK) {
159		// count them first
160		fStringCount = 0;
161		int32 id;
162		const char *name;
163		size_t length;
164		while (fResources->GetResourceInfo(RESOURCE_TYPE, fStringCount, &id,
165										   &name, &length)) {
166			fStringCount++;
167		}
168		// allocate a hash table with a nice size
169		// I don't have a heuristic at hand, so let's simply take the count.
170		error = _Rehash(fStringCount);
171		// load the resources
172		for (int32 i = 0; error == B_OK && i < fStringCount; i++) {
173			if (!fResources->GetResourceInfo(RESOURCE_TYPE, i, &id, &name,
174											 &length)) {
175				error = B_ERROR;
176			}
177			if (error == B_OK) {
178				const void *data
179					= fResources->LoadResource(RESOURCE_TYPE, id, &length);
180				if (data) {
181					_string_id_hash *entry = NULL;
182					if (length == 0)
183						entry = _AddString(NULL, id, false);
184					else
185						entry = _AddString((char*)data, id, false);
186					if (!entry)
187						error = B_ERROR;
188				} else
189					error = B_ERROR;
190			}
191		}
192	}
193	// if something went wrong, cleanup the mess
194	if (error != B_OK)
195		_Cleanup();
196	_init_error = error;
197	_string_lock.Unlock();
198	return error;
199}
200
201// GetStringFile
202/*!	\brief Returns an entry_ref referring to the resource file, the object is
203	currently initialized to.
204	\param outRef a pointer to an entry_ref variable to be initialized to the
205		   requested entry_ref
206	\return
207	- \c B_OK: Everything went fine.
208	- \c B_BAD_VALUE: \c NULL \a outRef.
209	- other error codes
210*/
211status_t
212BResourceStrings::GetStringFile(entry_ref *outRef)
213{
214	status_t error = (outRef ? B_OK : B_BAD_VALUE);
215	if (error == B_OK)
216		error = InitCheck();
217	if (error == B_OK) {
218		if (fFileRef == entry_ref())
219			error = B_ENTRY_NOT_FOUND;
220		else
221			*outRef = fFileRef;
222	}
223	return error;
224}
225
226
227// _Cleanup
228/*!	\brief Frees all resources associated with this object and sets all
229	member variables to harmless values.
230*/
231void
232BResourceStrings::_Cleanup()
233{
234//	_string_lock.Lock();
235	_MakeEmpty();
236	delete[] fHashTable;
237	fHashTable = NULL;
238	delete fResources;
239	fResources = NULL;
240	fFileRef = entry_ref();
241	fHashTableSize = 0;
242	fStringCount = 0;
243	_init_error = B_OK;
244//	_string_lock.Unlock();
245}
246
247// _MakeEmpty
248/*!	\brief Empties the id->string hash table.
249*/
250void
251BResourceStrings::_MakeEmpty()
252{
253	if (fHashTable) {
254		for (int32 i = 0; i < fHashTableSize; i++) {
255			while (_string_id_hash *entry = fHashTable[i]) {
256				fHashTable[i] = entry->next;
257				delete entry;
258			}
259		}
260		fStringCount = 0;
261	}
262}
263
264// _Rehash
265/*!	\brief Resizes the id->string hash table to the supplied size.
266	\param newSize the new hash table size
267	\return
268	- \c B_OK: Everything went fine.
269	- \c B_NO_MEMORY: Insuffient memory.
270*/
271status_t
272BResourceStrings::_Rehash(int32 newSize)
273{
274	status_t error = B_OK;
275	if (newSize > 0 && newSize != fHashTableSize) {
276		// alloc a new table and fill it with NULL
277		_string_id_hash **newHashTable
278			= new(nothrow) _string_id_hash*[newSize];
279		if (newHashTable) {
280			memset(newHashTable, 0, sizeof(_string_id_hash*) * newSize);
281			// move the entries to the new table
282			if (fHashTable && fHashTableSize > 0 && fStringCount > 0) {
283				for (int32 i = 0; i < fHashTableSize; i++) {
284					while (_string_id_hash *entry = fHashTable[i]) {
285						fHashTable[i] = entry->next;
286						int32 newPos = entry->id % newSize;
287						entry->next = newHashTable[newPos];
288						newHashTable[newPos] = entry;
289					}
290				}
291			}
292			// set the new table
293			delete[] fHashTable;
294			fHashTable = newHashTable;
295			fHashTableSize = newSize;
296		} else
297			error = B_NO_MEMORY;
298	}
299	return error;
300}
301
302// _AddString
303/*!	\brief Adds an entry to the id->string hash table.
304	If there is already a string with the given ID, it will be replaced.
305	\param str the string
306	\param id the id of the string
307	\param wasMalloced if \c true, the object will be responsible for
308		   free()ing the supplied string
309	\return the hash table entry or \c NULL, if something went wrong
310*/
311BResourceStrings::_string_id_hash *
312BResourceStrings::_AddString(char *str, int32 id, bool wasMalloced)
313{
314	_string_id_hash *entry = NULL;
315	if (fHashTable && fHashTableSize > 0)
316		entry = new(nothrow) _string_id_hash;
317	if (entry) {
318		entry->assign_string(str, false);
319		entry->id = id;
320		entry->data_alloced = wasMalloced;
321		int32 pos = id % fHashTableSize;
322		entry->next = fHashTable[pos];
323		fHashTable[pos] = entry;
324	}
325	return entry;
326}
327
328// _FindString
329/*!	\brief Returns the hash table entry for a given ID.
330	\param id the ID
331	\return the hash table entry or \c NULL, if there is no entry with this ID
332*/
333BResourceStrings::_string_id_hash *
334BResourceStrings::_FindString(int32 id)
335{
336	_string_id_hash *entry = NULL;
337	if (fHashTable && fHashTableSize > 0) {
338		int32 pos = id % fHashTableSize;
339		entry = fHashTable[pos];
340		while (entry != NULL && entry->id != id)
341			entry = entry->next;
342	}
343	return entry;
344}
345
346
347// FBC
348status_t BResourceStrings::_Reserved_ResourceStrings_0(void *) { return 0; }
349status_t BResourceStrings::_Reserved_ResourceStrings_1(void *) { return 0; }
350status_t BResourceStrings::_Reserved_ResourceStrings_2(void *) { return 0; }
351status_t BResourceStrings::_Reserved_ResourceStrings_3(void *) { return 0; }
352status_t BResourceStrings::_Reserved_ResourceStrings_4(void *) { return 0; }
353status_t BResourceStrings::_Reserved_ResourceStrings_5(void *) { return 0; }
354
355
356// _string_id_hash
357
358// constructor
359/*!	\brief Creates an uninitialized hash table entry.
360*/
361BResourceStrings::_string_id_hash::_string_id_hash()
362	: next(NULL),
363	  id(0),
364	  data(NULL),
365	  data_alloced(false)
366{
367}
368
369// destructor
370/*!	\brief Frees all resources associated with this object.
371	Only if \c data_alloced is \c true, the string will be free()d.
372*/
373BResourceStrings::_string_id_hash::~_string_id_hash()
374{
375	if (data_alloced)
376		free(data);
377}
378
379// assign_string
380/*!	\brief Sets the string of the hash table entry.
381	\param str the string
382	\param makeCopy If \c true, the supplied string is copied and the copy
383		   will be freed on destruction. If \c false, the entry points to the
384		   supplied string. It will not be freed() on destruction.
385*/
386void
387BResourceStrings::_string_id_hash::assign_string(const char *str,
388												 bool makeCopy)
389{
390	if (data_alloced)
391		free(data);
392	data = NULL;
393	data_alloced = false;
394	if (str) {
395		if (makeCopy) {
396			data = strdup(str);
397			data_alloced = true;
398		} else
399			data = const_cast<char*>(str);
400	}
401}
402
403
404
405
406