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