diff --git a/dub.sdl b/dub.sdl
index f05ae16..9eeb5fc 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -5,7 +5,7 @@
license "MIT"
# NOTE: Somehow, the dmd package in the Alpine repo does not contain rdmd.
-preBuildCommands "dmd -run setup.d version" platform="dmd"
+preBuildCommands "rdmd setup.d version" platform="dmd"
preBuildCommands "ldmd2 -run setup.d version" platform="ldc"
preBuildCommands "gdmd -run setup.d version" platform="gdc"
@@ -18,9 +18,9 @@
dflags "--vgc" "--vtls" platform="ldc"
}
-buildType "trace" {
+configuration "trace" {
+ targetType "executable"
versions "Trace"
- buildOptions "debugMode" "debugInfo"
}
#
diff --git a/src/ddhx.d b/src/ddhx.d
index d4fbc10..de7c4ff 100644
--- a/src/ddhx.d
+++ b/src/ddhx.d
@@ -68,10 +68,7 @@
return errorPrint;
}
- initiate;
- screen.cursorOffset;
- screen.renderOffset;
- readRender;
+ refresh;
version (Trace) trace("loop");
TerminalInput event;
@@ -190,9 +187,8 @@
version (Trace) trace("%(%s %)", argv);
string command = argv[0];
- //TODO: Check length of command string?
- switch (command[0]) {
+ switch (command[0]) { // shortcuts
case '/': // Search
if (command.length <= 1)
return errorSet(ErrorCode.missingArgumentType);
@@ -207,109 +203,109 @@
return errorSet(ErrorCode.missingArgumentNeedle);
return ddhx.lookup(command[1..$], argv[1], false, true);
- default: // Regular
- switch (argv[0]) {
- case "g", "goto":
- if (argc <= 1)
- return errorSet(ErrorCode.missingArgumentPosition);
-
- switch (argv[1])
- {
- case "e", "end":
- moveEnd;
- break;
- case "h", "home":
- moveStart;
- break;
- default:
- seek(argv[1]);
- }
- return 0;
- case "skip":
- ubyte byte_ = void;
- if (argc <= 1) {
- size_t p =
- (editor.cursor.y * setting.width) +
- editor.cursor.x;
- byte_ = readdata[p];
- } else {
- if (argv[1] == "zero")
- byte_ = 0;
- else if (convertToVal(byte_, argv[1]))
- return error.ecode;
- }
- return skip(byte_);
- case "i", "info":
- printFileInfo;
- return 0;
- case "refresh":
- refresh;
- return 0;
- case "q", "quit":
- exit;
- return 0;
- case "about":
- enum C = "Written by dd86k. " ~ DDHX_COPYRIGHT;
- screenMessage(C);
- return 0;
- case "version":
- screenMessage(DDHX_ABOUT);
- return 0;
- //
- // Settings
- //
- case "w", "width":
- if (argc <= 1)
- return errorSet(ErrorCode.missingArgumentWidth);
-
- if (settingsWidth(argv[1]))
- return error.ecode;
-
- refresh;
- return 0;
- case "o", "offset":
- if (argc <= 1)
- return errorSet(ErrorCode.missingArgumentType);
-
- if (settingsOffset(argv[1]))
- return error.ecode;
-
- render;
- return 0;
- case "d", "data":
- if (argc <= 1)
- return errorSet(ErrorCode.missingArgumentType);
-
- if (settingsData(argv[1]))
- return error.ecode;
-
- render;
- return 0;
- case "C", "defaultchar":
- if (argc <= 1)
- return errorSet(ErrorCode.missingArgumentCharacter);
-
- if (settingsDefaultChar(argv[1]))
- return error.ecode;
-
- render;
- return 0;
- case "cp", "charset":
- if (argc <= 1)
- return errorSet(ErrorCode.missingArgumentCharset);
-
- if (settingsCharset(argv[1]))
- return error.ecode;
-
- render;
- return 0;
- case "reset":
- resetSettings();
- render;
- return 0;
+ default:
+ }
+
+ switch (argv[0]) { // regular commands
+ case "g", "goto":
+ if (argc <= 1)
+ return errorSet(ErrorCode.missingArgumentPosition);
+
+ switch (argv[1])
+ {
+ case "e", "end":
+ moveEnd;
+ break;
+ case "h", "home":
+ moveStart;
+ break;
default:
- return errorSet(ErrorCode.invalidCommand);
+ seek(argv[1]);
}
+ return 0;
+ case "skip":
+ ubyte byte_ = void;
+ if (argc <= 1) {
+ byte_ = readdata[editor.cursor.position];
+ } else {
+ if (argv[1] == "zero")
+ byte_ = 0;
+ else if (convertToVal(byte_, argv[1]))
+ return error.ecode;
+ }
+ return skip(byte_);
+ case "i", "info":
+ printFileInfo;
+ return 0;
+ case "refresh":
+ refresh;
+ return 0;
+ case "q", "quit":
+ exit;
+ return 0;
+ case "about":
+ enum C = "Written by dd86k. " ~ DDHX_COPYRIGHT;
+ screenMessage(C);
+ return 0;
+ case "version":
+ screenMessage(DDHX_ABOUT);
+ return 0;
+ //
+ // Settings
+ //
+ case "w", "width":
+ if (argc <= 1)
+ return errorSet(ErrorCode.missingArgumentWidth);
+
+ if (settingsWidth(argv[1]))
+ return error.ecode;
+
+ refresh;
+ return 0;
+ case "o", "offset":
+ if (argc <= 1)
+ return errorSet(ErrorCode.missingArgumentType);
+
+ if (settingsOffset(argv[1]))
+ return error.ecode;
+
+ screen.cursorOffset;
+ screen.renderOffset;
+ render;
+ return 0;
+ case "d", "data":
+ if (argc <= 1)
+ return errorSet(ErrorCode.missingArgumentType);
+
+ if (settingsData(argv[1]))
+ return error.ecode;
+
+ render;
+ return 0;
+ case "C", "defaultchar":
+ if (argc <= 1)
+ return errorSet(ErrorCode.missingArgumentCharacter);
+
+ if (settingsDefaultChar(argv[1]))
+ return error.ecode;
+
+ render;
+ return 0;
+ case "cp", "charset":
+ if (argc <= 1)
+ return errorSet(ErrorCode.missingArgumentCharset);
+
+ if (settingsCharset(argv[1]))
+ return error.ecode;
+
+ render;
+ return 0;
+ case "reset":
+ resetSettings();
+ render;
+ return 0;
+ default:
+ return errorSet(ErrorCode.invalidCommand);
}
}
@@ -325,79 +321,71 @@
/// Move the cursor to the start of the data
void moveStart() {
- editor.cursorFileStart;
- readRender;
+ if (editor.cursorFileStart)
+ readRender;
+ updateStatus;
+ updateCursor;
}
/// Move the cursor to the end of the data
void moveEnd() {
- editor.cursorFileEnd;
- readRender;
+ if (editor.cursorFileEnd)
+ readRender;
+ updateStatus;
+ updateCursor;
}
/// Align cursor to start of row
void moveAlignStart() {
- //seek(io.position - (io.position % setting.width));
editor.cursorHome;
- readRender;
+ updateStatus;
+ updateCursor;
}
/// Align cursor to end of row
void moveAlignEnd() {
- /*const long n = io.position +
- (setting.width - io.position % setting.width);
- seek(n + io.readSize <= io.size ? n : io.size - io.readSize);*/
editor.cursorEnd;
- readRender;
+ updateStatus;
+ updateCursor;
}
/// Move cursor to one data group to the left (backwards)
void moveLeft() {
- /*if (io.position - 1 >= 0) // Else already at 0
- seek(io.position - 1);*/
- editor.cursorLeft;
- readRender;
+ if (editor.cursorLeft)
+ readRender;
+ updateStatus;
+ updateCursor;
}
/// Move cursor to one data group to the right (forwards)
void moveRight() {
- /*if (io.position + io.readSize + 1 <= io.size)
- seek(io.position + 1);
- else
- seek(io.size - io.readSize);*/
- editor.cursorRight;
- readRender;
+ if (editor.cursorRight)
+ readRender;
+ updateStatus;
+ updateCursor;
}
/// Move cursor to one row size up (backwards)
void moveRowUp() {
- /*if (io.position - setting.width >= 0)
- seek(io.position - setting.width);
- else
- seek(0);*/
- editor.cursorUp;
- readRender;
+ if (editor.cursorUp)
+ readRender;
+ updateStatus;
+ updateCursor;
}
/// Move cursor to one row size down (forwards)
void moveRowDown() {
- /*if (io.position + io.readSize + setting.width <= io.size)
- seek(io.position + setting.width);
- else
- seek(io.size - io.readSize);*/
- editor.cursorDown;
- readRender;
+ if (editor.cursorDown)
+ readRender;
+ updateStatus;
+ updateCursor;
}
/// Move cursor to one page size up (backwards)
void movePageUp() {
- /*if (io.position - cast(long)io.readSize >= 0)
- seek(io.position - io.readSize);
- else
- seek(0);*/
- editor.cursorPageUp;
- readRender;
+ if (editor.cursorPageUp)
+ readRender;
+ updateStatus;
+ updateCursor;
}
/// Move view to one page size down (forwards)
void movePageDown() {
- /*if (io.position + (io.readSize << 1) <= io.size)
- seek(io.position + io.readSize);
- else
- seek(io.size - io.readSize);*/
- editor.cursorDown;
- readRender;
+ if (editor.cursorPageDown)
+ readRender;
+ updateStatus;
+ updateCursor;
}
/// Initiate screen buffer
@@ -415,7 +403,7 @@
// read at current position
int read() {
- version (Trace) trace("");
+ version (Trace) trace;
editor.seek(editor.position);
// if (editor.err)
@@ -426,13 +414,14 @@
return 0;
}
+//TODO: Consider render with multiple parameters to select what to render
+
/// Render screen (all elements)
void render() {
- version (Trace) trace("");
+ version (Trace) trace;
updateContent;
updateStatus;
- updateCursor;
}
void updateOffset() {
@@ -440,35 +429,34 @@
screen.renderOffset;
}
-void updateContent(bool cursor = true) {
+void updateContent() {
screen.cursorContent;
screen.renderContent(editor.position, readdata);
}
-void updateStatus(bool cursor = true) {
+void updateStatus() {
import std.format : format;
- long cpos = editor.position + editor.readSize;
+ long c = editor.cursorTell + 1;
screen.cursorStatusbar;
screen.renderStatusBar(
- editor.edits.modestr,
- //TODO: editor obviously should return current data type
- "hex", //numbers[setting.dataType].name,
+ editor.editModeString,
+ screen.name,
transcoder.name,
formatBin(editor.readSize, setting.si),
format("%s (%f%%)",
- formatBin(cpos, setting.si),
- ((cast(float)cpos) / editor.fileSize) * 100));
+ formatBin(c, setting.si),
+ ((cast(double)c) / editor.fileSize) * 100));
}
void updateCursor() {
version (Trace)
with (editor.cursor)
- trace("x=%u y=%u n=%u", x, y, nibble);
+ trace("pos=%u n=%u", position, nibble);
with (editor.cursor)
- screen.cursor(x, y, nibble);
+ screen.cursor(position, nibble);
}
void readRender() {
@@ -483,13 +471,14 @@
/// 4. Read buffer
/// 5. Render
void refresh() {
- version (Trace) trace("");
+ version (Trace) trace;
- screen.screenClear;
+ screen.clear;
initiate;
read;
updateOffset;
render;
+ updateCursor;
}
/// Seek to position in data, reads view's worth, and display that.
@@ -555,15 +544,15 @@
else if (pos < 0)
pos = 0;
- editor.cursorTo(pos);
+ editor.cursorJump(pos, true);
}
-private enum LAST_BUFFER_SIZE = 128;
-private __gshared ubyte[LAST_BUFFER_SIZE] lastItem;
-private __gshared size_t lastSize;
-private __gshared string lastType;
-private __gshared bool lastForward;
-private __gshared bool lastAvailable;
+enum LAST_BUFFER_SIZE = 128;
+__gshared ubyte[LAST_BUFFER_SIZE] lastItem;
+__gshared size_t lastSize;
+__gshared string lastType;
+__gshared bool lastForward;
+__gshared bool lastAvailable;
/// Search last item.
/// Returns: Error code if set.
diff --git a/src/dump.d b/src/dump.d
index 47f82b7..e6f4c85 100644
--- a/src/dump.d
+++ b/src/dump.d
@@ -7,7 +7,19 @@
import os.terminal;
import error, editor, screen, settings;
-/// Dump to stdout, akin to xxd(1).
+/*TODO: DumpOutput
+// Then add to function as parameter
+
+// With custom "byte" formatter
+// Default formatter has " " as prefix/suffix
+// HTML formatter will have "
" as prefix and " | " as suffix
+
+enum DumpOutput {
+ text,
+ html
+}*/
+
+/// Dump to stdout, akin to xxd(1) or hexdump(1).
/// Params:
/// skip = If set, number of bytes to skip.
/// length = If set, maximum length to read.
diff --git a/src/editor.d b/src/editor.d
index 998b7d6..fbe2f25 100644
--- a/src/editor.d
+++ b/src/editor.d
@@ -80,11 +80,23 @@
}*/
/// Editor editing mode.
-//TODO: "get string" with "ins","ovr","rdo"
enum EditMode : ushort {
- insert, /// Data will be inserted.
- overwrite, /// Data will be overwritten.
- readOnly, /// Editing data is disallowed by user or permission.
+ /// Incoming data will be inserted at cursor position.
+ /// Editing: Enabled
+ /// Cursor: Enabled
+ insert,
+ /// Incoming data will be overwritten at cursor position.
+ /// Editing: Enabled
+ /// Cursor: Enabled
+ overwrite,
+ /// The file cannot be edited.
+ /// Editing: Disabled
+ /// Cursor: Enabled
+ readOnly,
+ /// The file can only be viewed.
+ /// Editing: Disabled
+ /// Cursor: Disabled
+ view,
}
/// Represents a single edit
@@ -96,7 +108,7 @@
}
private union Source {
- OSFile2 osfile;
+ OSFile osfile;
OSMmFile mmfile;
File stream;
MemoryStream memory;
@@ -106,26 +118,39 @@
__gshared const(char)[] fileName; /// File base name.
__gshared FileMode fileMode; /// Current file mode.
__gshared long position; /// Last known set position.
-private __gshared ubyte[] readBuffer; /// For input input.
-__gshared size_t readSize; /// For input input.
+__gshared size_t readSize; /// For input size.
+private __gshared ubyte[] readBuffer; /// For input data.
+private __gshared uint vheight; ///
// Editing stuff
-private struct Editing {
- EditMode mode; /// Current editing mode
- const(char[]) modestr = "ins"; /// Current editing mode string
- SList!Edit history; /// Temporary file edits
- size_t count; /// Amount of edits in history
- size_t index; /// Current edit position
-}
-__gshared Editing edits;
+private __gshared size_t editIndex; /// Current edit position
+private __gshared size_t editCount; /// Amount of edits in history
+private __gshared SList!Edit editHistory; /// Temporary file edits
+__gshared EditMode editMode; /// Current editing mode
-struct cursor_t {
- int x; /// Data group column position
- int y; /// Data group row position
- int nibble; /// Data group nibble position
+string editModeString(EditMode mode = editMode) {
+ final switch (mode) with (EditMode) {
+ case insert: return "inse";
+ case overwrite: return "over";
+ case readOnly: return "read";
+ case view: return "view";
+ }
}
-__gshared cursor_t cursor;
+bool editModeReadOnly(EditMode mode) {
+ switch (mode) with (EditMode) {
+ case readOnly, view: return true;
+ default: return false;
+ }
+}
+
+private struct cursor_t {
+ //TODO: Transform into an 1D position system
+ // 2D is just clumsy...
+ uint position; /// Screen cursor byte position
+ uint nibble; /// Data group nibble position
+}
+__gshared cursor_t cursor; /// Cursor state
// View properties
@@ -151,7 +176,7 @@
}
bool dirty() {
- return edits.index == 0;
+ return editIndex == 0;
}
// SECTION: File opening
@@ -159,7 +184,7 @@
int openFile(string path) {
version (Trace) trace("path='%s'", path);
- if (source.osfile.open(path))
+ if (source.osfile.open(path, editModeReadOnly(editMode)))
return errorSet(ErrorCode.os);
fileMode = FileMode.file;
@@ -167,11 +192,11 @@
return 0;
}
-int openMmfile(string path/*, bool create*/) {
+int openMmfile(string path) {
version (Trace) trace("path='%s'", path);
try {
- source.mmfile = new OSMmFile(path);
+ source.mmfile = new OSMmFile(path, editModeReadOnly(editMode));
} catch (Exception ex) {
return errorSet(ex);
}
@@ -216,21 +241,15 @@
// SECTION: View position management
//
-void seek(long pos) {
+long seek(long pos) {
position = pos;
final switch (fileMode) with (FileMode) {
- case file:
- source.osfile.seek(Seek.start, pos);
- return;
- case mmfile:
- source.mmfile.seek(pos);
- return;
- case memory:
- source.memory.seek(pos);
- return;
+ case file: return source.osfile.seek(Seek.start, pos);
+ case mmfile: return source.mmfile.seek(pos);
+ case memory: return source.memory.seek(pos);
case stream:
source.stream.seek(pos);
- return;
+ return pos;
}
}
@@ -280,7 +299,7 @@
}
void keydown(Key key) {
- debug assert(edits.mode != EditMode.readOnly,
+ debug assert(editMode != EditMode.readOnly,
"Editor should not be getting edits in read-only mode");
//TODO: Check by panel (binary or text)
@@ -290,7 +309,7 @@
/// Append change at current position
void appendEdit(ubyte data) {
- debug assert(edits.mode != EditMode.readOnly,
+ debug assert(editMode != EditMode.readOnly,
"Editor should not be getting edits in read-only mode");
@@ -310,28 +329,28 @@
// SECTION View position management
//
-
-// Reserve bytes for file allocation
-// void reserve(long nsize) ?
-
-void moveStart() {
+bool viewStart() {
+ bool z = cursor.position > readSize;
position = 0;
+ return z;
}
-void moveEnd() {
+bool viewEnd() {
+ long old = position;
position = fileSize - readSize;
+ return position != old;
}
-void moveUp() {
- if (position - setting.width >= 0)
- position -= setting.width;
- else
- position = 0;
+bool viewUp() {
+ if (position - setting.width < 0)
+ return false;
+ position -= setting.width;
+ return true;
}
-void moveDown() {
+bool viewDown() {
long fsize = fileSize;
- if (position + readSize + setting.width <= fsize)
- seek(position + setting.width);
- else
- seek(fsize - readSize);
+ if (position + readSize > fsize)
+ return false;
+ position += setting.width;
+ return true;
}
// !SECTION
@@ -340,105 +359,123 @@
// SECTION Cursor position management
//
-long tellPosition() {
- return position + (cursor.y * setting.width) + cursor.x;
+void cursorBound() {
+ if (cursor.position < 0)
+ cursor.position = 0;
+ else if (cursor.position >= readSize) {
+ if (cursor.position - setting.width >= readSize)
+ cursor.position = cast(uint)(readSize - 1);
+ else
+ cursor.position -= setting.width;
+ }
}
-void cursorFileStart() {
- moveStart;
- with (cursor) x = y = nibble = 0;
+// These return true if view moves.
+
+bool cursorFileStart() {
+ viewStart;
+ with (cursor) position = nibble = 0;
+ return true;
}
-void cursorFileEnd() {
- moveEnd;
+bool cursorFileEnd() {
+ viewEnd;
with (cursor) {
- x = setting.width - 1;
- y = (cast(int)readSize / setting.width) - 1;
+ position = cast(uint)readSize - 1;
nibble = 0;
}
+ return true;
}
-
-void cursorHome() {
- cursor.x = cursor.nibble = 0;
-}
-void cursorEnd() {
- cursor.x = setting.width - 1;
+bool cursorHome() {
+ with (setting)
+ cursor.position = (cursor.position / width) * width;
cursor.nibble = 0;
+ return false;
}
-void cursorLeft() {
- if (cursor.x == 0) {
- if (cursor.y == 0)
- return;
-
- --cursor.y;
+bool cursorEnd() {
+ with (setting)
+ cursor.position = ((cursor.position / width) * width) + width - 1;
+ cursor.nibble = 0;
+ return false;
+}
+bool cursorLeft() {
+ if (cursor.position == 0) {
+ if (position == 0)
+ return false;
cursorEnd;
- return;
+ return viewUp;
}
- --cursor.x;
+ --cursor.position;
+ cursor.nibble = 0;
+ return false;
}
-void cursorRight() {
- if (cursor.x == setting.width - 1) {
- size_t r = (readSize / setting.width) - 1;
- if (cursor.y == r) {
- moveDown;
- cursorHome;
- return;
- }
-
- ++cursor.y;
+bool cursorRight() {
+ if (cursorTell >= fileSize - 1)
+ return false;
+
+ if (cursor.position == readSize - 1) {
cursorHome;
- return;
+ return viewDown;
}
- ++cursor.x;
+ ++cursor.position;
+ cursor.nibble = 0;
+ return false;
}
-void cursorUp() {
- if (cursor.y == 0) {
- moveUp;
- return;
+bool cursorUp() {
+ if (cursor.position < setting.width) {
+ return viewUp;
}
- --cursor.y;
+ cursor.position -= setting.width;
+ return false;
}
-void cursorDown() {
- size_t r = (readSize / setting.width) - 1;
+bool cursorDown() {
+ long fs = fileSize;
- version (Trace) trace("rsz=%u w=%u r=%u", readSize, setting.width, r);
+ // last line
+ bool last = cursor.position > readSize - setting.width;
+ // should move down
+ bool force = cursorTell + setting.width >= fs;
+ bool ok = last && force;
- if (cursor.y == r) {
- moveDown;
- return;
+ if (cursor.position + setting.width >= readSize) {
+ bool m = viewDown;
+ if (force)
+ goto L_FORCE_CURSOR;
+ return m;
}
- ++cursor.y;
+L_FORCE_CURSOR:
+ if (force) {
+ uint rem = cast(uint)(fs % setting.width);
+ cursor.position = cast(uint)(readSize - setting.width + rem) - 1;
+ return false;
+ }
+
+ cursor.position += setting.width;
+ return false;
}
-void cursorPageUp() {
+bool cursorPageUp() {
+
+ return false;
}
-void cursorPageDown() {
- size_t r = (readSize / setting.width) - 1;
+bool cursorPageDown() {
+ return false;
}
/// Get cursor absolute position
long cursorTell() {
- return position + cursorView;
+ return position + cursor.position;
}
-void cursorTo(long m) { // absolute
+void cursorJump(long m, bool absolute) {
// Per view chunks, then per y chunks, then x
//long npos =
}
-/// Get cursor relative position to view
-long cursorView() {
- return (cursor.y * setting.width) + cursor.x;
-}
-void cursorJump(long m) { // relative
-
- //long npos =
-
-}
// !SECTION
diff --git a/src/error.d b/src/error.d
index 9508041..4ecff12 100644
--- a/src/error.d
+++ b/src/error.d
@@ -169,6 +169,10 @@
void traceInit() {
log.open("ddhx.log", "w");
}
+ void trace(string func = __FUNCTION__, int line = __LINE__, A...)() {
+ log.writefln("TRACE:%s:%u", func, line);
+ log.flush;
+ }
void trace(string func = __FUNCTION__, int line = __LINE__, A...)(string fmt, A args) {
log.writef("TRACE:%s:%u: ", func, line);
log.writefln(fmt, args);
diff --git a/src/main.d b/src/main.d
index 180f1c7..e81cc2a 100644
--- a/src/main.d
+++ b/src/main.d
@@ -30,7 +30,10 @@
\_/
SECRET";
+immutable string OPT_INSERT = "insert";
+immutable string OPT_OVERWRITE = "overwrite";
immutable string OPT_READONLY = "R|readonly";
+immutable string OPT_VIEW = "view";
immutable string OPT_SI = "si";
immutable string OPT_WIDTH = "w|width"; //TODO: rename to c|columns
immutable string OPT_OFFSET = "o|offset";
@@ -46,16 +49,16 @@
}
void cliList(string opt) {
- writeln("Values available for option ",opt,":");
+ writeln("Available values for ",opt,":");
import std.traits : EnumMembers;
switch (opt) {
case OPT_OFFSET, OPT_DATA:
foreach (m; EnumMembers!NumberType)
- writeln(m);
+ writeln("\t=", m);
break;
case OPT_CHARSET:
foreach (m; EnumMembers!CharacterSet)
- writeln(m);
+ writeln("\t=", m);
break;
default:
}
@@ -64,8 +67,17 @@
void cliOption(string opt, string val) {
final switch (opt) {
+ case OPT_INSERT:
+ editor.editMode = EditMode.insert;
+ return;
+ case OPT_OVERWRITE:
+ editor.editMode = EditMode.overwrite;
+ return;
case OPT_READONLY:
- editor.edits.mode = EditMode.readOnly;
+ editor.editMode = EditMode.readOnly;
+ return;
+ case OPT_VIEW:
+ editor.editMode = EditMode.view;
return;
case OPT_WIDTH:
if (settingsWidth(val))
@@ -127,7 +139,10 @@
OPT_DATA, "Set data mode (decimal, hex, or octal)", &cliOption,
OPT_DEFAULTCHAR, "Set non-printable replacement character (default='.')", &cliOption,
OPT_CHARSET, "Set character translation (default=ascii)", &cliOption,
- OPT_READONLY, "Set file mode to read-only", &cliOption,
+ OPT_INSERT, "Open file in insert editing mode", &cliOption,
+ OPT_OVERWRITE, "Open file in overwrite editing mode", &cliOption,
+ OPT_READONLY, "Open file in read-only editing mode", &cliOption,
+ OPT_VIEW, "Open file in view editing mode", &cliOption,
OPT_SI, "Use SI suffixes instead of IEC", &setting.si,
"m|mmfile", "Open file as mmfile (memory-mapped)", &cliMmfile,
"f|file", "Force opening file as regular", &cliFile,
@@ -160,13 +175,17 @@
return 0;
}
- version (Trace) traceInit;
+ version (Trace) {
+ traceInit;
+ trace(DDHX_ABOUT);
+ }
string cliPath = args.length > 1 ? args[1] : "-";
if (cliStdin == false) cliStdin = args.length <= 1;
// Open file
+ //TODO: Open memory
long skip, length;
if (cliStdin) {
if (editor.openStream(stdin))
diff --git a/src/os/file.d b/src/os/file.d
index 9134d6d..8637f0b 100644
--- a/src/os/file.d
+++ b/src/os/file.d
@@ -85,7 +85,7 @@
// useful when writing all changes to file
// Win32: Seek + SetEndOfFile
// others: ftruncate
-struct OSFile2 {
+struct OSFile {
private OSHANDLE handle;
bool eof, err;
@@ -100,7 +100,7 @@
// By default, at least on Windows, files aren't shared. Enabling
// sharing would allow refreshing view (manually) when a program
// writes to file.
- bool open(string path) {
+ bool open(string path, bool readOnly) {
version (Windows) {
// NOTE: toUTF16z/tempCStringW
// Phobos internally uses tempCStringW from std.internal
@@ -108,7 +108,9 @@
// Legacy baggage?
handle = CreateFileW(
path.toUTF16z, // lpFileName
- GENERIC_READ | GENERIC_WRITE, // dwDesiredAccess
+ readOnly ? // dwDesiredAccess
+ GENERIC_READ :
+ GENERIC_READ | GENERIC_WRITE,
0, // dwShareMode
null, // lpSecurityAttributes
OPEN_EXISTING, // dwCreationDisposition
@@ -117,7 +119,7 @@
);
return err = handle == INVALID_HANDLE_VALUE;
} else version (Posix) {
- handle = .open(path.toStringz, O_RDWR);
+ handle = .open(path.toStringz, readOnly ? O_RDONLY : O_RDWR);
return err = handle == -1;
}
}
diff --git a/src/os/mmfile.d b/src/os/mmfile.d
index f563a37..1aee212 100644
--- a/src/os/mmfile.d
+++ b/src/os/mmfile.d
@@ -11,13 +11,18 @@
// temp
public class OSMmFile : MmFile {
private void *address;
- this(string path) {
- super(path, MmFile.Mode.read, 0, address);
+ this(string path, bool readOnly) {
+ super(path,
+ readOnly ?
+ MmFile.Mode.read :
+ MmFile.Mode.readWriteNew,
+ 0,
+ address);
}
bool eof, err;
private long position;
- void seek(long pos) { // only do seek_set for now
- position = pos;
+ long seek(long pos) { // only do seek_set for now
+ return position = pos;
}
long tell() {
return position;
diff --git a/src/os/terminal.d b/src/os/terminal.d
index 99774eb..b49d095 100644
--- a/src/os/terminal.d
+++ b/src/os/terminal.d
@@ -20,7 +20,8 @@
///
private extern (C) int putchar(int);
-private import std.stdio : printf, stdin, stdout, _IONBF;
+private import std.stdio : _IONBF, _IOLBF, _IOFBF,
+ printf, stdin, stdout;
private import core.stdc.stdlib : system, atexit;
version (Windows) {
@@ -34,14 +35,25 @@
private __gshared HANDLE hIn, hOut;
private __gshared USHORT defaultColor = DEFAULT_COLOR;
private __gshared DWORD oldCP;
-}
-version (Posix) {
+} else version (Posix) {
private import core.sys.posix.sys.stat;
private import core.sys.posix.sys.ioctl;
private import core.sys.posix.unistd;
private import core.sys.posix.termios;
- version (CRuntime_Musl) {
+ // Bionic depends on the Linux system it's compiled on.
+ // But Glibc and Musl have the same settings, so does Bionic.
+ // ...And uClibc, at least on Linux.
+ // D are missing the following bindings.
+ version (CRuntime_Musl)
+ version = IncludeTermiosLinux;
+ version (CRuntime_Bionic)
+ version = IncludeTermiosLinux;
+ version (CRuntime_UClibc)
+ version = IncludeTermiosLinux;
+
+ version (IncludeTermiosLinux) {
+ // termios.h, bits/termios.h
private alias uint tcflag_t;
private alias uint speed_t;
private alias char cc_t;
@@ -49,7 +61,6 @@
private enum NCCS = 32;
private enum ICANON = 2;
private enum ECHO = 10;
- private enum TIOCGWINSZ = 0x5413;
private enum BRKINT = 2;
private enum INPCK = 20;
private enum ISTRIP = 40;
@@ -68,14 +79,16 @@
speed_t __c_ispeed;
speed_t __c_ospeed;
}
+ private extern (C) int tcgetattr(int fd, termios *termios_p);
+ private extern (C) int tcsetattr(int fd, int a, termios *termios_p);
+ // ioctl.h
+ private enum TIOCGWINSZ = 0x5413;
private struct winsize {
ushort ws_row;
ushort ws_col;
ushort ws_xpixel;
ushort ws_ypixel;
}
- private extern (C) int tcgetattr(int fd, termios *termios_p);
- private extern (C) int tcsetattr(int fd, int a, termios *termios_p);
private extern (C) int ioctl(int fd, ulong request, ...);
}
@@ -145,6 +158,7 @@
if (hOut == INVALID_HANDLE_VALUE)
throw new WindowsException(GetLastError);
+ stdout.flush;
stdout.windowsHandleOpen(hOut, "wb"); // fixes using write functions
SetStdHandle(STD_OUTPUT_HANDLE, hOut);
@@ -301,6 +315,28 @@
SetConsoleCursorPosition(hOut, c);
} else version (Posix) { // 1-based, so 0,0 needs to be output as 1,1
printf("\033[%d;%dH", ++y, ++x);
+ stdout.flush;
+ }
+}
+
+/// Directly write to output.
+/// Params:
+/// data = Character data.
+/// size = Amount in bytes.
+/// Returns: Number of bytes written.
+size_t terminalOutput(const(void) *data, size_t size) {
+ version (Windows) {
+ import core.sys.windows.winbase : STD_OUTPUT_HANDLE,
+ WriteFile, GetStdHandle;
+ uint r = void;
+ assert(WriteFile(hOut, data, cast(uint)size, &r, null));
+ return r;
+ } else version (Posix) {
+ import core.sys.posix.unistd : write, STDOUT_FILENO;
+ import core.sys.posix.sys.types : ssize_t;
+ ssize_t r = write(STDOUT_FILENO, data, size);
+ assert(r >= 0);
+ return r;
}
}
diff --git a/src/screen.d b/src/screen.d
index 898a1bf..67213a8 100644
--- a/src/screen.d
+++ b/src/screen.d
@@ -8,9 +8,10 @@
import std.stdio : stdout; // for cwrite family
import ddhx; // for setting, NumberType
import os.terminal, os.file;
+version (Trace) import std.datetime.stopwatch;
//TODO: Data grouping (1, 2, 4, 8, 16)
-// e.g., cd ab -> abcd
+// e.g., cd ab -> abcd, 128 64 -> 192
// cast(uint[]) is probably possible on a ubyte[] range
//TODO: Group endianness (when >1)
// native (default), little, big
@@ -29,8 +30,16 @@
// Rendering engine should be capable to take off whereever it stopped
// or be able to specify/toggle seperate regardless of column length.
// Probably useful for dump app.
+//TODO: Consider buffer strategy full for terminal-altscreen mode
+// + manual flushes here
+/// Last known terminal size.
__gshared TerminalSize termSize;
+// Internal buffer filling character.
+// For dump, that should be spaces and 0,
+// For interactive, that should be spaces and spaces.
+//__gshared char binaryFiller;
+//__gshared char textFiller;
void initiate() {
terminalInit(TermFeat.all);
@@ -43,23 +52,20 @@
//string screenPrompt(string prompt)
/// Update cursor position on the terminal screen
-void cursor(int x, int y, int nibble) {
+void cursor(uint pos, uint nibble) {
//TODO: (x * 3) -> x * datawidth
+ uint y = pos / setting.width;
+ uint x = pos % setting.width;
terminalPos(13 + (x * 3) + nibble, 1 + y);
}
-// Called after an editing action (undo/redo/insert/overwrite)
-// Not sure...
-void screenUpdateDirty() {
- //TODO: (w * 3) -> w * datawidth
- int x = 11 + (setting.width * 3) + 2;
- terminalPos(x, 0);
- cwrite(editor.dirty ? '*' : ' ');
+/// Clear entire terminal screen
+void clear() {
+ terminalClear;
}
-/// Clear entire terminal screen
-void screenClear() {
- terminalClear;
+string name() {
+ return dataFmt.name;
}
/*void clearStatusBar() {
@@ -83,7 +89,7 @@
}
private struct NumberFormatter {
- immutable(char)[] name; /// Short offset name
+ string name; /// Short offset name
align(2) char fmtchar; /// Format character for printf-like functions
uint size; /// Size for formatted byte
size_t function(char*,long) offset; /// Function to format offset
@@ -315,6 +321,10 @@
import std.typecons : scoped;
import std.conv : octal;
+ version (Trace) {
+ StopWatch sw = StopWatch(AutoStart.yes);
+ }
+
// Setup index formatting
//TODO: Consider SingleSpec or "maker" function
int dsz = numbers[setting.dataType].size;
@@ -338,8 +348,19 @@
outbuf.put(' ');
}
+ version (Trace) {
+ Duration a = sw.peek;
+ }
+
// OutBuffer.toString duplicates it, what a waste!
cwriteln(cast(const(char)[])outbuf.toBytes);
+
+ version (Trace) {
+ Duration b = sw.peek;
+ trace("gen='%s µs' print='%s µs'",
+ a.total!"usecs",
+ (b - a).total!"usecs");
+ }
}
///
@@ -347,29 +368,50 @@
import std.outbuffer : OutBuffer;
import std.typecons : scoped;
+ version (Trace) {
+ StopWatch sw = StopWatch(AutoStart.yes);
+ }
+
+ int w = termSize.width;
+
auto outbuf = scoped!OutBuffer();
- outbuf.reserve(termSize.width);
+ outbuf.reserve(w);
outbuf.put(' ');
foreach (item; items) {
if (outbuf.offset > 1) outbuf.put(" | ");
outbuf.put(item);
+ if (outbuf.offset >= w) {
+
+ }
}
// Fill rest by space
- outbuf.data[outbuf.offset..termSize.width] = ' ';
- outbuf.offset = termSize.width; // used in .toBytes
+ outbuf.data[outbuf.offset..w] = ' ';
+ outbuf.offset = w; // used in .toBytes
+ version (Trace) {
+ Duration a = sw.peek;
+ }
+
+L_WRITE:
cwrite(cast(const(char)[])outbuf.toBytes);
+
+ version (Trace) {
+ sw.stop;
+ Duration b = sw.peek;
+ trace("gen='%s µs' print='%s µs'",
+ a.total!"usecs",
+ (b - a).total!"usecs");
+ }
}
-//TODO: [0.5] Possibility to only redraw a specific byte.
-// renderContentByte(size_t bufpos, ubyte newData)
-
/// Update display from buffer.
/// Returns: Numbers of row written.
uint renderContent(long position, ubyte[] data) {
- version (Trace)
- trace("position=%u data.len=%u cursor=%s",
- position, data.length, cursor);
+ version (Trace) {
+ trace("position=%u data.len=%u",
+ position, data.length);
+ StopWatch swtotal = StopWatch(AutoStart.yes);
+ }
// Setup formatting related stuff
prepareView;
@@ -382,6 +424,11 @@
++lines;
}
+ version (Trace) {
+ swtotal.stop;
+ trace("totaltime='%s µs'", swtotal.peek.total!"usecs");
+ }
+
return lines;
}
@@ -400,6 +447,8 @@
private char[] renderRow(ubyte[] chunk, long pos) {
import core.stdc.string : memset;
+ //TODO: Consider realloc on terminal width
+ // In screen.initiate
enum BUFFER_SIZE = 2048;
__gshared char[BUFFER_SIZE] buffer;
__gshared char *bufferptr = buffer.ptr;
@@ -410,6 +459,7 @@
const uint dataLen = (setting.width * (dataFmt.size + 1)); /// data row character count
size_t indexChar = indexData + dataLen; // Position for character column
+
*(cast(ushort*)(bufferptr + indexChar)) = 0x2020; // DATA-CHAR spacer
indexChar += 2; // indexChar: indexData + dataLen + spacer
@@ -417,8 +467,7 @@
// NOTE: Smaller loops could fit in cache...
// And would separate data/text logic
foreach (data; chunk) {
-// for (size_t i; i < chunk.length; ++i) {
-// const ubyte data = chunk[i]; /// byte data
+ //TODO: Maybe binary data formatter should include space
// Data translation
bufferptr[indexData++] = ' ';
indexData += dataFmt.data(bufferptr + indexData, data);
@@ -430,15 +479,22 @@
} else // Invalid character, insert default character
bufferptr[indexChar++] = setting.defaultChar;
}
- // data length < minimum row requirement = pad DATA
+
+ size_t end = indexChar;
+
+ // data length < minimum row requirement = in-fill data and text columns
if (chunk.length < setting.width) {
- size_t left =
- (setting.width - chunk.length) // Bytes left
- * (dataFmt.size + 1); // space + 1x data size
- memset(bufferptr + indexData, ' ', left);
+ // In-fill characters: left = Columns - ChunkLength
+ size_t leftchar = (setting.width - chunk.length); // Bytes left
+ memset(bufferptr + indexChar, ' ', leftchar);
+ // In-fill binary data: left = CharactersLeft * (DataSize + 1)
+ size_t leftdata = leftchar * (dataFmt.size + 1);
+ memset(bufferptr + indexData, ' ', leftdata);
+
+ end += leftchar;
}
- return buffer[0..indexChar];
+ return buffer[0..end];
}
//TODO: More renderRow unittests
@@ -447,16 +503,33 @@
// With defaults
prepareView;
// Offset(hex) 0 1 2 3 4 5 6 7 8 9 a b c d e f
- assert(renderRow([ 0 ], 0) ==
- " 0 00 .");
- assert(renderRow([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf ], 0x10) ==
- " 10 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f ................");
+ assert(renderRow([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf ], 0) ==
+ " 0 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f ................");
+ assert(renderRow([ 0 ], 0x10) ==
+ " 10 00 . ");
}
// !SECTION
// SECTION Console Write functions
+size_t cwrite(char c) {
+ return terminalOutput(&c, 1);
+}
+size_t cwrite(const(char)[] _str) {
+ return terminalOutput(_str.ptr, _str.length);
+}
+size_t cwriteln(const(char)[] _str) {
+ return cwrite(_str) + cwrite('\n');
+}
+size_t cwritef(A...)(const(char)[] fmt, A args) {
+ import std.format : sformat;
+ char[128] buf = void;
+ return cwrite(sformat(buf, fmt, args));
+}
+size_t cwritefln(A...)(const(char)[] fmt, A args) {
+ return cwritef(fmt, args) + cwrite('\n');
+}
size_t cwriteAt(int x, int y, char c) {
terminalPos(x, y);
return cwrite(c);
@@ -477,32 +550,5 @@
terminalPos(x, y);
return cwritefln(fmt, args);
}
-size_t cwrite(char c) {
- import std.stdio : stdout;
- import core.stdc.stdio : fwrite, FILE;
-
- return fwrite(&c, 1, 1, stdout.getFP);
-}
-size_t cwrite(const(char)[] _str) {
- import std.stdio : stdout;
- import core.stdc.stdio : fwrite, FILE;
-
- return fwrite(_str.ptr, 1, _str.length, stdout.getFP);
-}
-size_t cwriteln(const(char)[] _str) {
- size_t c = cwrite(_str);
- cwrite("\n");
- return ++c;
-}
-size_t cwritef(A...)(const(char)[] fmt, A args) {
- import std.format : sformat;
- char[128] buf = void;
- return cwrite(sformat(buf, fmt, args));
-}
-size_t cwritefln(A...)(const(char)[] fmt, A args) {
- size_t c = cwritef(fmt, args);
- cwrite("\n");
- return ++c;
-}
// !SECTION
\ No newline at end of file
diff --git a/src/utils/memory.d b/src/utils/memory.d
index 020b2b6..52b3591 100644
--- a/src/utils/memory.d
+++ b/src/utils/memory.d
@@ -35,10 +35,10 @@
}
- void seek(long pos) {
+ long seek(long pos) {
/*final switch (origin) with (Seek) {
case start:*/
- position = pos;
+ return position = pos;
/* return 0;
case current:
position += pos;