1 /* 2 * Copyright 2004-2005, 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 fRealViewSize = fViewSize = fBlockSize; 509 fNeedsUpdate = true; 510 511 return B_OK; 512 } 513 514 515 status_t 516 DataEditor::InitCheck() 517 { 518 return fFile.InitCheck(); 519 } 520 521 522 void 523 DataEditor::AddChange(DataChange *change) 524 { 525 if (change == NULL) 526 return; 527 528 StateWatcher watcher(*this); 529 // update state observers 530 531 RemoveRedos(); 532 change->Apply(fRealViewOffset, fView, fRealViewSize); 533 534 SendNotices(change); 535 // update observers 536 537 // try to join changes 538 if (fLastChange == NULL || !fLastChange->Merge(change)) { 539 fChanges.AddItem(change); 540 fLastChange = change; 541 fChangesFromSaved++; 542 } else 543 delete change; 544 } 545 546 547 status_t 548 DataEditor::Replace(off_t offset, const uint8 *data, size_t length) 549 { 550 if (IsReadOnly()) 551 return B_NOT_ALLOWED; 552 553 BAutolock locker(this); 554 555 if (offset >= fSize) 556 return B_BAD_VALUE; 557 if (offset + length > fSize) 558 length = fSize - offset; 559 560 if (fNeedsUpdate) { 561 status_t status = Update(); 562 if (status < B_OK) 563 return status; 564 } 565 566 ReplaceChange *change = new ReplaceChange(offset, data, length); 567 AddChange(change); 568 569 return B_OK; 570 } 571 572 573 status_t 574 DataEditor::Remove(off_t offset, off_t length) 575 { 576 if (IsReadOnly()) 577 return B_NOT_ALLOWED; 578 579 BAutolock locker(this); 580 581 // not yet implemented 582 // ToDo: this needs some changes to the whole change mechanism 583 584 return B_ERROR; 585 } 586 587 588 status_t 589 DataEditor::Insert(off_t offset, const uint8 *text, size_t length) 590 { 591 if (IsReadOnly()) 592 return B_NOT_ALLOWED; 593 594 BAutolock locker(this); 595 596 // not yet implemented 597 // ToDo: this needs some changes to the whole change mechanism 598 599 return B_ERROR; 600 } 601 602 603 void 604 DataEditor::ApplyChanges() 605 { 606 if (fLastChange == NULL && fFirstChange == NULL) 607 return; 608 609 int32 firstIndex = fFirstChange != NULL ? fChanges.IndexOf(fFirstChange) + 1 : 0; 610 int32 lastIndex = fChanges.IndexOf(fLastChange); 611 612 if (fChangesFromSaved >= 0) { 613 // ascend changes 614 TRACE(("ApplyChanges(): ascend from %ld to %ld\n", firstIndex, lastIndex)); 615 616 for (int32 i = firstIndex; i <= lastIndex; i++) { 617 DataChange *change = fChanges.ItemAt(i); 618 change->Apply(fRealViewOffset, fView, fRealViewSize); 619 } 620 } else { 621 // descend changes 622 TRACE(("ApplyChanges(): descend from %ld to %ld\n", firstIndex - 1, lastIndex)); 623 624 for (int32 i = firstIndex - 1; i > lastIndex; i--) { 625 DataChange *change = fChanges.ItemAt(i); 626 change->Revert(fRealViewOffset, fView, fRealViewSize); 627 } 628 } 629 } 630 631 632 status_t 633 DataEditor::Save() 634 { 635 BAutolock locker(this); 636 637 if (!IsModified()) 638 return B_OK; 639 640 StateWatcher watcher(*this); 641 642 // Do we need to ascend or descend the list of changes? 643 644 int32 firstIndex = fFirstChange != NULL ? fChanges.IndexOf(fFirstChange) + 1 : 0; 645 int32 lastIndex = fChanges.IndexOf(fLastChange); 646 if (fChangesFromSaved < 0 && firstIndex != lastIndex) { 647 // swap indices 648 ASSERT(firstIndex > lastIndex); 649 650 int32 temp = firstIndex - 1; 651 firstIndex = lastIndex; 652 lastIndex = temp; 653 } 654 655 if (firstIndex < 0) 656 firstIndex = 0; 657 if (lastIndex > fChanges.CountItems() - 1) 658 lastIndex = fChanges.CountItems(); 659 660 // Collect ranges of data we need to write. 661 662 // ToDo: This is a very simple implementation and could be drastically 663 // improved by having items that save ranges, not just offsets. If Insert() 664 // and Remove() are going to be implemented, it should be improved that 665 // way to reduce the memory footprint to something acceptable. 666 667 BObjectList<off_t> list; 668 for (int32 i = firstIndex; i <= lastIndex; i++) { 669 DataChange *change = fChanges.ItemAt(i); 670 671 off_t offset, size; 672 change->GetRange(FileSize(), offset, size); 673 offset -= offset % BlockSize(); 674 675 while (size > 0) { 676 list.BinaryInsertCopyUnique(offset, CompareOffsets); 677 offset += BlockSize(); 678 size -= BlockSize(); 679 } 680 } 681 682 // read in data and apply changes, write it back to disk 683 684 off_t oldOffset = fViewOffset; 685 686 for (int32 i = 0; i < list.CountItems(); i++) { 687 off_t offset = *list.ItemAt(i); 688 689 // load the data into our view 690 SetViewOffset(offset, false); 691 692 if (fNeedsUpdate) { 693 status_t status = Update(); 694 if (status < B_OK) 695 return status; 696 } 697 698 // save back to disk 699 700 // don't try to change the file size 701 size_t size = fRealViewSize; 702 if (fRealViewOffset + fRealViewSize > fSize) 703 size = fSize - fRealViewOffset; 704 705 ssize_t bytesWritten; 706 if (IsAttribute()) 707 bytesWritten = fFile.WriteAttr(fAttribute, fType, fRealViewOffset, fView, size); 708 else 709 bytesWritten = fFile.WriteAt(fRealViewOffset, fView, size); 710 711 if (bytesWritten < B_OK) 712 return bytesWritten; 713 } 714 715 // update state 716 717 SetViewOffset(oldOffset, false); 718 if (fNeedsUpdate) 719 Update(); 720 721 fChangesFromSaved = 0; 722 fFirstChange = fLastChange; 723 724 return B_OK; 725 } 726 727 728 /** This method will be called by DataEditor::AddChange() 729 * immediately before a change is applied. 730 * It removes all pending redo nodes from the list that would 731 * come after the current change. 732 */ 733 734 void 735 DataEditor::RemoveRedos() 736 { 737 int32 start = fChanges.IndexOf(fLastChange) + 1; 738 739 for (int32 i = fChanges.CountItems(); i-- > start; ) { 740 DataChange *change = fChanges.RemoveItemAt(i); 741 delete change; 742 } 743 } 744 745 746 status_t 747 DataEditor::Undo() 748 { 749 BAutolock locker(this); 750 751 if (!CanUndo()) 752 return B_ERROR; 753 754 StateWatcher watcher(*this); 755 // update state observers 756 757 DataChange *undoChange = fLastChange; 758 759 int32 index = fChanges.IndexOf(undoChange); 760 fChangesFromSaved--; 761 undoChange->Revert(fRealViewOffset, fView, fRealViewSize); 762 763 if (index > 0) 764 fLastChange = fChanges.ItemAt(index - 1); 765 else 766 fLastChange = NULL; 767 768 // update observers 769 SendNotices(undoChange); 770 771 return B_OK; 772 } 773 774 775 status_t 776 DataEditor::Redo() 777 { 778 BAutolock locker(this); 779 780 if (!CanRedo()) 781 return B_ERROR; 782 783 StateWatcher watcher(*this); 784 // update state observers 785 786 int32 index = fChanges.IndexOf(fLastChange); 787 fLastChange = fChanges.ItemAt(index + 1); 788 fChangesFromSaved++; 789 790 fLastChange->Apply(fRealViewOffset, fView, fRealViewSize); 791 792 // update observers 793 SendNotices(fLastChange); 794 795 return B_OK; 796 } 797 798 799 bool 800 DataEditor::CanUndo() const 801 { 802 return fLastChange != NULL; 803 } 804 805 806 bool 807 DataEditor::CanRedo() const 808 { 809 return fChanges.IndexOf(fLastChange) < fChanges.CountItems() - 1; 810 } 811 812 813 status_t 814 DataEditor::SetFileSize(off_t size) 815 { 816 // ToDo: implement me! 817 //fSize = size; 818 //return B_OK; 819 return B_ERROR; 820 } 821 822 823 status_t 824 DataEditor::SetViewOffset(off_t offset, bool sendNotices) 825 { 826 BAutolock locker(this); 827 828 if (fView == NULL) { 829 status_t status = SetViewSize(fViewSize); 830 if (status < B_OK) 831 return status; 832 } 833 834 if (offset < 0 || offset > fSize) 835 return B_BAD_VALUE; 836 837 offset = (offset / fViewSize) * fViewSize; 838 if (offset == fViewOffset) 839 return B_OK; 840 841 fViewOffset = offset; 842 fRealViewOffset = (fViewOffset / fBlockSize) * fBlockSize; 843 fNeedsUpdate = true; 844 845 if (sendNotices) { 846 BMessage update; 847 update.AddInt64("offset", fViewOffset); 848 SendNotices(kMsgDataEditorParameterChange, &update); 849 } 850 851 return B_OK; 852 } 853 854 855 status_t 856 DataEditor::SetViewOffset(off_t offset) 857 { 858 return SetViewOffset(offset, true); 859 } 860 861 862 status_t 863 DataEditor::SetViewSize(size_t size, bool sendNotices) 864 { 865 BAutolock locker(this); 866 867 size_t realSize = (size + fBlockSize - 1) & ~(fBlockSize - 1); 868 // round to the next multiple of block size 869 870 if (realSize == fRealViewSize && fViewSize == size && fView != NULL) 871 return B_OK; 872 873 if (realSize == 0) 874 return B_BAD_VALUE; 875 876 if (realSize != fRealViewSize || fView == NULL) { 877 uint8 *view = (uint8 *)realloc(fView, realSize); 878 if (view == NULL) 879 return B_NO_MEMORY; 880 881 fView = view; 882 fRealViewSize = realSize; 883 } 884 885 fViewSize = size; 886 fNeedsUpdate = true; 887 888 // let's correct the view offset if necessary 889 if (fViewOffset % size) 890 SetViewOffset(fViewOffset); 891 892 if (sendNotices) { 893 BMessage update; 894 update.AddInt32("view_size", size); 895 SendNotices(kMsgDataEditorParameterChange, &update); 896 } 897 898 return B_OK; 899 } 900 901 902 status_t 903 DataEditor::SetViewSize(size_t size) 904 { 905 return SetViewSize(size, true); 906 } 907 908 909 status_t 910 DataEditor::SetBlockSize(size_t size) 911 { 912 BAutolock locker(this); 913 914 fBlockSize = size; 915 status_t status = SetViewOffset(fViewOffset, false); 916 // this will trigger an update of the real view offset 917 if (status == B_OK) 918 status = SetViewSize(fViewSize, false); 919 920 BMessage update; 921 update.AddInt32("block_size", size); 922 update.AddInt64("offset", fViewOffset); 923 SendNotices(kMsgDataEditorParameterChange, &update); 924 925 return status; 926 } 927 928 929 status_t 930 DataEditor::Update() 931 { 932 ssize_t bytesRead; 933 if (IsAttribute()) { 934 BNode node(&fAttributeRef); 935 bytesRead = node.ReadAttr(fAttribute, fType, fRealViewOffset, fView, fRealViewSize); 936 } else 937 bytesRead = fFile.ReadAt(fRealViewOffset, fView, fRealViewSize); 938 939 if (bytesRead < B_OK) 940 return bytesRead; 941 942 ApplyChanges(); 943 fNeedsUpdate = false; 944 945 return B_OK; 946 } 947 948 949 status_t 950 DataEditor::UpdateIfNeeded(bool *_updated) 951 { 952 if (!fNeedsUpdate) { 953 if (_updated) 954 *_updated = false; 955 return B_OK; 956 } 957 958 status_t status = B_OK; 959 960 if (fView == NULL) 961 status = SetViewOffset(fViewOffset); 962 963 if (status == B_OK && fNeedsUpdate) { 964 status = Update(); 965 if (status == B_OK && _updated) 966 *_updated = true; 967 } 968 969 return status; 970 } 971 972 973 status_t 974 DataEditor::ForceUpdate() 975 { 976 BAutolock locker(this); 977 978 status_t status = B_OK; 979 980 off_t newSize = fSize; 981 if (IsAttribute()) { 982 // update attribute size (we ignore the type for now) 983 attr_info info; 984 status = fFile.GetAttrInfo(fAttribute, &info); 985 if (status != B_OK) { 986 // The attribute may have just been removed before 987 // it gets rewritten, so we don't do anything 988 // else here (we just set the file size to 0) 989 newSize = 0; 990 } else 991 newSize = info.size; 992 } else if (!IsDevice()) { 993 // update file size 994 995 if (fFile.GetSize(&newSize) != B_OK) 996 return B_ERROR; 997 } 998 999 if (fSize != newSize) { 1000 fSize = newSize; 1001 1002 // update observers 1003 BMessage update; 1004 update.AddInt64("file_size", newSize); 1005 SendNotices(kMsgDataEditorParameterChange, &update); 1006 } 1007 1008 if (fView == NULL) 1009 status = SetViewOffset(fViewOffset); 1010 else { 1011 BMessage update; 1012 update.AddInt64("offset", fViewOffset); 1013 update.AddInt64("size", fViewSize); 1014 SendNotices(kMsgDataEditorUpdate, &update); 1015 } 1016 1017 if (status == B_OK) 1018 status = Update(); 1019 1020 return status; 1021 } 1022 1023 1024 status_t 1025 DataEditor::GetViewBuffer(const uint8 **_buffer) 1026 { 1027 if (!IsLocked()) 1028 debugger("DataEditor: view not locked"); 1029 1030 status_t status = UpdateIfNeeded(); 1031 if (status < B_OK) 1032 return status; 1033 1034 *_buffer = fView + fViewOffset - fRealViewOffset; 1035 return B_OK; 1036 } 1037 1038 1039 off_t 1040 DataEditor::Find(off_t startPosition, const uint8 *data, size_t dataSize, 1041 bool caseInsensitive, bool cyclic, BMessenger progressMonitor, 1042 volatile bool *stop) 1043 { 1044 if (data == NULL || dataSize == 0) 1045 return B_BAD_VALUE; 1046 1047 if (startPosition < 0) 1048 startPosition = 0; 1049 1050 BAutolock locker(this); 1051 1052 typedef int (*compare_func)(const uint8 *a, const uint8 *b, size_t size); 1053 compare_func compareFunc; 1054 if (caseInsensitive) 1055 compareFunc = CompareCaseInsensitive; 1056 else 1057 compareFunc = (compare_func)memcmp; 1058 1059 bool savedIsReadOnly = fIsReadOnly; 1060 fIsReadOnly = true; 1061 off_t savedOffset = fViewOffset; 1062 off_t position = (startPosition / fRealViewSize) * fRealViewSize; 1063 size_t firstByte = startPosition % fRealViewSize; 1064 size_t matchLastOffset = 0; 1065 off_t foundAt = B_ENTRY_NOT_FOUND; 1066 bool noStop = false; 1067 if (stop == NULL) 1068 stop = &noStop; 1069 1070 // start progress monitor 1071 { 1072 BMessage progress(kMsgDataEditorFindProgress); 1073 progress.AddBool("running", true); 1074 progressMonitor.SendMessage(&progress); 1075 } 1076 1077 bigtime_t lastReport = 0; 1078 1079 off_t blocks = fSize; 1080 if (!cyclic) 1081 blocks -= position; 1082 blocks = (blocks + fRealViewSize - 1) / fRealViewSize; 1083 1084 for (; blocks-- > 0 && !*stop; position += fRealViewSize) { 1085 if (position > fSize) 1086 position = 0; 1087 1088 SetViewOffset(position, false); 1089 if (fNeedsUpdate) 1090 Update(); 1091 1092 bigtime_t current = system_time(); 1093 if (lastReport + 500000LL < current) { 1094 // report the progress two times a second 1095 BMessage progress(kMsgDataEditorFindProgress); 1096 progress.AddInt64("position", position); 1097 progressMonitor.SendMessage(&progress); 1098 1099 lastReport = current; 1100 } 1101 1102 // search for data in current block 1103 1104 if (matchLastOffset != 0) { 1105 // we had a partial match in the previous block, let's 1106 // check if it is a whole match 1107 if (!compareFunc(fView, data + matchLastOffset, dataSize - matchLastOffset)) { 1108 matchLastOffset = 0; 1109 break; 1110 } 1111 1112 foundAt = B_ENTRY_NOT_FOUND; 1113 matchLastOffset = 0; 1114 } 1115 1116 for (size_t i = firstByte; i < fRealViewSize; i++) { 1117 if (position + i + dataSize > fSize) 1118 break; 1119 1120 if (!compareFunc(fView + i, data, 1)) { 1121 // one byte matches, compare the rest 1122 size_t size = dataSize - 1; 1123 size_t offset = i + 1; 1124 1125 if (offset + size > fRealViewSize) 1126 size = fRealViewSize - offset; 1127 1128 if (size == 0 || !compareFunc(fView + offset, data + 1, size)) { 1129 foundAt = position + i; 1130 1131 if (size != dataSize - 1) { 1132 // only a partial match, we have to check the start 1133 // of the next block 1134 matchLastOffset = size + 1; 1135 } 1136 break; 1137 } 1138 } 1139 } 1140 1141 if (foundAt >= 0 && matchLastOffset == 0) 1142 break; 1143 1144 firstByte = 0; 1145 } 1146 1147 fIsReadOnly = savedIsReadOnly; 1148 1149 if (foundAt >= 0 && matchLastOffset != 0) 1150 foundAt = B_ENTRY_NOT_FOUND; 1151 1152 // stop progress monitor 1153 { 1154 BMessage progress(kMsgDataEditorFindProgress); 1155 progress.AddBool("running", false); 1156 progress.AddInt64("position", foundAt >= 0 ? foundAt : savedOffset); 1157 progressMonitor.SendMessage(&progress); 1158 } 1159 1160 SetViewOffset(savedOffset, false); 1161 // this is to ensure that we're sending an update when 1162 // we're set to the found data offset 1163 1164 if (foundAt < 0 && *stop) 1165 return B_INTERRUPTED; 1166 1167 return foundAt; 1168 } 1169 1170 1171 void 1172 DataEditor::SendNotices(DataChange *change) 1173 { 1174 off_t offset, size; 1175 change->GetRange(FileSize(), offset, size); 1176 1177 // update observer 1178 BMessage update; 1179 update.AddInt64("offset", offset); 1180 update.AddInt64("size", size); 1181 SendNotices(kMsgDataEditorUpdate, &update); 1182 } 1183 1184 1185 void 1186 DataEditor::SendNotices(uint32 what, BMessage *message) 1187 { 1188 if (fObservers.CountItems() == 0) 1189 return; 1190 1191 BMessage *notice; 1192 if (message) { 1193 notice = new BMessage(*message); 1194 notice->what = what; 1195 } else 1196 notice = new BMessage(what); 1197 1198 for (int32 i = fObservers.CountItems(); i-- > 0;) { 1199 BMessenger *messenger = fObservers.ItemAt(i); 1200 messenger->SendMessage(notice); 1201 } 1202 1203 delete notice; 1204 } 1205 1206 1207 status_t 1208 DataEditor::StartWatching(BMessenger target) 1209 { 1210 BAutolock locker(this); 1211 1212 node_ref node; 1213 status_t status = fFile.GetNodeRef(&node); 1214 if (status < B_OK) 1215 return status; 1216 1217 fObservers.AddItem(new BMessenger(target)); 1218 1219 return watch_node(&node, B_WATCH_STAT | B_WATCH_ATTR, target); 1220 } 1221 1222 1223 status_t 1224 DataEditor::StartWatching(BHandler *handler, BLooper *looper) 1225 { 1226 return StartWatching(BMessenger(handler, looper)); 1227 } 1228 1229 1230 void 1231 DataEditor::StopWatching(BMessenger target) 1232 { 1233 BAutolock locker(this); 1234 1235 for (int32 i = fObservers.CountItems(); i-- > 0;) { 1236 BMessenger *messenger = fObservers.ItemAt(i); 1237 if (*messenger == target) { 1238 fObservers.RemoveItemAt(i); 1239 delete messenger; 1240 break; 1241 } 1242 } 1243 1244 stop_watching(target); 1245 } 1246 1247 1248 void 1249 DataEditor::StopWatching(BHandler *handler, BLooper *looper) 1250 { 1251 StopWatching(BMessenger(handler, looper)); 1252 } 1253 1254