Commit 5e84ab6e authored by Poul-Henning Kamp's avatar Poul-Henning Kamp

Bite the bullet and import Ed Schouten's terminal emulation "Teken"

This allows us to use TERM=xterm.
parent bc35cd71
......@@ -30,6 +30,12 @@ varnishtest_SOURCES = \
cmds.h \
vmods.h \
vtc.h \
teken.c \
teken.h \
teken_scs.h \
teken_subr.h \
teken_subr_compat.h \
teken_wcwidth.h \
vtc.c \
vtc_barrier.c \
vtc_client.c \
......@@ -51,7 +57,6 @@ varnishtest_SOURCES = \
vtc_proxy.c \
vtc_server.c \
vtc_subr.c \
vtc_term.c \
vtc_varnish.c
varnishtest_LDADD = \
......@@ -67,4 +72,16 @@ varnishtest_CFLAGS = \
-DTOP_BUILDDIR='"${top_builddir}"'
EXTRA_DIST = $(top_srcdir)/bin/varnishtest/tests/*.vtc \
$(top_srcdir)/bin/varnishtest/tests/README
$(top_srcdir)/bin/varnishtest/tests/README \
$(top_srcdir)/bin/varnishtest/gensequences \
$(top_srcdir)/bin/varnishtest/sequences \
$(top_srcdir)/bin/varnishtest/teken.3
teken_state.h: $(srcdir)/sequences $(srcdir)/gensequences
awk -f $(srcdir)/gensequences $(srcdir)/sequences \
> $(builddir)/teken_state.h
BUILT_SOURCES = teken_state.h
CLEANFILES = $(BUILT_SOURCES)
+libh(teken/teken.h)
// Tell FlexeLint when these don't return
-function(exit, vtc_fatal)
-function(__assert(1), vtc_log(2))
......@@ -8,6 +10,8 @@
-emacro({779}, ENC) // String constant in comparison operator '!='
-emacro({506}, CHKFRAME) // Constant value Boolean
-esym(522, teken_subr_*)
-esym(850, av)
-esym(534, snprintf) // Only for varnishtest, and not really nice
......
......@@ -4,6 +4,7 @@ FLOPS='
-DTOP_BUILDDIR="foo"
-I../../lib/libvgz
*.c
teken/teken.c
'
. ../../tools/flint_skel.sh
......
#!/usr/bin/awk -f
#-
# Copyright (c) 2008-2009 Ed Schouten <ed@FreeBSD.org>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $FreeBSD: head/sys/teken/gensequences 223574 2011-06-26 18:25:10Z ed $
function die(msg) {
print msg;
exit 1;
}
function cchar(str) {
if (str == "^[")
return "\\x1B";
return str;
}
BEGIN {
FS = "\t+"
while (getline > 0) {
if (NF == 0 || $1 ~ /^#/)
continue;
if (NF != 3 && NF != 4)
die("Invalid line layout: " NF " columns");
split($3, sequence, " +");
nsequences = 0;
for (s in sequence)
nsequences++;
prefix = "";
l_prefix_name[""] = "teken_state_init";
for (i = 1; i < nsequences; i++) {
n = prefix sequence[i];
l_prefix_parent[n] = prefix;
l_prefix_suffix[n] = sequence[i];
if (!l_prefix_name[n])
l_prefix_name[n] = "teken_state_" ++npr;
prefix = n;
}
suffix = sequence[nsequences];
cmd = prefix suffix;
# Fill lists
if (l_cmd_name[cmd] != "")
die(cmd " already exists");
l_cmd_prefix[cmd] = prefix;
l_cmd_suffix[cmd] = suffix;
l_cmd_args[cmd] = $4;
l_cmd_abbr[cmd] = $1;
l_cmd_name[cmd] = $2;
l_cmd_c_name[cmd] = "teken_subr_" tolower($2);
gsub(" ", "_", l_cmd_c_name[cmd]);
if ($4 != "")
l_prefix_numbercmds[prefix]++;
}
print "/* Generated file. Do not edit. */";
print "";
for (p in l_prefix_name) {
if (l_prefix_name[p] != "teken_state_init")
print "static teken_state_t " l_prefix_name[p] ";";
}
for (p in l_prefix_name) {
print "";
print "/* '" p "' */";
print "static void";
print l_prefix_name[p] "(teken_t *t, teken_char_t c)";
print "{";
if (l_prefix_numbercmds[p] > 0) {
print "";
print "\tif (teken_state_numbers(t, c))";
print "\t\treturn;";
}
print "";
print "\tswitch (c) {";
for (c in l_cmd_prefix) {
if (l_cmd_prefix[c] != p)
continue;
print "\tcase '" cchar(l_cmd_suffix[c]) "': /* " l_cmd_abbr[c] ": " l_cmd_name[c] " */";
if (l_cmd_args[c] == "v") {
print "\t\t" l_cmd_c_name[c] "(t, t->t_curnum, t->t_nums);";
} else {
printf "\t\t%s(t", l_cmd_c_name[c];
split(l_cmd_args[c], args, " ");
for (a = 1; args[a] != ""; a++) {
if (args[a] == "n")
printf ", (t->t_curnum < %d || t->t_nums[%d] == 0) ? 1 : t->t_nums[%d]", a, (a - 1), (a - 1);
else if (args[a] == "r")
printf ", t->t_curnum < %d ? 0 : t->t_nums[%d]", a, (a - 1);
else
die("Invalid argument type: " args[a]);
}
print ");";
}
print "\t\tbreak;";
}
for (pc in l_prefix_parent) {
if (l_prefix_parent[pc] != p)
continue;
print "\tcase '" cchar(l_prefix_suffix[pc]) "':";
print "\t\tteken_state_switch(t, " l_prefix_name[pc] ");";
print "\t\treturn;";
}
print "\tdefault:";
if (l_prefix_name[p] == "teken_state_init") {
print "\t\tteken_subr_regular_character(t, c);";
} else {
print "\t\tteken_printf(\"Unsupported sequence in " l_prefix_name[p] ": %u\\n\", (unsigned int)c);";
}
print "\t\tbreak;";
print "\t}";
if (l_prefix_name[p] != "teken_state_init") {
print "";
print "\tteken_state_switch(t, teken_state_init);";
}
print "}";
}
}
#-
# Copyright (c) 2008-2009 Ed Schouten <ed@FreeBSD.org>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $FreeBSD: head/sys/teken/sequences 322662 2017-08-18 15:40:40Z bde $
# File format is as follows:
# Abbr Abbreviation of sequence name
# Name Sequence name (will be converted to C function name)
# Sequence Bytes that form the sequence
# Arguments Standard value of arguments passed to this sequence
# - `n' non-zero number (0 gets converted to 1)
# - `r' regular numeric argument
# - `v' means a variable number of arguments
# Abbr Name Sequence Arguments
CBT Cursor Backward Tabulation ^[ [ Z n
CHT Cursor Forward Tabulation ^[ [ I n
CNL Cursor Next Line ^[ [ E n
CPL Cursor Previous Line ^[ [ F n
CPR Cursor Position Report ^[ [ n r
CUB Cursor Backward ^[ [ D n
CUD Cursor Down ^[ [ B n
CUD Cursor Down ^[ [ e n
CUF Cursor Forward ^[ [ C n
CUF Cursor Forward ^[ [ a n
CUP Cursor Position ^[ [ H n n
CUP Cursor Position ^[ [ f n n
CUU Cursor Up ^[ [ A n
DA1 Primary Device Attributes ^[ [ c r
DA2 Secondary Device Attributes ^[ [ > c r
DC Delete character ^[ [ P n
DCS Device Control String ^[ P
DECALN Alignment test ^[ # 8
DECDHL Double Height Double Width Line Top ^[ # 3
DECDHL Double Height Double Width Line Bottom ^[ # 4
DECDWL Single Height Double Width Line ^[ # 6
DECKPAM Keypad application mode ^[ =
DECKPNM Keypad numeric mode ^[ >
DECRC Restore cursor ^[ 8
DECRC Restore cursor ^[ [ u
DECRM Reset DEC mode ^[ [ ? l r
DECSC Save cursor ^[ 7
DECSC Save cursor ^[ [ s
DECSM Set DEC mode ^[ [ ? h r
DECSTBM Set top and bottom margins ^[ [ r r r
DECSWL Single Height Single Width Line ^[ # 5
DL Delete line ^[ [ M n
DSR Device Status Report ^[ [ ? n r
ECH Erase character ^[ [ X n
ED Erase display ^[ [ J r
EL Erase line ^[ [ K r
G0SCS0 G0 SCS Special Graphics ^[ ( 0
G0SCS1 G0 SCS US ASCII ^[ ( 1
G0SCS2 G0 SCS Special Graphics ^[ ( 2
G0SCSA G0 SCS UK National ^[ ( A
G0SCSB G0 SCS US ASCII ^[ ( B
G1SCS0 G1 SCS Special Graphics ^[ ) 0
G1SCS1 G1 SCS US ASCII ^[ ) 1
G1SCS2 G1 SCS Special Graphics ^[ ) 2
G1SCSA G1 SCS UK National ^[ ) A
G1SCSB G1 SCS US ASCII ^[ ) B
HPA Horizontal Position Absolute ^[ [ G n
HPA Horizontal Position Absolute ^[ [ ` n
HTS Horizontal Tab Set ^[ H
ICH Insert character ^[ [ @ n
IL Insert line ^[ [ L n
IND Index ^[ D
NEL Next line ^[ E
OSC Operating System Command ^[ ]
RI Reverse index ^[ M
RIS Reset to Initial State ^[ c
RM Reset Mode ^[ [ l r
SD Pan Up ^[ [ T n
SGR Set Graphic Rendition ^[ [ m v
SM Set Mode ^[ [ h r
ST String Terminator ^[ \\
SU Pan Down ^[ [ S n
TBC Tab Clear ^[ [ g r
VPA Vertical Position Absolute ^[ [ d n
# Cons25 compatibility sequences
C25BLPD Cons25 set bell pitch duration ^[ [ = B r r
C25BORD Cons25 set border ^[ [ = A r
C25DBG Cons25 set default background ^[ [ = G r
C25DFG Cons25 set default foreground ^[ [ = F r
C25GCS Cons25 set global cursor shape ^[ [ = C v
C25LCT Cons25 set local cursor type ^[ [ = S r
C25MODE Cons25 set terminal mode ^[ [ = T r
C25SGR Cons25 set graphic rendition ^[ [ x r r
C25VTSW Cons25 switch virtual terminal ^[ [ z r
# VT52 compatibility
#DECID VT52 DECID ^[ Z
.\" Copyright (c) 2011 Ed Schouten <ed@FreeBSD.org>
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
.\" $FreeBSD: head/sys/teken/libteken/teken.3 315418 2017-03-16 16:40:54Z bde $
.\"
.Dd Mar 13, 2017
.Dt TEKEN 3
.Os
.Sh NAME
.Nm teken
.Nd xterm-like terminal emulation interface
.Sh LIBRARY
.Lb libteken
.Sh SYNOPSIS
.In teken.h
.Ft void
.Fn teken_init "teken_t *t" "const teken_funcs_t *funcs" "void *thunk"
.Ft void
.Fn teken_input "teken_t *t" "const void *buf" "size_t nbytes"
.Ft const teken_pos_t *
.Fn teken_get_winsize "teken_t *t"
.Ft void
.Fn teken_set_winsize "teken_t *t" "const teken_pos_t *size"
.Ft const teken_pos_t *
.Fn teken_get_cursor "teken_t *t"
.Ft void
.Fn teken_set_cursor "teken_t *t" "const teken_pos_t *pos"
.Ft const teken_attr_t *
.Fn teken_get_curattr "teken_t *t"
.Ft void
.Fn teken_set_curattr "teken_t *t" "const teken_attr_t *attr"
.Ft const teken_attr_t *
.Fn teken_get_defattr "teken_t *t"
.Ft void
.Fn teken_set_defattr "teken_t *t" "const teken_attr_t *attr"
.Ft const char *
.Fn teken_get_sequence "teken_t *t" "unsigned int id"
.Ft teken_color_t
.Fn teken_256to16 "teken_color_t color"
.Ft teken_color_t
.Fn teken_256to8 "teken_color_t color"
.Ft void
.Fn teken_get_defattr_cons25 "teken_t *t" "int *fg" "int *bg"
.Ft void
.Fn teken_set_8bit "teken_t *t"
.Ft void
.Fn teken_set_cons25 "teken_t *t"
.Sh DESCRIPTION
The
.Nm
library implements the input parser of a 256-color xterm-like terminal.
It converts a stream of UTF-8 encoded characters into a series of
primitive drawing instructions that can be used by a console driver or
terminal emulator to render a terminal application.
.Pp
The
.Fn teken_init
function is used to initialize terminal state object
.Fa t ,
having type
.Vt teken_t .
The supplied
.Vt teken_funcs_t
structure
.Fa funcs
contains a set of callback functions, which are called when supplying
data to
.Fn teken_input .
The
.Fa thunk
argument stores an arbitrary pointer, which is passed to each invocation
of the callback functions.
.Pp
The
.Vt teken_funcs_t
structure stores the following callbacks:
.Bd -literal -offset indent
typedef struct {
tf_bell_t *tf_bell; /* Audible/visible bell. */
tf_cursor_t *tf_cursor; /* Move cursor to x/y. */
tf_putchar_t *tf_putchar; /* Put Unicode character at x/y. */
tf_fill_t *tf_fill; /* Fill rectangle with character. */
tf_copy_t *tf_copy; /* Copy rectangle to new location. */
tf_param_t *tf_param; /* Miscellaneous options. */
tf_respond_t *tf_respond; /* Send response string to user. */
} teken_funcs_t;
.Ed
.Pp
All callbacks must be provided, though unimplemented callbacks may some
times be sufficient.
The actual types of these callbacks can be found in
.In teken.h .
.Pp
By default,
.Fn teken_init
initializes the
.Vt teken_t
structure to emulate a terminal having 24 rows and 80 columns.
The
.Fn teken_get_winsize
and
.Fn teken_set_winsize
functions can be used to obtain and modify the dimensions of the
terminal.
.Pp
Even though the cursor position is normally controlled by input of data
through
.Fn teken_input
and returned by the
.Fn tf_cursor
callback, it can be obtained and modified manually using the
.Fn teken_get_cursor
and
.Fn teken_set_cursor
functions.
The same holds for
.Fn teken_get_curattr
and
.Fn teken_set_curattr ,
which can be used to change the currently selected font attributes and
foreground and background color.
.Pp
By default,
.Nm
emulates a white-on-black terminal, which means the default foreground
color is white, while the background color is black.
These defaults can be modified using
.Fn teken_get_defattr
and
.Fn teken_set_defattr .
.Pp
The
.Fn teken_get_sequence
function is a utility function that can be used to obtain escape
sequences of special keyboard keys, generated by user input.
The
.Fa id
parameter must be one of the
.Dv TKEY_*
parameters listed in
.In teken.h .
.Sh LEGACY FEATURES
This library also provides a set of functions that shouldn't be used in
any modern applications.
.Pp
The
.Fn teken_256to16
function converts an xterm-256 256-color code to an xterm 16-color code
whose color with default palettes is as similar as possible (not very
similar).
The lower 3 bits of the result are the ANSI color and the next lowest
bit is brightness.
Other layers (hardare and software) that only support 16 colors can use
this to avoid knowing the details of 256-color codes.
.Pp
The
.Fn teken_256to8
function is similar to
.Fn teken_256to16
except it converts to an ANSI 8-color code.
This is more accurate than discarding the brigtness bit in the result of
.Fn teken_256to16 .
.Pp
The
.Fn teken_get_defattr_cons25
function obtains the default terminal attributes as a pair of foreground
and background colors, using ANSI color numbering.
.Pp
The
.Fn teken_set_8bit
function disables UTF-8 processing and switches to 8-bit character mode,
which can be used to support character sets like CP437 and ISO-8859-1.
.Pp
The
.Fn teken_set_cons25
function switches terminal emulation to
.Dv cons25 ,
which is used by versions of
.Fx
prior to 9.0.
.Sh SEE ALSO
.Xr ncurses 3 ,
.Xr termcap 3 ,
.Xr syscons 4
.Sh HISTORY
The
.Nm
library appeared in
.Fx 8.0 ,
though it was only available and used inside the kernel.
In
.Fx 9.0 ,
the
.Nm
library appeared in userspace.
.Sh AUTHORS
.An Ed Schouten Aq ed@FreeBSD.org
.Sh SECURITY CONSIDERATIONS
The
.Fn tf_respond
callback is used to respond to device status requests commands generated
by an application.
In the past, there have been various security issues, where a malicious
application sends a device status request before termination, causing
the generated response to be interpreted by applications such as
.Xr sh 1 .
.Pp
.Nm
only implements a small subset of responses which are unlikely to cause
any harm.
Still, it is advised to leave
.Fn tf_respond
unimplemented.
This diff is collapsed.
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2008-2009 Ed Schouten <ed@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD: head/sys/teken/teken.h 326272 2017-11-27 15:23:17Z pfg $
*/
#ifndef _TEKEN_H_
#define _TEKEN_H_
#include <sys/types.h>
/*
* libteken: terminal emulation library.
*
* This library converts an UTF-8 stream of bytes to terminal drawing
* commands.
*/
typedef uint32_t teken_char_t;
typedef unsigned short teken_unit_t;
typedef unsigned char teken_format_t;
#define TF_BOLD 0x01 /* Bold character. */
#define TF_UNDERLINE 0x02 /* Underline character. */
#define TF_BLINK 0x04 /* Blinking character. */
#define TF_REVERSE 0x08 /* Reverse rendered character. */
#define TF_CJK_RIGHT 0x10 /* Right-hand side of CJK character. */
typedef unsigned char teken_color_t;
#define TC_BLACK 0
#define TC_RED 1
#define TC_GREEN 2
#define TC_BROWN 3
#define TC_BLUE 4
#define TC_MAGENTA 5
#define TC_CYAN 6
#define TC_WHITE 7
#define TC_NCOLORS 8
#define TC_LIGHT 8 /* ORed with the others. */
typedef struct {
teken_unit_t tp_row;
teken_unit_t tp_col;
} teken_pos_t;
typedef struct {
teken_pos_t tr_begin;
teken_pos_t tr_end;
} teken_rect_t;
typedef struct {
teken_format_t ta_format;
teken_color_t ta_fgcolor;
teken_color_t ta_bgcolor;
} teken_attr_t;
typedef struct {
teken_unit_t ts_begin;
teken_unit_t ts_end;
} teken_span_t;
typedef struct __teken teken_t;
typedef void teken_state_t(teken_t *, teken_char_t);
/*
* Drawing routines supplied by the user.
*/
typedef void tf_bell_t(void *);
typedef void tf_cursor_t(void *, const teken_pos_t *);
typedef void tf_putchar_t(void *, const teken_pos_t *, teken_char_t,
const teken_attr_t *);
typedef void tf_fill_t(void *, const teken_rect_t *, teken_char_t,
const teken_attr_t *);
typedef void tf_copy_t(void *, const teken_rect_t *, const teken_pos_t *);
typedef void tf_param_t(void *, int, unsigned int);
#define TP_SHOWCURSOR 0
#define TP_KEYPADAPP 1
#define TP_AUTOREPEAT 2
#define TP_SWITCHVT 3
#define TP_132COLS 4
#define TP_SETBELLPD 5
#define TP_SETBELLPD_PITCH(pd) ((pd) >> 16)
#define TP_SETBELLPD_DURATION(pd) ((pd) & 0xffff)
#define TP_MOUSE 6
#define TP_SETBORDER 7
#define TP_SETLOCALCURSOR 8
#define TP_SETGLOBALCURSOR 9
typedef void tf_respond_t(void *, const void *, size_t);
typedef struct {
tf_bell_t *tf_bell;
tf_cursor_t *tf_cursor;
tf_putchar_t *tf_putchar;
tf_fill_t *tf_fill;
tf_copy_t *tf_copy;
tf_param_t *tf_param;
tf_respond_t *tf_respond;
} teken_funcs_t;
typedef teken_char_t teken_scs_t(const teken_t *, teken_char_t);
/*
* Terminal state.
*/
struct __teken {
const teken_funcs_t *t_funcs;
void *t_softc;
teken_state_t *t_nextstate;
unsigned int t_stateflags;
#define T_NUMSIZE 8
unsigned int t_nums[T_NUMSIZE];
unsigned int t_curnum;
teken_pos_t t_cursor;
teken_attr_t t_curattr;
teken_pos_t t_saved_cursor;
teken_attr_t t_saved_curattr;
teken_attr_t t_defattr;
teken_pos_t t_winsize;
/* For DECSTBM. */
teken_span_t t_scrollreg;
/* For DECOM. */
teken_span_t t_originreg;
#define T_NUMCOL 160
unsigned int t_tabstops[T_NUMCOL / (sizeof(unsigned int) * 8)];
unsigned int t_utf8_left;
teken_char_t t_utf8_partial;
unsigned int t_curscs;
teken_scs_t *t_saved_curscs;
teken_scs_t *t_scs[2];
};
/* Initialize teken structure. */
void teken_init(teken_t *, const teken_funcs_t *, void *);
/* Deliver character input. */
void teken_input(teken_t *, const void *, size_t);
/* Get/set teken attributes. */
const teken_pos_t *teken_get_cursor(const teken_t *);
const teken_attr_t *teken_get_curattr(const teken_t *);
const teken_attr_t *teken_get_defattr(const teken_t *);
void teken_get_defattr_cons25(const teken_t *, int *, int *);
const teken_pos_t *teken_get_winsize(const teken_t *);
void teken_set_cursor(teken_t *, const teken_pos_t *);
void teken_set_curattr(teken_t *, const teken_attr_t *);
void teken_set_defattr(teken_t *, const teken_attr_t *);
void teken_set_winsize(teken_t *, const teken_pos_t *);
void teken_set_winsize_noreset(teken_t *, const teken_pos_t *);
/* Key input escape sequences. */
#define TKEY_UP 0x00
#define TKEY_DOWN 0x01
#define TKEY_LEFT 0x02
#define TKEY_RIGHT 0x03
#define TKEY_HOME 0x04
#define TKEY_END 0x05
#define TKEY_INSERT 0x06
#define TKEY_DELETE 0x07
#define TKEY_PAGE_UP 0x08
#define TKEY_PAGE_DOWN 0x09
#define TKEY_F1 0x0a
#define TKEY_F2 0x0b
#define TKEY_F3 0x0c
#define TKEY_F4 0x0d
#define TKEY_F5 0x0e
#define TKEY_F6 0x0f
#define TKEY_F7 0x10
#define TKEY_F8 0x11
#define TKEY_F9 0x12
#define TKEY_F10 0x13
#define TKEY_F11 0x14
#define TKEY_F12 0x15
const char *teken_get_sequence(const teken_t *, unsigned int);
/* Legacy features. */
void teken_set_8bit(teken_t *);
void teken_set_cons25(teken_t *);
/* Color conversion. */
teken_color_t teken_256to16(teken_color_t);
teken_color_t teken_256to8(teken_color_t);
#endif /* !_TEKEN_H_ */
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2009 Ed Schouten <ed@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD: head/sys/teken/teken_scs.h 326272 2017-11-27 15:23:17Z pfg $
*/
static inline teken_char_t
teken_scs_process(const teken_t *t, teken_char_t c)
{
return (t->t_scs[t->t_curscs](t, c));
}
/* Unicode points for VT100 box drawing. */
static const uint16_t teken_boxdrawing_unicode[31] = {
0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1,
0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba,
0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c,
0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7
};
/* ASCII points for VT100 box drawing. */
static const uint8_t teken_boxdrawing_8bit[31] = {
'?', '?', 'H', 'F', 'C', 'L', '?', '?',
'N', 'V', '+', '+', '+', '+', '+', '-',
'-', '-', '-', '-', '+', '+', '+', '+',
'|', '?', '?', '?', '?', '?', '?',
};
static teken_char_t
teken_scs_special_graphics(const teken_t *t, teken_char_t c)
{
/* Box drawing. */
if (c >= '`' && c <= '~')
return (t->t_stateflags & TS_8BIT ?
teken_boxdrawing_8bit[c - '`'] :
teken_boxdrawing_unicode[c - '`']);
return (c);
}
static teken_char_t
teken_scs_uk_national(const teken_t *t, teken_char_t c)
{
/* Pound sign. */
if (c == '#')
return (t->t_stateflags & TS_8BIT ? 0x9c : 0xa3);
return (c);
}
static teken_char_t
teken_scs_us_ascii(const teken_t *t, teken_char_t c)
{
/* No processing. */
(void)t;
return (c);
}
This diff is collapsed.
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 2008-2009 Ed Schouten <ed@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD: head/sys/teken/teken_subr_compat.h 326272 2017-11-27 15:23:17Z pfg $
*/
static void
teken_subr_cons25_set_border(const teken_t *t, unsigned int c)
{
teken_funcs_param(t, TP_SETBORDER, c);
}
static void
teken_subr_cons25_set_global_cursor_shape(const teken_t *t, unsigned int ncmds,
const unsigned int cmds[])
{
unsigned int code, i;
/*
* Pack the args to work around API deficiencies. This requires
* knowing too much about the low level to be fully compatible.
* Returning when ncmds > 3 is necessary and happens to be
* compatible. Discarding high bits is necessary and happens to
* be incompatible only for invalid args when ncmds == 3.
*/
if (ncmds > 3)
return;
code = 0;
for (i = ncmds; i > 0; i--)
code = (code << 8) | (cmds[i - 1] & 0xff);
code = (code << 8) | ncmds;
teken_funcs_param(t, TP_SETGLOBALCURSOR, code);
}
static void
teken_subr_cons25_set_local_cursor_type(const teken_t *t, unsigned int type)
{
teken_funcs_param(t, TP_SETLOCALCURSOR, type);
}
static const teken_color_t cons25_colors[8] = { TC_BLACK, TC_BLUE,
TC_GREEN, TC_CYAN, TC_RED, TC_MAGENTA, TC_BROWN, TC_WHITE };
static void
teken_subr_cons25_set_default_background(teken_t *t, unsigned int c)
{
t->t_defattr.ta_bgcolor = cons25_colors[c % 8] | (c & 8);
t->t_curattr.ta_bgcolor = cons25_colors[c % 8] | (c & 8);
}
static void
teken_subr_cons25_set_default_foreground(teken_t *t, unsigned int c)
{
t->t_defattr.ta_fgcolor = cons25_colors[c % 8] | (c & 8);
t->t_curattr.ta_fgcolor = cons25_colors[c % 8] | (c & 8);
}
static const teken_color_t cons25_revcolors[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
void
teken_get_defattr_cons25(const teken_t *t, int *fg, int *bg)
{
*fg = cons25_revcolors[teken_256to8(t->t_defattr.ta_fgcolor)];
if (t->t_defattr.ta_format & TF_BOLD)
*fg += 8;
*bg = cons25_revcolors[teken_256to8(t->t_defattr.ta_bgcolor)];
}
static void
teken_subr_cons25_switch_virtual_terminal(const teken_t *t, unsigned int vt)
{
teken_funcs_param(t, TP_SWITCHVT, vt);
}
static void
teken_subr_cons25_set_bell_pitch_duration(const teken_t *t, unsigned int pitch,
unsigned int duration)
{
teken_funcs_param(t, TP_SETBELLPD, (pitch << 16) |
(duration & 0xffff));
}
static void
teken_subr_cons25_set_graphic_rendition(teken_t *t, unsigned int cmd,
unsigned int param)
{
(void)param;
switch (cmd) {
case 0: /* Reset. */
t->t_curattr = t->t_defattr;
break;
default:
teken_printf("unsupported attribute %u\n", cmd);
}
}
static void
teken_subr_cons25_set_terminal_mode(teken_t *t, unsigned int mode)
{
switch (mode) {
case 0: /* Switch terminal to xterm. */
t->t_stateflags &= ~TS_CONS25;
break;
case 1: /* Switch terminal to cons25. */
t->t_stateflags |= TS_CONS25;
break;
default:
break;
}
}
#if 0
static void
teken_subr_vt52_decid(teken_t *t)
{
const char response[] = "\x1B/Z";
teken_funcs_respond(t, response, sizeof response - 1);
}
#endif
/*
* Markus Kuhn -- 2007-05-26 (Unicode 5.0)
*
* Permission to use, copy, modify, and distribute this software
* for any purpose and without fee is hereby granted. The author
* disclaims all warranties with regard to this software.
*
* Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
*
* $FreeBSD: head/sys/teken/teken_wcwidth.h 186681 2009-01-01 13:26:53Z ed $
*/
struct interval {
teken_char_t first;
teken_char_t last;
};
/* auxiliary function for binary search in interval table */
static int bisearch(teken_char_t ucs, const struct interval *table, int max) {
int min = 0;
int mid;
if (ucs < table[0].first || ucs > table[max].last)
return 0;
while (max >= min) {
mid = (min + max) / 2;
if (ucs > table[mid].last)
min = mid + 1;
else if (ucs < table[mid].first)
max = mid - 1;
else
return 1;
}
return 0;
}
static int teken_wcwidth(teken_char_t ucs)
{
/* sorted list of non-overlapping intervals of non-spacing characters */
/* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */
static const struct interval combining[] = {
{ 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 },
{ 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 },
{ 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 },
{ 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 },
{ 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED },
{ 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A },
{ 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 },
{ 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D },
{ 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 },
{ 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD },
{ 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C },
{ 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D },
{ 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC },
{ 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD },
{ 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C },
{ 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D },
{ 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 },
{ 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 },
{ 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC },
{ 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD },
{ 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D },
{ 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 },
{ 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E },
{ 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC },
{ 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 },
{ 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E },
{ 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 },
{ 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 },
{ 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 },
{ 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F },
{ 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 },
{ 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD },
{ 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD },
{ 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 },
{ 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B },
{ 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 },
{ 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 },
{ 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF },
{ 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 },
{ 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F },
{ 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B },
{ 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F },
{ 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB },
{ 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F },
{ 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 },
{ 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD },
{ 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F },
{ 0xE0100, 0xE01EF }
};
/* test for 8-bit control characters */
if (ucs == 0)
return 0;
if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0))
return -1;
/* binary search in table of non-spacing characters */
if (bisearch(ucs, combining,
sizeof(combining) / sizeof(struct interval) - 1))
return 0;
/* if we arrive here, ucs is not a combining or C0/C1 control character */
return 1 +
(ucs >= 0x1100 &&
(ucs <= 0x115f || /* Hangul Jamo init. consonants */
ucs == 0x2329 || ucs == 0x232a ||
(ucs >= 0x2e80 && ucs <= 0xa4cf &&
ucs != 0x303f) || /* CJK ... Yi */
(ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */
(ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */
(ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */
(ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */
(ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */
(ucs >= 0xffe0 && ucs <= 0xffe6) ||
(ucs >= 0x20000 && ucs <= 0x2fffd) ||
(ucs >= 0x30000 && ucs <= 0x3fffd)));
}
......@@ -36,5 +36,5 @@ process p1 -need-bytes 5000 -screen_dump
process p1 -winsz 25 132
process p1 -need-bytes 7000 -screen_dump -write {q} -wait
process p1 -need-bytes 4000 -screen_dump -write {q} -wait
......@@ -135,9 +135,3 @@ void vtc_expect(struct vtclog *, const char *, const char *, const char *,
const char *, const char *);
void vtc_wait4(struct vtclog *, long, int, int, int);
void *vtc_record(struct vtclog *, int, struct vsb *);
/* vtc_term.c */
struct term *Term_New(struct vtclog *, int, int);
void Term_Feed(struct term *, const char *, const char *);
void Term_Dump(const struct term *);
void Term_SetSize(struct term *, int, int);
......@@ -54,6 +54,8 @@
#include "vsb.h"
#include "vsub.h"
#include "teken.h"
struct process {
unsigned magic;
#define PROCESS_MAGIC 0x1617b43e
......@@ -84,14 +86,136 @@ struct process {
pthread_t tp;
unsigned hasthread;
struct term *term;
int lin;
int col;
int nlin;
int ncol;
char **vram;
teken_t tek[1];
};
static VTAILQ_HEAD(, process) processes =
VTAILQ_HEAD_INITIALIZER(processes);
/**********************************************************************
* Terminal emulation
*/
static void
term_cursor(void *priv, const teken_pos_t *pos)
{
(void)priv;
(void)pos;
}
static void
term_putchar(void *priv, const teken_pos_t *pos, teken_char_t ch,
const teken_attr_t *at)
{
struct process *pp;
CAST_OBJ_NOTNULL(pp, priv, PROCESS_MAGIC);
(void)at;
if (ch > 126 || ch < 32)
ch = '?';
assert(pos->tp_row < pp->nlin);
assert(pos->tp_col < pp->ncol);
pp->vram[pos->tp_row][pos->tp_col] = ch;
}
static void
term_fill(void *priv, const teken_rect_t *r, teken_char_t c,
const teken_attr_t *a)
{
teken_pos_t p;
/* Braindead implementation of fill() - just call putchar(). */
for (p.tp_row = r->tr_begin.tp_row;
p.tp_row < r->tr_end.tp_row; p.tp_row++)
for (p.tp_col = r->tr_begin.tp_col;
p.tp_col < r->tr_end.tp_col; p.tp_col++)
term_putchar(priv, &p, c, a);
}
static void
term_copy(void *priv, const teken_rect_t *r, const teken_pos_t *p)
{
struct process *pp;
int nrow, ncol, y; /* Has to be signed - >= 0 comparison */
/*
* Copying is a little tricky. We must make sure we do it in
* correct order, to make sure we don't overwrite our own data.
*/
CAST_OBJ_NOTNULL(pp, priv, PROCESS_MAGIC);
nrow = r->tr_end.tp_row - r->tr_begin.tp_row;
ncol = r->tr_end.tp_col - r->tr_begin.tp_col;
if (p->tp_row < r->tr_begin.tp_row) {
/* Copy from top to bottom. */
for (y = 0; y < nrow; y++)
memmove(&pp->vram[p->tp_row + y][p->tp_col],
&pp->vram[r->tr_begin.tp_row + y][r->tr_begin.tp_col], ncol);
} else {
/* Copy from bottom to top. */
for (y = nrow - 1; y >= 0; y--)
memmove(&pp->vram[p->tp_row + y][p->tp_col],
&pp->vram[r->tr_begin.tp_row + y][r->tr_begin.tp_col], ncol);
}
}
static const teken_funcs_t process_teken_func = {
.tf_cursor = term_cursor,
.tf_putchar = term_putchar,
.tf_fill = term_fill,
.tf_copy = term_copy,
};
static void
term_screen_dump(const struct process *pp)
{
int i;
for (i = 0; i < pp->nlin; i++)
vtc_dump(pp->vl, 3, "screen", pp->vram[i], pp->ncol);
}
static void
term_resize(struct process *pp, int lin, int col)
{
teken_pos_t pos;
char **vram;
int i, j;
vram = calloc(lin, sizeof *pp->vram);
AN(vram);
for (i = 0; i < lin; i++) {
vram[i] = malloc(col + 1L);
AN(vram[i]);
memset(vram[i], ' ', col);
vram[i][col] = '\0';
}
if (pp->vram != NULL) {
for (i = 0; i < lin; i++) {
if (i >= pp->nlin)
break;
j = col;
if (j > pp->ncol)
j = pp->ncol;
memcpy(vram[i], pp->vram[i], j);
}
for (i = 0; i < pp->nlin; i++)
free(pp->vram[i]);
free(pp->vram);
}
pp->vram = vram;
pp->nlin = lin;
pp->ncol = col;
pos.tp_row = lin;
pos.tp_col = col;
teken_set_winsize(pp->tek, &pos);
}
/**********************************************************************
* Allocate and initialize a process
*/
......@@ -131,10 +255,8 @@ process_new(const char *name)
p->fd_term = -1;
VTAILQ_INSERT_TAIL(&processes, p, list);
p->lin = 25;
p->col = 80;
p->term = Term_New(p->vl, p->lin, p->col);
AN(p->term);
teken_init(p->tek, &process_teken_func, p);
term_resize(p, 25, 80);
return (p);
}
......@@ -214,7 +336,7 @@ process_stdout(const struct vev *ev, int what)
else if (p->log == 3)
vtc_hexdump(p->vl, 4, "stdout", buf, i);
(void)write(p->f_stdout, buf, i);
Term_Feed(p->term, buf, buf + i);
teken_input(p->tek, buf, i);
return (0);
}
......@@ -311,14 +433,14 @@ process_thread(void *priv)
}
static void
process_winsz(struct process *p, int fd, int lin, int col)
process_winsz(struct process *p, int fd)
{
struct winsize ws;
int i;
memset(&ws, 0, sizeof ws);
ws.ws_row = (short)lin;
ws.ws_col = (short)col;
ws.ws_row = (short)p->nlin;
ws.ws_col = (short)p->ncol;
i = ioctl(fd, TIOCSWINSZ, &ws);
if (i)
vtc_log(p->vl, 4, "TIOCWINSZ %d %s", i, strerror(errno));
......@@ -330,7 +452,7 @@ process_init_term(struct process *p, int fd)
struct termios tt;
int i;
process_winsz(p, fd, p->lin, p->col);
process_winsz(p, fd);
memset(&tt, 0, sizeof tt);
tt.c_cflag = CREAD | CS8 | HUPCL;
......@@ -407,7 +529,7 @@ process_start(struct process *p)
VSUB_closefrom(STDERR_FILENO + 1);
process_init_term(p, slave);
AZ(setenv("TERM", "ansi.sys", 1));
AZ(setenv("TERM", "xterm", 1));
AZ(unsetenv("TERMCAP"));
// Not using NULL because GCC is now even more demented...
assert(write(STDERR_FILENO, "+", 1) == 1);
......@@ -509,6 +631,23 @@ process_write(const struct process *p, const char *text)
len, strerror(errno), errno);
}
static void
process_write_hex(const struct process *p, const char *text)
{
struct vsb *vsb;
int j;
if (!p->hasthread)
vtc_fatal(p->vl, "Cannot write to a non-running process");
vsb = vtc_hex_to_bin(p->vl, text);
assert(VSB_len(vsb) >= 0);
vtc_hexdump(p->vl, 4, "sendhex", VSB_data(vsb), VSB_len(vsb));
j = write(p->fd_term, VSB_data(vsb), VSB_len(vsb));
assert(j == VSB_len(vsb));
VSB_destroy(&vsb);
}
static void
process_close(struct process *p)
{
......@@ -614,6 +753,7 @@ cmd_process(CMD_ARGS)
{
struct process *p, *p2;
uintmax_t u, v;
unsigned lin,col;
(void)priv;
(void)cmd;
......@@ -713,7 +853,7 @@ cmd_process(CMD_ARGS)
continue;
}
if (!strcmp(*av, "-screen_dump")) {
Term_Dump(p->term);
term_screen_dump(p);
continue;
}
if (!strcmp(*av, "-start")) {
......@@ -730,19 +870,24 @@ cmd_process(CMD_ARGS)
continue;
}
if (!strcmp(*av, "-winsz")) {
p->lin = atoi(av[1]);
assert(p->lin > 1);
p->col = atoi(av[2]);
assert(p->col > 1);
lin = atoi(av[1]);
assert(lin > 1);
col = atoi(av[2]);
assert(col > 1);
av += 2;
Term_SetSize(p->term, p->lin, p->col);
process_winsz(p, p->fd_term, p->lin, p->col);
term_resize(p, lin, col);
process_winsz(p, p->fd_term);
}
if (!strcmp(*av, "-write")) {
process_write(p, av[1]);
av++;
continue;
}
if (!strcmp(*av, "-writehex")) {
process_write_hex(p, av[1]);
av++;
continue;
}
if (!strcmp(*av, "-writeln")) {
process_write(p, av[1]);
process_write(p, "\n");
......
/*-
* Copyright (c) 2018 Varnish Software AS
* All rights reserved.
*
* Author: Poul-Henning Kamp <phk@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* A trivial ANSI terminal emulation
*/
#include "config.h"
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "vtc.h"
struct term {
unsigned magic;
#define TERM_MAGIC 0x1c258f0f
struct vtclog *vl;
unsigned state;
#define NTERMARG 10
int arg[NTERMARG];
int *argp;
int nlin;
int ncol;
char **vram;
int col;
int line;
};
static void
term_clear(char * const *vram, int lin, int col)
{
int i;
for (i = 0; i < lin; i++) {
memset(vram[i], ' ', col);
vram[i][col] = '\0';
}
}
static void
term_scroll(const struct term *tp)
{
int i;
char *l;
l = tp->vram[0];
for (i = 0; i < tp->nlin -1; i++)
tp->vram[i] = tp->vram[i + 1];
tp->vram[i] = l;
memset(l, ' ', tp->ncol);
}
void
Term_Dump(const struct term *tp)
{
int i;
for (i = 0; i < tp->nlin; i++)
vtc_dump(tp->vl, 3, "screen", tp->vram[i], tp->ncol);
}
static void
term_escape(struct term *tp, int c, int n)
{
int i;
switch (c) {
case 'A':
// CUU - Cursor up
if (tp->arg[0] == -1) tp->arg[0] = 1;
tp->line -= tp->arg[0];
if (tp->line < 0)
vtc_fatal(tp->vl, "ANSI A[%d] outside vram",
tp->arg[0]);
break;
case 'B':
// CUD - Cursor down
if (tp->arg[0] == -1) tp->arg[0] = 1;
if (tp->arg[0] > tp->nlin)
vtc_fatal(tp->vl, "ANSI B[%d] outside vram",
tp->arg[0]);
tp->line += tp->arg[0];
while (tp->line >= tp->nlin) {
term_scroll(tp);
tp->line--;
}
break;
case 'C':
// CUF - Cursor forward
if (tp->arg[0] == -1) tp->arg[0] = 1;
tp->col += tp->arg[0];
if (tp->col >= tp->ncol)
tp->col = tp->ncol - 1;
break;
case 'D':
// CUB - Cursor backward
if (tp->arg[0] == -1) tp->arg[0] = 1;
tp->col -= tp->arg[0];
if (tp->col < 0)
tp->col = 0;
break;
case 'h':
// SM - Set Mode (mostly ignored XXX?)
tp->col = 0;
tp->line = 0;
break;
case 'H':
// CUP - Cursor Position
if (tp->arg[0] == -1) tp->arg[0] = 1;
if (tp->arg[1] == -1) tp->arg[1] = 1;
if (tp->arg[0] > tp->nlin || tp->arg[1] > tp->ncol)
vtc_fatal(tp->vl, "ANSI H[%d,%d] outside vram",
tp->arg[0], tp->arg[1]);
tp->line = tp->arg[0] - 1;
tp->col = tp->arg[1] - 1;
break;
case 'J':
// ED - Erase in Display (0=below, 1=above, 2=all)
switch (tp->arg[0]) {
case 2:
term_clear(tp->vram, tp->nlin, tp->ncol);
break;
default:
vtc_fatal(tp->vl, "ANSI J[%d]", tp->arg[0]);
}
break;
case 'K':
// EL - Erase in line (0=right, 1=left, 2=full line)
if (tp->arg[0] == -1) tp->arg[0] = 0;
switch (tp->arg[0]) {
case 0:
for (i = tp->col; i < tp->ncol; i++)
tp->vram[tp->line][i] = ' ';
break;
case 1:
for (i = 0; i < tp->col; i++)
tp->vram[tp->line][i] = ' ';
break;
case 2:
for (i = 0; i < tp->ncol; i++)
tp->vram[tp->line][i] = ' ';
break;
default:
vtc_fatal(tp->vl, "ANSI K[%d]", tp->arg[0]);
}
break;
case 'm':
// SGG - Character Attributes (ignored)
break;
default:
for (i = 0; i < n; i++)
vtc_log(tp->vl, 4, "ANSI arg %d", tp->arg[i]);
vtc_fatal(tp->vl, "ANSI unknown (%c)", c);
break;
}
}
static void
term_char(struct term *tp, char c)
{
assert(tp->col < tp->ncol);
assert(tp->line < tp->nlin);
assert(tp->state <= 3);
switch (c) {
case 0x00:
break;
case '\b':
if (tp->col > 0)
tp->col--;
break;
case '\t':
while (++tp->col % 8)
continue;
if (tp->col >= tp->ncol) {
tp->col = 0;
term_char(tp, '\n');
}
break;
case '\n':
if (tp->line == tp->nlin - 1)
term_scroll(tp);
else
tp->line++;
break;
case '\r':
tp->col = 0;
break;
default:
if (c < ' ' || c > '~')
c = '?';
tp->vram[tp->line][tp->col++] = c;
if (tp->col >= tp->ncol) {
tp->col = 0;
term_char(tp, '\n');
}
}
}
void
Term_Feed(struct term *tp, const char *b, const char *e)
{
int i;
while (b < e) {
assert(tp->col < tp->ncol);
assert(tp->line < tp->nlin);
assert(tp->state <= 3);
switch (tp->state) {
case 0:
if (*b == '\x1b')
tp->state = 1;
else if (*(const uint8_t*)b == 0x9b)
tp->state = 2;
else
term_char(tp, *b);
b++;
break;
case 1:
if (*b++ != '[')
vtc_fatal(tp->vl, "ANSI not '[' (0x%x)",
b[-1] & 0xff);
tp->state = 2;
break;
case 2:
tp->argp = tp->arg;
for (i=0; i < NTERMARG; i++)
tp->arg[i] = -1;
tp->state = 3;
if (*b == '?')
b++;
break;
case 3:
if (tp->argp - tp->arg >= NTERMARG)
vtc_fatal(tp->vl, "ANSI too many args");
if (isdigit(*b)) {
if (*tp->argp == -1)
*tp->argp = 0;
*tp->argp *= 10;
*tp->argp += *b++ - '0';
continue;
}
if (*b == ';') {
tp->argp++;
tp->state = 3;
b++;
continue;
}
term_escape(tp, *b++, tp->argp - tp->arg);
tp->state = 0;
break;
default:
WRONG("Wrong ansi state");
}
}
}
void
Term_SetSize(struct term *tp, int lin, int col)
{
char **vram;
int i, j;
vram = calloc(lin, sizeof *tp->vram);
AN(vram);
for (i = 0; i < lin; i++) {
vram[i] = malloc(col + 1L);
AN(vram[i]);
}
term_clear(vram, lin, col);
if (tp->vram != NULL) {
for (i = 0; i < lin; i++) {
if (i >= tp->nlin)
break;
j = col;
if (j > tp->ncol)
j = tp->ncol;
memcpy(vram[i], tp->vram[i], j);
}
for (i = 0; i < tp->nlin; i++)
free(tp->vram[i]);
free(tp->vram);
}
tp->vram = vram;
tp->nlin = lin;
tp->ncol = col;
}
struct term *
Term_New(struct vtclog *vl, int lin, int col)
{
struct term *tp;
ALLOC_OBJ(tp, TERM_MAGIC);
AN(tp);
tp->vl = vl;
Term_SetSize(tp, lin, col);
tp->line = tp->nlin - 1;
return (tp);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment