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