cosmopolitan/tool/build/lib/pty.c

1375 lines
36 KiB
C
Raw Normal View History

/*-*- 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 2020 Justine Alexandra Roberts Tunney
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA
*/
#include "libc/alg/arraylist2.h"
#include "libc/assert.h"
#include "libc/bits/bits.h"
#include "libc/bits/safemacros.h"
#include "libc/conv/conv.h"
#include "libc/conv/itoa.h"
#include "libc/log/check.h"
#include "libc/log/log.h"
#include "libc/macros.h"
#include "libc/mem/mem.h"
#include "libc/nexgen32e/bsr.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/str/thompike.h"
#include "libc/str/tpenc.h"
2020-10-27 10:39:46 +00:00
#include "libc/sysv/errfuns.h"
#include "libc/unicode/unicode.h"
#include "libc/x/x.h"
#include "tool/build/lib/pty.h"
/**
* @fileoverview Pseudoteletypewriter
*
* \t TAB
* \a BELL
* \r CURSOR START
* \b CURSOR LEFT
* \177 CURSOR LEFT
* \n CURSOR DOWN AND START IF OPOST
* \f CURSOR DOWN AND START IF OPOST
* \v CURSOR DOWN AND START IF OPOST
* \eD CURSOR DOWN AND START
* \eE CURSOR DOWN
* \eM CURSOR UP
* \ec FULL RESET
* \e7 SAVE CURSOR POSITION
* \e8 RESTORE CURSOR POSITION
* \e(0 DEC SPECIAL GRAPHICS
* \e(B USAS X3.4-1967
* \e#5 SINGLE WIDTH
* \e#6 DOUBLE WIDTH
* \e#8 SO MANY E
* \eZ \e/Z REPORT IDENTITY
* \e[𝑛A CURSOR UP [CLAMPED]
* \e[𝑛B CURSOR DOWN [CLAMPED]
* \e[𝑛C CURSOR RIGHT [CLAMPED]
* \e[𝑛D CURSOR LEFT [CLAMPED]
* \e[𝑦;𝑥H SET CURSOR POSITION [CLAMPED]
* \e[𝑥G SET CURSOR COLUMN [CLAMPED]
* \e[𝑦d SET CURSOR ROW [CLAMPED]
* \e[𝑛E CURSOR DOWN AND START [CLAMPED]
* \e[𝑛F CURSOR UP AND START [CLAMPED]
* \e[𝑛S SCROLL UP
* \e[𝑛T SCROLL DOWN
* \e[𝑛@ INSERT CELLS
* \e[𝑛P DELETE CELLS
* \e[𝑛L INSERT LINES
* \e[𝑛M DELETE LINES
* \e[J ERASE DISPLAY FORWARDS
* \e[1J ERASE DISPLAY BACKWARDS
* \e[2J ERASE DISPLAY
* \e[K ERASE LINE FORWARD
* \e[1K ERASE LINE BACKWARD
* \e[2K ERASE LINE
* \e[𝑛X ERASE CELLS
* \e[0m RESET
* \e[1m BOLD
* \e[2m FAINT
* \e[3m ITALIC
* \e[4m UNDER
* \e[5m BLINK
* \e[7m INVERT
* \e[8m CONCEAL
* \e[9m STRIKE
* \e[20m FRAKTUR
* \e[21m DUNDER
* \e[22m RESET BOLD & FAINT
* \e[23m RESET ITALIC & FRAKTUR
* \e[24m RESET UNDER & DUNDER
* \e[25m RESET BLINK
* \e[27m RESET INVERT
* \e[28m RESET CONCEAL
* \e[29m RESET STRIKE
* \e[39m RESET FOREGROUND
* \e[49m RESET BACKGROUND
* \e[30m BLACK FOREGROUND
* \e[31m RED FOREGROUND
* \e[32m GREEN FOREGROUND
* \e[33m YELLOW FOREGROUND
* \e[34m BLUE FOREGROUND
* \e[35m MAGENTA FOREGROUND
* \e[36m CYAN FOREGROUND
* \e[37m WHITE FOREGROUND
* \e[40m BLACK BACKGROUND
* \e[41m RED BACKGROUND
* \e[42m GREEN BACKGROUND
* \e[44m YELLOW BACKGROUND
* \e[44m BLUE BACKGROUND
* \e[45m MAGENTA BACKGROUND
* \e[46m CYAN BACKGROUND
* \e[47m WHITE BACKGROUND
* \e[90m BRIGHT BLACK FOREGROUND
* \e[91m BRIGHT RED FOREGROUND
* \e[92m BRIGHT GREEN FOREGROUND
* \e[93m BRIGHT YELLOW FOREGROUND
* \e[94m BRIGHT BLUE FOREGROUND
* \e[95m BRIGHT MAGENTA FOREGROUND
* \e[96m BRIGHT CYAN FOREGROUND
* \e[97m BRIGHT WHITE FOREGROUND
* \e[100m BRIGHT BLACK BACKGROUND
* \e[101m BRIGHT RED BACKGROUND
* \e[102m BRIGHT GREEN BACKGROUND
* \e[103m BRIGHT YELLOW BACKGROUND
* \e[104m BRIGHT BLUE BACKGROUND
* \e[105m BRIGHT MAGENTA BACKGROUND
* \e[106m BRIGHT CYAN BACKGROUND
* \e[107m BRIGHT WHITE BACKGROUND
* \e[38;5;𝑥m XTERM256 FOREGROUND
* \e[48;5;𝑥m XTERM256 BACKGROUND
* \e[38;2;𝑟;𝑔;𝑏m RGB FOREGROUND
* \e[48;2;𝑟;𝑔;𝑏m RGB BACKGROUND
* \e[?25h SHOW CURSOR
* \e[?25l HIDE CURSOR
* \e[s SAVE CURSOR POSITION
* \e[u RESTORE CURSOR POSITION
* \e[0q RESET LEDS
* \e[1q TURN ON FIRST LED
* \e[2q TURN ON SECOND LED
* \e[3q TURN ON THIRD LED
* \e[4q TURN ON FOURTH LED
* \e[c \e[?1;0c REPORT DEVICE TYPE
* \e[5n \e[0n REPORT DEVICE STATUS
* \e[6n \e[𝑦;𝑥R REPORT CURSOR POSITION
* \e7\e[9979;9979H\e[6n\e8 \e[𝑦;𝑥R REPORT DISPLAY DIMENSIONS
*
* @see https://vt100.net/docs/vt220-rm/chapter4.html
* @see https://invisible-island.net/xterm/
* @see ANSI X3.64-1979
* @see ISO/IEC 6429
* @see FIPS-86
* @see ECMA-48
*/
2020-10-27 10:39:46 +00:00
struct Pty *NewPty(void) {
struct Pty *pty;
pty = xcalloc(1, sizeof(struct Pty));
PtyResize(pty, 25, 80);
PtyFullReset(pty);
PtyErase(pty, 0, pty->yn * pty->xn);
return pty;
}
2020-10-27 10:39:46 +00:00
static void FreePtyPlanes(struct Pty *pty) {
free(pty->wcs);
free(pty->fgs);
free(pty->bgs);
free(pty->prs);
}
2020-10-27 10:39:46 +00:00
void FreePty(struct Pty *pty) {
if (pty) {
2020-10-27 10:39:46 +00:00
FreePtyPlanes(pty);
free(pty);
}
}
2020-10-27 10:39:46 +00:00
static wchar_t *GetXlatAscii(void) {
unsigned i;
static bool once;
static wchar_t xlat[128];
if (!once) {
for (i = 0; i < 128; ++i) {
xlat[i] = i;
}
once = true;
}
return xlat;
}
2020-10-27 10:39:46 +00:00
static wchar_t *GetXlatLineDrawing(void) {
unsigned i;
static bool once;
static wchar_t xlat[128];
if (!once) {
for (i = 0; i < 128; ++i) {
if (0x5F <= i && i <= 0x7E) {
xlat[i] = u" ◆▒␉␌␍␊°±␤␋┘┐┌└┼⎺⎻─⎼⎽├┤┴┬│≤≥π≠£·"[i - 0x5F];
} else {
xlat[i] = i;
}
}
once = true;
}
return xlat;
}
2020-10-27 10:39:46 +00:00
static void XlatAlphabet(wchar_t xlat[128], int a, int b) {
unsigned i;
for (i = 0; i < 128; ++i) {
if ('a' <= i && i <= 'z') {
xlat[i] = i - 'a' + a;
} else if ('A' <= i && i <= 'Z') {
xlat[i] = i - 'A' + b;
} else {
xlat[i] = i;
}
}
}
2020-10-27 10:39:46 +00:00
static wchar_t *GetXlatItalic(void) {
static bool once;
static wchar_t xlat[128];
if (!once) {
2020-10-27 10:39:46 +00:00
XlatAlphabet(xlat, L'𝑎', L'𝐴');
once = true;
}
return xlat;
}
2020-10-27 10:39:46 +00:00
static wchar_t *GetXlatBoldItalic(void) {
static bool once;
static wchar_t xlat[128];
if (!once) {
2020-10-27 10:39:46 +00:00
XlatAlphabet(xlat, L'𝒂', L'𝑨');
once = true;
}
return xlat;
}
2020-10-27 10:39:46 +00:00
static wchar_t *GetXlatBoldFraktur(void) {
static bool once;
static wchar_t xlat[128];
if (!once) {
2020-10-27 10:39:46 +00:00
XlatAlphabet(xlat, L'𝖆', L'𝕬');
once = true;
}
return xlat;
}
2020-10-27 10:39:46 +00:00
static wchar_t *GetXlatFraktur(void) {
unsigned i;
static bool once;
static wchar_t xlat[128];
if (!once) {
for (i = 0; i < ARRAYLEN(xlat); ++i) {
if ('A' <= i && i <= 'Z') {
xlat[i] = L"𝔄𝔅𝔇𝔈𝔉𝔊𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔𝔖𝔗𝔘𝔙𝔚𝔛𝔜"[i - 'A'];
} else if ('a' <= i && i <= 'z') {
xlat[i] = i - 'a' + L'𝔞';
} else {
xlat[i] = i;
}
}
once = true;
}
return xlat;
}
2020-10-27 10:39:46 +00:00
static wchar_t *GetXlatDoubleWidth(void) {
unsigned i;
static bool once;
static wchar_t xlat[128];
if (!once) {
for (i = 0; i < ARRAYLEN(xlat); ++i) {
if ('!' <= i && i <= '~') {
xlat[i] = -(i - '!' + L'');
} else {
xlat[i] = i;
}
}
once = true;
}
return xlat;
}
2020-10-27 10:39:46 +00:00
static wchar_t *GetXlatSgr(struct Pty *pty) {
switch (!!(pty->pr & kPtyFraktur) << 2 | !!(pty->pr & kPtyItalic) << 1 |
!!(pty->pr & kPtyBold) << 0) {
case 0b100:
case 0b110:
2020-10-27 10:39:46 +00:00
return GetXlatFraktur();
case 0b101:
case 0b111:
2020-10-27 10:39:46 +00:00
return GetXlatBoldFraktur();
case 0b011:
2020-10-27 10:39:46 +00:00
return GetXlatBoldItalic();
case 0b010:
2020-10-27 10:39:46 +00:00
return GetXlatItalic();
default:
2020-10-27 10:39:46 +00:00
return GetXlatAscii();
}
}
2020-10-27 10:39:46 +00:00
static void PtySetXlat(struct Pty *pty, wchar_t *xlat) {
pty->xlat = xlat;
2020-10-27 10:39:46 +00:00
pty->pr &= ~(kPtyItalic | kPtyFraktur);
}
2020-10-27 10:39:46 +00:00
static void PtySetCodepage(struct Pty *pty, char id) {
unsigned i;
switch (id) {
default:
case 'B':
2020-10-27 10:39:46 +00:00
PtySetXlat(pty, GetXlatAscii());
break;
case '0':
2020-10-27 10:39:46 +00:00
PtySetXlat(pty, GetXlatLineDrawing());
break;
}
}
2020-10-27 10:39:46 +00:00
void PtyErase(struct Pty *pty, long dst, long n) {
DCHECK_LE(dst + n, pty->yn * pty->xn);
wmemset((void *)(pty->wcs + dst), ' ', n);
wmemset((void *)(pty->prs + dst), 0, n);
}
2020-10-27 10:39:46 +00:00
void PtyMemmove(struct Pty *pty, long dst, long src, long n) {
DCHECK_LE(src + n, pty->yn * pty->xn);
DCHECK_LE(dst + n, pty->yn * pty->xn);
memmove(pty->wcs + dst, pty->wcs + src, n * 4);
memmove(pty->fgs + dst, pty->fgs + src, n * 4);
memmove(pty->bgs + dst, pty->bgs + src, n * 4);
memmove(pty->prs + dst, pty->prs + src, n * 4);
}
2020-10-27 10:39:46 +00:00
void PtyFullReset(struct Pty *pty) {
pty->y = 0;
pty->x = 0;
pty->pr = 0;
pty->u8 = 0;
pty->n8 = 0;
pty->conf = 0;
pty->save = 0;
pty->state = 0;
pty->esc.i = 0;
pty->input.i = 0;
2020-10-27 10:39:46 +00:00
pty->xlat = GetXlatAscii();
PtyErase(pty, 0, pty->yn * pty->xn);
}
2020-10-27 10:39:46 +00:00
void PtySetY(struct Pty *pty, int y) {
pty->conf &= ~kPtyRedzone;
pty->y = MAX(0, MIN(pty->yn - 1, y));
}
2020-10-27 10:39:46 +00:00
void PtySetX(struct Pty *pty, int x) {
pty->conf &= ~kPtyRedzone;
pty->x = MAX(0, MIN(pty->xn - 1, x));
}
2020-10-27 10:39:46 +00:00
void PtyResize(struct Pty *pty, int yn, int xn) {
unsigned y, ym, xm, y0;
uint32_t *wcs, *fgs, *bgs, *prs;
if (xn < 80) xn = 80;
if (yn < 25) yn = 25;
if (xn == pty->xn && yn == pty->yn) return;
wcs = xcalloc(yn * xn, 4);
fgs = xcalloc(yn * xn, 4);
bgs = xcalloc(yn * xn, 4);
prs = xcalloc(yn * xn, 4);
y0 = yn > pty->y + 1 ? 0 : pty->y + 1 - yn;
ym = MIN(yn, pty->yn) + y0;
xm = MIN(xn, pty->xn);
for (y = y0; y < ym; ++y) {
memcpy(wcs + y * xn, pty->wcs + y * pty->xn, xm * 4);
memcpy(fgs + y * xn, pty->fgs + y * pty->xn, xm * 4);
memcpy(bgs + y * xn, pty->bgs + y * pty->xn, xm * 4);
memcpy(prs + y * xn, pty->prs + y * pty->xn, xm * 4);
}
2020-10-27 10:39:46 +00:00
FreePtyPlanes(pty);
pty->wcs = wcs;
pty->fgs = fgs;
pty->bgs = bgs;
pty->prs = prs;
pty->yn = yn;
pty->xn = xn;
2020-10-27 10:39:46 +00:00
PtySetY(pty, pty->y);
PtySetX(pty, pty->x);
}
2020-10-27 10:39:46 +00:00
static void PtyConcatInput(struct Pty *pty, const char *data, size_t n) {
CONCAT(&pty->input.p, &pty->input.i, &pty->input.n, data, n);
}
2020-10-27 10:39:46 +00:00
static void PtyScroll(struct Pty *pty) {
PtyMemmove(pty, 0, pty->xn, pty->xn * (pty->yn - 1));
PtyErase(pty, pty->xn * (pty->yn - 1), pty->xn);
}
2020-10-27 10:39:46 +00:00
static void PtyReverse(struct Pty *pty) {
PtyMemmove(pty, pty->xn, 0, pty->xn * (pty->yn - 1));
PtyErase(pty, 0, pty->xn);
}
2020-10-27 10:39:46 +00:00
static void PtyIndex(struct Pty *pty) {
if (pty->y < pty->yn - 1) {
++pty->y;
} else {
2020-10-27 10:39:46 +00:00
PtyScroll(pty);
}
}
2020-10-27 10:39:46 +00:00
static void PtyReverseIndex(struct Pty *pty) {
if (pty->y) {
--pty->y;
} else {
2020-10-27 10:39:46 +00:00
PtyReverse(pty);
}
}
2020-10-27 10:39:46 +00:00
static void PtyCarriageReturn(struct Pty *pty) {
PtySetX(pty, 0);
}
2020-10-27 10:39:46 +00:00
static void PtyNewline(struct Pty *pty) {
PtyIndex(pty);
if (!(pty->conf & kPtyNoopost)) {
PtyCarriageReturn(pty);
}
}
2020-10-27 10:39:46 +00:00
static void PtyAdvance(struct Pty *pty) {
DCHECK_EQ(pty->xn - 1, pty->x);
2020-10-27 10:39:46 +00:00
DCHECK(pty->conf & kPtyRedzone);
pty->conf &= ~kPtyRedzone;
pty->x = 0;
if (pty->y < pty->yn - 1) {
++pty->y;
} else {
2020-10-27 10:39:46 +00:00
PtyScroll(pty);
}
}
2020-10-27 10:39:46 +00:00
static void PtyWriteGlyph(struct Pty *pty, wint_t wc, int w) {
uint32_t i;
if (w < 1) wc = ' ', w = 1;
2020-10-27 10:39:46 +00:00
if ((pty->conf & kPtyRedzone) || pty->x + w > pty->xn) {
PtyAdvance(pty);
}
i = pty->y * pty->xn + pty->x;
pty->wcs[i] = wc;
2020-10-27 10:39:46 +00:00
if ((pty->prs[i] = pty->pr) & (kPtyFg | kPtyBg)) {
pty->fgs[i] = pty->fg;
pty->bgs[i] = pty->bg;
}
if ((pty->x += w) >= pty->xn) {
pty->x = pty->xn - 1;
2020-10-27 10:39:46 +00:00
pty->conf |= kPtyRedzone;
}
}
2020-10-27 10:39:46 +00:00
static void PtyWriteTab(struct Pty *pty) {
unsigned x, x2;
2020-10-27 10:39:46 +00:00
if (pty->conf & kPtyRedzone) {
PtyAdvance(pty);
}
x2 = MIN(pty->xn, ROUNDUP(pty->x + 1, 8));
for (x = pty->x; x < x2; ++x) {
pty->wcs[pty->y * pty->xn + x] = ' ';
}
if (x2 < pty->xn) {
pty->x = x2;
} else {
pty->x = pty->xn - 1;
2020-10-27 10:39:46 +00:00
pty->conf |= kPtyRedzone;
}
}
2020-10-27 10:39:46 +00:00
int PtyAtoi(const char *s, const char **e) {
int i;
for (i = 0; isdigit(*s); ++s) i *= 10, i += *s - '0';
if (e) *e = s;
return i;
}
2020-10-27 10:39:46 +00:00
static int PtyGetMoveParam(struct Pty *pty) {
int x = PtyAtoi(pty->esc.s, NULL);
if (x < 1) x = 1;
return x;
}
2020-10-27 10:39:46 +00:00
static void PtySetCursorPosition(struct Pty *pty) {
int row, col;
const char *s = pty->esc.s;
2020-10-27 10:39:46 +00:00
row = max(1, PtyAtoi(s, &s));
if (*s == ';') ++s;
2020-10-27 10:39:46 +00:00
col = max(1, PtyAtoi(s, &s));
PtySetY(pty, row - 1);
PtySetX(pty, col - 1);
}
2020-10-27 10:39:46 +00:00
static void PtySetCursorRow(struct Pty *pty) {
PtySetY(pty, PtyGetMoveParam(pty) - 1);
}
2020-10-27 10:39:46 +00:00
static void PtySetCursorColumn(struct Pty *pty) {
PtySetX(pty, PtyGetMoveParam(pty) - 1);
}
2020-10-27 10:39:46 +00:00
static void PtyMoveCursor(struct Pty *pty, int dy, int dx) {
int n = PtyGetMoveParam(pty);
PtySetY(pty, pty->y + dy * n);
PtySetX(pty, pty->x + dx * n);
}
2020-10-27 10:39:46 +00:00
static void PtyScrollUp(struct Pty *pty) {
int n = PtyGetMoveParam(pty);
while (n--) PtyScroll(pty);
}
2020-10-27 10:39:46 +00:00
static void PtyScrollDown(struct Pty *pty) {
int n = PtyGetMoveParam(pty);
while (n--) PtyReverse(pty);
}
2020-10-27 10:39:46 +00:00
static void PtySetCursorStatus(struct Pty *pty, bool status) {
if (status) {
2020-10-27 10:39:46 +00:00
pty->conf &= ~kPtyNocursor;
} else {
2020-10-27 10:39:46 +00:00
pty->conf |= kPtyNocursor;
}
}
2020-10-27 10:39:46 +00:00
static void PtySetMode(struct Pty *pty, bool status) {
const char *p = pty->esc.s;
switch (*p++) {
case '?':
while (isdigit(*p)) {
2020-10-27 10:39:46 +00:00
switch (PtyAtoi(p, &p)) {
case 25:
2020-10-27 10:39:46 +00:00
PtySetCursorStatus(pty, status);
break;
default:
break;
}
if (*p == ';') {
++p;
}
}
break;
default:
break;
}
}
2020-10-27 10:39:46 +00:00
static void PtySaveCursorPosition(struct Pty *pty) {
pty->save = (pty->y & 0x7FFF) | (pty->x & 0x7FFF) << 16;
}
2020-10-27 10:39:46 +00:00
static void PtyRestoreCursorPosition(struct Pty *pty) {
PtySetY(pty, (pty->save & 0x00007FFF) >> 000);
PtySetX(pty, (pty->save & 0x7FFF0000) >> 020);
}
2020-10-27 10:39:46 +00:00
static void PtyEraseDisplay(struct Pty *pty) {
switch (PtyAtoi(pty->esc.s, NULL)) {
case 0:
2020-10-27 10:39:46 +00:00
PtyErase(pty, pty->y * pty->xn + pty->x,
pty->yn * pty->xn - (pty->y * pty->xn + pty->x));
break;
case 1:
2020-10-27 10:39:46 +00:00
PtyErase(pty, 0, pty->y * pty->xn + pty->x);
break;
case 2:
case 3:
2020-10-27 10:39:46 +00:00
PtyErase(pty, 0, pty->yn * pty->xn);
break;
default:
break;
}
}
2020-10-27 10:39:46 +00:00
static void PtyEraseLine(struct Pty *pty) {
switch (PtyAtoi(pty->esc.s, NULL)) {
case 0:
2020-10-27 10:39:46 +00:00
PtyErase(pty, pty->y * pty->xn + pty->x, pty->xn - pty->x);
break;
case 1:
2020-10-27 10:39:46 +00:00
PtyErase(pty, pty->y * pty->xn, pty->x);
break;
case 2:
2020-10-27 10:39:46 +00:00
PtyErase(pty, pty->y * pty->xn, pty->xn);
break;
default:
break;
}
}
2020-10-27 10:39:46 +00:00
static void PtyEraseCells(struct Pty *pty) {
int i, n, x;
i = pty->y * pty->xn + pty->x;
n = pty->yn * pty->xn;
2020-10-27 10:39:46 +00:00
x = min(max(PtyAtoi(pty->esc.s, NULL), 1), n - i);
PtyErase(pty, i, x);
}
2020-10-27 10:39:46 +00:00
static int PtyArg1(struct Pty *pty) {
return max(1, PtyAtoi(pty->esc.s, NULL));
}
2020-10-27 10:39:46 +00:00
static void PtyInsertCells(struct Pty *pty) {
int n = min(pty->xn - pty->x, PtyArg1(pty));
PtyMemmove(pty, pty->y * pty->xn + pty->x + n, pty->y * pty->xn + pty->x,
pty->xn - (pty->x + n));
PtyErase(pty, pty->y * pty->xn + pty->x, n);
}
2020-10-27 10:39:46 +00:00
static void PtyInsertLines(struct Pty *pty) {
int n = min(pty->yn - pty->y, PtyArg1(pty));
PtyMemmove(pty, (pty->y + n) * pty->xn, pty->y * pty->xn,
(pty->yn - pty->y - n) * pty->xn);
PtyErase(pty, pty->y * pty->xn, n * pty->xn);
}
2020-10-27 10:39:46 +00:00
static void PtyDeleteCells(struct Pty *pty) {
int n = min(pty->xn - pty->x, PtyArg1(pty));
PtyMemmove(pty, pty->y * pty->xn + pty->x, pty->y * pty->xn + pty->x + n,
pty->xn - (pty->x + n));
PtyErase(pty, pty->y * pty->xn + pty->x, n);
}
2020-10-27 10:39:46 +00:00
static void PtyDeleteLines(struct Pty *pty) {
int n = min(pty->yn - pty->y, PtyArg1(pty));
PtyMemmove(pty, pty->y * pty->xn, (pty->y + n) * pty->xn,
(pty->yn - pty->y - n) * pty->xn);
PtyErase(pty, (pty->y + n) * pty->xn, n * pty->xn);
}
2020-10-27 10:39:46 +00:00
static void PtyReportDeviceStatus(struct Pty *pty) {
PtyWriteInput(pty, "\e[0n", 4);
}
2020-10-27 10:39:46 +00:00
static void PtyReportPreferredVtType(struct Pty *pty) {
PtyWriteInput(pty, "\e[?1;0c", 4);
}
2020-10-27 10:39:46 +00:00
static void PtyReportPreferredVtIdentity(struct Pty *pty) {
PtyWriteInput(pty, "\e/Z", 4);
}
2020-10-27 10:39:46 +00:00
static void PtyBell(struct Pty *pty) {
pty->conf |= kPtyBell;
}
2020-10-27 10:39:46 +00:00
static void PtyLed(struct Pty *pty) {
switch (PtyAtoi(pty->esc.s, NULL)) {
case 0:
2020-10-27 10:39:46 +00:00
pty->conf &= ~kPtyLed1;
pty->conf &= ~kPtyLed2;
pty->conf &= ~kPtyLed3;
pty->conf &= ~kPtyLed4;
break;
case 1:
2020-10-27 10:39:46 +00:00
pty->conf |= kPtyLed1;
break;
case 2:
2020-10-27 10:39:46 +00:00
pty->conf |= kPtyLed2;
break;
case 3:
2020-10-27 10:39:46 +00:00
pty->conf |= kPtyLed3;
break;
case 4:
2020-10-27 10:39:46 +00:00
pty->conf |= kPtyLed4;
break;
default:
break;
}
}
2020-10-27 10:39:46 +00:00
static void PtyReportCursorPosition(struct Pty *pty) {
char *p;
char buf[2 + 10 + 1 + 10 + 1];
p = buf;
*p++ = '\e';
*p++ = '[';
p += uint64toarray_radix10((pty->y + 1) & 0x7fff, p);
*p++ = ';';
p += uint64toarray_radix10((pty->x + 1) & 0x7fff, p);
*p++ = 'R';
2020-10-27 10:39:46 +00:00
PtyWriteInput(pty, buf, p - buf);
}
2020-10-27 10:39:46 +00:00
static void PtyCsiN(struct Pty *pty) {
switch (PtyAtoi(pty->esc.s, NULL)) {
case 5:
2020-10-27 10:39:46 +00:00
PtyReportDeviceStatus(pty);
break;
case 6:
2020-10-27 10:39:46 +00:00
PtyReportCursorPosition(pty);
break;
default:
break;
}
}
2020-10-27 10:39:46 +00:00
static void PtySelectGraphicsRendition(struct Pty *pty) {
char *p, c;
unsigned x;
uint8_t code[4];
enum {
kSgr,
kSgrFg = 010,
kSgrFgTrue = 012,
kSgrFgXterm = 015,
kSgrBg = 020,
kSgrBgTrue = 022,
kSgrBgXterm = 025,
} t;
x = 0;
t = kSgr;
p = pty->esc.s;
memset(code, 0, sizeof(code));
for (;;) {
c = *p++;
switch (c) {
case '\0':
return;
case '0' ... '9':
x *= 10;
x += c - '0';
break;
case ';':
case 'm':
code[code[3]] = x;
x = 0;
switch (t) {
case kSgr:
switch (code[0]) {
case 38:
t = kSgrFg;
break;
case 48:
t = kSgrBg;
break;
case 0:
pty->pr = 0;
2020-10-27 10:39:46 +00:00
pty->xlat = GetXlatSgr(pty);
break;
case 1:
2020-10-27 10:39:46 +00:00
pty->pr |= kPtyBold;
pty->xlat = GetXlatSgr(pty);
break;
case 2:
2020-10-27 10:39:46 +00:00
pty->pr |= kPtyFaint;
break;
case 3:
2020-10-27 10:39:46 +00:00
pty->pr |= kPtyItalic;
pty->xlat = GetXlatSgr(pty);
break;
case 4:
2020-10-27 10:39:46 +00:00
pty->pr |= kPtyUnder;
break;
case 5:
2020-10-27 10:39:46 +00:00
pty->pr |= kPtyBlink;
break;
case 7:
2020-10-27 10:39:46 +00:00
pty->pr |= kPtyFlip;
break;
case 8:
2020-10-27 10:39:46 +00:00
pty->pr |= kPtyConceal;
break;
case 9:
2020-10-27 10:39:46 +00:00
pty->pr |= kPtyStrike;
break;
case 20:
2020-10-27 10:39:46 +00:00
pty->pr |= kPtyFraktur;
pty->xlat = GetXlatSgr(pty);
break;
case 21:
2020-10-27 10:39:46 +00:00
pty->pr |= kPtyUnder | kPtyDunder;
break;
case 22:
2020-10-27 10:39:46 +00:00
pty->pr &= ~(kPtyFaint | kPtyBold);
pty->xlat = GetXlatSgr(pty);
break;
case 23:
2020-10-27 10:39:46 +00:00
pty->pr &= ~kPtyItalic;
pty->xlat = GetXlatSgr(pty);
break;
case 24:
2020-10-27 10:39:46 +00:00
pty->pr &= ~(kPtyUnder | kPtyDunder);
break;
case 25:
2020-10-27 10:39:46 +00:00
pty->pr &= ~kPtyBlink;
break;
case 27:
2020-10-27 10:39:46 +00:00
pty->pr &= ~kPtyFlip;
break;
case 28:
2020-10-27 10:39:46 +00:00
pty->pr &= ~kPtyConceal;
break;
case 29:
2020-10-27 10:39:46 +00:00
pty->pr &= ~kPtyStrike;
break;
case 39:
2020-10-27 10:39:46 +00:00
pty->pr &= ~kPtyFg;
break;
case 49:
2020-10-27 10:39:46 +00:00
pty->pr &= ~kPtyBg;
break;
case 90 ... 97:
code[0] -= 90 - 30;
code[0] += 8;
/* fallthrough */
case 30 ... 37:
pty->fg = code[0] - 30;
2020-10-27 10:39:46 +00:00
pty->pr |= kPtyFg;
pty->pr &= ~kPtyTrue;
break;
case 100 ... 107:
code[0] -= 100 - 40;
code[0] += 8;
/* fallthrough */
case 40 ... 47:
pty->bg = code[0] - 40;
2020-10-27 10:39:46 +00:00
pty->pr |= kPtyBg;
pty->pr &= ~kPtyTrue;
break;
default:
break;
}
break;
case kSgrFg:
case kSgrBg:
switch (code[0]) {
case 2:
case 5:
t += code[0];
break;
default:
t = kSgr;
break;
}
break;
case kSgrFgTrue:
if (++code[3] == 3) {
code[3] = 0;
t = kSgr;
pty->fg = READ32LE(code);
2020-10-27 10:39:46 +00:00
pty->pr |= kPtyFg;
pty->pr |= kPtyTrue;
}
break;
case kSgrBgTrue:
if (++code[3] == 3) {
code[3] = 0;
t = kSgr;
pty->bg = READ32LE(code);
2020-10-27 10:39:46 +00:00
pty->pr |= kPtyBg;
pty->pr |= kPtyTrue;
}
break;
case kSgrFgXterm:
t = kSgr;
pty->fg = code[0];
2020-10-27 10:39:46 +00:00
pty->pr |= kPtyFg;
pty->pr &= ~kPtyTrue;
break;
case kSgrBgXterm:
t = kSgr;
pty->bg = code[0];
2020-10-27 10:39:46 +00:00
pty->pr |= kPtyBg;
pty->pr &= ~kPtyTrue;
break;
default:
abort();
}
break;
default:
break;
}
}
}
2020-10-27 10:39:46 +00:00
static void PtyCsi(struct Pty *pty) {
switch (pty->esc.s[pty->esc.i - 1]) {
case 'f':
case 'H':
2020-10-27 10:39:46 +00:00
PtySetCursorPosition(pty);
break;
case 'G':
2020-10-27 10:39:46 +00:00
PtySetCursorColumn(pty);
break;
case 'd':
2020-10-27 10:39:46 +00:00
PtySetCursorRow(pty);
break;
case 'F':
pty->x = 0;
/* fallthrough */
case 'A':
2020-10-27 10:39:46 +00:00
PtyMoveCursor(pty, -1, +0);
break;
case 'E':
pty->x = 0;
/* fallthrough */
case 'B':
2020-10-27 10:39:46 +00:00
PtyMoveCursor(pty, +1, +0);
break;
case 'C':
2020-10-27 10:39:46 +00:00
PtyMoveCursor(pty, +0, +1);
break;
case 'D':
2020-10-27 10:39:46 +00:00
PtyMoveCursor(pty, +0, -1);
break;
case 'S':
2020-10-27 10:39:46 +00:00
PtyScrollUp(pty);
break;
case 'T':
2020-10-27 10:39:46 +00:00
PtyScrollDown(pty);
break;
case '@':
2020-10-27 10:39:46 +00:00
PtyInsertCells(pty);
break;
case 'P':
2020-10-27 10:39:46 +00:00
PtyDeleteCells(pty);
break;
case 'L':
2020-10-27 10:39:46 +00:00
PtyInsertLines(pty);
break;
case 'M':
2020-10-27 10:39:46 +00:00
PtyDeleteLines(pty);
break;
case 'J':
2020-10-27 10:39:46 +00:00
PtyEraseDisplay(pty);
break;
case 'K':
2020-10-27 10:39:46 +00:00
PtyEraseLine(pty);
break;
case 'X':
2020-10-27 10:39:46 +00:00
PtyEraseCells(pty);
break;
case 's':
2020-10-27 10:39:46 +00:00
PtySaveCursorPosition(pty);
break;
case 'u':
2020-10-27 10:39:46 +00:00
PtyRestoreCursorPosition(pty);
break;
case 'n':
2020-10-27 10:39:46 +00:00
PtyCsiN(pty);
break;
case 'm':
2020-10-27 10:39:46 +00:00
PtySelectGraphicsRendition(pty);
break;
case 'h':
2020-10-27 10:39:46 +00:00
PtySetMode(pty, true);
break;
case 'l':
2020-10-27 10:39:46 +00:00
PtySetMode(pty, false);
break;
case 'c':
2020-10-27 10:39:46 +00:00
PtyReportPreferredVtType(pty);
break;
case 'q':
2020-10-27 10:39:46 +00:00
PtyLed(pty);
break;
default:
break;
}
}
2020-10-27 10:39:46 +00:00
static void PtyScreenAlignmentDisplay(struct Pty *pty) {
wmemset((void *)pty->wcs, 'E', pty->yn * pty->xn);
}
2020-10-27 10:39:46 +00:00
static void PtyEscHash(struct Pty *pty) {
switch (pty->esc.s[1]) {
case '5':
2020-10-27 10:39:46 +00:00
PtySetXlat(pty, GetXlatAscii());
break;
case '6':
2020-10-27 10:39:46 +00:00
PtySetXlat(pty, GetXlatDoubleWidth());
break;
case '8':
2020-10-27 10:39:46 +00:00
PtyScreenAlignmentDisplay(pty);
break;
default:
break;
}
}
2020-10-27 10:39:46 +00:00
static void PtyEsc(struct Pty *pty) {
switch (pty->esc.s[0]) {
case 'c':
2020-10-27 10:39:46 +00:00
PtyFullReset(pty);
break;
case '7':
2020-10-27 10:39:46 +00:00
PtySaveCursorPosition(pty);
break;
case '8':
2020-10-27 10:39:46 +00:00
PtyRestoreCursorPosition(pty);
break;
case 'E':
pty->x = 0;
case 'D':
2020-10-27 10:39:46 +00:00
PtyIndex(pty);
break;
case 'M':
2020-10-27 10:39:46 +00:00
PtyReverseIndex(pty);
break;
case 'Z':
2020-10-27 10:39:46 +00:00
PtyReportPreferredVtIdentity(pty);
break;
case '(':
2020-10-27 10:39:46 +00:00
PtySetCodepage(pty, pty->esc.s[1]);
break;
case '#':
2020-10-27 10:39:46 +00:00
PtyEscHash(pty);
break;
default:
break;
}
}
2020-10-27 10:39:46 +00:00
static void PtyCntrl(struct Pty *pty, int c01) {
switch (c01) {
case '\a':
2020-10-27 10:39:46 +00:00
PtyBell(pty);
break;
case 0x85:
case '\f':
case '\v':
case '\n':
2020-10-27 10:39:46 +00:00
PtyNewline(pty);
break;
case '\r':
2020-10-27 10:39:46 +00:00
PtyCarriageReturn(pty);
break;
case '\e':
2020-10-27 10:39:46 +00:00
pty->state = kPtyEsc;
pty->esc.i = 0;
break;
case '\t':
2020-10-27 10:39:46 +00:00
PtyWriteTab(pty);
break;
case 0x7F:
case '\b':
pty->x = MAX(0, pty->x - 1);
break;
case 0x84:
2020-10-27 10:39:46 +00:00
PtyIndex(pty);
break;
case 0x8D:
2020-10-27 10:39:46 +00:00
PtyReverseIndex(pty);
break;
case 0x9B:
2020-10-27 10:39:46 +00:00
pty->state = kPtyCsi;
break;
default:
break;
}
}
2020-10-27 10:39:46 +00:00
static void PtyEscAppend(struct Pty *pty, char c) {
pty->esc.i = MIN(pty->esc.i + 1, ARRAYLEN(pty->esc.s) - 1);
pty->esc.s[pty->esc.i - 1] = c;
pty->esc.s[pty->esc.i - 0] = 0;
}
2020-10-27 10:39:46 +00:00
ssize_t PtyWrite(struct Pty *pty, const void *data, size_t n) {
int i;
wchar_t wc;
const uint8_t *p;
for (p = data, i = 0; i < n; ++i) {
switch (pty->state) {
2020-10-27 10:39:46 +00:00
case kPtyAscii:
if (0x00 <= p[i] && p[i] <= 0x7F) {
if (0x20 <= p[i] && p[i] <= 0x7E) {
if ((wc = pty->xlat[p[i]]) >= 0) {
2020-10-27 10:39:46 +00:00
PtyWriteGlyph(pty, wc, 1);
} else {
2020-10-27 10:39:46 +00:00
PtyWriteGlyph(pty, -wc, 2);
}
} else {
2020-10-27 10:39:46 +00:00
PtyCntrl(pty, p[i]);
}
} else if (!ThomPikeCont(p[i])) {
2020-10-27 10:39:46 +00:00
pty->state = kPtyUtf8;
pty->u8 = ThomPikeByte(p[i]);
pty->n8 = ThomPikeLen(p[i]) - 1;
}
break;
2020-10-27 10:39:46 +00:00
case kPtyUtf8:
if (ThomPikeCont(p[i])) {
pty->u8 = ThomPikeMerge(pty->u8, p[i]);
if (--pty->n8) break;
}
wc = pty->u8;
if ((0x00 <= wc && wc <= 0x1F) || (0x7F <= wc && wc <= 0x9F)) {
2020-10-27 10:39:46 +00:00
PtyCntrl(pty, wc);
} else {
2020-10-27 10:39:46 +00:00
PtyWriteGlyph(pty, wc, wcwidth(wc));
}
2020-10-27 10:39:46 +00:00
pty->state = kPtyAscii;
pty->u8 = 0;
--i;
break;
2020-10-27 10:39:46 +00:00
case kPtyEsc:
if (p[i] == '[') {
2020-10-27 10:39:46 +00:00
pty->state = kPtyCsi;
} else if (0x30 <= p[i] && p[i] <= 0x7E) {
PtyEscAppend(pty, p[i]);
PtyEsc(pty);
pty->state = kPtyAscii;
} else if (0x20 <= p[i] && p[i] <= 0x2F) {
PtyEscAppend(pty, p[i]);
} else {
2020-10-27 10:39:46 +00:00
pty->state = kPtyAscii;
}
break;
2020-10-27 10:39:46 +00:00
case kPtyCsi:
PtyEscAppend(pty, p[i]);
switch (p[i]) {
case ':':
case ';':
case '<':
case '=':
case '>':
case '?':
case '0' ... '9':
break;
case '`':
case '~':
case '^':
case '@':
case '[':
case ']':
case '{':
case '}':
case '_':
case '|':
case '\\':
case 'A' ... 'Z':
case 'a' ... 'z':
2020-10-27 10:39:46 +00:00
PtyCsi(pty);
pty->state = kPtyAscii;
break;
default:
2020-10-27 10:39:46 +00:00
pty->state = kPtyAscii;
continue;
}
break;
default:
unreachable;
}
}
return n;
}
2020-10-27 10:39:46 +00:00
ssize_t PtyWriteInput(struct Pty *pty, const void *data, size_t n) {
PtyConcatInput(pty, data, n);
if (!(pty->conf & kPtyNoecho)) {
PtyWrite(pty, data, n);
}
return n;
}
2020-10-27 10:39:46 +00:00
ssize_t PtyRead(struct Pty *pty, void *buf, size_t size) {
char *p;
size_t n;
n = MIN(size, pty->input.i);
2020-10-27 10:39:46 +00:00
if (!(pty->conf & kPtyNocanon)) {
if ((p = memchr(pty->input.p, '\n', n))) {
n = MIN(n, pty->input.p - p + 1);
} else {
n = 0;
}
}
memcpy(buf, pty->input.p, n);
memcpy(pty->input.p, pty->input.p + n, pty->input.i - n);
pty->input.i -= n;
return n;
}
2020-10-27 10:39:46 +00:00
static char *PtyEncodeRgb(char *p, int rgb) {
*p++ = '2';
*p++ = ';';
p += uint64toarray_radix10((rgb & 0x0000ff) >> 000, p);
*p++ = ';';
p += uint64toarray_radix10((rgb & 0x00ff00) >> 010, p);
*p++ = ';';
p += uint64toarray_radix10((rgb & 0xff0000) >> 020, p);
return p;
}
2020-10-27 10:39:46 +00:00
static char *PtyEncodeXterm256(char *p, int xt) {
*p++ = '5';
*p++ = ';';
p += uint64toarray_radix10(xt, p);
return p;
}
2020-10-27 10:39:46 +00:00
char *PtyEncodeStyle(char *p, uint32_t xr, uint32_t pr, uint32_t fg,
uint32_t bg) {
*p++ = '\e';
*p++ = '[';
2020-10-27 10:39:46 +00:00
if (pr & (kPtyBold | kPtyFaint | kPtyFlip | kPtyUnder | kPtyDunder |
kPtyBlink | kPtyStrike | kPtyFg | kPtyBg)) {
if (xr & (kPtyBold | kPtyFaint)) {
if ((xr & (kPtyBold | kPtyFaint)) ^ (pr & (kPtyBold | kPtyFaint))) {
*p++ = '2';
*p++ = '2';
*p++ = ';';
}
2020-10-27 10:39:46 +00:00
if (pr & kPtyBold) {
*p++ = '1';
*p++ = ';';
}
2020-10-27 10:39:46 +00:00
if (pr & kPtyFaint) {
*p++ = '2';
*p++ = ';';
}
}
2020-10-27 10:39:46 +00:00
if (xr & (kPtyUnder | kPtyDunder)) {
if ((xr & (kPtyUnder | kPtyDunder)) ^ (pr & (kPtyUnder | kPtyDunder))) {
*p++ = '2';
*p++ = '4';
*p++ = ';';
}
2020-10-27 10:39:46 +00:00
if (pr & kPtyUnder) {
*p++ = '4';
*p++ = ';';
}
2020-10-27 10:39:46 +00:00
if (pr & kPtyDunder) {
*p++ = '2';
*p++ = '1';
*p++ = ';';
}
}
2020-10-27 10:39:46 +00:00
if (xr & (kPtyFlip | kPtyBlink | kPtyStrike)) {
if (xr & kPtyFlip) {
if (!(pr & kPtyFlip)) *p++ = '2';
*p++ = '7';
*p++ = ';';
}
2020-10-27 10:39:46 +00:00
if (xr & kPtyBlink) {
if (!(pr & kPtyBlink)) *p++ = '2';
*p++ = '5';
*p++ = ';';
}
2020-10-27 10:39:46 +00:00
if (xr & kPtyStrike) {
if (!(pr & kPtyStrike)) *p++ = '2';
*p++ = '9';
*p++ = ';';
}
}
2020-10-27 10:39:46 +00:00
if (xr & (kPtyFg | kPtyTrue)) {
*p++ = '3';
2020-10-27 10:39:46 +00:00
if (pr & kPtyFg) {
*p++ = '8';
*p++ = ';';
2020-10-27 10:39:46 +00:00
if (pr & kPtyTrue) {
p = PtyEncodeRgb(p, fg);
} else {
2020-10-27 10:39:46 +00:00
p = PtyEncodeXterm256(p, fg);
}
} else {
*p++ = '9';
}
*p++ = ';';
}
2020-10-27 10:39:46 +00:00
if (xr & (kPtyBg | kPtyTrue)) {
*p++ = '4';
2020-10-27 10:39:46 +00:00
if (pr & kPtyBg) {
*p++ = '8';
*p++ = ';';
2020-10-27 10:39:46 +00:00
if (pr & kPtyTrue) {
p = PtyEncodeRgb(p, bg);
} else {
2020-10-27 10:39:46 +00:00
p = PtyEncodeXterm256(p, bg);
}
} else {
*p++ = '9';
}
*p++ = ';';
}
DCHECK_EQ(';', p[-1]);
p[-1] = 'm';
} else {
*p++ = '0';
*p++ = 'm';
}
return p;
}
2020-10-27 10:39:46 +00:00
int PtyAppendLine(struct Pty *pty, struct Buffer *buf, unsigned y) {
uint64_t u;
char *p, *pb;
2020-10-27 10:39:46 +00:00
uint32_t i, j, n, w, wc, np, xp, pr, fg, bg, ci;
if (y >= pty->yn) return einval();
n = buf->i + pty->xn * 60; /* torture character length */
if (n > buf->n) {
if (!(p = realloc(buf->p, n))) return -1;
buf->p = p;
buf->n = n;
}
i = y * pty->xn;
j = (y + 1) * pty->xn;
pb = buf->p + buf->i;
2020-10-27 10:39:46 +00:00
ci = !(pty->conf & kPtyNocursor) && y == pty->y ? i + pty->x : -1;
for (pr = 0; i < j; i += w) {
np = pty->prs[i];
2020-10-27 10:39:46 +00:00
if (!(np & kPtyConceal)) {
wc = pty->wcs[i];
DCHECK(!(0x00 <= wc && wc <= 0x1F));
DCHECK(!(0x7F <= wc && wc <= 0x9F));
if (0x20 <= wc && wc <= 0x7E) {
u = wc;
w = 1;
} else {
u = tpenc(wc);
w = max(1, wcwidth(wc));
}
} else {
u = ' ';
w = 1;
}
if (i == ci) {
if (u != ' ') {
2020-10-27 10:39:46 +00:00
np ^= kPtyFlip;
} else {
u = tpenc(u'');
2020-10-27 10:39:46 +00:00
if (pty->conf & kPtyBlinkcursor) {
np |= kPtyBlink;
}
}
}
fg = bg = -1;
xp = pr ^ np;
2020-10-27 10:39:46 +00:00
if (np & (kPtyFg | kPtyBg)) {
if (np & kPtyFg) {
if (pty->fgs[i] != fg) xp |= kPtyFg;
fg = pty->fgs[i];
}
2020-10-27 10:39:46 +00:00
if (np & kPtyBg) {
if (pty->bgs[i] != bg) xp |= kPtyBg;
bg = pty->bgs[i];
}
}
p = pb;
if (xp) {
pr = np;
2020-10-27 10:39:46 +00:00
p = PtyEncodeStyle(p, xp, pr, fg, bg);
}
do {
*p++ = u & 0xFF;
u >>= 8;
} while (u);
DCHECK_LE(p - pb, 60);
pb = p;
}
DCHECK_LE(pb - buf->p, buf->n);
buf->i = pb - buf->p;
2020-10-27 10:39:46 +00:00
return 0;
}