Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

8616 lines (6986 sloc) 215.031 kb
/* File: "os_tty.c" */
/* Copyright (c) 1994-2011 by Marc Feeley, All Rights Reserved. */
/*
* This module implements the operating system specific routines
* related to ttys.
*/
#define ___INCLUDED_FROM_OS_TTY
#define ___VERSION 406003
#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, 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_STYLE(d->current.attrs);
return MAKE_TEXT_ATTRS(output_style & TEXT_STYLE_REVERSE,
GET_FOREGROUND_COLOR(output_attrs),
GET_BACKGROUND_COLOR(output_attrs));
}
___HIDDEN ___SCMOBJ lineeditor_output_current_hist
___P((___device_tty *self,
int start,
int len),
(self,
start,
len)
___device_tty *self;
int start;
int len;)
{
/*
* This routine sends to the terminal "len" characters of the line
* being edited starting at position "start". The line is logically
* padded with spaces at both ends (so that a negative "start"
* outputs spaces first and if "start+len" is beyond the end of the
* line some spaces will be output at the end).
*/
___device_tty *d = self;
___SCMOBJ e;
extensible_string *edited = &d->current.hist->edited;
int spaces_at_head;
int chars_from_line;
spaces_at_head = -start;
if (spaces_at_head < 0)
spaces_at_head = 0;
else
{
if (spaces_at_head > len)
spaces_at_head = len;
start += spaces_at_head;
len -= spaces_at_head;
}
chars_from_line = edited->length - start;
if (chars_from_line < 0)
chars_from_line = 0;
else
{
if (chars_from_line > len)
chars_from_line = len;
len -= chars_from_line;
}
if (spaces_at_head > 0)
if ((e = lineeditor_output_char_repetition
(d,
___UNICODE_SPACE,
spaces_at_head,
lineeditor_erase_attrs (d)))
!= ___FIX(___NO_ERR))
return e;
if (chars_from_line > 0)
if ((e = lineeditor_output_chars
(d,
&edited->buffer[start],
chars_from_line,
d->input_attrs))
!= ___FIX(___NO_ERR))
return e;
if (len > 0)
if ((e = lineeditor_output_char_repetition
(d,
___UNICODE_SPACE,
len,
lineeditor_erase_attrs (d)))
!= ___FIX(___NO_ERR))
return e;
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ lineeditor_output_force_delayed_wrap
___P((___device_tty *self),
(self)
___device_tty *self;)
{
/*
* This routine forces the cursor to wrap to the next line if the
* wrap was delayed.
*/
___device_tty *d = self;
if (d->terminal_delayed_wrap)
return lineeditor_output_current_hist
(d,
d->terminal_cursor + 1 - d->current.line_start,
1);
return ___FIX(___NO_ERR);
}
___HIDDEN ___SCMOBJ lineeditor_move_cursor_plain
___P((___device_tty *self,
int dist),
(self,
dist)
___device_tty *self;
int dist;)
{
/*
* This routine sends the appropriate commands to the terminal to
* move the cursor "dist" positions forward/backward by writing
* plain characters or backspace characters. We assume that the
* terminal has auto left/right margi