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 * (was hurtta+elm@ozone.FMI.FI) * or Kari Hurtta ***************************************************************************** * * 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 #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 #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 : "")); 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 : "")); } } 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 : "")); 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: */