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