/* input.c -- character input functions for readline. */ /* Copyright (C) 1994 Free Software Foundation, Inc. This file is part of the GNU Readline Library, a library for reading lines of text with interactive input and history editing. The GNU Readline Library is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. The GNU Readline Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. The GNU General Public License is often shipped with GNU software, and is generally kept in a file called COPYING or LICENSE. If you do not have a copy of the license, write to the Free Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111 USA. */ #define READLINE_LIBRARY #if defined (HAVE_CONFIG_H) # include <config.h> #endif #include <sys/types.h> #include <fcntl.h> #if defined (HAVE_SYS_FILE_H) # include <sys/file.h> #endif /* HAVE_SYS_FILE_H */ #if defined (HAVE_UNISTD_H) # include <unistd.h> #endif /* HAVE_UNISTD_H */ #if defined (HAVE_STDLIB_H) # include <stdlib.h> #else # include "ansi_stdlib.h" #endif /* HAVE_STDLIB_H */ #if defined (HAVE_SELECT) # if !defined (HAVE_SYS_SELECT_H) || !defined (M_UNIX) # include <sys/time.h> # endif #endif /* HAVE_SELECT */ #if defined (HAVE_SYS_SELECT_H) # include <sys/select.h> #endif #if defined (FIONREAD_IN_SYS_IOCTL) # include <sys/ioctl.h> #endif #include <stdio.h> #include <errno.h> #if !defined (errno) extern int errno; #endif /* !errno */ /* System-specific feature definitions and include files. */ #include "rldefs.h" /* Some standard library routines. */ #include "readline.h" #include "rlprivate.h" #include "rlshell.h" #include "xmalloc.h" /* What kind of non-blocking I/O do we have? */ #if !defined (O_NDELAY) && defined (O_NONBLOCK) # define O_NDELAY O_NONBLOCK /* Posix style */ #endif /* Non-null means it is a pointer to a function to run while waiting for character input. */ Function *rl_event_hook = (Function *)NULL; Function *rl_getc_function = rl_getc; /* **************************************************************** */ /* */ /* Character Input Buffering */ /* */ /* **************************************************************** */ static int pop_index, push_index; static unsigned char ibuffer[512]; static int ibuffer_len = sizeof (ibuffer) - 1; #define any_typein (push_index != pop_index) int _rl_any_typein () { return any_typein; } /* Return the amount of space available in the buffer for stuffing characters. */ static int ibuffer_space () { if (pop_index > push_index) return (pop_index - push_index - 1); else return (ibuffer_len - (push_index - pop_index)); } /* Get a key from the buffer of characters to be read. Return the key in KEY. Result is KEY if there was a key, or 0 if there wasn't. */ static int rl_get_char (key) int *key; { if (push_index == pop_index) return (0); *key = ibuffer[pop_index++]; if (pop_index >= ibuffer_len) pop_index = 0; return (1); } /* Stuff KEY into the *front* of the input buffer. Returns non-zero if successful, zero if there is no space left in the buffer. */ static int rl_unget_char (key) int key; { if (ibuffer_space ()) { pop_index--; if (pop_index < 0) pop_index = ibuffer_len - 1; ibuffer[pop_index] = key; return (1); } return (0); } /* If a character is available to be read, then read it and stuff it into IBUFFER. Otherwise, just return. */ static void rl_gather_tyi () { int tty; register int tem, result; int chars_avail; char input; #if defined(HAVE_SELECT) fd_set readfds, exceptfds; struct timeval timeout; #endif tty = fileno (rl_instream); #if defined (HAVE_SELECT) FD_ZERO (&readfds); FD_ZERO (&exceptfds); FD_SET (tty, &readfds); FD_SET (tty, &exceptfds); timeout.tv_sec = 0; timeout.tv_usec = 100000; /* 0.1 seconds */ if (select (tty + 1, &readfds, (fd_set *)NULL, &exceptfds, &timeout) <= 0) return; /* Nothing to read. */ #endif result = -1; #if defined (FIONREAD) result = ioctl (tty, FIONREAD, &chars_avail); #endif #if defined (O_NDELAY) if (result == -1) { tem = fcntl (tty, F_GETFL, 0); fcntl (tty, F_SETFL, (tem | O_NDELAY)); chars_avail = read (tty, &input, 1); fcntl (tty, F_SETFL, tem); if (chars_avail == -1 && errno == EAGAIN) return; } #endif /* O_NDELAY */ /* If there's nothing available, don't waste time trying to read something. */ if (chars_avail <= 0) return; tem = ibuffer_space (); if (chars_avail > tem) chars_avail = tem; /* One cannot read all of the available input. I can only read a single character at a time, or else programs which require input can be thwarted. If the buffer is larger than one character, I lose. Damn! */ if (tem < ibuffer_len) chars_avail = 0; if (result != -1) { while (chars_avail--) rl_stuff_char ((*rl_getc_function) (rl_instream)); } else { if (chars_avail) rl_stuff_char (input); } } /* Is there input available to be read on the readline input file descriptor? Only works if the system has select(2) or FIONREAD. */ int _rl_input_available () { #if defined(HAVE_SELECT) fd_set readfds, exceptfds; struct timeval timeout; #endif #if defined(FIONREAD) int chars_avail; #endif int tty; tty = fileno (rl_instream); #if defined (HAVE_SELECT) FD_ZERO (&readfds); FD_ZERO (&exceptfds); FD_SET (tty, &readfds); FD_SET (tty, &exceptfds); timeout.tv_sec = 0; timeout.tv_usec = 100000; /* 0.1 seconds */ return (select (tty + 1, &readfds, (fd_set *)NULL, &exceptfds, &timeout) > 0); #endif #if defined (FIONREAD) if (ioctl (tty, FIONREAD, &chars_avail) == 0) return (chars_avail); #endif return 0; } void _rl_insert_typein (c) int c; { int key, t, i; char *string; i = key = 0; string = xmalloc (ibuffer_len + 1); string[i++] = (char) c; while ((t = rl_get_char (&key)) && _rl_keymap[key].type == ISFUNC && _rl_keymap[key].function == rl_insert) string[i++] = key; if (t) rl_unget_char (key); string[i] = '\0'; rl_insert_text (string); free (string); } /* Add KEY to the buffer of characters to be read. Returns 1 if the character was stuffed correctly; 0 otherwise. */ int rl_stuff_char (key) int key; { if (ibuffer_space () == 0) return 0; if (key == EOF) { key = NEWLINE; rl_pending_input = EOF; } ibuffer[push_index++] = key; if (push_index >= ibuffer_len) push_index = 0; return 1; } /* Make C be the next command to be executed. */ int rl_execute_next (c) int c; { rl_pending_input = c; return 0; } /* **************************************************************** */ /* */ /* Character Input */ /* */ /* **************************************************************** */ /* Read a key, including pending input. */ int rl_read_key () { int c; rl_key_sequence_length++; if (rl_pending_input) { c = rl_pending_input; rl_pending_input = 0; } else { /* If input is coming from a macro, then use that. */ if (c = _rl_next_macro_key ()) return (c); /* If the user has an event function, then call it periodically. */ if (rl_event_hook) { while (rl_event_hook && rl_get_char (&c) == 0) { (*rl_event_hook) (); rl_gather_tyi (); } } else { if (rl_get_char (&c) == 0) c = (*rl_getc_function) (rl_instream); } } return (c); } int rl_getc (stream) FILE *stream; { int result; unsigned char c; while (1) { result = read (fileno (stream), &c, sizeof (unsigned char)); if (result == sizeof (unsigned char)) return (c); /* If zero characters are returned, then the file that we are reading from is empty! Return EOF in that case. */ if (result == 0) return (EOF); #if defined (__BEOS__) if (errno == EINTR) continue; #endif #if defined (EWOULDBLOCK) # define X_EWOULDBLOCK EWOULDBLOCK #else # define X_EWOULDBLOCK -99 #endif #if defined (EAGAIN) # define X_EAGAIN EAGAIN #else # define X_EAGAIN -99 #endif if (errno == X_EWOULDBLOCK || errno == X_EAGAIN) { if (unset_nodelay_mode (fileno (stream)) < 0) return (EOF); continue; } #undef X_EWOULDBLOCK #undef X_EAGAIN /* If the error that we received was SIGINT, then try again, this is simply an interrupted system call to read (). Otherwise, some error ocurred, also signifying EOF. */ if (errno != EINTR) return (EOF); } }