1 /* 2 * Copyright 2004-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 7 #include "DataEditor.h" 8 9 #include <Autolock.h> 10 #include <NodeMonitor.h> 11 #include <Directory.h> 12 #include <Drivers.h> 13 #include <fs_attr.h> 14 #include <fs_info.h> 15 16 #include <string.h> 17 #include <unistd.h> 18 #include <ctype.h> 19 #include <errno.h> 20 21 22 #ifdef TRACE 23 # undef TRACE 24 #endif 25 26 //#define TRACE_DATA_EDITOR 27 #ifdef TRACE_DATA_EDITOR 28 # define TRACE(x) printf x 29 #else 30 # define TRACE(x) ; 31 #endif 32 33 34 class StateWatcher { 35 public: 36 StateWatcher(DataEditor &editor); 37 ~StateWatcher(); 38 39 private: 40 DataEditor &fEditor; 41 bool fCouldUndo; 42 bool fCouldRedo; 43 bool fWasModified; 44 }; 45 46 class DataChange { 47 public: 48 virtual ~DataChange(); 49 50 virtual void Apply(off_t offset, uint8 *buffer, size_t size) = 0; 51 virtual void Revert(off_t offset, uint8 *buffer, size_t size) = 0; 52 virtual bool Merge(DataChange *change); 53 virtual void GetRange(off_t fileSize, off_t &_offset, off_t &_size) = 0; 54 }; 55 56 class ReplaceChange : public DataChange { 57 public: 58 ReplaceChange(off_t offset, const uint8 *data, size_t size); 59 ~ReplaceChange(); 60 61 virtual void Apply(off_t offset, uint8 *buffer, size_t size); 62 virtual void Revert(off_t offset, uint8 *buffer, size_t size); 63 virtual bool Merge(DataChange *change); 64 virtual void GetRange(off_t fileSize, off_t &_offset, off_t &_size); 65 66 private: 67 void Normalize(off_t bufferOffset, size_t bufferSize, 68 off_t &offset, size_t &dataOffset, size_t &size); 69 70 uint8 *fNewData; 71 uint8 *fOldData; 72 size_t fSize; 73 off_t fOffset; 74 }; 75 76 77 //--------------------- 78 79 80 #ifdef TRACE_DATA_EDITOR 81 82 #define DUMPED_BLOCK_SIZE 16 83 84 static void 85 dump_block(const uint8 *buffer, int size, const char *prefix) 86 { 87 for (int i = 0; i < size;) { 88 int start = i; 89 90 printf(prefix); 91 for (; i < start + DUMPED_BLOCK_SIZE; i++) { 92 if (!(i % 4)) 93 printf(" "); 94 95 if (i >= size) 96 printf(" "); 97 else 98 printf("%02x", *(unsigned char *)(buffer + i)); 99 } 100 printf(" "); 101 102 for (i = start; i < start + DUMPED_BLOCK_SIZE; i++) { 103 if (i < size) { 104 char c = buffer[i]; 105 106 if (c < 30) 107 printf("."); 108 else 109 printf("%c", c); 110 } else 111 break; 112 } 113 printf("\n"); 114 } 115 } 116 #endif // TRACE_DATA_EDITOR 117 118 119 static int 120 CompareCaseInsensitive(const uint8 *a, const uint8 *b, size_t size) 121 { 122 for (size_t i = 0; i < size; i++) { 123 uint8 diff = tolower(a[i]) - tolower(b[i]); 124 if (diff) 125 return diff; 126 } 127 128 return 0; 129 } 130 131 132 static int 133 CompareOffsets(const off_t *a, const off_t *b) 134 { 135 if (*a < *b) 136 return -1; 137 else if (*a > *b) 138 return 1; 139 140 return 0; 141 } 142 143 144 // #pragma mark - 145 146 147 StateWatcher::StateWatcher(DataEditor &editor) 148 : 149 fEditor(editor) 150 { 151 fCouldUndo = editor.CanUndo(); 152 fCouldRedo = editor.CanRedo(); 153 fWasModified = editor.IsModified(); 154 } 155 156 157 StateWatcher::~StateWatcher() 158 { 159 BMessage update; 160 if (fCouldRedo != fEditor.CanRedo()) 161 update.AddBool("can_redo", fEditor.CanRedo()); 162 if (fCouldUndo != fEditor.CanUndo()) 163 update.AddBool("can_undo", fEditor.CanUndo()); 164 if (fWasModified != fEditor.IsModified()) 165 update.AddBool("modified", fEditor.IsModified()); 166 167 // only send an update if we have to report anything 168 if (!update.IsEmpty()) 169 fEditor.SendNotices(kMsgDataEditorStateChange, &update); 170 } 171 172 173 // #pragma mark - 174 175 176 DataChange::~DataChange() 177 { 178 } 179 180 181 bool 182 DataChange::Merge(DataChange *change) 183 { 184 return false; 185 } 186 187 188 // #pragma mark - 189 190 191 ReplaceChange::ReplaceChange(off_t offset, const uint8 *data, size_t size) 192 { 193 fOffset = offset; 194 195 fNewData = (uint8 *)malloc(size); 196 fOldData = (uint8 *)malloc(size); 197 if (fNewData != NULL && fOldData != NULL) { 198 memcpy(fNewData, data, size); 199 fSize = size; 200 } else 201 fSize = 0; 202 } 203 204 205 ReplaceChange::~ReplaceChange() 206 { 207 free(fNewData); 208 free(fOldData); 209 } 210 211 212 /** Normalizes the supplied offset & size pair to be limited by 213 * the buffer offset and size. 214 * All parameters must have been initialized before calling this 215 * method. 216 */ 217 218 void 219 ReplaceChange::Normalize(off_t bufferOffset, size_t bufferSize, off_t &offset, 220 size_t &dataOffset, size_t &size) 221 { 222 if (fOffset < bufferOffset) { 223 offset = bufferOffset; 224 dataOffset = bufferOffset - fOffset; 225 size -= dataOffset; 226 } 227 228 if (offset + size > bufferOffset + bufferSize) 229 size = bufferOffset + bufferSize - offset; 230 } 231 232 233 void 234 ReplaceChange::Apply(off_t bufferOffset, uint8 *buffer, size_t bufferSize) 235 { 236 // is it in our range? 237 if (fOffset > bufferOffset + bufferSize || fOffset + fSize < bufferOffset) 238 return; 239 240 // don't change anything outside the supplied buffer 241 off_t offset = fOffset; 242 size_t dataOffset = 0; 243 size_t size = fSize; 244 Normalize(bufferOffset, bufferSize, offset, dataOffset, size); 245 if (size == 0) 246 return; 247 248 #ifdef TRACE_DATA_EDITOR 249 printf("Apply %p (buffer offset = %Ld):\n", this, bufferOffset); 250 dump_block(buffer + offset - bufferOffset, size, "old:"); 251 dump_block(fNewData + dataOffset, size, "new:"); 252 #endif 253 254 // now we can safely exchange the buffer! 255 memcpy(fOldData + dataOffset, buffer + offset - bufferOffset, size); 256 memcpy(buffer + offset - bufferOffset, fNewData + dataOffset, size); 257 } 258 259 260 void 261 ReplaceChange::Revert(off_t bufferOffset, uint8 *buffer, size_t bufferSize) 262 { 263 // is it in our range? 264 if (fOffset - bufferOffset > bufferSize || fOffset + fSize < bufferOffset) 265 return; 266 267 // don't change anything outside the supplied buffer 268 off_t offset = fOffset; 269 size_t dataOffset = 0; 270 size_t size = fSize; 271 Normalize(bufferOffset, bufferSize, offset, dataOffset, size); 272 if (size == 0) 273 return; 274 275 #ifdef TRACE_DATA_EDITOR 276 printf("Revert %p (buffer offset = %Ld):\n", this, bufferOffset); 277 dump_block(buffer + offset - bufferOffset, size, "old:"); 278 dump_block(fOldData + dataOffset, size, "new:"); 279 #endif 280 281 // now we can safely revert the buffer! 282 memcpy(buffer + offset - bufferOffset, fOldData + dataOffset, size); 283 } 284 285 286 bool 287 ReplaceChange::Merge(DataChange *_change) 288 { 289 ReplaceChange *change = dynamic_cast<ReplaceChange *>(_change); 290 if (change == NULL) 291 return false; 292 293 if (change->fOffset + change->fSize == fOffset + fSize && change->fSize == 1) { 294 // this is a special case - the new change changed the last byte of 295 // the old change: we do this since the same byte is changed twice 296 // in hex mode editing. 297 fNewData[fSize - 1] = change->fNewData[0]; 298 #ifdef TRACE_DATA_EDITOR 299 printf("Merge one byte %p (offset = %Ld, size = %lu):\n", this, fOffset, fSize); 300 dump_block(fOldData, fSize, "old:"); 301 dump_block(fNewData, fSize, "new:"); 302 #endif 303 return true; 304 } 305 306 // are the changes adjacent? 307 308 if (change->fOffset + change->fSize != fOffset 309 && fOffset + fSize != change->fOffset) 310 return false; 311 312 // okay, we can try to merge the two changes 313 314 uint8 *newData = fOffset < change->fOffset ? fNewData : change->fNewData; 315 size_t size = fSize + change->fSize; 316 317 if ((newData = (uint8 *)realloc(newData, size)) == NULL) 318 return false; 319 320 uint8 *oldData = fOffset < change->fOffset ? fOldData : change->fOldData; 321 if ((oldData = (uint8 *)realloc(oldData, size)) == NULL) { 322 fNewData = (uint8 *)realloc(newData, fSize); 323 // if this fails, we can't do anything about it 324 return false; 325 } 326 327 if (fOffset < change->fOffset) { 328 memcpy(newData + fSize, change->fNewData, change->fSize); 329 memcpy(oldData + fSize, change->fOldData, change->fSize); 330 } else { 331 memcpy(newData + change->fSize, fNewData, fSize); 332 memcpy(oldData + change->fSize, fOldData, fSize); 333 change->fNewData = fNewData; 334 change->fOldData = fOldData; 335 // transfer ownership, so that they will be deleted with the change 336 fOffset = change->fOffset; 337 } 338 339 fNewData = newData; 340 fOldData = oldData; 341 fSize = size; 342 343 #ifdef TRACE_DATA_EDITOR 344 printf("Merge %p (offset = %Ld, size = %lu):\n", this, fOffset, fSize); 345 dump_block(fOldData, fSize, "old:"); 346 dump_block(fNewData, fSize, "new:"); 347 #endif 348 349 return true; 350 } 351 352 353 void 354 ReplaceChange::GetRange(off_t /*fileSize*/, off_t &_offset, off_t &_size) 355 { 356 _offset = fOffset; 357 _size = fSize; 358 } 359 360 361 // #pragma mark - 362 363 364 DataEditor::DataEditor() 365 : BLocker("data view") 366 { 367 } 368 369 370 DataEditor::DataEditor(entry_ref &ref, const char *attribute) 371 : BLocker("data view") 372 { 373 SetTo(ref, attribute); 374 } 375 376 377 DataEditor::DataEditor(BEntry &entry, const char *attribute) 378 : BLocker("data view") 379 { 380 SetTo(entry, attribute); 381 } 382 383 384 DataEditor::DataEditor(const DataEditor &editor) 385 : BLocker("data view") 386 { 387 } 388 389 390 DataEditor::~DataEditor() 391 { 392 } 393 394 395 status_t 396 DataEditor::SetTo(const char *path, const char *attribute) 397 { 398 BEntry entry(path); 399 return SetTo(entry, attribute); 400 } 401 402 403 status_t 404 DataEditor::SetTo(entry_ref &ref, const char *attribute) 405 { 406 BEntry entry(&ref); 407 return SetTo(entry, attribute); 408 } 409 410 411 status_t 412 DataEditor::SetTo(BEntry &entry, const char *attribute) 413 { 414 fSize = 0; 415 fLastChange = fFirstChange = NULL; 416 fChangesFromSaved = 0; 417 fView = NULL; 418 fRealViewOffset = 0; 419 fViewOffset = 0; 420 fRealViewSize = fViewSize = fBlockSize = 512; 421 422 struct stat stat; 423 status_t status = entry.GetStat(&stat); 424 if (status < B_OK) 425 return status; 426 427 entry.GetRef(&fAttributeRef); 428 429 bool isFileSystem = false; 430 431 if (entry.IsDirectory()) { 432 // we redirect root directories to their volumes 433 BDirectory directory(&entry); 434 if (directory.InitCheck() == B_OK && directory.IsRootDirectory()) { 435 fs_info info; 436 if (fs_stat_dev(stat.st_dev, &info) != 0) 437 return errno; 438 439 status = entry.SetTo(info.device_name); 440 if (status < B_OK) 441 return status; 442 443 entry.GetStat(&stat); 444 445 fBlockSize = info.block_size; 446 if (fBlockSize > 0 && fBlockSize <= 65536) 447 isFileSystem = true; 448 } 449 } 450 451 status = fFile.SetTo(&entry, B_READ_WRITE); 452 if (status < B_OK) { 453 // try to open read only 454 status = fFile.SetTo(&entry, B_READ_ONLY); 455 if (status < B_OK) 456 return status; 457 458 fIsReadOnly = true; 459 } else 460 fIsReadOnly = false; 461 462 entry.GetRef(&fRef); 463 fIsDevice = S_ISBLK(stat.st_mode) || S_ISCHR(stat.st_mode); 464 465 if (attribute != NULL) 466 fAttribute = strdup(attribute); 467 else 468 fAttribute = NULL; 469 470 if (IsAttribute()) { 471 BNode node(&fAttributeRef); 472 attr_info info; 473 status = node.GetAttrInfo(fAttribute, &info); 474 if (status != B_OK) { 475 fFile.Unset(); 476 return status; 477 } 478 479 fSize = info.size; 480 fType = info.type; 481 } else if (fIsDevice) { 482 device_geometry geometry; 483 int device = fFile.Dup(); 484 if (device < 0 || ioctl(device, B_GET_GEOMETRY, &geometry) < 0) { 485 if (device >= 0) 486 close(device); 487 fFile.Unset(); 488 return B_ERROR; 489 } 490 close(device); 491 492 fSize = 1LL * geometry.head_count * geometry.cylinder_count 493 * geometry.sectors_per_track * geometry.bytes_per_sector; 494 495 if (!isFileSystem) 496 fBlockSize = geometry.bytes_per_sector; 497 } else if (entry.IsDirectory() || entry.IsSymLink()) { 498 fSize = 0; 499 fIsReadOnly = true; 500 } else { 501 status = fFile.GetSize(&fSize); 502 if (status < B_OK) { 503 fFile.Unset(); 504 return status; 505 } 506 } 507 508 if (fBlockSize == 0) 509 fBlockSize = 512; 510 511 fRealViewSize = fViewSize = fBlockSize; 512 fNeedsUpdate = true; 513 514 return B_OK; 515 } 516 517 518 status_t 519 DataEditor::InitCheck() 520 { 521 return fFile.InitCheck(); 522 } 523 524 525 void 526 DataEditor::AddChange(DataChange *change) 527 { 528 if (change == NULL) 529 return; 530 531 StateWatcher watcher(*this); 532 // update state observers 533 534 RemoveRedos(); 535 change->Apply(fRealViewOffset, fView, fRealViewSize); 536 537 SendNotices(change); 538 // update observers 539 540 // try to join changes 541 if (fLastChange == NULL || !fLastChange->Merge(change)) { 542 fChanges.AddItem(change); 543 fLastChange = change; 544 fChangesFromSaved++; 545 } else 546 delete change; 547 } 548 549 550 status_t 551 DataEditor::Replace(off_t offset, const uint8 *data, size_t length) 552 { 553 if (IsReadOnly()) 554 return B_NOT_ALLOWED; 555 556 BAutolock locker(this); 557 558 if (offset >= fSize) 559 return B_BAD_VALUE; 560 if (offset + length > fSize) 561 length = fSize - offset; 562 563 if (fNeedsUpdate) { 564 status_t status = Update(); 565 if (status < B_OK) 566 return status; 567 } 568 569 ReplaceChange *change = new ReplaceChange(offset, data, length); 570 AddChange(change); 571 572 return B_OK; 573 } 574 575 576 status_t 577 DataEditor::Remove(off_t offset, off_t length) 578 { 579 if (IsReadOnly()) 580 return B_NOT_ALLOWED; 581 582 BAutolock locker(this); 583 584 // not yet implemented 585 // ToDo: this needs some changes to the whole change mechanism 586 587 return B_ERROR; 588 } 589 590 591 status_t 592 DataEditor::Insert(off_t offset, const uint8 *text, size_t length) 593 { 594 if (IsReadOnly()) 595 return B_NOT_ALLOWED; 596 597 BAutolock locker(this); 598 599 // not yet implemented 600 // ToDo: this needs some changes to the whole change mechanism 601 602 return B_ERROR; 603 } 604 605 606 void 607 DataEditor::ApplyChanges() 608 { 609 if (fLastChange == NULL && fFirstChange == NULL) 610 return; 611 612 int32 firstIndex = fFirstChange != NULL ? fChanges.IndexOf(fFirstChange) + 1 : 0; 613 int32 lastIndex = fChanges.IndexOf(fLastChange); 614 615 if (fChangesFromSaved >= 0) { 616 // ascend changes 617 TRACE(("ApplyChanges(): ascend from %ld to %ld\n", firstIndex, lastIndex)); 618 619 for (int32 i = firstIndex; i <= lastIndex; i++) { 620 DataChange *change = fChanges.ItemAt(i); 621 change->Apply(fRealViewOffset, fView, fRealViewSize); 622 } 623 } else { 624 // descend changes 625 TRACE(("ApplyChanges(): descend from %ld to %ld\n", firstIndex - 1, lastIndex)); 626 627 for (int32 i = firstIndex - 1; i > lastIndex; i--) { 628 DataChange *change = fChanges.ItemAt(i); 629 change->Revert(fRealViewOffset, fView, fRealViewSize); 630 } 631 } 632 } 633 634 635 status_t 636 DataEditor::Save() 637 { 638 BAutolock locker(this); 639 640 if (!IsModified()) 641 return B_OK; 642 643 StateWatcher watcher(*this); 644 645 // Do we need to ascend or descend the list of changes? 646 647 int32 firstIndex = fFirstChange != NULL ? fChanges.IndexOf(fFirstChange) + 1 : 0; 648 int32 lastIndex = fChanges.IndexOf(fLastChange); 649 if (fChangesFromSaved < 0 && firstIndex != lastIndex) { 650 // swap indices 651 ASSERT(firstIndex > lastIndex); 652 653 int32 temp = firstIndex - 1; 654 firstIndex = lastIndex; 655 lastIndex = temp; 656 } 657 658 if (firstIndex < 0) 659 firstIndex = 0; 660 if (lastIndex > fChanges.CountItems() - 1) 661 lastIndex = fChanges.CountItems(); 662 663 // Collect ranges of data we need to write. 664 665 // ToDo: This is a very simple implementation and could be drastically 666 // improved by having items that save ranges, not just offsets. If Insert() 667 // and Remove() are going to be implemented, it should be improved that 668 // way to reduce the memory footprint to something acceptable. 669 670 BObjectList<off_t> list; 671 for (int32 i = firstIndex; i <= lastIndex; i++) { 672 DataChange *change = fChanges.ItemAt(i); 673 674 off_t offset, size; 675 change->GetRange(FileSize(), offset, size); 676 offset -= offset % BlockSize(); 677 678 while (size > 0) { 679 list.BinaryInsertCopyUnique(offset, CompareOffsets); 680 offset += BlockSize(); 681 size -= BlockSize(); 682 } 683 } 684 685 // read in data and apply changes, write it back to disk 686 687 off_t oldOffset = fViewOffset; 688 689 for (int32 i = 0; i < list.CountItems(); i++) { 690 off_t offset = *list.ItemAt(i); 691 692 // load the data into our view 693 SetViewOffset(offset, false); 694 695 if (fNeedsUpdate) { 696 status_t status = Update(); 697 if (status < B_OK) 698 return status; 699 } 700 701 // save back to disk 702 703 // don't try to change the file size 704 size_t size = fRealViewSize; 705 if (fRealViewOffset + fRealViewSize > fSize) 706 size = fSize - fRealViewOffset; 707 708 ssize_t bytesWritten; 709 if (IsAttribute()) 710 bytesWritten = fFile.WriteAttr(fAttribute, fType, fRealViewOffset, fView, size); 711 else 712 bytesWritten = fFile.WriteAt(fRealViewOffset, fView, size); 713 714 if (bytesWritten < B_OK) 715 return bytesWritten; 716 } 717 718 // update state 719 720 SetViewOffset(oldOffset, false); 721 if (fNeedsUpdate) 722 Update(); 723 724 fChangesFromSaved = 0; 725 fFirstChange = fLastChange; 726 727 return B_OK; 728 } 729 730 731 /** This method will be called by DataEditor::AddChange() 732 * immediately before a change is applied. 733 * It removes all pending redo nodes from the list that would 734 * come after the current change. 735 */ 736 737 void 738 DataEditor::RemoveRedos() 739 { 740 int32 start = fChanges.IndexOf(fLastChange) + 1; 741 742 for (int32 i = fChanges.CountItems(); i-- > start; ) { 743 DataChange *change = fChanges.RemoveItemAt(i); 744 delete change; 745 } 746 } 747 748 749 status_t 750 DataEditor::Undo() 751 { 752 BAutolock locker(this); 753 754 if (!CanUndo()) 755 return B_ERROR; 756 757 StateWatcher watcher(*this); 758 // update state observers 759 760 DataChange *undoChange = fLastChange; 761 762 int32 index = fChanges.IndexOf(undoChange); 763 fChangesFromSaved--; 764 undoChange->Revert(fRealViewOffset, fView, fRealViewSize); 765 766 if (index > 0) 767 fLastChange = fChanges.ItemAt(index - 1); 768 else 769 fLastChange = NULL; 770 771 // update observers 772 SendNotices(undoChange); 773 774 return B_OK; 775 } 776 777 778 status_t 779 DataEditor::Redo() 780 { 781 BAutolock locker(this); 782 783 if (!CanRedo()) 784 return B_ERROR; 785 786 StateWatcher watcher(*this); 787 // update state observers 788 789 int32 index = fChanges.IndexOf(fLastChange); 790 fLastChange = fChanges.ItemAt(index + 1); 791 fChangesFromSaved++; 792 793 fLastChange->Apply(fRealViewOffset, fView, fRealViewSize); 794 795 // update observers 796 SendNotices(fLastChange); 797 798 return B_OK; 799 } 800 801 802 bool 803 DataEditor::CanUndo() const 804 { 805 return fLastChange != NULL; 806 } 807 808 809 bool 810 DataEditor::CanRedo() const 811 { 812 return fChanges.IndexOf(fLastChange) < fChanges.CountItems() - 1; 813 } 814 815 816 status_t 817 DataEditor::SetFileSize(off_t size) 818 { 819 // ToDo: implement me! 820 //fSize = size; 821 //return B_OK; 822 return B_ERROR; 823 } 824 825 826 status_t 827 DataEditor::SetViewOffset(off_t offset, bool sendNotices) 828 { 829 BAutolock locker(this); 830 831 if (fView == NULL) { 832 status_t status = SetViewSize(fViewSize); 833 if (status < B_OK) 834 return status; 835 } 836 837 if (offset < 0 || offset > fSize) 838 return B_BAD_VALUE; 839 840 offset = (offset / fViewSize) * fViewSize; 841 if (offset == fViewOffset) 842 return B_OK; 843 844 fViewOffset = offset; 845 fRealViewOffset = (fViewOffset / fBlockSize) * fBlockSize; 846 fNeedsUpdate = true; 847 848 if (sendNotices) { 849 BMessage update; 850 update.AddInt64("offset", fViewOffset); 851 SendNotices(kMsgDataEditorParameterChange, &update); 852 } 853 854 return B_OK; 855 } 856 857 858 status_t 859 DataEditor::SetViewOffset(off_t offset) 860 { 861 return SetViewOffset(offset, true); 862 } 863 864 865 status_t 866 DataEditor::SetViewSize(size_t size, bool sendNotices) 867 { 868 BAutolock locker(this); 869 870 size_t realSize = (size + fBlockSize - 1) & ~(fBlockSize - 1); 871 // round to the next multiple of block size 872 873 if (realSize == fRealViewSize && fViewSize == size && fView != NULL) 874 return B_OK; 875 876 if (realSize == 0) 877 return B_BAD_VALUE; 878 879 if (realSize != fRealViewSize || fView == NULL) { 880 uint8 *view = (uint8 *)realloc(fView, realSize); 881 if (view == NULL) 882 return B_NO_MEMORY; 883 884 fView = view; 885 fRealViewSize = realSize; 886 } 887 888 fViewSize = size; 889 fNeedsUpdate = true; 890 891 // let's correct the view offset if necessary 892 if (fViewOffset % size) 893 SetViewOffset(fViewOffset); 894 895 if (sendNotices) { 896 BMessage update; 897 update.AddInt32("view_size", size); 898 SendNotices(kMsgDataEditorParameterChange, &update); 899 } 900 901 return B_OK; 902 } 903 904 905 status_t 906 DataEditor::SetViewSize(size_t size) 907 { 908 return SetViewSize(size, true); 909 } 910 911 912 status_t 913 DataEditor::SetBlockSize(size_t size) 914 { 915 BAutolock locker(this); 916 917 fBlockSize = size; 918 status_t status = SetViewOffset(fViewOffset, false); 919 // this will trigger an update of the real view offset 920 if (status == B_OK) 921 status = SetViewSize(fViewSize, false); 922 923 BMessage update; 924 update.AddInt32("block_size", size); 925 update.AddInt64("offset", fViewOffset); 926 SendNotices(kMsgDataEditorParameterChange, &update); 927 928 return status; 929 } 930 931 932 status_t 933 DataEditor::Update() 934 { 935 ssize_t bytesRead; 936 if (IsAttribute()) { 937 BNode node(&fAttributeRef); 938 bytesRead = node.ReadAttr(fAttribute, fType, fRealViewOffset, fView, fRealViewSize); 939 } else 940 bytesRead = fFile.ReadAt(fRealViewOffset, fView, fRealViewSize); 941 942 if (bytesRead < B_OK) 943 return bytesRead; 944 945 if ((size_t)bytesRead < fRealViewSize) { 946 // make sure the rest of data is cleared 947 memset(fView + bytesRead, 0, fRealViewSize - bytesRead); 948 } 949 950 ApplyChanges(); 951 fNeedsUpdate = false; 952 953 return B_OK; 954 } 955 956 957 status_t 958 DataEditor::UpdateIfNeeded(bool *_updated) 959 { 960 if (!fNeedsUpdate) { 961 if (_updated) 962 *_updated = false; 963 return B_OK; 964 } 965 966 status_t status = B_OK; 967 968 if (fView == NULL) 969 status = SetViewOffset(fViewOffset); 970 971 if (status == B_OK && fNeedsUpdate) { 972 status = Update(); 973 if (status == B_OK && _updated) 974 *_updated = true; 975 } 976 977 return status; 978 } 979 980 981 status_t 982 DataEditor::ForceUpdate() 983 { 984 BAutolock locker(this); 985 986 status_t status = B_OK; 987 988 off_t newSize = fSize; 989 if (IsAttribute()) { 990 // update attribute size (we ignore the type for now) 991 attr_info info; 992 status = fFile.GetAttrInfo(fAttribute, &info); 993 if (status != B_OK) { 994 // The attribute may have just been removed before 995 // it gets rewritten, so we don't do anything 996 // else here (we just set the file size to 0) 997 newSize = 0; 998 } else 999 newSize = info.size; 1000 } else if (!IsDevice()) { 1001 // update file size 1002 1003 if (fFile.GetSize(&newSize) != B_OK) 1004 return B_ERROR; 1005 } 1006 1007 if (fSize != newSize) { 1008 fSize = newSize; 1009 1010 // update observers 1011 BMessage update; 1012 update.AddInt64("file_size", newSize); 1013 SendNotices(kMsgDataEditorParameterChange, &update); 1014 } 1015 1016 if (fView == NULL) 1017 status = SetViewOffset(fViewOffset); 1018 else { 1019 BMessage update; 1020 update.AddInt64("offset", fViewOffset); 1021 update.AddInt64("size", fViewSize); 1022 SendNotices(kMsgDataEditorUpdate, &update); 1023 } 1024 1025 if (status == B_OK) 1026 status = Update(); 1027 1028 return status; 1029 } 1030 1031 1032 status_t 1033 DataEditor::GetViewBuffer(const uint8 **_buffer) 1034 { 1035 if (!IsLocked()) 1036 debugger("DataEditor: view not locked"); 1037 1038 status_t status = UpdateIfNeeded(); 1039 if (status < B_OK) 1040 return status; 1041 1042 *_buffer = fView + fViewOffset - fRealViewOffset; 1043 return B_OK; 1044 } 1045 1046 1047 off_t 1048 DataEditor::Find(off_t startPosition, const uint8 *data, size_t dataSize, 1049 bool caseInsensitive, bool cyclic, BMessenger progressMonitor, 1050 volatile bool *stop) 1051 { 1052 if (data == NULL || dataSize == 0) 1053 return B_BAD_VALUE; 1054 1055 if (startPosition < 0) 1056 startPosition = 0; 1057 1058 BAutolock locker(this); 1059 1060 typedef int (*compare_func)(const uint8 *a, const uint8 *b, size_t size); 1061 compare_func compareFunc; 1062 if (caseInsensitive) 1063 compareFunc = CompareCaseInsensitive; 1064 else 1065 compareFunc = (compare_func)memcmp; 1066 1067 bool savedIsReadOnly = fIsReadOnly; 1068 fIsReadOnly = true; 1069 off_t savedOffset = fViewOffset; 1070 off_t position = (startPosition / fRealViewSize) * fRealViewSize; 1071 size_t firstByte = startPosition % fRealViewSize; 1072 size_t matchLastOffset = 0; 1073 off_t foundAt = B_ENTRY_NOT_FOUND; 1074 bool noStop = false; 1075 if (stop == NULL) 1076 stop = &noStop; 1077 1078 // start progress monitor 1079 { 1080 BMessage progress(kMsgDataEditorFindProgress); 1081 progress.AddBool("running", true); 1082 progressMonitor.SendMessage(&progress); 1083 } 1084 1085 bigtime_t lastReport = 0; 1086 1087 off_t blocks = fSize; 1088 if (!cyclic) 1089 blocks -= position; 1090 blocks = (blocks + fRealViewSize - 1) / fRealViewSize; 1091 1092 for (; blocks-- > 0 && !*stop; position += fRealViewSize) { 1093 if (position > fSize) 1094 position = 0; 1095 1096 SetViewOffset(position, false); 1097 if (fNeedsUpdate) 1098 Update(); 1099 1100 bigtime_t current = system_time(); 1101 if (lastReport + 500000LL < current) { 1102 // report the progress two times a second 1103 BMessage progress(kMsgDataEditorFindProgress); 1104 progress.AddInt64("position", position); 1105 progressMonitor.SendMessage(&progress); 1106 1107 lastReport = current; 1108 } 1109 1110 // search for data in current block 1111 1112 if (matchLastOffset != 0) { 1113 // we had a partial match in the previous block, let's 1114 // check if it is a whole match 1115 if (!compareFunc(fView, data + matchLastOffset, dataSize - matchLastOffset)) { 1116 matchLastOffset = 0; 1117 break; 1118 } 1119 1120 foundAt = B_ENTRY_NOT_FOUND; 1121 matchLastOffset = 0; 1122 } 1123 1124 for (size_t i = firstByte; i < fRealViewSize; i++) { 1125 if (position + i + dataSize > fSize) 1126 break; 1127 1128 if (!compareFunc(fView + i, data, 1)) { 1129 // one byte matches, compare the rest 1130 size_t size = dataSize - 1; 1131 size_t offset = i + 1; 1132 1133 if (offset + size > fRealViewSize) 1134 size = fRealViewSize - offset; 1135 1136 if (size == 0 || !compareFunc(fView + offset, data + 1, size)) { 1137 foundAt = position + i; 1138 1139 if (size != dataSize - 1) { 1140 // only a partial match, we have to check the start 1141 // of the next block 1142 matchLastOffset = size + 1; 1143 } 1144 break; 1145 } 1146 } 1147 } 1148 1149 if (foundAt >= 0 && matchLastOffset == 0) 1150 break; 1151 1152 firstByte = 0; 1153 } 1154 1155 fIsReadOnly = savedIsReadOnly; 1156 1157 if (foundAt >= 0 && matchLastOffset != 0) 1158 foundAt = B_ENTRY_NOT_FOUND; 1159 1160 // stop progress monitor 1161 { 1162 BMessage progress(kMsgDataEditorFindProgress); 1163 progress.AddBool("running", false); 1164 progress.AddInt64("position", foundAt >= 0 ? foundAt : savedOffset); 1165 progressMonitor.SendMessage(&progress); 1166 } 1167 1168 SetViewOffset(savedOffset, false); 1169 // this is to ensure that we're sending an update when 1170 // we're set to the found data offset 1171 1172 if (foundAt < 0 && *stop) 1173 return B_INTERRUPTED; 1174 1175 return foundAt; 1176 } 1177 1178 1179 void 1180 DataEditor::SendNotices(DataChange *change) 1181 { 1182 off_t offset, size; 1183 change->GetRange(FileSize(), offset, size); 1184 1185 // update observer 1186 BMessage update; 1187 update.AddInt64("offset", offset); 1188 update.AddInt64("size", size); 1189 SendNotices(kMsgDataEditorUpdate, &update); 1190 } 1191 1192 1193 void 1194 DataEditor::SendNotices(uint32 what, BMessage *message) 1195 { 1196 if (fObservers.CountItems() == 0) 1197 return; 1198 1199 BMessage *notice; 1200 if (message) { 1201 notice = new BMessage(*message); 1202 notice->what = what; 1203 } else 1204 notice = new BMessage(what); 1205 1206 for (int32 i = fObservers.CountItems(); i-- > 0;) { 1207 BMessenger *messenger = fObservers.ItemAt(i); 1208 messenger->SendMessage(notice); 1209 } 1210 1211 delete notice; 1212 } 1213 1214 1215 status_t 1216 DataEditor::StartWatching(BMessenger target) 1217 { 1218 BAutolock locker(this); 1219 1220 node_ref node; 1221 status_t status = fFile.GetNodeRef(&node); 1222 if (status < B_OK) 1223 return status; 1224 1225 fObservers.AddItem(new BMessenger(target)); 1226 1227 return watch_node(&node, B_WATCH_STAT | B_WATCH_ATTR, target); 1228 } 1229 1230 1231 status_t 1232 DataEditor::StartWatching(BHandler *handler, BLooper *looper) 1233 { 1234 return StartWatching(BMessenger(handler, looper)); 1235 } 1236 1237 1238 void 1239 DataEditor::StopWatching(BMessenger target) 1240 { 1241 BAutolock locker(this); 1242 1243 for (int32 i = fObservers.CountItems(); i-- > 0;) { 1244 BMessenger *messenger = fObservers.ItemAt(i); 1245 if (*messenger == target) { 1246 fObservers.RemoveItemAt(i); 1247 delete messenger; 1248 break; 1249 } 1250 } 1251 1252 stop_watching(target); 1253 } 1254 1255 1256 void 1257 DataEditor::StopWatching(BHandler *handler, BLooper *looper) 1258 { 1259 StopWatching(BMessenger(handler, looper)); 1260 } 1261 1262