Skip to content
Fetching contributors…
Cannot retrieve contributors at this time
8617 lines (6987 sloc) 210 KB
/* File: "os_tty.c" */
/* Copyright (c) 1994-2012 by Marc Feeley, All Rights Reserved. */
/*
* This module implements the operating system specific routines
* related to ttys.
*/
#define ___INCLUDED_FROM_OS_TTY
#define ___VERSION 406005
#include "gambit.h"
#include "os_base.h"
#include "os_tty.h"
#include "os_shell.h"
#include "os_io.h"
#include "setup.h"
#include "c_intf.h"
/*---------------------------------------------------------------------------*/
/* Extensible string routines */
___HIDDEN ___SCMOBJ extensible_string_setup
___P((extensible_string *str,
int n),
(str,
n)
extensible_string *str;
int n;)
{
#define EXTENSIBLE_STRING_INITIAL_BUFFER_SIZE 32
if (n < EXTENSIBLE_STRING_INITIAL_BUFFER_SIZE)
n = EXTENSIBLE_STRING_INITIAL_BUFFER_SIZE;
str->buffer = ___CAST(extensible_string_char*,
___alloc_mem (n * sizeof (extensible_string_char)));
if (str->buffer == NULL)
return ___FIX(___HEAP_OVERFLOW_ERR);
str->length = 0;
str->max_length = n;
return ___FIX(___NO_ERR);
}
___HIDDEN void extensible_string_cleanup
___P((extensible_string *str),
(str)
extensible_string *str;)
{
___free_mem (str->buffer);
}
___HIDDEN ___SCMOBJ extensible_string_copy
___P((extensible_string_char *src,
int len,
extensible_string *dst,
int fudge),
(src,
len,
dst,
fudge)
extensible_string_char *src;
int len;
extensible_string *dst;
int fudge;)
{
extensible_string_char *buf;
buf = ___CAST(extensible_string_char*,
___alloc_mem ((len+fudge) * sizeof (extensible_string_char)));
if (buf == NULL)
return ___FIX(___HEAP_OVERFLOW_ERR);
dst->buffer = buf;
dst->length = len;
dst->max_length = len+fudge;
while (len-- > 0)
buf[len] = src[len];
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ extensible_string_set_length
___P((extensible_string *str,
int len,
int fudge),
(str,
len,
fudge)
extensible_string *str;
int len;
int fudge;)
{
if (len > str->max_length || 2*len+1 < str->max_length)
{
int i;
int new_max_length = (fudge<0) ? 3*len/2+1 : len+fudge;
extensible_string_char *old_buffer = str->buffer;
extensible_string_char *new_buffer =
___CAST(extensible_string_char*,
___alloc_mem (new_max_length *
sizeof (extensible_string_char)));
if (new_buffer == NULL)
return ___FIX(___HEAP_OVERFLOW_ERR);
i = str->length;
if (i > len)
i = len;
while (i-- > 0)
new_buffer[i] = old_buffer[i];
___free_mem (old_buffer);
str->buffer = new_buffer;
str->max_length = new_max_length;
}
str->length = len;
return ___FIX(___NO_ERR);
}
___HIDDEN void extensible_string_delete
___P((extensible_string *str,
int pos,
int len),
(str,
pos,
len)
extensible_string *str;
int pos;
int len;)
{
int i;
if (pos < 0)
pos = 0;
else if (pos > str->length)
pos = str->length;
i = str->length - pos;
if (len < 0)
len = 0;
else if (len > i)
len = i;
for (i=pos; i<str->length-len; i++)
str->buffer[i] = str->buffer[i+len];
str->length -= len;
}
___HIDDEN ___SCMOBJ extensible_string_insert
___P((extensible_string *str,
int pos,
int len,
extensible_string_char *chars),
(str,
pos,
len,
chars)
extensible_string *str;
int pos;
int len;
extensible_string_char *chars;)
{
___SCMOBJ e = ___FIX(___NO_ERR);
int i;
if (len > 0)
{
if (pos < 0)
pos = 0;
else if (pos > str->length)
pos = str->length;
if ((e = extensible_string_set_length (str, str->length+len, -1))
== ___FIX(___NO_ERR))
{
for (i=str->length-len-1; i>=pos; i--)
str->buffer[i+len] = str->buffer[i];
for (i=len-1; i>=0; i--)
str->buffer[i+pos] = chars[i];
}
}
return e;
}
___HIDDEN ___SCMOBJ extensible_string_insert_at_end
___P((extensible_string *str,
int len,
extensible_string_char *chars),
(str,
len,
chars)
extensible_string *str;
int len;
extensible_string_char *chars;)
{
return extensible_string_insert (str, str->length, len, chars);
}
/*---------------------------------------------------------------------------*/
/* TTY mode setting */
___HIDDEN ___SCMOBJ ___device_tty_mode_get
___P((___device_tty *self),
(self)
___device_tty *self;)
{
___device_tty *d = self;
#ifdef USE_POSIX
#ifdef ___DEBUG_TTY
___printf ("tcgetattr d->fd = %d\n", d->fd);
#endif
if (tcgetattr (d->fd, &d->initial_termios) < 0 ||
(d->initial_flags = fcntl (d->fd, F_GETFL, 0)) < 0)
return err_code_from_errno ();
#ifdef ___DEBUG_TTY
___printf ("___device_tty_mode_get d->fd = %d\n", d->fd);
___printf (" d->initial_termios.c_iflag = 0x%08x\n", d->initial_termios.c_iflag);
___printf (" d->initial_termios.c_oflag = 0x%08x\n", d->initial_termios.c_oflag);
___printf (" d->initial_termios.c_lflag = 0x%08x\n", d->initial_termios.c_lflag);
#endif
#endif
#ifdef USE_WIN32
if (!GetConsoleMode (d->hin, &d->hin_initial_mode) ||
!GetConsoleMode (d->hout, &d->hout_initial_mode))
return err_code_from_GetLastError ();
#endif
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___device_tty_mode_save
___P((___device_tty *self),
(self)
___device_tty *self;)
{
___device_tty *d = self;
___SCMOBJ e;
if ((e = ___device_tty_mode_get (d)) != ___FIX(___NO_ERR))
return e;
d->mode_save_stack_next = ___tty_mod.mode_save_stack;
___tty_mod.mode_save_stack = d;
d->stage = TTY_STAGE_MODE_NOT_SET;
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___device_tty_mode_update
___P((___device_tty *self,
___BOOL current),
(self,
current)
___device_tty *self;
___BOOL current;)
{
___device_tty *d = self;
___SCMOBJ e = ___FIX(___NO_ERR);
#ifdef USE_POSIX
{
struct termios new_termios = d->initial_termios;
int new_flags = d->initial_flags;
#ifdef ___DEBUG_TTY
___printf ("tcsetattr d->fd = %d\n", d->fd);
___printf (" d->lineeditor_mode = %d\n", d->lineeditor_mode);
___printf (" d->input_allow_special = %d\n", d->input_allow_special);
___printf (" d->input_echo = %d\n", d->input_echo);
___printf (" d->input_raw = %d\n", d->input_raw);
___printf (" d->output_raw = %d\n", d->output_raw);
___printf (" new_termios.c_iflag = 0x%08x\n", new_termios.c_iflag);
___printf (" new_termios.c_oflag = 0x%08x\n", new_termios.c_oflag);
___printf (" new_termios.c_lflag = 0x%08x\n", new_termios.c_lflag);
#endif
if (current)
{
if (d->input_allow_special)
{
new_termios.c_iflag |= (IXON);
new_termios.c_lflag |= (IEXTEN | ISIG);
}
else
{
new_termios.c_iflag &= ~(IXON);
new_termios.c_lflag &= ~(IEXTEN | ISIG);
}
if (d->input_raw
#ifdef USE_LINEEDITOR
|| d->lineeditor_mode != LINEEDITOR_MODE_DISABLE
#endif
)
{
new_termios.c_iflag &= ~(IMAXBEL
| ISTRIP
| ICRNL
| INLCR
| IGNCR
| ICRNL
| IXON
| IXOFF
#ifdef IUCLC
| IUCLC
#endif
);
new_termios.c_iflag &= ~(ICRNL);
new_termios.c_lflag &= ~(ICANON | ECHO | ECHOCTL);
#ifndef _POSIX_VDISABLE
#define _POSIX_VDISABLE 0xff
#endif
#if 0
new_termios.c_cc[VEOF] = _POSIX_VDISABLE;
new_termios.c_cc[VEOL] = _POSIX_VDISABLE;
new_termios.c_cc[VEOL2] = _POSIX_VDISABLE;
new_termios.c_cc[VERASE] = _POSIX_VDISABLE;
new_termios.c_cc[VWERASE] = _POSIX_VDISABLE;
new_termios.c_cc[VKILL] = _POSIX_VDISABLE;
new_termios.c_cc[VREPRINT] = _POSIX_VDISABLE;
new_termios.c_cc[VINTR] = _POSIX_VDISABLE;
new_termios.c_cc[VQUIT] = _POSIX_VDISABLE;
new_termios.c_cc[VSUSP] = _POSIX_VDISABLE;
#endif
#ifdef VDSUSP
new_termios.c_cc[VDSUSP] = _POSIX_VDISABLE;
#endif
#if 0
new_termios.c_cc[VSTART] = _POSIX_VDISABLE;
new_termios.c_cc[VSTOP] = _POSIX_VDISABLE;
#endif
#ifdef VLNEXT
new_termios.c_cc[VLNEXT] = _POSIX_VDISABLE;
#endif
#if 0
new_termios.c_cc[VDISCARD] = _POSIX_VDISABLE;
new_termios.c_cc[VMIN] = _POSIX_VDISABLE;
new_termios.c_cc[VTIME] = _POSIX_VDISABLE;
new_termios.c_cc[VSTATUS] = _POSIX_VDISABLE;
#endif
new_termios.c_cc[VMIN] = 1;
new_termios.c_cc[VTIME] = 0;
}
else
{
new_termios.c_iflag |= (ICRNL);
new_termios.c_lflag |= (ICANON | ECHO | ECHOCTL);
}
if (!d->input_echo)
new_termios.c_lflag &= ~(ECHO | ECHOCTL);
if (d->output_raw)
new_termios.c_oflag &= ~(OPOST | ONLCR);
else
new_termios.c_oflag |= (OPOST);
new_termios.c_iflag |= (IGNBRK | IGNPAR);
new_termios.c_cflag &= ~(CSIZE | PARENB);
new_termios.c_cflag |= (CS8 | CLOCAL | CREAD);
if (d->speed != 0)
{
int speed_code = -1;
switch (d->speed)
{
#ifdef B50
case 50: speed_code = B50; break;
#endif
#ifdef B75
case 75: speed_code = B75; break;
#endif
#ifdef B110
case 110: speed_code = B110; break;
#endif
#ifdef B134
case 134: speed_code = B134; break;
#endif
#ifdef B150
case 150: speed_code = B150; break;
#endif
#ifdef B200
case 200: speed_code = B200; break;
#endif
#ifdef B300
case 300: speed_code = B300; break;
#endif
#ifdef B600
case 600: speed_code = B600; break;
#endif
#ifdef B1200
case 1200: speed_code = B1200; break;
#endif
#ifdef B1800
case 1800: speed_code = B1800; break;
#endif
#ifdef B2400
case 2400: speed_code = B2400; break;
#endif
#ifdef B4800
case 4800: speed_code = B4800; break;
#endif
#ifdef B9600
case 9600: speed_code = B9600; break;
#endif
#ifdef B19200
case 19200: speed_code = B19200; break;
#endif
#ifdef B38400
case 38400: speed_code = B38400; break;
#endif
#ifdef B57600
case 57600: speed_code = B57600; break;
#endif
#ifdef B115200
case 115200: speed_code = B115200; break;
#endif
#ifdef B230400
case 230400: speed_code = B230400; break;
#endif
#ifdef B460800
case 460800: speed_code = B460800; break;
#endif
#ifdef B500000
case 500000: speed_code = B500000; break;
#endif
#ifdef B576000
case 576000: speed_code = B576000; break;
#endif
#ifdef B921600
case 921600: speed_code = B921600; break;
#endif
#ifdef B1000000
case 1000000: speed_code = B1000000; break;
#endif
#ifdef B1152000
case 1152000: speed_code = B1152000; break;
#endif
#ifdef B1500000
case 1500000: speed_code = B1500000; break;
#endif
#ifdef B2000000
case 2000000: speed_code = B2000000; break;
#endif
#ifdef B2500000
case 2500000: speed_code = B2500000; break;
#endif
#ifdef B3000000
case 3000000: speed_code = B3000000; break;
#endif
#ifdef B3500000
case 3500000: speed_code = B3500000; break;
#endif
#ifdef B4000000
case 4000000: speed_code = B4000000; break;
#endif
}
if (speed_code != -1)
{
cfsetispeed (&new_termios, speed_code);
cfsetospeed (&new_termios, speed_code);
}
}
new_flags = new_flags | O_NONBLOCK;
}
#ifdef ___DEBUG_TTY
___printf (" new_termios.c_iflag = 0x%08x\n", new_termios.c_iflag);
___printf (" new_termios.c_oflag = 0x%08x\n", new_termios.c_oflag);
___printf (" new_termios.c_lflag = 0x%08x\n", new_termios.c_lflag);
#endif
if (tcsetattr (d->fd, TCSANOW, &new_termios) < 0 ||
fcntl (d->fd, F_SETFL, new_flags) < 0)
e = err_code_from_errno ();
}
#endif
#ifdef USE_WIN32
{
DWORD hin_mode = d->hin_initial_mode;
DWORD hout_mode = d->hout_initial_mode;
if (current)
{
if (d->input_allow_special)
hin_mode |= (ENABLE_PROCESSED_INPUT);
else
hin_mode &= ~(ENABLE_PROCESSED_INPUT);
#ifndef USE_LINEEDITOR
if (!d->input_raw)
hin_mode |= (ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
else
#endif
hin_mode &= ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT);
#if 0
#ifndef USE_LINEEDITOR
if (!d->output_raw)
hout_mode |= (ENABLE_PROCESSED_OUTPUT);
else
#endif
hout_mode &= ~(ENABLE_PROCESSED_OUTPUT);
#endif
hout_mode |= (ENABLE_WRAP_AT_EOL_OUTPUT | ENABLE_PROCESSED_OUTPUT);
hin_mode |= (ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT);
}
if (!SetConsoleMode (d->hin, hin_mode) ||
!SetConsoleMode (d->hout, hout_mode))
e = err_code_from_GetLastError ();
}
#endif
return e;
}
___HIDDEN ___SCMOBJ ___device_tty_mode_restore
___P((___device_tty *self,
___BOOL remove),
(self,
remove)
___device_tty *self;
___BOOL remove;)
{
___device_tty *d = self;
___SCMOBJ e = ___FIX(___NO_ERR);
___device_tty *curr = ___tty_mod.mode_save_stack;
___device_tty *prev = NULL;
___device_tty *next;
while (curr != d)
{
if ((e = ___device_tty_mode_update (curr, 0)) != ___FIX(___NO_ERR))
break;
next = curr->mode_save_stack_next;
curr->mode_save_stack_next = prev;
prev = curr;
curr = next;
}
if (e == ___FIX(___NO_ERR) &&
curr != NULL &&
(e = ___device_tty_mode_update (curr, !remove)) == ___FIX(___NO_ERR) &&
remove)
{
d->stage = TTY_STAGE_MODE_NOT_SAVED;
curr = curr->mode_save_stack_next; /* remove d from mode save stack */
}
while (prev != NULL)
{
___SCMOBJ e2;
next = curr;
curr = prev;
prev = prev->mode_save_stack_next;
curr->mode_save_stack_next = next;
if ((e2 = ___device_tty_mode_get (curr)) != ___FIX(___NO_ERR) ||
(e2 = ___device_tty_mode_update (curr, 1)) != ___FIX(___NO_ERR))
{
if (e == ___FIX(___NO_ERR))
e = e2;
}
}
___tty_mod.mode_save_stack = curr;
return e;
}
___HIDDEN ___SCMOBJ ___device_tty_mode_set
___P((___device_tty *self,
___BOOL input_allow_special,
___BOOL input_echo,
___BOOL input_raw,
___BOOL output_raw,
int speed),
(self,
input_allow_special,
input_echo,
input_raw,
output_raw,
speed)
___device_tty *self;
___BOOL input_allow_special;
___BOOL input_echo;
___BOOL input_raw;
___BOOL output_raw;
int speed;)
{
___device_tty *d = self;
/**************** TODO: begin critical section (must block SIGCONT) */
d->input_allow_special = input_allow_special;
d->input_echo = input_echo;
d->input_raw = input_raw;
d->output_raw = output_raw;
d->speed = speed;
/**************** TODO: end critical section (must block SIGCONT) */
return ___device_tty_mode_restore (d, 0);
}
___HIDDEN ___SCMOBJ ___device_tty_update_size
___P((___device_tty *self),
(self)
___device_tty *self;)
{
___device_tty *d = self;
if (d->size_needs_update)
{
int prev_line_start_col = d->current.line_start % d->terminal_nb_cols;
int prev_line_start_row = d->current.line_start / d->terminal_nb_cols;
#ifdef USE_POSIX
#ifdef USE_ioctl
#ifdef TIOCGWINSZ
struct winsize size;
if (ioctl (d->fd, TIOCGWINSZ, &size) < 0)
return err_code_from_errno ();
if (size.ws_col > 0)
d->terminal_nb_cols = size.ws_col;
if (size.ws_row > 0)
d->terminal_nb_rows = size.ws_row;
#endif
#endif
#endif
#ifdef USE_WIN32
CONSOLE_SCREEN_BUFFER_INFO info;
if (!GetConsoleScreenBufferInfo (self->hout, &info))
return err_code_from_GetLastError ();
d->terminal_nb_cols = info.dwSize.X;
d->terminal_nb_rows = info.dwSize.Y;
#endif
d->terminal_size =
d->terminal_nb_rows * d->terminal_nb_cols;
d->terminal_cursor =
d->terminal_row * d->terminal_nb_cols + d->terminal_col;
d->current.line_start =
prev_line_start_row * d->terminal_nb_cols + prev_line_start_col;
d->terminal_delayed_wrap = 0;
d->size_needs_update = 0;
}
return ___FIX(___NO_ERR);
}
___HIDDEN ___BOOL lineeditor_under_emacs ___PVOID
{
static ___UCS_2 emacs_env_name[] = { 'E', 'M', 'A', 'C', 'S', '\0' };
___UCS_2STRING cvalue;
if (___getenv_UCS_2 (emacs_env_name, &cvalue) == ___FIX(___NO_ERR))
{
if (cvalue != 0)
{
___free_mem (cvalue);
return 1;
}
}
return 0;
}
#ifdef USE_WIN32
#ifdef USE_GetConsoleWindow
___BEGIN_C_LINKAGE
HWND WINAPI GetConsoleWindow (void);
___END_C_LINKAGE
#endif
___HIDDEN BOOL WINAPI console_event_handler
___P((DWORD dwCtrlType),
());
#endif
/* forward declaration */
___HIDDEN ___SCMOBJ lineeditor_redraw
___P((___device_tty *self),
());
___HIDDEN ___SCMOBJ ___device_tty_force_open
___P((___device_tty *self),
(self)
___device_tty *self;)
{
___device_tty *d = self;
switch (d->stage)
{
case TTY_STAGE_NOT_OPENED:
{
#ifdef USE_POSIX
int fd;
#ifndef HAVE_ctermid
char* term_name = "/dev/tty";
#else
char term_name[L_ctermid];
ctermid (term_name); /* get controlling terminal's name */
#endif
if ((fd = open (term_name,
#ifdef LINEEDITOR_WITH_NONBLOCKING_IO
O_NONBLOCK |
#endif
#ifdef O_BINARY
O_BINARY |
#endif
O_RDWR,
0))
< 0)
{
#ifdef ENXIO
if (errno == ENXIO)
{
/*
* There is no controlling terminal! This is a fatal
* error, because trying to display an error message
* will just cause the open to be tried again to
* report the problem, and this will lead to an
* infinite loop.
*/
static char *msgs[] =
{ "No controlling terminal (try using the -:d- runtime option)",
NULL
};
___fatal_error (msgs);
}
#endif
return fnf_or_err_code_from_errno ();
}
d->fd = fd;
#endif
#ifdef USE_WIN32
DWORD m;
HANDLE in;
HANDLE out;
HANDLE sin = GetStdHandle (STD_INPUT_HANDLE); /* ignore error */
HANDLE sout = GetStdHandle (STD_OUTPUT_HANDLE); /* ignore error */
HANDLE serr = GetStdHandle (STD_ERROR_HANDLE); /* ignore error */
#ifdef USE_GetConsoleWindow
HWND cons_wind = GetConsoleWindow ();
if (cons_wind == NULL || !IsWindowVisible (cons_wind))
FreeConsole ();
#endif
if (AllocConsole ())
{
/* restore initial standard handles */
SetConsoleCtrlHandler (console_event_handler, TRUE); /* ignore error */
SetStdHandle (STD_INPUT_HANDLE, sin); /* ignore error */
SetStdHandle (STD_OUTPUT_HANDLE, sout); /* ignore error */
SetStdHandle (STD_ERROR_HANDLE, serr); /* ignore error */
}
in = CreateFile
(_T("CONIN$"),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL);
if (in == INVALID_HANDLE_VALUE)
return fnf_or_err_code_from_GetLastError ();
out = CreateFile
(_T("CONOUT$"),
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL);
if (out == INVALID_HANDLE_VALUE)
{
___SCMOBJ e = fnf_or_err_code_from_GetLastError ();
CloseHandle (in); /* ignore error */
return e;
}
d->hin = in;
d->hout = out;
#endif
d->stage = TTY_STAGE_MODE_NOT_SAVED;
/* fall through */
}
case TTY_STAGE_MODE_NOT_SAVED:
{
___SCMOBJ e;
if ((e = ___device_tty_mode_save (d)) != ___FIX(___NO_ERR))
return e;
/* fall through */
}
case TTY_STAGE_MODE_NOT_SET:
{
___SCMOBJ e;
if ((e = ___device_tty_mode_restore (d, 0))
!= ___FIX(___NO_ERR))
return e;
d->stage = TTY_STAGE_INIT_DONE;
}
}
if (d->size_needs_update)
{
___SCMOBJ e;
int prev_nb_cols = d->terminal_nb_cols;
if ((e = ___device_tty_update_size (d)) != ___FIX(___NO_ERR))
return e;
if (d->editing_line && prev_nb_cols != d->terminal_nb_cols)
if ((e = lineeditor_redraw (d)) != ___FIX(___NO_ERR))
return e;
}
return ___FIX(___NO_ERR);
}
#ifdef USE_WIN32
___HIDDEN void show_cursor
___P((___device_tty *self),
(self)
___device_tty *self;)
{
___device_tty *d = self;
CONSOLE_SCREEN_BUFFER_INFO info;
if (GetConsoleScreenBufferInfo (d->hout, &info))
SetConsoleCursorPosition
(d->hout,
info.dwCursorPosition); /* ignore error */
}
#endif
___HIDDEN ___SCMOBJ ___device_tty_write
___P((___device_tty *self,
___U8 *buf,
___stream_index len,
___stream_index *len_done),
(self,
buf,
len,
len_done)
___device_tty *self;
___U8 *buf;
___stream_index len;
___stream_index *len_done;)
{
___device_tty *d = self;
#ifdef USE_POSIX
{
int n;
if ((n = write (d->fd, buf, len)) < 0)
return err_code_from_errno ();
*len_done = n;
}
#endif
#ifdef USE_WIN32
{
DWORD n;
int len_in_chars = TTY_CHAR_SELECT(len,len>>1); /* convert bytes to chars */
if (len_in_chars < 1)
return ___FIX(___UNKNOWN_ERR);
if (!TTY_CHAR_SELECT(WriteConsoleA (d->hout, buf, len_in_chars, &n, NULL),
WriteConsoleW (d->hout, buf, len_in_chars, &n, NULL)))
return err_code_from_GetLastError ();
show_cursor (d);
*len_done = TTY_CHAR_SELECT(n,n<<1); /* convert chars to bytes */
}
#endif
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ ___device_tty_write_raw_no_lineeditor
___P((___device_tty *self,
___U8 *buf,
___stream_index len,
___stream_index *len_done),
(self,
buf,
len,
len_done)
___device_tty *self;
___U8 *buf;
___stream_index len;
___stream_index *len_done;)
{
___device_tty *d = self;
if (d->base.base.write_stage != ___STAGE_OPEN)
return ___FIX(___CLOSED_DEVICE_ERR);
return ___device_tty_write (d, buf, len, len_done);
}
___HIDDEN ___SCMOBJ ___device_tty_read_raw_no_lineeditor
___P((___device_tty *self,
___U8 *buf,
___stream_index len,
___stream_index *len_done),
(self,
buf,
len,
len_done)
___device_tty *self;
___U8 *buf;
___stream_index len;
___stream_index *len_done;)
{
___device_tty *d = self;
if (d->base.base.read_stage != ___STAGE_OPEN)
return ___FIX(___CLOSED_DEVICE_ERR);
#ifdef USE_POSIX
{
int n;
#ifdef ___DEBUG
___printf ("read len=%d\n", len);
#endif
if ((n = read (d->fd, buf, len)) < 0)
return err_code_from_errno ();
#ifdef ___DEBUG
___printf ("read n=%d\n", n);
#endif
*len_done = n;
}
#endif
#ifdef USE_WIN32
{
char *seq;
int len_in_chars = TTY_CHAR_SELECT(len,len>>1); /* convert bytes to chars */
if (len_in_chars < 1)
return ___FIX(___UNKNOWN_ERR);
next_chars:
seq = d->key_seq;
if (seq != NULL)
{
TTY_CHAR *tty_char_buf = ___CAST(TTY_CHAR*,buf);
int i = 0;
while (i < len_in_chars && *seq != '\0')
{
*tty_char_buf++ = *seq++;
i++;
}
if (*seq == '\0')
d->key_seq = NULL;
else
d->key_seq = seq;
if (i > 0)
{
*len_done = TTY_CHAR_SELECT(i,i<<1); /* convert chars to bytes */
return ___FIX(___NO_ERR);
}
}
next_event:
if (d->ir.EventType != KEY_EVENT ||
d->ir.Event.KeyEvent.wRepeatCount <= 0)
{
DWORD avail;
if (!GetNumberOfConsoleInputEvents (d->hin, &avail))
return err_code_from_GetLastError ();
if (avail <= 0)
return ___ERR_CODE_EAGAIN;
if (!TTY_CHAR_SELECT(ReadConsoleInputA (d->hin, &d->ir, 1, &avail),
ReadConsoleInputW (d->hin, &d->ir, 1, &avail)))
return err_code_from_GetLastError ();
if (avail <= 0)
return ___ERR_CODE_EAGAIN;
}
switch (d->ir.EventType)
{
case KEY_EVENT:
{
DWORD ctrl_keys;
___BOOL ctrl;
___BOOL alt;
___BOOL shift;
char *seq;
TTY_CHAR c;
if (!(d->ir.Event.KeyEvent.bKeyDown &&
d->ir.Event.KeyEvent.wRepeatCount > 0))
{
d->ir.EventType = MOUSE_EVENT; /* anything but KEY_EVENT */
goto next_event;
}
ctrl_keys = d->ir.Event.KeyEvent.dwControlKeyState;
ctrl = ctrl_keys & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED);
alt = ctrl_keys & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED);
shift = ctrl_keys & SHIFT_PRESSED;
c = TTY_CHAR_SELECT(d->ir.Event.KeyEvent.uChar.AsciiChar,
d->ir.Event.KeyEvent.uChar.UnicodeChar);
seq = NULL;
if (!ctrl && alt && !shift && c>='a' && c<='z')
{
static char alt_seq[3];
alt_seq[0] = ___UNICODE_ESCAPE;
alt_seq[1] = c;
alt_seq[2] = '\0';
seq = alt_seq;
}
else if (!ctrl && !alt && !shift && c == 0)
switch (d->ir.Event.KeyEvent.wVirtualKeyCode)
{
case VK_BACK: seq = "\010"; break;
case VK_UP: seq = "\033[A"; break;
case VK_DOWN: seq = "\033[B"; break;
case VK_RIGHT: seq = "\033[C"; break;
case VK_LEFT: seq = "\033[D"; break;
case VK_HOME: seq = "\033[H"; break;
case VK_INSERT: seq = "\033[2~"; break;
case VK_DELETE: seq = "\177"; break;
case VK_END: seq = "\033[F"; break;
case VK_PRIOR: seq = "\033[5~"; break;
case VK_NEXT: seq = "\033[6~"; break;
case VK_F1: seq = "\033OP"; break;
case VK_F2: seq = "\033OQ"; break;
case VK_F3: seq = "\033OR"; break;
case VK_F4: seq = "\033OS"; break;
case VK_F5: seq = "\033[15~"; break;
case VK_F6: seq = "\033[17~"; break;
case VK_F7: seq = "\033[18~"; break;
case VK_F8: seq = "\033[19~"; break;
case VK_F9: seq = "\033[20~"; break;
case VK_F10: seq = "\033[21~"; break;
case VK_F11: seq = "\033[23~"; break;
case VK_F12: seq = "\033[24~"; break;
}
if (seq == NULL)
{
if (c == 0)
{
d->ir.EventType = MOUSE_EVENT; /* anything but KEY_EVENT */
goto next_event;
}
if (ctrl && c == ' ') /* ctrl-space => NUL character */
c = '\0';
TTY_CHAR_SELECT(
buf[0] = c & 0xff;
*len_done = 1;
,
buf[0] = c & 0xff;
buf[1] = c >> 8;
*len_done = 2;
)
d->ir.Event.KeyEvent.wRepeatCount--;
return ___FIX(___NO_ERR);
}
else
{
d->key_seq = seq;
d->ir.Event.KeyEvent.wRepeatCount--;
goto next_chars;
}
break;
}
case MOUSE_EVENT:
{
#if 0
/*************************/
COORD c = d->ir.Event.MouseEvent.dwMousePosition;
CONSOLE_SCREEN_BUFFER_INFO info;
COORD pos;
printf ("m.X=%d m.Y=%d\n",c.X,c.Y);
if (GetConsoleScreenBufferInfo (d->hout, &info))
{
printf (" dwSize.X=%d dwSize.Y=%d\n",info.dwSize.X,info.dwSize.Y);
printf (" dwCursorPosition.X=%d dwCursorPosition.Y=%d\n",info.dwCursorPosition.X,info.dwCursorPosition.Y);
printf (" dwMaximumWindowSize.X=%d dwMaximumWindowSize.Y=%d\n",info.dwMaximumWindowSize.X,info.dwMaximumWindowSize.Y);
printf (" srWindow.Left=%d srWindow.Top=%d\n",info.srWindow.Left,info.srWindow.Top);
printf (" srWindow.Right=%d srWindow.Bottom=%d\n",info.srWindow.Right,info.srWindow.Bottom);
fflush(stdout);
}
___WORD e;
if (i > 0)
goto done; /* in case paste returns an error */
state->input_rlo++;
if (d->ir.Event.MouseEvent.dwEventFlags == 0 &&
(d->ir.Event.MouseEvent.dwButtonState ==
(FROM_LEFT_1ST_BUTTON_PRESSED | RIGHTMOST_BUTTON_PRESSED) ||
d->ir.Event.MouseEvent.dwButtonState ==
FROM_LEFT_2ND_BUTTON_PRESSED))
if ((e = lineeditor_paste_from_clipboard (state->les))
!= ___FIX(___NO_ERR))
return e;
goto done; /* pasted text is available to be read */
#endif
goto next_event;
}
case WINDOW_BUFFER_SIZE_EVENT:
{
d->size_needs_update = 1;
#if 0
COORD c = d->ir.Event.WindowBufferSizeEvent.dwSize;
printf ("c.X=%d c.Y=%d\n", c.X, c.Y);fflush(stdout);/********************/
#endif
goto next_event;
}
default:
goto next_event;
}
}
#endif
return ___FIX(___NO_ERR);
}
/*****************************************************************************/
/* Line editing subsystem */
#ifdef USE_LINEEDITOR
/*---------------------------------------------------------------------------*/
/* Line editing input decoder routines */
___HIDDEN ___SCMOBJ lineeditor_input_decoder_setup
___P((lineeditor_input_decoder *decoder),
(decoder)
lineeditor_input_decoder *decoder;)
{
#define LINEEDITOR_INPUT_DECODER_INITIAL_BUFFER_SIZE 150
int n = LINEEDITOR_INPUT_DECODER_INITIAL_BUFFER_SIZE;
decoder->buffer = ___CAST(lineeditor_input_test*,
___alloc_mem (n * sizeof (lineeditor_input_test)));
if (decoder->buffer == NULL)
return ___FIX(___HEAP_OVERFLOW_ERR);
decoder->length = 0;
decoder->max_length = n;
return ___FIX(___NO_ERR);
}
___HIDDEN void lineeditor_input_decoder_cleanup
___P((lineeditor_input_decoder *decoder),
(decoder)
lineeditor_input_decoder *decoder;)
{
___free_mem (decoder->buffer);
}
___HIDDEN ___SCMOBJ lineeditor_input_decoder_set_length
___P((lineeditor_input_decoder *decoder,
int len,
int fudge),
(decoder,
len,
fudge)
lineeditor_input_decoder *decoder;
int len;
int fudge;)
{
if (len > LINEEDITOR_INPUT_DECODER_MAX_LENGTH)
return ___FIX(___UNKNOWN_ERR);
if (len > decoder->max_length)
{
int i;
int new_max_length = (fudge<0) ? 3*len/2+1 : len+fudge;
lineeditor_input_test *old_buffer = decoder->buffer;
lineeditor_input_test *new_buffer =
___CAST(lineeditor_input_test*,
___alloc_mem (new_max_length *
sizeof (lineeditor_input_test)));
if (new_buffer == NULL)
return ___FIX(___HEAP_OVERFLOW_ERR);
i = decoder->length;
if (i > len)
i = len;
while (i-- > 0)
new_buffer[i] = old_buffer[i];
___free_mem (old_buffer);
decoder->buffer = new_buffer;
decoder->max_length = new_max_length;
}
decoder->length = len;
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ lineeditor_input_decoder_add
___P((lineeditor_input_decoder *decoder,
char *seq,
___U8 event),
(decoder,
seq,
event)
lineeditor_input_decoder *decoder;
char *seq;
___U8 event;)
{
___SCMOBJ e;
___U8 b;
int i = 0;
char *p = seq;
if (event & WITH_ESC_PREFIX)
b = ___UNICODE_ESCAPE;
else
b = ___CAST_U8(*p++);
if (decoder->length > 0)
{
while (b != ___CAST_U8('\0') || p == seq+1)
{
if (decoder->buffer[i].trigger == b)
{
int a = decoder->buffer[i].action;
if (a >= decoder->length)
return ___FIX(___NO_ERR); /* prefix of sequence exists */
i = a;
b = ___CAST_U8(*p++);
}
else
{
int n = decoder->buffer[i].next;
if (n >= decoder->length)
{
decoder->buffer[i].next = decoder->length;
break;
}
i = n;
}
}
}
if (b != ___CAST_U8('\0') || p == seq+1)
{
/* sequence is not a prefix of a preexisting sequence */
while (b != ___CAST_U8('\0') || p == seq+1)
{
i = decoder->length;
if ((e = lineeditor_input_decoder_set_length (decoder, i+1, -1))
!= ___FIX(___NO_ERR))
return e;
decoder->buffer[i].trigger = b;
decoder->buffer[i].action = i+1;
decoder->buffer[i].next = LINEEDITOR_INPUT_DECODER_STATE_MAX;
b = ___CAST_U8(*p++);
}
decoder->buffer[i].action =
LINEEDITOR_INPUT_DECODER_STATE_MAX-(event&~WITH_ESC_PREFIX);
}
return ___FIX(___NO_ERR);
}
typedef struct lineeditor_defseq_struct
{
char *seq;
___U8 event;
} lineeditor_defseq;
___HIDDEN lineeditor_defseq lineeditor_defseq_common[] =
{
/* sequences that all terminals support */
{ "\012", LINEEDITOR_EV_RETURN }
,{ "\015", LINEEDITOR_EV_RETURN }
,{ "\010", LINEEDITOR_EV_BACK }
,{ "\010", LINEEDITOR_EV_BACK_SEXPR | WITH_ESC_PREFIX }
,{ "\011", LINEEDITOR_EV_TAB }
};
___HIDDEN lineeditor_defseq lineeditor_defseq_map_rubout_to_backspace[] =
{
/* sequences that map the rubout key to backspace */
{ "\177", LINEEDITOR_EV_BACK }
,{ "\177", LINEEDITOR_EV_BACK_WORD | WITH_ESC_PREFIX }
};
___HIDDEN lineeditor_defseq lineeditor_defseq_emacs[] =
{
/* sequences specific to emacs mode */
{ "\0", LINEEDITOR_EV_MARK }
,{ "\031", LINEEDITOR_EV_PASTE }
,{ "\027", LINEEDITOR_EV_CUT }
,{ "\013", LINEEDITOR_EV_CUT_RIGHT }
,{ "\025", LINEEDITOR_EV_CUT_LEFT }
,{ "\014", LINEEDITOR_EV_REFRESH }
,{ "\024", LINEEDITOR_EV_TRANSPOSE }
,{ "\024", LINEEDITOR_EV_TRANSPOSE_SEXPR | WITH_ESC_PREFIX }
,{ "t", LINEEDITOR_EV_TRANSPOSE_WORD | WITH_ESC_PREFIX }
,{ "T", LINEEDITOR_EV_TRANSPOSE_WORD | WITH_ESC_PREFIX }
,{ "p", LINEEDITOR_EV_UP | WITH_ESC_PREFIX }
,{ "P", LINEEDITOR_EV_UP | WITH_ESC_PREFIX }
,{ "n", LINEEDITOR_EV_DOWN | WITH_ESC_PREFIX }
,{ "N", LINEEDITOR_EV_DOWN | WITH_ESC_PREFIX }
,{ "\020", LINEEDITOR_EV_UP }
,{ "\016", LINEEDITOR_EV_DOWN }
,{ "\006", LINEEDITOR_EV_RIGHT }
,{ "\002", LINEEDITOR_EV_LEFT }
,{ "\006", LINEEDITOR_EV_RIGHT_SEXPR | WITH_ESC_PREFIX }
,{ "f", LINEEDITOR_EV_RIGHT_WORD | WITH_ESC_PREFIX }
,{ "F", LINEEDITOR_EV_RIGHT_WORD | WITH_ESC_PREFIX }
,{ "\002", LINEEDITOR_EV_LEFT_SEXPR | WITH_ESC_PREFIX }
,{ "b", LINEEDITOR_EV_LEFT_WORD | WITH_ESC_PREFIX }
,{ "B", LINEEDITOR_EV_LEFT_WORD | WITH_ESC_PREFIX }
,{ "\001", LINEEDITOR_EV_HOME }
,{ "\004", LINEEDITOR_EV_DELETE }
,{ "\005", LINEEDITOR_EV_END }
,{ "\004", LINEEDITOR_EV_DELETE_SEXPR | WITH_ESC_PREFIX }
,{ "d", LINEEDITOR_EV_DELETE_WORD | WITH_ESC_PREFIX }
,{ "D", LINEEDITOR_EV_DELETE_WORD | WITH_ESC_PREFIX }
};
___HIDDEN lineeditor_defseq lineeditor_defseq_widespread[] =
{
/* sequences that many types of terminals support */
{ "[A", LINEEDITOR_EV_UP | WITH_ESC_PREFIX }
,{ "[B", LINEEDITOR_EV_DOWN | WITH_ESC_PREFIX }
,{ "[C", LINEEDITOR_EV_RIGHT | WITH_ESC_PREFIX }
,{ "[D", LINEEDITOR_EV_LEFT | WITH_ESC_PREFIX }
,{ "\033[A", LINEEDITOR_EV_NONE | WITH_ESC_PREFIX }
,{ "\033[B", LINEEDITOR_EV_NONE | WITH_ESC_PREFIX }
,{ "\033[C", LINEEDITOR_EV_RIGHT_SEXPR | WITH_ESC_PREFIX }
,{ "\033[D", LINEEDITOR_EV_LEFT_SEXPR | WITH_ESC_PREFIX }
,{ "OA", LINEEDITOR_EV_UP | WITH_ESC_PREFIX }
,{ "OB", LINEEDITOR_EV_DOWN | WITH_ESC_PREFIX }
,{ "OC", LINEEDITOR_EV_RIGHT | WITH_ESC_PREFIX }
,{ "OD", LINEEDITOR_EV_LEFT | WITH_ESC_PREFIX }
,{ "\033OA", LINEEDITOR_EV_NONE | WITH_ESC_PREFIX }
,{ "\033OB", LINEEDITOR_EV_NONE | WITH_ESC_PREFIX }
,{ "\033OC", LINEEDITOR_EV_RIGHT_SEXPR | WITH_ESC_PREFIX }
,{ "\033OD", LINEEDITOR_EV_LEFT_SEXPR | WITH_ESC_PREFIX }
,{ "A", LINEEDITOR_EV_UP | WITH_ESC_PREFIX }
,{ "B", LINEEDITOR_EV_DOWN | WITH_ESC_PREFIX }
,{ "C", LINEEDITOR_EV_RIGHT | WITH_ESC_PREFIX }
,{ "D", LINEEDITOR_EV_LEFT | WITH_ESC_PREFIX }
,{ "\033A", LINEEDITOR_EV_NONE | WITH_ESC_PREFIX }
,{ "\033B", LINEEDITOR_EV_NONE | WITH_ESC_PREFIX }
,{ "\033C", LINEEDITOR_EV_RIGHT_SEXPR | WITH_ESC_PREFIX }
,{ "\033D", LINEEDITOR_EV_LEFT_SEXPR | WITH_ESC_PREFIX }
#ifdef USE_XTERM_CTRL_COMBINATIONS
,{ "[5A", LINEEDITOR_EV_NONE | WITH_ESC_PREFIX }
,{ "[5B", LINEEDITOR_EV_NONE | WITH_ESC_PREFIX }
,{ "[5C", LINEEDITOR_EV_RIGHT_SEXPR | WITH_ESC_PREFIX }
,{ "[5D", LINEEDITOR_EV_LEFT_SEXPR | WITH_ESC_PREFIX }
#endif
,{ "[1~", LINEEDITOR_EV_HOME | WITH_ESC_PREFIX }
,{ "[2~", LINEEDITOR_EV_INSERT | WITH_ESC_PREFIX }
,{ "[3~", LINEEDITOR_EV_DELETE | WITH_ESC_PREFIX }
,{ "[4~", LINEEDITOR_EV_END | WITH_ESC_PREFIX }
,{ "[5~", LINEEDITOR_EV_HOME_DOC | WITH_ESC_PREFIX }
,{ "[6~", LINEEDITOR_EV_END_DOC | WITH_ESC_PREFIX }
,{ "\033[1~", LINEEDITOR_EV_HOME_DOC | WITH_ESC_PREFIX }
,{ "\033[2~", LINEEDITOR_EV_NONE | WITH_ESC_PREFIX }
,{ "\033[3~", LINEEDITOR_EV_DELETE_SEXPR | WITH_ESC_PREFIX }
,{ "\033[4~", LINEEDITOR_EV_END_DOC | WITH_ESC_PREFIX }
,{ "\033[5~", LINEEDITOR_EV_NONE | WITH_ESC_PREFIX }
,{ "\033[6~", LINEEDITOR_EV_NONE | WITH_ESC_PREFIX }
,{ "[H", LINEEDITOR_EV_HOME | WITH_ESC_PREFIX }
,{ "[L", LINEEDITOR_EV_INSERT | WITH_ESC_PREFIX }
,{ "[F", LINEEDITOR_EV_END | WITH_ESC_PREFIX }
,{ "\177", LINEEDITOR_EV_DELETE }
,{ "\033[H", LINEEDITOR_EV_HOME_DOC | WITH_ESC_PREFIX }
,{ "\177", LINEEDITOR_EV_DELETE_SEXPR | WITH_ESC_PREFIX }
,{ "\033[F", LINEEDITOR_EV_END_DOC | WITH_ESC_PREFIX }
#ifdef USE_XTERM_CTRL_COMBINATIONS
,{ "[1;5~", LINEEDITOR_EV_HOME_DOC | WITH_ESC_PREFIX }
,{ "[2;5~", LINEEDITOR_EV_NONE | WITH_ESC_PREFIX }
,{ "[3;5~", LINEEDITOR_EV_DELETE_SEXPR | WITH_ESC_PREFIX }
,{ "[4;5~", LINEEDITOR_EV_END_DOC | WITH_ESC_PREFIX }
,{ "[5;5~", LINEEDITOR_EV_NONE | WITH_ESC_PREFIX }
,{ "[6;5~", LINEEDITOR_EV_NONE | WITH_ESC_PREFIX }
,{ "[5H", LINEEDITOR_EV_HOME_DOC | WITH_ESC_PREFIX }
,{ "[5L", LINEEDITOR_EV_NONE | WITH_ESC_PREFIX }
,{ "[5F", LINEEDITOR_EV_END_DOC | WITH_ESC_PREFIX }
#endif
,{ "OP", LINEEDITOR_EV_F1 | WITH_ESC_PREFIX }
,{ "OQ", LINEEDITOR_EV_F2 | WITH_ESC_PREFIX }
,{ "OR", LINEEDITOR_EV_F3 | WITH_ESC_PREFIX }
,{ "OS", LINEEDITOR_EV_F4 | WITH_ESC_PREFIX }
,{ "\033OP", LINEEDITOR_EV_META_F1 | WITH_ESC_PREFIX }
,{ "\033OQ", LINEEDITOR_EV_META_F2 | WITH_ESC_PREFIX }
,{ "\033OR", LINEEDITOR_EV_META_F3 | WITH_ESC_PREFIX }
,{ "\033OS", LINEEDITOR_EV_META_F4 | WITH_ESC_PREFIX }
#ifdef USE_XTERM_CTRL_COMBINATIONS
,{ "O5P", LINEEDITOR_EV_META_F1 | WITH_ESC_PREFIX }
,{ "O5Q", LINEEDITOR_EV_META_F2 | WITH_ESC_PREFIX }
,{ "O5R", LINEEDITOR_EV_META_F3 | WITH_ESC_PREFIX }
,{ "O5S", LINEEDITOR_EV_META_F4 | WITH_ESC_PREFIX }
#endif
,{ "[11~", LINEEDITOR_EV_F1 | WITH_ESC_PREFIX }
,{ "[12~", LINEEDITOR_EV_F2 | WITH_ESC_PREFIX }
,{ "[13~", LINEEDITOR_EV_F3 | WITH_ESC_PREFIX }
,{ "[14~", LINEEDITOR_EV_F4 | WITH_ESC_PREFIX }
,{ "\033[11~", LINEEDITOR_EV_META_F1 | WITH_ESC_PREFIX }
,{ "\033[12~", LINEEDITOR_EV_META_F2 | WITH_ESC_PREFIX }
,{ "\033[13~", LINEEDITOR_EV_META_F3 | WITH_ESC_PREFIX }
,{ "\033[14~", LINEEDITOR_EV_META_F4 | WITH_ESC_PREFIX }
#ifdef USE_XTERM_CTRL_COMBINATIONS
,{ "[11;5~", LINEEDITOR_EV_META_F1 | WITH_ESC_PREFIX }
,{ "[12;5~", LINEEDITOR_EV_META_F2 | WITH_ESC_PREFIX }
,{ "[13;5~", LINEEDITOR_EV_META_F3 | WITH_ESC_PREFIX }
,{ "[14;5~", LINEEDITOR_EV_META_F4 | WITH_ESC_PREFIX }
#endif
#ifdef LINEEDITOR_SUPPORT_F5_TO_F12
,{ "[15~", LINEEDITOR_EV_F5 | WITH_ESC_PREFIX }
,{ "[17~", LINEEDITOR_EV_F6 | WITH_ESC_PREFIX }
,{ "[18~", LINEEDITOR_EV_F7 | WITH_ESC_PREFIX }
,{ "[19~", LINEEDITOR_EV_F8 | WITH_ESC_PREFIX }
,{ "[20~", LINEEDITOR_EV_F9 | WITH_ESC_PREFIX }
,{ "[21~", LINEEDITOR_EV_F10 | WITH_ESC_PREFIX }
,{ "[23~", LINEEDITOR_EV_F11 | WITH_ESC_PREFIX }
,{ "[24~", LINEEDITOR_EV_F12 | WITH_ESC_PREFIX }
,{ "\033[15~", LINEEDITOR_EV_META_F5 | WITH_ESC_PREFIX }
,{ "\033[17~", LINEEDITOR_EV_META_F6 | WITH_ESC_PREFIX }
,{ "\033[18~", LINEEDITOR_EV_META_F7 | WITH_ESC_PREFIX }
,{ "\033[19~", LINEEDITOR_EV_META_F8 | WITH_ESC_PREFIX }
,{ "\033[20~", LINEEDITOR_EV_META_F9 | WITH_ESC_PREFIX }
,{ "\033[21~", LINEEDITOR_EV_META_F10 | WITH_ESC_PREFIX }
,{ "\033[23~", LINEEDITOR_EV_META_F11 | WITH_ESC_PREFIX }
,{ "\033[24~", LINEEDITOR_EV_META_F12 | WITH_ESC_PREFIX }
#ifdef USE_XTERM_CTRL_COMBINATIONS
,{ "[15;5~", LINEEDITOR_EV_META_F5 | WITH_ESC_PREFIX }
,{ "[17;5~", LINEEDITOR_EV_META_F6 | WITH_ESC_PREFIX }
,{ "[18;5~", LINEEDITOR_EV_META_F7 | WITH_ESC_PREFIX }
,{ "[19;5~", LINEEDITOR_EV_META_F8 | WITH_ESC_PREFIX }
,{ "[20;5~", LINEEDITOR_EV_META_F9 | WITH_ESC_PREFIX }
,{ "[21;5~", LINEEDITOR_EV_META_F10 | WITH_ESC_PREFIX }
,{ "[23;5~", LINEEDITOR_EV_META_F11 | WITH_ESC_PREFIX }
,{ "[24;5~", LINEEDITOR_EV_META_F12 | WITH_ESC_PREFIX }
#endif
#endif
};
___HIDDEN ___SCMOBJ lineeditor_defseq_install_table
___P((lineeditor_input_decoder *decoder,
lineeditor_defseq *table,
int len),
(decoder,
table,
len)
lineeditor_input_decoder *decoder;
lineeditor_defseq *table;
int len;)
{
___SCMOBJ e;
int i;
for (i=0; i<len; i++)
if ((e = lineeditor_input_decoder_add
(decoder,
table[i].seq,
table[i].event))
!= ___FIX(___NO_ERR))
return e;
return ___FIX(___NO_ERR);
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#ifdef USE_CURSES
typedef struct lineeditor_dkey_struct
{
char *cap;
___U8 event_no_escape;
___U8 event_with_escape;
} lineeditor_dkey;
#ifdef USE_TERMCAP
#define DKEY(tcap_name,tinfo_name,xterm_def,event_no_esc,event_with_esc) \
{ tcap_name, event_no_esc, event_with_esc | WITH_ESC_PREFIX }
#else
#ifdef USE_TERMINFO
#define DKEY(tcap_name,tinfo_name,xterm_def,event_no_esc,event_with_esc) \
{ tinfo_name, event_no_esc, event_with_esc | WITH_ESC_PREFIX }
#else
#define DKEY(tcap_name,tinfo_name,xterm_def,event_no_esc,event_with_esc) \
{ xterm_def, event_no_esc, event_with_esc | WITH_ESC_PREFIX }
#endif
#endif
typedef struct lineeditor_dcap_struct
{
char *cap;
#ifdef USE_TERMCAP_OR_TERMINFO
char *xterm_cap;
#endif
} lineeditor_dcap;
#ifdef USE_TERMCAP
#define DCAP(tcap_name,tinfo_name,xterm_def) { tcap_name, xterm_def }
#else
#ifdef USE_TERMINFO
#define DCAP(tcap_name,tinfo_name,xterm_def) { tinfo_name, xterm_def }
#else
#define DCAP(tcap_name,tinfo_name,xterm_def) { xterm_def }
#endif
#endif
___HIDDEN lineeditor_dcap lineeditor_dcap_table[LINEEDITOR_CAP_LAST+1] =
{
/* order must match LINEEDITOR_CAP_XXX */
DCAP("ho","home", "\033[H" ) /* home cursor */
,DCAP("cl","clear","\033[H\033[J" ) /* home cursor and clear screen */
,DCAP("up","cuu1", "\033[A" ) /* move up one line */
,DCAP("do","cud1", "\033[B" ) /* move down one line */
,DCAP("UP","cuu", "\033[%p1%dA" ) /* move up N lines */
,DCAP("DO","cud", "\033[%p1%dB" ) /* move down N lines */
,DCAP("RI","cuf", "\033[%p1%dC" ) /* move right N columns */
,DCAP("LE","cub", "\033[%p1%dD" ) /* move left N columns */
,DCAP("cm","cup", "\033[%i%p1%d;%p2%dH") /* move cursor to (ROW,COL) */
,DCAP("me","sgr0", "\033[m" ) /* turn off all attributes */
,DCAP("md","bold", "\033[1m" ) /* turn on bold attribute */
,DCAP("us","smul", "\033[4m" ) /* turn on underline attribute */
,DCAP("mr","rev", "\033[7m" ) /* turn on reverse attribute */
,DCAP("AF","setaf","\033[3%p1%dm" ) /* ANSI set foreground color */
,DCAP("AB","setab","\033[4%p1%dm" ) /* ANSI set background color */
,DCAP("cd","ed", "\033[J" ) /* erase to end of screen */
,DCAP("ce","el", "\033[K" ) /* erase to end of line */
,DCAP("cb","el1", "\033[1K" ) /* erase to beginning of line */
,DCAP(" 0"," 0", "\033[%p1%dt" ) /* window operation with no arg */
,DCAP(" 1"," 1", "\033[%p1%d;%p2%dt" ) /* window operation with 1 arg */
,DCAP(" 2"," 2", "\033[%p1%d;%p2%d;%p3%dt") /* window operation with 2 args */
,DCAP(" 3"," 3", "\033]%p1%d;" ) /* window operation with text arg */
};
___HIDDEN lineeditor_dkey lineeditor_dkey_table[] =
{
DKEY("ku","kcuu1","\033OA", LINEEDITOR_EV_UP, LINEEDITOR_EV_NONE )
,DKEY("kd","kcud1","\033OB", LINEEDITOR_EV_DOWN, LINEEDITOR_EV_NONE )
,DKEY("kr","kcuf1","\033OC", LINEEDITOR_EV_RIGHT, LINEEDITOR_EV_RIGHT_SEXPR )
,DKEY("kl","kcub1","\033OD", LINEEDITOR_EV_LEFT, LINEEDITOR_EV_LEFT_SEXPR )
,DKEY("kh","khome","\033[1~", LINEEDITOR_EV_HOME, LINEEDITOR_EV_HOME_DOC )
,DKEY("kI","kich1","\033[2~", LINEEDITOR_EV_INSERT, LINEEDITOR_EV_NONE )
,DKEY("kD","kdch1","\033[3~", LINEEDITOR_EV_DELETE, LINEEDITOR_EV_DELETE_SEXPR)
,DKEY("@7","kend", "\033[4~", LINEEDITOR_EV_END, LINEEDITOR_EV_END_DOC )
,DKEY("kP","kpp", "\033[5~", LINEEDITOR_EV_HOME_DOC,LINEEDITOR_EV_NONE )
,DKEY("kN","knp", "\033[6~", LINEEDITOR_EV_END_DOC, LINEEDITOR_EV_NONE )
,DKEY("k1","kf1", "\033OP", LINEEDITOR_EV_F1, LINEEDITOR_EV_META_F1 )
,DKEY("k2","kf2", "\033OQ", LINEEDITOR_EV_F2, LINEEDITOR_EV_META_F2 )
,DKEY("k3","kf3", "\033OR", LINEEDITOR_EV_F3, LINEEDITOR_EV_META_F3 )
,DKEY("k4","kf4", "\033OS", LINEEDITOR_EV_F4, LINEEDITOR_EV_META_F4 )
#ifdef LINEEDITOR_SUPPORT_ALTERNATE_ESCAPES
,DKEY("kh","khome","\033OH", LINEEDITOR_EV_HOME, LINEEDITOR_EV_HOME_DOC )
,DKEY("@7","kend", "\033OF", LINEEDITOR_EV_END, LINEEDITOR_EV_END_DOC )
,DKEY("k1","kf1", "\033[11~",LINEEDITOR_EV_F1, LINEEDITOR_EV_META_F1 )
,DKEY("k2","kf2", "\033[12~",LINEEDITOR_EV_F2, LINEEDITOR_EV_META_F2 )
,DKEY("k3","kf3", "\033[13~",LINEEDITOR_EV_F3, LINEEDITOR_EV_META_F3 )
,DKEY("k4","kf4", "\033[14~",LINEEDITOR_EV_F4, LINEEDITOR_EV_META_F4 )
#endif
#ifdef LINEEDITOR_SUPPORT_F5_TO_F12
,DKEY("k5","kf5", "\033[15~",LINEEDITOR_EV_F5, LINEEDITOR_EV_META_F5 )
,DKEY("k6","kf6", "\033[17~",LINEEDITOR_EV_F6, LINEEDITOR_EV_META_F6 )
,DKEY("k7","kf7", "\033[18~",LINEEDITOR_EV_F7, LINEEDITOR_EV_META_F7 )
,DKEY("k8","kf8", "\033[19~",LINEEDITOR_EV_F8, LINEEDITOR_EV_META_F8 )
,DKEY("k9","kf9", "\033[20~",LINEEDITOR_EV_F9, LINEEDITOR_EV_META_F9 )
,DKEY("k;","kf10", "\033[21~",LINEEDITOR_EV_F10, LINEEDITOR_EV_META_F10 )
,DKEY("F1","kf11", "\033[23~",LINEEDITOR_EV_F11, LINEEDITOR_EV_META_F11 )
,DKEY("F2","kf12", "\033[24~",LINEEDITOR_EV_F12, LINEEDITOR_EV_META_F12 )
#endif
};
___HIDDEN ___SCMOBJ lineeditor_dkey_install
___P((lineeditor_input_decoder *decoder,
char *cap,
___U8 event_no_escape,
___U8 event_with_escape,
___BOOL force_xterm),
(decoder,
cap,
event_no_escape,
event_with_escape,
force_xterm)
lineeditor_input_decoder *decoder;
char *cap;
___U8 event_no_escape;
___U8 event_with_escape;
___BOOL force_xterm;)
{
___SCMOBJ e = ___FIX(___NO_ERR);
char *seq;
#ifdef USE_TERMCAP_OR_TERMINFO
if (!force_xterm)
{
#ifdef USE_TERMCAP
seq = tgetstr (cap, NULL);
#else
seq = tigetstr (cap);
#endif
}
else
#endif
seq = cap;
if (seq != ___CAST(char*,-1) && seq != NULL)
if ((e = lineeditor_input_decoder_add (decoder, seq, event_no_escape))
!= ___FIX(___NO_ERR) &&
event_with_escape != (LINEEDITOR_EV_NONE|WITH_ESC_PREFIX))
e = lineeditor_input_decoder_add (decoder, seq, event_with_escape);
return e;
}
___HIDDEN ___SCMOBJ lineeditor_dkey_install_table
___P((lineeditor_input_decoder *decoder,
lineeditor_dkey *table,
int len,
___BOOL force_xterm),
(decoder,
table,
len,
force_xterm)
lineeditor_input_decoder *decoder;
lineeditor_dkey *table;
int len;
___BOOL force_xterm;)
{
___SCMOBJ e;
int i;
for (i=0; i<len; i++)
if ((e = lineeditor_dkey_install
(decoder,
table[i].cap,
table[i].event_no_escape,
table[i].event_with_escape,
force_xterm))
!= ___FIX(___NO_ERR))
return e;
return ___FIX(___NO_ERR);
}
#endif
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
___HIDDEN ___SCMOBJ lineeditor_input_decoder_init
___P((lineeditor_input_decoder *decoder,
___BOOL map_rubout_to_backspace,
___BOOL emacs_bindings,
___BOOL force_xterm),
(decoder,
map_rubout_to_backspace,
emacs_bindings,
force_xterm)
lineeditor_input_decoder *decoder;
___BOOL map_rubout_to_backspace;
___BOOL emacs_bindings;
___BOOL force_xterm;)
{
___SCMOBJ e;
decoder->length = 0;
if ((e = lineeditor_defseq_install_table
(decoder,
lineeditor_defseq_common,
___NBELEMS(lineeditor_defseq_common)))
!= ___FIX(___NO_ERR))
return e;
if (map_rubout_to_backspace)
if ((e = lineeditor_defseq_install_table
(decoder,
lineeditor_defseq_map_rubout_to_backspace,
___NBELEMS(lineeditor_defseq_map_rubout_to_backspace)))
!= ___FIX(___NO_ERR))
return e;
if (emacs_bindings)
if ((e = lineeditor_defseq_install_table
(decoder,
lineeditor_defseq_emacs,
___NBELEMS(lineeditor_defseq_emacs)))
!= ___FIX(___NO_ERR))
return e;
#ifdef USE_CURSES
if ((e = lineeditor_dkey_install_table
(decoder,
lineeditor_dkey_table,
___NBELEMS(lineeditor_dkey_table),
force_xterm))
!= ___FIX(___NO_ERR))
return e;
#endif
if ((e = lineeditor_defseq_install_table
(decoder,
lineeditor_defseq_widespread,
___NBELEMS(lineeditor_defseq_widespread)))
!= ___FIX(___NO_ERR))
return e;
return ___FIX(___NO_ERR);
}
/*---------------------------------------------------------------------------*/
/* Line editing history routines */
___HIDDEN ___SCMOBJ lineeditor_history_begin_edit
___P((___device_tty *self,
lineeditor_history *h),
(self,
h)
___device_tty *self;
lineeditor_history *h;)
{
#define LINEEDITOR_FUDGE 80
___device_tty *d = self;
___SCMOBJ e;
if (h->edited.buffer == NULL)
{
if ((e = extensible_string_copy
(h->actual.buffer,
h->actual.length,
&h->edited,
LINEEDITOR_FUDGE))
!= ___FIX(___NO_ERR))
return e;
}
return ___FIX(___NO_ERR);
}
___HIDDEN void lineeditor_history_end_edit
___P((___device_tty *self,
lineeditor_history *h),
(self,
h)
___device_tty *self;
lineeditor_history *h;)
{
___device_tty *d = self;
if (h->edited.buffer != NULL)
{
extensible_string_cleanup (&h->edited);
h->edited.buffer = NULL;
}
}
___HIDDEN ___SCMOBJ lineeditor_history_setup
___P((___device_tty *self,
lineeditor_history **hist),
(self,
hist)
___device_tty *self;
lineeditor_history **hist;)
{
___device_tty *d = self;
___SCMOBJ e;
lineeditor_history *h;
h = ___CAST(lineeditor_history*,
___alloc_mem (sizeof (lineeditor_history)));
if (h == NULL)
return ___FIX(___HEAP_OVERFLOW_ERR);
if ((e = extensible_string_setup (&h->actual, 0)) != ___FIX(___NO_ERR))
{
___free_mem (h);
return e;
}
h->edited.buffer = NULL;
h->prev = h; /* create a circular list */
h->next = h;
*hist = h;
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ lineeditor_history_cleanup
___P((___device_tty *self,
lineeditor_history *h),
(self,
h)
___device_tty *self;
lineeditor_history *h;)
{
___device_tty *d = self;
lineeditor_history_end_edit (d, h);
extensible_string_cleanup (&h->actual);
___free_mem (h);
return ___FIX(___NO_ERR);
}
___HIDDEN void lineeditor_history_remove
___P((___device_tty *self,
lineeditor_history *item),
(self,
item)
___device_tty *self;
lineeditor_history *item;)
{
___device_tty *d = self;
lineeditor_history *prev = item->prev;
lineeditor_history *next = item->next;
if (prev == item)
d->hist_last = NULL;
else
{
next->prev = prev;
prev->next = next;
item->prev = item;
item->next = item;
if (d->hist_last == item)
d->hist_last = prev;
}
}
___HIDDEN void lineeditor_history_trim_to
___P((___device_tty *self,
int max_length),
(self,
max_length)
___device_tty *self;
int max_length;)
{
___device_tty *d = self;
while (d->history_length > max_length)
{
lineeditor_history *first = d->hist_last->next;
lineeditor_history_remove (d, first);
lineeditor_history_cleanup (d, first); /* ignore error */
d->history_length--;
}
}
___HIDDEN void lineeditor_history_trim
___P((___device_tty *self),
(self)
___device_tty *self;)
{
___device_tty *d = self;
lineeditor_history_trim_to (d, d->history_max_length);
}
___HIDDEN void lineeditor_set_history_max_length
___P((___device_tty *self,
int history_max_length),
(self,
history_max_length)
___device_tty *self;
int history_max_length;)
{
___device_tty *d = self;
if (history_max_length >= 0)
{
d->history_max_length = history_max_length;
lineeditor_history_trim (d);
}
}
___HIDDEN void lineeditor_history_add_after
___P((___device_tty *self,
lineeditor_history *item,
lineeditor_history *dest),
(self,
item,
dest)
___device_tty *self;
lineeditor_history *item;
lineeditor_history *dest;)
{
___device_tty *d = self;
if (dest == NULL)
{
item->prev = item;
item->next = item;
}
else
{
lineeditor_history *after_dest = dest->next;
item->next = after_dest;
item->prev = dest;
dest->next = item;
after_dest->prev = item;
}
d->history_length++;
}
___HIDDEN void lineeditor_history_add_last
___P((___device_tty *self,
lineeditor_history *item),
(self,
item)
___device_tty *self;
lineeditor_history *item;)
{
___device_tty *d = self;
lineeditor_history_add_after (d, item, d->hist_last);
d->hist_last = item;
}
___HIDDEN ___SCMOBJ lineeditor_history_add_line_before_last
___P((___device_tty *self,
int len,
extensible_string_char *chars),
(self,
len,
chars)
___device_tty *self;
int len;
extensible_string_char *chars;)
{
___SCMOBJ e;
lineeditor_history *line;
if ((e = lineeditor_history_setup (self, &line))
== ___FIX(___NO_ERR))
{
if ((e = extensible_string_insert_at_end (&line->actual, len, chars))
== ___FIX(___NO_ERR))
lineeditor_history_add_after (self, line, self->hist_last->prev);
else
lineeditor_history_cleanup (self, line); /* ignore error */
}
return e;
}
/*---------------------------------------------------------------------------*/
/* Line editing routines */
___HIDDEN ___SCMOBJ lineeditor_output_drain
___P((___device_tty *self),
(self)
___device_tty *self;)
{
/*
* This routine drains the output buffers. The return value is
* ___FIX(___NO_ERR) if and only if the output buffers were
* completely drained. The return value is different from
* ___FIX(___NO_ERR) only when ___device_tty_write
* returned an error.
*/
___device_tty *d = self;
___SCMOBJ e;
for (;;)
{
int byte_avail;
int len = d->output_byte_hi - d->output_byte_lo;
while (len > 0)
{
___stream_index len_done;
___U8 *byte_buf = d->output_byte + d->output_byte_lo;
if ((e = ___device_tty_write
(d,
byte_buf,
len,
&len_done)) != ___FIX(___NO_ERR))
return e;
len = d->output_byte_hi - (d->output_byte_lo += len_done);
}
d->output_byte_lo = 0;
d->output_byte_hi = 0;
len = d->output_char.length - d->output_char_lo;
if (len <= 0)
break;
do
{
___C *char_buf = d->output_char.buffer + d->output_char_lo;
___U8 *byte_buf = d->output_byte + d->output_byte_hi;
byte_avail = ___NBELEMS(d->output_byte) - d->output_byte_hi;
if (chars_to_bytes (char_buf,
&len,
byte_buf,
&byte_avail,
&d->output_encoding_state)
== ___ILLEGAL_CHAR)
len--; /* skip over the illegal character */
#ifdef ___DEBUG_TTY
{
int i;
int n = (___NBELEMS(d->output_byte) - d->output_byte_hi) - byte_avail;
___printf ("lineeditor_output_drain nb_bytes: %d ", n);
for (i=0; i<n; i++)
___printf (" %02x", d->output_byte[i]);
___printf ("\n");
}
#endif
d->output_char_lo = d->output_char.length - len;
d->output_byte_hi = ___NBELEMS(d->output_byte) - byte_avail;
} while (byte_avail > 0 && len > 0);
if (len <= 0)
{
extensible_string_set_length
(&d->output_char, 0, 1); /* ignore error */
d->output_char.length = 0; /* in case set_length failed */
d->output_char_lo = 0;
}
}
return ___FIX(___NO_ERR);
}
/* forward declaration needed because lineeditor_output is recursive */
___HIDDEN ___SCMOBJ lineeditor_output_terminal_emulate
___P((___device_tty *self,
___C *buf,
int len),
());
___HIDDEN ___SCMOBJ lineeditor_output
___P((___device_tty *self,
___C *buf,
int len),
(self,
buf,
len)
___device_tty *self;
___C *buf;
int len;)
{
/*
* This routine outputs the string of characters "buf" of length
* "len" to the terminal.
*/
___device_tty *d = self;
___SCMOBJ e;
#ifdef ___DEBUG_TTY
{
int i;
___printf ("et:%d row:%2d col:%2d cursor:%2d dw:%d len:%3d ",
d->emulate_terminal,
d->terminal_row,
d->terminal_col,
d->terminal_cursor,
d->terminal_delayed_wrap,
len);
___printf ("\"");
for (i=0; i<len; i++)
if (buf[i] < 32 || buf[i] >= 127)
___printf ("\\x%02x", buf[i]);
else
___printf ("%c", buf[i]);
___printf ("\"\n");
}
#endif
if (d->emulate_terminal)
{
d->emulate_terminal = 0;
e = lineeditor_output_terminal_emulate (d, buf, len);
d->emulate_terminal = 1;
}
else
e = extensible_string_insert
(&d->output_char,
d->output_char.length,
len,
buf);
#ifdef USE_WIN32
e = lineeditor_output_drain (d);/******************************/
#endif
return e;
}
___HIDDEN ___SCMOBJ lineeditor_output_curses_drain
___P((int len),
(len)
int len;)
{
/*
* This routine drains the curses output buffer. The parameters it
* uses are stored in the TTY module structure "___tty_mod" (because
* C does not have closures...).
*/
struct ___curses_struct *cs = &CURRENT_CURSES_STRUCT;
return lineeditor_output (___tty_mod.curses_tty, cs->output, len);
}
___HIDDEN int lineeditor_output_curses
___P((int c),
(c)
int c;)
{
/*
* This routine outputs a C character. It is passed as an argument
* to the termcap/terminfo "tputs" routine for outputing individual
* characters. The parameters it uses are stored in the TTY module
* structure "___tty_mod" (because C does not have closures...).
*/
struct ___curses_struct *cs = &CURRENT_CURSES_STRUCT;
if (cs->last_err == ___FIX(___NO_ERR))
{
cs->output[cs->output_lo++] = c;
if (cs->output_lo >= ___CAST(int,___NBELEMS(cs->output)))
{
cs->last_err = lineeditor_output_curses_drain
(___NBELEMS(cs->output));
cs->output_lo = 0;
}
}
return c;
}
___HIDDEN char *lineeditor_cap
___P((___device_tty *self,
int cap),
(self,
cap)
___device_tty *self;
int cap;)
{
/*
* This routine returns the curses string for the terminal
* capability "cap".
*/
___device_tty *d = self;
#ifdef TERMINAL_EMULATION_USES_CURSES
if (!d->emulate_terminal)
return d->capability[cap];
#endif
#ifdef USE_TERMCAP_OR_TERMINFO
return lineeditor_dcap_table[cap].xterm_cap;
#else
return lineeditor_dcap_table[cap].cap;
#endif
}
___HIDDEN ___SCMOBJ lineeditor_output_cap3
___P((___device_tty *self,
int cap,
int arg1,
int arg2,
int arg3,
int rep),
(self,
cap,
arg1,
arg2,
arg3,
rep)
___device_tty *self;
int cap;
int arg1;
int arg2;
int arg3;
int rep;)
{
/*
* This routine outputs the character sequence for the terminal
* capability "cap" with integer parameters "arg1", "arg2" and "arg3"
* (-1 means no parameter). This is repeated "rep" times.
*/
___device_tty *d = self;
struct ___curses_struct *cs;
char *str = lineeditor_cap (d, cap);
if (str == NULL)
return ___FIX(___NO_ERR);
___tty_mod.curses_tty = d;
cs = &CURRENT_CURSES_STRUCT;
cs->output_lo = 0;
cs->last_err = ___FIX(___NO_ERR);
while (rep > 0)
{
char *p;
if (cap < LINEEDITOR_CAP_SGR0) /******** TODO: this belongs elsewhere */
d->terminal_delayed_wrap = 0; /* cursor cmds cancel delayed wrap */
#ifdef TERMINAL_EMULATION_USES_CURSES
#ifdef USE_TERMCAP_OR_TERMINFO
if (!d->emulate_terminal)
{
p = str;
if (arg1 >= 0 && arg3 < 0)
{
#ifdef USE_TERMCAP
p = tgoto (p, arg2, arg1);
#else
if (arg2 >= 0)
p = tparm (p, arg1, arg2);
else
p = tparm (p, arg1);
#endif
}
if (tputs (p, 1, lineeditor_output_curses) == ERR &&
cs->last_err == ___FIX(___NO_ERR))
cs->last_err = ___FIX(___UNKNOWN_ERR);
}
else
#endif
#endif
{
int params[3];
int stack[10];
int sp = 0;
params[0] = arg1;
params[1] = arg2;
params[2] = arg3;
p = str;
while (*p != '\0')
{
___C c = *p++;
if (c == '%') /* start of a formatting command? */
{
switch (*p++)
{
case 'i':
params[0]++;
params[1]++;
break;
case 'p':
if (sp < ___CAST(int,___NBELEMS(stack)))
stack[sp++] = params[*p++ - '1'];
break;
case 'd':
{
int d = 1;
int n;
if (sp > 0)
n = stack[--sp];
else
n = 0;
while (d*10 <= n)
d *= 10;
while (d > 0)
{
lineeditor_output_curses ('0' + (n / d) % 10);
d /= 10;
}
break;
}
}
}
else
lineeditor_output_curses (c);
}
}
if (cs->last_err != ___FIX(___NO_ERR))
return cs->last_err;
rep--;
}
return lineeditor_output_curses_drain (cs->output_lo);
}
___HIDDEN ___SCMOBJ lineeditor_output_cap0
___P((___device_tty *self,
int cap,
int rep),
(self,
cap,
rep)
___device_tty *self;
int cap;
int rep;)
{
return lineeditor_output_cap3 (self, cap, -1, -1, -1, rep);
}
___HIDDEN ___SCMOBJ lineeditor_output_cap1
___P((___device_tty *self,
int cap,
int arg1,
int rep),
(self,
cap,
arg1,
rep)
___device_tty *self;
int cap;
int arg1;
int rep;)
{
return lineeditor_output_cap3 (self, cap, arg1, -1, -1, rep);
}
___HIDDEN ___SCMOBJ lineeditor_output_cap2
___P((___device_tty *self,
int cap,
int arg1,
int arg2,
int rep),
(self,
cap,
arg1,
arg2,
rep)
___device_tty *self;
int cap;
int arg1;
int arg2;
int rep;)
{
return lineeditor_output_cap3 (self, cap, arg1, arg2, -1, rep);
}
___HIDDEN ___SCMOBJ lineeditor_output_set_attrs
___P((___device_tty *self,
tty_text_attrs attrs),
(self,
attrs)
___device_tty *self;
tty_text_attrs attrs;)
{
/*
* This routine outputs the character sequence that sets the text
* attributes of the next characters sent to the terminal.
*/
___device_tty *d = self;
___SCMOBJ e;
int turn_on;
tty_text_attrs current_attrs;
#ifdef TERMINAL_EMULATION_USES_CURSES
if (!d->emulate_terminal)
{
current_attrs = d->terminal_attrs;
d->terminal_attrs = attrs;
}
else
#endif
{
current_attrs = d->current.attrs;
d->current.attrs = attrs;
}
if (current_attrs == attrs)
return ___FIX(___NO_ERR);
turn_on = GET_STYLE(attrs);
if ((GET_STYLE(current_attrs) & ~turn_on) != 0 ||
(GET_FOREGROUND_COLOR(attrs) >= DEFAULT_TEXT_COLOR &&
GET_FOREGROUND_COLOR(current_attrs) < DEFAULT_TEXT_COLOR) ||
(GET_BACKGROUND_COLOR(attrs) >= DEFAULT_TEXT_COLOR &&
GET_BACKGROUND_COLOR(current_attrs) < DEFAULT_TEXT_COLOR))
{
if ((e = lineeditor_output_cap0 (d, LINEEDITOR_CAP_SGR0, 1))
!= ___FIX(___NO_ERR))
return e;
current_attrs = MAKE_TEXT_ATTRS(0,DEFAULT_TEXT_COLOR,DEFAULT_TEXT_COLOR);
}
else
turn_on = (~GET_STYLE(current_attrs) & turn_on);
if (turn_on & TEXT_STYLE_BOLD)
if ((e = lineeditor_output_cap0 (d, LINEEDITOR_CAP_BOLD, 1))
!= ___FIX(___NO_ERR))
return e;
if (turn_on & TEXT_STYLE_UNDERLINE)
if ((e = lineeditor_output_cap0 (d, LINEEDITOR_CAP_SMUL, 1))
!= ___FIX(___NO_ERR))
return e;
if (turn_on & TEXT_STYLE_REVERSE)
if ((e = lineeditor_output_cap0 (d, LINEEDITOR_CAP_REV, 1))
!= ___FIX(___NO_ERR))
return e;
if (GET_FOREGROUND_COLOR(attrs) < DEFAULT_TEXT_COLOR &&
GET_FOREGROUND_COLOR(attrs) != GET_FOREGROUND_COLOR(current_attrs))
if ((e = lineeditor_output_cap1
(d,
LINEEDITOR_CAP_SETAF,
GET_FOREGROUND_COLOR(attrs),
1))
!= ___FIX(___NO_ERR))
return e;
if (GET_BACKGROUND_COLOR(attrs) < DEFAULT_TEXT_COLOR &&
GET_BACKGROUND_COLOR(attrs) != GET_BACKGROUND_COLOR(current_attrs))
if ((e = lineeditor_output_cap1
(d,
LINEEDITOR_CAP_SETAB,
GET_BACKGROUND_COLOR(attrs),
1))
!= ___FIX(___NO_ERR))
return e;
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ lineeditor_output_terminal_plain_chars
___P((___device_tty *self,
___C *buf,
int len),
(self,
buf,
len)
___device_tty *self;
___C *buf;
int len;)
{
/*
* This routine processes a string of plain characters (with no
* control characters) that the emulated terminal received. It also
* tracks the movement of the emulated terminal's cursor.
*/
___device_tty *d = self;
int col = d->terminal_col + d->terminal_delayed_wrap + len;
if (col >= d->terminal_nb_cols)
{
if (d->has_auto_right_margin)
{
int row = d->terminal_row + col / d->terminal_nb_cols;
col = col % d->terminal_nb_cols;
if (col == 0 && d->has_eat_newline_glitch)
{
col = d->terminal_nb_cols - 1;
row--;
d->terminal_delayed_wrap = 1; /* delay wrap */
}
else
d->terminal_delayed_wrap = 0;
if (row >= d->terminal_nb_rows)
{
d->current.line_start -= d->terminal_nb_cols *
(row - d->terminal_nb_rows + 1);
row = d->terminal_nb_rows - 1;
}
d->terminal_row = row;
}
else
col = d->terminal_nb_cols - 1;
}
else
d->terminal_delayed_wrap = 0;
d->terminal_col = col;
d->terminal_cursor = d->terminal_row * d->terminal_nb_cols + col;
return lineeditor_output (d, buf, len);
}
#ifdef USE_WIN32
___HIDDEN ___SCMOBJ lineeditor_output_terminal_op_move_abs
___P((___device_tty *self,
int dest_col,
int dest_row),
());
#endif
___HIDDEN ___SCMOBJ lineeditor_output_terminal_op_move_col
___P((___device_tty *self,
int dist),
(self,
dist)
___device_tty *self;
int dist;)
{
/*
* This routine performs a relative cursor positioning operation
* that changes the column of the cursor. It also tracks the
* movement of the emulated terminal's cursor.
*/
___device_tty *d = self;
int col = d->terminal_col;
int dest_col = col + dist;
if (dest_col < 0)
dest_col = 0;
else if (dest_col >= d->terminal_nb_cols)
dest_col = d->terminal_nb_cols - 1;
#ifdef USE_WIN32
return lineeditor_output_terminal_op_move_abs
(d,
dest_col,
d->terminal_row);
#else
dist = dest_col - col;
if (dist != 0)
{
d->terminal_col = dest_col;
d->terminal_cursor = d->terminal_row * d->terminal_nb_cols + dest_col;
d->terminal_delayed_wrap = 0;
if (dist > 0)
return lineeditor_output_cap1 (d, LINEEDITOR_CAP_CUF, dist, 1);
else
return lineeditor_output_cap1 (d, LINEEDITOR_CAP_CUB, -dist, 1);
}
return ___FIX(___NO_ERR);
#endif
}
___HIDDEN ___SCMOBJ lineeditor_output_terminal_op_move_row
___P((___device_tty *self,
int dist),
(self,
dist)
___device_tty *self;
int dist;)
{
/*
* This routine performs a relative cursor positioning operation
* that changes the row of the cursor. It also tracks the movement
* of the emulated terminal's cursor.
*/
___device_tty *d = self;
int row = d->terminal_row;
int dest_row = row + dist;
if (dest_row < 0)
dest_row = 0;
else if (dest_row >= d->terminal_nb_rows)
dest_row = d->terminal_nb_rows - 1;
#ifdef USE_WIN32
return lineeditor_output_terminal_op_move_abs
(d,
d->terminal_col,
dest_row);
#else
dist = dest_row - row;
if (dist != 0)
{
d->terminal_row = dest_row;
d->terminal_cursor = dest_row * d->terminal_nb_cols + d->terminal_col;
d->terminal_delayed_wrap = 0;
if (dist > 0)
{
if ((dist == 1 || lineeditor_cap (d, LINEEDITOR_CAP_CUD) == NULL) &&
lineeditor_cap (d, LINEEDITOR_CAP_CUD1) != NULL)
return lineeditor_output_cap0 (d, LINEEDITOR_CAP_CUD1, dist);
else
return lineeditor_output_cap1 (d, LINEEDITOR_CAP_CUD, dist, 1);
}
else
{
if ((dist == -1 || lineeditor_cap (d, LINEEDITOR_CAP_CUU) == NULL) &&
lineeditor_cap (d, LINEEDITOR_CAP_CUU1) != NULL)
return lineeditor_output_cap0 (d, LINEEDITOR_CAP_CUU1, -dist);
else
return lineeditor_output_cap1 (d, LINEEDITOR_CAP_CUU, -dist, 1);
}
}
return ___FIX(___NO_ERR);
#endif
}
___HIDDEN ___SCMOBJ lineeditor_output_terminal_op_move_abs
___P((___device_tty *self,
int dest_col,
int dest_row),
(self,
dest_col,
dest_row)
___device_tty *self;
int dest_col;
int dest_row;)
{
/*
* This routine performs an absolute cursor positioning operation.
* It also tracks the movement of the emulated terminal's cursor.
*/
___device_tty *d = self;
___SCMOBJ e = ___FIX(___NO_ERR);
#ifdef USE_WIN32
{
CONSOLE_SCREEN_BUFFER_INFO info;
if (!GetConsoleScreenBufferInfo (d->hout, &info))
e = err_code_from_GetLastError ();
else
{
COORD pos = info.dwCursorPosition;
pos.X += dest_col - d->terminal_col;
pos.Y += dest_row - d->terminal_row;
d->terminal_col = dest_col;
d->terminal_row = dest_row;
d->terminal_cursor = dest_row * d->terminal_nb_cols + dest_col;
d->terminal_delayed_wrap = 0;
if (!SetConsoleCursorPosition (d->hout, pos))
e = err_code_from_GetLastError ();
}
}
#else
if (dest_col == 0 &&
dest_row == 0 &&
lineeditor_cap (d, LINEEDITOR_CAP_HOME) != NULL)
{
d->terminal_col = 0;
d->terminal_row = 0;
d->terminal_cursor = 0;
d->terminal_delayed_wrap = 0;
return lineeditor_output_cap0 (d, LINEEDITOR_CAP_HOME, 1);
}
if (lineeditor_cap (d, LINEEDITOR_CAP_CUP) != NULL)
{
d->terminal_col = dest_col;
d->terminal_row = dest_row;
d->terminal_cursor = dest_row * d->terminal_nb_cols + dest_col;
d->terminal_delayed_wrap = 0;
return lineeditor_output_cap2
(d,
LINEEDITOR_CAP_CUP,
dest_row,
dest_col,
1);
}
if ((e = lineeditor_output_terminal_op_move_col
(d,
dest_col - d->terminal_col)) == ___FIX(___NO_ERR))
e = lineeditor_output_terminal_op_move_row
(d,
dest_row - d->terminal_row);
#endif
return e;
}
#define TERMINAL_MOVE_ABS 1
#define TERMINAL_MOVE_ROW 0
#define TERMINAL_MOVE_COL -1
#define TERMINAL_ERASE_DISP -2
#define TERMINAL_ERASE_LINE -3
#define TERMINAL_SET_ATTRS -4
#define TERMINAL_NOOP -5
#define TERMINAL_CTRL -6
#define TERMINAL_WINDOW_OP -7
___HIDDEN ___SCMOBJ lineeditor_output_terminal_op
___P((___device_tty *self,
int op,
int arg,
___U8 *text_arg),
(self,
op,
arg,
text_arg)
___device_tty *self;
int op;
int arg;
___U8 *text_arg;)
{
/*
* This routine performs an operation of the emulated terminal and
* tracks the movement of the cursor.
*/
___device_tty *d = self;
___SCMOBJ e = ___FIX(___NO_ERR);
switch (op)
{
case TERMINAL_NOOP:
break;
case TERMINAL_CTRL - ___UNICODE_BELL:
{
#ifdef USE_POSIX
{
___C c = ___UNICODE_BELL;
e = lineeditor_output (d, &c, 1);
}
#endif
#ifdef USE_WIN32
if (!MessageBeep (MB_OK))
e = err_code_from_GetLastError ();
#endif
break;
}
case TERMINAL_CTRL - ___UNICODE_BACKSPACE:
{
if (d->terminal_col > 0)
d->terminal_col--;
else if (d->terminal_row > 0 && d->has_auto_left_margin)
{
d->terminal_row--;
d->terminal_col = d->terminal_nb_cols - 1;
}
d->terminal_cursor = d->terminal_row * d->terminal_nb_cols +
d->terminal_col;
d->terminal_delayed_wrap = 0;
#ifdef USE_POSIX
{
___C c = ___UNICODE_BACKSPACE;
e = lineeditor_output (d, &c, 1);
}
#endif
#ifdef USE_WIN32
{
CONSOLE_SCREEN_BUFFER_INFO info;
if (!GetConsoleScreenBufferInfo (d->hout, &info))
e = err_code_from_GetLastError ();
else
{
COORD pos = info.dwCursorPosition;
if (pos.X > 0)
pos.X--;
else if (pos.Y > info.srWindow.Top && d->has_auto_left_margin)
{
pos.X = info.dwSize.X - 1;
pos.Y--;
}
if (!SetConsoleCursorPosition (d->hout, pos))
e = err_code_from_GetLastError ();
}
}
#endif
break;
}
case TERMINAL_CTRL - ___UNICODE_TAB:
{
e = lineeditor_output_terminal_op_move_col
(d,
8 - d->terminal_col % 8);
break;
}
case TERMINAL_CTRL - ___UNICODE_LINEFEED:
{
if (d->terminal_row < d->terminal_nb_rows-1)
d->terminal_row++;
else
d->current.line_start -= d->terminal_nb_cols;
if (d->linefeed_moves_to_left_margin || !d->output_raw)
d->terminal_col = 0;
d->terminal_cursor = d->terminal_row * d->terminal_nb_cols +
d->terminal_col;
d->terminal_delayed_wrap = 0;
#ifdef USE_POSIX
{
___C c = ___UNICODE_LINEFEED;
e = lineeditor_output (d, &c, 1);
}
#endif
#ifdef USE_WIN32
{
CONSOLE_SCREEN_BUFFER_INFO info;
if (!GetConsoleScreenBufferInfo (d->hout, &info))
e = err_code_from_GetLastError ();
else
{
COORD pos = info.dwCursorPosition;
if (pos.Y >= info.dwSize.Y - 1)
{
SMALL_RECT rect;
CHAR_INFO fill;
COORD dest;
rect.Top = 0;
rect.Bottom = info.dwSize.Y - 1;
rect.Left = 0;
rect.Right = info.dwSize.X - 1;
dest.X = 0;
dest.Y = -1;
fill.Attributes = info.wAttributes;
TTY_CHAR_SELECT(fill.Char.AsciiChar = ' ',
fill.Char.UnicodeChar = ' ');
if (!ScrollConsoleScreenBuffer (d->hout,
&rect,
&rect,
dest,
&fill))
e = err_code_from_GetLastError ();
pos.Y = info.dwSize.Y - 1;
}
else
pos.Y++;
if (e == ___FIX(___NO_ERR))
{
if (d->linefeed_moves_to_left_margin || !d->output_raw)
pos.X = 0;
if (!SetConsoleCursorPosition (d->hout, pos))
e = err_code_from_GetLastError ();
}
}
}
#endif
break;
}
case TERMINAL_CTRL - ___UNICODE_RETURN:
{
d->terminal_col = 0;
d->terminal_cursor = d->terminal_row * d->terminal_nb_cols;
d->terminal_delayed_wrap = 0;
#ifdef USE_POSIX
{
___C c = ___UNICODE_RETURN;
e = lineeditor_output (d, &c, 1);
}
#endif
#ifdef USE_WIN32
{
CONSOLE_SCREEN_BUFFER_INFO info;
if (!GetConsoleScreenBufferInfo (d->hout, &info))
e = err_code_from_GetLastError ();
else
{
COORD pos = info.dwCursorPosition;
pos.X = 0;
if (!SetConsoleCursorPosition (d->hout, pos))
e = err_code_from_GetLastError ();
}
}
#endif
break;
}
case TERMINAL_SET_ATTRS:
{
#ifdef USE_POSIX
e = lineeditor_output_set_attrs (d, arg);
#endif
#ifdef USE_WIN32
{
int style = GET_STYLE(arg);
int fg = GET_FOREGROUND_COLOR(arg);
int bg = GET_BACKGROUND_COLOR(arg);
WORD attr = 0;
if (fg == DEFAULT_TEXT_COLOR)
fg = NORMAL_FOREGROUND;
if (bg == DEFAULT_TEXT_COLOR)
bg = NORMAL_BACKGROUND;
if (style & TEXT_STYLE_BOLD)
attr |= FOREGROUND_INTENSITY;
if (style & TEXT_STYLE_UNDERLINE)
attr |= BACKGROUND_INTENSITY;
if (style & TEXT_STYLE_REVERSE)
{
int temp = fg;
fg = bg;
bg = temp;
}
if (fg & 4) attr |= FOREGROUND_BLUE;
if (fg & 2) attr |= FOREGROUND_GREEN;
if (fg & 1) attr |= FOREGROUND_RED;
if (bg & 4) attr |= BACKGROUND_BLUE;
if (bg & 2) attr |= BACKGROUND_GREEN;
if (bg & 1) attr |= BACKGROUND_RED;
if (!SetConsoleTextAttribute (d->hout, attr))
e = err_code_from_GetLastError ();
}
#endif
break;
}
#ifdef USE_WIN32
case TERMINAL_ERASE_DISP:
case TERMINAL_ERASE_LINE:
{
if (arg <= 2) /* argument valid? */
{
COORD pos;
CONSOLE_SCREEN_BUFFER_INFO info;
DWORD n;
DWORD written;
if (!GetConsoleScreenBufferInfo (d->hout, &info))
return err_code_from_GetLastError ();
if (d->terminal_col == 0 &&
d->terminal_row == 0)
{
pos.X = 0;
pos.Y = 0;
if (!SetConsoleCursorPosition (d->hout, pos))
return err_code_from_GetLastError ();
}
else
pos = info.dwCursorPosition;
if (arg == 0)
n = info.dwSize.X - pos.X;
else
{
if (arg == 1)
n = pos.X;
else
n = info.dwSize.X;
pos.X = 0;
}
if (op == TERMINAL_ERASE_DISP)
{
if (arg == 0)
n += info.dwSize.X * (info.dwSize.Y - pos.Y - 1);
else
{
if (arg == 1)
n += info.dwSize.X * pos.Y;
else
n = info.dwSize.X * info.dwSize.Y;
pos.Y = 0;
}
}
if (!FillConsoleOutputAttribute
(d->hout,
info.wAttributes,
n,
pos,
&written) ||
!FillConsoleOutputCharacter
(d->hout,
' ',
n,
pos,
&written))
e = err_code_from_GetLastError ();
}
break;
}
case TERMINAL_WINDOW_OP:
{
int window_op = arg & ((1<<8)-1);
int arg1 = (arg >> 8) & ((1<<12)-1);
int arg2 = (arg >> 20) & ((1<<12)-1);
HWND cons_wind = GetConsoleWindow ();
if (cons_wind != NULL)
{
if (text_arg != NULL)
{
SetWindowTextA (cons_wind,
___CAST(LPCSTR,text_arg)); /* ignore error */
}
else
{
switch (window_op)
{
case 1: /* De-iconify window */
case 2: /* Iconify window */
ShowWindow (cons_wind,
(window_op == 1) ? SW_RESTORE : SW_MINIMIZE);
break;
case 3: /* Move window to [arg1, arg2] */
SetWindowPos (cons_wind,
cons_wind,
arg1,
arg2,
0,
0,
SWP_NOZORDER | SWP_NOSIZE);
break;
case 4: /* Resize window to height=arg1 and width=arg2 in pixels */
SetWindowPos (cons_wind,
cons_wind,
0,
0,
arg2,
arg1,
SWP_NOZORDER | SWP_NOMOVE);
break;
case 5: /* Raise the window to the front of the stacking order */
case 6: /* Lower the window to the bottom of the stacking order */
SetWindowPos (cons_wind,
(window_op == 5) ? HWND_TOP : HWND_BOTTOM,
0,
0,
0,
0,
SWP_NOSIZE | SWP_NOMOVE);
break;
case 7: /* Refresh the window */
break;
case 8: /* Resize window to height=arg1 and width=arg2 in chars */
break;
case 9: /* Maximize or un-maximize window (arg1=0 or arg1=1) */
ShowWindow (cons_wind,
(arg1 == 0) ? SW_MAXIMIZE : SW_RESTORE);
break;
}
}
}
break;
}
#else
case TERMINAL_ERASE_DISP:
{
switch (arg)
{
case 1: /* erase from beginning of screen */
break;
case 2: /* erase all screen */
if (d->terminal_col != 0 || d->terminal_row != 0)
break;
case 0: /* erase to end of screen */
e = lineeditor_output_cap0 (d, LINEEDITOR_CAP_ED, 1);
break;
}
break;
}
case TERMINAL_ERASE_LINE:
{
switch (arg)
{
case 1: /* erase from beginning of line */
e = lineeditor_output_cap0 (d, LINEEDITOR_CAP_EL1, 1);
break;
case 2: /* erase all line */
if (d->terminal_col != 0)
break;
case 0: /* erase to end of line */
e = lineeditor_output_cap0 (d, LINEEDITOR_CAP_EL, 1);
break;
}
break;
}
case TERMINAL_WINDOW_OP:
{
int window_op = arg & ((1<<8)-1);
int arg1 = (arg >> 8) & ((1<<12)-1);
int arg2 = (arg >> 20) & ((1<<12)-1);
if (text_arg != NULL)
{
___C c;
e = lineeditor_output_cap1
(d,
LINEEDITOR_CAP_WINDOW_OP3,
window_op,
1);
while (e == ___FIX(___NO_ERR) &&
*text_arg != ___UNICODE_NUL)
{
c = *text_arg++;
e = lineeditor_output (d, &c, 1);
}
if (e == ___FIX(___NO_ERR))
{
c = ___UNICODE_BELL;
e = lineeditor_output (d, &c, 1);
}
}
else
{
switch (window_op)
{
case 1: /* De-iconify window */
case 2: /* Iconify window */
case 5: /* Raise the window to the front of the stacking order */
case 6: /* Lower the window to the bottom of the stacking order */
case 7: /* Refresh the window */
e = lineeditor_output_cap1
(d,
LINEEDITOR_CAP_WINDOW_OP0,
window_op,
1);
break;
case 9: /* Maximize or un-maximize window (arg1=0 or arg1=1) */
e = lineeditor_output_cap2
(d,
LINEEDITOR_CAP_WINDOW_OP1,
window_op,
arg1,
1);
break;
case 3: /* Move window to [arg1, arg2] */
case 4: /* Resize window to height=arg1 and width=arg2 in pixels */
case 8: /* Resize window to height=arg1 and width=arg2 in chars */
e = lineeditor_output_cap3
(d,
LINEEDITOR_CAP_WINDOW_OP2,
window_op,
arg1,
arg2,
1);
break;
}
}
break;
}
#endif
case TERMINAL_MOVE_COL:
{
e = lineeditor_output_terminal_op_move_col (d, arg);
break;
}
case TERMINAL_MOVE_ROW:
{
e = lineeditor_output_terminal_op_move_row (d, arg);
break;
}
default:
{
if (op >= TERMINAL_MOVE_ABS)
e = lineeditor_output_terminal_op_move_abs
(d,
arg,
op - TERMINAL_MOVE_ABS);
break;
}
}
return e;
}
___HIDDEN ___SCMOBJ lineeditor_output_terminal_emulate
___P((___device_tty *self,
___C *buf,
int len),
(self,
buf,
len)
___device_tty *self;
___C *buf;
int len;)
{
/*
* This routine processes a string of characters (possibly
* containing control characters and escape sequences) that the
* emulated terminal received. It also tracks the movement of the
* emulated terminal's cursor.
*/
___device_tty *d = self;
___SCMOBJ e;
int pn;
___C c;
#ifdef ___DEBUG_TTY
{
int i;
___printf ("lineeditor_output_terminal_emulate len: %d ", len);
___printf ("\"");
for (i=0; i<len; i++)
if (buf[i] < 32 || buf[i] >= 127)
___printf ("\\x%02x", buf[i]);
else
___printf ("%c", buf[i]);
___printf ("\"\n");
}
#endif
pn = d->terminal_param_num;
while (len > 0)
{
___C c = *buf++;
len--;
if (!d->editing_line)
{
/* accumulate prompt */
int i = d->prompt_length;
if (i < ___CAST(int,___NBELEMS(d->prompt)))
{
d->prompt[i] = c;
d->prompt_length = i+1;
}
}
switch (pn)
{
case -2:
{
/* outside of an escape sequence */
if (c >= ___UNICODE_SPACE)
{
int n = 0;
while (n < len && *buf >= ___UNICODE_SPACE)
{
n++;
buf++;
}
if (!d->editing_line)
{
/* accumulate prompt */
___C *p = buf - n;
int i = d->prompt_length;
while (i < ___CAST(int,___NBELEMS(d->prompt)) && p < buf)
d->prompt[i++] = *p++;
d->prompt_length = i;
}
len -= n;
n++;
if ((e = lineeditor_output_terminal_plain_chars (d, buf-n, n))
!= ___FIX(___NO_ERR))
{
d->terminal_param_num = pn;
return e;
}
}
else if (c != ___UNICODE_ESCAPE) /* non ESC control character? */
{
if (c == ___UNICODE_LINEFEED)
{
if (!d->editing_line)
d->prompt_length = 0; /* reset prompt */
/******** TODO: should check if cr-lf, etc is needed */
if ((e = lineeditor_output_terminal_op
(d,
TERMINAL_CTRL - c,
0,
NULL))
!= ___FIX(___NO_ERR))
{
d->terminal_param_num = pn;
return e;
}
}
else
{
if ((e = lineeditor_output_terminal_op
(d,
TERMINAL_CTRL - c,
0,
NULL))
!= ___FIX(___NO_ERR))
{
d->terminal_param_num = pn;
return e;
}
}
}
else
pn = -1; /* start of an escape sequence */
break;
}
case -1:
{
/* after an ESC */
if (c == '[')
{
d->terminal_op_type = 0;
pn = 0;
d->terminal_param[0] = 0;
}
else if (c == ']')
{
d->terminal_op_type = 1;
pn = 0;
d->terminal_param[0] = 0;
}
else
pn = -2;
break;
}
default:
{
/* accumulating parameters after an ESC '[' */
if (d->terminal_op_type == 1 && pn == 1)
{
if (c == ___UNICODE_BELL)
{
pn = -2;
if ((e = lineeditor_output_terminal_op
(d,
TERMINAL_WINDOW_OP,
d->terminal_param[0],
d->terminal_param_text))
!= ___FIX(___NO_ERR))
{
d->terminal_param_num = pn;
return e;
}
}
else
{
if (d->terminal_param[1] <
___CAST(int,___NBELEMS(d->terminal_param_text))-1)
d->terminal_param_text[d->terminal_param[1]++] = c;
}
}
else if (c >= '0' && c <= '9')
{
int x = c - '0';
int p = d->terminal_param[pn];
if (p < 1000)
d->terminal_param[pn] = p*10 + x;
}
else if (c == ';')
{
if (pn < ___CAST(int,___NBELEMS(d->terminal_param))-1)
pn++;
d->terminal_param[pn] = 0;
}
else
{
int op = TERMINAL_NOOP;
int arg = 0;
if (c == 'A')
{
op = TERMINAL_MOVE_ROW;
arg = -d->terminal_param[0];
if (arg >= 0) arg = -1;
}
else if (c == 'B')
{
op = TERMINAL_MOVE_ROW;
arg = d->terminal_param[0];
if (arg <= 0) arg = 1;
}
else if (c == 'C')
{
op = TERMINAL_MOVE_COL;
arg = d->terminal_param[0];
if (arg <= 0) arg = 1;
}
else if (c == 'D')
{
op = TERMINAL_MOVE_COL;
arg = -d->terminal_param[0];
if (arg >= 0) arg = -1;
}
else if (c == 'H' || c == 'f')
{
op = d->terminal_param[0];
if (op <= 0) op = 1;
op += TERMINAL_MOVE_ABS - 1;
arg = d->terminal_param[1];
if (pn < 1 || arg <= 0) arg = 1;
arg--;
}
else if (c == 'J')
{
op = TERMINAL_ERASE_DISP;
arg = d->terminal_param[0];
if (arg <= 0) arg = 0;
}
else if (c == 'K')
{
op = TERMINAL_ERASE_LINE;
arg = d->terminal_param[0];
if (arg <= 0) arg = 0;
}
else if (c == 'm')
{
int j;
int style;
int fg;
int bg;
op = TERMINAL_SET_ATTRS;
arg = d->terminal_attrs;
style = GET_STYLE(arg);
fg = GET_FOREGROUND_COLOR(arg);
bg = GET_BACKGROUND_COLOR(arg);
for (j=0; j<=pn; j++)
{
int x = d->terminal_param[j];
if (x <= 0)
{
style = TEXT_STYLE_NORMAL;
fg = DEFAULT_TEXT_COLOR;
bg = DEFAULT_TEXT_COLOR;
}
else if (x == 1)
style |= TEXT_STYLE_BOLD;
else if (x == 4)
style |= TEXT_STYLE_UNDERLINE;
else if (x == 7)
style |= TEXT_STYLE_REVERSE;
else if (x >= 30 && x <= 37)
fg = x-30;
else if (x >= 40 && x <= 47)
bg = x-40;
}
arg = MAKE_TEXT_ATTRS(style,fg,bg);
}
else if (c == 't')
{
switch (d->terminal_param[0])
{
case 3:
case 4:
case 8:
if (pn == 2 &&
d->terminal_param[1] <= 4095 &&
d->terminal_param[2] <= 4095)
{
arg = (d->terminal_param[2] << 20) +
(d->terminal_param[1] << 8) +
d->terminal_param[0];
op = TERMINAL_WINDOW_OP;
}
break;
case 9:
if (pn == 1 &&
d->terminal_param[1] <= 4095)
{
arg = (d->terminal_param[1] << 8) +
d->terminal_param[0];
op = TERMINAL_WINDOW_OP;
}
break;
default:
if (pn == 0 &&
d->terminal_param[0] >= 1 &&
d->terminal_param[0] <= 9)
{
arg = d->terminal_param[0];
op = TERMINAL_WINDOW_OP;
}
break;
}
}
pn = -2;
if ((e = lineeditor_output_terminal_op (d, op, arg, NULL))
!= ___FIX(___NO_ERR))
{
d->terminal_param_num = pn;
return e;
}
}
break;
}
}
}
d->terminal_param_num = pn;
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ lineeditor_output_chars
___P((___device_tty *self,
___C *buf,
___stream_index len,
tty_text_attrs attrs),
(self,
buf,
len,
attrs)
___device_tty *self;
___C *buf;
___stream_index len;
tty_text_attrs attrs;)
{
/*
* This routine outputs "len" characters from the buffer "buf" using
* the text attributes "attrs".
*/
___device_tty *d = self;
___SCMOBJ e;
if ((e = lineeditor_output_set_attrs (d, attrs))
== ___FIX(___NO_ERR))
e = lineeditor_output (d, buf, len);
return e;
}
___HIDDEN ___SCMOBJ lineeditor_output_char_repetition
___P((___device_tty *self,
___C c,
int rep,
tty_text_attrs attrs),
(self,
c,
rep,
attrs)
___device_tty *self;
___C c;
int rep;
tty_text_attrs attrs;)
{
/*
* This routine outputs the character "c" a total of "rep" times
* using the text attributes "attrs".
*/
#define CHAR_BUFFER_SIZE (80*50)
___device_tty *d = self;
___SCMOBJ e;
___C char_buffer[CHAR_BUFFER_SIZE];
int n;
n = rep;
if (n > CHAR_BUFFER_SIZE)
n = CHAR_BUFFER_SIZE;
while (n > 0)
char_buffer[--n] = c;
while (rep > 0)
{
n = rep;
if (n > CHAR_BUFFER_SIZE)
n = CHAR_BUFFER_SIZE;
if ((e = lineeditor_output_chars (d, char_buffer, n, attrs))
!= ___FIX(___NO_ERR))
return e;
rep -= n;
}
return ___FIX(___NO_ERR);
}
___HIDDEN tty_text_attrs lineeditor_erase_attrs
___P((___device_tty *self),
(self)
___device_tty *self;)
{
/*
* This routine returns the text attributes that should be used to
* erase portions of the screen.
*/
___device_tty *d = self;
tty_text_attrs output_attrs = d->output_attrs;
int output_style = GET_STYLE(output_attrs);
int current_style = GET_S