xref: /haiku/src/kits/storage/ResourceStrings.cpp (revision 4c8e85b316c35a9161f5a1c50ad70bc91c83a76f)
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 
23 using namespace std;
24 
25 
26 // constructor
27 /*! \brief Creates an object initialized to the application's string resources.
28 */
29 BResourceStrings::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 */
46 BResourceStrings::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 */
61 BResourceStrings::~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 */
73 status_t
74 BResourceStrings::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 */
89 BString *
90 BResourceStrings::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 */
111 const char *
112 BResourceStrings::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 */
131 status_t
132 BResourceStrings::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 */
211 status_t
212 BResourceStrings::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 */
231 void
232 BResourceStrings::_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 */
250 void
251 BResourceStrings::_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 */
271 status_t
272 BResourceStrings::_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 */
311 BResourceStrings::_string_id_hash *
312 BResourceStrings::_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 */
333 BResourceStrings::_string_id_hash *
334 BResourceStrings::_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
348 status_t BResourceStrings::_Reserved_ResourceStrings_0(void *) { return 0; }
349 status_t BResourceStrings::_Reserved_ResourceStrings_1(void *) { return 0; }
350 status_t BResourceStrings::_Reserved_ResourceStrings_2(void *) { return 0; }
351 status_t BResourceStrings::_Reserved_ResourceStrings_3(void *) { return 0; }
352 status_t BResourceStrings::_Reserved_ResourceStrings_4(void *) { return 0; }
353 status_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 */
361 BResourceStrings::_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 */
373 BResourceStrings::_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 */
386 void
387 BResourceStrings::_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