/*
 *  This file is part of ixemul.library for the Amiga.
 *  Copyright (C) 1991, 1992  Markus M. Wild
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This 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
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#define KERNEL
#include "ixemul.h"
#include <sgtty.h>
#define OB0	B0	
#define OB50	B50	
#define OB75	B75	
#define OB110	B110	
#define OB134	B134	
#define OB150	B150	
#define OB200	B200	
#define OB300	B300	
#define OB600	B600	
#define OB1200	B1200	
#define	OB1800	B1800	
#define OB2400	B2400	
#define OB4800	B4800	
#define OB9600	B9600	
#define OEXTA	EXTA	
#define OEXTB	EXTB	
#undef B0	
#undef B50	
#undef B75	
#undef B110	
#undef B134	
#undef B150	
#undef B200	
#undef B300	
#undef B600	
#undef B1200	
#undef B1800	
#undef B2400	
#undef B4800	
#undef B9600	
#undef EXTA	
#undef EXTB	
#include <sys/termios.h>
#include <ctype.h>

#if __GNUC__ != 2
#define alloca __builtin_alloca
#endif

/* IOCTLs on "interactive" files */

int
__tioctl(struct file *f, unsigned int cmd, unsigned int inout, 
	 unsigned int arglen, unsigned int arg)
{
  int omask, result, err;

  omask = syscall (SYS_sigsetmask, ~0);
  result = -1;

  if (/*!IsInteractive(CTOBPTR(f->f_fh))*/ ! f->f_fh->fh_Port)
    {
      err = ENOTTY;
      goto ret;
    }

  switch (cmd)
    {
    case TIOCGETA:
      {
        struct termios *t = (struct termios *)arg;
        unsigned char *cp;
        t->c_iflag = IGNBRK|IGNPAR|ICRNL|IXON;
        t->c_oflag = (f->f_flags & FTTYRAW) ? 0 : OPOST|ONLCR;
        t->c_cflag = CS8|CLOCAL;
        t->c_ispeed=
        t->c_ospeed= B38400;
	/* Conman does ECHOCTL, Commo doesn't.. I use Conman:-)) */
        t->c_lflag = ECHOCTL|((f->f_flags & FTTYRAW)?0:ICANON|ECHO);
	/* t->c_line  = 0; */
	cp = t->c_cc;
	cp[VSTART] = 'q' & 31;
	cp[VSTOP] = 's' & 31;
	cp[VSUSP] = 0; /* sneef.. would that be nice... */
	cp[VDSUSP] = 0;
	cp[VREPRINT] = 0;
	cp[VDISCARD] = 'x' & 31;
	cp[VWERASE] = 0;
	cp[VLNEXT] = 0;
	cp[VSTATUS] = 0;
	cp[VINTR] = 3;
	cp[VQUIT] = 0;
	cp[VERASE] = 8;
	cp[VKILL] = 'x' & 31;
	cp[VEOF] = '\\' & 31;
	cp[VEOL] = 10;
	cp[VEOL2] = 0;
	result = 0;
	goto ret;
      }

    case TIOCSETA:
    case TIOCSETAW:
    case TIOCSETAF:
      {
        struct termios *t = (struct termios *)arg;
	/* the only thing that counts so far.. if ICANON is disabled,        
	 * we disable ECHO too, no matter what the user wanted, and 
	 * send a RAW-packet.. */
	if ((t->c_lflag & ICANON) && (f->f_flags & FTTYRAW))
          {
	    f->f_flags &= ~FTTYRAW;
	    __wait_packet(&f->f_sp);
	    SendPacket1(f,__rwport,ACTION_SCREEN_MODE,0);
	    __wait_packet(&f->f_sp);
	  }
	else if (!(t->c_lflag & ICANON) && !(f->f_flags & FTTYRAW))
	  {
	    f->f_flags |= FTTYRAW;
	    __wait_packet(&f->f_sp);
	    SendPacket1(f,__rwport,ACTION_SCREEN_MODE,-1);
	    __wait_packet(&f->f_sp);
	  }
	result = 0;
	goto ret;
      }

    case TIOCGETP:
      {
	struct sgttyb *s = (struct sgttyb *)arg;
	s->sg_erase = 8;
	s->sg_kill = 'x' & 31;
	s->sg_flags = ODDP|EVENP|ANYP|
		      ((f->f_flags&FTTYRAW) ? (CBREAK|RAW) : (ECHO|CRMOD));
	s->sg_ispeed =
	s->sg_ospeed = OEXTB;
	result = 0;
	goto ret;
      }

    case TIOCSETN:
    case TIOCSETP:
      {
	struct sgttyb *s = (struct sgttyb *)arg;
	if ((s->sg_flags & (RAW|CBREAK)) && !(f->f_flags & FTTYRAW))
          {
            f->f_flags |= FTTYRAW;
	    __wait_packet(&f->f_sp);
	    SendPacket1(f,__rwport,ACTION_SCREEN_MODE,-1);
	    __wait_packet(&f->f_sp);
	  }
        else if (!(s->sg_flags & (RAW|CBREAK)) && (f->f_flags & FTTYRAW))
	  {
	    f->f_flags &= ~FTTYRAW;
	    SendPacket1(f,__rwport,ACTION_SCREEN_MODE,0);
	    __wait_packet(&f->f_sp);
	  }
	result = 0;
	goto ret;
      }

    case TIOCGWINSZ:
      {
	struct winsize *ws = (struct winsize *) arg;
	struct InfoData *info;
	struct Window *w;
	unsigned char *buf;

	buf = alloca (80);
	
	info = alloca (sizeof (struct InfoData) + 2);
	info = LONG_ALIGN (info);
	bzero (info, sizeof (struct InfoData));
	
	SendPacket1 (f, __rwport, ACTION_DISK_INFO, CTOBPTR (info));
	__wait_packet (&f->f_sp);
	if (LastResult (f) != -1) goto ret;

	w = (struct Window *) info->id_VolumeNode;
	if (! w) goto ret;

    	ws->ws_xpixel = w->Width - w->BorderLeft - w->BorderRight;
    	ws->ws_ypixel = w->Height - w->BorderTop - w->BorderBottom;

	/* query window size report */
	if (! (f->f_flags & FTTYRAW))
	  {
	    __wait_packet (&f->f_sp);
	    SendPacket1(f, __rwport, ACTION_SCREEN_MODE, 1);
	  }
	__swrite (f, "\2330 q", 4);
	__read (f, buf, 80);
	if (! (f->f_flags & FTTYRAW))
	  {
	    __wait_packet (&f->f_sp);
	    SendPacket1(f, __rwport, ACTION_SCREEN_MODE, 0);
	  }
	if (*buf == 155) buf++;
	else if (*buf == 27) buf += 2;
	/* and scan window status report */
	/* skip "1;1;" */
	buf += 4;
	ws->ws_row = 0;
	while (isdigit (*buf)) ws->ws_row = ws->ws_row*10 + *buf++ - '0';
	while (! isdigit (*buf)) buf++;
	ws->ws_col = 0;
	while (isdigit (*buf)) ws->ws_col = ws->ws_col*10 + *buf++ - '0';

	result = 0;
	goto ret;
      }

    case TIOCOUTQ:
      {
	int *count = (int *)arg;
	*count = 0;
	result = 0;
	goto ret;
      }

    case TIOCSWINSZ:
      /* should I really try to resize the window ?? */

    default:
      /* this is no error, but nevertheless we don't take any actions.. */      
      result = 0;
      goto ret;
    }

ret:
    LastResult(f) = 0;
    syscall (SYS_sigsetmask, omask);
    errno = err;
    return result;
}
