static char rcsid[] = "@(#)$Id: curs_input.c,v 1.20 2006/06/26 17:10:32 hurtta Exp $";
/******************************************************************************
* The Elm (ME+) Mail System - $Revision: 1.20 $ $State: Exp $
*
* Modified by: Kari Hurtta <hurtta+elm@posti.FMI.FI>
* (was hurtta+elm@ozone.FMI.FI)
* or Kari Hurtta <elm@elmme-mailer.org>
*****************************************************************************
*
* Some code copied from ../src/curses.c. It have following copyright:
*
* Copyright (c) 1988-1992 USENET Community Trust
* Copyright (c) 1986,1987 Dave Taylor
*****************************************************************************/
#include "def_screen.h"
DEBUG_VAR(Debug,__FILE__,"screen");
#include <errno.h>
#if POLL_METHOD
static unsigned char *read_buffer = NULL;
static int read_buffer_len = 0;
static int last_read_error = 0;
static int background_read P_((int fd, void *data));
static int background_read(fd,data)
int fd;
void *data;
{
int result;
unsigned char ch;
result = read(fd, &ch, 1);
if (result < 0) {
int err = errno;
SIGDPRINT(Debug,4,(&Debug,
"background_read: errno = %d [%s]\n",err,
error_description(err)));
if((err == EINTR)
#ifdef EAGAIN
|| (err == EAGAIN)
#endif
#ifdef EWOULDBLOCK
|| (err == EWOULDBLOCK)
#endif
) {
return 1; /* Retry */
}
last_read_error = err;
return 0;
}
if (0 == result) {
SIGDPRINT(Debug,4,(&Debug,
"background_read: Got zero bytes...\n"));
last_read_error = -1;
return 0;
}
read_buffer = safe_realloc(read_buffer,read_buffer_len+1);
SIGDPRINT(Debug,20,(&Debug,
"background_read(fd=%d)=1 [%d]",fd,read_buffer_len));
SIGDPRINT(Debug,50,(&Debug,"=%d",ch));
SIGDPRINT(Debug,20,(&Debug,"\n"));
read_buffer[read_buffer_len++] = ch;
last_read_error = 0;
return 1;
}
void clear_input_buffer()
{
SIGDPRINT(Debug,4,(&Debug,
"Resetting background_read buffer (%d bytes)...\n",
read_buffer_len));
read_buffer_len = 0;
last_read_error = 0;
}
int error_sleep(seconds)
int seconds;
{
int ret;
if (read_buffer_len > 0) {
SIGDPRINT(Debug,5,(&Debug,
"error_sleep(%d)=1 -- sleep skipped\n",
seconds));
return 1;
}
FlushBuffer();
SIGDPRINT(Debug,5,(&Debug,
"error_sleep(%d) ...\n",seconds));
ret = wait_for_action_or_timeout(background_read,seconds);
DPRINT(Debug,5,(&Debug,
"error_sleep=%d\n",ret));
return ret;
}
#else
int error_sleep(seconds)
int seconds;
{
FlushBuffer();
return sleep(seconds);
}
#endif
#ifdef TERMIOS
#include <termios.h>
#endif
struct charset_state * last_state = NULL;
void set_last_state(cs)
charset_t cs;
{
if (last_state && last_state->charset != cs) {
DPRINT(Debug,4,(&Debug,
"Keyboard input charset is changed -- was %s\n",
last_state->charset->MIME_name ?
last_state->charset->MIME_name :
"<no MIME name>"));
free_state(&last_state);
}
if (!last_state) {
last_state = new_state(cs);
DPRINT(Debug,4,(&Debug,
"Keyboard input charset is %s\n",
last_state->charset->MIME_name ?
last_state->charset->MIME_name :
"<no MIME name>"));
}
}
struct charset_state * cur_ReadCh2(flags,break_flag)
int flags;
int break_flag;
{
int redraw = (flags & READCH_MASK);
int cursorkeys = (flags & READCH_CURSOR) != 0;
int nocursor = (flags & READCH_NOCURSOR) != 0 && cursor_control;
int term_char = (flags & READCH_term_char) != 0;
int resize_flag = (flags & READCH_resize) != 0;
int sig_char = (flags & READCH_sig_char) != 0;
int quote_char = (flags & READCH_quote) != 0;
int poll_it = (flags & READCH_poll) != 0;
int line,col;
unsigned char input_buffer[20];
/*
* read a character with Raw mode set!
*
* EAGAIN & EWOULDBLOCK are recognized just in case
* O_NONBLOCK happens to be in effect.
*/
/* This is static array so we can initialize it in here ...
*/
static struct {
char ** CONST str;
CONST int result;
int maybe;
} keytable[] =
{ { &_key_up, UP_MARK, 0 },
{ &_key_down, DOWN_MARK, 0 },
{ &_key_left, LEFT_MARK, 0 },
{ &_key_right, RIGHT_MARK, 0 },
{ &_key_pageup, PAGEUP_MARK, 0 },
{ &_key_pagedown, PAGEDOWN_MARK, 0 },
{ &_key_home, HOME_MARK, 0 },
{ &_key_help, HELP_MARK, 0 },
{ &_key_find, FIND_MARK, 0 },
{ &_key_backspace, TERMCH_backspace, 0 },
{ &_key_delete, DELETE_MARK, 0 },
{ &_key_end, END_MARK, 0 },
{ NULL, 0, 0 }
};
int read_p,found_key;
#ifdef TERMIOS
struct tf_state T;
bzero((void *)&T,sizeof T);
#endif
DPRINT(Debug,4,(&Debug,
"cur_ReadCH2: flags=%d %s%s%s%s%s%s%s%s %s\n",
flags,
redraw ? " redraw" : "",
cursorkeys ? " cursorkeys" : "",
nocursor ? " nocurosr" : "",
term_char ? " term_char" : "",
resize_flag ? " resize_flag" : "",
sig_char ? " sig_char" : "",
quote_char ? " quote_char" : "",
poll_it ? " poll" : "",
break_flag ? "break_flag" : ""));
#if POLL_METHOD
change_action(terminal_fd,0,background_read,no_action_routine,
no_action_routine,
NULL);
#endif
cur_GetXYLocation(&line,&col); /* Background actions may print error messages .. */
DPRINT(Debug,4,(&Debug,"cur_ReadCH2: line=%d col=%d\n",
line,col));
set_last_state(system_charset);
if (state_ready(last_state)) {
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2: starting reading new character (set %s)\n",
last_state->charset->MIME_name ?
last_state->charset->MIME_name :
"<no MIME name>"));
reset_state(last_state,0);
}
last_state->caller_flags = 0;
#ifdef TERMIOS
if (sig_char || quote_char) {
if (toggle_lflag(&T,0,ISIG)) {
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2: Disabled generation of signals\n"));
}
} else {
toggle_lflag(&T,0,0); /* Just read values */
}
#endif
reinit_ReadChar:
read_p = 0;
found_key = 0;
if (redraw && !cur_RawState()) { /* Check that we have in 'raw' mode */
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2: Going to Raw mode\n"));
cur_Raw(ON);
cur_ClearScreen();
reset_state(last_state,1);
last_state->caller_flags = redraw;
#ifdef TERMIOS
reset_lfag(&T);
#endif
return last_state;
}
cur_MoveCursor(line,col,NULL);
FlushBuffer();
check_changes();
if (resize_flag && default_context->changed) {
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2: Pending resize...\n"));
reset_state(last_state,1);
last_state->caller_flags = RESIZE_MARK;
#ifdef TERMIOS
reset_lfag(&T);
#endif
return last_state;
}
if (redraw && default_context->redraw) {
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2: Pending redraw...\n"));
cur_ClearScreen();
reset_state(last_state,1);
last_state->caller_flags = redraw;
#ifdef TERMIOS
reset_lfag(&T);
#endif
return last_state;
}
if ((_intransmit != ON || default_context->redraw) &&
cursorkeys && cursor_control && _transmit_on) {
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2: Enabling cursor keys...\n"));
transmit_functions(ON);
}
if ((_intransmit != OFF || default_context->redraw) &&
nocursor && _transmit_off) {
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2: Disabling cursor keys...\n"));
transmit_functions(OFF);
}
if (cursorkeys) {
int i;
DPRINT(Debug,8,(&Debug,
"cur_ReadCh2: Available function keys:"));
for (i = 0; keytable[i].str != 0; i++) {
char * CONST str = *(keytable[i].str);
if(str && str[0] != '\0') {
keytable[i].maybe = 1; /* Initially every function key is possible */
SIGDPRINT(Debug,8,(&Debug,
" [%d] %d",i,keytable[i].result));
}
}
DPRINT(Debug,4,(&Debug,
"\n"));
}
while (found_key == 0) {
unsigned char ch;
#if POLL_METHOD
if (read_buffer_len < 1 && last_read_error == 0) {
int wait_result;
errno= 0;
if (poll_it)
wait_result = wait_for_action_or_timeout(background_read,1);
else
wait_result = wait_for_action(background_read);
if(!wait_result) {
if (errno != 0) {
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2: wait_for_action got error %d (%s)\n",
errno, error_description(errno)));
check_changes();
if (resize_flag && default_context->changed) {
DPRINT(Debug,4,(&Debug,
" ... return resize to caller\n"));
found_key = RESIZE_MARK;
continue;
}
/* Return error: */
if ((redraw && default_context->redraw)
|| break_flag /* GetPrompt wants to see errors! */
) {
DPRINT(Debug,4,(&Debug,
" ... return error to caller\n"));
found_key = -1;
continue;
}
if (errno == EINTR
#ifdef EAGAIN
|| (errno == EAGAIN)
#endif
#ifdef EWOULDBLOCK
|| (errno == EWOULDBLOCK)
#endif
) {
if (poll_it) {
DPRINT(Debug,4,(&Debug,
" ... retry ... TIMEOUT_MARK\n"));
found_key = TIMEOUT_MARK;
} else {
DPRINT(Debug,4,(&Debug,
" ... retry (ignored)\n"));
}
} else
found_key = -1;
continue;
}
if (poll_it) {
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2: interrupted ... TIMEOUT_MARK\n"));
found_key = TIMEOUT_MARK;
} else {
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2: interrupted (ignored)\n"));
}
continue;
}
}
if (read_buffer_len > 0) {
ch = read_buffer[0];
if (--read_buffer_len > 0)
memmove(read_buffer,read_buffer+1,read_buffer_len);
} else if (last_read_error != 0) {
if (-1 == last_read_error) {
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2: background_read got zero bytes...\n"));
found_key = -1;
continue;
}
check_changes();
if (resize_flag && default_context->changed) {
DPRINT(Debug,4,(&Debug,
" ... return resize to caller\n"));
found_key = RESIZE_MARK;
continue;
}
/* Return error: */
if ((redraw && default_context->redraw)
|| break_flag /* GetPrompt wants to see errors! */
) {
found_key = -1;
continue;
}
if (last_read_error != 0) {
errno = last_read_error;
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2: background_read got error %d (%s)\n",
last_read_error,
error_description(last_read_error)));
found_key = -1;
}
continue; /* continue or quit */
} else {
if (poll_it) {
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2: ... TIMEOUT_MARK\n"));
found_key = TIMEOUT_MARK;
} else {
DPRINT(Debug,1,(&Debug,
"cur_ReadCh2: SOFTWARE ERROR !!\n"));
}
break;
}
#else
int result;
if (poll_it) {
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2: POLL is unsupported, returning TIMEOUT_MARK\n"));
found_key = TIMEOUT_MARK;
} else {
result = read(terminal_fd, &ch, 1);
if (result < 0) {
int err = errno;
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2: errno = %d [%s]\n",err,
error_description(err)));
check_changes();
if (resize_flag && mc->changed) {
DPRINT(Debug,4,(&Debug,
" ... return resize to caller\n"));
found_key = RESIZE_MARK;
continue;
}
/* Return error: */
if (redraw && default_contect->redraw
|| break_flag /* GetPrompt wants to see errors! */
) {
found_key = -1;
continue;
}
if((errno == EINTR)
#ifdef EAGAIN
|| (errno == EAGAIN)
#endif
#ifdef EWOULDBLOCK
|| (errno == EWOULDBLOCK)
#endif
) {
continue; /* Retry */
}
break;
}
if (0 == result) {
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2: Got zero bytes...\n"));
found_key = -1;
continue;
}
}
#endif
#ifdef USE_DLOPEN
{
union xxx_rand {
int ch;
char bytes[sizeof (int)];
} A;
A.ch = ch;
seed_rand_bits(A.bytes, sizeof A,
3 /* Assume 3 bits per character ... */);
}
#endif
DPRINT(Debug,50,(&Debug,
"cur_ReadCh2: Looking char %d (read_p=%d)\n",ch,read_p));
if (quote_char) {
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2: Quoting current character %0xd\n",ch));
found_key = ch;
continue;
}
if (term_char) {
if (backspace == ch)
found_key = TERMCH_backspace;
if (kill_line == ch)
found_key = TERMCH_kill_line;
if (word_erase == ch)
found_key = TERMCH_word_erase;
if (reprint_char == ch)
found_key = TERMCH_reprint_char;
if (eof_char == ch)
found_key = TERMCH_eof_char;
if (found_key) {
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2: found termchar = %d (char=%02X)\n",
found_key,ch));
/* Soft reset state */
reset_state(last_state,0);
continue;
}
}
if (sig_char) {
if (interrupt_char == ch)
found_key = TERMCH_interrupt_char;
if (VQUIT_char == ch)
found_key = TERMCH_interrupt_char;
if (VSUSP_char == ch) {
SIGHAND_TYPE (*sig1) P_((int)), (*sig2) P_((int));
DPRINT(Debug,1,(&Debug,
"cur_ReadCh2: Found suspend character!\n"));
#ifdef SIGTSTP
menu_context_redraw();
switch_title(0);
CarriageReturn();
Raw(OFF);
sig1 = signal(SIGTSTP, SIG_DFL);
sig2 = signal(SIGCONT, SIG_DFL);
WriteRaw(Stopped_Text);
kill(getpid(), SIGSTOP);
CarriageReturn();
WriteRaw(Back_Text);
Raw(ON);
signal(SIGTSTP, sig1);
signal(SIGCONT, sig2);
reset_state(last_state,1);
if (redraw) {
DPRINT(Debug,1,(&Debug,
"cur_ReadCh2: returning redraw because of suspend\n"));
cur_ClearScreen();
found_key = redraw;
} else
goto reinit_ReadChar;
#endif
}
if (found_key) {
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2: found sigchar = %d (char=%02X)\n",
found_key,ch));
/* Soft reset state */
reset_state(last_state,0);
continue;
}
}
if (cursorkeys) {
int match = 0;
int i;
for (i = 0; keytable[i].str != NULL; i++) {
if (keytable[i].maybe) {
unsigned char * CONST str =
(unsigned char *) *(keytable[i].str);
if (str[read_p] == ch) {
match++;
if (str[read_p+1] == '\0') {
int temp = keytable[i].result;
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2: Found function key = %d (keytable = %d, read_p =%d)\n",
temp,i,read_p));
/* Soft reset state */
reset_state(last_state,0);
if (temp >= TERMCH_min_char && !term_char) {
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2: Found key is terminal char and term_char is not set. Ignoring key.\n"));
goto reinit_ReadChar;
} else
found_key = temp;
}
} else {
keytable[i].maybe = 0;
}
}
}
if (read_p < sizeof input_buffer -1) {
input_buffer[read_p] = ch;
input_buffer[read_p+1] = '\0';
}
if (match == 0) { /* Not in keytable */
if (read_p == 0)
found_key = ch; /* Normal key */
else {
int i;
/* But maybe escape sequence is valid state change ? */
for (i = 0;
i <= read_p && i < sizeof input_buffer -1;
i++) {
if (state_ready(last_state)) {
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2: Unsupporting -- function keys and state change sequences overlap? (OR BAD SEQUENCE) \n"));
goto BAD_sequence;
}
if (!add_streambyte_to_state(last_state,
input_buffer[i]))
goto BAD_sequence;
}
if (state_ready(last_state))
goto got_key;
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2: need more bytes for character...\n"));
goto reinit_ReadChar;
BAD_sequence:
/* Soft reset state */
reset_state(last_state,0);
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2: Bad escape sequence; ch = %d, read_p = %d\n",
ch,read_p));
#ifdef DEBUG
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2: Sequence was:"));
for (i = 0; i <= read_p && i < sizeof input_buffer -1; i++) {
if (isascii(input_buffer[i]) && isprint(input_buffer[i])) {
DPRINT(Debug,4,(&Debug," %c (0x%02X)",
input_buffer[i],input_buffer[i]));
} else {
DPRINT(Debug,4,(&Debug," 0x%02X", input_buffer[i]));
}
}
if (read_p > sizeof input_buffer -1)
DPRINT(Debug,4,(&Debug," ..."));
DPRINT(Debug,4,(&Debug,"\n"));
#endif
/* Ring a bell */
cur_Writechar('\007',NULL);
goto reinit_ReadChar;
}
} else
read_p++;
} else
found_key = ch;
}
if (found_key <= 0 && redraw && default_context->redraw) {
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2: Redraw...\n"));
if(!cur_RawState()) { /* Check that we have in 'raw' mode */
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2: Going to Raw mode\n"));
cur_Raw(ON);
}
cur_ClearScreen();
reset_state(last_state,1);
last_state->caller_flags = redraw;
#ifdef TERMIOS
reset_lfag(&T);
#endif
return last_state;
}
cur_MoveCursor(line,col,NULL);
DPRINT(Debug,50,(&Debug,
"cur_ReadCh2: found_key=%d (line=%d col=%d)\n",
found_key, line,col));
if (found_key < 0) {
reset_state(last_state,1);
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2=NULL (state)\n"));
#ifdef TERMIOS
reset_lfag(&T);
#endif
return NULL;
} else {
if (found_key >= 256) {
reset_state(last_state,1);
last_state->caller_flags = found_key;
} else {
last_state->caller_flags = 0;
if (!add_streambyte_to_state(last_state,found_key)) {
/* Ring a bell */
Writechar('\007');
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2: bad sequence...\n"));
reset_state(last_state,1);
}
}
}
if (!state_ready(last_state) &&
!last_state->caller_flags) {
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2: need more bytes for character...\n"));
goto reinit_ReadChar;
}
got_key:
DPRINT(Debug,4,(&Debug,
"cur_ReadCh2=%p (state): caller_flags=%d, ready=%d\n",
last_state,last_state->caller_flags,
state_ready(last_state)));
#ifdef TERMIOS
reset_lfag(&T);
#endif
return last_state;
}
/*
* Local Variables:
* mode:c
* c-basic-offset:4
* buffer-file-coding-system: iso-8859-1
* End:
*/
syntax highlighted by Code2HTML, v. 0.9.1