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