mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-07-02 17:28:30 +00:00
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:
parent
968474d291
commit
5029e20bef
23 changed files with 408 additions and 209 deletions
261
third_party/linenoise/linenoise.c
vendored
261
third_party/linenoise/linenoise.c
vendored
|
@ -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) {
|
||||
|
|
13
third_party/python/Modules/getbuildinfo.c
vendored
13
third_party/python/Modules/getbuildinfo.c
vendored
|
@ -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 *
|
||||
|
|
1
third_party/python/Modules/getpath.c
vendored
1
third_party/python/Modules/getpath.c
vendored
|
@ -492,7 +492,6 @@ calculate_path(void)
|
|||
|
||||
if(IsWindows())
|
||||
{
|
||||
fprintf(stderr, "python APE on Windows\n");
|
||||
delimiter[0] = L';';
|
||||
separator[0] = L'\\';
|
||||
}
|
||||
|
|
80
third_party/python/Programs/python.c
vendored
80
third_party/python/Programs/python.c
vendored
|
@ -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 */
|
||||
|
|
1
third_party/python/Python/pylifecycle.c
vendored
1
third_party/python/Python/pylifecycle.c
vendored
|
@ -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"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue