/*
 *		Cross Development System for Atari ST 
 *     Copyright (c) 1988, Memorial University of Newfoundland
 *
 * See ChangeLog for further history  ++jrb
 *
 * 1.5		ERS	modified for benefit of 16 bit compilers
 * 1.4		ERS
 * 	Changed BIOS calls into GEMDOS calls.
 *
 * 1.3		jrd
 *
 * Revision 1.2  88/02/03  22:56:30  m68k
 * Added unix like tty driver stuff
 * 
 * Revision 1.1  88/01/29  17:31:39  m68k
 * Initial revision
 * 
 */
#include	<osbind.h>
#include	<ioctl.h>
#include	<signal.h>
#include	<tchars.h>
#include	<unistd.h>
#include	<device.h>
#include	<limits.h>
#include 	<errno.h>
#include	"lib.h"
#ifndef _COMPILER_H
#include <compiler.h>
#endif

#define	iswhite(c)	((c) == ' ' || (c) == '\t')
#define	isvisable(c)	((unsigned char)(c) >= ' ')
#define	echochar(fd, c)	if (__ttymode & ECHO) (void) _echochar(fd, c); else
#define	delchar(fd, n)	if (__ttymode & ECHO) _delchar(fd, n); else

extern int	errno;
extern int	__col_pos;	/* defined in write.c */
static int	start_col;
static char	*thebuf;

static int _echochar __PROTO((int fd, int c));
static void _delchar __PROTO((int fd, int n));
static int str_length __PROTO((char *p, int n));

extern unsigned int console_read_byte(int);
extern void console_write_byte(int, int);
extern int console_input_status(int);

#ifdef __MSHORT__
int
read(fd, buf, nbytes)
	int	fd;
	void	*buf;
	unsigned int	nbytes;
{
      return (int)(_read(fd, buf, (unsigned long) nbytes));
}

#else

asm(".stabs \"_read\",5,0,0,__read"); /* dept of clean tricks */

#endif

long
_read(fd, buf, n)
        int     fd;
        void    *buf;
        unsigned long    n;
  {
	long		rval, nbytes;
	long	        cnt = 0;
	char		*p = buf;
	struct _device	*dev;

	if(n > ((unsigned long)LONG_MAX))
	{
	    errno = EBADARG;
	    return -1L;
	}

	nbytes = (long)n;
	if ((dev = _dev_fd(fd)) && dev->read)
		return (*dev->read)(fd, buf, nbytes);

	if (!isatty(fd))
		{ /* really cant do much about the signdness of nbytes here */
		if ((rval = Fread(fd, nbytes, buf)) < 0) 
			{
			errno = -rval;
			rval = -1;
			}
		return rval;
		}
	thebuf = buf;
	start_col = __col_pos;
	while (1) {
		*p = console_read_byte(fd);
		if (__ttymode & RAW) 
			{
			if (__ttymode & ECHO) 
				{
				console_write_byte(fd, *p);
				if (*p == '\r')
					__col_pos = 0;
				else if (isvisable(*p))
					__col_pos++;
				}
			if (++cnt >= nbytes || !console_input_status(fd))
				return cnt;
			p++;
			continue;
			}
		if ((__ttymode & CRMOD) && *p == '\r')
			*p = '\n';
		if (*p == __tchars[TC_INTRC]) 
			{
			/* Do the bsd thing here, i.e. flush buffers
			 * and continue to read after the interupt
			 */
			echochar(fd, *p);
			p = buf;
			cnt = 0;
			raise(SIGINT);
			} 
		    else 
		if (*p == __tchars[TC_QUITC]) 
			{
			echochar(fd, *p);
			p = buf;
			cnt = 0;
			raise(SIGQUIT);
			}
		if (__ttymode & CBREAK) 
			{
			if (*p == __tchars[TC_LNEXTC])
				*p = console_read_byte(fd);
			if (__ttymode & ECHO) 
				{
				if (*p == '\n' && (__ttymode & CRMOD)) 
					{
					console_write_byte(fd, '\n');
					console_write_byte(fd, '\r');
					__col_pos = 0;
					} 
				    else
					(void) _echochar(fd, *p);
				}
			++cnt;

			if (!console_input_status(fd))
				return cnt;
			p++;
			} 
		    else
		if (*p == __tchars[TC_LNEXTC]) 
			{
			if (__ttymode & ECHO) 
				{
				console_write_byte(fd, '^');
				console_write_byte(fd, '\b');
				}
			*p = console_read_byte(fd);
			echochar(fd, *p++);
			cnt++;
			}
		    else
		if (*p == __tchars[TC_EOFC]) 
			{
			if (__ttymode & ECHO)
				{
				int i = _echochar(fd, *p);
				__col_pos -= i;
				while (i-- > 0)
					console_write_byte(fd, '\b');
				}
			return cnt;
			}
		    else
		if (*p == '\n' || *p == __tchars[TC_BRKC]) 
			{
			if (__ttymode & ECHO)
				if (*p == '\n')
					{
					console_write_byte(fd, '\n');
					if (__ttymode & CRMOD) 
						{
						console_write_byte(fd, '\r');
						__col_pos = 0;
						}
					}
				    else
					(void) _echochar(fd, *p);
			return ++cnt;
			}
		    else
		if ((*p == __tchars[TC_ERASE]) || (*p == __tchars[TC_ERASE]))
			{
			if (cnt) 
				{
				--p; --cnt;
				delchar(fd, (int)cnt);
				}
			}
		    else
		if (*p == __tchars[TC_KILL]) 
			{
			while (--cnt >= 0) 
				{
				delchar(fd, (int)cnt);
				p--;
				}
			cnt = 0;
			} 
		    else
		if (*p == __tchars[TC_WERASC]) 
			{
			p--;
			while (cnt && iswhite(*p)) 
				{
				--p; --cnt;
				delchar(fd, (int)cnt);
				}
			while (cnt && !iswhite(*p)) 
				{
				--p; --cnt;
				delchar(fd, (int)cnt);
				}
			p++;
			}
		    else
		if (*p == __tchars[TC_RPRNTC]) 
			{
			char	*s;

			echochar(fd, __tchars[TC_RPRNTC]);
			console_write_byte(fd, '\r');
			console_write_byte(fd, '\n');
			__col_pos = 0;
			start_col = 0;
			if (__ttymode & ECHO)
				for (s = buf ; s < p ; s++)
					echochar(fd, *s);
			}
		    else
			{
			echochar(fd, *p); p++;
			cnt++;
			}
		if (cnt >= nbytes)
			return cnt;
	}
	/*NOTREACHED*/
}

static	int
_echochar(fd, c)
	int	fd;
	int	c;
{
	int	len = 0;

	c &= 0xff;
	if (c < ' ') {
		if (c == '\t') {
			int	i;

			len = ((__col_pos | 7) + 1) - __col_pos;
			if (__ttymode & XTABS)
				for (i = len ; i-- ;)
					console_write_byte(fd, ' ');
			else
				console_write_byte(fd, '\t');
		} else {
			console_write_byte(fd, '^');
			console_write_byte(fd, c + 0x40);
			len += 2;
		}
	} else if (c == 0x7f) {
		console_write_byte(fd, '^');
		console_write_byte(fd, '?');
		len += 2;
	} else {
		console_write_byte(fd, c);
		len++;
	}
	__col_pos += len;
	return len;
}

static	void
_delchar(fd, n)
	int	fd, n;
{
	int	len = 0;
	char	c = thebuf[n];

	if ((c >= 0 && c < ' ') || c == 0x7f) {
		if (c == '\t')
			len = __col_pos - str_length(thebuf, n);
		else
			len += 2;
	} else
		len++;
	__col_pos -= len;
	while (len--) {
		console_write_byte(fd, '\b');
		console_write_byte(fd, ' ');
		console_write_byte(fd, '\b');
	}
}

static	int
str_length(p, n)
	char	*p;
	int	n;
{
	int	pos = start_col;
	char	c;

	while (n--) {
		c = *p++;
		if ((c >= 0 && c < ' ') || c == 0x7f)
			if (c == '\t')
				pos = (pos | 7) + 1;
			else
				pos += 2;
		else
			pos++;
	}
	return pos;
}
