mirror of
https://github.com/adtools/clib2.git
synced 2025-12-08 14:59:05 +00:00
- Integrated Peter Bengtsson's termios code. Thank you very much! I chose
to add it to libunix.a rather than keeping it in a separate libtermios.a library. git-svn-id: file:///Users/olsen/Code/migration-svn-zu-git/logical-line-staging/clib2/trunk@14971 87f5fb63-7c3d-0410-a384-fd976d0f7a62
This commit is contained in:
654
library/termios_console_fdhookentry.c
Executable file
654
library/termios_console_fdhookentry.c
Executable file
@ -0,0 +1,654 @@
|
||||
/*
|
||||
* $Id: termios_console_fdhookentry.c,v 1.1 2005-06-04 10:46:21 obarthel Exp $
|
||||
*
|
||||
* :ts=4
|
||||
*
|
||||
* Portable ISO 'C' (1994) runtime library for the Amiga computer
|
||||
* Copyright (c) 2002-2005 by Olaf Barthel <olsen@sourcery.han.de>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* - Neither the name of Olaf Barthel nor the names of contributors
|
||||
* may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _TERMIOS_HEADERS_H
|
||||
#include "termios_headers.h"
|
||||
#endif /* _TERMIOS_HEADERS_H */
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/*
|
||||
* Hook for termios emulation on a console. This can probably be cleaned up a bit
|
||||
* by removing things which will (should) never happen on a console.
|
||||
*/
|
||||
|
||||
#ifndef _STDIO_HEADERS_H
|
||||
#include "stdio_headers.h"
|
||||
#endif /* _STDIO_HEADERS_H */
|
||||
|
||||
#ifndef _UNISTD_HEADERS_H
|
||||
#include "unistd_headers.h"
|
||||
#endif /* _UNISTD_HEADERS_H */
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
#ifndef _STDLIB_MEMORY_H
|
||||
#include "stdlib_memory.h"
|
||||
#endif /* _STDLIB_MEMORY_H */
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
#include <strings.h>
|
||||
#include <limits.h>
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
/*
|
||||
* Emulate canonical no-echo mode with a simple line-editor in raw mode.
|
||||
*/
|
||||
static int
|
||||
LineEditor(BPTR file,char *buf,const int buflen,struct termios *tios)
|
||||
{
|
||||
int pos = 0,len = 0;
|
||||
unsigned char z;
|
||||
int do_edit = 1;
|
||||
int shift_mode = 0;
|
||||
|
||||
SetMode(file,DOSTRUE); /* Set raw mode. */
|
||||
|
||||
while(do_edit && len < buflen)
|
||||
{
|
||||
if(WaitForChar(file,5000000) != DOSFALSE) /* 5 seconds. */
|
||||
{
|
||||
if(Read(file,&z,1) == ERROR)
|
||||
{
|
||||
len = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
if(z == tios->c_cc[VQUIT])
|
||||
break;
|
||||
|
||||
switch(z)
|
||||
{
|
||||
case '\n': /* NL */
|
||||
case '\r': /* CR */
|
||||
|
||||
do_edit = 0;
|
||||
|
||||
buf[len++] = '\n';
|
||||
continue;
|
||||
|
||||
case 155: /* CSI */
|
||||
|
||||
shift_mode = 1;
|
||||
continue;
|
||||
|
||||
case '\b': /* Backspace */
|
||||
|
||||
if(pos > 0)
|
||||
{
|
||||
memmove(&buf[pos-1],&buf[pos],len-pos);
|
||||
pos--;
|
||||
len--;
|
||||
}
|
||||
|
||||
continue;
|
||||
|
||||
case 127: /* Delete */
|
||||
|
||||
if(pos < len)
|
||||
{
|
||||
memmove(&buf[pos],&buf[pos+1],len-pos-1);
|
||||
len--;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if(shift_mode)
|
||||
{
|
||||
shift_mode = 0;
|
||||
|
||||
switch(z)
|
||||
{
|
||||
case 'C': /* Right arrowkey */
|
||||
|
||||
if(pos < len)
|
||||
pos++;
|
||||
|
||||
continue;
|
||||
|
||||
case 'D': /* Left arrowkey */
|
||||
|
||||
if(pos > 0)
|
||||
pos--;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if(pos != len)
|
||||
memmove(&buf[pos + 1],&buf[pos],len - pos);
|
||||
|
||||
buf[pos]=z;
|
||||
pos++;
|
||||
len++;
|
||||
}
|
||||
}
|
||||
|
||||
if(len >= 0 && len < buflen) /* Does not hurt to null-terminate if we can. */
|
||||
buf[len] = '\0';
|
||||
|
||||
SetMode(file,DOSFALSE); /* Restore mode */
|
||||
|
||||
return(len); /* Number of characters read. */
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
|
||||
int
|
||||
__termios_console_hook(
|
||||
struct fd * fd,
|
||||
struct file_action_message * fam)
|
||||
{
|
||||
const unsigned char CR = '\r',NL = '\n';
|
||||
struct FileHandle * fh;
|
||||
char * buffer = NULL;
|
||||
int result = EOF;
|
||||
int actual_out;
|
||||
BOOL is_aliased;
|
||||
BPTR file;
|
||||
struct termios *tios;
|
||||
|
||||
ENTER();
|
||||
|
||||
assert( fam != NULL && fd != NULL );
|
||||
assert( __is_valid_fd(fd) );
|
||||
assert( FLAG_IS_SET(fd->fd_Flags,FDF_TERMIOS) );
|
||||
assert( fd->fd_Aux != NULL);
|
||||
|
||||
tios = (struct termios *)fd->fd_Aux;
|
||||
|
||||
/* Careful: file_action_close has to monkey with the file descriptor
|
||||
table and therefore needs to obtain the stdio lock before
|
||||
it locks this particular descriptor entry. */
|
||||
if(fam->fam_Action == file_action_close)
|
||||
__stdio_lock();
|
||||
|
||||
__fd_lock(fd);
|
||||
|
||||
#if defined(__THREAD_SAFE)
|
||||
{
|
||||
/* Check if this file should be dynamically bound to one of the
|
||||
three standard I/O streams. */
|
||||
if(FLAG_IS_SET(fd->fd_Flags,FDF_STDIO))
|
||||
{
|
||||
switch(fd->fd_DefaultFile)
|
||||
{
|
||||
case STDIN_FILENO:
|
||||
|
||||
file = Input();
|
||||
break;
|
||||
|
||||
case STDOUT_FILENO:
|
||||
|
||||
file = Output();
|
||||
break;
|
||||
|
||||
case STDERR_FILENO:
|
||||
|
||||
#if defined(__amigaos4__)
|
||||
{
|
||||
file = ErrorOutput();
|
||||
}
|
||||
#else
|
||||
{
|
||||
struct Process * this_process = (struct Process *)FindTask(NULL);
|
||||
|
||||
file = this_process->pr_CES;
|
||||
}
|
||||
#endif /* __amigaos4__ */
|
||||
|
||||
/* The following is rather controversial; if the standard error stream
|
||||
is unavailable, we default to reuse the standard output stream. This
|
||||
is problematic if the standard output stream was redirected and should
|
||||
not be the same as the standard error output stream. */
|
||||
if(file == ZERO)
|
||||
file = Output();
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
file = ZERO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
file = fd->fd_DefaultFile;
|
||||
}
|
||||
}
|
||||
#else
|
||||
{
|
||||
file = fd->fd_DefaultFile;
|
||||
}
|
||||
#endif /* __THREAD_SAFE */
|
||||
|
||||
if(file == ZERO)
|
||||
{
|
||||
SHOWMSG("file is closed");
|
||||
|
||||
fam->fam_Error = EBADF;
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch(fam->fam_Action)
|
||||
{
|
||||
case file_action_read:
|
||||
|
||||
SHOWMSG("file_action_read");
|
||||
|
||||
if(FLAG_IS_CLEAR(tios->c_cflag,CREAD))
|
||||
{
|
||||
SHOWMSG("Reading is not enabled for this console descriptor.");
|
||||
fam->fam_Error = EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
assert( fam->fam_Data != NULL );
|
||||
assert( fam->fam_Size > 0 );
|
||||
|
||||
D(("read %ld bytes from position %ld to 0x%08lx",fam->fam_Size,Seek(file,0,OFFSET_CURRENT),fam->fam_Data));
|
||||
|
||||
PROFILE_OFF();
|
||||
|
||||
/* Attempt to fake everything needed in non-canonical mode. */
|
||||
|
||||
if(FLAG_IS_SET(tios->c_lflag,ICANON)) /* Canonical read = same as usual. Unless... */
|
||||
{
|
||||
if(FLAG_IS_CLEAR(tios->c_lflag,ECHO)) /* No-echo mode needs to be emulated. */
|
||||
result = LineEditor(file,fam->fam_Data,fam->fam_Size,tios);
|
||||
else
|
||||
result = Read(file,fam->fam_Data,fam->fam_Size);
|
||||
}
|
||||
else if (fam->fam_Size > 0)
|
||||
{
|
||||
/* Non-canonical reads have timeouts and a minimum number of characters to read. */
|
||||
int i = 0;
|
||||
|
||||
result = 0;
|
||||
|
||||
if(tios->c_cc[VMIN]>0)
|
||||
{
|
||||
i = Read(file,fam->fam_Data,1); /* Reading the first character is not affected by the timeout unless VMIN==0. */
|
||||
if(i == ERROR)
|
||||
{
|
||||
fam->fam_Error = EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
result = i;
|
||||
|
||||
while((result < tios->c_cc[VMIN]) && (result < fam->fam_Size))
|
||||
{
|
||||
if(tios->c_cc[VTIME] > 0)
|
||||
{
|
||||
if(WaitForChar(file,100000 * tios->c_cc[VTIME]) == DOSFALSE)
|
||||
break; /* No more characters available within alloted time. */
|
||||
}
|
||||
|
||||
i = Read(file,&fam->fam_Data[result],1);
|
||||
if(i <= 0)
|
||||
break; /* Break out of this while loop only. */
|
||||
|
||||
result += i;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(WaitForChar(fd->fd_DefaultFile,100000*tios->c_cc[VTIME]))
|
||||
result = Read(file,fam->fam_Data,fam->fam_Size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result = 0; /* Reading zero characters will always succeed. */
|
||||
}
|
||||
|
||||
PROFILE_ON();
|
||||
|
||||
if(result == ERROR)
|
||||
{
|
||||
D(("read failed ioerr=%ld",IoErr()));
|
||||
|
||||
fam->fam_Error = __translate_io_error_to_errno(IoErr());
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(result > 0)
|
||||
{
|
||||
if(tios->c_iflag != 0) /* Input processing enabled. */
|
||||
{
|
||||
int i,n;
|
||||
int num_bytes = result;
|
||||
unsigned char byte_in;
|
||||
|
||||
/* XXX The input substitution could possibly be moved to the console handler with an input-map. (?) */
|
||||
for(i = n = 0 ; i < num_bytes ; i++)
|
||||
{
|
||||
byte_in = fam->fam_Data[i];
|
||||
|
||||
if(FLAG_IS_SET(tios->c_iflag,ISTRIP)) /* Strip 8:th bit. Done before any other processing. */
|
||||
byte_in &= 0x7f;
|
||||
|
||||
if(FLAG_IS_SET(tios->c_iflag,IGNCR) && byte_in == CR) /* Remove CR */
|
||||
{
|
||||
result--;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(FLAG_IS_SET(tios->c_iflag,ICRNL) && byte_in == CR) /* Map CR->NL */
|
||||
byte_in = NL;
|
||||
|
||||
if(FLAG_IS_SET(tios->c_iflag,INLCR) && byte_in == NL) /* Map NL->CR */
|
||||
byte_in = CR;
|
||||
|
||||
fam->fam_Data[n++] = byte_in;
|
||||
}
|
||||
}
|
||||
|
||||
if(FLAG_IS_SET(tios->c_lflag,ECHO) && FLAG_IS_CLEAR(tios->c_lflag,ICANON) && FLAG_IS_SET(fd->fd_Flags,FDF_WRITE))
|
||||
{
|
||||
if(Write(file,fam->fam_Data,result) == ERROR)
|
||||
{
|
||||
/* "Silently" disable echoing. */
|
||||
SHOWMSG("Echo failed and has been disabled.");
|
||||
CLEAR_FLAG(tios->c_lflag,ECHO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(FLAG_IS_SET(fd->fd_Flags,FDF_CACHE_POSITION))
|
||||
fd->fd_Position += (ULONG)result;
|
||||
|
||||
break;
|
||||
|
||||
case file_action_write:
|
||||
|
||||
SHOWMSG("file_action_write");
|
||||
|
||||
assert( fam->fam_Data != NULL );
|
||||
assert( fam->fam_Size > 0 );
|
||||
|
||||
if(FLAG_IS_SET(tios->c_oflag,OPOST)) /* Output processing enabled. */
|
||||
{
|
||||
unsigned char byte_out;
|
||||
int i,n;
|
||||
|
||||
buffer = malloc(2 * fam->fam_Size);
|
||||
if(buffer == NULL)
|
||||
{
|
||||
fam->fam_Error = ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for(i = n = 0 ; i < fam->fam_Size ; i++)
|
||||
{
|
||||
byte_out=fam->fam_Data[i];
|
||||
|
||||
if(FLAG_IS_SET(tios->c_oflag,ONLRET) && byte_out == CR)
|
||||
continue;
|
||||
|
||||
if(FLAG_IS_SET(tios->c_oflag,OCRNL) && byte_out == CR)
|
||||
byte_out = NL;
|
||||
|
||||
if(FLAG_IS_SET(tios->c_oflag,ONOCR) && byte_out == CR)
|
||||
byte_out = NL;
|
||||
|
||||
if(FLAG_IS_SET(tios->c_oflag,ONLCR) && byte_out == NL)
|
||||
{
|
||||
buffer[n++] = CR;
|
||||
byte_out = NL;
|
||||
}
|
||||
|
||||
buffer[n++] = byte_out;
|
||||
}
|
||||
|
||||
actual_out = n;
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer = fam->fam_Data;
|
||||
actual_out = fam->fam_Size;
|
||||
}
|
||||
|
||||
/* Note. When output processing is enabled, write() can return _more_ than the data length. */
|
||||
D(("write %ld bytes to position %ld from 0x%08lx",actual_out,Seek(file,0,OFFSET_CURRENT),buffer));
|
||||
|
||||
if(actual_out > 0)
|
||||
{
|
||||
PROFILE_OFF();
|
||||
|
||||
result = Write(file,buffer,actual_out);
|
||||
|
||||
PROFILE_ON();
|
||||
}
|
||||
else
|
||||
{
|
||||
result = 0;
|
||||
}
|
||||
|
||||
if(buffer == fam->fam_Data)
|
||||
buffer = NULL; /* Must do this to avoid freeing the user data. */
|
||||
|
||||
if(result == ERROR)
|
||||
{
|
||||
D(("write failed ioerr=%ld",IoErr()));
|
||||
|
||||
fam->fam_Error = __translate_io_error_to_errno(IoErr());
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(FLAG_IS_SET(fd->fd_Flags,FDF_CACHE_POSITION))
|
||||
fd->fd_Position += (ULONG)result;
|
||||
|
||||
break;
|
||||
|
||||
case file_action_close:
|
||||
|
||||
SHOWMSG("file_action_close");
|
||||
|
||||
/* The following is almost guaranteed not to fail. */
|
||||
result = OK;
|
||||
|
||||
/* If this is an alias, just remove it. */
|
||||
is_aliased = __fd_is_aliased(fd);
|
||||
if(is_aliased)
|
||||
{
|
||||
__remove_fd_alias(fd);
|
||||
}
|
||||
else if (FLAG_IS_CLEAR(fd->fd_Flags,FDF_STDIO))
|
||||
{
|
||||
/* Should we reset this file into line buffered mode? */
|
||||
if(FLAG_IS_SET(fd->fd_Flags,FDF_NON_BLOCKING) && FLAG_IS_SET(fd->fd_Flags,FDF_IS_INTERACTIVE))
|
||||
SetMode(fd->fd_DefaultFile,DOSFALSE);
|
||||
|
||||
/* Are we allowed to close this file? */
|
||||
if(FLAG_IS_CLEAR(fd->fd_Flags,FDF_NO_CLOSE))
|
||||
{
|
||||
/* Call a cleanup function, such as the one which
|
||||
* releases locked records.
|
||||
*/
|
||||
if(fd->fd_Cleanup != NULL)
|
||||
(*fd->fd_Cleanup)(fd);
|
||||
|
||||
PROFILE_OFF();
|
||||
|
||||
if(CANNOT Close(fd->fd_DefaultFile))
|
||||
{
|
||||
fam->fam_Error = __translate_io_error_to_errno(IoErr());
|
||||
|
||||
result = EOF;
|
||||
}
|
||||
|
||||
PROFILE_ON();
|
||||
|
||||
fd->fd_DefaultFile = ZERO;
|
||||
}
|
||||
}
|
||||
|
||||
__fd_unlock(fd);
|
||||
|
||||
#if defined(__THREAD_SAFE)
|
||||
{
|
||||
/* Free the lock semaphore now. */
|
||||
if(NOT is_aliased)
|
||||
__delete_semaphore(fd->fd_Lock);
|
||||
}
|
||||
#endif /* __THREAD_SAFE */
|
||||
|
||||
/* And that's the last for this file descriptor. */
|
||||
memset(fd,0,sizeof(*fd));
|
||||
|
||||
break;
|
||||
|
||||
case file_action_seek:
|
||||
|
||||
SHOWMSG("file_action_seek");
|
||||
|
||||
fam->fam_Error = EINVAL;
|
||||
goto out;
|
||||
|
||||
case file_action_set_blocking:
|
||||
|
||||
SHOWMSG("file_action_set_blocking");
|
||||
|
||||
PROFILE_OFF();
|
||||
|
||||
if(FLAG_IS_SET(fd->fd_Flags,FDF_IS_INTERACTIVE))
|
||||
{
|
||||
LONG mode;
|
||||
|
||||
SHOWMSG("changing the mode");
|
||||
|
||||
if(fam->fam_Arg != 0)
|
||||
mode = DOSFALSE; /* buffered mode */
|
||||
else
|
||||
mode = DOSTRUE; /* single character mode */
|
||||
|
||||
if(CANNOT SetMode(file,mode))
|
||||
{
|
||||
fam->fam_Error = __translate_io_error_to_errno(IoErr());
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Update tios to reflect state change. */
|
||||
if(mode == DOSTRUE)
|
||||
CLEAR_FLAG(tios->c_lflag,ICANON);
|
||||
else
|
||||
SET_FLAG(tios->c_lflag,ICANON);
|
||||
|
||||
result = OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
SHOWMSG("can't do anything here");
|
||||
|
||||
fam->fam_Error = EBADF;
|
||||
}
|
||||
|
||||
PROFILE_ON();
|
||||
|
||||
break;
|
||||
|
||||
case file_action_examine:
|
||||
|
||||
SHOWMSG("file_action_examine");
|
||||
|
||||
fh = BADDR(file);
|
||||
|
||||
if(CANNOT __safe_examine_file_handle(file,fam->fam_FileInfo))
|
||||
{
|
||||
LONG error;
|
||||
|
||||
/* So that didn't work. Did the file system simply fail to
|
||||
respond to the request or is something more sinister
|
||||
at work? */
|
||||
error = IoErr();
|
||||
if(error != ERROR_ACTION_NOT_KNOWN)
|
||||
{
|
||||
SHOWMSG("couldn't examine the file");
|
||||
|
||||
fam->fam_Error = __translate_io_error_to_errno(error);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* OK, let's have another look at this file. Could it be a
|
||||
console stream? */
|
||||
if(NOT IsInteractive(file))
|
||||
{
|
||||
SHOWMSG("whatever it is, we don't know");
|
||||
|
||||
fam->fam_Error = ENOSYS;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Make up some stuff for this stream. */
|
||||
memset(fam->fam_FileInfo,0,sizeof(*fam->fam_FileInfo));
|
||||
|
||||
DateStamp(&fam->fam_FileInfo->fib_Date);
|
||||
|
||||
fam->fam_FileInfo->fib_DirEntryType = ST_CONSOLE;
|
||||
}
|
||||
|
||||
fam->fam_FileSystem = fh->fh_Type;
|
||||
|
||||
result = OK;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
|
||||
SHOWVALUE(fam->fam_Action);
|
||||
|
||||
fam->fam_Error = EBADF;
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
|
||||
__fd_unlock(fd);
|
||||
|
||||
if(fam->fam_Action == file_action_close)
|
||||
__stdio_unlock();
|
||||
|
||||
if(buffer != NULL)
|
||||
free(buffer);
|
||||
|
||||
SHOWVALUE(result);
|
||||
|
||||
RETURN(result);
|
||||
return(result);
|
||||
}
|
||||
Reference in New Issue
Block a user