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.
/*-
* 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.c 326272 2017-11-27 15:23:17Z pfg $
*/
#include "config.h"
#include <sys/types.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "vdef.h"
#include "vas.h"
#define teken_assert(x) assert(x)
/* debug messages */
#define teken_printf(x,...)
/* Private flags for t_stateflags. */
#define TS_FIRSTDIGIT 0x0001 /* First numeric digit in escape sequence. */
#define TS_INSERT 0x0002 /* Insert mode. */
#define TS_AUTOWRAP 0x0004 /* Autowrap. */
#define TS_ORIGIN 0x0008 /* Origin mode. */
#define TS_WRAPPED 0x0010 /* Next character should be printed on col 0. */
#define TS_8BIT 0x0020 /* UTF-8 disabled. */
#define TS_CONS25 0x0040 /* cons25 emulation. */
#define TS_INSTRING 0x0080 /* Inside string. */
#define TS_CURSORKEYS 0x0100 /* Cursor keys mode. */
/* Character that blanks a cell. */
#define BLANK ' '
#include "teken.h"
#include "teken_wcwidth.h"
#include "teken_scs.h"
static teken_state_t teken_state_init;
/*
* Wrappers for hooks.
*/
static inline void
teken_funcs_bell(const teken_t *t)
{
if (t->t_funcs->tf_bell != NULL)
t->t_funcs->tf_bell(t->t_softc);
}
static inline void
teken_funcs_cursor(const teken_t *t)
{
teken_assert(t->t_cursor.tp_row < t->t_winsize.tp_row);
teken_assert(t->t_cursor.tp_col < t->t_winsize.tp_col);
teken_assert(t->t_funcs->tf_cursor != NULL);
t->t_funcs->tf_cursor(t->t_softc, &t->t_cursor);
}
static inline void
teken_funcs_putchar(const teken_t *t, const teken_pos_t *p, teken_char_t c,
const teken_attr_t *a)
{
teken_assert(p->tp_row < t->t_winsize.tp_row);
teken_assert(p->tp_col < t->t_winsize.tp_col);
teken_assert(t->t_funcs->tf_putchar != NULL);
t->t_funcs->tf_putchar(t->t_softc, p, c, a);
}
static inline void
teken_funcs_fill(const teken_t *t, const teken_rect_t *r,
const teken_char_t c, const teken_attr_t *a)
{
teken_assert(r->tr_end.tp_row > r->tr_begin.tp_row);
teken_assert(r->tr_end.tp_row <= t->t_winsize.tp_row);
teken_assert(r->tr_end.tp_col > r->tr_begin.tp_col);
teken_assert(r->tr_end.tp_col <= t->t_winsize.tp_col);
teken_assert(t->t_funcs->tf_fill != NULL);
t->t_funcs->tf_fill(t->t_softc, r, c, a);
}
static inline void
teken_funcs_copy(const teken_t *t, const teken_rect_t *r, const teken_pos_t *p)
{
teken_assert(r->tr_end.tp_row > r->tr_begin.tp_row);
teken_assert(r->tr_end.tp_row <= t->t_winsize.tp_row);
teken_assert(r->tr_end.tp_col > r->tr_begin.tp_col);
teken_assert(r->tr_end.tp_col <= t->t_winsize.tp_col);
teken_assert(p->tp_row + (r->tr_end.tp_row - r->tr_begin.tp_row) <= t->t_winsize.tp_row);
teken_assert(p->tp_col + (r->tr_end.tp_col - r->tr_begin.tp_col) <= t->t_winsize.tp_col);
teken_assert(t->t_funcs->tf_copy != NULL);
t->t_funcs->tf_copy(t->t_softc, r, p);
}
static inline void
teken_funcs_param(const teken_t *t, int cmd, unsigned int value)
{
if (t->t_funcs->tf_param != NULL)
t->t_funcs->tf_param(t->t_softc, cmd, value);
}
static inline void
teken_funcs_respond(const teken_t *t, const void *buf, size_t len)
{
if (t->t_funcs->tf_respond != NULL)
t->t_funcs->tf_respond(t->t_softc, buf, len);
}
#include "teken_subr.h"
#include "teken_subr_compat.h"
/*
* Programming interface.
*/
void
teken_init(teken_t *t, const teken_funcs_t *tf, void *softc)
{
teken_pos_t tp = { .tp_row = 24, .tp_col = 80 };
t->t_funcs = tf;
t->t_softc = softc;
t->t_nextstate = teken_state_init;
t->t_stateflags = 0;
t->t_utf8_left = 0;
t->t_defattr.ta_format = 0;
t->t_defattr.ta_fgcolor = TC_WHITE;
t->t_defattr.ta_bgcolor = TC_BLACK;
teken_subr_do_reset(t);
teken_set_winsize(t, &tp);
}
static void
teken_input_char(teken_t *t, teken_char_t c)
{
/*
* There is no support for DCS and OSC. Just discard strings
* until we receive characters that may indicate string
* termination.
*/
if (t->t_stateflags & TS_INSTRING) {
switch (c) {
case '\x1B':
t->t_stateflags &= ~TS_INSTRING;
break;
case '\a':
t->t_stateflags &= ~TS_INSTRING;
return;
default:
return;
}
}
switch (c) {
case '\0':
break;
case '\a':
teken_subr_bell(t);
break;
case '\b':
teken_subr_backspace(t);
break;
case '\n':
case '\x0B':
teken_subr_newline(t);
break;
case '\x0C':
teken_subr_newpage(t);
break;
case '\x0E':
if (t->t_stateflags & TS_CONS25)
t->t_nextstate(t, c);
else
t->t_curscs = 1;
break;
case '\x0F':
if (t->t_stateflags & TS_CONS25)
t->t_nextstate(t, c);
else
t->t_curscs = 0;
break;
case '\r':
teken_subr_carriage_return(t);
break;
case '\t':
teken_subr_horizontal_tab(t);
break;
default:
t->t_nextstate(t, c);
break;
}
/* Post-processing assertions. */
teken_assert(t->t_cursor.tp_row >= t->t_originreg.ts_begin);
teken_assert(t->t_cursor.tp_row < t->t_originreg.ts_end);
teken_assert(t->t_cursor.tp_row < t->t_winsize.tp_row);
teken_assert(t->t_cursor.tp_col < t->t_winsize.tp_col);
teken_assert(t->t_saved_cursor.tp_row < t->t_winsize.tp_row);
teken_assert(t->t_saved_cursor.tp_col < t->t_winsize.tp_col);
teken_assert(t->t_scrollreg.ts_end <= t->t_winsize.tp_row);
teken_assert(t->t_scrollreg.ts_begin < t->t_scrollreg.ts_end);
/* Origin region has to be window size or the same as scrollreg. */
teken_assert((t->t_originreg.ts_begin == t->t_scrollreg.ts_begin &&
t->t_originreg.ts_end == t->t_scrollreg.ts_end) ||
(t->t_originreg.ts_begin == 0 &&
t->t_originreg.ts_end == t->t_winsize.tp_row));
}
static void
teken_input_byte(teken_t *t, unsigned char c)
{
/*
* UTF-8 handling.
*/
if ((c & 0x80) == 0x00 || t->t_stateflags & TS_8BIT) {
/* One-byte sequence. */
t->t_utf8_left = 0;
teken_input_char(t, c);
} else if ((c & 0xe0) == 0xc0) {
/* Two-byte sequence. */
t->t_utf8_left = 1;
t->t_utf8_partial = c & 0x1f;
} else if ((c & 0xf0) == 0xe0) {
/* Three-byte sequence. */
t->t_utf8_left = 2;
t->t_utf8_partial = c & 0x0f;
} else if ((c & 0xf8) == 0xf0) {
/* Four-byte sequence. */
t->t_utf8_left = 3;
t->t_utf8_partial = c & 0x07;
} else if ((c & 0xc0) == 0x80) {
if (t->t_utf8_left == 0)
return;
t->t_utf8_left--;
t->t_utf8_partial = (t->t_utf8_partial << 6) | (c & 0x3f);
if (t->t_utf8_left == 0) {
teken_printf("Got UTF-8 char %x\n", t->t_utf8_partial);
teken_input_char(t, t->t_utf8_partial);
}
}
}
void
teken_input(teken_t *t, const void *buf, size_t len)
{
const char *c = buf;
while (len-- > 0)
teken_input_byte(t, *c++);
}
const teken_pos_t *
teken_get_cursor(const teken_t *t)
{
return (&t->t_cursor);
}
void
teken_set_cursor(teken_t *t, const teken_pos_t *p)
{
/* XXX: bounds checking with originreg! */
teken_assert(p->tp_row < t->t_winsize.tp_row);
teken_assert(p->tp_col < t->t_winsize.tp_col);
t->t_cursor = *p;
}
const teken_attr_t *
teken_get_curattr(const teken_t *t)
{
return (&t->t_curattr);
}
void
teken_set_curattr(teken_t *t, const teken_attr_t *a)
{
t->t_curattr = *a;
}
const teken_attr_t *
teken_get_defattr(const teken_t *t)
{
return (&t->t_defattr);
}
void
teken_set_defattr(teken_t *t, const teken_attr_t *a)
{
t->t_curattr = t->t_saved_curattr = t->t_defattr = *a;
}
const teken_pos_t *
teken_get_winsize(const teken_t *t)
{
return (&t->t_winsize);
}
static void
teken_trim_cursor_pos(teken_t *t, const teken_pos_t *new)
{
const teken_pos_t *cur;
cur = &t->t_winsize;
if (cur->tp_row < new->tp_row || cur->tp_col < new->tp_col)
return;
if (t->t_cursor.tp_row >= new->tp_row)
t->t_cursor.tp_row = new->tp_row - 1;
if (t->t_cursor.tp_col >= new->tp_col)
t->t_cursor.tp_col = new->tp_col - 1;
}
void
teken_set_winsize(teken_t *t, const teken_pos_t *p)
{
teken_trim_cursor_pos(t, p);
t->t_winsize = *p;
teken_subr_do_reset(t);
}
void
teken_set_winsize_noreset(teken_t *t, const teken_pos_t *p)
{
teken_trim_cursor_pos(t, p);
t->t_winsize = *p;
teken_subr_do_resize(t);
}
void
teken_set_8bit(teken_t *t)
{
t->t_stateflags |= TS_8BIT;
}
void
teken_set_cons25(teken_t *t)
{
t->t_stateflags |= TS_CONS25;
}
/*
* State machine.
*/
static void
teken_state_switch(teken_t *t, teken_state_t *s)
{
t->t_nextstate = s;
t->t_curnum = 0;
t->t_stateflags |= TS_FIRSTDIGIT;
}
static int
teken_state_numbers(teken_t *t, teken_char_t c)
{
teken_assert(t->t_curnum < T_NUMSIZE);
if (c >= '0' && c <= '9') {
if (t->t_stateflags & TS_FIRSTDIGIT) {
/* First digit. */
t->t_stateflags &= ~TS_FIRSTDIGIT;
t->t_nums[t->t_curnum] = c - '0';
} else if (t->t_nums[t->t_curnum] < UINT_MAX / 100) {
/*
* There is no need to continue parsing input
* once the value exceeds the size of the
* terminal. It would only allow for integer
* overflows when performing arithmetic on the
* cursor position.
*
* Ignore any further digits if the value is
* already UINT_MAX / 100.
*/
t->t_nums[t->t_curnum] =
t->t_nums[t->t_curnum] * 10 + c - '0';
}
return (1);
} else if (c == ';') {
if (t->t_stateflags & TS_FIRSTDIGIT)
t->t_nums[t->t_curnum] = 0;
/* Only allow a limited set of arguments. */
if (++t->t_curnum == T_NUMSIZE) {
teken_state_switch(t, teken_state_init);
return (1);
}
t->t_stateflags |= TS_FIRSTDIGIT;
return (1);
} else {
if (t->t_stateflags & TS_FIRSTDIGIT && t->t_curnum > 0) {
/* Finish off the last empty argument. */
t->t_nums[t->t_curnum] = 0;
t->t_curnum++;
} else if ((t->t_stateflags & TS_FIRSTDIGIT) == 0) {
/* Also count the last argument. */
t->t_curnum++;
}
}
return (0);
}
#define k TC_BLACK
#define b TC_BLUE
#define y TC_BROWN
#define c TC_CYAN
#define g TC_GREEN
#define m TC_MAGENTA
#define r TC_RED
#define w TC_WHITE
#define K (TC_BLACK | TC_LIGHT)
#define B (TC_BLUE | TC_LIGHT)
#define Y (TC_BROWN | TC_LIGHT)
#define C (TC_CYAN | TC_LIGHT)
#define G (TC_GREEN | TC_LIGHT)
#define M (TC_MAGENTA | TC_LIGHT)
#define R (TC_RED | TC_LIGHT)
#define W (TC_WHITE | TC_LIGHT)
/**
* The xterm-256 color map has steps of 0x28 (in the range 0-0xff), except
* for the first step which is 0x5f. Scale to the range 0-6 by dividing
* by 0x28 and rounding down. The range of 0-5 cannot represent the
* larger first step.
*
* This table is generated by the follow rules:
* - if all components are equal, the result is black for (0, 0, 0) and
* (2, 2, 2), else white; otherwise:
* - subtract the smallest component from all components
* - if this gives only one nonzero component, then that is the color
* - else if one component is 2 or more larger than the other nonzero one,
* then that component gives the color
* - else there are 2 nonzero components. The color is that of a small
* equal mixture of these components (cyan, yellow or magenta). E.g.,
* (0, 5, 6) (Turquoise2) is a much purer cyan than (0, 2, 3)
* (DeepSkyBlue4), but we map both to cyan since we can't represent
* delicate shades of either blue or cyan and blue would be worse.
* Here it is important that components of 1 never occur. Blue would
* be twice as large as green in (0, 1, 2).
*/
static const teken_color_t teken_256to8tab[] = {
/* xterm normal colors: */
k, r, g, y, b, m, c, w,
/* xterm bright colors: */
k, r, g, y, b, m, c, w,
/* Red0 submap. */
k, b, b, b, b, b,
g, c, c, b, b, b,
g, c, c, c, b, b,
g, g, c, c, c, b,
g, g, g, c, c, c,
g, g, g, g, c, c,
/* Red2 submap. */
r, m, m, b, b, b,
y, k, b, b, b, b,
y, g, c, c, b, b,
g, g, c, c, c, b,
g, g, g, c, c, c,
g, g, g, g, c, c,
/* Red3 submap. */
r, m, m, m, b, b,
y, r, m, m, b, b,
y, y, w, b, b, b,
y, y, g, c, c, b,
g, g, g, c, c, c,
g, g, g, g, c, c,
/* Red4 submap. */
r, r, m, m, m, b,
r, r, m, m, m, b,
y, y, r, m, m, b,
y, y, y, w, b, b,
y, y, y, g, c, c,
g, g, g, g, c, c,
/* Red5 submap. */
r, r, r, m, m, m,
r, r, r, m, m, m,
r, r, r, m, m, m,
y, y, y, r, m, m,
y, y, y, y, w, b,
y, y, y, y, g, c,
/* Red6 submap. */
r, r, r, r, m, m,
r, r, r, r, m, m,
r, r, r, r, m, m,
r, r, r, r, m, m,
y, y, y, y, r, m,
y, y, y, y, y, w,
/* Grey submap. */
k, k, k, k, k, k,
k, k, k, k, k, k,
w, w, w, w, w, w,
w, w, w, w, w, w,
};
/*
* This table is generated from the previous one by setting TC_LIGHT for
* entries whose luminosity in the xterm256 color map is 60% or larger.
* Thus the previous table is currently not really needed. It will be
* used for different fine tuning of the tables.
*/
static const teken_color_t teken_256to16tab[] = {
/* xterm normal colors: */
k, r, g, y, b, m, c, w,
/* xterm bright colors: */
K, R, G, Y, B, M, C, W,
/* Red0 submap. */
k, b, b, b, b, b,
g, c, c, b, b, b,
g, c, c, c, b, b,
g, g, c, c, c, b,
g, g, g, c, c, c,
g, g, g, g, c, c,
/* Red2 submap. */
r, m, m, b, b, b,
y, K, b, b, B, B,
y, g, c, c, B, B,
g, g, c, c, C, B,
g, G, G, C, C, C,
g, G, G, G, C, C,
/* Red3 submap. */
r, m, m, m, b, b,
y, r, m, m, B, B,
y, y, w, B, B, B,
y, y, G, C, C, B,
g, G, G, C, C, C,
g, G, G, G, C, C,
/* Red4 submap. */
r, r, m, m, m, b,
r, r, m, m, M, B,
y, y, R, M, M, B,
y, y, Y, W, B, B,
y, Y, Y, G, C, C,
g, G, G, G, C, C,
/* Red5 submap. */
r, r, r, m, m, m,
r, R, R, M, M, M,
r, R, R, M, M, M,
y, Y, Y, R, M, M,
y, Y, Y, Y, W, B,
y, Y, Y, Y, G, C,
/* Red6 submap. */
r, r, r, r, m, m,
r, R, R, R, M, M,
r, R, R, R, M, M,
r, R, R, R, M, M,
y, Y, Y, Y, R, M,
y, Y, Y, Y, Y, W,
/* Grey submap. */
k, k, k, k, k, k,
K, K, K, K, K, K,
w, w, w, w, w, w,
W, W, W, W, W, W,
};
#undef k
#undef b
#undef y
#undef c
#undef g
#undef m
#undef r
#undef w
#undef K
#undef B
#undef Y
#undef C
#undef G
#undef M
#undef R
#undef W
teken_color_t
teken_256to8(teken_color_t c)
{
return (teken_256to8tab[c % 256]);
}
teken_color_t
teken_256to16(teken_color_t c)
{
return (teken_256to16tab[c % 256]);
}
static const char * const special_strings_cons25[] = {
[TKEY_UP] = "\x1B[A", [TKEY_DOWN] = "\x1B[B",
[TKEY_LEFT] = "\x1B[D", [TKEY_RIGHT] = "\x1B[C",
[TKEY_HOME] = "\x1B[H", [TKEY_END] = "\x1B[F",
[TKEY_INSERT] = "\x1B[L", [TKEY_DELETE] = "\x7F",
[TKEY_PAGE_UP] = "\x1B[I", [TKEY_PAGE_DOWN] = "\x1B[G",
[TKEY_F1] = "\x1B[M", [TKEY_F2] = "\x1B[N",
[TKEY_F3] = "\x1B[O", [TKEY_F4] = "\x1B[P",
[TKEY_F5] = "\x1B[Q", [TKEY_F6] = "\x1B[R",
[TKEY_F7] = "\x1B[S", [TKEY_F8] = "\x1B[T",
[TKEY_F9] = "\x1B[U", [TKEY_F10] = "\x1B[V",
[TKEY_F11] = "\x1B[W", [TKEY_F12] = "\x1B[X",
};
static const char * const special_strings_ckeys[] = {
[TKEY_UP] = "\x1BOA", [TKEY_DOWN] = "\x1BOB",
[TKEY_LEFT] = "\x1BOD", [TKEY_RIGHT] = "\x1BOC",
[TKEY_HOME] = "\x1BOH", [TKEY_END] = "\x1BOF",
};
static const char * const special_strings_normal[] = {
[TKEY_UP] = "\x1B[A", [TKEY_DOWN] = "\x1B[B",
[TKEY_LEFT] = "\x1B[D", [TKEY_RIGHT] = "\x1B[C",
[TKEY_HOME] = "\x1B[H", [TKEY_END] = "\x1B[F",
[TKEY_INSERT] = "\x1B[2~", [TKEY_DELETE] = "\x1B[3~",
[TKEY_PAGE_UP] = "\x1B[5~", [TKEY_PAGE_DOWN] = "\x1B[6~",
[TKEY_F1] = "\x1BOP", [TKEY_F2] = "\x1BOQ",
[TKEY_F3] = "\x1BOR", [TKEY_F4] = "\x1BOS",
[TKEY_F5] = "\x1B[15~", [TKEY_F6] = "\x1B[17~",
[TKEY_F7] = "\x1B[18~", [TKEY_F8] = "\x1B[19~",
[TKEY_F9] = "\x1B[20~", [TKEY_F10] = "\x1B[21~",
[TKEY_F11] = "\x1B[23~", [TKEY_F12] = "\x1B[24~",
};
const char *
teken_get_sequence(const teken_t *t, unsigned int k)
{
/* Cons25 mode. */
if (t->t_stateflags & TS_CONS25 &&
k < sizeof special_strings_cons25 / sizeof(char *))
return (special_strings_cons25[k]);
/* Cursor keys mode. */
if (t->t_stateflags & TS_CURSORKEYS &&
k < sizeof special_strings_ckeys / sizeof(char *))
return (special_strings_ckeys[k]);
/* Default xterm sequences. */
if (k < sizeof special_strings_normal / sizeof(char *))
return (special_strings_normal[k]);
return (NULL);
}
#include "teken_state.h"
/*-
* 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);
}
/*-
* 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.h 326272 2017-11-27 15:23:17Z pfg $
*/
static void teken_subr_cursor_up(teken_t *, unsigned int);
static void teken_subr_erase_line(const teken_t *, unsigned int);
static void teken_subr_regular_character(teken_t *, teken_char_t);
static void teken_subr_reset_to_initial_state(teken_t *);
static void teken_subr_save_cursor(teken_t *);
static inline int
teken_tab_isset(const teken_t *t, unsigned int col)
{
unsigned int b, o;
if (col >= T_NUMCOL)
return ((col % 8) == 0);
b = col / (sizeof(unsigned int) * 8);
o = col % (sizeof(unsigned int) * 8);
return (t->t_tabstops[b] & (1U << o));
}
static inline void
teken_tab_clear(teken_t *t, unsigned int col)
{
unsigned int b, o;
if (col >= T_NUMCOL)
return;
b = col / (sizeof(unsigned int) * 8);
o = col % (sizeof(unsigned int) * 8);
t->t_tabstops[b] &= ~(1U << o);
}
static inline void
teken_tab_set(teken_t *t, unsigned int col)
{
unsigned int b, o;
if (col >= T_NUMCOL)
return;
b = col / (sizeof(unsigned int) * 8);
o = col % (sizeof(unsigned int) * 8);
t->t_tabstops[b] |= 1U << o;
}
static void
teken_tab_default(teken_t *t)
{
unsigned int i;
memset(t->t_tabstops, 0, T_NUMCOL / 8);
for (i = 8; i < T_NUMCOL; i += 8)
teken_tab_set(t, i);
}
static void
teken_subr_do_scroll(const teken_t *t, int amount)
{
teken_rect_t tr;
teken_pos_t tp;
teken_assert(t->t_cursor.tp_row <= t->t_winsize.tp_row);
teken_assert(t->t_scrollreg.ts_end <= t->t_winsize.tp_row);
teken_assert(amount != 0);
/* Copy existing data 1 line up. */
if (amount > 0) {
/* Scroll down. */
/* Copy existing data up. */
if (t->t_scrollreg.ts_begin + amount < t->t_scrollreg.ts_end) {
tr.tr_begin.tp_row = t->t_scrollreg.ts_begin + amount;
tr.tr_begin.tp_col = 0;
tr.tr_end.tp_row = t->t_scrollreg.ts_end;
tr.tr_end.tp_col = t->t_winsize.tp_col;
tp.tp_row = t->t_scrollreg.ts_begin;
tp.tp_col = 0;
teken_funcs_copy(t, &tr, &tp);
tr.tr_begin.tp_row = t->t_scrollreg.ts_end - amount;
} else {
tr.tr_begin.tp_row = t->t_scrollreg.ts_begin;
}
/* Clear the last lines. */
tr.tr_begin.tp_col = 0;
tr.tr_end.tp_row = t->t_scrollreg.ts_end;
tr.tr_end.tp_col = t->t_winsize.tp_col;
teken_funcs_fill(t, &tr, BLANK, &t->t_curattr);
} else {
/* Scroll up. */
amount = -amount;
/* Copy existing data down. */
if (t->t_scrollreg.ts_begin + amount < t->t_scrollreg.ts_end) {
tr.tr_begin.tp_row = t->t_scrollreg.ts_begin;
tr.tr_begin.tp_col = 0;
tr.tr_end.tp_row = t->t_scrollreg.ts_end - amount;
tr.tr_end.tp_col = t->t_winsize.tp_col;
tp.tp_row = t->t_scrollreg.ts_begin + amount;
tp.tp_col = 0;
teken_funcs_copy(t, &tr, &tp);
tr.tr_end.tp_row = t->t_scrollreg.ts_begin + amount;
} else {
tr.tr_end.tp_row = t->t_scrollreg.ts_end;
}
/* Clear the first lines. */
tr.tr_begin.tp_row = t->t_scrollreg.ts_begin;
tr.tr_begin.tp_col = 0;
tr.tr_end.tp_col = t->t_winsize.tp_col;
teken_funcs_fill(t, &tr, BLANK, &t->t_curattr);
}
}
static ssize_t
teken_subr_do_cpr(const teken_t *t, unsigned int cmd, char response[16])
{
switch (cmd) {
case 5: /* Operating status. */
strcpy(response, "0n");
return (2);
case 6: { /* Cursor position. */
int len;
len = snprintf(response, 16, "%u;%uR",
(t->t_cursor.tp_row - t->t_originreg.ts_begin) + 1,
t->t_cursor.tp_col + 1);
if (len >= 16)
return (-1);
return (len);
}
case 15: /* Printer status. */
strcpy(response, "13n");
return (3);
case 25: /* UDK status. */
strcpy(response, "20n");
return (3);
case 26: /* Keyboard status. */
strcpy(response, "27;1n");
return (5);
default:
teken_printf("Unknown DSR\n");
return (-1);
}
}
static void
teken_subr_alignment_test(teken_t *t)
{
teken_rect_t tr;
t->t_cursor.tp_row = t->t_cursor.tp_col = 0;
t->t_scrollreg.ts_begin = 0;
t->t_scrollreg.ts_end = t->t_winsize.tp_row;
t->t_originreg = t->t_scrollreg;
t->t_stateflags &= ~(TS_WRAPPED|TS_ORIGIN);
teken_funcs_cursor(t);
tr.tr_begin.tp_row = 0;
tr.tr_begin.tp_col = 0;
tr.tr_end = t->t_winsize;
teken_funcs_fill(t, &tr, 'E', &t->t_defattr);
}
static void
teken_subr_backspace(teken_t *t)
{
if (t->t_stateflags & TS_CONS25) {
if (t->t_cursor.tp_col == 0) {
if (t->t_cursor.tp_row == t->t_originreg.ts_begin)
return;
t->t_cursor.tp_row--;
t->t_cursor.tp_col = t->t_winsize.tp_col - 1;
} else {
t->t_cursor.tp_col--;
}
} else {
if (t->t_cursor.tp_col == 0)
return;
t->t_cursor.tp_col--;
t->t_stateflags &= ~TS_WRAPPED;
}
teken_funcs_cursor(t);
}
static void
teken_subr_bell(const teken_t *t)
{
teken_funcs_bell(t);
}
static void
teken_subr_carriage_return(teken_t *t)
{
t->t_cursor.tp_col = 0;
t->t_stateflags &= ~TS_WRAPPED;
teken_funcs_cursor(t);
}
static void
teken_subr_cursor_backward(teken_t *t, unsigned int ncols)
{
if (ncols > t->t_cursor.tp_col)
t->t_cursor.tp_col = 0;
else
t->t_cursor.tp_col -= ncols;
t->t_stateflags &= ~TS_WRAPPED;
teken_funcs_cursor(t);
}
static void
teken_subr_cursor_backward_tabulation(teken_t *t, unsigned int ntabs)
{
do {
/* Stop when we've reached the beginning of the line. */
if (t->t_cursor.tp_col == 0)
break;
t->t_cursor.tp_col--;
/* Tab marker set. */
if (teken_tab_isset(t, t->t_cursor.tp_col))
ntabs--;
} while (ntabs > 0);
teken_funcs_cursor(t);
}
static void
teken_subr_cursor_down(teken_t *t, unsigned int nrows)
{
if (t->t_cursor.tp_row + nrows >= t->t_scrollreg.ts_end)
t->t_cursor.tp_row = t->t_scrollreg.ts_end - 1;
else
t->t_cursor.tp_row += nrows;
t->t_stateflags &= ~TS_WRAPPED;
teken_funcs_cursor(t);
}
static void
teken_subr_cursor_forward(teken_t *t, unsigned int ncols)
{
if (t->t_cursor.tp_col + ncols >= t->t_winsize.tp_col)
t->t_cursor.tp_col = t->t_winsize.tp_col - 1;
else
t->t_cursor.tp_col += ncols;
t->t_stateflags &= ~TS_WRAPPED;
teken_funcs_cursor(t);
}
static void
teken_subr_cursor_forward_tabulation(teken_t *t, unsigned int ntabs)
{
do {
/* Stop when we've reached the end of the line. */
if (t->t_cursor.tp_col == t->t_winsize.tp_col - 1)
break;
t->t_cursor.tp_col++;
/* Tab marker set. */
if (teken_tab_isset(t, t->t_cursor.tp_col))
ntabs--;
} while (ntabs > 0);
teken_funcs_cursor(t);
}
static void
teken_subr_cursor_next_line(teken_t *t, unsigned int ncols)
{
t->t_cursor.tp_col = 0;
teken_subr_cursor_down(t, ncols);
}
static void
teken_subr_cursor_position(teken_t *t, unsigned int row, unsigned int col)
{
row = (row - 1) + t->t_originreg.ts_begin;
t->t_cursor.tp_row = row < t->t_originreg.ts_end ?
row : t->t_originreg.ts_end - 1;
col--;
t->t_cursor.tp_col = col < t->t_winsize.tp_col ?
col : t->t_winsize.tp_col - 1;
t->t_stateflags &= ~TS_WRAPPED;
teken_funcs_cursor(t);
}
static void
teken_subr_cursor_position_report(const teken_t *t, unsigned int cmd)
{
char response[18] = "\x1B[";
ssize_t len;
len = teken_subr_do_cpr(t, cmd, response + 2);
if (len < 0)
return;
teken_funcs_respond(t, response, len + 2);
}
static void
teken_subr_cursor_previous_line(teken_t *t, unsigned int ncols)
{
t->t_cursor.tp_col = 0;
teken_subr_cursor_up(t, ncols);
}
static void
teken_subr_cursor_up(teken_t *t, unsigned int nrows)
{
if (t->t_scrollreg.ts_begin + nrows >= t->t_cursor.tp_row)
t->t_cursor.tp_row = t->t_scrollreg.ts_begin;
else
t->t_cursor.tp_row -= nrows;
t->t_stateflags &= ~TS_WRAPPED;
teken_funcs_cursor(t);
}
static void
teken_subr_delete_character(const teken_t *t, unsigned int ncols)
{
teken_rect_t tr;
tr.tr_begin.tp_row = t->t_cursor.tp_row;
tr.tr_end.tp_row = t->t_cursor.tp_row + 1;
tr.tr_end.tp_col = t->t_winsize.tp_col;
if (t->t_cursor.tp_col + ncols >= t->t_winsize.tp_col) {
tr.tr_begin.tp_col = t->t_cursor.tp_col;
} else {
/* Copy characters to the left. */
tr.tr_begin.tp_col = t->t_cursor.tp_col + ncols;
teken_funcs_copy(t, &tr, &t->t_cursor);
tr.tr_begin.tp_col = t->t_winsize.tp_col - ncols;
}
/* Blank trailing columns. */
teken_funcs_fill(t, &tr, BLANK, &t->t_curattr);
}
static void
teken_subr_delete_line(const teken_t *t, unsigned int nrows)
{
teken_rect_t tr;
/* Ignore if outside scrolling region. */
if (t->t_cursor.tp_row < t->t_scrollreg.ts_begin ||
t->t_cursor.tp_row >= t->t_scrollreg.ts_end)
return;
tr.tr_begin.tp_col = 0;
tr.tr_end.tp_row = t->t_scrollreg.ts_end;
tr.tr_end.tp_col = t->t_winsize.tp_col;
if (t->t_cursor.tp_row + nrows >= t->t_scrollreg.ts_end) {
tr.tr_begin.tp_row = t->t_cursor.tp_row;
} else {
teken_pos_t tp;
/* Copy rows up. */
tr.tr_begin.tp_row = t->t_cursor.tp_row + nrows;
tp.tp_row = t->t_cursor.tp_row;
tp.tp_col = 0;
teken_funcs_copy(t, &tr, &tp);
tr.tr_begin.tp_row = t->t_scrollreg.ts_end - nrows;
}
/* Blank trailing rows. */
teken_funcs_fill(t, &tr, BLANK, &t->t_curattr);
}
static void
teken_subr_device_control_string(teken_t *t)
{
teken_printf("Unsupported device control string\n");
t->t_stateflags |= TS_INSTRING;
}
static void
teken_subr_device_status_report(const teken_t *t, unsigned int cmd)
{
char response[19] = "\x1B[?";
ssize_t len;
len = teken_subr_do_cpr(t, cmd, response + 3);
if (len < 0)
return;
teken_funcs_respond(t, response, len + 3);
}
static void
teken_subr_double_height_double_width_line_top(const teken_t *t)
{
(void)t;
teken_printf("double height double width top\n");
}
static void
teken_subr_double_height_double_width_line_bottom(const teken_t *t)
{
(void)t;
teken_printf("double height double width bottom\n");
}
static void
teken_subr_erase_character(const teken_t *t, unsigned int ncols)
{
teken_rect_t tr;
tr.tr_begin = t->t_cursor;
tr.tr_end.tp_row = t->t_cursor.tp_row + 1;
if (t->t_cursor.tp_col + ncols >= t->t_winsize.tp_col)
tr.tr_end.tp_col = t->t_winsize.tp_col;
else
tr.tr_end.tp_col = t->t_cursor.tp_col + ncols;
teken_funcs_fill(t, &tr, BLANK, &t->t_curattr);
}
static void
teken_subr_erase_display(const teken_t *t, unsigned int mode)
{
teken_rect_t r;
r.tr_begin.tp_col = 0;
r.tr_end.tp_col = t->t_winsize.tp_col;
switch (mode) {
case 1: /* Erase from the top to the cursor. */
teken_subr_erase_line(t, 1);
/* Erase lines above. */
if (t->t_cursor.tp_row == 0)
return;
r.tr_begin.tp_row = 0;
r.tr_end.tp_row = t->t_cursor.tp_row;
break;
case 2: /* Erase entire display. */
r.tr_begin.tp_row = 0;
r.tr_end.tp_row = t->t_winsize.tp_row;
break;
default: /* Erase from cursor to the bottom. */
teken_subr_erase_line(t, 0);
/* Erase lines below. */
if (t->t_cursor.tp_row == t->t_winsize.tp_row - 1)
return;
r.tr_begin.tp_row = t->t_cursor.tp_row + 1;
r.tr_end.tp_row = t->t_winsize.tp_row;
break;
}
teken_funcs_fill(t, &r, BLANK, &t->t_curattr);
}
static void
teken_subr_erase_line(const teken_t *t, unsigned int mode)
{
teken_rect_t r;
r.tr_begin.tp_row = t->t_cursor.tp_row;
r.tr_end.tp_row = t->t_cursor.tp_row + 1;
switch (mode) {
case 1: /* Erase from the beginning of the line to the cursor. */
r.tr_begin.tp_col = 0;
r.tr_end.tp_col = t->t_cursor.tp_col + 1;
break;
case 2: /* Erase entire line. */
r.tr_begin.tp_col = 0;
r.tr_end.tp_col = t->t_winsize.tp_col;
break;
default: /* Erase from cursor to the end of the line. */
r.tr_begin.tp_col = t->t_cursor.tp_col;
r.tr_end.tp_col = t->t_winsize.tp_col;
break;
}
teken_funcs_fill(t, &r, BLANK, &t->t_curattr);
}
static void
teken_subr_g0_scs_special_graphics(teken_t *t)
{
t->t_scs[0] = teken_scs_special_graphics;
}
static void
teken_subr_g0_scs_uk_national(teken_t *t)
{
t->t_scs[0] = teken_scs_uk_national;
}
static void
teken_subr_g0_scs_us_ascii(teken_t *t)
{
t->t_scs[0] = teken_scs_us_ascii;
}
static void
teken_subr_g1_scs_special_graphics(teken_t *t)
{
t->t_scs[1] = teken_scs_special_graphics;
}
static void
teken_subr_g1_scs_uk_national(teken_t *t)
{
t->t_scs[1] = teken_scs_uk_national;
}
static void
teken_subr_g1_scs_us_ascii(teken_t *t)
{
t->t_scs[1] = teken_scs_us_ascii;
}
static void
teken_subr_horizontal_position_absolute(teken_t *t, unsigned int col)
{
col--;
t->t_cursor.tp_col = col < t->t_winsize.tp_col ?
col : t->t_winsize.tp_col - 1;
t->t_stateflags &= ~TS_WRAPPED;
teken_funcs_cursor(t);
}
static void
teken_subr_horizontal_tab(teken_t *t)
{
teken_subr_cursor_forward_tabulation(t, 1);
}
static void
teken_subr_horizontal_tab_set(teken_t *t)
{
teken_tab_set(t, t->t_cursor.tp_col);
}
static void
teken_subr_index(teken_t *t)
{
if (t->t_cursor.tp_row < t->t_scrollreg.ts_end - 1) {
t->t_cursor.tp_row++;
t->t_stateflags &= ~TS_WRAPPED;
teken_funcs_cursor(t);
} else {
teken_subr_do_scroll(t, 1);
}
}
static void
teken_subr_insert_character(const teken_t *t, unsigned int ncols)
{
teken_rect_t tr;
tr.tr_begin = t->t_cursor;
tr.tr_end.tp_row = t->t_cursor.tp_row + 1;
if (t->t_cursor.tp_col + ncols >= t->t_winsize.tp_col) {
tr.tr_end.tp_col = t->t_winsize.tp_col;
} else {
teken_pos_t tp;
/* Copy characters to the right. */
tr.tr_end.tp_col = t->t_winsize.tp_col - ncols;
tp.tp_row = t->t_cursor.tp_row;
tp.tp_col = t->t_cursor.tp_col + ncols;
teken_funcs_copy(t, &tr, &tp);
tr.tr_end.tp_col = t->t_cursor.tp_col + ncols;
}
/* Blank current location. */
teken_funcs_fill(t, &tr, BLANK, &t->t_curattr);
}
static void
teken_subr_insert_line(const teken_t *t, unsigned int nrows)
{
teken_rect_t tr;
/* Ignore if outside scrolling region. */
if (t->t_cursor.tp_row < t->t_scrollreg.ts_begin ||
t->t_cursor.tp_row >= t->t_scrollreg.ts_end)
return;
tr.tr_begin.tp_row = t->t_cursor.tp_row;
tr.tr_begin.tp_col = 0;
tr.tr_end.tp_col = t->t_winsize.tp_col;
if (t->t_cursor.tp_row + nrows >= t->t_scrollreg.ts_end) {
tr.tr_end.tp_row = t->t_scrollreg.ts_end;
} else {
teken_pos_t tp;
/* Copy lines down. */
tr.tr_end.tp_row = t->t_scrollreg.ts_end - nrows;
tp.tp_row = t->t_cursor.tp_row + nrows;
tp.tp_col = 0;
teken_funcs_copy(t, &tr, &tp);
tr.tr_end.tp_row = t->t_cursor.tp_row + nrows;
}
/* Blank current location. */
teken_funcs_fill(t, &tr, BLANK, &t->t_curattr);
}
static void
teken_subr_keypad_application_mode(const teken_t *t)
{
teken_funcs_param(t, TP_KEYPADAPP, 1);
}
static void
teken_subr_keypad_numeric_mode(const teken_t *t)
{
teken_funcs_param(t, TP_KEYPADAPP, 0);
}
static void
teken_subr_newline(teken_t *t)
{
t->t_cursor.tp_row++;
if (t->t_cursor.tp_row >= t->t_scrollreg.ts_end) {
teken_subr_do_scroll(t, 1);
t->t_cursor.tp_row = t->t_scrollreg.ts_end - 1;
}
t->t_stateflags &= ~TS_WRAPPED;
teken_funcs_cursor(t);
}
static void
teken_subr_newpage(teken_t *t)
{
if (t->t_stateflags & TS_CONS25) {
teken_rect_t tr;
/* Clear screen. */
tr.tr_begin.tp_row = t->t_originreg.ts_begin;
tr.tr_begin.tp_col = 0;
tr.tr_end.tp_row = t->t_originreg.ts_end;
tr.tr_end.tp_col = t->t_winsize.tp_col;
teken_funcs_fill(t, &tr, BLANK, &t->t_curattr);
/* Cursor at top left. */
t->t_cursor.tp_row = t->t_originreg.ts_begin;
t->t_cursor.tp_col = 0;
t->t_stateflags &= ~TS_WRAPPED;
teken_funcs_cursor(t);
} else {
teken_subr_newline(t);
}
}
static void
teken_subr_next_line(teken_t *t)
{
t->t_cursor.tp_col = 0;
teken_subr_newline(t);
}
static void
teken_subr_operating_system_command(teken_t *t)
{
teken_printf("Unsupported operating system command\n");
t->t_stateflags |= TS_INSTRING;
}
static void
teken_subr_pan_down(const teken_t *t, unsigned int nrows)
{
teken_subr_do_scroll(t, (int)nrows);
}
static void
teken_subr_pan_up(const teken_t *t, unsigned int nrows)
{
teken_subr_do_scroll(t, -(int)nrows);
}
static void
teken_subr_primary_device_attributes(const teken_t *t, unsigned int request)
{
if (request == 0) {
const char response[] = "\x1B[?1;2c";
teken_funcs_respond(t, response, sizeof response - 1);
} else {
teken_printf("Unknown DA1\n");
}
}
static void
teken_subr_do_putchar(const teken_t *t, const teken_pos_t *tp, teken_char_t c,
int width)
{
if (t->t_stateflags & TS_INSERT &&
tp->tp_col < t->t_winsize.tp_col - width) {
teken_rect_t ctr;
teken_pos_t ctp;
/* Insert mode. Move existing characters to the right. */
ctr.tr_begin = *tp;
ctr.tr_end.tp_row = tp->tp_row + 1;
ctr.tr_end.tp_col = t->t_winsize.tp_col - width;
ctp.tp_row = tp->tp_row;
ctp.tp_col = tp->tp_col + width;
teken_funcs_copy(t, &ctr, &ctp);
}
teken_funcs_putchar(t, tp, c, &t->t_curattr);
if (width == 2 && tp->tp_col + 1 < t->t_winsize.tp_col) {
teken_pos_t tp2;
teken_attr_t attr;
/* Print second half of CJK fullwidth character. */
tp2.tp_row = tp->tp_row;
tp2.tp_col = tp->tp_col + 1;
attr = t->t_curattr;
attr.ta_format |= TF_CJK_RIGHT;
teken_funcs_putchar(t, &tp2, c, &attr);
}
}
static void
teken_subr_regular_character(teken_t *t, teken_char_t c)
{
int width;
if (t->t_stateflags & TS_8BIT) {
if (!(t->t_stateflags & TS_CONS25) && (c <= 0x1b || c == 0x7f))
return;
c = teken_scs_process(t, c);
width = 1;
} else {
c = teken_scs_process(t, c);
width = teken_wcwidth(c);
/* XXX: Don't process zero-width characters yet. */
if (width <= 0)
return;
}
if (t->t_stateflags & TS_CONS25) {
teken_subr_do_putchar(t, &t->t_cursor, c, width);
t->t_cursor.tp_col += width;
if (t->t_cursor.tp_col >= t->t_winsize.tp_col) {
if (t->t_cursor.tp_row == t->t_scrollreg.ts_end - 1) {
/* Perform scrolling. */
teken_subr_do_scroll(t, 1);
} else {
/* No scrolling needed. */
if (t->t_cursor.tp_row <
t->t_winsize.tp_row - 1)
t->t_cursor.tp_row++;
}
t->t_cursor.tp_col = 0;
}
} else if (t->t_stateflags & TS_AUTOWRAP &&
((t->t_stateflags & TS_WRAPPED &&
t->t_cursor.tp_col + 1 == t->t_winsize.tp_col) ||
t->t_cursor.tp_col + width > t->t_winsize.tp_col)) {
teken_pos_t tp;
/*
* Perform line wrapping, if:
* - Autowrapping is enabled, and
* - We're in the wrapped state at the last column, or
* - The character to be printed does not fit anymore.
*/
if (t->t_cursor.tp_row == t->t_scrollreg.ts_end - 1) {
/* Perform scrolling. */
teken_subr_do_scroll(t, 1);
tp.tp_row = t->t_scrollreg.ts_end - 1;
} else {
/* No scrolling needed. */
tp.tp_row = t->t_cursor.tp_row + 1;
if (tp.tp_row == t->t_winsize.tp_row) {
/*
* Corner case: regular character
* outside scrolling region, but at the
* bottom of the screen.
*/
teken_subr_do_putchar(t, &t->t_cursor,
c, width);
return;
}
}
tp.tp_col = 0;
teken_subr_do_putchar(t, &tp, c, width);
t->t_cursor.tp_row = tp.tp_row;
t->t_cursor.tp_col = width;
t->t_stateflags &= ~TS_WRAPPED;
} else {
/* No line wrapping needed. */
teken_subr_do_putchar(t, &t->t_cursor, c, width);
t->t_cursor.tp_col += width;
if (t->t_cursor.tp_col >= t->t_winsize.tp_col) {
t->t_stateflags |= TS_WRAPPED;
t->t_cursor.tp_col = t->t_winsize.tp_col - 1;
} else {
t->t_stateflags &= ~TS_WRAPPED;
}
}
teken_funcs_cursor(t);
}
static void
teken_subr_reset_dec_mode(teken_t *t, unsigned int cmd)
{
switch (cmd) {
case 1: /* Cursor keys mode. */
t->t_stateflags &= ~TS_CURSORKEYS;
break;
case 2: /* DECANM: ANSI/VT52 mode. */
teken_printf("DECRST VT52\n");
break;
case 3: /* 132 column mode. */
teken_funcs_param(t, TP_132COLS, 0);
teken_subr_reset_to_initial_state(t);
break;
case 5: /* Inverse video. */
teken_printf("DECRST inverse video\n");
break;
case 6: /* Origin mode. */
t->t_stateflags &= ~TS_ORIGIN;
t->t_originreg.ts_begin = 0;
t->t_originreg.ts_end = t->t_winsize.tp_row;
t->t_cursor.tp_row = t->t_cursor.tp_col = 0;
t->t_stateflags &= ~TS_WRAPPED;
teken_funcs_cursor(t);
break;
case 7: /* Autowrap mode. */
t->t_stateflags &= ~TS_AUTOWRAP;
break;
case 8: /* Autorepeat mode. */
teken_funcs_param(t, TP_AUTOREPEAT, 0);
break;
case 25: /* Hide cursor. */
teken_funcs_param(t, TP_SHOWCURSOR, 0);
break;
case 40: /* Disallow 132 columns. */
teken_printf("DECRST allow 132\n");
break;
case 45: /* Disable reverse wraparound. */
teken_printf("DECRST reverse wraparound\n");
break;
case 47: /* Switch to alternate buffer. */
teken_printf("Switch to alternate buffer\n");
break;
case 1000: /* Mouse input. */
teken_funcs_param(t, TP_MOUSE, 0);
break;
default:
teken_printf("Unknown DECRST: %u\n", cmd);
}
}
static void
teken_subr_reset_mode(teken_t *t, unsigned int cmd)
{
switch (cmd) {
case 4:
t->t_stateflags &= ~TS_INSERT;
break;
default:
teken_printf("Unknown reset mode: %u\n", cmd);
}
}
static void
teken_subr_do_resize(teken_t *t)
{
t->t_scrollreg.ts_begin = 0;
t->t_scrollreg.ts_end = t->t_winsize.tp_row;
t->t_originreg = t->t_scrollreg;
}
static void
teken_subr_do_reset(teken_t *t)
{
t->t_curattr = t->t_defattr;
t->t_cursor.tp_row = t->t_cursor.tp_col = 0;
t->t_scrollreg.ts_begin = 0;
t->t_scrollreg.ts_end = t->t_winsize.tp_row;
t->t_originreg = t->t_scrollreg;
t->t_stateflags &= TS_8BIT|TS_CONS25;
t->t_stateflags |= TS_AUTOWRAP;
t->t_scs[0] = teken_scs_us_ascii;
t->t_scs[1] = teken_scs_us_ascii;
t->t_curscs = 0;
teken_subr_save_cursor(t);
teken_tab_default(t);
}
static void
teken_subr_reset_to_initial_state(teken_t *t)
{
teken_subr_do_reset(t);
teken_subr_erase_display(t, 2);
teken_funcs_param(t, TP_SHOWCURSOR, 1);
teken_funcs_cursor(t);
}
static void
teken_subr_restore_cursor(teken_t *t)
{
t->t_cursor = t->t_saved_cursor;
t->t_curattr = t->t_saved_curattr;
t->t_scs[t->t_curscs] = t->t_saved_curscs;
t->t_stateflags &= ~TS_WRAPPED;
/* Get out of origin mode when the cursor is moved outside. */
if (t->t_cursor.tp_row < t->t_originreg.ts_begin ||
t->t_cursor.tp_row >= t->t_originreg.ts_end) {
t->t_stateflags &= ~TS_ORIGIN;
t->t_originreg.ts_begin = 0;
t->t_originreg.ts_end = t->t_winsize.tp_row;
}
teken_funcs_cursor(t);
}
static void
teken_subr_reverse_index(teken_t *t)
{
if (t->t_cursor.tp_row > t->t_scrollreg.ts_begin) {
t->t_cursor.tp_row--;
t->t_stateflags &= ~TS_WRAPPED;
teken_funcs_cursor(t);
} else {
teken_subr_do_scroll(t, -1);
}
}
static void
teken_subr_save_cursor(teken_t *t)
{
t->t_saved_cursor = t->t_cursor;
t->t_saved_curattr = t->t_curattr;
t->t_saved_curscs = t->t_scs[t->t_curscs];
}
static void
teken_subr_secondary_device_attributes(const teken_t *t, unsigned int request)
{
if (request == 0) {
const char response[] = "\x1B[>0;10;0c";
teken_funcs_respond(t, response, sizeof response - 1);
} else {
teken_printf("Unknown DA2\n");
}
}
static void
teken_subr_set_dec_mode(teken_t *t, unsigned int cmd)
{
switch (cmd) {
case 1: /* Cursor keys mode. */
t->t_stateflags |= TS_CURSORKEYS;
break;
case 2: /* DECANM: ANSI/VT52 mode. */
teken_printf("DECSET VT52\n");
break;
case 3: /* 132 column mode. */
teken_funcs_param(t, TP_132COLS, 1);
teken_subr_reset_to_initial_state(t);
break;
case 5: /* Inverse video. */
teken_printf("DECSET inverse video\n");
break;
case 6: /* Origin mode. */
t->t_stateflags |= TS_ORIGIN;
t->t_originreg = t->t_scrollreg;
t->t_cursor.tp_row = t->t_scrollreg.ts_begin;
t->t_cursor.tp_col = 0;
t->t_stateflags &= ~TS_WRAPPED;
teken_funcs_cursor(t);
break;
case 7: /* Autowrap mode. */
t->t_stateflags |= TS_AUTOWRAP;
break;
case 8: /* Autorepeat mode. */
teken_funcs_param(t, TP_AUTOREPEAT, 1);
break;
case 25: /* Display cursor. */
teken_funcs_param(t, TP_SHOWCURSOR, 1);
break;
case 40: /* Allow 132 columns. */
teken_printf("DECSET allow 132\n");
break;
case 45: /* Enable reverse wraparound. */
teken_printf("DECSET reverse wraparound\n");
break;
case 47: /* Switch to alternate buffer. */
teken_printf("Switch away from alternate buffer\n");
break;
case 1000: /* Mouse input. */
teken_funcs_param(t, TP_MOUSE, 1);
break;
default:
teken_printf("Unknown DECSET: %u\n", cmd);
}
}
static void
teken_subr_set_mode(teken_t *t, unsigned int cmd)
{
switch (cmd) {
case 4:
teken_printf("Insert mode\n");
t->t_stateflags |= TS_INSERT;
break;
default:
teken_printf("Unknown set mode: %u\n", cmd);
}
}
static void
teken_subr_set_graphic_rendition(teken_t *t, unsigned int ncmds,
const unsigned int cmds[])
{
unsigned int i, n;
/* No attributes means reset. */
if (ncmds == 0) {
t->t_curattr = t->t_defattr;
return;
}
for (i = 0; i < ncmds; i++) {
n = cmds[i];
switch (n) {
case 0: /* Reset. */
t->t_curattr = t->t_defattr;
break;
case 1: /* Bold. */
t->t_curattr.ta_format |= TF_BOLD;
break;
case 4: /* Underline. */
t->t_curattr.ta_format |= TF_UNDERLINE;
break;
case 5: /* Blink. */
t->t_curattr.ta_format |= TF_BLINK;
break;
case 7: /* Reverse. */
t->t_curattr.ta_format |= TF_REVERSE;
break;
case 22: /* Remove bold. */
t->t_curattr.ta_format &= ~TF_BOLD;
break;
case 24: /* Remove underline. */
t->t_curattr.ta_format &= ~TF_UNDERLINE;
break;
case 25: /* Remove blink. */
t->t_curattr.ta_format &= ~TF_BLINK;
break;
case 27: /* Remove reverse. */
t->t_curattr.ta_format &= ~TF_REVERSE;
break;
case 30: /* Set foreground color: black */
case 31: /* Set foreground color: red */
case 32: /* Set foreground color: green */
case 33: /* Set foreground color: brown */
case 34: /* Set foreground color: blue */
case 35: /* Set foreground color: magenta */
case 36: /* Set foreground color: cyan */
case 37: /* Set foreground color: white */
t->t_curattr.ta_fgcolor = n - 30;
break;
case 38: /* Set foreground color: 256 color mode */
if (i + 2 >= ncmds || cmds[i + 1] != 5)
continue;
t->t_curattr.ta_fgcolor = cmds[i + 2];
i += 2;
break;
case 39: /* Set default foreground color. */
t->t_curattr.ta_fgcolor = t->t_defattr.ta_fgcolor;
break;
case 40: /* Set background color: black */
case 41: /* Set background color: red */
case 42: /* Set background color: green */
case 43: /* Set background color: brown */
case 44: /* Set background color: blue */
case 45: /* Set background color: magenta */
case 46: /* Set background color: cyan */
case 47: /* Set background color: white */
t->t_curattr.ta_bgcolor = n - 40;
break;
case 48: /* Set background color: 256 color mode */
if (i + 2 >= ncmds || cmds[i + 1] != 5)
continue;
t->t_curattr.ta_bgcolor = cmds[i + 2];
i += 2;
break;
case 49: /* Set default background color. */
t->t_curattr.ta_bgcolor = t->t_defattr.ta_bgcolor;
break;
case 90: /* Set bright foreground color: black */
case 91: /* Set bright foreground color: red */
case 92: /* Set bright foreground color: green */
case 93: /* Set bright foreground color: brown */
case 94: /* Set bright foreground color: blue */
case 95: /* Set bright foreground color: magenta */
case 96: /* Set bright foreground color: cyan */
case 97: /* Set bright foreground color: white */
t->t_curattr.ta_fgcolor = (n - 90) + 8;
break;
case 100: /* Set bright background color: black */
case 101: /* Set bright background color: red */
case 102: /* Set bright background color: green */
case 103: /* Set bright background color: brown */
case 104: /* Set bright background color: blue */
case 105: /* Set bright background color: magenta */
case 106: /* Set bright background color: cyan */
case 107: /* Set bright background color: white */
t->t_curattr.ta_bgcolor = (n - 100) + 8;
break;
default:
teken_printf("unsupported attribute %u\n", n);
}
}
}
static void
teken_subr_set_top_and_bottom_margins(teken_t *t, unsigned int top,
unsigned int bottom)
{
/* Adjust top row number. */
if (top > 0)
top--;
/* Adjust bottom row number. */
if (bottom == 0 || bottom > t->t_winsize.tp_row)
bottom = t->t_winsize.tp_row;
/* Invalid arguments. */
if (top >= bottom - 1) {
top = 0;
bottom = t->t_winsize.tp_row;
}
/* Apply scrolling region. */
t->t_scrollreg.ts_begin = top;
t->t_scrollreg.ts_end = bottom;
if (t->t_stateflags & TS_ORIGIN)
t->t_originreg = t->t_scrollreg;
/* Home cursor to the top left of the scrolling region. */
t->t_cursor.tp_row = t->t_originreg.ts_begin;
t->t_cursor.tp_col = 0;
t->t_stateflags &= ~TS_WRAPPED;
teken_funcs_cursor(t);
}
static void
teken_subr_single_height_double_width_line(const teken_t *t)
{
(void)t;
teken_printf("single height double width???\n");
}
static void
teken_subr_single_height_single_width_line(const teken_t *t)
{
(void)t;
teken_printf("single height single width???\n");
}
static void
teken_subr_string_terminator(const teken_t *t)
{
(void)t;
/*
* Strings are already terminated in teken_input_char() when ^[
* is inserted.
*/
}
static void
teken_subr_tab_clear(teken_t *t, unsigned int cmd)
{
switch (cmd) {
case 0:
teken_tab_clear(t, t->t_cursor.tp_col);
break;
case 3:
memset(t->t_tabstops, 0, T_NUMCOL / 8);
break;
default:
break;
}
}
static void
teken_subr_vertical_position_absolute(teken_t *t, unsigned int row)
{
row = (row - 1) + t->t_originreg.ts_begin;
t->t_cursor.tp_row = row < t->t_originreg.ts_end ?
row : t->t_originreg.ts_end - 1;
t->t_stateflags &= ~TS_WRAPPED;
teken_funcs_cursor(t);
}
/*-
* 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