Add help dialog to Blinkenlights emulator

This commit is contained in:
Justine Tunney 2021-02-19 16:56:06 -08:00
parent 02b4a5c824
commit 5f1415af3a
9 changed files with 248 additions and 147 deletions

View file

@ -1511,17 +1511,20 @@ long: push $GDT_LONG_DATA
xor %ebp,%ebp xor %ebp,%ebp
mov $REAL_STACK_FRAME+FRAMESIZE,%esp mov $REAL_STACK_FRAME+FRAMESIZE,%esp
call __map_image call __map_image
ezlea metal,ax ezlea metal.thunk,ax
jmp *%rax jmp *%rax
.endfn long .endfn long
// Long mode in virtual address space. // Long mode in virtual address space.
// @noreturn // @noreturn
metal: metal.thunk:
#if USE_SYMBOL_HACK #if USE_SYMBOL_HACK
.byte 0x0f,0x1f,0207 # nop rdi binbase .byte 0x0f,0x1f,0207 # nop rdi binbase
.long (IMAGE_BASE_VIRTUAL-IMAGE_BASE_REAL)/512 .long (IMAGE_BASE_VIRTUAL-IMAGE_BASE_REAL)/512
#endif #endif
/ 𝑠𝑙𝑖𝑑𝑒
.endfn metal.thunk
metal:
xor %eax,%eax # clear bss xor %eax,%eax # clear bss
mov $ape_bss_vaddr,%edi mov $ape_bss_vaddr,%edi
mov $ape_bss_memsz,%ecx mov $ape_bss_memsz,%ecx

View file

@ -16,8 +16,6 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/bits/safemacros.h"
#include "libc/mem/mem.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/x/x.h" #include "libc/x/x.h"
@ -37,26 +35,24 @@
* @see gc() * @see gc()
*/ */
char *(xstrcat)(const char *s, ...) { char *(xstrcat)(const char *s, ...) {
char *p, b[2];
va_list va; va_list va;
size_t i, n, n2, l; size_t n, m;
char *p, b[2];
p = NULL; p = NULL;
i = n = 0; n = 0;
va_start(va, s); va_start(va, s);
do { do {
if ((intptr_t)s > 0 && (intptr_t)s <= 255) { if ((intptr_t)s > 0 && (intptr_t)s <= 255) {
b[0] = (unsigned char)(intptr_t)s; b[0] = (unsigned char)(intptr_t)s;
b[1] = '\0'; b[1] = '\0';
s = b; s = b;
l = 1; m = 1;
} else { } else {
l = strlen(s); m = strlen(s);
} }
if ((n2 = i + l + 16) >= n) { p = xrealloc(p, n + m + 1);
p = xrealloc(p, (n = n2 + (n2 >> 1))); memcpy(p + n, s, m + 1);
} n += m;
memcpy(p + i, s, l + 1);
i += l;
} while ((s = va_arg(va, const char *))); } while ((s = va_arg(va, const char *)));
va_end(va); va_end(va);
return p; return p;

View file

@ -126,6 +126,27 @@ FEATURES\n\
8086, 8087, i386, x86_64, SSE3, SSSE3, POPCNT, MDA, CGA, TTY\n\ 8086, 8087, i386, x86_64, SSE3, SSSE3, POPCNT, MDA, CGA, TTY\n\
\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 MAXZOOM 16
#define DUMPWIDTH 64 #define DUMPWIDTH 64
#define DISPWIDTH 80 #define DISPWIDTH 80
@ -255,17 +276,18 @@ static int64_t breakpointsstart;
static uint64_t last_opcount; static uint64_t last_opcount;
static char *codepath; static char *codepath;
static void *onbusted; static void *onbusted;
static char *dialog;
static char *statusmessage; static char *statusmessage;
static struct Pty *pty; static struct Pty *pty;
static struct Machine *m; static struct Machine *m;
static struct Panels pan; static struct Panels pan;
static struct Breakpoints breakpoints;
static struct MemoryView codeview; static struct MemoryView codeview;
static struct MemoryView readview; static struct MemoryView readview;
static struct MemoryView writeview; static struct MemoryView writeview;
static struct MemoryView stackview; static struct MemoryView stackview;
static struct MachineState laststate; static struct MachineState laststate;
static struct Breakpoints breakpoints;
static struct MachineMemstat lastmemstat; static struct MachineMemstat lastmemstat;
static struct XmmType xmmtype; static struct XmmType xmmtype;
static struct Elf elf[1]; static struct Elf elf[1];
@ -424,15 +446,24 @@ static void CopyMachineState(struct MachineState *ms) {
memcpy(&ms->sse, &m->sse, sizeof(m->sse)); memcpy(&ms->sse, &m->sse, sizeof(m->sse));
} }
/**
* Handles file mapped page faults in valid page but past eof.
*/
static void OnSigBusted(void) { static void OnSigBusted(void) {
CHECK(onbusted); CHECK(onbusted);
longjmp(onbusted, 1); longjmp(onbusted, 1);
} }
/**
* Returns true if 𝑣 is a shadow memory virtual address.
*/
static bool IsShadow(int64_t v) { static bool IsShadow(int64_t v) {
return 0x7fff8000 <= v && v < 0x100080000000; return 0x7fff8000 <= v && v < 0x100080000000;
} }
/**
* Returns glyph representing byte at virtual address 𝑣.
*/
static int VirtualBing(int64_t v) { static int VirtualBing(int64_t v) {
int rc; int rc;
uint8_t *p; uint8_t *p;
@ -451,6 +482,9 @@ static int VirtualBing(int64_t v) {
return rc; return rc;
} }
/**
* Returns ASAN shadow uint8 concomitant to address 𝑣 or -1.
*/
static int VirtualShadow(int64_t v) { static int VirtualShadow(int64_t v) {
int rc; int rc;
char *p; char *p;
@ -547,9 +581,7 @@ static void TuiRejuvinate(void) {
static void OnQ(void) { static void OnQ(void) {
LOGF("OnQ"); LOGF("OnQ");
if (action & FAILURE) exit(1); action |= EXIT;
action |= INT;
breakpoints.i = 0;
} }
static void OnV(void) { static void OnV(void) {
@ -594,6 +626,7 @@ static void TuiCleanup(void) {
TtyRestore1(); TtyRestore1();
DisableMouseTracking(); DisableMouseTracking();
tuimode = false; tuimode = false;
LeaveScreen();
} }
static void ResolveBreakpoints(void) { static void ResolveBreakpoints(void) {
@ -603,8 +636,10 @@ static void ResolveBreakpoints(void) {
if ((sym = DisFindSymByName(dis, breakpoints.p[i].symbol)) != -1) { if ((sym = DisFindSymByName(dis, breakpoints.p[i].symbol)) != -1) {
breakpoints.p[i].addr = dis->syms.p[sym].addr; breakpoints.p[i].addr = dis->syms.p[sym].addr;
} else { } else {
fprintf(stderr, "error: breakpoint not found: %s\n", fprintf(
breakpoints.p[i].symbol); stderr,
"error: breakpoint not found: %s (out of %,ld loaded symbols)\n",
breakpoints.p[i].symbol, dis->syms.i);
exit(1); exit(1);
} }
} }
@ -2155,12 +2190,9 @@ static void OnBinbase(struct Machine *m) {
int64_t skew; int64_t skew;
skew = m->xedd->op.disp * 512; skew = m->xedd->op.disp * 512;
LOGF("skew binbase %,ld @ %p", skew, GetIp()); LOGF("skew binbase %,ld @ %p", skew, GetIp());
for (i = 0; i < dis->syms.i; ++i) { for (i = 0; i < dis->syms.i; ++i) dis->syms.p[i].addr += skew;
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;
for (i = 0; i < dis->loads.i; ++i) {
dis->loads.p[i].addr += skew;
}
Disassemble(); Disassemble();
} }
@ -2270,7 +2302,7 @@ static void OnFinish(void) {
action &= ~CONTINUE; action &= ~CONTINUE;
} }
static void OnContinue(void) { static void OnContinueTui(void) {
action ^= CONTINUE; action ^= CONTINUE;
action &= ~STEP; action &= ~STEP;
action &= ~NEXT; action &= ~NEXT;
@ -2278,6 +2310,15 @@ static void OnContinue(void) {
action &= ~FAILURE; action &= ~FAILURE;
} }
static void OnContinueExec(void) {
tuimode = false;
action |= CONTINUE;
action &= ~STEP;
action &= ~NEXT;
action &= ~FINISH;
action &= ~FAILURE;
}
static void OnQuit(void) { static void OnQuit(void) {
action |= QUIT; action |= QUIT;
} }
@ -2394,41 +2435,41 @@ static void OnMouse(char *p) {
y -= ep->top; y -= ep->top;
x -= ep->left; x -= ep->left;
switch (e) { switch (e) {
case kMouseWheelUp: CASE(kMouseWheelUp, OnMouseWheelUp(ep, y, x));
OnMouseWheelUp(ep, y, x); CASE(kMouseWheelDown, OnMouseWheelDown(ep, y, x));
break; CASE(kMouseCtrlWheelUp, OnMouseCtrlWheelUp(ep, y, x));
case kMouseWheelDown: CASE(kMouseCtrlWheelDown, OnMouseCtrlWheelDown(ep, y, x));
OnMouseWheelDown(ep, y, x);
break;
case kMouseCtrlWheelUp:
OnMouseCtrlWheelUp(ep, y, x);
break;
case kMouseCtrlWheelDown:
OnMouseCtrlWheelDown(ep, y, x);
break;
default: default:
break; break;
} }
} }
} }
static void OnHelp(void) {
DEBUGF("setting dialog");
dialog = HELP;
}
static void ReadKeyboard(void) { static void ReadKeyboard(void) {
char buf[64], *p = buf; char buf[64], *p = buf;
memset(buf, 0, sizeof(buf)); memset(buf, 0, sizeof(buf));
dialog = NULL;
if (readansi(ttyin, buf, sizeof(buf)) == -1) { if (readansi(ttyin, buf, sizeof(buf)) == -1) {
if (errno == EINTR) { if (errno == EINTR) {
LOGF("readkeyboard interrupted"); LOGF("ReadKeyboard interrupted");
return; return;
} }
FATALF("readkeyboard failed: %s", strerror(errno)); FATALF("ReadKeyboard failed: %s", strerror(errno));
} }
switch (*p++) { switch (*p++) {
CASE('q', OnQ()); CASE('q', OnQ());
CASE('v', OnV()); CASE('v', OnV());
CASE('?', OnHelp());
CASE('s', OnStep()); CASE('s', OnStep());
CASE('n', OnNext()); CASE('n', OnNext());
CASE('f', OnFinish()); CASE('f', OnFinish());
CASE('c', OnContinue()); CASE('c', OnContinueTui());
CASE('C', OnContinueExec());
CASE('R', OnRestart()); CASE('R', OnRestart());
CASE('x', OnXmmDisp()); CASE('x', OnXmmDisp());
CASE('t', OnXmmType()); CASE('t', OnXmmType());
@ -2451,57 +2492,26 @@ static void ReadKeyboard(void) {
CASE(CTRL('T'), OnTurbo()); CASE(CTRL('T'), OnTurbo());
case '\e': case '\e':
switch (*p++) { switch (*p++) {
CASE('v', OnPageUp()); CASE('v', OnPageUp()); /* alt+v */
CASE('t', OnSlowmo()); CASE('t', OnSlowmo()); /* alt+t */
case 'O':
switch (*p++) {
CASE('P', OnHelp()); /* \eOP is F1 */
default:
break;
}
break;
case '[': case '[':
switch (*p++) { switch (*p++) {
CASE('<', OnMouse(p)); CASE('<', OnMouse(p));
CASE('A', OnUpArrow()); CASE('A', OnUpArrow()); /* \e[A is up */
CASE('B', OnDownArrow()); CASE('B', OnDownArrow()); /* \e[B is down */
CASE('F', OnEnd()); CASE('F', OnEnd()); /* \e[F is end */
CASE('H', OnHome()); CASE('H', OnHome()); /* \e[H is home */
case '1': CASE('1', OnHome()); /* \e[1~ is home */
switch (*p++) { CASE('4', OnEnd()); /* \e[1~ is end */
CASE('~', OnHome()); CASE('5', OnPageUp()); /* \e[1~ is pgup */
default: CASE('6', OnPageDown()); /* \e[1~ is pgdn */
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;
default: default:
break; break;
} }
@ -2609,14 +2619,13 @@ static void Tui(void) {
SetupDraw(); SetupDraw();
ScrollOp(&pan.disassembly, GetDisIndex()); ScrollOp(&pan.disassembly, GetDisIndex());
if (!(interrupt = setjmp(m->onhalt))) { if (!(interrupt = setjmp(m->onhalt))) {
for (;;) { do {
if (!(action & FAILURE)) { if (!(action & FAILURE)) {
LoadInstruction(m); LoadInstruction(m);
if ((action & (FINISH | NEXT | CONTINUE)) && if ((action & (FINISH | NEXT | CONTINUE)) &&
(bp = IsAtBreakpoint(&breakpoints, GetIp())) != -1) { (bp = IsAtBreakpoint(&breakpoints, GetIp())) != -1) {
action &= ~(FINISH | NEXT | CONTINUE); action &= ~(FINISH | NEXT | CONTINUE);
LOGF("BREAK %p", breakpoints.p[bp].addr); LOGF("BREAK %p", breakpoints.p[bp].addr);
break;
} }
} else { } else {
m->xedd = (struct XedDecodedInst *)m->icache[0]; m->xedd = (struct XedDecodedInst *)m->icache[0];
@ -2642,6 +2651,9 @@ static void Tui(void) {
tick = 0; tick = 0;
Redraw(); Redraw();
} }
if (dialog) {
PrintMessageBox(ttyout, dialog, tyn, txn);
}
if (action & FAILURE) { if (action & FAILURE) {
LOGF("TUI FAILURE"); LOGF("TUI FAILURE");
PrintMessageBox(ttyout, systemfailure, tyn, txn); PrintMessageBox(ttyout, systemfailure, tyn, txn);
@ -2651,8 +2663,9 @@ static void Tui(void) {
LeaveScreen(); LeaveScreen();
exit(1); exit(1);
} }
} else if (!IsExecuting() || (!(action & CONTINUE) && !(action & INT) && } else if (dialog || !IsExecuting() ||
HasPendingKeyboard())) { (!(action & CONTINUE) && !(action & INT) &&
HasPendingKeyboard())) {
ReadKeyboard(); ReadKeyboard();
} }
if (action & INT) { if (action & INT) {
@ -2661,13 +2674,11 @@ static void Tui(void) {
if (action & (CONTINUE | NEXT | FINISH)) { if (action & (CONTINUE | NEXT | FINISH)) {
action &= ~(CONTINUE | NEXT | FINISH); action &= ~(CONTINUE | NEXT | FINISH);
} else { } else {
tuimode = false; action |= EXIT;
action |= CONTINUE;
break; break;
} }
} }
if (action & EXIT) { if (action & EXIT) {
LeaveScreen();
LOGF("TUI EXIT"); LOGF("TUI EXIT");
break; break;
} }
@ -2723,7 +2734,7 @@ static void Tui(void) {
} }
} }
} }
} } while (tuimode);
} else { } else {
if (OnHalt(interrupt)) { if (OnHalt(interrupt)) {
goto KeepGoing; goto KeepGoing;
@ -2794,42 +2805,37 @@ int Emulator(int argc, char *argv[]) {
int rc, fd; int rc, fd;
codepath = argv[optind++]; codepath = argv[optind++];
m->fds.p = xcalloc((m->fds.n = 8), sizeof(struct MachineFd)); m->fds.p = xcalloc((m->fds.n = 8), sizeof(struct MachineFd));
Restart: do {
action = 0; action = 0;
LoadProgram(m, codepath, argv + optind, environ, elf); LoadProgram(m, codepath, argv + optind, environ, elf);
AddHostFd(STDIN_FILENO); AddHostFd(0);
AddHostFd(STDOUT_FILENO); AddHostFd(1);
AddHostFd(STDERR_FILENO); AddHostFd(2);
if (tuimode) { if (tuimode) {
ttyin = isatty(STDIN_FILENO) ? STDIN_FILENO : OpenDevTty(); ttyin = isatty(0) ? 0 : OpenDevTty();
ttyout = isatty(STDOUT_FILENO) ? STDOUT_FILENO : OpenDevTty(); ttyout = isatty(1) ? 1 : OpenDevTty();
} else {
ttyin = -1;
ttyout = -1;
}
if (ttyout != -1) {
atexit(TtyRestore1);
xsigaction(SIGWINCH, OnSigWinch, 0, 0, 0);
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;
}
while (!(action & EXIT)) {
if (!tuimode) {
Exec();
} else { } else {
Tui(); ttyin = -1;
ttyout = -1;
} }
if (action & RESTART) { if (ttyout != -1) {
goto Restart; atexit(TtyRestore1);
xsigaction(SIGWINCH, OnSigWinch, 0, 0, 0);
tyn = 24;
txn = 80;
GetTtySize(ttyout);
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;
} }
} do {
if (tuimode) { if (!tuimode) {
LeaveScreen(); Exec();
} } else {
Tui();
}
} while (!(action & (RESTART | EXIT)));
} while (action & RESTART);
if (printstats) { if (printstats) {
fprintf(stderr, "taken: %,ld\n", taken); fprintf(stderr, "taken: %,ld\n", taken);
fprintf(stderr, "ntaken: %,ld\n", ntaken); fprintf(stderr, "ntaken: %,ld\n", ntaken);

View file

@ -18,6 +18,7 @@
*/ */
#include "libc/calls/calls.h" #include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h" #include "libc/calls/struct/stat.h"
#include "libc/elf/elf.h"
#include "libc/log/check.h" #include "libc/log/check.h"
#include "libc/runtime/gc.h" #include "libc/runtime/gc.h"
#include "libc/sysv/consts/map.h" #include "libc/sysv/consts/map.h"
@ -28,13 +29,15 @@
void LoadDebugSymbols(struct Elf *elf) { void LoadDebugSymbols(struct Elf *elf) {
int fd; int fd;
size_t n;
void *elfmap; void *elfmap;
struct stat st; struct stat st;
const char *path; const char *path;
if (elf->ehdr) return; if (elf->ehdr && GetElfSymbolTable(elf->ehdr, elf->size, &n) && n) return;
DCHECK_NOTNULL(elf->prog); DCHECK_NOTNULL(elf->prog);
fprintf(stderr, "HI %s\n", elf->prog);
if ((fd = open(gc(xstrcat(elf->prog, ".dbg")), O_RDONLY)) != -1 || 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 && if (fstat(fd, &st) != -1 &&
(elfmap = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) != (elfmap = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) !=
MAP_FAILED) { MAP_FAILED) {

View file

@ -203,5 +203,5 @@ void DisLoadElf(struct Dis *d, struct Elf *elf) {
DisLoadElfLoads(d, elf); DisLoadElfLoads(d, elf);
DisLoadElfSyms(d, elf); DisLoadElfSyms(d, elf);
DisSortSyms(d); DisSortSyms(d);
DisCanonizeSyms(d); /* DisCanonizeSyms(d); */
} }

54
tool/build/lib/lines.c Normal file
View 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
View 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_ */

View file

@ -140,7 +140,7 @@ void LoadProgram(struct Machine *m, const char *prog, char **args, char **vars,
size_t i, mappedsize; size_t i, mappedsize;
DCHECK_NOTNULL(prog); DCHECK_NOTNULL(prog);
elf->prog = 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)) { (fstat(fd, &st) == -1 || !st.st_size)) {
fputs(prog, stderr); fputs(prog, stderr);
fputs(": not found\n", stderr); fputs(": not found\n", stderr);

View file

@ -17,30 +17,51 @@
PERFORMANCE OF THIS SOFTWARE. PERFORMANCE OF THIS SOFTWARE.
*/ */
#include "libc/log/check.h" #include "libc/log/check.h"
#include "libc/macros.h"
#include "libc/math.h" #include "libc/math.h"
#include "libc/mem/mem.h" #include "libc/mem/mem.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "libc/unicode/unicode.h"
#include "tool/build/lib/buffer.h" #include "tool/build/lib/buffer.h"
#include "tool/build/lib/lines.h"
#include "tool/build/lib/panel.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) { void PrintMessageBox(int fd, const char *msg, long tyn, long txn) {
char buf[1];
struct Buffer b; struct Buffer b;
int i, w, h, x, y; int i, w, h, x, y;
h = 2 + 1 + 2; struct Lines *lines;
w = 3 + strlen(msg) + 3; lines = NewLines();
AppendLines(lines, msg);
h = 3 + lines->n + 3;
w = 4 + GetWidthOfLongestLine(lines) + 4;
x = lrint(txn / 2. - w / 2.); x = lrint(txn / 2. - w / 2.);
y = lrint(tyn / 2. - h / 2.); y = lrint(tyn / 2. - h / 2.);
memset(&b, 0, sizeof(b)); memset(&b, 0, sizeof(b));
AppendFmt(&b, "\e[%d;%dH╔", y++, x); AppendFmt(&b, "\e[%d;%dH", y++, x);
for (i = 0; i < w - 2; ++i) AppendStr(&b, ""); for (i = 0; i < w; ++i) AppendStr(&b, " ");
AppendStr(&b, ""); AppendFmt(&b, "\e[%d;%dH ╔", y++, x);
AppendFmt(&b, "\e[%d;%dH║ %-*s ║", y++, x, w - 6, ""); for (i = 0; i < w - 4; ++i) AppendStr(&b, "");
AppendFmt(&b, "\e[%d;%dH║ %-*s ║", y++, x, w - 6, msg); AppendStr(&b, "");
AppendFmt(&b, "\e[%d;%dH║ %-*s ║", y++, x, w - 6, ""); AppendFmt(&b, "\e[%d;%dH ║ %-*s ║ ", y++, x, w - 8, "");
AppendFmt(&b, "\e[%d;%dH╚", y++, x); for (i = 0; i < lines->n; ++i) {
for (i = 0; i < w - 2; ++i) AppendStr(&b, ""); AppendFmt(&b, "\e[%d;%dH ║ %-*s ║ ", y++, x, w - 8, lines->p[i]);
AppendStr(&b, ""); }
FreeLines(lines);
AppendFmt(&b, "\e[%d;%dH ║ %-*s ║ ", y++, x, w - 8, "");
AppendFmt(&b, "\e[%d;%dH ╚", y++, x);
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)); CHECK_NE(-1, WriteBuffer(&b, fd));
free(b.p); free(b.p);
} }