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 */
BResourceStrings()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 */
BResourceStrings(const entry_ref & ref)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 */
~BResourceStrings()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
InitCheck()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 *
NewString(int32 id)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 *
FindString(int32 id)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
SetStringFile(const entry_ref * ref)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
GetStringFile(entry_ref * outRef)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
_Cleanup()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
_MakeEmpty()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
_Rehash(int32 newSize)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 *
_AddString(char * str,int32 id,bool wasMalloced)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 *
_FindString(int32 id)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
_Reserved_ResourceStrings_0(void *)348 status_t BResourceStrings::_Reserved_ResourceStrings_0(void *) { return 0; }
_Reserved_ResourceStrings_1(void *)349 status_t BResourceStrings::_Reserved_ResourceStrings_1(void *) { return 0; }
_Reserved_ResourceStrings_2(void *)350 status_t BResourceStrings::_Reserved_ResourceStrings_2(void *) { return 0; }
_Reserved_ResourceStrings_3(void *)351 status_t BResourceStrings::_Reserved_ResourceStrings_3(void *) { return 0; }
_Reserved_ResourceStrings_4(void *)352 status_t BResourceStrings::_Reserved_ResourceStrings_4(void *) { return 0; }
_Reserved_ResourceStrings_5(void *)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 */
_string_id_hash()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 */
~_string_id_hash()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
assign_string(const char * str,bool makeCopy)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