mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-02-25 15:29:03 +00:00
Add lua repl interface to redbean
You can now interact with the global web server state on the command line, which the web server is running. This supports Emacs shortcuts with history, readline parity, <tab> completions, plus hints. Enjoy!
This commit is contained in:
parent
f6b6204b9e
commit
a6b02ce5a6
24 changed files with 848 additions and 463 deletions
libc
calls
fmt
intrin
log
runtime
str
third_party
tool
|
@ -36,13 +36,23 @@ textwindows int ioctl_tcgets_nt(int ignored, struct termios *tio) {
|
|||
if (inok | outok) {
|
||||
bzero(tio, sizeof(*tio));
|
||||
if (inok) {
|
||||
if (inmode & kNtEnableLineInput) tio->c_lflag |= ICANON;
|
||||
if (inmode & kNtEnableEchoInput) tio->c_lflag |= ECHO;
|
||||
if (inmode & kNtEnableProcessedInput) tio->c_lflag |= IEXTEN | ISIG;
|
||||
if (inmode & kNtEnableLineInput) {
|
||||
tio->c_lflag |= ICANON;
|
||||
}
|
||||
if (inmode & kNtEnableEchoInput) {
|
||||
tio->c_lflag |= ECHO;
|
||||
}
|
||||
if (inmode & kNtEnableProcessedInput) {
|
||||
tio->c_lflag |= IEXTEN | ISIG;
|
||||
}
|
||||
}
|
||||
if (outok) {
|
||||
if (outmode & kNtEnableProcessedOutput) tio->c_oflag |= OPOST;
|
||||
if (!(outmode & kNtDisableNewlineAutoReturn)) tio->c_oflag |= ONLCR;
|
||||
if (outmode & kNtEnableProcessedOutput) {
|
||||
tio->c_oflag |= OPOST;
|
||||
}
|
||||
if (!(outmode & kNtDisableNewlineAutoReturn)) {
|
||||
tio->c_oflag |= ONLCR;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
|
|
|
@ -23,11 +23,10 @@
|
|||
#include "libc/calls/termios.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/nomultics.internal.h"
|
||||
#include "libc/sysv/consts/termios.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
||||
extern bool __nomultics;
|
||||
|
||||
int ioctl_tcsets_nt(int, uint64_t, const struct termios *);
|
||||
|
||||
static int ioctl_tcsets_metal(int fd, uint64_t request,
|
||||
|
@ -85,7 +84,9 @@ int ioctl_tcsets(int fd, uint64_t request, ...) {
|
|||
rc = einval();
|
||||
}
|
||||
if (rc != -1) {
|
||||
__nomultics = !(tio->c_oflag & OPOST);
|
||||
if (__nomultics == 0 || __nomultics == 1) {
|
||||
__nomultics = !(tio->c_oflag & OPOST);
|
||||
}
|
||||
}
|
||||
STRACE("ioctl_tcsets(%d, %p, %p) → %d% m", fd, request, tio, rc);
|
||||
return rc;
|
||||
|
|
|
@ -20,7 +20,11 @@
|
|||
#include "libc/calls/internal.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/sock/internal.h"
|
||||
#include "libc/sock/sock.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
|
@ -62,8 +66,9 @@
|
|||
* @norestart
|
||||
*/
|
||||
int poll(struct pollfd *fds, size_t nfds, int timeout_ms) {
|
||||
int rc;
|
||||
int i, rc;
|
||||
uint64_t millis;
|
||||
|
||||
if (IsAsan() && !__asan_is_valid(fds, nfds * sizeof(struct pollfd))) {
|
||||
rc = efault();
|
||||
} else if (!IsWindows()) {
|
||||
|
@ -76,6 +81,23 @@ int poll(struct pollfd *fds, size_t nfds, int timeout_ms) {
|
|||
millis = timeout_ms;
|
||||
rc = sys_poll_nt(fds, nfds, &millis);
|
||||
}
|
||||
STRACE("poll(%p, %'lu, %'d) → %d% lm", fds, nfds, timeout_ms, rc);
|
||||
|
||||
#if defined(SYSDEBUG) && _POLLTRACE
|
||||
if (__strace > 0) {
|
||||
if (rc == -1 && errno == EFAULT) {
|
||||
STRACE("poll(%p, %'lu, %'d) → %d% lm", fds, nfds, timeout_ms, rc);
|
||||
} else {
|
||||
kprintf(STRACE_PROLOGUE "poll({");
|
||||
for (i = 0; i < MIN(5, nfds); ++i) {
|
||||
kprintf("%s{%d,%s,%s}", i ? ", " : "", fds[i].fd,
|
||||
DescribePollFlags(fds[i].events),
|
||||
DescribePollFlags(fds[i].revents));
|
||||
}
|
||||
kprintf("%s}, %'zu, %'d) → %d% lm%n", i == 5 ? "..." : "", nfds,
|
||||
timeout_ms, rc);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,32 @@
|
|||
/**
|
||||
* Obtains the termios struct.
|
||||
*
|
||||
* Here are the general defaults you can expect across platforms:
|
||||
*
|
||||
* c_iflag = ICRNL IXON
|
||||
* c_oflag = OPOST ONLCR NL0 CR0 TAB0 BS0 VT0 FF0
|
||||
* c_cflag = ISIG CREAD CS8
|
||||
* c_lflag = ISIG ICANON ECHO ECHOE ECHOK IEXTEN ECHOCTL ECHOKE
|
||||
* c_ispeed = 38400
|
||||
* c_ospeed = 38400
|
||||
* c_cc[VINTR] = CTRL-C
|
||||
* c_cc[VQUIT] = CTRL-\ # ignore this comment
|
||||
* c_cc[VERASE] = CTRL-?
|
||||
* c_cc[VKILL] = CTRL-U
|
||||
* c_cc[VEOF] = CTRL-D
|
||||
* c_cc[VTIME] = CTRL-@
|
||||
* c_cc[VMIN] = CTRL-A
|
||||
* c_cc[VSTART] = CTRL-Q
|
||||
* c_cc[VSTOP] = CTRL-S
|
||||
* c_cc[VSUSP] = CTRL-Z
|
||||
* c_cc[VEOL] = CTRL-@
|
||||
* c_cc[VSWTC] = CTRL-@
|
||||
* c_cc[VREPRINT] = CTRL-R
|
||||
* c_cc[VDISCARD] = CTRL-O
|
||||
* c_cc[VWERASE] = CTRL-W
|
||||
* c_cc[VLNEXT] = CTRL-V
|
||||
* c_cc[VEOL2] = CTRL-@
|
||||
*
|
||||
* @param fd open file descriptor that isatty()
|
||||
* @param tio is where result is stored
|
||||
* @return -1 w/ errno on error
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "libc/fmt/fmts.h"
|
||||
#include "libc/fmt/internal.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/nomultics.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
#include "libc/nexgen32e/bsr.h"
|
||||
|
@ -40,9 +41,6 @@
|
|||
} \
|
||||
} while (0)
|
||||
|
||||
extern bool __nomultics;
|
||||
extern bool __replmode;
|
||||
|
||||
static const char kSpecialFloats[2][2][4] = {{"INF", "inf"}, {"NAN", "nan"}};
|
||||
|
||||
static void __fmt_free_dtoa(char **mem) {
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "libc/intrin/asancodes.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/lockcmpxchg.h"
|
||||
#include "libc/intrin/nomultics.internal.h"
|
||||
#include "libc/log/backtrace.internal.h"
|
||||
#include "libc/log/internal.h"
|
||||
#include "libc/log/libfatal.internal.h"
|
||||
|
@ -153,7 +154,6 @@ struct ReportOriginHeap {
|
|||
};
|
||||
|
||||
bool __asan_noreentry;
|
||||
extern bool __nomultics;
|
||||
static struct AsanMorgue __asan_morgue;
|
||||
|
||||
static wontreturn void __asan_unreachable(void) {
|
||||
|
|
|
@ -14,6 +14,7 @@ const char *DescribeFlags(char *, size_t, struct DescribeFlags *, size_t,
|
|||
|
||||
const char *DescribeMapFlags(int);
|
||||
const char *DescribeProtFlags(int);
|
||||
const char *DescribePollFlags(int);
|
||||
const char *DescribeRemapFlags(int);
|
||||
|
||||
const char *DescribeNtPageFlags(uint32_t);
|
||||
|
|
41
libc/intrin/describepollflags.greg.c
Normal file
41
libc/intrin/describepollflags.greg.c
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*-*- 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 2022 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/intrin/describeflags.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nt/enum/filemapflags.h"
|
||||
#include "libc/sysv/consts/poll.h"
|
||||
|
||||
const char *DescribePollFlags(int x) {
|
||||
const struct DescribeFlags kPollFlags[] = {
|
||||
{POLLIN, "IN"}, // order matters
|
||||
{POLLOUT, "OUT"}, // order matters
|
||||
{POLLPRI, "PRI"}, //
|
||||
{POLLHUP, "HUP"}, //
|
||||
{POLLERR, "ERR"}, //
|
||||
{POLLNVAL, "NVAL"}, //
|
||||
{POLLRDBAND, "RDBAND"}, //
|
||||
{POLLRDHUP, "RDHUP"}, //
|
||||
{POLLRDNORM, "RDNORM"}, //
|
||||
{POLLWRBAND, "WRBAND"}, //
|
||||
{POLLWRNORM, "WRNORM"}, //
|
||||
};
|
||||
static char pollflags[64];
|
||||
return DescribeFlags(pollflags, sizeof(pollflags), kPollFlags,
|
||||
ARRAYLEN(kPollFlags), "POLL", x);
|
||||
}
|
|
@ -29,6 +29,7 @@
|
|||
#include "libc/intrin/cmpxchg.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/lockcmpxchg.h"
|
||||
#include "libc/intrin/nomultics.internal.h"
|
||||
#include "libc/intrin/spinlock.h"
|
||||
#include "libc/limits.h"
|
||||
#include "libc/macros.internal.h"
|
||||
|
@ -54,8 +55,6 @@ struct Timestamps {
|
|||
};
|
||||
|
||||
extern int __pid;
|
||||
extern bool __replmode;
|
||||
extern bool __nomultics;
|
||||
unsigned long long __kbirth; // see fork-nt.c
|
||||
|
||||
privileged static struct Timestamps kenter(void) {
|
||||
|
|
|
@ -26,5 +26,13 @@
|
|||
*
|
||||
* @see kprintf()
|
||||
*/
|
||||
bool __nomultics;
|
||||
bool __replmode;
|
||||
char __nomultics;
|
||||
|
||||
/**
|
||||
* Controls ANSI prefix for log emissions.
|
||||
*
|
||||
* This should be true in raw tty mode repls.
|
||||
*
|
||||
* @see kprintf(), vflogf(), linenoise()
|
||||
*/
|
||||
char __replmode;
|
||||
|
|
11
libc/intrin/nomultics.internal.h
Normal file
11
libc/intrin/nomultics.internal.h
Normal file
|
@ -0,0 +1,11 @@
|
|||
#ifndef COSMOPOLITAN_LIBC_INTRIN_NOMULTICS_INTERNAL_H_
|
||||
#define COSMOPOLITAN_LIBC_INTRIN_NOMULTICS_INTERNAL_H_
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
extern bool __nomultics;
|
||||
extern bool __replmode;
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_LIBC_INTRIN_NOMULTICS_INTERNAL_H_ */
|
|
@ -4,6 +4,7 @@
|
|||
#include "libc/calls/struct/rusage.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/winsize.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/nexgen32e/stackframe.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/stdio/stdio.h"
|
||||
|
@ -200,22 +201,26 @@ extern unsigned __log_level; /* log level for runtime check */
|
|||
|
||||
#define LOGIFNEG1(FORM) \
|
||||
({ \
|
||||
int e = errno; \
|
||||
autotype(FORM) Ax = (FORM); \
|
||||
if (UNLIKELY(Ax == (typeof(Ax))(-1)) && LOGGABLE(kLogWarn)) { \
|
||||
++g_ftrace; \
|
||||
__logerrno(__FILE__, __LINE__, #FORM); \
|
||||
--g_ftrace; \
|
||||
errno = e; \
|
||||
} \
|
||||
Ax; \
|
||||
})
|
||||
|
||||
#define LOGIFNULL(FORM) \
|
||||
({ \
|
||||
int e = errno; \
|
||||
autotype(FORM) Ax = (FORM); \
|
||||
if (Ax == NULL && LOGGABLE(kLogWarn)) { \
|
||||
++g_ftrace; \
|
||||
__logerrno(__FILE__, __LINE__, #FORM); \
|
||||
--g_ftrace; \
|
||||
errno = e; \
|
||||
} \
|
||||
Ax; \
|
||||
})
|
||||
|
|
|
@ -54,7 +54,7 @@ void vflogf_onfail(FILE *f) {
|
|||
fseek(f, SEEK_SET, 0);
|
||||
f->beg = f->end = 0;
|
||||
clearerr(f);
|
||||
(fprintf)(f, "performed emergency log truncation: %s\n", strerror(err));
|
||||
(fprintf)(f, "performed emergency log truncation: %s%n", strerror(err));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,7 +104,7 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f,
|
|||
vflogf_onfail(f);
|
||||
}
|
||||
(vfprintf)(f, fmt, va);
|
||||
fputs("\n", f);
|
||||
fprintf(f, "%n");
|
||||
if (bufmode == _IOLBF) {
|
||||
f->bufmode = _IOLBF;
|
||||
fflush(f);
|
||||
|
@ -113,7 +113,7 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f,
|
|||
__start_fatal(file, line);
|
||||
strcpy(buf32, "unknown");
|
||||
gethostname(buf32, sizeof(buf32));
|
||||
(dprintf)(STDERR_FILENO, "fatality %s pid %d\n", buf32, getpid());
|
||||
(dprintf)(STDERR_FILENO, "fatality %s pid %d%n", buf32, getpid());
|
||||
__die();
|
||||
unreachable;
|
||||
}
|
||||
|
|
|
@ -19,8 +19,12 @@
|
|||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/sigset.h"
|
||||
#include "libc/calls/struct/termios.h"
|
||||
#include "libc/calls/termios.h"
|
||||
#include "libc/calls/ttydefaults.h"
|
||||
#include "libc/dce.h"
|
||||
#include "libc/dns/dns.h"
|
||||
#include "libc/errno.h"
|
||||
#include "libc/intrin/describeflags.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/macros.internal.h"
|
||||
|
@ -44,6 +48,7 @@
|
|||
#include "libc/sysv/consts/f.h"
|
||||
#include "libc/sysv/consts/poll.h"
|
||||
#include "libc/sysv/consts/sig.h"
|
||||
#include "libc/sysv/consts/termios.h"
|
||||
#include "tool/decode/lib/idname.h"
|
||||
#include "tool/decode/lib/x86idnames.h"
|
||||
|
||||
|
@ -133,11 +138,13 @@ textstartup void __printargs(const char *prologue) {
|
|||
uintptr_t *auxp;
|
||||
struct utsname uts;
|
||||
char path[PATH_MAX];
|
||||
int x, st, ft, flags;
|
||||
struct termios termios;
|
||||
int e, x, st, ft, flags;
|
||||
struct pollfd pfds[128];
|
||||
struct AuxiliaryValue *auxinfo;
|
||||
st = __strace, __strace = 0;
|
||||
ft = g_ftrace, g_ftrace = 0;
|
||||
e = errno;
|
||||
|
||||
PRINT("");
|
||||
PRINT("SYSTEM");
|
||||
|
@ -327,6 +334,143 @@ textstartup void __printargs(const char *prologue) {
|
|||
PRINT("MEMTRACK");
|
||||
PrintMemoryIntervals(2, &_mmi);
|
||||
|
||||
PRINT("");
|
||||
PRINT("TERMIOS");
|
||||
for (i = 0; i < 2; ++i) {
|
||||
if (!tcgetattr(i, &termios)) {
|
||||
PRINT(" - stdin");
|
||||
kprintf(prologue);
|
||||
kprintf(" c_iflag =");
|
||||
if (termios.c_iflag & IGNBRK) kprintf(" IGNBRK");
|
||||
if (termios.c_iflag & BRKINT) kprintf(" BRKINT");
|
||||
if (termios.c_iflag & IGNPAR) kprintf(" IGNPAR");
|
||||
if (termios.c_iflag & PARMRK) kprintf(" PARMRK");
|
||||
if (termios.c_iflag & INPCK) kprintf(" INPCK");
|
||||
if (termios.c_iflag & ISTRIP) kprintf(" ISTRIP");
|
||||
if (termios.c_iflag & INLCR) kprintf(" INLCR");
|
||||
if (termios.c_iflag & IGNCR) kprintf(" IGNCR");
|
||||
if (termios.c_iflag & ICRNL) kprintf(" ICRNL");
|
||||
if (termios.c_iflag & IXON) kprintf(" IXON");
|
||||
if (termios.c_iflag & IXANY) kprintf(" IXANY");
|
||||
if (termios.c_iflag & IXOFF) kprintf(" IXOFF");
|
||||
if (termios.c_iflag & IMAXBEL) kprintf(" IMAXBEL");
|
||||
if (termios.c_iflag & IUTF8) kprintf(" IUTF8");
|
||||
if (termios.c_iflag & IUCLC) kprintf(" IUCLC");
|
||||
kprintf("%n");
|
||||
kprintf(prologue);
|
||||
kprintf(" c_oflag =");
|
||||
if (termios.c_oflag & OPOST) kprintf(" OPOST");
|
||||
if (termios.c_oflag & ONLCR) kprintf(" ONLCR");
|
||||
if (termios.c_oflag & OCRNL) kprintf(" OCRNL");
|
||||
if (termios.c_oflag & ONOCR) kprintf(" ONOCR");
|
||||
if (termios.c_oflag & ONLRET) kprintf(" ONLRET");
|
||||
if (termios.c_oflag & OFILL) kprintf(" OFILL");
|
||||
if (termios.c_oflag & OFDEL) kprintf(" OFDEL");
|
||||
if (termios.c_oflag & OLCUC) kprintf(" OLCUC");
|
||||
if ((termios.c_oflag & NLDLY) == NL0) {
|
||||
kprintf(" NL0");
|
||||
} else if ((termios.c_oflag & NLDLY) == NL1) {
|
||||
kprintf(" NL1");
|
||||
} else if ((termios.c_oflag & NLDLY) == NL2) {
|
||||
kprintf(" NL2");
|
||||
} else if ((termios.c_oflag & NLDLY) == NL3) {
|
||||
kprintf(" NL3");
|
||||
}
|
||||
if ((termios.c_oflag & CRDLY) == CR0) {
|
||||
kprintf(" CR0");
|
||||
} else if ((termios.c_oflag & CRDLY) == CR1) {
|
||||
kprintf(" CR1");
|
||||
} else if ((termios.c_oflag & CRDLY) == CR2) {
|
||||
kprintf(" CR2");
|
||||
} else if ((termios.c_oflag & CRDLY) == CR3) {
|
||||
kprintf(" CR3");
|
||||
}
|
||||
if ((termios.c_oflag & TABDLY) == TAB0) {
|
||||
kprintf(" TAB0");
|
||||
} else if ((termios.c_oflag & TABDLY) == TAB1) {
|
||||
kprintf(" TAB1");
|
||||
} else if ((termios.c_oflag & TABDLY) == TAB2) {
|
||||
kprintf(" TAB2");
|
||||
} else if ((termios.c_oflag & TABDLY) == TAB3) {
|
||||
kprintf(" TAB3");
|
||||
}
|
||||
if ((termios.c_oflag & BSDLY) == BS0) {
|
||||
kprintf(" BS0");
|
||||
} else if ((termios.c_oflag & BSDLY) == BS1) {
|
||||
kprintf(" BS1");
|
||||
}
|
||||
if ((termios.c_oflag & VTDLY) == VT0) {
|
||||
kprintf(" VT0");
|
||||
} else if ((termios.c_oflag & VTDLY) == VT1) {
|
||||
kprintf(" VT1");
|
||||
}
|
||||
if ((termios.c_oflag & FFDLY) == FF0) {
|
||||
kprintf(" FF0");
|
||||
} else if ((termios.c_oflag & FFDLY) == FF1) {
|
||||
kprintf(" FF1");
|
||||
}
|
||||
kprintf("%n");
|
||||
kprintf(prologue);
|
||||
kprintf(" c_cflag =");
|
||||
if (termios.c_cflag & ISIG) kprintf(" ISIG");
|
||||
if (termios.c_cflag & CSTOPB) kprintf(" CSTOPB");
|
||||
if (termios.c_cflag & CREAD) kprintf(" CREAD");
|
||||
if (termios.c_cflag & PARENB) kprintf(" PARENB");
|
||||
if (termios.c_cflag & PARODD) kprintf(" PARODD");
|
||||
if (termios.c_cflag & HUPCL) kprintf(" HUPCL");
|
||||
if (termios.c_cflag & CLOCAL) kprintf(" CLOCAL");
|
||||
if ((termios.c_cflag & CSIZE) == CS5) {
|
||||
kprintf(" CS5");
|
||||
} else if ((termios.c_cflag & CSIZE) == CS6) {
|
||||
kprintf(" CS6");
|
||||
} else if ((termios.c_cflag & CSIZE) == CS7) {
|
||||
kprintf(" CS7");
|
||||
} else if ((termios.c_cflag & CSIZE) == CS8) {
|
||||
kprintf(" CS8");
|
||||
}
|
||||
kprintf("%n");
|
||||
kprintf(prologue);
|
||||
kprintf(" c_lflag =");
|
||||
if (termios.c_lflag & ISIG) kprintf(" ISIG");
|
||||
if (termios.c_lflag & ICANON) kprintf(" ICANON");
|
||||
if (termios.c_lflag & ECHO) kprintf(" ECHO");
|
||||
if (termios.c_lflag & ECHOE) kprintf(" ECHOE");
|
||||
if (termios.c_lflag & ECHOK) kprintf(" ECHOK");
|
||||
if (termios.c_lflag & ECHONL) kprintf(" ECHONL");
|
||||
if (termios.c_lflag & NOFLSH) kprintf(" NOFLSH");
|
||||
if (termios.c_lflag & TOSTOP) kprintf(" TOSTOP");
|
||||
if (termios.c_lflag & IEXTEN) kprintf(" IEXTEN");
|
||||
if (termios.c_lflag & ECHOCTL) kprintf(" ECHOCTL");
|
||||
if (termios.c_lflag & ECHOPRT) kprintf(" ECHOPRT");
|
||||
if (termios.c_lflag & ECHOKE) kprintf(" ECHOKE");
|
||||
if (termios.c_lflag & FLUSHO) kprintf(" FLUSHO");
|
||||
if (termios.c_lflag & PENDIN) kprintf(" PENDIN");
|
||||
if (termios.c_lflag & XCASE) kprintf(" XCASE");
|
||||
kprintf("%n");
|
||||
PRINT(" c_ispeed = %u", termios.c_ispeed);
|
||||
PRINT(" c_ospeed = %u", termios.c_ospeed);
|
||||
PRINT(" c_cc[VINTR] = CTRL-%c", CTRL(termios.c_cc[VINTR]));
|
||||
PRINT(" c_cc[VQUIT] = CTRL-%c", CTRL(termios.c_cc[VQUIT]));
|
||||
PRINT(" c_cc[VERASE] = CTRL-%c", CTRL(termios.c_cc[VERASE]));
|
||||
PRINT(" c_cc[VKILL] = CTRL-%c", CTRL(termios.c_cc[VKILL]));
|
||||
PRINT(" c_cc[VEOF] = CTRL-%c", CTRL(termios.c_cc[VEOF]));
|
||||
PRINT(" c_cc[VTIME] = CTRL-%c", CTRL(termios.c_cc[VTIME]));
|
||||
PRINT(" c_cc[VMIN] = CTRL-%c", CTRL(termios.c_cc[VMIN]));
|
||||
PRINT(" c_cc[VSTART] = CTRL-%c", CTRL(termios.c_cc[VSTART]));
|
||||
PRINT(" c_cc[VSTOP] = CTRL-%c", CTRL(termios.c_cc[VSTOP]));
|
||||
PRINT(" c_cc[VSUSP] = CTRL-%c", CTRL(termios.c_cc[VSUSP]));
|
||||
PRINT(" c_cc[VEOL] = CTRL-%c", CTRL(termios.c_cc[VEOL]));
|
||||
PRINT(" c_cc[VSWTC] = CTRL-%c", CTRL(termios.c_cc[VSWTC]));
|
||||
PRINT(" c_cc[VREPRINT] = CTRL-%c", CTRL(termios.c_cc[VREPRINT]));
|
||||
PRINT(" c_cc[VDISCARD] = CTRL-%c", CTRL(termios.c_cc[VDISCARD]));
|
||||
PRINT(" c_cc[VWERASE] = CTRL-%c", CTRL(termios.c_cc[VWERASE]));
|
||||
PRINT(" c_cc[VLNEXT] = CTRL-%c", CTRL(termios.c_cc[VLNEXT]));
|
||||
PRINT(" c_cc[VEOL2] = CTRL-%c", CTRL(termios.c_cc[VEOL2]));
|
||||
} else {
|
||||
PRINT(" - tcgetattr(%d) failed %m", i);
|
||||
}
|
||||
}
|
||||
|
||||
if (IsWindows()) {
|
||||
struct NtStartupInfo startinfo;
|
||||
GetStartupInfo(&startinfo);
|
||||
|
@ -400,4 +544,5 @@ textstartup void __printargs(const char *prologue) {
|
|||
PRINT("");
|
||||
__strace = st;
|
||||
g_ftrace = ft;
|
||||
errno = e;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "libc/errno.h"
|
||||
#include "libc/fmt/fmt.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/nomultics.internal.h"
|
||||
#include "libc/log/libfatal.internal.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/nexgen32e/bsr.h"
|
||||
|
@ -86,7 +87,6 @@ struct WinArgs {
|
|||
};
|
||||
|
||||
extern int __pid;
|
||||
extern bool __nomultics;
|
||||
extern uint32_t __winmainpid;
|
||||
extern int64_t __wincrashearly;
|
||||
extern const char kConsoleHandles[3];
|
||||
|
|
|
@ -22,6 +22,6 @@ bool startswithi(const char *s, const char *prefix) {
|
|||
for (;;) {
|
||||
if (!*prefix) return true;
|
||||
if (!*s) return false;
|
||||
if (kToLower[*s++ & 255] != (*prefix++ & 255)) return false;
|
||||
if (kToLower[*s++ & 255] != kToLower[*prefix++ & 255]) return false;
|
||||
}
|
||||
}
|
||||
|
|
81
third_party/linenoise/linenoise.c
vendored
81
third_party/linenoise/linenoise.c
vendored
|
@ -122,7 +122,9 @@
|
|||
#include "libc/assert.h"
|
||||
#include "libc/bits/bits.h"
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/sig.internal.h"
|
||||
#include "libc/calls/sigbits.h"
|
||||
#include "libc/calls/strace.internal.h"
|
||||
#include "libc/calls/struct/sigaction.h"
|
||||
#include "libc/calls/struct/stat.h"
|
||||
#include "libc/calls/termios.h"
|
||||
|
@ -131,6 +133,9 @@
|
|||
#include "libc/errno.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/intrin/asan.internal.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/nomultics.internal.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/macros.internal.h"
|
||||
#include "libc/mem/mem.h"
|
||||
|
@ -160,6 +165,8 @@ Copyright 2018-2020 Justine Tunney <jtunney@gmail.com>\\n\
|
|||
Copyright 2010-2016 Salvatore Sanfilippo <antirez@gmail.com>\\n\
|
||||
Copyright 2010-2013 Pieter Noordhuis <pcnoordhuis@gmail.com>\"");
|
||||
|
||||
#define LINENOISE_POLL_MS __SIG_POLLING_INTERVAL_MS
|
||||
|
||||
#define LINENOISE_MAX_RING 8
|
||||
#define LINENOISE_MAX_DEBUG 16
|
||||
#define LINENOISE_MAX_HISTORY 1024
|
||||
|
@ -249,7 +256,6 @@ static char maskmode;
|
|||
static char ispaused;
|
||||
static char iscapital;
|
||||
static int historylen;
|
||||
extern bool __replmode;
|
||||
static struct linenoiseRing ring;
|
||||
static struct sigaction orig_int;
|
||||
static struct sigaction orig_quit;
|
||||
|
@ -257,6 +263,7 @@ static struct sigaction orig_cont;
|
|||
static struct sigaction orig_winch;
|
||||
static struct termios orig_termios;
|
||||
static char *history[LINENOISE_MAX_HISTORY];
|
||||
static linenoisePollCallback *pollCallback;
|
||||
static linenoiseXlatCallback *xlatCallback;
|
||||
static linenoiseHintsCallback *hintsCallback;
|
||||
static linenoiseFreeHintsCallback *freeHintsCallback;
|
||||
|
@ -703,6 +710,22 @@ static void linenoiseDebug(struct linenoiseState *l, const char *fmt, ...) {
|
|||
free(msg);
|
||||
}
|
||||
|
||||
static int linenoisePoll(struct linenoiseState *l, int fd) {
|
||||
int rc, ms;
|
||||
for (ms = LINENOISE_POLL_MS;;) {
|
||||
if (pollCallback) {
|
||||
rc = pollCallback(fd, ms);
|
||||
} else {
|
||||
rc = poll((struct pollfd[]){{fd, POLLIN}}, 1, ms);
|
||||
}
|
||||
if (rc) {
|
||||
return rc;
|
||||
} else {
|
||||
linenoiseRefreshLine(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t linenoiseRead(int fd, char *buf, size_t size,
|
||||
struct linenoiseState *l) {
|
||||
ssize_t rc;
|
||||
|
@ -719,6 +742,7 @@ static ssize_t linenoiseRead(int fd, char *buf, size_t size,
|
|||
}
|
||||
if (l && gotwinch) refreshme = 1;
|
||||
if (refreshme) linenoiseRefreshLine(l);
|
||||
if (linenoisePoll(l, fd) == -1) return -1;
|
||||
rc = readansi(fd, buf, size);
|
||||
} while (rc == -1 && errno == EINTR);
|
||||
if (l && rc > 0) {
|
||||
|
@ -777,7 +801,7 @@ void linenoiseClearScreen(int fd) {
|
|||
}
|
||||
|
||||
static void linenoiseBeep(void) {
|
||||
/* THE TERMINAL BELL IS DEAD - HISTORY HAS KILLED IT */
|
||||
// THE TERMINAL BELL IS DEAD - HISTORY HAS KILLED IT
|
||||
}
|
||||
|
||||
static char linenoiseGrow(struct linenoiseState *ls, size_t n) {
|
||||
|
@ -829,27 +853,31 @@ static ssize_t linenoiseCompleteLine(struct linenoiseState *ls, char *seq,
|
|||
// if there's a multiline completions, then do nothing and wait and
|
||||
// see if the user presses tab again. if the user does this we then
|
||||
// print ALL the completions, to above the editing line
|
||||
nread = linenoiseRead(ls->ifd, seq, size, ls);
|
||||
if (nread == 1 && seq[0] == '\t') {
|
||||
itemlen = linenoiseMaxCompletionLength(&lc) + 4;
|
||||
perline = MAX(1, (ls->ws.ws_col - 1) / itemlen);
|
||||
abInit(&ab);
|
||||
abAppends(&ab, "\r\033[K");
|
||||
for (i = 0; i < lc.len;) {
|
||||
for (j = 0; i < lc.len && j < perline; ++j, ++i) {
|
||||
n = GetMonospaceWidth(lc.cvec[i], strlen(lc.cvec[i]), 0);
|
||||
abAppends(&ab, lc.cvec[i]);
|
||||
for (k = n; k < itemlen; ++k) {
|
||||
abAppendw(&ab, ' ');
|
||||
for (;;) {
|
||||
nread = linenoiseRead(ls->ifd, seq, size, ls);
|
||||
if (nread == 1 && seq[0] == '\t') {
|
||||
itemlen = linenoiseMaxCompletionLength(&lc) + 4;
|
||||
perline = MAX(1, (ls->ws.ws_col - 1) / itemlen);
|
||||
abInit(&ab);
|
||||
abAppends(&ab, "\r\n\033[K");
|
||||
for (i = 0; i < lc.len;) {
|
||||
for (j = 0; i < lc.len && j < perline; ++j, ++i) {
|
||||
n = GetMonospaceWidth(lc.cvec[i], strlen(lc.cvec[i]), 0);
|
||||
abAppends(&ab, lc.cvec[i]);
|
||||
for (k = n; k < itemlen; ++k) {
|
||||
abAppendw(&ab, ' ');
|
||||
}
|
||||
}
|
||||
abAppendw(&ab, READ16LE("\r\n"));
|
||||
}
|
||||
abAppendw(&ab, READ16LE("\r\n"));
|
||||
ab.len -= 2;
|
||||
abAppends(&ab, "\n");
|
||||
linenoiseWriteStr(ls->ofd, ab.b);
|
||||
linenoiseRefreshLine(ls);
|
||||
abFree(&ab);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
ab.len -= 2;
|
||||
abAppends(&ab, "\n");
|
||||
linenoiseWriteStr(ls->ofd, ab.b);
|
||||
linenoiseRefreshLine(ls);
|
||||
abFree(&ab);
|
||||
}
|
||||
}
|
||||
linenoiseFreeCompletions(&lc);
|
||||
|
@ -1697,6 +1725,7 @@ static void linenoiseEditCtrlq(struct linenoiseState *l) {
|
|||
*/
|
||||
static ssize_t linenoiseEdit(int stdin_fd, int stdout_fd, const char *prompt,
|
||||
char **obuf) {
|
||||
int st;
|
||||
ssize_t rc;
|
||||
uint64_t w;
|
||||
size_t nread;
|
||||
|
@ -2108,6 +2137,7 @@ char *linenoise(const char *prompt) {
|
|||
* however if it contains a slash / dot then we'll assume prog is
|
||||
* the history filename which as determined by the caller
|
||||
* @return chomped allocated string of read line or null on eof/error
|
||||
* noting that on eof your errno is not changed
|
||||
*/
|
||||
char *linenoiseWithHistory(const char *prompt, const char *prog) {
|
||||
char *line, *res;
|
||||
|
@ -2137,7 +2167,9 @@ char *linenoiseWithHistory(const char *prompt, const char *prog) {
|
|||
}
|
||||
}
|
||||
if (path.len) {
|
||||
linenoiseHistoryLoad(path.b);
|
||||
if (linenoiseHistoryLoad(path.b) == -1) {
|
||||
kprintf("%r%s: failed to load history: %m%n", path);
|
||||
}
|
||||
}
|
||||
line = linenoise(prompt);
|
||||
if (path.len && line && *line) {
|
||||
|
@ -2185,6 +2217,13 @@ void linenoiseSetXlatCallback(linenoiseXlatCallback *fn) {
|
|||
xlatCallback = fn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets terminal fd pollin callback.
|
||||
*/
|
||||
void linenoiseSetPollCallback(linenoisePollCallback *fn) {
|
||||
pollCallback = fn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds completion.
|
||||
*
|
||||
|
|
2
third_party/linenoise/linenoise.h
vendored
2
third_party/linenoise/linenoise.h
vendored
|
@ -13,12 +13,14 @@ typedef char *(linenoiseHintsCallback)(const char *, const char **,
|
|||
const char **);
|
||||
typedef void(linenoiseFreeHintsCallback)(void *);
|
||||
typedef wint_t(linenoiseXlatCallback)(wint_t);
|
||||
typedef int(linenoisePollCallback)(int, int);
|
||||
|
||||
void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
|
||||
void linenoiseSetHintsCallback(linenoiseHintsCallback *);
|
||||
void linenoiseSetFreeHintsCallback(linenoiseFreeHintsCallback *);
|
||||
void linenoiseAddCompletion(linenoiseCompletions *, const char *);
|
||||
void linenoiseSetXlatCallback(linenoiseXlatCallback *);
|
||||
void linenoiseSetPollCallback(linenoisePollCallback *);
|
||||
|
||||
char *linenoise(const char *) dontdiscard;
|
||||
char *linenoiseRaw(const char *, int, int) dontdiscard;
|
||||
|
|
296
third_party/lua/lrepl.c
vendored
Normal file
296
third_party/lua/lrepl.c
vendored
Normal file
|
@ -0,0 +1,296 @@
|
|||
#define lua_c
|
||||
#include "libc/calls/calls.h"
|
||||
#include "libc/calls/sigbits.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/runtime/gc.h"
|
||||
#include "libc/runtime/runtime.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/x/x.h"
|
||||
#include "third_party/linenoise/linenoise.h"
|
||||
#include "third_party/lua/lauxlib.h"
|
||||
#include "third_party/lua/lprefix.h"
|
||||
#include "third_party/lua/lua.h"
|
||||
#include "third_party/lua/lualib.h"
|
||||
// clang-format off
|
||||
|
||||
static lua_State *globalL;
|
||||
static const char *g_progname;
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Read-Eval-Print Loop (REPL)
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
#if !defined(LUA_PROMPT)
|
||||
#define LUA_PROMPT ">: "
|
||||
#define LUA_PROMPT2 ">>: "
|
||||
#endif
|
||||
|
||||
#if !defined(LUA_MAXINPUT)
|
||||
#define LUA_MAXINPUT 512
|
||||
#endif
|
||||
|
||||
static void lua_readline_addcompletion(linenoiseCompletions *c, char *s) {
|
||||
char **p = c->cvec;
|
||||
size_t n = c->len + 1;
|
||||
if ((p = realloc(p, n * sizeof(*p)))) {
|
||||
p[n - 1] = s;
|
||||
c->cvec = p;
|
||||
c->len = n;
|
||||
}
|
||||
}
|
||||
|
||||
void lua_readline_completions(const char *p, linenoiseCompletions *c) {
|
||||
lua_State *L;
|
||||
const char *name;
|
||||
L = globalL;
|
||||
lua_pushglobaltable(L);
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, -2) != 0) {
|
||||
name = lua_tostring(L, -2);
|
||||
if (startswithi(name, p)) {
|
||||
lua_readline_addcompletion(c, strdup(name));
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
char *lua_readline_hint(const char *p, const char **ansi1, const char **ansi2) {
|
||||
char *h = 0;
|
||||
linenoiseCompletions c = {0};
|
||||
lua_readline_completions(p, &c);
|
||||
if (c.len == 1) h = strdup(c.cvec[0] + strlen(p));
|
||||
linenoiseFreeCompletions(&c);
|
||||
return h;
|
||||
}
|
||||
|
||||
static void lua_freeline (lua_State *L, char *b) {
|
||||
free(b);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the string to be used as a prompt by the interpreter. Leave
|
||||
** the string (or nil, if using the default value) on the stack, to keep
|
||||
** it anchored.
|
||||
*/
|
||||
static const char *get_prompt (lua_State *L, int firstline) {
|
||||
if (lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2") == LUA_TNIL)
|
||||
return (firstline ? LUA_PROMPT : LUA_PROMPT2); /* use the default */
|
||||
else { /* apply 'tostring' over the value */
|
||||
const char *p = luaL_tolstring(L, -1, NULL);
|
||||
lua_remove(L, -2); /* remove original value */
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
/* mark in error messages for incomplete statements */
|
||||
#define EOFMARK "<eof>"
|
||||
#define marklen (sizeof(EOFMARK)/sizeof(char) - 1)
|
||||
|
||||
|
||||
/*
|
||||
** Check whether 'status' signals a syntax error and the error
|
||||
** message at the top of the stack ends with the above mark for
|
||||
** incomplete statements.
|
||||
*/
|
||||
static int incomplete (lua_State *L, int status) {
|
||||
if (status == LUA_ERRSYNTAX) {
|
||||
size_t lmsg;
|
||||
const char *msg = lua_tolstring(L, -1, &lmsg);
|
||||
if (lmsg >= marklen && strcmp(msg + lmsg - marklen, EOFMARK) == 0) {
|
||||
lua_pop(L, 1);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0; /* else... */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Prompt the user, read a line, and push it into the Lua stack.
|
||||
*/
|
||||
static int pushline (lua_State *L, int firstline) {
|
||||
char *b;
|
||||
size_t l;
|
||||
globalL = L;
|
||||
const char *prmt = get_prompt(L, firstline);
|
||||
if (!(b = linenoiseWithHistory(prmt, g_progname)))
|
||||
return 0; /* no input (prompt will be popped by caller) */
|
||||
lua_pop(L, 1); /* remove prompt */
|
||||
l = strlen(b);
|
||||
if (l > 0 && b[l-1] == '\n') /* line ends with newline? */
|
||||
b[--l] = '\0'; /* remove it */
|
||||
if (firstline && b[0] == '=') /* for compatibility with 5.2, ... */
|
||||
lua_pushfstring(L, "return %s", b + 1); /* change '=' to 'return' */
|
||||
else
|
||||
lua_pushlstring(L, b, l);
|
||||
lua_freeline(L, b);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Try to compile line on the stack as 'return <line>;'; on return, stack
|
||||
** has either compiled chunk or original line (if compilation failed).
|
||||
*/
|
||||
static int addreturn (lua_State *L) {
|
||||
const char *line = lua_tostring(L, -1); /* original line */
|
||||
const char *retline = lua_pushfstring(L, "return %s;", line);
|
||||
int status = luaL_loadbuffer(L, retline, strlen(retline), "=stdin");
|
||||
if (status == LUA_OK) {
|
||||
lua_remove(L, -2); /* remove modified line */
|
||||
} else {
|
||||
lua_pop(L, 2); /* pop result from 'luaL_loadbuffer' and modified line */
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Hook set by signal function to stop the interpreter.
|
||||
*/
|
||||
static void lstop (lua_State *L, lua_Debug *ar) {
|
||||
(void)ar; /* unused arg. */
|
||||
lua_sethook(L, NULL, 0, 0); /* reset hook */
|
||||
luaL_error(L, "interrupted!");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Read multiple lines until a complete Lua statement
|
||||
*/
|
||||
static int multiline (lua_State *L) {
|
||||
for (;;) { /* repeat until gets a complete statement */
|
||||
size_t len;
|
||||
const char *line = lua_tolstring(L, 1, &len); /* get what it has */
|
||||
int status = luaL_loadbuffer(L, line, len, "=stdin"); /* try it */
|
||||
if (!incomplete(L, status) || !pushline(L, 0)) {
|
||||
return status; /* cannot or should not try to add continuation line */
|
||||
}
|
||||
lua_pushliteral(L, "\n"); /* add newline... */
|
||||
lua_insert(L, -2); /* ...between the two lines */
|
||||
lua_concat(L, 3); /* join them */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void lua_initrepl(const char *progname) {
|
||||
g_progname = progname;
|
||||
linenoiseSetCompletionCallback(lua_readline_completions);
|
||||
linenoiseSetHintsCallback(lua_readline_hint);
|
||||
linenoiseSetFreeHintsCallback(free);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Read a line and try to load (compile) it first as an expression (by
|
||||
** adding "return " in front of it) and second as a statement. Return
|
||||
** the final status of load/call with the resulting function (if any)
|
||||
** in the top of the stack.
|
||||
*/
|
||||
int lua_loadline (lua_State *L) {
|
||||
int status;
|
||||
lua_settop(L, 0);
|
||||
if (!pushline(L, 1))
|
||||
return -1; /* no input */
|
||||
if ((status = addreturn(L)) != LUA_OK) /* 'return ...' did not work? */
|
||||
status = multiline(L); /* try as command, maybe with continuation lines */
|
||||
lua_remove(L, 1); /* remove line from the stack */
|
||||
lua_assert(lua_gettop(L) == 1);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Function to be called at a C signal. Because a C signal cannot
|
||||
** just change a Lua state (as there is no proper synchronization),
|
||||
** this function only sets a hook that, when called, will stop the
|
||||
** interpreter.
|
||||
*/
|
||||
static void laction (int i) {
|
||||
int flag = LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE | LUA_MASKCOUNT;
|
||||
lua_sethook(globalL, lstop, flag, 1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Message handler used to run all chunks
|
||||
*/
|
||||
static int msghandler (lua_State *L) {
|
||||
const char *msg = lua_tostring(L, 1);
|
||||
if (msg == NULL) { /* is error object not a string? */
|
||||
if (luaL_callmeta(L, 1, "__tostring") && /* does it have a metamethod */
|
||||
lua_type(L, -1) == LUA_TSTRING) /* that produces a string? */
|
||||
return 1; /* that is the message */
|
||||
else
|
||||
msg = lua_pushfstring(L, "(error object is a %s value)",
|
||||
luaL_typename(L, 1));
|
||||
}
|
||||
luaL_traceback(L, L, msg, 1); /* append a standard traceback */
|
||||
return 1; /* return the traceback */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Interface to 'lua_pcall', which sets appropriate message function
|
||||
** and C-signal handler. Used to run all chunks.
|
||||
*/
|
||||
int lua_runchunk (lua_State *L, int narg, int nres) {
|
||||
struct sigaction sa, saold;
|
||||
int status;
|
||||
int base = lua_gettop(L) - narg; /* function index */
|
||||
lua_pushcfunction(L, msghandler); /* push message handler */
|
||||
lua_insert(L, base); /* put it under function and args */
|
||||
globalL = L; /* to be available to 'laction' */
|
||||
sa.sa_flags = SA_RESETHAND; /* if another int happens, terminate */
|
||||
sa.sa_handler = laction;
|
||||
sigemptyset(&sa.sa_mask); /* do not mask any signal */
|
||||
sigaction(SIGINT, &sa, &saold);
|
||||
status = lua_pcall(L, narg, nres, base);
|
||||
sigaction(SIGINT, &saold, 0); /* restore C-signal handler */
|
||||
lua_remove(L, base); /* remove message handler from the stack */
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Prints an error message, adding the program name in front of it
|
||||
** (if present)
|
||||
*/
|
||||
void lua_l_message (const char *pname, const char *msg) {
|
||||
if (pname) lua_writestringerror("%s: ", pname);
|
||||
lua_writestringerror("%s\n", msg);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Prints (calling the Lua 'print' function) any values on the stack
|
||||
*/
|
||||
void lua_l_print (lua_State *L) {
|
||||
int n = lua_gettop(L);
|
||||
if (n > 0) { /* any result to be printed? */
|
||||
luaL_checkstack(L, LUA_MINSTACK, "too many results to print");
|
||||
lua_getglobal(L, "print");
|
||||
lua_insert(L, 1);
|
||||
if (lua_pcall(L, n, 0, 0) != LUA_OK)
|
||||
lua_l_message(g_progname, lua_pushfstring(L, "error calling 'print' (%s)",
|
||||
lua_tostring(L, -1)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Check whether 'status' is not OK and, if so, prints the error
|
||||
** message on the top of the stack. It assumes that the error object
|
||||
** is a string, as it was either generated by Lua or by 'msghandler'.
|
||||
*/
|
||||
int lua_report (lua_State *L, int status) {
|
||||
if (status != LUA_OK) {
|
||||
const char *msg = lua_tostring(L, -1);
|
||||
lua_l_message(g_progname, msg);
|
||||
lua_pop(L, 1); /* remove message */
|
||||
}
|
||||
return status;
|
||||
}
|
19
third_party/lua/lrepl.h
vendored
Normal file
19
third_party/lua/lrepl.h
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
#ifndef COSMOPOLITAN_THIRD_PARTY_LUA_LREPL_H_
|
||||
#define COSMOPOLITAN_THIRD_PARTY_LUA_LREPL_H_
|
||||
#include "third_party/linenoise/linenoise.h"
|
||||
#include "third_party/lua/lauxlib.h"
|
||||
#if !(__ASSEMBLER__ + __LINKER__ + 0)
|
||||
COSMOPOLITAN_C_START_
|
||||
|
||||
int lua_loadline(lua_State *);
|
||||
void lua_l_print(lua_State *);
|
||||
void lua_initrepl(const char *);
|
||||
int lua_report(lua_State *, int);
|
||||
int lua_runchunk(lua_State *, int, int);
|
||||
void lua_l_message(const char *, const char *);
|
||||
char *lua_readline_hint(const char *, const char **, const char **);
|
||||
void lua_readline_completions(const char *, linenoiseCompletions *);
|
||||
|
||||
COSMOPOLITAN_C_END_
|
||||
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
|
||||
#endif /* COSMOPOLITAN_THIRD_PARTY_LUA_LREPL_H_ */
|
362
third_party/lua/lua.main.c
vendored
362
third_party/lua/lua.main.c
vendored
|
@ -13,11 +13,14 @@
|
|||
#include "libc/log/log.h"
|
||||
#include "libc/runtime/gc.h"
|
||||
#include "libc/runtime/stack.h"
|
||||
#include "libc/str/str.h"
|
||||
#include "libc/sysv/consts/exit.h"
|
||||
#include "libc/sysv/consts/sa.h"
|
||||
#include "libc/x/x.h"
|
||||
#include "third_party/linenoise/linenoise.h"
|
||||
#include "third_party/lua/lauxlib.h"
|
||||
#include "third_party/lua/lprefix.h"
|
||||
#include "third_party/lua/lrepl.h"
|
||||
#include "third_party/lua/lua.h"
|
||||
#include "third_party/lua/lualib.h"
|
||||
|
||||
|
@ -38,49 +41,10 @@ STATIC_STACK_SIZE(0x40000);
|
|||
|
||||
static lua_State *globalL = NULL;
|
||||
static const char *progname = LUA_PROGNAME;
|
||||
static const char *histpath;
|
||||
|
||||
|
||||
#if defined(LUA_USE_POSIX) /* { */
|
||||
|
||||
/*
|
||||
** Use 'sigaction' when available.
|
||||
*/
|
||||
static void setsignal (int sig, void (*handler)(int)) {
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = handler;
|
||||
sa.sa_flags = 0;
|
||||
sigemptyset(&sa.sa_mask); /* do not mask any signal */
|
||||
sigaction(sig, &sa, NULL);
|
||||
}
|
||||
|
||||
#else /* }{ */
|
||||
|
||||
#define setsignal signal
|
||||
|
||||
#endif /* } */
|
||||
|
||||
|
||||
/*
|
||||
** Hook set by signal function to stop the interpreter.
|
||||
*/
|
||||
static void lstop (lua_State *L, lua_Debug *ar) {
|
||||
(void)ar; /* unused arg. */
|
||||
lua_sethook(L, NULL, 0, 0); /* reset hook */
|
||||
luaL_error(L, "interrupted!");
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Function to be called at a C signal. Because a C signal cannot
|
||||
** just change a Lua state (as there is no proper synchronization),
|
||||
** this function only sets a hook that, when called, will stop the
|
||||
** interpreter.
|
||||
*/
|
||||
static void laction (int i) {
|
||||
int flag = LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE | LUA_MASKCOUNT;
|
||||
setsignal(i, SIG_DFL); /* if another SIGINT happens, terminate process */
|
||||
lua_sethook(globalL, lstop, flag, 1);
|
||||
static bool lua_stdin_is_tty(void) {
|
||||
return isatty(0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -106,67 +70,6 @@ static void print_usage (const char *badoption) {
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
** Prints an error message, adding the program name in front of it
|
||||
** (if present)
|
||||
*/
|
||||
static void l_message (const char *pname, const char *msg) {
|
||||
if (pname) lua_writestringerror("%s: ", pname);
|
||||
lua_writestringerror("%s\n", msg);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Check whether 'status' is not OK and, if so, prints the error
|
||||
** message on the top of the stack. It assumes that the error object
|
||||
** is a string, as it was either generated by Lua or by 'msghandler'.
|
||||
*/
|
||||
static int report (lua_State *L, int status) {
|
||||
if (status != LUA_OK) {
|
||||
const char *msg = lua_tostring(L, -1);
|
||||
l_message(progname, msg);
|
||||
lua_pop(L, 1); /* remove message */
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Message handler used to run all chunks
|
||||
*/
|
||||
static int msghandler (lua_State *L) {
|
||||
const char *msg = lua_tostring(L, 1);
|
||||
if (msg == NULL) { /* is error object not a string? */
|
||||
if (luaL_callmeta(L, 1, "__tostring") && /* does it have a metamethod */
|
||||
lua_type(L, -1) == LUA_TSTRING) /* that produces a string? */
|
||||
return 1; /* that is the message */
|
||||
else
|
||||
msg = lua_pushfstring(L, "(error object is a %s value)",
|
||||
luaL_typename(L, 1));
|
||||
}
|
||||
luaL_traceback(L, L, msg, 1); /* append a standard traceback */
|
||||
return 1; /* return the traceback */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Interface to 'lua_pcall', which sets appropriate message function
|
||||
** and C-signal handler. Used to run all chunks.
|
||||
*/
|
||||
static int docall (lua_State *L, int narg, int nres) {
|
||||
int status;
|
||||
int base = lua_gettop(L) - narg; /* function index */
|
||||
lua_pushcfunction(L, msghandler); /* push message handler */
|
||||
lua_insert(L, base); /* put it under function and args */
|
||||
globalL = L; /* to be available to 'laction' */
|
||||
setsignal(SIGINT, laction); /* set C-signal handler */
|
||||
status = lua_pcall(L, narg, nres, base);
|
||||
setsignal(SIGINT, SIG_DFL); /* reset C-signal handler */
|
||||
lua_remove(L, base); /* remove message handler from the stack */
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
static void print_version (void) {
|
||||
lua_writestring(LUA_COPYRIGHT, strlen(LUA_COPYRIGHT));
|
||||
lua_writeline();
|
||||
|
@ -195,8 +98,8 @@ static void createargtable (lua_State *L, char **argv, int argc, int script) {
|
|||
|
||||
|
||||
static int dochunk (lua_State *L, int status) {
|
||||
if (status == LUA_OK) status = docall(L, 0, 0);
|
||||
return report(L, status);
|
||||
if (status == LUA_OK) status = lua_runchunk(L, 0, 0);
|
||||
return lua_report(L, status);
|
||||
}
|
||||
|
||||
|
||||
|
@ -218,10 +121,10 @@ static int dolibrary (lua_State *L, const char *name) {
|
|||
int status;
|
||||
lua_getglobal(L, "require");
|
||||
lua_pushstring(L, name);
|
||||
status = docall(L, 1, 1); /* call 'require(name)' */
|
||||
status = lua_runchunk(L, 1, 1); /* call 'require(name)' */
|
||||
if (status == LUA_OK)
|
||||
lua_setglobal(L, name); /* global[name] = require return */
|
||||
return report(L, status);
|
||||
return lua_report(L, status);
|
||||
}
|
||||
|
||||
|
||||
|
@ -249,9 +152,9 @@ static int handle_script (lua_State *L, char **argv) {
|
|||
status = luaL_loadfile(L, fname);
|
||||
if (status == LUA_OK) {
|
||||
int n = pushargs(L); /* push arguments to script */
|
||||
status = docall(L, n, LUA_MULTRET);
|
||||
status = lua_runchunk(L, n, LUA_MULTRET);
|
||||
}
|
||||
return report(L, status);
|
||||
return lua_report(L, status);
|
||||
}
|
||||
|
||||
|
||||
|
@ -364,225 +267,6 @@ static int handle_luainit (lua_State *L) {
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
** {==================================================================
|
||||
** Read-Eval-Print Loop (REPL)
|
||||
** ===================================================================
|
||||
*/
|
||||
|
||||
#if !defined(LUA_PROMPT)
|
||||
#define LUA_PROMPT ">: "
|
||||
#define LUA_PROMPT2 ">>: "
|
||||
#endif
|
||||
|
||||
#if !defined(LUA_MAXINPUT)
|
||||
#define LUA_MAXINPUT 512
|
||||
#endif
|
||||
|
||||
static bool lua_stdin_is_tty(void) {
|
||||
return isatty(0);
|
||||
}
|
||||
|
||||
static bool lua_istartswith(const char *s, const char *prefix) {
|
||||
for (;;) {
|
||||
if (!*prefix) return true;
|
||||
if (!*s) return false;
|
||||
if (tolower(*s++) != tolower(*prefix++)) return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void lua_readline_addcompletion(linenoiseCompletions *c, char *s) {
|
||||
char **p = c->cvec;
|
||||
size_t n = c->len + 1;
|
||||
if ((p = realloc(p, n * sizeof(*p)))) {
|
||||
p[n - 1] = s;
|
||||
c->cvec = p;
|
||||
c->len = n;
|
||||
}
|
||||
}
|
||||
|
||||
static void lua_readline_completions(const char *p, linenoiseCompletions *c) {
|
||||
lua_State *L;
|
||||
const char *name;
|
||||
L = globalL;
|
||||
lua_pushglobaltable(L);
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, -2) != 0) {
|
||||
name = lua_tostring(L, -2);
|
||||
if (lua_istartswith(name, p)) {
|
||||
lua_readline_addcompletion(c, strdup(name));
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
static char *lua_readline_hint(const char *p, const char **ansi1, const char **ansi2) {
|
||||
char *h = 0;
|
||||
linenoiseCompletions c = {0};
|
||||
lua_readline_completions(p, &c);
|
||||
if (c.len == 1) h = strdup(c.cvec[0] + strlen(p));
|
||||
linenoiseFreeCompletions(&c);
|
||||
return h;
|
||||
}
|
||||
|
||||
static void lua_initreadline(lua_State *L) {
|
||||
histpath = xasprintf("%s/.%s_history", _gc(xhomedir()), LUA_PROGNAME);
|
||||
linenoiseSetCompletionCallback(lua_readline_completions);
|
||||
linenoiseSetHintsCallback(lua_readline_hint);
|
||||
linenoiseSetFreeHintsCallback(free);
|
||||
}
|
||||
|
||||
static int lua_readline(lua_State *L, char **b, const char *prompt) {
|
||||
globalL = L;
|
||||
linenoiseHistoryLoad(histpath);
|
||||
return !!(*b = linenoise(prompt));
|
||||
}
|
||||
|
||||
static void lua_saveline(lua_State *L, const char *line) {
|
||||
linenoiseHistoryLoad(histpath);
|
||||
linenoiseHistoryAdd(line);
|
||||
linenoiseHistorySave(histpath);
|
||||
}
|
||||
|
||||
static void lua_freeline (lua_State *L, char *b) {
|
||||
free(b);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the string to be used as a prompt by the interpreter. Leave
|
||||
** the string (or nil, if using the default value) on the stack, to keep
|
||||
** it anchored.
|
||||
*/
|
||||
static const char *get_prompt (lua_State *L, int firstline) {
|
||||
if (lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2") == LUA_TNIL)
|
||||
return (firstline ? LUA_PROMPT : LUA_PROMPT2); /* use the default */
|
||||
else { /* apply 'tostring' over the value */
|
||||
const char *p = luaL_tolstring(L, -1, NULL);
|
||||
lua_remove(L, -2); /* remove original value */
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
/* mark in error messages for incomplete statements */
|
||||
#define EOFMARK "<eof>"
|
||||
#define marklen (sizeof(EOFMARK)/sizeof(char) - 1)
|
||||
|
||||
|
||||
/*
|
||||
** Check whether 'status' signals a syntax error and the error
|
||||
** message at the top of the stack ends with the above mark for
|
||||
** incomplete statements.
|
||||
*/
|
||||
static int incomplete (lua_State *L, int status) {
|
||||
if (status == LUA_ERRSYNTAX) {
|
||||
size_t lmsg;
|
||||
const char *msg = lua_tolstring(L, -1, &lmsg);
|
||||
if (lmsg >= marklen && strcmp(msg + lmsg - marklen, EOFMARK) == 0) {
|
||||
lua_pop(L, 1);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0; /* else... */
|
||||
}
|
||||
|
||||
/*
|
||||
** Prompt the user, read a line, and push it into the Lua stack.
|
||||
*/
|
||||
static int pushline (lua_State *L, int firstline) {
|
||||
char buffer[LUA_MAXINPUT];
|
||||
char *b = buffer;
|
||||
size_t l;
|
||||
const char *prmt = get_prompt(L, firstline);
|
||||
int readstatus = lua_readline(L, &b, prmt);
|
||||
if (readstatus == 0)
|
||||
return 0; /* no input (prompt will be popped by caller) */
|
||||
lua_pop(L, 1); /* remove prompt */
|
||||
l = strlen(b);
|
||||
if (l > 0 && b[l-1] == '\n') /* line ends with newline? */
|
||||
b[--l] = '\0'; /* remove it */
|
||||
if (firstline && b[0] == '=') /* for compatibility with 5.2, ... */
|
||||
lua_pushfstring(L, "return %s", b + 1); /* change '=' to 'return' */
|
||||
else
|
||||
lua_pushlstring(L, b, l);
|
||||
lua_freeline(L, b);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Try to compile line on the stack as 'return <line>;'; on return, stack
|
||||
** has either compiled chunk or original line (if compilation failed).
|
||||
*/
|
||||
static int addreturn (lua_State *L) {
|
||||
const char *line = lua_tostring(L, -1); /* original line */
|
||||
const char *retline = lua_pushfstring(L, "return %s;", line);
|
||||
int status = luaL_loadbuffer(L, retline, strlen(retline), "=stdin");
|
||||
if (status == LUA_OK) {
|
||||
lua_remove(L, -2); /* remove modified line */
|
||||
if (line[0] != '\0') /* non empty? */
|
||||
lua_saveline(L, line); /* keep history */
|
||||
}
|
||||
else
|
||||
lua_pop(L, 2); /* pop result from 'luaL_loadbuffer' and modified line */
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Read multiple lines until a complete Lua statement
|
||||
*/
|
||||
static int multiline (lua_State *L) {
|
||||
for (;;) { /* repeat until gets a complete statement */
|
||||
size_t len;
|
||||
const char *line = lua_tolstring(L, 1, &len); /* get what it has */
|
||||
int status = luaL_loadbuffer(L, line, len, "=stdin"); /* try it */
|
||||
if (!incomplete(L, status) || !pushline(L, 0)) {
|
||||
lua_saveline(L, line); /* keep history */
|
||||
return status; /* cannot or should not try to add continuation line */
|
||||
}
|
||||
lua_pushliteral(L, "\n"); /* add newline... */
|
||||
lua_insert(L, -2); /* ...between the two lines */
|
||||
lua_concat(L, 3); /* join them */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Read a line and try to load (compile) it first as an expression (by
|
||||
** adding "return " in front of it) and second as a statement. Return
|
||||
** the final status of load/call with the resulting function (if any)
|
||||
** in the top of the stack.
|
||||
*/
|
||||
static int loadline (lua_State *L) {
|
||||
int status;
|
||||
lua_settop(L, 0);
|
||||
if (!pushline(L, 1))
|
||||
return -1; /* no input */
|
||||
if ((status = addreturn(L)) != LUA_OK) /* 'return ...' did not work? */
|
||||
status = multiline(L); /* try as command, maybe with continuation lines */
|
||||
lua_remove(L, 1); /* remove line from the stack */
|
||||
lua_assert(lua_gettop(L) == 1);
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Prints (calling the Lua 'print' function) any values on the stack
|
||||
*/
|
||||
static void l_print (lua_State *L) {
|
||||
int n = lua_gettop(L);
|
||||
if (n > 0) { /* any result to be printed? */
|
||||
luaL_checkstack(L, LUA_MINSTACK, "too many results to print");
|
||||
lua_getglobal(L, "print");
|
||||
lua_insert(L, 1);
|
||||
if (lua_pcall(L, n, 0, 0) != LUA_OK)
|
||||
l_message(progname, lua_pushfstring(L, "error calling 'print' (%s)",
|
||||
lua_tostring(L, -1)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Do the REPL: repeatedly read (load) a line, evaluate (call) it, and
|
||||
** print any results.
|
||||
|
@ -591,12 +275,15 @@ static void doREPL (lua_State *L) {
|
|||
int status;
|
||||
const char *oldprogname = progname;
|
||||
progname = NULL; /* no 'progname' on errors in interactive mode */
|
||||
lua_initreadline(L);
|
||||
while ((status = loadline(L)) != -1) {
|
||||
lua_initrepl(LUA_PROGNAME);
|
||||
while ((status = lua_loadline(L)) != -1) {
|
||||
if (status == LUA_OK)
|
||||
status = docall(L, 0, LUA_MULTRET);
|
||||
if (status == LUA_OK) l_print(L);
|
||||
else report(L, status);
|
||||
status = lua_runchunk(L, 0, LUA_MULTRET);
|
||||
if (status == LUA_OK) {
|
||||
lua_l_print(L);
|
||||
} else {
|
||||
lua_report(L, status);
|
||||
}
|
||||
}
|
||||
lua_settop(L, 0); /* clear stack */
|
||||
lua_writeline();
|
||||
|
@ -654,13 +341,15 @@ static int pmain (lua_State *L) {
|
|||
|
||||
|
||||
int main (int argc, char **argv) {
|
||||
ShowCrashReports();
|
||||
int status, result;
|
||||
lua_State *L;
|
||||
if (!IsModeDbg()) {
|
||||
ShowCrashReports();
|
||||
}
|
||||
/* if (IsModeDbg()) ShowCrashReports(); */
|
||||
L = luaL_newstate(); /* create state */
|
||||
if (L == NULL) {
|
||||
l_message(argv[0], "cannot create state: not enough memory");
|
||||
lua_l_message(argv[0], "cannot create state: not enough memory");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
lua_pushcfunction(L, &pmain); /* to call 'pmain' in protected mode */
|
||||
|
@ -668,7 +357,10 @@ int main (int argc, char **argv) {
|
|||
lua_pushlightuserdata(L, argv); /* 2nd argument */
|
||||
status = lua_pcall(L, 2, 1, 0); /* do the call */
|
||||
result = lua_toboolean(L, -1); /* get result */
|
||||
report(L, status);
|
||||
lua_report(L, status);
|
||||
lua_close(L);
|
||||
if (IsModeDbg()) {
|
||||
CheckForMemoryLeaks();
|
||||
}
|
||||
return (result && status == LUA_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include "libc/fmt/fmt.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/kprintf.h"
|
||||
#include "libc/intrin/nomultics.internal.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/log/log.h"
|
||||
#include "libc/mem/mem.h"
|
||||
|
@ -66,9 +67,6 @@
|
|||
#define PIPE 11
|
||||
#define OCTAL 0x80
|
||||
|
||||
extern bool __replmode;
|
||||
extern bool __nomultics;
|
||||
|
||||
static const long __NR_brk = 12;
|
||||
static const long __NR_sigreturn = 15;
|
||||
|
||||
|
|
|
@ -54,16 +54,17 @@ TOOL_NET_DIRECTDEPS = \
|
|||
LIBC_ZIPOS \
|
||||
NET_HTTP \
|
||||
NET_HTTPS \
|
||||
TOOL_BUILD_LIB \
|
||||
THIRD_PARTY_ARGON2 \
|
||||
THIRD_PARTY_GDTOA \
|
||||
THIRD_PARTY_GETOPT \
|
||||
THIRD_PARTY_LINENOISE \
|
||||
THIRD_PARTY_LUA \
|
||||
THIRD_PARTY_MAXMIND \
|
||||
THIRD_PARTY_MBEDTLS \
|
||||
THIRD_PARTY_REGEX \
|
||||
THIRD_PARTY_MAXMIND \
|
||||
THIRD_PARTY_SQLITE3 \
|
||||
THIRD_PARTY_ZLIB \
|
||||
THIRD_PARTY_ARGON2 \
|
||||
TOOL_BUILD_LIB \
|
||||
TOOL_DECODE_LIB
|
||||
|
||||
TOOL_NET_DEPS := \
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "libc/errno.h"
|
||||
#include "libc/fmt/conv.h"
|
||||
#include "libc/fmt/itoa.h"
|
||||
#include "libc/intrin/nomultics.internal.h"
|
||||
#include "libc/log/backtrace.internal.h"
|
||||
#include "libc/log/check.h"
|
||||
#include "libc/log/log.h"
|
||||
|
@ -92,6 +93,7 @@
|
|||
#include "libc/sysv/consts/sol.h"
|
||||
#include "libc/sysv/consts/tcp.h"
|
||||
#include "libc/sysv/consts/w.h"
|
||||
#include "libc/sysv/errfuns.h"
|
||||
#include "libc/testlib/testlib.h"
|
||||
#include "libc/time/time.h"
|
||||
#include "libc/x/x.h"
|
||||
|
@ -102,8 +104,10 @@
|
|||
#include "net/http/url.h"
|
||||
#include "net/https/https.h"
|
||||
#include "third_party/getopt/getopt.h"
|
||||
#include "third_party/linenoise/linenoise.h"
|
||||
#include "third_party/lua/cosmo.h"
|
||||
#include "third_party/lua/lauxlib.h"
|
||||
#include "third_party/lua/lrepl.h"
|
||||
#include "third_party/lua/ltests.h"
|
||||
#include "third_party/lua/lua.h"
|
||||
#include "third_party/lua/luaconf.h"
|
||||
|
@ -360,6 +364,7 @@ static bool hascontenttype;
|
|||
static bool sslclientverify;
|
||||
static bool connectionclose;
|
||||
static bool hasonworkerstop;
|
||||
static bool isexitingworker;
|
||||
static bool hasonworkerstart;
|
||||
static bool leakcrashreports;
|
||||
static bool hasonhttprequest;
|
||||
|
@ -408,6 +413,7 @@ static const char *brand;
|
|||
static char gzip_footer[8];
|
||||
static const char *pidpath;
|
||||
static const char *logpath;
|
||||
static const char *histpath;
|
||||
static struct pollfd *polls;
|
||||
static struct Strings loops;
|
||||
static size_t payloadlength;
|
||||
|
@ -453,7 +459,6 @@ static struct TlsBio g_bio;
|
|||
static char slashpath[PATH_MAX];
|
||||
static struct DeflateGenerator dg;
|
||||
|
||||
static wontreturn void ExitWorker(void);
|
||||
static char *Route(const char *, size_t, const char *, size_t);
|
||||
static char *RouteHost(const char *, size_t, const char *, size_t);
|
||||
static char *RoutePath(const char *, size_t);
|
||||
|
@ -6563,8 +6568,18 @@ static void CloseServerFds(void) {
|
|||
}
|
||||
}
|
||||
|
||||
static void HandleConnection(size_t i) {
|
||||
int pid;
|
||||
static int ExitWorker(void) {
|
||||
if (!IsModeDbg()) {
|
||||
_Exit(0);
|
||||
} else {
|
||||
isexitingworker = true;
|
||||
return eintr();
|
||||
}
|
||||
}
|
||||
|
||||
// returns 0 otherwise -1 if worker needs to unwind stack and exit
|
||||
static int HandleConnection(size_t i) {
|
||||
int pid, rc = 0;
|
||||
clientaddrsize = sizeof(clientaddr);
|
||||
if ((client = accept4(servers.p[i].fd, &clientaddr, &clientaddrsize,
|
||||
SOCK_CLOEXEC)) != -1) {
|
||||
|
@ -6572,7 +6587,7 @@ static void HandleConnection(size_t i) {
|
|||
messageshandled = 0;
|
||||
if (hasonclientconnection && LuaOnClientConnection()) {
|
||||
close(client);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
if (uniprocess) {
|
||||
pid = -1;
|
||||
|
@ -6591,7 +6606,7 @@ static void HandleConnection(size_t i) {
|
|||
break;
|
||||
case -1:
|
||||
HandleForkFailure();
|
||||
return;
|
||||
return 0;
|
||||
default:
|
||||
++shared->workers;
|
||||
close(client);
|
||||
|
@ -6599,7 +6614,7 @@ static void HandleConnection(size_t i) {
|
|||
if (hasonprocesscreate) {
|
||||
LuaOnProcessCreate(pid);
|
||||
}
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (!pid && !IsWindows()) {
|
||||
|
@ -6613,7 +6628,7 @@ static void HandleConnection(size_t i) {
|
|||
if (hasonworkerstop) {
|
||||
CallSimpleHook("OnWorkerStop");
|
||||
}
|
||||
ExitWorker();
|
||||
rc = ExitWorker();
|
||||
} else {
|
||||
close(client);
|
||||
oldin.p = 0;
|
||||
|
@ -6631,68 +6646,87 @@ static void HandleConnection(size_t i) {
|
|||
mbedtls_ssl_session_reset(&ssl);
|
||||
}
|
||||
#endif
|
||||
CollectGarbage();
|
||||
}
|
||||
} else if (errno == EINTR || errno == EAGAIN) {
|
||||
LockInc(&shared->c.acceptinterrupts);
|
||||
} else if (errno == ENFILE) {
|
||||
LockInc(&shared->c.enfiles);
|
||||
WARNF("(srvr) too many open files");
|
||||
meltdown = true;
|
||||
} else if (errno == EMFILE) {
|
||||
LockInc(&shared->c.emfiles);
|
||||
WARNF("(srvr) ran out of open file quota");
|
||||
meltdown = true;
|
||||
} else if (errno == ENOMEM) {
|
||||
LockInc(&shared->c.enomems);
|
||||
WARNF("(srvr) ran out of memory");
|
||||
meltdown = true;
|
||||
} else if (errno == ENOBUFS) {
|
||||
LockInc(&shared->c.enobufs);
|
||||
WARNF("(srvr) ran out of buffer");
|
||||
meltdown = true;
|
||||
} else if (errno == ENONET) {
|
||||
LockInc(&shared->c.enonets);
|
||||
WARNF("(srvr) %s network gone", DescribeServer());
|
||||
polls[i].fd = -polls[i].fd;
|
||||
} else if (errno == ENETDOWN) {
|
||||
LockInc(&shared->c.enetdowns);
|
||||
WARNF("(srvr) %s network down", DescribeServer());
|
||||
polls[i].fd = -polls[i].fd;
|
||||
} else if (errno == ECONNABORTED) {
|
||||
LockInc(&shared->c.acceptresets);
|
||||
WARNF("(srvr) %s connection reset before accept");
|
||||
} else if (errno == ENETUNREACH || errno == EHOSTUNREACH ||
|
||||
errno == EOPNOTSUPP || errno == ENOPROTOOPT || errno == EPROTO) {
|
||||
LockInc(&shared->c.accepterrors);
|
||||
WARNF("(srvr) %s ephemeral accept error: %m", DescribeServer());
|
||||
CollectGarbage();
|
||||
} else {
|
||||
DIEF("(srvr) %s accept error: %m", DescribeServer());
|
||||
if (errno == EINTR || errno == EAGAIN) {
|
||||
LockInc(&shared->c.acceptinterrupts);
|
||||
} else if (errno == ENFILE) {
|
||||
LockInc(&shared->c.enfiles);
|
||||
WARNF("(srvr) too many open files");
|
||||
meltdown = true;
|
||||
} else if (errno == EMFILE) {
|
||||
LockInc(&shared->c.emfiles);
|
||||
WARNF("(srvr) ran out of open file quota");
|
||||
meltdown = true;
|
||||
} else if (errno == ENOMEM) {
|
||||
LockInc(&shared->c.enomems);
|
||||
WARNF("(srvr) ran out of memory");
|
||||
meltdown = true;
|
||||
} else if (errno == ENOBUFS) {
|
||||
LockInc(&shared->c.enobufs);
|
||||
WARNF("(srvr) ran out of buffer");
|
||||
meltdown = true;
|
||||
} else if (errno == ENONET) {
|
||||
LockInc(&shared->c.enonets);
|
||||
WARNF("(srvr) %s network gone", DescribeServer());
|
||||
polls[i].fd = -polls[i].fd;
|
||||
} else if (errno == ENETDOWN) {
|
||||
LockInc(&shared->c.enetdowns);
|
||||
WARNF("(srvr) %s network down", DescribeServer());
|
||||
polls[i].fd = -polls[i].fd;
|
||||
} else if (errno == ECONNABORTED) {
|
||||
LockInc(&shared->c.acceptresets);
|
||||
WARNF("(srvr) %s connection reset before accept");
|
||||
} else if (errno == ENETUNREACH || errno == EHOSTUNREACH ||
|
||||
errno == EOPNOTSUPP || errno == ENOPROTOOPT || errno == EPROTO) {
|
||||
LockInc(&shared->c.accepterrors);
|
||||
WARNF("(srvr) %s ephemeral accept error: %m", DescribeServer());
|
||||
} else {
|
||||
DIEF("(srvr) %s accept error: %m", DescribeServer());
|
||||
}
|
||||
errno = 0;
|
||||
}
|
||||
errno = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void HandlePoll(void) {
|
||||
// returns 2 if we should stay in the redbean event loop
|
||||
// returns 1 if poll() says stdin has user input available
|
||||
// returns 0 if poll() timed out after ms
|
||||
// returns -1 if worker is unwinding exit
|
||||
static int HandlePoll(int ms) {
|
||||
size_t i;
|
||||
if (poll(polls, servers.n, HEARTBEAT) != -1) {
|
||||
int nfds;
|
||||
if ((nfds = poll(polls, 1 + servers.n, ms)) != -1) {
|
||||
for (i = 0; i < servers.n; ++i) {
|
||||
if (polls[i].revents) {
|
||||
if (polls[1 + i].revents) {
|
||||
serveraddr = &servers.p[i].addr;
|
||||
ishandlingconnection = true;
|
||||
HandleConnection(i);
|
||||
if (HandleConnection(i) == -1) return -1;
|
||||
ishandlingconnection = false;
|
||||
}
|
||||
}
|
||||
} else if (errno == EINTR || errno == EAGAIN) {
|
||||
LockInc(&shared->c.pollinterrupts);
|
||||
} else if (errno == ENOMEM) {
|
||||
LockInc(&shared->c.enomems);
|
||||
WARNF("(srvr) %s ran out of memory");
|
||||
meltdown = true;
|
||||
// are we polling stdin for the repl?
|
||||
if (polls[0].fd >= 0) {
|
||||
if (polls[0].revents) {
|
||||
return 1; // user entered a keystroke
|
||||
} else if (!nfds) {
|
||||
return 0; // let linenoise know it timed out
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DIEF("(srvr) poll error: %m");
|
||||
if (errno == EINTR || errno == EAGAIN) {
|
||||
LockInc(&shared->c.pollinterrupts);
|
||||
} else if (errno == ENOMEM) {
|
||||
LockInc(&shared->c.enomems);
|
||||
WARNF("(srvr) %s ran out of memory");
|
||||
meltdown = true;
|
||||
} else {
|
||||
DIEF("(srvr) poll error: %m");
|
||||
}
|
||||
errno = 0;
|
||||
}
|
||||
errno = 0;
|
||||
return 2;
|
||||
}
|
||||
|
||||
static void RestoreApe(void) {
|
||||
|
@ -6769,18 +6803,21 @@ static void Listen(void) {
|
|||
}
|
||||
}
|
||||
servers.n = n;
|
||||
polls = malloc(n * sizeof(*polls));
|
||||
polls = malloc((1 + n) * sizeof(*polls));
|
||||
polls[0].fd = -1;
|
||||
polls[0].events = POLLIN;
|
||||
polls[0].revents = 0;
|
||||
for (i = 0; i < n; ++i) {
|
||||
polls[i].fd = servers.p[i].fd;
|
||||
polls[i].events = POLLIN;
|
||||
polls[i].revents = 0;
|
||||
polls[1 + i].fd = servers.p[i].fd;
|
||||
polls[1 + i].events = POLLIN;
|
||||
polls[1 + i].revents = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void HandleShutdown(void) {
|
||||
CloseServerFds();
|
||||
INFOF("(srvr) received %s", strsignal(shutdownsig));
|
||||
if (shutdownsig == SIGTERM) {
|
||||
if (shutdownsig != SIGINT && shutdownsig != SIGQUIT) {
|
||||
if (!killed) terminated = false;
|
||||
INFOF("(srvr) killing process group");
|
||||
KillGroup();
|
||||
|
@ -6788,9 +6825,14 @@ static void HandleShutdown(void) {
|
|||
WaitAll();
|
||||
}
|
||||
|
||||
static void HandleEvents(void) {
|
||||
// this function coroutines with linenoise
|
||||
static int EventLoop(int fd, int ms) {
|
||||
int rc;
|
||||
long double t;
|
||||
rc = -1;
|
||||
polls[0].fd = 0;
|
||||
while (!terminated) {
|
||||
errno = 0;
|
||||
if (zombied) {
|
||||
ReapZombies();
|
||||
} else if (invalidated) {
|
||||
|
@ -6799,13 +6841,42 @@ static void HandleEvents(void) {
|
|||
} else if (meltdown) {
|
||||
EnterMeltdownMode();
|
||||
meltdown = false;
|
||||
} else if ((t = nowl()) - lastheartbeat > .5) {
|
||||
} else if ((t = nowl()) - lastheartbeat > HEARTBEAT / 1000.) {
|
||||
lastheartbeat = t;
|
||||
HandleHeartbeat();
|
||||
} else {
|
||||
HandlePoll();
|
||||
} else if ((rc = HandlePoll(ms)) != 2) {
|
||||
break; // return control to linenoise
|
||||
}
|
||||
}
|
||||
polls[0].fd = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void ReplEventLoop(void) {
|
||||
int status;
|
||||
lua_State *L = GL;
|
||||
__nomultics = 2;
|
||||
__replmode = true;
|
||||
lua_initrepl("redbean");
|
||||
linenoiseSetPollCallback(EventLoop);
|
||||
while ((status = lua_loadline(L)) != -1) {
|
||||
if (status == LUA_OK) {
|
||||
status = lua_runchunk(L, 0, LUA_MULTRET);
|
||||
}
|
||||
if (status == LUA_OK) {
|
||||
lua_l_print(L);
|
||||
} else {
|
||||
lua_report(L, status);
|
||||
}
|
||||
}
|
||||
if (!terminated && !isexitingworker) {
|
||||
OnTerm(SIGHUP); // eof event
|
||||
}
|
||||
lua_settop(L, 0); // clear stack
|
||||
lua_writeline();
|
||||
__replmode = false;
|
||||
__nomultics = 0;
|
||||
polls[0].fd = -1;
|
||||
}
|
||||
|
||||
static void SigInit(void) {
|
||||
|
@ -6917,16 +6988,6 @@ static void MemDestroy(void) {
|
|||
Free(&polls);
|
||||
}
|
||||
|
||||
static wontreturn void ExitWorker(void) {
|
||||
if (IsModeDbg()) {
|
||||
LuaDestroy();
|
||||
TlsDestroy();
|
||||
MemDestroy();
|
||||
CheckForMemoryLeaks();
|
||||
}
|
||||
_Exit(0);
|
||||
}
|
||||
|
||||
static void GetOpts(int argc, char *argv[]) {
|
||||
int opt;
|
||||
bool storeasset = false;
|
||||
|
@ -7032,9 +7093,19 @@ void RedBean(int argc, char *argv[]) {
|
|||
inbuf = inbuf_actual;
|
||||
isinitialized = true;
|
||||
CallSimpleHookIfDefined("OnServerStart");
|
||||
HandleEvents();
|
||||
HandleShutdown();
|
||||
CallSimpleHookIfDefined("OnServerStop");
|
||||
#ifdef STATIC
|
||||
EventLoop();
|
||||
#else
|
||||
if (isatty(0)) {
|
||||
ReplEventLoop();
|
||||
} else {
|
||||
EventLoop(-1, HEARTBEAT);
|
||||
}
|
||||
#endif
|
||||
if (!isexitingworker) {
|
||||
HandleShutdown();
|
||||
CallSimpleHookIfDefined("OnServerStop");
|
||||
}
|
||||
if (!IsTiny()) {
|
||||
LuaDestroy();
|
||||
TlsDestroy();
|
||||
|
|
Loading…
Add table
Reference in a new issue