1 //---------------------------------------------------------------------- 2 // This software is part of the OpenBeOS 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