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