Improve linenoise and get it working on Windows

Some progress has been made on introducing completion but there's been
difficulties using the Python C API to get local shell variables.
This commit is contained in:
Justine Tunney 2021-08-15 00:05:27 -07:00
parent 968474d291
commit 5029e20bef
23 changed files with 408 additions and 209 deletions

View file

@ -16,6 +16,7 @@
#include "libc/macros.internal.h"
#include "libc/nexgen32e/x86feature.h"
#include "libc/rand/rand.h"
#include "libc/rand/xorshift.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/ex.h"
@ -25,6 +26,12 @@
#include "libc/testlib/hyperion.h"
#include "third_party/getopt/getopt.h"
#define B 4096
bool isdone;
bool isbinary;
unsigned long count = -1;
uint64_t bcast(uint64_t f(void)) {
unsigned i;
uint64_t x;
@ -95,6 +102,28 @@ uint64_t vigna(void) {
return z ^ (z >> 31);
}
uint64_t rngset64(void) {
static unsigned i;
static uint64_t s;
if (!i) {
s = rand64();
i = (s + 1) & (511);
}
return MarsagliaXorshift64(&s);
}
uint64_t xorshift64(void) {
static uint64_t s = kMarsagliaXorshift64Seed;
return MarsagliaXorshift64(&s);
}
uint64_t xorshift32(void) {
static uint32_t s = kMarsagliaXorshift32Seed;
uint64_t a = MarsagliaXorshift32(&s);
uint64_t b = MarsagliaXorshift32(&s);
return (uint64_t)a << 32 | b;
}
uint64_t libc(void) {
uint64_t x;
CHECK_EQ(8, getrandom(&x, 8, 0));
@ -151,27 +180,26 @@ const struct Function {
const char *s;
uint64_t (*f)(void);
} kFunctions[] = {
{"ape", ape}, //
{"hardware", hardware}, //
{"inc", inc}, //
{"kernel", kernel}, //
{"knuth", knuth}, //
{"libc", libc}, //
{"moby", moby}, //
{"rand64", rand64}, //
{"rdrand", rdrnd}, //
{"rdrnd", rdrnd}, //
{"rdseed", rdseed}, //
{"unixv6", unixv6}, //
{"unixv7", unixv7}, //
{"vigna", vigna}, //
{"zero", zero}, //
{"ape", ape}, //
{"hardware", hardware}, //
{"inc", inc}, //
{"kernel", kernel}, //
{"knuth", knuth}, //
{"libc", libc}, //
{"moby", moby}, //
{"rand64", rand64}, //
{"rdrand", rdrnd}, //
{"rdrnd", rdrnd}, //
{"rdseed", rdseed}, //
{"rngset64", rngset64}, //
{"unixv6", unixv6}, //
{"unixv7", unixv7}, //
{"vigna", vigna}, //
{"xorshift32", xorshift32}, //
{"xorshift64", xorshift64}, //
{"zero", zero}, //
};
bool isdone;
bool isbinary;
unsigned long count = -1;
void OnInt(int sig) {
isdone = true;
}
@ -182,16 +210,19 @@ wontreturn void PrintUsage(FILE *f, int rc) {
}
int main(int argc, char *argv[]) {
char *p;
int i, opt;
ssize_t rc;
uint64_t x;
static char buf[B];
uint64_t (*f)(void);
while ((opt = getopt(argc, argv, "hbn:")) != -1) {
while ((opt = getopt(argc, argv, "hbc:n:")) != -1) {
switch (opt) {
case 'b':
isbinary = true;
break;
case 'c':
case 'n':
count = strtoul(optarg, 0, 0);
break;
@ -235,10 +266,23 @@ int main(int argc, char *argv[]) {
}
while (count && !isdone) {
x = f();
rc = write(1, &x, MIN(8, count));
if (count >= B) {
for (i = 0; i < B / 8; ++i) {
x = f();
p = buf + i * 8;
WRITE64LE(p, x);
}
for (i = 0; i < B; i += rc) {
rc = write(1, buf + i, B - i);
if (rc == -1 && errno == EPIPE) exit(1);
if (rc == -1) perror("write"), exit(1);
}
} else {
x = f();
rc = write(1, &x, MIN(8, count));
}
if (!rc) break;
if (rc == -1 && errno == EPIPE) return 1;
if (rc == -1 && errno == EPIPE) exit(1);
if (rc == -1) perror("write"), exit(1);
count -= rc;
}

View file

@ -267,8 +267,12 @@ int editorReadKey(int64_t fd) {
if (read(fd, seq + 2, 1) == 0) return CTRL('[');
if (seq[2] == '~') {
switch (seq[1]) {
case '1':
return HOME_KEY;
case '3':
return DEL_KEY;
case '4':
return END_KEY;
case '5':
return PAGE_UP;
case '6':

View file

@ -41,7 +41,8 @@ textwindows int ioctl_tcgets_nt(int ignored, struct termios *tio) {
if (inmode & kNtEnableProcessedInput) tio->c_lflag |= IEXTEN | ISIG;
}
if (outok) {
if (inmode & kNtEnableProcessedInput) tio->c_lflag |= IEXTEN | ISIG;
if (outmode & kNtEnableProcessedOutput) tio->c_oflag |= OPOST;
if (!(outmode & kNtDisableNewlineAutoReturn)) tio->c_oflag |= ONLCR;
}
return 0;
} else {

View file

@ -51,8 +51,8 @@ textwindows int ioctl_tcsets_nt(int ignored, uint64_t request,
}
if (outok) {
outmode |= kNtEnableWrapAtEolOutput;
outmode |= kNtEnableProcessedOutput;
if (!(tio->c_oflag & OPOST)) outmode |= kNtDisableNewlineAutoReturn;
if (tio->c_oflag & OPOST) outmode |= kNtEnableProcessedOutput;
if (!(tio->c_oflag & ONLCR)) outmode |= kNtDisableNewlineAutoReturn;
if (NtGetVersion() >= kNtVersionWindows10) {
outmode |= kNtEnableVirtualTerminalProcessing;
}

View file

@ -102,6 +102,7 @@ ssize_t readansi(int fd, char *buf, size_t size) {
break;
case kCsi:
switch (c) {
case '[':
case ':':
case ';':
case '<':

View file

@ -19,6 +19,7 @@
#include "libc/bits/bits.h"
#include "libc/dce.h"
#include "libc/log/backtrace.internal.h"
#include "libc/log/internal.h"
#include "libc/log/log.h"
/**
@ -29,6 +30,7 @@
relegated wontreturn void __die(void) {
static bool once;
if (cmpxchg(&once, false, true)) {
__restore_tty();
if (IsDebuggerPresent(false)) DebugBreak();
ShowBacktrace(2, NULL);
}

View file

@ -2,16 +2,19 @@
#define COSMOPOLITAN_LIBC_LOG_INTERNAL_H_
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/siginfo.h"
#include "libc/calls/struct/termios.h"
#include "libc/calls/ucontext.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
extern int kCrashSigs[8] hidden;
extern struct termios g_oldtermios hidden;
extern struct sigaction g_oldcrashacts[8] hidden;
void __start_fatal(const char *, int) hidden;
void __start_fatal_ndebug(void) hidden;
void __oncrash(int, struct siginfo *, struct ucontext *) relegated;
void __restore_tty(void);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

45
libc/log/oldtermios.c Normal file
View file

@ -0,0 +1,45 @@
/*-*- 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/calls/calls.h"
#include "libc/calls/termios.h"
#include "libc/log/internal.h"
#include "libc/str/str.h"
#define SHOW_CURSOR "\e[?25h"
#define DISABLE_MOUSE "\e[?1000;1002;1015;1006l"
#define ANSI_RESTORE SHOW_CURSOR DISABLE_MOUSE
struct termios g_oldtermios;
static textstartup void g_oldtermios_init() {
if (isatty(1)) {
tcgetattr(1, &g_oldtermios);
}
}
const void *const g_oldtermios_ctor[] initarray = {
g_oldtermios_init,
};
void __restore_tty(void) {
if (isatty(1)) {
write(1, ANSI_RESTORE, strlen(ANSI_RESTORE));
tcsetattr(1, TCSAFLUSH, &g_oldtermios);
}
}

View file

@ -20,7 +20,9 @@
#include "libc/calls/sigbits.h"
#include "libc/calls/struct/sigaction.h"
#include "libc/calls/struct/siginfo.h"
#include "libc/calls/struct/termios.h"
#include "libc/calls/struct/utsname.h"
#include "libc/calls/termios.h"
#include "libc/calls/ucontext.h"
#include "libc/dce.h"
#include "libc/errno.h"
@ -41,6 +43,7 @@
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/consts/o.h"
#include "libc/sysv/consts/sig.h"
#include "libc/sysv/consts/termios.h"
/**
* @fileoverview Abnormal termination handling & GUI debugging.
@ -262,6 +265,7 @@ relegated void __oncrash(int sig, struct siginfo *si, ucontext_t *ctx) {
: 0);
}
if (gdbpid > 0 && (sig == SIGTRAP || sig == SIGQUIT)) return;
__restore_tty();
ShowCrashReport(err, STDERR_FILENO, sig, si, ctx);
exit(128 + sig);
unreachable;

View file

@ -20,6 +20,7 @@
#include "libc/bits/bits.h"
#include "libc/calls/calls.h"
#include "libc/calls/sigbits.h"
#include "libc/calls/termios.h"
#include "libc/calls/typedef/sigaction_f.h"
#include "libc/dce.h"
#include "libc/log/check.h"

View file

@ -32,6 +32,7 @@
relegated void __start_fatal(const char *file, int line) {
bool colorful;
char s[16 + 16 + 16 + 16 + PATH_MAX + 16 + NAME_MAX + 16], *p = s;
__restore_tty();
colorful = cancolor();
*p++ = '\r';
if (colorful) p = stpcpy(p, "\e[J\e[30;101m");

View file

@ -29,6 +29,7 @@
*/
relegated void __start_fatal_ndebug(void) {
char s[16 + 16 + 16 + 16 + PATH_MAX + 16], *p = s;
__restore_tty();
*p++ = '\r';
if (cancolor()) p = stpcpy(p, "\e[J");
p = stpcpy(p, "error:");

View file

@ -45,10 +45,11 @@ static bool have_getrandom;
* This random number seed generator blends information from:
*
* - getrandom() on Linux
* - RtlGenRandom() on Windows
* - getentropy() on XNU and OpenBSD
* - sysctl(KERN_ARND) on FreeBSD and NetBSD
* - RDSEED on Broadwell+ and Xen+ unless GRND_NORDRND
* - RDRAND on Ivybridge+ and Xen+ unless GRND_NORDRND|GRND_RANDOM
* - RDRAND on Ivybridge+ and Xen+ unless GRND_NORDRND
*
* The following flags may be specified:
*
@ -75,7 +76,9 @@ ssize_t getrandom(void *p, size_t n, unsigned f) {
sigset_t neu, old;
if (n > 256) n = 256;
if (!IsTiny() &&
(f & ~(GRND_RANDOM | GRND_NONBLOCK | GRND_NORDRND | GRND_NOSYSTEM))) {
((f & ~(GRND_RANDOM | GRND_NONBLOCK | GRND_NORDRND | GRND_NOSYSTEM)) ||
(f & (GRND_NORDRND | GRND_NOSYSTEM)) ==
(GRND_NORDRND | GRND_NOSYSTEM))) {
return einval();
}
if (!(f & GRND_NOSYSTEM)) {

View file

@ -23,8 +23,8 @@
/**
* Fills memory with random bytes, e.g.
*
* char buf[1024];
* rngset(buf, sizeof(buf), rand64, -1);
* char buf[1024];
* rngset(buf, sizeof(buf), rand64, -1);
*
* @param seed can be rand64() and is always called at least once
* @param reseed is bytes between seed() calls and -1 disables it

View file

@ -53,6 +53,7 @@ void *xunbinga(size_t, const char16_t *) attributeallocalign((1)) _XMAL _XRET;
void *xunbing(const char16_t *) _XMAL _XRET;
char16_t *utf8toutf16(const char *, size_t, size_t *) nodiscard;
char *utf16toutf8(const char16_t *, size_t, size_t *) nodiscard;
char *xhomedir(void) nodiscard;
/*───────────────────────────────────────────────────────────────────────────│─╗
cosmopolitan § eXtended apis » files

43
libc/x/xhomedir.c Normal file
View file

@ -0,0 +1,43 @@
/*-*- 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/dce.h"
#include "libc/runtime/runtime.h"
#include "libc/x/x.h"
/**
* Returns home directory.
*/
char *xhomedir(void) {
int fd;
const char *a, *b;
if ((a = getenv("HOME"))) {
b = "";
} else if (IsWindows()) {
a = getenv("HOMEDRIVE");
b = getenv("HOMEPATH");
if (!a || !b) {
a = "C:";
b = "";
}
} else {
a = ".";
b = "";
}
return xasprintf("%s%s", a, b);
}

View file

@ -15,6 +15,7 @@
Copyright (c) 2010-2016, Salvatore Sanfilippo <antirez at gmail dot com>
Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com>
Copyright (c) 2018-2020, Justine Tunney <jtunney at gmail dot com>
All rights reserved.
@ -43,67 +44,22 @@
Todo list:
- Filter bogus Ctrl+<char> combinations.
- Win32 support
CHANGES
Bloat: [say what?!] MUST DO:
- History search like Ctrl+r in readline?
- Remove bell
- Windows support
- Filters out unsupported control sequences
- Filters out Thompson-Pike input sequences
List of escape sequences used by this program, we do everything just
with three sequences. In order to be so cheap we may have some
flickering effect with some slow terminal, but the lesser sequences
the more compatible.
TODO
EL (Erase Line)
Sequence: ESC [ n K
Effect: if n is 0 or missing, clear from cursor to end of line
Effect: if n is 1, clear from beginning of line to cursor
Effect: if n is 2, clear entire line
CUF (CUrsor Forward)
Sequence: ESC [ n C
Effect: moves cursor forward n chars
CUB (CUrsor Backward)
Sequence: ESC [ n D
Effect: moves cursor backward n chars
The following is used to get the terminal width if getting
the width with the TIOCGWINSZ ioctl fails
DSR (Device Status Report)
Sequence: ESC [ 6 n
Effect: reports the current cusor position as ESC [ n ; m R
where n is the row and m is the column
When multi line mode is enabled, we also use an additional escape
sequence. However multi line editing is disabled by default.
CUU (Cursor Up)
Sequence: ESC [ n A
Effect: moves cursor up of n chars.
CUD (Cursor Down)
Sequence: ESC [ n B
Effect: moves cursor down of n chars.
When linenoiseClearScreen() is called, two additional escape sequences
are used in order to clear the screen and position the cursor at home
position.
CUP (Cursor position)
Sequence: ESC [ H
Effect: moves the cursor to upper left corner
ED (Erase display)
Sequence: ESC [ 2 J
Effect: clear the whole screen
- Kill ring
- History search
- Thompson-Pike Encoding
REFERENCE
the big scary coding you 𝘮𝘶𝘴𝘵 use curses to abstract
The big scary coding you 𝘮𝘶𝘴𝘵 use curses to abstract.
\t TAB
\a BELL
@ -221,6 +177,7 @@
*/
#include "libc/calls/calls.h"
#include "libc/calls/termios.h"
#include "libc/calls/ttydefaults.h"
#include "libc/calls/weirdtypes.h"
#include "libc/errno.h"
#include "libc/fmt/fmt.h"
@ -267,28 +224,6 @@ struct linenoiseState {
int history_index; /* The history index we are currently editing. */
};
enum KEY_ACTION{
KEY_NULL = 0, /* NULL */
CTRL_A = 1, /* Ctrl+a */
CTRL_B = 2, /* Ctrl-b */
CTRL_C = 3, /* Ctrl-c */
CTRL_D = 4, /* Ctrl-d */
CTRL_E = 5, /* Ctrl-e */
CTRL_F = 6, /* Ctrl-f */
CTRL_H = 8, /* Ctrl-h */
TAB = 9, /* Tab */
CTRL_K = 11, /* Ctrl+k */
CTRL_L = 12, /* Ctrl+l */
ENTER = 13, /* Enter */
CTRL_N = 14, /* Ctrl-n */
CTRL_P = 16, /* Ctrl-p */
CTRL_T = 20, /* Ctrl-t */
CTRL_U = 21, /* Ctrl+u */
CTRL_W = 23, /* Ctrl+w */
ESC = 27, /* Escape */
BACKSPACE = 127 /* Backspace */
};
static void linenoiseAtExit(void);
static void refreshLine(struct linenoiseState *);
@ -362,7 +297,7 @@ static int enableRawMode(int fd) {
raw.c_oflag &= ~(OPOST);
/* control modes - set 8 bit chars */
raw.c_cflag |= (CS8);
/* local modes - choing off, canonical off, no extended functions,
/* local modes - echoing off, canonical off, no extended functions,
* no signal chars (^Z,^C) */
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN);
/* control chars - set return condition: min number of bytes and timer.
@ -391,7 +326,7 @@ static int getCursorPosition(int ifd, int ofd) {
int cols, rows;
unsigned int i = 0;
/* Report cursor location */
if (write(ofd, "\x1b[6n", 4) != 4) return -1;
if (write(ofd, "\e[6n", 4) != 4) return -1;
/* Read the response: ESC [ rows ; cols R */
while (i < sizeof(buf)-1) {
if (read(ifd,buf+i,1) != 1) break;
@ -400,7 +335,7 @@ static int getCursorPosition(int ifd, int ofd) {
}
buf[i] = '\0';
/* Parse it. */
if (buf[0] != ESC || buf[1] != '[') return -1;
if (buf[0] != '\e' || buf[1] != '[') return -1;
if (sscanf(buf+2,"%d;%d",&rows,&cols) != 2) return -1;
return cols;
}
@ -416,13 +351,13 @@ static int getColumns(int ifd, int ofd) {
start = getCursorPosition(ifd,ofd);
if (start == -1) goto failed;
/* Go to right margin and get position. */
if (write(ofd,"\x1b[999C",6) != 6) goto failed;
if (write(ofd,"\e[999C",6) != 6) goto failed;
cols = getCursorPosition(ifd,ofd);
if (cols == -1) goto failed;
/* Restore position. */
if (cols > start) {
char seq[32];
snprintf(seq,32,"\x1b[%dD",cols-start);
snprintf(seq,32,"\e[%dD",cols-start);
if (write(ofd,seq,strlen(seq)) == -1) {
/* Can't recover... */
}
@ -437,7 +372,7 @@ failed:
/* Clear the screen. Used to handle ctrl+l */
void linenoiseClearScreen(void) {
if (write(STDOUT_FILENO,"\x1b[H\x1b[2J",7) <= 0) {
if (write(STDOUT_FILENO,"\e[H\e[2J",7) <= 0) {
/* nothing to do, just to avoid warning. */
}
}
@ -445,7 +380,8 @@ void linenoiseClearScreen(void) {
/* Beep, used for completion when there is nothing to complete or when all
* the choices were already shown. */
static void linenoiseBeep(void) {
fprintf(stderr, "\x7");
/* NOOOO */
/* fprintf(stderr, "\x7"); */
fflush(stderr);
}
@ -494,11 +430,11 @@ static int completeLine(struct linenoiseState *ls) {
return -1;
}
switch(c) {
case 9: /* tab */
case '\t':
i = (i+1) % (lc.len+1);
if (i == lc.len) linenoiseBeep();
break;
case 27: /* escape */
case '\e':
/* Re-show original buffer */
if (i < lc.len) refreshLine(ls);
stop = 1;
@ -595,13 +531,13 @@ void refreshShowHints(struct abuf *ab, struct linenoiseState *l, int plen) {
if (hintlen > hintmaxlen) hintlen = hintmaxlen;
if (bold == 1 && color == -1) color = 37;
if (color != -1 || bold != 0)
snprintf(seq,64,"\033[%d;%d;49m",bold,color);
snprintf(seq,64,"\e[%d;%d;49m",bold,color);
else
seq[0] = '\0';
abAppend(ab,seq,strlen(seq));
abAppend(ab,hint,hintlen);
if (color != -1 || bold != 0)
abAppend(ab,"\033[0m",4);
abAppend(ab,"\e[0m",4);
/* Call the function to free the hint returned. */
if (freeHintsCallback) freeHintsCallback(hint);
}
@ -642,10 +578,10 @@ static void refreshSingleLine(struct linenoiseState *l) {
/* Show hits if any. */
refreshShowHints(&ab,l,plen);
/* Erase to right */
snprintf(seq,64,"\x1b[0K");
snprintf(seq,64,"\e[0K");
abAppend(&ab,seq,strlen(seq));
/* Move cursor to original position. */
snprintf(seq,64,"\r\x1b[%dC", (int)(pos+plen));
snprintf(seq,64,"\r\e[%dC", (int)(pos+plen));
abAppend(&ab,seq,strlen(seq));
if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */
abFree(&ab);
@ -672,18 +608,18 @@ static void refreshMultiLine(struct linenoiseState *l) {
abInit(&ab);
if (old_rows-rpos > 0) {
lndebug("go down %d", old_rows-rpos);
snprintf(seq,64,"\x1b[%dB", old_rows-rpos);
snprintf(seq,64,"\e[%dB", old_rows-rpos);
abAppend(&ab,seq,strlen(seq));
}
/* Now for every row clear it, go up. */
for (j = 0; j < old_rows-1; j++) {
lndebug("clear+up");
snprintf(seq,64,"\r\x1b[0K\x1b[1A");
snprintf(seq,64,"\r\e[0K\e[1A");
abAppend(&ab,seq,strlen(seq));
}
/* Clean the top line. */
lndebug("clear");
snprintf(seq,64,"\r\x1b[0K");
snprintf(seq,64,"\r\e[0K");
abAppend(&ab,seq,strlen(seq));
/* Write the prompt and the current buffer content */
abAppend(&ab,l->prompt,strlen(l->prompt));
@ -714,14 +650,14 @@ static void refreshMultiLine(struct linenoiseState *l) {
/* Go up till we reach the expected positon. */
if (rows-rpos2 > 0) {
lndebug("go-up %d", rows-rpos2);
snprintf(seq,64,"\x1b[%dA", rows-rpos2);
snprintf(seq,64,"\e[%dA", rows-rpos2);
abAppend(&ab,seq,strlen(seq));
}
/* Set column. */
col = (plen+(int)l->pos) % (int)l->cols;
lndebug("set col %d", 1+col);
if (col)
snprintf(seq,64,"\r\x1b[%dC", col);
snprintf(seq,64,"\r\e[%dC", col);
else
snprintf(seq,64,"\r");
abAppend(&ab,seq,strlen(seq));
@ -778,6 +714,28 @@ void linenoiseEditMoveLeft(struct linenoiseState *l) {
}
}
/* Move cursor on the left. */
void linenoiseEditMoveLeftWord(struct linenoiseState *l) {
if (l->pos > 0) {
while (l->pos > 0 && l->buf[l->pos-1] == ' ')
l->pos--;
while (l->pos > 0 && l->buf[l->pos-1] != ' ')
l->pos--;
refreshLine(l);
}
}
/* Move cursor on the right. */
void linenoiseEditMoveRightWord(struct linenoiseState *l) {
if (l->pos != l->len) {
while (l->pos < l->len && l->buf[l->pos] == ' ')
l->pos++;
while (l->pos < l->len && l->buf[l->pos] != ' ')
l->pos++;
refreshLine(l);
}
}
/* Move cursor on the right. */
void linenoiseEditMoveRight(struct linenoiseState *l) {
if (l->pos != l->len) {
@ -896,15 +854,17 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen,
linenoiseHistoryAdd("");
if (write(l.ofd,prompt,l.plen) == -1) return -1;
while(1) {
char c;
int i;
int c;
int nread;
char seq[3];
nread = read(l.ifd,&c,1);
char seq[32];
nread = readansi(l.ifd,seq,sizeof(seq));
if (nread <= 0) return l.len;
c = seq[0];
/* Only autocomplete when the callback is set. It returns < 0 when
* there was an error reading from fd. Otherwise it will return the
* character that should be handled next. */
if (c == 9 && completionCallback != NULL) {
if (c == '\t' && completionCallback) {
c = completeLine(&l);
/* Return on errors */
if (c < 0) return l.len;
@ -912,7 +872,7 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen,
if (c == 0) continue;
}
switch(c) {
case ENTER: /* enter */
case '\r': /* enter */
history_len--;
free(history[history_len]);
if (mlmode) linenoiseEditMoveEnd(&l);
@ -925,15 +885,15 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen,
hintsCallback = hc;
}
return (int)l.len;
case CTRL_C: /* ctrl-c */
case CTRL('C'):
errno = EAGAIN;
return -1;
case BACKSPACE: /* backspace */
case 8: /* ctrl-h */
case CTRL('?'): /* backspace a.k.a. 0177 a.k.a. 127 */
case CTRL('H'):
linenoiseEditBackspace(&l);
break;
case CTRL_D: /* ctrl-d, remove char at right of cursor, or if the
line is empty, act as end-of-file. */
case CTRL('D'): /* remove char at right of cursor, or if the
line is empty, act as end-of-file. */
if (l.len > 0) {
linenoiseEditDelete(&l);
} else {
@ -942,7 +902,7 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen,
return -1;
}
break;
case CTRL_T: /* ctrl-t, swaps current character with previous. */
case CTRL('T'): /* swaps current character with previous. */
if (l.pos > 0 && l.pos < l.len) {
int aux = buf[l.pos-1];
buf[l.pos-1] = buf[l.pos];
@ -951,95 +911,110 @@ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen,
refreshLine(&l);
}
break;
case CTRL_B: /* ctrl-b */
case CTRL('B'):
linenoiseEditMoveLeft(&l);
break;
case CTRL_F: /* ctrl-f */
case CTRL('F'):
linenoiseEditMoveRight(&l);
break;
case CTRL_P: /* ctrl-p */
case CTRL('P'):
linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV);
break;
case CTRL_N: /* ctrl-n */
case CTRL('N'):
linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT);
break;
case ESC: /* escape sequence */
case '\e': /* escape sequence */
/* Read the next two bytes representing the escape sequence.
* Use two calls to handle slow terminals returning the two
* chars at different times. */
if (read(l.ifd,seq,1) == -1) break;
if (read(l.ifd,seq+1,1) == -1) break;
/* ESC [ sequences. */
if (seq[0] == '[') {
if (seq[1] >= '0' && seq[1] <= '9') {
/* Extended escape, read additional byte. */
if (read(l.ifd,seq+2,1) == -1) break;
if (seq[2] == '~') {
switch(seq[1]) {
case '3': /* Delete key. */
if (nread < 2) break;
if (seq[1] == '[') {
if (nread < 3) break;
if (seq[2] >= '0' && seq[2] <= '9') {
if (nread < 4) break;
if (seq[3] == '~') {
switch(seq[2]) {
case '1': /* "\e[1~" is home */
linenoiseEditMoveHome(&l);
break;
case '3': /* "\e[3~" is delete */
linenoiseEditDelete(&l);
break;
case '4': /* "\e[4~" is end */
linenoiseEditMoveEnd(&l);
break;
}
}
} else {
switch(seq[1]) {
case 'A': /* Up */
switch(seq[2]) {
case 'A': /* "\e[A" is up */
linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_PREV);
break;
case 'B': /* Down */
case 'B': /* "\e[B" is down */
linenoiseEditHistoryNext(&l, LINENOISE_HISTORY_NEXT);
break;
case 'C': /* Right */
case 'C': /* "\e[C" is right */
linenoiseEditMoveRight(&l);
break;
case 'D': /* Left */
case 'D': /* "\e[D" is left */
linenoiseEditMoveLeft(&l);
break;
case 'H': /* Home */
case 'H': /* "\e[H" is home */
linenoiseEditMoveHome(&l);
break;
case 'F': /* End*/
case 'F': /* "\e[F" is end */
linenoiseEditMoveEnd(&l);
break;
}
}
}
/* ESC O sequences. */
else if (seq[0] == 'O') {
switch(seq[1]) {
case 'H': /* Home */
else if (seq[1] == 'O') {
if (nread < 3) break;
switch(seq[2]) {
case 'H': /* "\eOH" is home */
linenoiseEditMoveHome(&l);
break;
case 'F': /* End*/
case 'F': /* "\eOF" is end */
linenoiseEditMoveEnd(&l);
break;
}
}
else if (seq[1] == 'b') { /* "\eb" is alt-b */
linenoiseEditMoveLeftWord(&l);
} else if (seq[1] == 'f') { /* "\ef" is alt-f */
linenoiseEditMoveRightWord(&l);
}
break;
default:
if (linenoiseEditInsert(&l,c)) return -1;
if (32 <= seq[0] && seq[0] < 127) {
if (linenoiseEditInsert(&l,seq[0])==-1) {
return -1;
}
}
break;
case CTRL_U: /* Ctrl+u, delete the whole line. */
case CTRL('U'): /* delete the whole line */
/* TODO(jart): delete backwards */
buf[0] = '\0';
l.pos = l.len = 0;
refreshLine(&l);
break;
case CTRL_K: /* Ctrl+k, delete from current to end of line. */
case CTRL('K'): /* delete from current to end of line */
/* TODO(jart): add to kill ring */
buf[l.pos] = '\0';
l.len = l.pos;
refreshLine(&l);
break;
case CTRL_A: /* Ctrl+a, go to the start of the line */
case CTRL('A'): /* go to the start of the line */
linenoiseEditMoveHome(&l);
break;
case CTRL_E: /* ctrl+e, go to the end of the line */
case CTRL('E'): /* go to the end of the line */
linenoiseEditMoveEnd(&l);
break;
case CTRL_L: /* ctrl+l, clear screen */
case CTRL('L'): /* clear screen */
linenoiseClearScreen();
refreshLine(&l);
break;
case CTRL_W: /* ctrl+w, delete previous word */
case CTRL('W'): /* delete previous word */
linenoiseEditDeletePrevWord(&l);
break;
}
@ -1083,7 +1058,7 @@ static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
if (enableRawMode(STDIN_FILENO) == -1) return -1;
count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt);
linenoiseDisableRawMode(STDIN_FILENO);
printf("\n");
if (count != -1) printf("\n");
return count;
}
@ -1154,8 +1129,6 @@ void linenoiseFree(void *ptr) {
free(ptr);
}
/* ================================ History ================================= */
/* Free the history, but does not reset it. Only used when we have to
* exit() to avoid memory leaks are reported by valgrind & co. */
static void freeHistory(void) {

View file

@ -38,18 +38,7 @@
const char *
Py_GetBuildInfo(void)
{
static char buildinfo[50 + sizeof(GITVERSION) +
((sizeof(GITTAG) > sizeof(GITBRANCH)) ?
sizeof(GITTAG) : sizeof(GITBRANCH))];
const char *revision = _Py_gitversion();
const char *sep = *revision ? ":" : "";
const char *gitid = _Py_gitidentifier();
if (!(*gitid))
gitid = "default";
PyOS_snprintf(buildinfo, sizeof(buildinfo),
"%s%s%s, %.20s, %.9s", gitid, sep, revision,
DATE, TIME);
return buildinfo;
return "cosmopolitan";
}
const char *

View file

@ -492,7 +492,6 @@ calculate_path(void)
if(IsWindows())
{
fprintf(stderr, "python APE on Windows\n");
delimiter[0] = L';';
separator[0] = L'\\';
}

View file

@ -7,17 +7,24 @@
#include "libc/calls/calls.h"
#include "libc/errno.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/fileno.h"
#include "libc/sysv/consts/sig.h"
#include "libc/unicode/locale.h"
#include "third_party/linenoise/linenoise.h"
#include "third_party/python/Include/ceval.h"
#include "third_party/python/Include/dictobject.h"
#include "third_party/python/Include/fileutils.h"
#include "third_party/python/Include/import.h"
#include "third_party/python/Include/pylifecycle.h"
#include "third_party/python/Include/pymem.h"
#include "third_party/python/Include/pyport.h"
#include "third_party/python/Include/pythonrun.h"
#include "third_party/python/Include/unicodeobject.h"
/* clang-format off */
static jmp_buf jbuf;
@ -28,6 +35,77 @@ OnKeyboardInterrupt(int sig)
longjmp(jbuf, 1);
}
static PyObject *
GetMember(const char *s, Py_ssize_t n, PyObject *o)
{
const char *t;
PyObject *k, *v;
Py_ssize_t i, m;
if (!o) return 0;
for (i = 0; PyDict_Next(o, &i, &k, &v);) {
if (v != Py_None && PyUnicode_Check(k)) {
t = PyUnicode_AsUTF8AndSize(k, &m);
printf("\r%`'.*s vs. %`'.*s\n", n, s, m, t);
if (n == m && !memcmp(s, t, n)) {
Py_INCREF(v);
return v;
}
}
}
return 0;
}
static PyObject *
GetVar(const char *s, Py_ssize_t n)
{
PyObject *o;
/*
* TODO: Why doesn't PyEval_GetLocals() work?
*/
if ((o = GetMember(s, n, PyEval_GetLocals()))) return o;
if ((o = GetMember(s, n, PyEval_GetGlobals()))) return o;
if ((o = GetMember(s, n, PyEval_GetBuiltins()))) return o;
return 0;
}
static void
TerminalComplete(const char *s, linenoiseCompletions *c, PyObject *o)
{
const char *t;
PyObject *k, *v;
Py_ssize_t i, n, m;
if (!o) return;
for (n = strlen(s), i = 0; PyDict_Next(o, &i, &k, &v);) {
if (v != Py_None && PyUnicode_Check(k)) {
t = PyUnicode_AsUTF8AndSize(k, &m);
if (m > n && !memcmp(t, s, n)) {
c->cvec = realloc(c->cvec, ++c->len * sizeof(*c->cvec));
c->cvec[c->len - 1] = strdup(t);
}
}
}
}
static void
TerminalCompletion(const char *s, linenoiseCompletions *c)
{
const char *p;
PyObject *o, *q;
if ((p = strchr(s, '.'))) {
if (!(o = GetVar(s, p - s))) return;
for (s = p + 1; (p = strchr(s, '.')); o = q) {
if ((q = GetMember(s, p - s, o))) return;
Py_DECREF(o);
}
TerminalComplete(s, c, o);
Py_DECREF(o);
} else {
TerminalComplete(s, c, PyEval_GetLocals());
TerminalComplete(s, c, PyEval_GetGlobals());
TerminalComplete(s, c, PyEval_GetBuiltins());
}
}
char *
TerminalReadline(FILE *sys_stdin, FILE *sys_stdout, const char *prompt)
{
@ -66,6 +144,8 @@ main(int argc, char **argv)
int i, res;
char *oldloc;
showcrashreports();
linenoiseSetCompletionCallback(TerminalCompletion);
PyOS_ReadlineFunctionPointer = TerminalReadline;
/* Force malloc() allocator to bootstrap Python */

View file

@ -14,6 +14,7 @@
#include "libc/log/log.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/sysv/consts/sig.h"
#include "libc/unicode/locale.h"
#include "third_party/python/Include/Python-ast.h"
#include "third_party/python/Include/abstract.h"

View file

@ -16,15 +16,11 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/calls/struct/stat.h"
#include "libc/dce.h"
#include "libc/fmt/fmt.h"
#include "libc/log/check.h"
#include "libc/mem/mem.h"
#include "libc/runtime/runtime.h"
#include "libc/stdio/stdio.h"
#include "libc/runtime/gc.internal.h"
#include "libc/sysv/consts/o.h"
#include "libc/x/x.h"
#include "tool/build/lib/psk.h"
/**
@ -32,23 +28,9 @@
*/
void *GetRunitPsk(void) {
int fd;
char *r, *p;
struct stat st;
const char *a, *b;
char *r, p[PATH_MAX + 1];
if ((a = getenv("HOME"))) {
b = "";
} else if (IsWindows()) {
a = getenv("HOMEDRIVE");
b = getenv("HOMEPATH");
if (!a || !b) {
a = "C:";
b = "";
}
} else {
fprintf(stderr, "need $HOME\n");
exit(1);
}
snprintf(p, sizeof(p), "%s%s/.runit.psk", a, b);
p = gc(xasprintf("%s/.runit.psk", gc(xhomedir())));
if (stat(p, &st) == -1 || st.st_size != 32) {
fprintf(stderr, "need o//examples/getrandom.com -bn32 >~/.runit.psk\n");
exit(1);

View file

@ -1216,6 +1216,27 @@ static optimizesize void ReadKeyboard(void) {
case 'S': /* \eOS is F4 */
pf4_ = !pf4_;
break;
case 'T': /* \eOT is F5 */
pf5_ = !pf5_;
break;
case 'U': /* \eOU is F6 */
pf6_ = !pf6_;
break;
case 'V': /* \eOV is F7 */
pf7_ = !pf7_;
break;
case 'W': /* \eOW is F8 */
pf8_ = !pf8_;
break;
case 'Y': /* \eOY is F10 */
pf10_ = !pf10_;
break;
case 'Z': /* \eOZ is F11 */
pf11_ = !pf11_;
break;
case '[': /* \eO[ is F12 */
pf12_ = !pf12_;
break;
default:
break;
}