xref: /haiku/src/apps/diskprobe/DataEditor.cpp (revision 16c83730262f1e4f0fc69d80744bb36dcfbbe3af)
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