1 /* 2 * Copyright 2004-2006, 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 if ((size_t)bytesRead < fRealViewSize) { 943 // make sure the rest of data is cleared 944 memset(fView + bytesRead, 0, fRealViewSize - bytesRead); 945 } 946 947 ApplyChanges(); 948 fNeedsUpdate = false; 949 950 return B_OK; 951 } 952 953 954 status_t 955 DataEditor::UpdateIfNeeded(bool *_updated) 956 { 957 if (!fNeedsUpdate) { 958 if (_updated) 959 *_updated = false; 960 return B_OK; 961 } 962 963 status_t status = B_OK; 964 965 if (fView == NULL) 966 status = SetViewOffset(fViewOffset); 967 968 if (status == B_OK && fNeedsUpdate) { 969 status = Update(); 970 if (status == B_OK && _updated) 971 *_updated = true; 972 } 973 974 return status; 975 } 976 977 978 status_t 979 DataEditor::ForceUpdate() 980 { 981 BAutolock locker(this); 982 983 status_t status = B_OK; 984 985 off_t newSize = fSize; 986 if (IsAttribute()) { 987 // update attribute size (we ignore the type for now) 988 attr_info info; 989 status = fFile.GetAttrInfo(fAttribute, &info); 990 if (status != B_OK) { 991 // The attribute may have just been removed before 992 // it gets rewritten, so we don't do anything 993 // else here (we just set the file size to 0) 994 newSize = 0; 995 } else 996 newSize = info.size; 997 } else if (!IsDevice()) { 998 // update file size 999 1000 if (fFile.GetSize(&newSize) != B_OK) 1001 return B_ERROR; 1002 } 1003 1004 if (fSize != newSize) { 1005 fSize = newSize; 1006 1007 // update observers 1008 BMessage update; 1009 update.AddInt64("file_size", newSize); 1010 SendNotices(kMsgDataEditorParameterChange, &update); 1011 } 1012 1013 if (fView == NULL) 1014 status = SetViewOffset(fViewOffset); 1015 else { 1016 BMessage update; 1017 update.AddInt64("offset", fViewOffset); 1018 update.AddInt64("size", fViewSize); 1019 SendNotices(kMsgDataEditorUpdate, &update); 1020 } 1021 1022 if (status == B_OK) 1023 status = Update(); 1024 1025 return status; 1026 } 1027 1028 1029 status_t 1030 DataEditor::GetViewBuffer(const uint8 **_buffer) 1031 { 1032 if (!IsLocked()) 1033 debugger("DataEditor: view not locked"); 1034 1035 status_t status = UpdateIfNeeded(); 1036 if (status < B_OK) 1037 return status; 1038 1039 *_buffer = fView + fViewOffset - fRealViewOffset; 1040 return B_OK; 1041 } 1042 1043 1044 off_t 1045 DataEditor::Find(off_t startPosition, const uint8 *data, size_t dataSize, 1046 bool caseInsensitive, bool cyclic, BMessenger progressMonitor, 1047 volatile bool *stop) 1048 { 1049 if (data == NULL || dataSize == 0) 1050 return B_BAD_VALUE; 1051 1052 if (startPosition < 0) 1053 startPosition = 0; 1054 1055 BAutolock locker(this); 1056 1057 typedef int (*compare_func)(const uint8 *a, const uint8 *b, size_t size); 1058 compare_func compareFunc; 1059 if (caseInsensitive) 1060 compareFunc = CompareCaseInsensitive; 1061 else 1062 compareFunc = (compare_func)memcmp; 1063 1064 bool savedIsReadOnly = fIsReadOnly; 1065 fIsReadOnly = true; 1066 off_t savedOffset = fViewOffset; 1067 off_t position = (startPosition / fRealViewSize) * fRealViewSize; 1068 size_t firstByte = startPosition % fRealViewSize; 1069 size_t matchLastOffset = 0; 1070 off_t foundAt = B_ENTRY_NOT_FOUND; 1071 bool noStop = false; 1072 if (stop == NULL) 1073 stop = &noStop; 1074 1075 // start progress monitor 1076 { 1077 BMessage progress(kMsgDataEditorFindProgress); 1078 progress.AddBool("running", true); 1079 progressMonitor.SendMessage(&progress); 1080 } 1081 1082 bigtime_t lastReport = 0; 1083 1084 off_t blocks = fSize; 1085 if (!cyclic) 1086 blocks -= position; 1087 blocks = (blocks + fRealViewSize - 1) / fRealViewSize; 1088 1089 for (; blocks-- > 0 && !*stop; position += fRealViewSize) { 1090 if (position > fSize) 1091 position = 0; 1092 1093 SetViewOffset(position, false); 1094 if (fNeedsUpdate) 1095 Update(); 1096 1097 bigtime_t current = system_time(); 1098 if (lastReport + 500000LL < current) { 1099 // report the progress two times a second 1100 BMessage progress(kMsgDataEditorFindProgress); 1101 progress.AddInt64("position", position); 1102 progressMonitor.SendMessage(&progress); 1103 1104 lastReport = current; 1105 } 1106 1107 // search for data in current block 1108 1109 if (matchLastOffset != 0) { 1110 // we had a partial match in the previous block, let's 1111 // check if it is a whole match 1112 if (!compareFunc(fView, data + matchLastOffset, dataSize - matchLastOffset)) { 1113 matchLastOffset = 0; 1114 break; 1115 } 1116 1117 foundAt = B_ENTRY_NOT_FOUND; 1118 matchLastOffset = 0; 1119 } 1120 1121 for (size_t i = firstByte; i < fRealViewSize; i++) { 1122 if (position + i + dataSize > fSize) 1123 break; 1124 1125 if (!compareFunc(fView + i, data, 1)) { 1126 // one byte matches, compare the rest 1127 size_t size = dataSize - 1; 1128 size_t offset = i + 1; 1129 1130 if (offset + size > fRealViewSize) 1131 size = fRealViewSize - offset; 1132 1133 if (size == 0 || !compareFunc(fView + offset, data + 1, size)) { 1134 foundAt = position + i; 1135 1136 if (size != dataSize - 1) { 1137 // only a partial match, we have to check the start 1138 // of the next block 1139 matchLastOffset = size + 1; 1140 } 1141 break; 1142 } 1143 } 1144 } 1145 1146 if (foundAt >= 0 && matchLastOffset == 0) 1147 break; 1148 1149 firstByte = 0; 1150 } 1151 1152 fIsReadOnly = savedIsReadOnly; 1153 1154 if (foundAt >= 0 && matchLastOffset != 0) 1155 foundAt = B_ENTRY_NOT_FOUND; 1156 1157 // stop progress monitor 1158 { 1159 BMessage progress(kMsgDataEditorFindProgress); 1160 progress.AddBool("running", false); 1161 progress.AddInt64("position", foundAt >= 0 ? foundAt : savedOffset); 1162 progressMonitor.SendMessage(&progress); 1163 } 1164 1165 SetViewOffset(savedOffset, false); 1166 // this is to ensure that we're sending an update when 1167 // we're set to the found data offset 1168 1169 if (foundAt < 0 && *stop) 1170 return B_INTERRUPTED; 1171 1172 return foundAt; 1173 } 1174 1175 1176 void 1177 DataEditor::SendNotices(DataChange *change) 1178 { 1179 off_t offset, size; 1180 change->GetRange(FileSize(), offset, size); 1181 1182 // update observer 1183 BMessage update; 1184 update.AddInt64("offset", offset); 1185 update.AddInt64("size", size); 1186 SendNotices(kMsgDataEditorUpdate, &update); 1187 } 1188 1189 1190 void 1191 DataEditor::SendNotices(uint32 what, BMessage *message) 1192 { 1193 if (fObservers.CountItems() == 0) 1194 return; 1195 1196 BMessage *notice; 1197 if (message) { 1198 notice = new BMessage(*message); 1199 notice->what = what; 1200 } else 1201 notice = new BMessage(what); 1202 1203 for (int32 i = fObservers.CountItems(); i-- > 0;) { 1204 BMessenger *messenger = fObservers.ItemAt(i); 1205 messenger->SendMessage(notice); 1206 } 1207 1208 delete notice; 1209 } 1210 1211 1212 status_t 1213 DataEditor::StartWatching(BMessenger target) 1214 { 1215 BAutolock locker(this); 1216 1217 node_ref node; 1218 status_t status = fFile.GetNodeRef(&node); 1219 if (status < B_OK) 1220 return status; 1221 1222 fObservers.AddItem(new BMessenger(target)); 1223 1224 return watch_node(&node, B_WATCH_STAT | B_WATCH_ATTR, target); 1225 } 1226 1227 1228 status_t 1229 DataEditor::StartWatching(BHandler *handler, BLooper *looper) 1230 { 1231 return StartWatching(BMessenger(handler, looper)); 1232 } 1233 1234 1235 void 1236 DataEditor::StopWatching(BMessenger target) 1237 { 1238 BAutolock locker(this); 1239 1240 for (int32 i = fObservers.CountItems(); i-- > 0;) { 1241 BMessenger *messenger = fObservers.ItemAt(i); 1242 if (*messenger == target) { 1243 fObservers.RemoveItemAt(i); 1244 delete messenger; 1245 break; 1246 } 1247 } 1248 1249 stop_watching(target); 1250 } 1251 1252 1253 void 1254 DataEditor::StopWatching(BHandler *handler, BLooper *looper) 1255 { 1256 StopWatching(BMessenger(handler, looper)); 1257 } 1258 1259