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