diff --git a/.gitignore b/.gitignore index f28438d..7eda75b 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ *.exe dub.selections.json /ddhx +ddhx.log *.zip *.tar *.gz diff --git a/dub.sdl b/dub.sdl index f9b3051..559a4d8 100644 --- a/dub.sdl +++ b/dub.sdl @@ -6,4 +6,9 @@ buildType "verbose" { dflags "-vgc" "-vtls" +} + +buildType "trace" { + versions "Trace" + buildOptions "debugMode" "debugInfo" } \ No newline at end of file diff --git a/src/ddhx/ddhx.d b/src/ddhx/ddhx.d index c8b8118..dfe8729 100644 --- a/src/ddhx/ddhx.d +++ b/src/ddhx/ddhx.d @@ -55,7 +55,7 @@ string fileName; /// const(ubyte)[] buffer; /// // bool omitHeader; /// -// bool omitOffset; /// +// bool omitOffsetBar; /// // bool omitOffset; /// // Internals int termHeight; /// Last known terminal height @@ -66,18 +66,30 @@ __gshared Globals globals; /// Single-instance of globals. __gshared Input input; /// Input file/stream +int printError(int code = 1, A...)(string fmt, A args) { + stderr.write("error: "); + stderr.writefln(fmt, args); + return code; +} + int ddhxOpenFile(string path) { + version (Trace) trace("path=%s", path); + import std.path : baseName; globals.fileName = baseName(path); return input.openFile(path); } int ddhxOpenMmfile(string path) { + version (Trace) trace("path=%s", path); + import std.path : baseName; globals.fileName = baseName(path); return input.openMmfile(path); } int ddhxOpenStdin() { - globals.fileName = "--"; + version (Trace) trace("-"); + + globals.fileName = "-"; return input.openStdin(); } @@ -86,34 +98,42 @@ //TODO: Consider hiding terminal cursor //TODO: Consider changing the buffering strategy // e.g., flush+setvbuf/puts+flush - if (input.mode == InputMode.stdin) { - stderr.writeln("ddhx: Stdin not supported in interactive mode"); - return 2; - } if (skip < 0) { skip = +skip; } + if (input.mode == InputMode.stdin) { + version (Trace) trace("slurp +skip=%u", skip); + input.slurpStdin(skip); + } + input.position = skip; globals.fileSizeString = input.formatSize(); + version (Trace) trace("coninit"); coninit; + version (Trace) trace("conclear"); conclear; + version (Trace) trace("conheight"); globals.termHeight = conheight; ddhxPrepBuffer(true); globals.buffer = input.read(); + version (Trace) trace("buffer+read=%u", globals.buffer.length); ddhxRender(); - + InputInfo k; + version (Trace) trace("loop"); L_KEY: coninput(k); + version (Trace) trace("key=%d", k.value); + with (globals) switch (k.value) { - + // // Navigation // - + case Key.UpArrow, Key.K: if (input.position - rowWidth >= 0) ddhxSeek(input.position - rowWidth); @@ -187,29 +207,27 @@ break; case Key.Q: ddhxExit; break; default: + version (Trace) trace("unknown key=%u", k.value); } goto L_KEY; } /// int ddhxDump(long skip, long length) { - if (length < 0) { - stderr.writefln("dump: length cannot be negative"); - return 2; - } + if (length < 0) + return printError!2("length negative"); + + version (Trace) trace("skip=%d length=%d", skip, length); final switch (input.mode) { case InputMode.file, InputMode.mmfile: if (skip < 0) { skip = input.size + skip; } - if (skip + length > input.size) { - stderr.writefln("dump: length is overflowed"); - return 2; - } - if (length == 0) { + if (skip + length > input.size) + return printError!2("length overflow"); + if (length == 0) length = input.size - skip; - } if (skip) input.seek(skip); @@ -232,10 +250,8 @@ break; case InputMode.stdin: - if (skip < 0) { - stderr.writefln("dump: skip cannot be negative with stdin mode"); - return 2; - } + if (skip < 0) + return printError!2("skip value negative in stdin mode"); size_t l = void; if (skip) { @@ -263,8 +279,6 @@ return 0; } -//int ddhxPeek(string def, long skip, long length) - /// Refresh the entire screen void ddhxRefresh() { ddhxPrepBuffer(); @@ -291,6 +305,7 @@ /// void ddhxUpdateOffsetbarRaw() { + //TODO: Redo ddhxUpdateOffsetbarRaw /*enum OFFSET = "Offset "; __gshared char[512] line = "Offset "; size_t lineindex = OFFSET.sizeof; @@ -306,6 +321,7 @@ static char[8] fmt = " %02x"; fmt[4] = formatTable[globals.offset]; printf("Offset %c ", offsetTable[globals.offset]); + //TODO: Better rendering for large positions if (input.position > 0xffff_ffff) putchar(' '); for (ushort i; i < globals.rowWidth; ++i) printf(cast(char*)fmt, i); @@ -343,6 +359,8 @@ /// Determine input.bufferSize and buffer size void ddhxPrepBuffer(bool skipTerm = false) { + version (Trace) trace("skip=%s", skipTerm); + debug import std.conv : text; const int h = (skipTerm ? globals.termHeight : conheight) - 2; debug assert(h > 0); @@ -350,6 +368,7 @@ int newSize = h * globals.rowWidth; // Proposed buffer size if (newSize >= input.size) newSize = cast(uint)(input.size - input.position); + version (Trace) trace("newSize=%u", newSize); input.adjust(newSize); } @@ -360,6 +379,8 @@ * Params: pos = New position */ void ddhxSeek(long pos) { + version (Trace) trace("pos=%d", pos); + if (input.bufferSize < input.size) { input.seek(pos); globals.buffer = input.read(); @@ -374,6 +395,8 @@ * Params: str = String as a number */ void ddhxSeek(string str) { + version (Trace) trace("str=%s", str); + const char seekmode = str[0]; if (seekmode == '+' || seekmode == '-') { // relative input.position str = str[1..$]; @@ -411,6 +434,8 @@ * Params: pos = New position */ void ddhxSeekSafe(long pos) { + version (Trace) trace("pos=%s", pos); + if (pos + input.bufferSize > input.size) ddhxSeek(input.size - input.bufferSize); else diff --git a/src/ddhx/error.d b/src/ddhx/error.d index 426f5a8..47f3897 100644 --- a/src/ddhx/error.d +++ b/src/ddhx/error.d @@ -48,3 +48,18 @@ default: return "Unknown error occured."; } } + +version (Trace) { + import std.stdio; + + private __gshared File log; + + void traceInit() { + log.open("ddhx.log", "w"); + } + void trace(string func = __FUNCTION__, int line = __LINE__, A...)(string fmt, A args) { + log.writef("TRACE:%s:%u: ", func, line); + log.writefln(fmt, args); + log.flush; + } +} diff --git a/src/ddhx/input.d b/src/ddhx/input.d index 58c25bb..df6062e 100644 --- a/src/ddhx/input.d +++ b/src/ddhx/input.d @@ -1,8 +1,9 @@ module ddhx.input; -import std.stdio : File, stdin; +import std.stdio : File, stdin, FILE; import std.mmfile; import std.file : getSize; +import std.container : Array; import ddhx.error; import ddhx.utils : formatsize; @@ -23,12 +24,13 @@ union { File file; MmFile mmfile; + ubyte[] inBuffer; // or //Array!ubyte inBuffer; } union { ubyte[] fBuffer; ubyte *mmAddress; } - long position; /// buffer position + long position; /// file/buffer position uint bufferSize; /// buffer size int openFile(string path) { @@ -80,6 +82,7 @@ } } + /// Seek into input. void delegate(long) seek; private void seekFile(long pos) { @@ -89,6 +92,7 @@ position = pos; } + /// Read input. ubyte[] delegate() read; private ubyte[] readFile() { @@ -100,7 +104,14 @@ private ubyte[] readStdin() { return stdin.rawRead(fBuffer); } + private ubyte[] readStdin2() { + version (D_LP64) + return inBuffer[position..position+bufferSize]; + else + return inBuffer[cast(uint)position..cast(uint)position+bufferSize]; + } + /// Read into a buffer. ubyte[] delegate(ubyte[]) readBuffer; private ubyte[] readBufferFile(ubyte[] buffer) { @@ -112,9 +123,50 @@ private ubyte[] readBufferStdin(ubyte[] buffer) { return stdin.rawRead(buffer); } + private ubyte[] readBufferStdin2(ubyte[] buffer) { + version (D_LP64) + return inBuffer[position..position+buffer.length]; + else + return inBuffer[cast(uint)position..cast(uint)position+buffer.length]; + } + + void slurpStdin(long skip = 0, long length = 0) { + enum innerBufferSize = 512; + + seek = &seekMmfile; + read = &readStdin2; + readBuffer = &readBufferStdin2; + + size_t l = void; + ubyte[] buffer; + if (skip) { + if (skip > innerBufferSize) { + buffer = new ubyte[innerBufferSize]; + } else { + buffer = new ubyte[cast(uint)skip]; + } + do { + l = stdin.rawRead(buffer).length; + } while (l >= innerBufferSize); + } + + buffer = new ubyte[innerBufferSize]; + + import core.stdc.stdio : fread; + FILE *_stdin = stdin.getFP; + do { + //inBuffer ~= stdin.rawRead(buffer); + l = fread(buffer.ptr, 1, innerBufferSize, _stdin); + if (l == 0) break; + inBuffer ~= buffer[0..l]; + //} while (stdin.eof() == false); + } while (l); + + size = inBuffer.length; + } const(char)[] formatSize() { - __gshared char[32] b; - return mode == InputMode.stdin ? "--" : formatsize(b, size); + __gshared char[32] b = void; + return formatsize(b, size); } } \ No newline at end of file diff --git a/src/ddhx/terminal.d b/src/ddhx/terminal.d index fb188f2..5b48c45 100644 --- a/src/ddhx/terminal.d +++ b/src/ddhx/terminal.d @@ -3,14 +3,17 @@ */ module ddhx.terminal; +import ddhx.error; + /// private extern (C) int getchar(); -private import core.stdc.stdio : printf; +private import std.stdio; private import core.stdc.stdlib : system; version (Windows) { private import core.sys.windows.windows; + private import std.windows.syserror : WindowsException; private enum ALT_PRESSED = RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED; private enum CTRL_PRESSED = RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED; private enum DEFAULT_COLOR = @@ -19,6 +22,7 @@ private __gshared USHORT defaultColor = DEFAULT_COLOR; } 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; @@ -63,9 +67,20 @@ version (Windows) { hOut = GetStdHandle(STD_OUTPUT_HANDLE); hIn = GetStdHandle(STD_INPUT_HANDLE); - SetConsoleMode(hIn, ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT); + if (GetFileType(hIn) == FILE_TYPE_PIPE) { + version (Trace) trace("stdin is pipe"); + hIn = CreateFileA("CONIN$", GENERIC_READ, 0, null, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, null); + if (hIn == INVALID_HANDLE_VALUE) + throw new WindowsException(GetLastError); + stdin.windowsHandleOpen(hIn, "r"); + } + SetConsoleMode(hIn, ENABLE_EXTENDED_FLAGS | ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT); } version (Posix) { + stat_t s = void; + fstat(STDIN_FILENO, &s); + if (S_ISFIFO(s.st_mode)) + stdin.reopen("/dev/tty", "r"); tcgetattr(STDIN_FILENO, &old_tio); new_tio = old_tio; new_tio.c_lflag &= TERM_ATTR; @@ -152,10 +167,12 @@ version (Windows) { INPUT_RECORD ir = void; DWORD num = void; + char i = void; L_READ: - if (ReadConsoleInput(hIn, &ir, 1, &num) == 0) + if (ReadConsoleInputA(hIn, &ir, 1, &num) == 0) + throw new WindowsException(GetLastError); + if (num == 0) goto L_READ; - // Despite being bit fields, a switch is recommended switch (ir.EventType) { case KEY_EVENT: if (ir.KeyEvent.bKeyDown == FALSE) @@ -391,12 +408,10 @@ ubyte ctrl; /// If either CTRL was held down. ubyte alt; /// If either ALT was held down. ubyte shift; /// If SHIFT was held down. - } + } key_t key; struct mouse_t { ushort x, y; - } - key_t key; - mouse_t mouse; + } mouse_t mouse; } } diff --git a/src/main.d b/src/main.d index 0ee82de..25c85eb 100644 --- a/src/main.d +++ b/src/main.d @@ -12,19 +12,19 @@ void cliOptionWidth(string, string val) { if (optionWidth(val)) { - writeln("main: invalid 'width' value: ", val); + stderr.writeln("error: invalid 'width' value: ", val); exit(1); } } void cliOptionOffset(string, string val) { if (optionOffset(val)) { - writeln("main: invalid 'offset' value: ", val); + stderr.writeln("error: invalid 'offset' value: ", val); exit(1); } } void cliOptionDefaultChar(string, string val) { if (optionDefaultChar(val)) { - writeln("main: invalid 'defaultchar' value: ", val); + stderr.writeln("error: invalid 'defaultchar' value: ", val); exit(1); } } @@ -64,8 +64,7 @@ "ver", "Print only the version and exit", &cliVer ); } catch (Exception ex) { - stderr.writefln("main: %s", ex.msg); - return 1; + return printError(ex.msg); } if (res.helpWanted) { @@ -84,9 +83,10 @@ if (cliStdin == false) cliStdin = args.length <= 1; string cliInput = cliStdin ? "-" : args[1]; - + + version (Trace) traceInit; + long seek, length; - int e = void; if (cliStdin) { if (ddhxOpenStdin()) goto L_ERROR; @@ -99,23 +99,18 @@ } if (cliSeek) { - if (unformat(cliSeek, seek) == false) { - stderr.writeln("main: ", ddhxErrorMsg); - return 1; - } + if (unformat(cliSeek, seek) == false) + return printError(ddhxErrorMsg); } else seek = 0; if (cliDump) { if (cliLength) { - if (unformat(cliLength, length) == false) { - stderr.writeln("main: ", ddhxErrorMsg); - return 1; - } + if (unformat(cliLength, length) == false) + return printError(ddhxErrorMsg); } else length = 0; ddhxDump(seek, length); } else ddhxInteractive(seek); return 0; L_ERROR: - stderr.writeln("ddhx: ", ddhxErrorMsg); - return 2; + return printError!2(ddhxErrorMsg); } \ No newline at end of file