1 /* 2 ** Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3 ** Distributed under the terms of the OpenBeOS License. 4 */ 5 6 7 #include "DataEditor.h" 8 9 #include <Autolock.h> 10 #include <NodeMonitor.h> 11 #include <Drivers.h> 12 13 #include <string.h> 14 #include <unistd.h> 15 16 17 class DataChange { 18 public: 19 virtual ~DataChange(); 20 21 virtual void Apply(off_t offset, uint8 *buffer, size_t size) = 0; 22 virtual void Revert(off_t offset, uint8 *buffer, size_t size) = 0; 23 }; 24 25 class ReplaceChange : public DataChange { 26 public: 27 ReplaceChange(off_t offset, const uint8 *data, size_t size); 28 ~ReplaceChange(); 29 30 virtual void Apply(off_t offset, uint8 *buffer, size_t size); 31 virtual void Revert(off_t offset, uint8 *buffer, size_t size); 32 33 private: 34 void Normalize(off_t bufferOffset, size_t bufferSize, 35 off_t &offset, size_t &dataOffset, size_t &size); 36 37 uint8 *fNewData; 38 uint8 *fOldData; 39 size_t fSize; 40 off_t fOffset; 41 }; 42 43 44 DataChange::~DataChange() 45 { 46 } 47 48 49 // #pragma mark - 50 51 52 ReplaceChange::ReplaceChange(off_t offset, const uint8 *data, size_t size) 53 { 54 fOffset = offset; 55 56 fNewData = (uint8 *)malloc(size); 57 fOldData = (uint8 *)malloc(size); 58 if (fNewData != NULL && fOldData != NULL) { 59 memcpy(fNewData, data, size); 60 fSize = size; 61 } else 62 fSize = 0; 63 } 64 65 66 ReplaceChange::~ReplaceChange() 67 { 68 free(fNewData); 69 free(fOldData); 70 } 71 72 73 /** Normalizes the supplied offset & size pair to be limited by 74 * the buffer offset and size. 75 * All parameters must have been initialized before calling this 76 * method. 77 */ 78 79 void 80 ReplaceChange::Normalize(off_t bufferOffset, size_t bufferSize, off_t &offset, 81 size_t &dataOffset, size_t &size) 82 { 83 if (fOffset < bufferOffset) { 84 offset = bufferOffset; 85 dataOffset = bufferOffset - fOffset; 86 size -= dataOffset; 87 } 88 89 if (offset + size > bufferOffset + bufferSize) 90 size = offset - bufferOffset + offset; 91 } 92 93 94 void 95 ReplaceChange::Apply(off_t bufferOffset, uint8 *buffer, size_t bufferSize) 96 { 97 // is it in our range? 98 if (fOffset - bufferOffset > bufferSize || fOffset + fSize < bufferOffset) 99 return; 100 101 // don't change anything outside the supplied buffer 102 off_t offset = fOffset; 103 size_t dataOffset = 0; 104 size_t size = fSize; 105 Normalize(bufferOffset, bufferSize, offset, dataOffset, size); 106 if (size == 0) 107 return; 108 109 // now we can safely exchange the buffer! 110 memcpy(fOldData + dataOffset, buffer + offset - bufferOffset, size); 111 memcpy(buffer + offset - bufferOffset, fNewData + dataOffset, size); 112 } 113 114 115 void 116 ReplaceChange::Revert(off_t bufferOffset, uint8 *buffer, size_t bufferSize) 117 { 118 // is it in our range? 119 if (fOffset - bufferOffset > bufferSize || fOffset + fSize < bufferOffset) 120 return; 121 122 // don't change anything outside the supplied buffer 123 off_t offset = fOffset; 124 size_t dataOffset = 0; 125 size_t size = fSize; 126 Normalize(bufferOffset, bufferSize, offset, dataOffset, size); 127 if (size == 0) 128 return; 129 130 // now we can safely revert the buffer! 131 memcpy(buffer + offset - bufferOffset, fOldData + dataOffset, size); 132 } 133 134 135 // #pragma mark - 136 137 138 DataEditor::DataEditor() 139 : BLocker("data view") 140 { 141 } 142 143 144 DataEditor::DataEditor(entry_ref &ref, const char *attribute) 145 : BLocker("data view") 146 { 147 SetTo(ref, attribute); 148 } 149 150 151 DataEditor::DataEditor(BEntry &entry, const char *attribute) 152 : BLocker("data view") 153 { 154 SetTo(entry, attribute); 155 } 156 157 158 DataEditor::DataEditor(const DataEditor &editor) 159 : BLocker("data view") 160 { 161 } 162 163 164 DataEditor::~DataEditor() 165 { 166 } 167 168 169 status_t 170 DataEditor::SetTo(const char *path, const char *attribute) 171 { 172 BEntry entry(path); 173 return SetTo(entry, attribute); 174 } 175 176 177 status_t 178 DataEditor::SetTo(entry_ref &ref, const char *attribute) 179 { 180 BEntry entry(&ref); 181 return SetTo(entry, attribute); 182 } 183 184 185 status_t 186 DataEditor::SetTo(BEntry &entry, const char *attribute) 187 { 188 status_t status = fFile.SetTo(&entry, B_READ_WRITE); 189 if (status < B_OK) { 190 // try to open read only 191 status = fFile.SetTo(&entry, B_READ_ONLY); 192 if (status < B_OK) 193 return status; 194 195 fIsReadOnly = true; 196 } 197 198 struct stat stat; 199 stat.st_mode = 0; 200 201 fFile.GetStat(&stat); 202 fIsDevice = (stat.st_mode & (S_IFBLK | S_IFCHR)) != 0; 203 204 fBlockSize = 512; 205 206 if (fIsDevice) { 207 // ToDo: is there any other possibility to issue a ioctl() from a BFile? 208 device_geometry geometry; 209 int device = fFile.Dup(); 210 if (device < 0 || ioctl(device, B_GET_GEOMETRY, &geometry) < 0) { 211 if (device >= 0) 212 close(device); 213 fFile.Unset(); 214 return B_ERROR; 215 } 216 close(device); 217 218 fSize = 1LL * geometry.head_count * geometry.cylinder_count 219 * geometry.sectors_per_track * geometry.bytes_per_sector; 220 fBlockSize = geometry.bytes_per_sector; 221 } else if (IsAttribute()) { 222 // ToDo: add support for attributes! 223 fSize = 0; 224 } else { 225 status = fFile.GetSize(&fSize); 226 if (status < B_OK) { 227 fFile.Unset(); 228 return status; 229 } 230 } 231 232 if (attribute != NULL) 233 fAttribute = strdup(attribute); 234 else 235 fAttribute = NULL; 236 237 fView = NULL; 238 fRealViewOffset = 0; 239 fViewOffset = 0; 240 fRealViewSize = fViewSize = fBlockSize; 241 fNeedsUpdate = true; 242 243 return B_OK; 244 } 245 246 247 status_t 248 DataEditor::SetToAttribute(const char *attribute) 249 { 250 status_t status = InitCheck(); 251 if (status < B_OK) 252 return status; 253 254 fAttribute = attribute; 255 256 // ToDo: attributes are not yet supported 257 return B_ERROR; 258 } 259 260 261 status_t 262 DataEditor::InitCheck() 263 { 264 if (fAttribute != NULL) 265 // ToDo: for now! 266 return B_ERROR; 267 268 return fFile.InitCheck(); 269 } 270 271 272 void 273 DataEditor::AddChange(DataChange *change) 274 { 275 if (change == NULL) 276 return; 277 278 RemoveRedos(); 279 280 fChanges.AddItem(change); 281 fLastChange = change; 282 283 fLastChange->Apply(fRealViewOffset, fView, fRealViewSize); 284 } 285 286 287 status_t 288 DataEditor::Replace(off_t offset, const uint8 *data, size_t length) 289 { 290 if (!IsLocked()) 291 debugger("DataEditor: view not locked"); 292 293 if (fNeedsUpdate) { 294 status_t status = Update(); 295 if (status < B_OK) 296 return status; 297 } 298 299 ReplaceChange *change = new ReplaceChange(offset, data, length); 300 AddChange(change); 301 302 return B_OK; 303 } 304 305 306 status_t 307 DataEditor::Remove(off_t offset, off_t length) 308 { 309 if (!IsLocked()) 310 debugger("DataEditor: view not locked"); 311 312 // not yet implemented 313 314 return B_ERROR; 315 } 316 317 318 status_t 319 DataEditor::Insert(off_t offset, const uint8 *text, size_t length) 320 { 321 if (!IsLocked()) 322 debugger("DataEditor: view not locked"); 323 324 // not yet implemented 325 326 return B_ERROR; 327 } 328 329 330 void 331 DataEditor::ApplyChanges() 332 { 333 if (fLastChange == NULL) 334 return; 335 336 int32 count = fChanges.IndexOf(fLastChange) + 1; 337 338 for (int32 i = 0; i < count; i++) { 339 DataChange *change = fChanges.ItemAt(i); 340 change->Apply(fRealViewOffset, fView, fRealViewSize); 341 } 342 } 343 344 345 /** This method will be called by DataEditor::AddChange() 346 * immediately before a change is applied. 347 * It removes all pending redo nodes from the list that would 348 * come after the current change. 349 */ 350 351 void 352 DataEditor::RemoveRedos() 353 { 354 if (fLastChange == NULL) 355 return; 356 357 int32 start = fChanges.IndexOf(fLastChange) + 1; 358 359 for (int32 i = fChanges.CountItems(); i-- > start; ) { 360 DataChange *change = fChanges.RemoveItemAt(i); 361 delete change; 362 } 363 } 364 365 366 status_t 367 DataEditor::Undo() 368 { 369 BAutolock locker(this); 370 371 if (!CanUndo()) 372 return B_ERROR; 373 374 int32 index = fChanges.IndexOf(fLastChange); 375 fLastChange->Revert(fRealViewOffset, fView, fRealViewSize); 376 377 if (index > 0) 378 fLastChange = fChanges.ItemAt(index - 1); 379 else 380 fLastChange = NULL; 381 382 return B_OK; 383 } 384 385 386 status_t 387 DataEditor::Redo() 388 { 389 BAutolock locker(this); 390 391 if (!CanRedo()) 392 return B_ERROR; 393 394 int32 index = fChanges.IndexOf(fLastChange); 395 fLastChange = fChanges.ItemAt(index + 1); 396 397 fLastChange->Apply(fRealViewOffset, fView, fRealViewSize); 398 399 return B_OK; 400 } 401 402 403 bool 404 DataEditor::CanUndo() const 405 { 406 return fLastChange != NULL; 407 } 408 409 410 bool 411 DataEditor::CanRedo() const 412 { 413 return fChanges.IndexOf(fLastChange) < fChanges.CountItems() - 1; 414 } 415 416 417 status_t 418 DataEditor::SetFileSize(off_t size) 419 { 420 fSize = size; 421 return B_OK; 422 } 423 424 425 status_t 426 DataEditor::SetViewOffset(off_t offset) 427 { 428 if (fView == NULL) { 429 status_t status = SetViewSize(fViewSize); 430 if (status < B_OK) 431 return status; 432 } 433 434 fRealViewOffset = (offset / fBlockSize) * fBlockSize; 435 fViewOffset = offset; 436 fNeedsUpdate = true; 437 438 return B_OK; 439 } 440 441 442 status_t 443 DataEditor::SetViewSize(size_t size) 444 { 445 size_t realSize = (size + fBlockSize - 1) & ~(fBlockSize - 1); 446 // round to the next multiple of block size 447 448 if (realSize == fRealViewSize && fView != NULL) { 449 fViewSize = size; 450 return B_OK; 451 } 452 453 uint8 *view = (uint8 *)realloc(fView, realSize); 454 if (view == NULL) 455 return B_NO_MEMORY; 456 457 fView = view; 458 fRealViewSize = realSize; 459 fViewSize = size; 460 fNeedsUpdate = true; 461 462 return B_OK; 463 } 464 465 466 void 467 DataEditor::SetBlockSize(size_t size) 468 { 469 fBlockSize = size; 470 } 471 472 473 status_t 474 DataEditor::Update() 475 { 476 ssize_t bytesRead = fFile.ReadAt(fRealViewOffset, fView, fRealViewSize); 477 if (bytesRead < B_OK) 478 return bytesRead; 479 480 ApplyChanges(); 481 fNeedsUpdate = false; 482 483 return B_OK; 484 } 485 486 487 status_t 488 DataEditor::GetViewBuffer(const uint8 **_buffer) 489 { 490 if (!IsLocked()) 491 debugger("DataEditor: view not locked"); 492 493 status_t status = B_OK; 494 495 if (fView == NULL) 496 status = SetViewOffset(fViewOffset); 497 498 if (status == B_OK && fNeedsUpdate) 499 status = Update(); 500 501 if (status < B_OK) 502 return status; 503 504 *_buffer = fView + fViewOffset - fRealViewOffset; 505 return B_OK; 506 } 507 508 509 void 510 DataEditor::SendNotices(uint32 what, BMessage *message) 511 { 512 if (fObservers.CountItems() == 0) 513 return; 514 515 BMessage *notice; 516 if (message) { 517 notice = new BMessage(*message); 518 notice->what = B_OBSERVER_NOTICE_CHANGE; 519 notice->AddInt32(B_OBSERVE_ORIGINAL_WHAT, message->what); 520 } else 521 notice = new BMessage(B_OBSERVER_NOTICE_CHANGE); 522 523 notice->AddInt32(B_OBSERVE_WHAT_CHANGE, what); 524 525 for (int32 i = fObservers.CountItems(); i-- > 0;) { 526 BMessenger *messenger = fObservers.ItemAt(i); 527 messenger->SendMessage(notice); 528 } 529 530 delete notice; 531 } 532 533 534 status_t 535 DataEditor::StartWatching(BMessenger target) 536 { 537 BAutolock locker(this); 538 539 node_ref node; 540 status_t status = fFile.GetNodeRef(&node); 541 if (status < B_OK) 542 return status; 543 544 fObservers.AddItem(new BMessenger(target)); 545 546 return watch_node(&node, B_WATCH_STAT | B_WATCH_ATTR, target); 547 } 548 549 550 status_t 551 DataEditor::StartWatching(BHandler *handler, BLooper *looper) 552 { 553 return StartWatching(BMessenger(handler, looper)); 554 } 555 556 557 void 558 DataEditor::StopWatching(BMessenger target) 559 { 560 BAutolock locker(this); 561 562 for (int32 i = fObservers.CountItems(); i-- > 0;) { 563 BMessenger *messenger = fObservers.ItemAt(i); 564 if (*messenger == target) { 565 fObservers.RemoveItemAt(i); 566 delete messenger; 567 break; 568 } 569 } 570 571 stop_watching(target); 572 } 573 574 575 void 576 DataEditor::StopWatching(BHandler *handler, BLooper *looper) 577 { 578 StopWatching(BMessenger(handler, looper)); 579 } 580 581