mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-03-03 07:29:23 +00:00
Add help dialog to Blinkenlights emulator
This commit is contained in:
parent
02b4a5c824
commit
5f1415af3a
9 changed files with 248 additions and 147 deletions
|
@ -1511,17 +1511,20 @@ long: push $GDT_LONG_DATA
|
|||
xor %ebp,%ebp
|
||||
mov $REAL_STACK_FRAME+FRAMESIZE,%esp
|
||||
call __map_image
|
||||
ezlea metal,ax
|
||||
ezlea metal.thunk,ax
|
||||
jmp *%rax
|
||||
.endfn long
|
||||
|
||||
// Long mode in virtual address space.
|
||||
// @noreturn
|
||||
metal:
|
||||
metal.thunk:
|
||||
#if USE_SYMBOL_HACK
|
||||
.byte 0x0f,0x1f,0207 # nop rdi binbase
|
||||
.long (IMAGE_BASE_VIRTUAL-IMAGE_BASE_REAL)/512
|
||||
#endif
|
||||
/ 𝑠𝑙𝑖𝑑𝑒
|
||||
.endfn metal.thunk
|
||||
metal:
|
||||
xor %eax,%eax # clear bss
|
||||
mov $ape_bss_vaddr,%edi
|
||||
mov $ape_bss_memsz,%ecx
|
||||
|
|
|
@ -16,8 +16,6 @@
|
|||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/bits/safemacros.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/x/x.h"
|
||||
|
||||
|
@ -37,26 +35,24 @@
|
|||
* @see gc()
|
||||
*/
|
||||
char *(xstrcat)(const char *s, ...) {
|
||||
char *p, b[2];
|
||||
va_list va;
|
||||
size_t i, n, n2, l;
|
||||
size_t n, m;
|
||||
char *p, b[2];
|
||||
p = NULL;
|
||||
i = n = 0;
|
||||
n = 0;
|
||||
va_start(va, s);
|
||||
do {
|
||||
if ((intptr_t)s > 0 && (intptr_t)s <= 255) {
|
||||
b[0] = (unsigned char)(intptr_t)s;
|
||||
b[1] = '\0';
|
||||
s = b;
|
||||
l = 1;
|
||||
m = 1;
|
||||
} else {
|
||||
l = strlen(s);
|
||||
m = strlen(s);
|
||||
}
|
||||
if ((n2 = i + l + 16) >= n) {
|
||||
p = xrealloc(p, (n = n2 + (n2 >> 1)));
|
||||
}
|
||||
memcpy(p + i, s, l + 1);
|
||||
i += l;
|
||||
p = xrealloc(p, n + m + 1);
|
||||
memcpy(p + n, s, m + 1);
|
||||
n += m;
|
||||
} while ((s = va_arg(va, const char *)));
|
||||
va_end(va);
|
||||
return p;
|
||||
|
|
|
@ -126,6 +126,27 @@ FEATURES\n\
|
|||
8086, 8087, i386, x86_64, SSE3, SSSE3, POPCNT, MDA, CGA, TTY\n\
|
||||
\n"
|
||||
|
||||
#define HELP \
|
||||
"\e[1mBLINKENLIGHTS v1.o\e[22m\
|
||||
https://justine.lol/blinkenlights/\n\
|
||||
\n\
|
||||
KEYBOARD SHORTCUTS CLI FLAGS\n\
|
||||
\n\
|
||||
ctrl-c interrupt -t tui mode\n\
|
||||
s step -r real mode\n\
|
||||
n next -s statistics\n\
|
||||
c continue -b ADDR push breakpoint\n\
|
||||
q quit -L PATH log file location\n\
|
||||
f finish -R reactive tui mode\n\
|
||||
R restart -H disable highlighting\n\
|
||||
x hex -v increase verbosity\n\
|
||||
? help -? help\n\
|
||||
t sse type\n\
|
||||
w sse width\n\
|
||||
B pop breakpoint\n\
|
||||
ctrl-t turbo\n\
|
||||
alt-t slowmo"
|
||||
|
||||
#define MAXZOOM 16
|
||||
#define DUMPWIDTH 64
|
||||
#define DISPWIDTH 80
|
||||
|
@ -255,17 +276,18 @@ static int64_t breakpointsstart;
|
|||
static uint64_t last_opcount;
|
||||
static char *codepath;
|
||||
static void *onbusted;
|
||||
static char *dialog;
|
||||
static char *statusmessage;
|
||||
static struct Pty *pty;
|
||||
static struct Machine *m;
|
||||
|
||||
static struct Panels pan;
|
||||
static struct Breakpoints breakpoints;
|
||||
static struct MemoryView codeview;
|
||||
static struct MemoryView readview;
|
||||
static struct MemoryView writeview;
|
||||
static struct MemoryView stackview;
|
||||
static struct MachineState laststate;
|
||||
static struct Breakpoints breakpoints;
|
||||
static struct MachineMemstat lastmemstat;
|
||||
static struct XmmType xmmtype;
|
||||
static struct Elf elf[1];
|
||||
|
@ -424,15 +446,24 @@ static void CopyMachineState(struct MachineState *ms) {
|
|||
memcpy(&ms->sse, &m->sse, sizeof(m->sse));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles file mapped page faults in valid page but past eof.
|
||||
*/
|
||||
static void OnSigBusted(void) {
|
||||
CHECK(onbusted);
|
||||
longjmp(onbusted, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if 𝑣 is a shadow memory virtual address.
|
||||
*/
|
||||
static bool IsShadow(int64_t v) {
|
||||
return 0x7fff8000 <= v && v < 0x100080000000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns glyph representing byte at virtual address 𝑣.
|
||||
*/
|
||||
static int VirtualBing(int64_t v) {
|
||||
int rc;
|
||||
uint8_t *p;
|
||||
|
@ -451,6 +482,9 @@ static int VirtualBing(int64_t v) {
|
|||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns ASAN shadow uint8 concomitant to address 𝑣 or -1.
|
||||
*/
|
||||
static int VirtualShadow(int64_t v) {
|
||||
int rc;
|
||||
char *p;
|
||||
|
@ -547,9 +581,7 @@ static void TuiRejuvinate(void) {
|
|||
|
||||
static void OnQ(void) {
|
||||
LOGF("OnQ");
|
||||
if (action & FAILURE) exit(1);
|
||||
action |= INT;
|
||||
breakpoints.i = 0;
|
||||
action |= EXIT;
|
||||
}
|
||||
|
||||
static void OnV(void) {
|
||||
|
@ -594,6 +626,7 @@ static void TuiCleanup(void) {
|
|||
TtyRestore1();
|
||||
DisableMouseTracking();
|
||||
tuimode = false;
|
||||
LeaveScreen();
|
||||
}
|
||||
|
||||
static void ResolveBreakpoints(void) {
|
||||
|
@ -603,8 +636,10 @@ static void ResolveBreakpoints(void) {
|
|||
if ((sym = DisFindSymByName(dis, breakpoints.p[i].symbol)) != -1) {
|
||||
breakpoints.p[i].addr = dis->syms.p[sym].addr;
|
||||
} else {
|
||||
fprintf(stderr, "error: breakpoint not found: %s\n",
|
||||
breakpoints.p[i].symbol);
|
||||
fprintf(
|
||||
stderr,
|
||||
"error: breakpoint not found: %s (out of %,ld loaded symbols)\n",
|
||||
breakpoints.p[i].symbol, dis->syms.i);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
@ -2155,12 +2190,9 @@ static void OnBinbase(struct Machine *m) {
|
|||
int64_t skew;
|
||||
skew = m->xedd->op.disp * 512;
|
||||
LOGF("skew binbase %,ld @ %p", skew, GetIp());
|
||||
for (i = 0; i < dis->syms.i; ++i) {
|
||||
dis->syms.p[i].addr += skew;
|
||||
}
|
||||
for (i = 0; i < dis->loads.i; ++i) {
|
||||
dis->loads.p[i].addr += skew;
|
||||
}
|
||||
for (i = 0; i < dis->syms.i; ++i) dis->syms.p[i].addr += skew;
|
||||
for (i = 0; i < dis->loads.i; ++i) dis->loads.p[i].addr += skew;
|
||||
for (i = 0; i < breakpoints.i; ++i) breakpoints.p[i].addr += skew;
|
||||
Disassemble();
|
||||
}
|
||||
|
||||
|
@ -2270,7 +2302,7 @@ static void OnFinish(void) {
|
|||
action &= ~CONTINUE;
|
||||
}
|
||||
|
||||
static void OnContinue(void) {
|
||||
static void OnContinueTui(void) {
|
||||
action ^= CONTINUE;
|
||||
action &= ~STEP;
|
||||
action &= ~NEXT;
|
||||
|
@ -2278,6 +2310,15 @@ static void OnContinue(void) {
|
|||
action &= ~FAILURE;
|
||||
}
|
||||
|
||||
static void OnContinueExec(void) {
|
||||
tuimode = false;
|
||||
action |= CONTINUE;
|
||||
action &= ~STEP;
|
||||
action &= ~NEXT;
|
||||
action &= ~FINISH;
|
||||
action &= ~FAILURE;
|
||||
}
|
||||
|
||||
static void OnQuit(void) {
|
||||
action |= QUIT;
|
||||
}
|
||||
|
@ -2394,41 +2435,41 @@ static void OnMouse(char *p) {
|
|||
y -= ep->top;
|
||||
x -= ep->left;
|
||||
switch (e) {
|
||||
case kMouseWheelUp:
|
||||
OnMouseWheelUp(ep, y, x);
|
||||
break;
|
||||
case kMouseWheelDown:
|
||||
OnMouseWheelDown(ep, y, x);
|
||||
break;
|
||||
case kMouseCtrlWheelUp:
|
||||
OnMouseCtrlWheelUp(ep, y, x);
|
||||
break;
|
||||
case kMouseCtrlWheelDown:
|
||||
OnMouseCtrlWheelDown(ep, y, x);
|
||||
break;
|
||||
CASE(kMouseWheelUp, OnMouseWheelUp(ep, y, x));
|
||||
CASE(kMouseWheelDown, OnMouseWheelDown(ep, y, x));
|
||||
CASE(kMouseCtrlWheelUp, OnMouseCtrlWheelUp(ep, y, x));
|
||||
CASE(kMouseCtrlWheelDown, OnMouseCtrlWheelDown(ep, y, x));
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void OnHelp(void) {
|
||||
DEBUGF("setting dialog");
|
||||
dialog = HELP;
|
||||
}
|
||||
|
||||
static void ReadKeyboard(void) {
|
||||
char buf[64], *p = buf;
|
||||
memset(buf, 0, sizeof(buf));
|
||||
dialog = NULL;
|
||||
if (readansi(ttyin, buf, sizeof(buf)) == -1) {
|
||||
if (errno == EINTR) {
|
||||
LOGF("readkeyboard interrupted");
|
||||
LOGF("ReadKeyboard interrupted");
|
||||
return;
|
||||
}
|
||||
FATALF("readkeyboard failed: %s", strerror(errno));
|
||||
FATALF("ReadKeyboard failed: %s", strerror(errno));
|
||||
}
|
||||
switch (*p++) {
|
||||
CASE('q', OnQ());
|
||||
CASE('v', OnV());
|
||||
CASE('?', OnHelp());
|
||||
CASE('s', OnStep());
|
||||
CASE('n', OnNext());
|
||||
CASE('f', OnFinish());
|
||||
CASE('c', OnContinue());
|
||||
CASE('c', OnContinueTui());
|
||||
CASE('C', OnContinueExec());
|
||||
CASE('R', OnRestart());
|
||||
CASE('x', OnXmmDisp());
|
||||
CASE('t', OnXmmType());
|
||||
|
@ -2451,57 +2492,26 @@ static void ReadKeyboard(void) {
|
|||
CASE(CTRL('T'), OnTurbo());
|
||||
case '\e':
|
||||
switch (*p++) {
|
||||
CASE('v', OnPageUp());
|
||||
CASE('t', OnSlowmo());
|
||||
CASE('v', OnPageUp()); /* alt+v */
|
||||
CASE('t', OnSlowmo()); /* alt+t */
|
||||
case 'O':
|
||||
switch (*p++) {
|
||||
CASE('P', OnHelp()); /* \eOP is F1 */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '[':
|
||||
switch (*p++) {
|
||||
CASE('<', OnMouse(p));
|
||||
CASE('A', OnUpArrow());
|
||||
CASE('B', OnDownArrow());
|
||||
CASE('F', OnEnd());
|
||||
CASE('H', OnHome());
|
||||
case '1':
|
||||
switch (*p++) {
|
||||
CASE('~', OnHome());
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '4':
|
||||
switch (*p++) {
|
||||
CASE('~', OnEnd());
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '5':
|
||||
switch (*p++) {
|
||||
CASE('~', OnPageUp());
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '6':
|
||||
switch (*p++) {
|
||||
CASE('~', OnPageDown());
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '7':
|
||||
switch (*p++) {
|
||||
CASE('~', OnHome());
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '8':
|
||||
switch (*p++) {
|
||||
CASE('~', OnEnd());
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
CASE('A', OnUpArrow()); /* \e[A is up */
|
||||
CASE('B', OnDownArrow()); /* \e[B is down */
|
||||
CASE('F', OnEnd()); /* \e[F is end */
|
||||
CASE('H', OnHome()); /* \e[H is home */
|
||||
CASE('1', OnHome()); /* \e[1~ is home */
|
||||
CASE('4', OnEnd()); /* \e[1~ is end */
|
||||
CASE('5', OnPageUp()); /* \e[1~ is pgup */
|
||||
CASE('6', OnPageDown()); /* \e[1~ is pgdn */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -2609,14 +2619,13 @@ static void Tui(void) {
|
|||
SetupDraw();
|
||||
ScrollOp(&pan.disassembly, GetDisIndex());
|
||||
if (!(interrupt = setjmp(m->onhalt))) {
|
||||
for (;;) {
|
||||
do {
|
||||
if (!(action & FAILURE)) {
|
||||
LoadInstruction(m);
|
||||
if ((action & (FINISH | NEXT | CONTINUE)) &&
|
||||
(bp = IsAtBreakpoint(&breakpoints, GetIp())) != -1) {
|
||||
action &= ~(FINISH | NEXT | CONTINUE);
|
||||
LOGF("BREAK %p", breakpoints.p[bp].addr);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
m->xedd = (struct XedDecodedInst *)m->icache[0];
|
||||
|
@ -2642,6 +2651,9 @@ static void Tui(void) {
|
|||
tick = 0;
|
||||
Redraw();
|
||||
}
|
||||
if (dialog) {
|
||||
PrintMessageBox(ttyout, dialog, tyn, txn);
|
||||
}
|
||||
if (action & FAILURE) {
|
||||
LOGF("TUI FAILURE");
|
||||
PrintMessageBox(ttyout, systemfailure, tyn, txn);
|
||||
|
@ -2651,7 +2663,8 @@ static void Tui(void) {
|
|||
LeaveScreen();
|
||||
exit(1);
|
||||
}
|
||||
} else if (!IsExecuting() || (!(action & CONTINUE) && !(action & INT) &&
|
||||
} else if (dialog || !IsExecuting() ||
|
||||
(!(action & CONTINUE) && !(action & INT) &&
|
||||
HasPendingKeyboard())) {
|
||||
ReadKeyboard();
|
||||
}
|
||||
|
@ -2661,13 +2674,11 @@ static void Tui(void) {
|
|||
if (action & (CONTINUE | NEXT | FINISH)) {
|
||||
action &= ~(CONTINUE | NEXT | FINISH);
|
||||
} else {
|
||||
tuimode = false;
|
||||
action |= CONTINUE;
|
||||
action |= EXIT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (action & EXIT) {
|
||||
LeaveScreen();
|
||||
LOGF("TUI EXIT");
|
||||
break;
|
||||
}
|
||||
|
@ -2723,7 +2734,7 @@ static void Tui(void) {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (tuimode);
|
||||
} else {
|
||||
if (OnHalt(interrupt)) {
|
||||
goto KeepGoing;
|
||||
|
@ -2794,15 +2805,15 @@ int Emulator(int argc, char *argv[]) {
|
|||
int rc, fd;
|
||||
codepath = argv[optind++];
|
||||
m->fds.p = xcalloc((m->fds.n = 8), sizeof(struct MachineFd));
|
||||
Restart:
|
||||
do {
|
||||
action = 0;
|
||||
LoadProgram(m, codepath, argv + optind, environ, elf);
|
||||
AddHostFd(STDIN_FILENO);
|
||||
AddHostFd(STDOUT_FILENO);
|
||||
AddHostFd(STDERR_FILENO);
|
||||
AddHostFd(0);
|
||||
AddHostFd(1);
|
||||
AddHostFd(2);
|
||||
if (tuimode) {
|
||||
ttyin = isatty(STDIN_FILENO) ? STDIN_FILENO : OpenDevTty();
|
||||
ttyout = isatty(STDOUT_FILENO) ? STDOUT_FILENO : OpenDevTty();
|
||||
ttyin = isatty(0) ? 0 : OpenDevTty();
|
||||
ttyout = isatty(1) ? 1 : OpenDevTty();
|
||||
} else {
|
||||
ttyin = -1;
|
||||
ttyout = -1;
|
||||
|
@ -2813,23 +2824,18 @@ Restart:
|
|||
tyn = 24;
|
||||
txn = 80;
|
||||
GetTtySize(ttyout);
|
||||
if (isatty(STDIN_FILENO)) m->fds.p[STDIN_FILENO].cb = &kMachineFdCbPty;
|
||||
if (isatty(STDOUT_FILENO)) m->fds.p[STDOUT_FILENO].cb = &kMachineFdCbPty;
|
||||
if (isatty(STDERR_FILENO)) m->fds.p[STDERR_FILENO].cb = &kMachineFdCbPty;
|
||||
if (isatty(0)) m->fds.p[0].cb = &kMachineFdCbPty;
|
||||
if (isatty(1)) m->fds.p[1].cb = &kMachineFdCbPty;
|
||||
if (isatty(2)) m->fds.p[2].cb = &kMachineFdCbPty;
|
||||
}
|
||||
while (!(action & EXIT)) {
|
||||
do {
|
||||
if (!tuimode) {
|
||||
Exec();
|
||||
} else {
|
||||
Tui();
|
||||
}
|
||||
if (action & RESTART) {
|
||||
goto Restart;
|
||||
}
|
||||
}
|
||||
if (tuimode) {
|
||||
LeaveScreen();
|
||||
}
|
||||
} while (!(action & (RESTART | EXIT)));
|
||||
} while (action & RESTART);
|
||||
if (printstats) {
|
||||
fprintf(stderr, "taken: %,ld\n", taken);
|
||||
fprintf(stderr, "ntaken: %,ld\n", ntaken);
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/elf/elf.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/runtime/gc.h"
|
||||
#include "libc/sysv/consts/map.h"
|
||||
|
@ -28,13 +29,15 @@
|
|||
|
||||
void LoadDebugSymbols(struct Elf *elf) {
|
||||
int fd;
|
||||
size_t n;
|
||||
void *elfmap;
|
||||
struct stat st;
|
||||
const char *path;
|
||||
if (elf->ehdr) return;
|
||||
if (elf->ehdr && GetElfSymbolTable(elf->ehdr, elf->size, &n) && n) return;
|
||||
DCHECK_NOTNULL(elf->prog);
|
||||
fprintf(stderr, "HI %s\n", elf->prog);
|
||||
if ((fd = open(gc(xstrcat(elf->prog, ".dbg")), O_RDONLY)) != -1 ||
|
||||
(fd = open(elf->prog, O_RDONLY))) {
|
||||
(fd = open(elf->prog, O_RDONLY)) != -1) {
|
||||
if (fstat(fd, &st) != -1 &&
|
||||
(elfmap = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) !=
|
||||
MAP_FAILED) {
|
||||
|
|
|
@ -203,5 +203,5 @@ void DisLoadElf(struct Dis *d, struct Elf *elf) {
|
|||
DisLoadElfLoads(d, elf);
|
||||
DisLoadElfSyms(d, elf);
|
||||
DisSortSyms(d);
|
||||
DisCanonizeSyms(d);
|
||||
/* DisCanonizeSyms(d); */
|
||||
}
|
||||
|
|
54
tool/build/lib/lines.c
Normal file
54
tool/build/lib/lines.c
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
|
||||
│vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│
|
||||
╞══════════════════════════════════════════════════════════════════════════════╡
|
||||
│ Copyright 2021 Justine Alexandra Roberts Tunney │
|
||||
│ │
|
||||
│ Permission to use, copy, modify, and/or distribute this software for │
|
||||
│ any purpose with or without fee is hereby granted, provided that the │
|
||||
│ above copyright notice and this permission notice appear in all copies. │
|
||||
│ │
|
||||
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL │
|
||||
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED │
|
||||
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE │
|
||||
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL │
|
||||
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR │
|
||||
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER │
|
||||
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
|
||||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "tool/build/lib/lines.h"
|
||||
|
||||
struct Lines *NewLines(void) {
|
||||
return calloc(1, sizeof(struct Lines));
|
||||
}
|
||||
|
||||
void FreeLines(struct Lines *lines) {
|
||||
size_t i;
|
||||
for (i = 0; i < lines->n; ++i) {
|
||||
free(lines->p[i]);
|
||||
}
|
||||
free(lines);
|
||||
}
|
||||
|
||||
void AppendLine(struct Lines *lines, const char *s, size_t n) {
|
||||
lines->p = realloc(lines->p, ++lines->n * sizeof(*lines->p));
|
||||
lines->p[lines->n - 1] = strndup(s, n);
|
||||
}
|
||||
|
||||
void AppendLines(struct Lines *lines, const char *s) {
|
||||
const char *p;
|
||||
for (;;) {
|
||||
p = strchr(s, '\n');
|
||||
if (p) {
|
||||
AppendLine(lines, s, p - s);
|
||||
s = p + 1;
|
||||
} else {
|
||||
if (*s) {
|
||||
AppendLine(lines, s, -1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
18
tool/build/lib/lines.h
Normal file
18
tool/build/lib/lines.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
#ifndef COSMOPOLITAN_TOOL_BUILD_LIB_LINES_H_
|
||||
#define COSMOPOLITAN_TOOL_BUILD_LIB_LINES_H_
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
struct Lines {
|
||||
size_t n;
|
||||
char **p;
|
||||
};
|
||||
|
||||
struct Lines *NewLines(void);
|
||||
void FreeLines(struct Lines *);
|
||||
void AppendLine(struct Lines *, const char *, size_t);
|
||||
void AppendLines(struct Lines *, const char *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_TOOL_BUILD_LIB_LINES_H_ */
|
|
@ -140,7 +140,7 @@ void LoadProgram(struct Machine *m, const char *prog, char **args, char **vars,
|
|||
size_t i, mappedsize;
|
||||
DCHECK_NOTNULL(prog);
|
||||
elf->prog = prog;
|
||||
if ((fd = open(prog, O_RDWR)) == -1 ||
|
||||
if ((fd = open(prog, O_RDONLY)) == -1 ||
|
||||
(fstat(fd, &st) == -1 || !st.st_size)) {
|
||||
fputs(prog, stderr);
|
||||
fputs(": not found\n", stderr);
|
||||
|
|
|
@ -17,30 +17,51 @@
|
|||
│ PERFORMANCE OF THIS SOFTWARE. │
|
||||
╚─────────────────────────────────────────────────────────────────────────────*/
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/macros.h"
|
||||
#include "libc/math.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/unicode/unicode.h"
|
||||
#include "tool/build/lib/buffer.h"
|
||||
#include "tool/build/lib/lines.h"
|
||||
#include "tool/build/lib/panel.h"
|
||||
|
||||
static int GetWidthOfLongestLine(struct Lines *lines) {
|
||||
int i, w, m;
|
||||
for (m = i = 0; i < lines->n; ++i) {
|
||||
w = strwidth(lines->p[i], 0);
|
||||
m = MAX(m, w);
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
void PrintMessageBox(int fd, const char *msg, long tyn, long txn) {
|
||||
char buf[1];
|
||||
struct Buffer b;
|
||||
int i, w, h, x, y;
|
||||
h = 2 + 1 + 2;
|
||||
w = 3 + strlen(msg) + 3;
|
||||
struct Lines *lines;
|
||||
lines = NewLines();
|
||||
AppendLines(lines, msg);
|
||||
h = 3 + lines->n + 3;
|
||||
w = 4 + GetWidthOfLongestLine(lines) + 4;
|
||||
x = lrint(txn / 2. - w / 2.);
|
||||
y = lrint(tyn / 2. - h / 2.);
|
||||
memset(&b, 0, sizeof(b));
|
||||
AppendFmt(&b, "\e[%d;%dH", y++, x);
|
||||
for (i = 0; i < w; ++i) AppendStr(&b, " ");
|
||||
AppendFmt(&b, "\e[%d;%dH ╔", y++, x);
|
||||
for (i = 0; i < w - 2; ++i) AppendStr(&b, "═");
|
||||
for (i = 0; i < w - 4; ++i) AppendStr(&b, "═");
|
||||
AppendStr(&b, "╗ ");
|
||||
AppendFmt(&b, "\e[%d;%dH║ %-*s ║", y++, x, w - 6, "");
|
||||
AppendFmt(&b, "\e[%d;%dH║ %-*s ║", y++, x, w - 6, msg);
|
||||
AppendFmt(&b, "\e[%d;%dH║ %-*s ║", y++, x, w - 6, "");
|
||||
AppendFmt(&b, "\e[%d;%dH ║ %-*s ║ ", y++, x, w - 8, "");
|
||||
for (i = 0; i < lines->n; ++i) {
|
||||
AppendFmt(&b, "\e[%d;%dH ║ %-*s ║ ", y++, x, w - 8, lines->p[i]);
|
||||
}
|
||||
FreeLines(lines);
|
||||
AppendFmt(&b, "\e[%d;%dH ║ %-*s ║ ", y++, x, w - 8, "");
|
||||
AppendFmt(&b, "\e[%d;%dH ╚", y++, x);
|
||||
for (i = 0; i < w - 2; ++i) AppendStr(&b, "═");
|
||||
for (i = 0; i < w - 4; ++i) AppendStr(&b, "═");
|
||||
AppendStr(&b, "╝ ");
|
||||
AppendFmt(&b, "\e[%d;%dH", y++, x);
|
||||
for (i = 0; i < w; ++i) AppendStr(&b, " ");
|
||||
CHECK_NE(-1, WriteBuffer(&b, fd));
|
||||
free(b.p);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue