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