From 1df68d1dee54532fb02515cff83c9d8392adacd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Thu, 15 Jan 2004 17:21:23 +0000 Subject: [PATCH] Started to work on a DiskProbe replacement as a side project. Implemented the basic editor engine that has features like undo/redo and everything else DiskProbe could possibly need - not complete yet, though it already has everything that would be needed for a 1:1 DiskProbe duplicate. The DiskProbe app is currently just a small text based test for that engine. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@6096 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/apps/diskprobe/DataEditor.cpp | 475 ++++++++++++++++++++++++++++++ src/apps/diskprobe/DataEditor.h | 96 ++++++ src/apps/diskprobe/DiskProbe.cpp | 153 ++++++++++ src/apps/diskprobe/Jamfile | 12 + 4 files changed, 736 insertions(+) create mode 100644 src/apps/diskprobe/DataEditor.cpp create mode 100644 src/apps/diskprobe/DataEditor.h create mode 100644 src/apps/diskprobe/DiskProbe.cpp create mode 100644 src/apps/diskprobe/Jamfile diff --git a/src/apps/diskprobe/DataEditor.cpp b/src/apps/diskprobe/DataEditor.cpp new file mode 100644 index 0000000000..3861be2f34 --- /dev/null +++ b/src/apps/diskprobe/DataEditor.cpp @@ -0,0 +1,475 @@ +/* +** Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved. +** Distributed under the terms of the OpenBeOS License. +*/ + + +#include "DataEditor.h" + +#include +#include + + +class DataChange { + public: + virtual ~DataChange(); + + virtual void Apply(off_t offset, uint8 *buffer, size_t size) = 0; + virtual void Revert(off_t offset, uint8 *buffer, size_t size) = 0; +}; + +class ReplaceChange : public DataChange { + public: + ReplaceChange(off_t bufferOffset, uint8 *buffer, + off_t offset, const uint8 *data, size_t size); + ~ReplaceChange(); + + virtual void Apply(off_t offset, uint8 *buffer, size_t size); + virtual void Revert(off_t offset, uint8 *buffer, size_t size); + + private: + void Normalize(off_t bufferOffset, size_t bufferSize, + off_t &offset, size_t &dataOffset, size_t &size); + + uint8 *fNewData; + uint8 *fOldData; + size_t fSize; + off_t fOffset; +}; + + +DataChange::~DataChange() +{ +} + + +// #pragma mark - + + +ReplaceChange::ReplaceChange(off_t bufferOffset, uint8 *buffer, + off_t offset, const uint8 *data, size_t size) +{ + fOffset = offset; + + fNewData = (uint8 *)malloc(size); + fOldData = (uint8 *)malloc(size); + if (fNewData != NULL && fOldData != NULL) { + // Save old data, and replace the data in the supplied buffer. + // Note, the buffer must be large enough to hold the changes + // at this point! + memcpy(fNewData, data, size); + memcpy(fOldData, buffer + offset - bufferOffset, size); + memcpy(buffer + offset - bufferOffset, fNewData, size); + + fSize = size; + } else + fSize = 0; +} + + +ReplaceChange::~ReplaceChange() +{ + free(fNewData); + free(fOldData); +} + + +/** Normalizes the supplied offset & size pair to be limited by + * the buffer offset and size. + * All parameters must have been initialized before calling this + * method. + */ + +void +ReplaceChange::Normalize(off_t bufferOffset, size_t bufferSize, off_t &offset, + size_t &dataOffset, size_t &size) +{ + if (fOffset < bufferOffset) { + offset = bufferOffset; + dataOffset = bufferOffset - fOffset; + size -= dataOffset; + } + + if (offset + size > bufferOffset + bufferSize) + size = offset - bufferOffset + offset; +} + + +void +ReplaceChange::Apply(off_t bufferOffset, uint8 *buffer, size_t bufferSize) +{ + // is it in our range? + if (fOffset - bufferOffset > bufferSize || fOffset + fSize < bufferOffset) + return; + + // don't change anything outside the supplied buffer + off_t offset = fOffset; + size_t dataOffset = 0; + size_t size = fSize; + Normalize(bufferOffset, bufferSize, offset, dataOffset, size); + if (size == 0) + return; + + // now we can safely change the buffer! + memcpy(buffer + offset - bufferOffset, fNewData + dataOffset, size); +} + + +void +ReplaceChange::Revert(off_t bufferOffset, uint8 *buffer, size_t bufferSize) +{ + // is it in our range? + if (fOffset - bufferOffset > bufferSize || fOffset + fSize < bufferOffset) + return; + + // don't change anything outside the supplied buffer + off_t offset = fOffset; + size_t dataOffset = 0; + size_t size = fSize; + Normalize(bufferOffset, bufferSize, offset, dataOffset, size); + if (size == 0) + return; + + // now we can safely revert the buffer! + memcpy(buffer + offset - bufferOffset, fOldData + dataOffset, size); +} + + +// #pragma mark - + + +DataEditor::DataEditor() + : + fLock("data view") +{ +} + + +DataEditor::DataEditor(entry_ref &ref, const char *attribute) + : + fLock("data view") +{ + SetTo(ref, attribute); +} + + +DataEditor::DataEditor(BEntry &entry, const char *attribute) + : + fLock("data view") +{ + SetTo(entry, attribute); +} + + +DataEditor::DataEditor(const DataEditor &editor) + : + fLock("data view") +{ +} + + +DataEditor::~DataEditor() +{ +} + + +status_t +DataEditor::SetTo(const char *path, const char *attribute) +{ + BEntry entry(path); + SetTo(entry, attribute); +} + + +status_t +DataEditor::SetTo(entry_ref &ref, const char *attribute) +{ + BEntry entry(&ref); + SetTo(entry, attribute); +} + + +status_t +DataEditor::SetTo(BEntry &entry, const char *attribute) +{ + status_t status = fFile.SetTo(&entry, B_READ_WRITE); + if (status < B_OK) { + // try to open read only + status = fFile.SetTo(&entry, B_READ_ONLY); + if (status < B_OK) + return status; + + fIsReadOnly = true; + } + + status = fFile.GetSize(&fSize); + if (status < B_OK) { + fFile.Unset(); + return status; + } + + fBlockSize = 512; + fAttribute = attribute; + fRealViewOffset = -1; + fViewOffset = 0; + fRealViewSize = fViewSize = fBlockSize; + fNeedsUpdate = true; +} + + +status_t +DataEditor::SetToAttribute(const char *attribute) +{ + status_t status = InitCheck(); + if (status < B_OK) + return status; + + fAttribute = attribute; + + // ToDo: attributes are not yet supported + return B_ERROR; +} + + +status_t +DataEditor::InitCheck() +{ + if (fAttribute != NULL) + // ToDo: for now! + return B_ERROR; + + return fFile.InitCheck(); +} + + +void +DataEditor::AddChange(DataChange *change) +{ + if (change == NULL) + return; + + fChanges.AddItem(change); + fLastChange = change; +} + + +status_t +DataEditor::Replace(off_t offset, const uint8 *data, size_t length) +{ + if (!fLock.IsLocked()) + debugger("DataEditor: view not locked"); + + if (fNeedsUpdate) { + status_t status = Update(); + if (status < B_OK) + return status; + } + + ReplaceChange *change = new ReplaceChange(fRealViewOffset, fView, offset, data, length); + AddChange(change); + + return B_OK; +} + + +status_t +DataEditor::Remove(off_t offset, off_t length) +{ + if (!fLock.IsLocked()) + debugger("DataEditor: view not locked"); + + // not yet implemented + + return B_ERROR; +} + + +status_t +DataEditor::Insert(off_t offset, const uint8 *text, size_t length) +{ + if (!fLock.IsLocked()) + debugger("DataEditor: view not locked"); + + // not yet implemented + + return B_ERROR; +} + + +void +DataEditor::ApplyChanges() +{ + int32 count = fChanges.CountItems(); + + for (int32 i = 0; i < count; i++) { + DataChange *change = fChanges.ItemAt(i); + change->Apply(fRealViewOffset, fView, fRealViewSize); + } +} + + +void +DataEditor::RemoveRedos() +{ +} + + +status_t +DataEditor::Undo() +{ + if (!CanUndo()) + return B_ERROR; + + int32 index = fChanges.IndexOf(fLastChange); + fLastChange->Revert(fRealViewOffset, fView, fRealViewSize); + + if (index > 0) + fLastChange = fChanges.ItemAt(index - 1); + else + fLastChange = NULL; + + return B_OK; +} + + +status_t +DataEditor::Redo() +{ +} + + +bool +DataEditor::CanUndo() const +{ + return fLastChange != NULL; +} + + +bool +DataEditor::CanRedo() const +{ + return fLastChange != NULL && fChanges.IndexOf(fLastChange) < fChanges.CountItems() - 1; +} + + +status_t +DataEditor::SetFileSize(off_t size) +{ + fSize = size; +} + + +status_t +DataEditor::SetViewOffset(off_t offset) +{ + if (fView == NULL) { + status_t status = SetViewSize(fViewSize); + if (status < B_OK) + return status; + } + + fRealViewOffset = (offset / fBlockSize) * fBlockSize; + fViewOffset = offset; + fNeedsUpdate = true; +} + + +status_t +DataEditor::SetViewSize(size_t size) +{ + size_t realSize = (size + fBlockSize - 1) & ~(fBlockSize - 1); + // round to the next multiple of block size + + if (realSize == fRealViewSize && fView != NULL) { + fViewSize = size; + return B_OK; + } + + uint8 *view = (uint8 *)realloc(fView, realSize); + if (view == NULL) + return B_NO_MEMORY; + + fView = view; + fRealViewSize = realSize; + fViewSize = size; + fNeedsUpdate = true; + + return B_OK; +} + + +void +DataEditor::SetBlockSize(size_t size) +{ + fBlockSize = size; +} + + +status_t +DataEditor::Update() +{ + ssize_t bytesRead = fFile.ReadAt(fRealViewOffset, fView, fRealViewSize); + if (bytesRead < B_OK) + return bytesRead; + + ApplyChanges(); + fNeedsUpdate = false; + + return B_OK; +} + + +status_t +DataEditor::GetViewBuffer(const uint8 **_buffer) +{ + if (!fLock.IsLocked()) + debugger("DataEditor: view not locked"); + + status_t status = B_OK; + + if (fView == NULL) + status = SetViewOffset(fViewOffset); + + if (status == B_OK && fNeedsUpdate) + status = Update(); + + if (status < B_OK) + return status; + + *_buffer = fView + fViewOffset - fRealViewOffset; + return B_OK; +} + + +bool +DataEditor::Lock() +{ + return fLock.Lock(); +} + + +void +DataEditor::Unlock() +{ + fLock.Unlock(); +} + + +status_t +DataEditor::StartWatching(BMessenger target) +{ + node_ref node; + status_t status = fFile.GetNodeRef(&node); + if (status < B_OK) + return status; + + return watch_node(&node, B_WATCH_STAT | B_WATCH_ATTR, target); +} + + +void +DataEditor::StopWatching(BMessenger target) +{ + stop_watching(target); +} + diff --git a/src/apps/diskprobe/DataEditor.h b/src/apps/diskprobe/DataEditor.h new file mode 100644 index 0000000000..13021d9ab9 --- /dev/null +++ b/src/apps/diskprobe/DataEditor.h @@ -0,0 +1,96 @@ +/* +** Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved. +** Distributed under the terms of the OpenBeOS License. +*/ +#ifndef DATA_EDITOR_H +#define DATA_EDITOR_H + + +#include +#include +#include +#include + + +class DataChange; + +class DataEditor { + public: + DataEditor(); + DataEditor(entry_ref &ref, const char *attribute = NULL); + DataEditor(BEntry &entry, const char *attribute = NULL); + DataEditor(const DataEditor &editor); + ~DataEditor(); + + status_t SetTo(const char *path, const char *attribute = NULL); + status_t SetTo(entry_ref &ref, const char *attribute = NULL); + status_t SetTo(BEntry &entry, const char *attribute = NULL); + status_t SetToAttribute(const char *attribute); + + bool IsReadOnly() const { return fIsReadOnly; } + bool IsDevice() const { return fIsDevice; } + //bool IsModified() const { return fIsModified; } + + status_t InitCheck(); + + status_t Replace(off_t offset, const uint8 *data, size_t length); + status_t Remove(off_t offset, off_t length); + status_t Insert(off_t offset, const uint8 *data, size_t length); + + status_t MoveBy(int32 bytes); + status_t MoveTo(off_t offset); + + status_t Undo(); + status_t Redo(); + + bool CanUndo() const; + bool CanRedo() const; + + status_t SetFileSize(off_t size); + off_t FileSize() const { return fSize; } + + status_t SetViewOffset(off_t offset); + off_t ViewOffset() const { return fViewOffset; } + + status_t SetViewSize(size_t size); + off_t ViewSize() const { return fViewSize; } + + void SetBlockSize(size_t size); + size_t BlockSize() const { return fBlockSize; } + + status_t GetViewBuffer(const uint8 **_buffer); + + BLocker &Locker() { return fLock; } + bool Lock(); + void Unlock(); + + status_t StartWatching(BMessenger target); + void StopWatching(BMessenger target); + + BFile &File() { return fFile; } + + private: + status_t Update(); + void AddChange(DataChange *change); + void ApplyChanges(); + void RemoveRedos(); + + BFile fFile; + const char *fAttribute; + bool fIsDevice, fIsReadOnly; + off_t fRealSize, fSize; + + BObjectList fChanges; + DataChange *fFirstChange; + DataChange *fLastChange; + + BLocker fLock; + uint8 *fView; + off_t fRealViewOffset, fViewOffset; + size_t fRealViewSize, fViewSize; + bool fNeedsUpdate; + + size_t fBlockSize; +}; + +#endif /* DATA_EDITOR_H */ diff --git a/src/apps/diskprobe/DiskProbe.cpp b/src/apps/diskprobe/DiskProbe.cpp new file mode 100644 index 0000000000..5e6afbf3e9 --- /dev/null +++ b/src/apps/diskprobe/DiskProbe.cpp @@ -0,0 +1,153 @@ +/* +** Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved. +** Distributed under the terms of the OpenBeOS License. +*/ + + +#include "DataEditor.h" + +#include +#include + +#include +#include + + +static const char *kSignature = "application/x-vnd.OpenBeOS-DiskProbe"; + + +class DiskProbe : public BApplication { + public: + DiskProbe(); + ~DiskProbe(); +}; + + +#define DUMPED_BLOCK_SIZE 16 + +void +dumpBlock(const uint8 *buffer, int size, const char *prefix) +{ + int i; + + for (i = 0; i < size;) { + int start = i; + + printf(prefix); + for (; i < start+DUMPED_BLOCK_SIZE; i++) { + if (!(i % 4)) + printf(" "); + + if (i >= size) + printf(" "); + else + printf("%02x", *(unsigned char *)(buffer + i)); + } + printf(" "); + + for (i = start; i < start + DUMPED_BLOCK_SIZE; i++) { + if (i < size) { + char c = buffer[i]; + + if (c < 30) + printf("."); + else + printf("%c", c); + } else + break; + } + printf("\n"); + } +} + + +status_t +printBlock(DataEditor &editor, const char *text) +{ + puts(text); + + const uint8 *buffer; + status_t status = editor.GetViewBuffer(&buffer); + if (status < B_OK) { + fprintf(stderr, "Could not get block: %s\n", strerror(status)); + return status; + } + + dumpBlock(buffer, editor.FileSize() > editor.ViewOffset() + editor.ViewSize() ? + editor.ViewSize() : editor.FileSize() - editor.ViewOffset(), + "\t"); + + return B_OK; +} + + +// #pragma mark - + + +DiskProbe::DiskProbe() + : BApplication(kSignature) +{ + DataEditor editor; + status_t status = editor.SetTo("/boot/beos/apps/DiskProbe"); + if (status < B_OK) { + fprintf(stderr, "Could not set editor: %s\n", strerror(status)); + return; + } + + editor.SetBlockSize(512); + editor.SetViewOffset(0); + editor.SetViewSize(32); + + BAutolock locker(editor.Locker()); + + if (printBlock(editor, "before (location 0):") < B_OK) + return; + + editor.Replace(5, (const uint8 *)"*REPLACED*", 10); + if (printBlock(editor, "after (location 0):") < B_OK) + return; + + editor.SetViewOffset(700); + if (printBlock(editor, "before (location 700):") < B_OK) + return; + + editor.Replace(713, (const uint8 *)"**REPLACED**", 12); + if (printBlock(editor, "after (location 700):") < B_OK) + return; + + status = editor.Undo(); + if (status < B_OK) + fprintf(stderr, "Could not undo: %s\n", strerror(status)); + if (printBlock(editor, "undo (location 700):") < B_OK) + return; + + editor.SetViewOffset(0); + if (printBlock(editor, "after (location 0):") < B_OK) + return; + + status = editor.Undo(); + if (status < B_OK) + fprintf(stderr, "Could not undo: %s\n", strerror(status)); + if (printBlock(editor, "undo (location 0):") < B_OK) + return; + + PostMessage(B_QUIT_REQUESTED); +} + + +DiskProbe::~DiskProbe() +{ +} + + +// #pragma mark - + + +int +main(int argc, char **argv) +{ + DiskProbe probe; + + probe.Run(); + return 0; +} diff --git a/src/apps/diskprobe/Jamfile b/src/apps/diskprobe/Jamfile new file mode 100644 index 0000000000..2f66b7d6cf --- /dev/null +++ b/src/apps/diskprobe/Jamfile @@ -0,0 +1,12 @@ +SubDir OBOS_TOP src apps diskprobe ; + +UsePrivateHeaders shared ; + +# AddResources DiskProbe : DiskProbe.rsrc ; + +App DiskProbe : + DiskProbe.cpp + DataEditor.cpp + ; + +LinkSharedOSLibs DiskProbe : be ;