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