btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / apps / diskprobe / DataEditor.cpp
blobd537785ff43e355764920bc687aa61cd6d515894
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 */
7 #include "DataEditor.h"
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>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <unistd.h>
20 #include <ctype.h>
21 #include <errno.h>
24 #ifdef TRACE
25 # undef TRACE
26 #endif
28 //#define TRACE_DATA_EDITOR
29 #ifdef TRACE_DATA_EDITOR
30 # define TRACE(x) printf x
31 #else
32 # define TRACE(x) ;
33 #endif
36 class StateWatcher {
37 public:
38 StateWatcher(DataEditor &editor);
39 ~StateWatcher();
41 private:
42 DataEditor &fEditor;
43 bool fCouldUndo;
44 bool fCouldRedo;
45 bool fWasModified;
48 class DataChange {
49 public:
50 virtual ~DataChange();
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;
58 class ReplaceChange : public DataChange {
59 public:
60 ReplaceChange(off_t offset, const uint8 *data, size_t size);
61 ~ReplaceChange();
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);
68 private:
69 void Normalize(off_t bufferOffset, size_t bufferSize,
70 off_t &offset, size_t &dataOffset, size_t &size);
72 uint8 *fNewData;
73 uint8 *fOldData;
74 size_t fSize;
75 off_t fOffset;
79 //---------------------
82 #ifdef TRACE_DATA_EDITOR
84 #define DUMPED_BLOCK_SIZE 16
86 static void
87 dump_block(const uint8 *buffer, int size, const char *prefix)
89 for (int i = 0; i < size;) {
90 int start = i;
92 printf(prefix);
93 for (; i < start + DUMPED_BLOCK_SIZE; i++) {
94 if (!(i % 4))
95 printf(" ");
97 if (i >= size)
98 printf(" ");
99 else
100 printf("%02x", *(unsigned char *)(buffer + i));
102 printf(" ");
104 for (i = start; i < start + DUMPED_BLOCK_SIZE; i++) {
105 if (i < size) {
106 char c = buffer[i];
108 if (c < 30)
109 printf(".");
110 else
111 printf("%c", c);
112 } else
113 break;
115 printf("\n");
118 #endif // TRACE_DATA_EDITOR
121 static int
122 CompareCaseInsensitive(const uint8 *a, const uint8 *b, size_t size)
124 for (size_t i = 0; i < size; i++) {
125 uint8 diff = tolower(a[i]) - tolower(b[i]);
126 if (diff)
127 return diff;
130 return 0;
134 static int
135 CompareOffsets(const off_t *a, const off_t *b)
137 if (*a < *b)
138 return -1;
139 else if (*a > *b)
140 return 1;
142 return 0;
146 // #pragma mark -
149 StateWatcher::StateWatcher(DataEditor &editor)
151 fEditor(editor)
153 fCouldUndo = editor.CanUndo();
154 fCouldRedo = editor.CanRedo();
155 fWasModified = editor.IsModified();
159 StateWatcher::~StateWatcher()
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());
169 // only send an update if we have to report anything
170 if (!update.IsEmpty())
171 fEditor.SendNotices(kMsgDataEditorStateChange, &update);
175 // #pragma mark -
178 DataChange::~DataChange()
183 bool
184 DataChange::Merge(DataChange *change)
186 return false;
190 // #pragma mark -
193 ReplaceChange::ReplaceChange(off_t offset, const uint8 *data, size_t size)
195 fOffset = offset;
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;
207 ReplaceChange::~ReplaceChange()
209 free(fNewData);
210 free(fOldData);
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.
220 void
221 ReplaceChange::Normalize(off_t bufferOffset, size_t bufferSize, off_t &offset,
222 size_t &dataOffset, size_t &size)
224 if (fOffset < bufferOffset) {
225 offset = bufferOffset;
226 dataOffset = bufferOffset - fOffset;
227 size -= dataOffset;
230 if (offset + size > bufferOffset + bufferSize)
231 size = bufferOffset + bufferSize - offset;
235 void
236 ReplaceChange::Apply(off_t bufferOffset, uint8 *buffer, size_t bufferSize)
238 // is it in our range?
239 if (fOffset > (bufferOffset + (off_t)bufferSize)
240 || (fOffset + (off_t)fSize) < bufferOffset) {
241 return;
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;
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
258 // now we can safely exchange the buffer!
259 memcpy(fOldData + dataOffset, buffer + offset - bufferOffset, size);
260 memcpy(buffer + offset - bufferOffset, fNewData + dataOffset, size);
264 void
265 ReplaceChange::Revert(off_t bufferOffset, uint8 *buffer, size_t bufferSize)
267 // is it in our range?
268 if (fOffset - bufferOffset > (off_t)bufferSize
269 || fOffset + (off_t)fSize < bufferOffset) {
270 return;
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;
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
287 // now we can safely revert the buffer!
288 memcpy(buffer + offset - bufferOffset, fOldData + dataOffset, size);
292 bool
293 ReplaceChange::Merge(DataChange *_change)
295 ReplaceChange *change = dynamic_cast<ReplaceChange *>(_change);
296 if (change == NULL)
297 return false;
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;
314 // are the changes adjacent?
316 if (change->fOffset + (off_t)change->fSize != fOffset
317 && fOffset + (off_t)fSize != change->fOffset)
318 return false;
320 // okay, we can try to merge the two changes
322 uint8 *newData = fOffset < change->fOffset ? fNewData : change->fNewData;
323 size_t size = fSize + change->fSize;
325 if ((newData = (uint8 *)realloc(newData, size)) == NULL)
326 return false;
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;
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;
347 fNewData = newData;
348 fOldData = oldData;
349 fSize = size;
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
357 return true;
361 void
362 ReplaceChange::GetRange(off_t /*fileSize*/, off_t &_offset, off_t &_size)
364 _offset = fOffset;
365 _size = fSize;
369 // #pragma mark -
372 DataEditor::DataEditor()
374 BLocker("data view"),
375 fAttribute(NULL)
380 DataEditor::DataEditor(entry_ref &ref, const char *attribute)
382 BLocker("data view"),
383 fAttribute(NULL)
385 SetTo(ref, attribute);
389 DataEditor::DataEditor(BEntry &entry, const char *attribute)
391 BLocker("data view"),
392 fAttribute(NULL)
394 SetTo(entry, attribute);
398 DataEditor::DataEditor(const DataEditor &editor)
400 BLocker("data view"),
401 fAttribute(NULL)
406 DataEditor::~DataEditor()
408 free((void*)fAttribute);
412 status_t
413 DataEditor::SetTo(const char *path, const char *attribute)
415 BEntry entry(path);
416 return SetTo(entry, attribute);
420 status_t
421 DataEditor::SetTo(entry_ref &ref, const char *attribute)
423 BEntry entry(&ref);
424 return SetTo(entry, attribute);
428 status_t
429 DataEditor::SetTo(BEntry &entry, const char *attribute)
431 fSize = 0;
432 fLastChange = fFirstChange = NULL;
433 fChangesFromSaved = 0;
434 fView = NULL;
435 fRealViewOffset = 0;
436 fViewOffset = 0;
437 fRealViewSize = fViewSize = fBlockSize = 512;
439 free((void*)fAttribute);
440 if (attribute != NULL)
441 fAttribute = strdup(attribute);
442 else
443 fAttribute = NULL;
445 struct stat stat;
446 status_t status = entry.GetStat(&stat);
447 if (status < B_OK)
448 return status;
450 entry.GetRef(&fAttributeRef);
452 bool isFileSystem = false;
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;
462 status = entry.SetTo(info.device_name);
463 if (status < B_OK)
464 return status;
466 entry.GetStat(&stat);
468 fBlockSize = info.block_size;
469 if (fBlockSize > 0 && fBlockSize <= 65536)
470 isFileSystem = true;
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;
481 fIsReadOnly = true;
482 } else
483 fIsReadOnly = false;
485 entry.GetRef(&fRef);
486 fIsDevice = S_ISBLK(stat.st_mode) || S_ISCHR(stat.st_mode);
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;
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;
508 close(device);
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;
527 if (fBlockSize == 0)
528 fBlockSize = 512;
530 fRealViewSize = fViewSize = fBlockSize;
531 fNeedsUpdate = true;
533 return B_OK;
537 status_t
538 DataEditor::InitCheck()
540 return fFile.InitCheck();
544 void
545 DataEditor::AddChange(DataChange *change)
547 if (change == NULL)
548 return;
550 StateWatcher watcher(*this);
551 // update state observers
553 RemoveRedos();
554 change->Apply(fRealViewOffset, fView, fRealViewSize);
556 SendNotices(change);
557 // update observers
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;
569 status_t
570 DataEditor::Replace(off_t offset, const uint8 *data, size_t length)
572 if (IsReadOnly())
573 return B_NOT_ALLOWED;
575 BAutolock locker(this);
577 if (offset >= fSize)
578 return B_BAD_VALUE;
579 if (offset + (off_t)length > fSize)
580 length = fSize - offset;
582 if (fNeedsUpdate) {
583 status_t status = Update();
584 if (status < B_OK)
585 return status;
588 ReplaceChange *change = new ReplaceChange(offset, data, length);
589 AddChange(change);
591 return B_OK;
595 status_t
596 DataEditor::Remove(off_t offset, off_t length)
598 if (IsReadOnly())
599 return B_NOT_ALLOWED;
601 BAutolock locker(this);
603 // not yet implemented
604 // ToDo: this needs some changes to the whole change mechanism
606 return B_ERROR;
610 status_t
611 DataEditor::Insert(off_t offset, const uint8 *text, size_t length)
613 if (IsReadOnly())
614 return B_NOT_ALLOWED;
616 BAutolock locker(this);
618 // not yet implemented
619 // ToDo: this needs some changes to the whole change mechanism
621 return B_ERROR;
625 void
626 DataEditor::ApplyChanges()
628 if (fLastChange == NULL && fFirstChange == NULL)
629 return;
631 int32 firstIndex = fFirstChange != NULL ? fChanges.IndexOf(fFirstChange) + 1
632 : 0;
633 int32 lastIndex = fChanges.IndexOf(fLastChange);
635 if (fChangesFromSaved >= 0) {
636 // ascend changes
637 TRACE(("ApplyChanges(): ascend from %ld to %ld\n", firstIndex,
638 lastIndex));
640 for (int32 i = firstIndex; i <= lastIndex; i++) {
641 DataChange *change = fChanges.ItemAt(i);
642 change->Apply(fRealViewOffset, fView, fRealViewSize);
644 } else {
645 // descend changes
646 TRACE(("ApplyChanges(): descend from %ld to %ld\n", firstIndex - 1,
647 lastIndex));
649 for (int32 i = firstIndex - 1; i > lastIndex; i--) {
650 DataChange *change = fChanges.ItemAt(i);
651 change->Revert(fRealViewOffset, fView, fRealViewSize);
657 status_t
658 DataEditor::Save()
660 BAutolock locker(this);
662 if (!IsModified())
663 return B_OK;
665 StateWatcher watcher(*this);
667 // Do we need to ascend or descend the list of changes?
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);
676 int32 temp = firstIndex - 1;
677 firstIndex = lastIndex;
678 lastIndex = temp;
681 if (firstIndex < 0)
682 firstIndex = 0;
683 if (lastIndex > fChanges.CountItems() - 1)
684 lastIndex = fChanges.CountItems();
686 // Collect ranges of data we need to write.
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.
693 BObjectList<off_t> list;
694 for (int32 i = firstIndex; i <= lastIndex; i++) {
695 DataChange *change = fChanges.ItemAt(i);
697 off_t offset, size;
698 change->GetRange(FileSize(), offset, size);
699 offset -= offset % BlockSize();
701 while (size > 0) {
702 list.BinaryInsertCopyUnique(offset, CompareOffsets);
703 offset += BlockSize();
704 size -= BlockSize();
708 // read in data and apply changes, write it back to disk
710 off_t oldOffset = fViewOffset;
712 for (int32 i = 0; i < list.CountItems(); i++) {
713 off_t offset = *list.ItemAt(i);
715 // load the data into our view
716 SetViewOffset(offset, false);
718 if (fNeedsUpdate) {
719 status_t status = Update();
720 if (status < B_OK)
721 return status;
724 // save back to disk
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;
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);
738 if (bytesWritten < B_OK)
739 return bytesWritten;
742 // update state
744 SetViewOffset(oldOffset, false);
745 if (fNeedsUpdate)
746 Update();
748 fChangesFromSaved = 0;
749 fFirstChange = fLastChange;
751 return B_OK;
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.
761 void
762 DataEditor::RemoveRedos()
764 int32 start = fChanges.IndexOf(fLastChange) + 1;
766 for (int32 i = fChanges.CountItems(); i-- > start; ) {
767 DataChange *change = fChanges.RemoveItemAt(i);
768 delete change;
773 status_t
774 DataEditor::Undo()
776 BAutolock locker(this);
778 if (!CanUndo())
779 return B_ERROR;
781 StateWatcher watcher(*this);
782 // update state observers
784 DataChange *undoChange = fLastChange;
786 int32 index = fChanges.IndexOf(undoChange);
787 fChangesFromSaved--;
788 undoChange->Revert(fRealViewOffset, fView, fRealViewSize);
790 if (index > 0)
791 fLastChange = fChanges.ItemAt(index - 1);
792 else
793 fLastChange = NULL;
795 // update observers
796 SendNotices(undoChange);
798 return B_OK;
802 status_t
803 DataEditor::Redo()
805 BAutolock locker(this);
807 if (!CanRedo())
808 return B_ERROR;
810 StateWatcher watcher(*this);
811 // update state observers
813 int32 index = fChanges.IndexOf(fLastChange);
814 fLastChange = fChanges.ItemAt(index + 1);
815 fChangesFromSaved++;
817 fLastChange->Apply(fRealViewOffset, fView, fRealViewSize);
819 // update observers
820 SendNotices(fLastChange);
822 return B_OK;
826 bool
827 DataEditor::CanUndo() const
829 return fLastChange != NULL;
833 bool
834 DataEditor::CanRedo() const
836 return fChanges.IndexOf(fLastChange) < fChanges.CountItems() - 1;
840 status_t
841 DataEditor::SetFileSize(off_t size)
843 // ToDo: implement me!
844 //fSize = size;
845 //return B_OK;
846 return B_ERROR;
850 status_t
851 DataEditor::SetViewOffset(off_t offset, bool sendNotices)
853 BAutolock locker(this);
855 if (fView == NULL) {
856 status_t status = SetViewSize(fViewSize);
857 if (status < B_OK)
858 return status;
861 if (offset < 0 || offset > fSize)
862 return B_BAD_VALUE;
864 offset = (offset / fViewSize) * fViewSize;
865 if (offset == fViewOffset)
866 return B_OK;
868 fViewOffset = offset;
869 fRealViewOffset = (fViewOffset / fBlockSize) * fBlockSize;
870 fNeedsUpdate = true;
872 if (sendNotices) {
873 BMessage update;
874 update.AddInt64("offset", fViewOffset);
875 SendNotices(kMsgDataEditorParameterChange, &update);
878 return B_OK;
882 status_t
883 DataEditor::SetViewOffset(off_t offset)
885 return SetViewOffset(offset, true);
889 status_t
890 DataEditor::SetViewSize(size_t size, bool sendNotices)
892 BAutolock locker(this);
894 size_t realSize = (size + fBlockSize - 1) & ~(fBlockSize - 1);
895 // round to the next multiple of block size
897 if (realSize == fRealViewSize && fViewSize == size && fView != NULL)
898 return B_OK;
900 if (realSize == 0)
901 return B_BAD_VALUE;
903 if (realSize != fRealViewSize || fView == NULL) {
904 uint8 *view = (uint8 *)realloc(fView, realSize);
905 if (view == NULL)
906 return B_NO_MEMORY;
908 fView = view;
909 fRealViewSize = realSize;
912 fViewSize = size;
913 fNeedsUpdate = true;
915 // let's correct the view offset if necessary
916 if (fViewOffset % size)
917 SetViewOffset(fViewOffset);
919 if (sendNotices) {
920 BMessage update;
921 update.AddInt32("view_size", size);
922 SendNotices(kMsgDataEditorParameterChange, &update);
925 return B_OK;
929 status_t
930 DataEditor::SetViewSize(size_t size)
932 return SetViewSize(size, true);
936 status_t
937 DataEditor::SetBlockSize(size_t size)
939 BAutolock locker(this);
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);
947 BMessage update;
948 update.AddInt32("block_size", size);
949 update.AddInt64("offset", fViewOffset);
950 SendNotices(kMsgDataEditorParameterChange, &update);
952 return status;
956 status_t
957 DataEditor::Update()
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);
967 if (bytesRead < B_OK)
968 return bytesRead;
970 if ((size_t)bytesRead < fRealViewSize) {
971 // make sure the rest of data is cleared
972 memset(fView + bytesRead, 0, fRealViewSize - bytesRead);
975 ApplyChanges();
976 fNeedsUpdate = false;
978 return B_OK;
982 status_t
983 DataEditor::UpdateIfNeeded(bool *_updated)
985 if (!fNeedsUpdate) {
986 if (_updated)
987 *_updated = false;
988 return B_OK;
991 status_t status = B_OK;
993 if (fView == NULL)
994 status = SetViewOffset(fViewOffset);
996 if (status == B_OK && fNeedsUpdate) {
997 status = Update();
998 if (status == B_OK && _updated)
999 *_updated = true;
1002 return status;
1006 status_t
1007 DataEditor::ForceUpdate()
1009 BAutolock locker(this);
1011 status_t status = B_OK;
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
1028 if (fFile.GetSize(&newSize) != B_OK)
1029 return B_ERROR;
1032 if (fSize != newSize) {
1033 fSize = newSize;
1035 // update observers
1036 BMessage update;
1037 update.AddInt64("file_size", newSize);
1038 SendNotices(kMsgDataEditorParameterChange, &update);
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);
1050 if (status == B_OK)
1051 status = Update();
1053 return status;
1057 status_t
1058 DataEditor::GetViewBuffer(const uint8 **_buffer)
1060 if (!IsLocked())
1061 debugger("DataEditor: view not locked");
1063 status_t status = UpdateIfNeeded();
1064 if (status < B_OK)
1065 return status;
1067 *_buffer = fView + fViewOffset - fRealViewOffset;
1068 return B_OK;
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)
1077 if (data == NULL || dataSize == 0)
1078 return B_BAD_VALUE;
1080 if (startPosition < 0)
1081 startPosition = 0;
1083 BAutolock locker(this);
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;
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;
1103 // start progress monitor
1105 BMessage progress(kMsgDataEditorFindProgress);
1106 progress.AddBool("running", true);
1107 progressMonitor.SendMessage(&progress);
1110 bigtime_t lastReport = 0;
1112 off_t blocks = fSize;
1113 if (!cyclic)
1114 blocks -= position;
1115 blocks = (blocks + fRealViewSize - 1) / fRealViewSize;
1117 for (; blocks-- > 0 && !*stop; position += fRealViewSize) {
1118 if (position > fSize)
1119 position = 0;
1121 SetViewOffset(position, false);
1122 if (fNeedsUpdate)
1123 Update();
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);
1132 lastReport = current;
1135 // search for data in current block
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;
1146 foundAt = B_ENTRY_NOT_FOUND;
1147 matchLastOffset = 0;
1150 for (size_t i = firstByte; i < fRealViewSize; i++) {
1151 if (position + (off_t)(i + dataSize) > (off_t)fSize)
1152 break;
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;
1159 if (offset + size > fRealViewSize)
1160 size = fRealViewSize - offset;
1162 if (size == 0 || !compareFunc(fView + offset, data + 1, size)) {
1163 foundAt = position + i;
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;
1170 break;
1175 if (foundAt >= 0 && matchLastOffset == 0)
1176 break;
1178 firstByte = 0;
1181 fIsReadOnly = savedIsReadOnly;
1183 if (foundAt >= 0 && matchLastOffset != 0)
1184 foundAt = B_ENTRY_NOT_FOUND;
1186 // stop progress monitor
1188 BMessage progress(kMsgDataEditorFindProgress);
1189 progress.AddBool("running", false);
1190 progress.AddInt64("position", foundAt >= 0 ? foundAt : savedOffset);
1191 progressMonitor.SendMessage(&progress);
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
1198 if (foundAt < 0 && *stop)
1199 return B_INTERRUPTED;
1201 return foundAt;
1205 void
1206 DataEditor::SendNotices(DataChange *change)
1208 off_t offset, size;
1209 change->GetRange(FileSize(), offset, size);
1211 // update observer
1212 BMessage update;
1213 update.AddInt64("offset", offset);
1214 update.AddInt64("size", size);
1215 SendNotices(kMsgDataEditorUpdate, &update);
1219 void
1220 DataEditor::SendNotices(uint32 what, BMessage *message)
1222 if (fObservers.CountItems() == 0)
1223 return;
1225 BMessage *notice;
1226 if (message) {
1227 notice = new BMessage(*message);
1228 notice->what = what;
1229 } else
1230 notice = new BMessage(what);
1232 for (int32 i = fObservers.CountItems(); i-- > 0;) {
1233 BMessenger *messenger = fObservers.ItemAt(i);
1234 messenger->SendMessage(notice);
1237 delete notice;
1241 status_t
1242 DataEditor::StartWatching(BMessenger target)
1244 BAutolock locker(this);
1246 node_ref node;
1247 status_t status = fFile.GetNodeRef(&node);
1248 if (status < B_OK)
1249 return status;
1251 fObservers.AddItem(new BMessenger(target));
1253 return watch_node(&node, B_WATCH_STAT | B_WATCH_ATTR, target);
1257 status_t
1258 DataEditor::StartWatching(BHandler *handler, BLooper *looper)
1260 return StartWatching(BMessenger(handler, looper));
1264 void
1265 DataEditor::StopWatching(BMessenger target)
1267 BAutolock locker(this);
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;
1278 stop_watching(target);
1282 void
1283 DataEditor::StopWatching(BHandler *handler, BLooper *looper)
1285 StopWatching(BMessenger(handler, looper));