Skip to content

Commit

Permalink
shell: Add colors to warning and error output (stderr)
Browse files Browse the repository at this point in the history
  • Loading branch information
dd86k committed Sep 24, 2024
1 parent bdca1ba commit 9090792
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 62 deletions.
95 changes: 57 additions & 38 deletions debugger/shell.d
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
/// License: BSD-3-Clause-Clear
module shell;

// TODO: Pre-load/Exit event handlers (e.g., closing disassembler) functions

import adbg;
import adbg.machines;
import adbg.process;
Expand All @@ -21,6 +23,10 @@ import term;

extern (C):

enum { // Shell options
SHELL_NOCOLORS = 1, /// Disable colors for logger
}

///
__gshared int opt_pid;
///
Expand Down Expand Up @@ -102,22 +108,18 @@ int shell_start(int argc, const(char)** argv) {
return 2;
}

// Start process if specified
if (argc > 0 && argv) {
// Assume argv is null-terminated as it is on msvcrt and glibc
if (shell_proc_spawn(*argv, argc > 1 ? argv + 1 : null)) {
printf("Error: %s\n", adbg_error_message());
return 1;
}
} else if (opt_pid) { // Or attach to process if specified
if (shell_proc_attach(opt_pid)) {
printf("Error: %s\n", adbg_error_message());
return 1;
}
}

coninit();

// TODO: Use commands directly?
// Start or attach to process if specified
if (argc > 0 && argv && shell_spawn(*argv, argc > 1 ? argv + 1 : null)) {
logerror("Could not spawn process: %s", adbg_error_message());
return 1;
} else if (opt_pid && shell_attach(opt_pid)) {
logerror("Could not attach to process: %s", adbg_error_message());
return 1;
}

int ecode = void;
Lcommand:
printf("(adbg) ");
Expand All @@ -127,8 +129,8 @@ Lcommand:
// also make its pointer null.
char* line = conrdln().ptr;

// Invalid line or
if (line == null || line[0] == 4) // 4 == ^D
// Invalid line or CTRL+D
if (line == null || line[0] == 4)
return 0;

// External shell command
Expand Down Expand Up @@ -160,13 +162,16 @@ int shell_execv(int argc, const(char) **argv) {
immutable(command2_t) *command = shell_findcommand(ucommand);
if (command == null)
return ShellError.invalidCommand;
assert(command.entry);

assert(command.entry, "Command missing entry function");
return command.entry(argc, argv);
}

private:
__gshared:

int shellflags;

// NOTE: Process management
// Right now the shell is only capable of dealing with one process
adbg_process_t *process;
Expand All @@ -182,29 +187,45 @@ const(char)** last_spawn_argv;
FILE *logfd;
int loginit(const(char) *path) {
logfd = path ? fopen(path, "wb") : stderr;
if (path) setvbuf(logfd, null, _IONBF, 0);
if (path) {
shellflags |= SHELL_NOCOLORS; // Override choice
setvbuf(logfd, null, _IONBF, 0);
}
return logfd == null;
}
void logerror(const(char) *fmt, ...) {
va_list args = void;
va_start(args, fmt);
logwrite("error", fmt, args);

if ((shellflags & SHELL_NOCOLORS) == 0)
concoltext(TextColor.red, logfd);
fputs("error", logfd);
if ((shellflags & SHELL_NOCOLORS) == 0)
concolrst(logfd);
fputs(": ", logfd);

logwrite(fmt, args);
}
void logwarn(const(char) *fmt, ...) {
va_list args = void;
va_start(args, fmt);
logwrite("warning", fmt, args);

if ((shellflags & SHELL_NOCOLORS) == 0)
concoltext(TextColor.yellow, logfd);
fputs("warning", logfd);
if ((shellflags & SHELL_NOCOLORS) == 0)
concolrst(logfd);
fputs(": ", logfd);

logwrite(fmt, args);
}
void loginfo(const(char) *fmt, ...) {
va_list args = void;
va_start(args, fmt);
logwrite(null, fmt, args);

logwrite(fmt, args);
}
void logwrite(const(char) *level, const(char) *fmt, va_list args) {
if (level) {
fputs(level, logfd);
fputs(": ", logfd);
}
void logwrite(const(char) *fmt, va_list args) {
vfprintf(logfd, fmt, args);
putchar('\n');
}
Expand Down Expand Up @@ -509,19 +530,17 @@ immutable command2_t[] shell_commands = [
];

immutable(command2_t)* shell_findcommand(const(char) *ucommand) {
debug { // Crash command
static immutable(command2_t) ucommand_crash = {
debug static immutable(command2_t) ucommand_crash = {
[ "crash" ],
null,
[],
null, null,
[],
&command_crash
};

if (strcmp(ucommand, ucommand_crash.names[0].ptr) == 0)
debug if (strcmp(ucommand, ucommand_crash.names[0].ptr) == 0)
return &ucommand_crash;
} // debug
// NOTE: Can't use foreach for local var escape
for (size_t i; i < shell_commands.length; ++i) {
immutable(command2_t) *cmd = &shell_commands[i];
Expand All @@ -535,7 +554,7 @@ debug { // Crash command
return null;
}

int shell_proc_spawn(const(char) *exec, const(char) **argv) {
int shell_spawn(const(char) *exec, const(char) **argv) {
// Save for restart
last_spawn_exec = exec;
last_spawn_argv = argv;
Expand Down Expand Up @@ -563,7 +582,7 @@ int shell_proc_spawn(const(char) *exec, const(char) **argv) {
return 0;
}

int shell_proc_attach(int pid) {
int shell_attach(int pid) {
// Save for restart
opt_pid = pid;

Expand Down Expand Up @@ -787,15 +806,14 @@ int command_spawn(int argc, const(char) **argv) {
if (argc < 2)
return ShellError.missingArgument;

// Assume argv is null-terminated
return shell_proc_spawn(argv[1], argv + 2);
return shell_spawn(argv[1], argv + 2);
}

int command_attach(int argc, const(char) **argv) {
if (argc < 2)
return ShellError.invalidParameter;

return shell_proc_attach(atoi(argv[1]));
return shell_attach(atoi(argv[1]));
}

int command_detach(int argc, const(char) **argv) {
Expand All @@ -809,20 +827,21 @@ int command_detach(int argc, const(char) **argv) {
int command_restart(int argc, const(char) **argv) {
int e;

// TODO: Use commands directly?
switch (process.creation) with (AdbgCreation) {
case spawned:
// Terminate first, ignore on error (e.g., already gone)
adbg_debugger_terminate(process);

// Spawn, shell still messages status
e = shell_proc_spawn(last_spawn_exec, last_spawn_argv);
e = shell_spawn(last_spawn_exec, last_spawn_argv);
break;
case attached:
// Detach first, ignore on error (e.g., already detached)
adbg_debugger_detach(process);

// Attach, shell still messages status
e = shell_proc_attach(opt_pid);
e = shell_attach(opt_pid);
break;
default:
return ShellError.unattached;
Expand Down
111 changes: 87 additions & 24 deletions debugger/term.d
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/// In-house console/terminal library
/// Terminal utility.
///
/// Authors: dd86k <dd@dax.moe>
/// Copyright: © dd86k <dd@dax.moe>
Expand All @@ -8,8 +8,6 @@ module term;
import core.stdc.stdlib;
import adbg.include.c.stdio;

//TODO: Consider using PDCurses instead

// NOTE: Functions prefixed with "con" to avoid clashing with the "tc" POSIX stuff

extern (C):
Expand All @@ -21,7 +19,8 @@ version (Windows) {
private import core.sys.windows.windows;
private enum ALT_PRESSED = RIGHT_ALT_PRESSED | LEFT_ALT_PRESSED;
private enum CTRL_PRESSED = RIGHT_CTRL_PRESSED | LEFT_CTRL_PRESSED;
private __gshared HANDLE handleIn, handleOut, handleOld;
private __gshared HANDLE handleIn, handleOut, handleErr;
private __gshared ushort defaultColor;
} else version (Posix) {
private import core.sys.posix.sys.ioctl;
private import core.sys.posix.unistd;
Expand Down Expand Up @@ -93,31 +92,97 @@ version (Posix) {
} else {
handleOut = GetStdHandle(STD_OUTPUT_HANDLE);
handleIn = GetStdHandle(STD_INPUT_HANDLE);
handleErr = GetStdHandle(STD_ERROR_HANDLE);

// Disable annoying mouse mode.
DWORD mode = void;
GetConsoleMode(handleIn, &mode);
mode &= ~ENABLE_MOUSE_INPUT;
SetConsoleMode(handleIn, mode);

CONSOLE_SCREEN_BUFFER_INFO csbi = void;
GetConsoleScreenBufferInfo(handleOut, &csbi);
defaultColor = csbi.wAttributes;
}
return 0;
}

// Invert console color with defaultColor
/*void thcolin() {
version (Windows)
SetConsoleTextAttribute(hOut, COMMON_LVB_REVERSE_VIDEO | defaultColor);
version (Posix)
fputs("\033[7m", stdout);
/* windows:
FOREGROUND_BLUE
FOREGROUND_GREEN
FOREGROUND_RED
FOREGROUND_INTENSITY
BACKGROUND_BLUE
BACKGROUND_GREEN
BACKGROUND_RED
BACKGROUND_INTENSITY
*/

enum TextColor {
red,
yellow,
}

// Reset console color to defaultColor
void thcolrst() {
version (Windows)
SetConsoleTextAttribute(hOut, defaultColor);
version (Posix)
fputs("\033[0m", stdout);
}*/
// https://ss64.com/nt/syntax-ansi.html
// https://ss64.com/nt/color.html

version (Windows)
ushort concolor(TextColor color) {
final switch (color) {
case TextColor.red: return FOREGROUND_RED;
case TextColor.yellow: return FOREGROUND_GREEN | FOREGROUND_RED;
}
}

version (Windows)
HANDLE conhandle(FILE *stream) {
if (stream == stdout)
return handleOut;
else if (stream == stderr)
return handleErr;
assert(false, "no stream map");
}

version (Posix)
const(char)* concolor(TextColor color) {
final switch (color) {
case TextColor.red : return "\033[31m";
case TextColor.yellow: return "\033[33m";
}
}

// Set foreground text color for stdout
void concoltext(TextColor color, FILE *stream = stdout) {
version (Windows) {
ushort c = concolor(color);
HANDLE h = conhandle(stream);
SetConsoleTextAttribute(h, (defaultColor & 0xfff0) | c);
}
version (Posix) {
const(char) *c = concolor(color);
fputs(c, stream);
}
}

// Invert console color with default color
void concolinv(FILE *stream = stdout) {
version (Windows) {
HANDLE h = conhandle(stream);
SetConsoleTextAttribute(h, COMMON_LVB_REVERSE_VIDEO | defaultColor);
}
version (Posix)
fputs("\033[7m", stream);
}

// Reset console color to default color
void concolrst(FILE *stream = stdout) {
version (Windows) {
HANDLE h = conhandle(stream);
SetConsoleTextAttribute(h, defaultColor);
}
version (Posix)
fputs("\033[0m", stream);
}

/// Clear screen
void conclear() {
Expand Down Expand Up @@ -320,24 +385,22 @@ L_DEFAULT:
char[] conrdln() {
import core.stdc.ctype : isprint;

// GNU readline has this set to 512
enum BUFFERSIZE = 1024;
enum BUFFERSIZE = 512; // GNU readline has this set to 512

__gshared char* buffer;

if (buffer == null) {
if (buffer == null)
buffer = cast(char*)malloc(BUFFERSIZE);
if (buffer == null)
return null;
}
if (buffer == null)
return null;

// NOTE: stdin is line-buffered by the host console in their own buffer.
// Hitting return or enter makes the console write its buffer to stdin.
// Reading stdin, we copy until we see a newline.
size_t len;
while (len < BUFFERSIZE) {
int c = getchar();
if (c == '\n' || c == EOF)
if (c == '\n' || c == EOF || c == 0)
break;
buffer[len++] = cast(char)c;
}
Expand Down

0 comments on commit 9090792

Please sign in to comment.