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