/* Top half of the terminal driver, responsible for interacting with users.

   Copyright (C) 1994 Free Software Foundation

   This program 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.

   This program 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.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */


#include "term.h"
#include <hurd/trivfs.h>
#include <fcntl.h>
#include <string.h>

#define TTYDEFCHARS
#include <sys/ttydefaults.h>

#include "io_reply_U.h"
#include "term_S.h"
#include "tioctl_S.h"
#include "ourmsg_U.h"

/* Pending write requests */
struct write_req 
{
  struct write_req *next;

  pid_t pid, pgrp;

  /* Info for io_write_reply */
  mach_port_t reply;
  mach_msg_type_name_t replytype;
  int amount;
  struct trivfs_protid *user;

  /* Pointer into buffer of next character to write */
  char *dp;

  /* One past last character in DATA */
  char *stop;

  /* Actual buffer */
  char data[0];
};
struct write_req *pending_writes_head, *pending_writes_tail;

/* Pending read requests */
struct read_req
{
  mach_port_t reply;
  mach_msg_type_name_t replytype;
  struct trivfs_protid *user;
  pid_t pid, pgrp;
  int wanted;
  struct read_req *next;
};
struct read_req *pending_reads_head, *pending_reads_tail;

/* Pending select operation */
struct select_req
{
  mach_port_t reply;
  mach_msg_type_name_t replytype;
  int type;
  int idtag;
  struct select_req *next;
};
struct select_req *pending_selects;

/* io_async requests */
struct async_req
{
  mach_port_t notify;
  struct async_req *next;
};
struct async_req *async_requests;

/* Attach this on the hook of any protid that is a ctty. */
struct protid_hook
{
  int refcnt;
  pid_t pid, pgrp;
};

#define HONORED_STATE_MODES (O_APPEND|O_ASYNC|O_FSYNC|O_NONBLOCK)

static int ustate;

/* Lowerhalf has asked for output to be stopped */
#define OUTPUT_STOPPED 0x00000001

/* Carrier not present. */
#define NOT_CONNECTED  0x00000002

/* Exclusive use */
#define EXCLUSIVE_USE  0x00000004

/* Someone is asking for SIGIO to be sent to foreground_id. */
#define ICKY_ASYNC     0x00000008

/* foreground_id is invalid */
#define NO_OWNER       0x00000010

/* User has asked for output to be stopped (with VSTOP). */
#define OUTPUT_SUSP    0x00000020

/* Output should be discarded with RPC's returning success. */
#define OUTPUT_DROP    0x00000040

/* On drain, set the termstate to be this */
struct termios ondrainstate;
int ondrainstatevalid;

int nperopens;

/* Characters that need special processing in read */
char watchchars[10] = "";

/* ustate bits that prevent output  */
#define NO_OUTPUT (OUTPUT_STOPPED|NOT_CONNECTED|OUTPUT_SUSP)


int trivfs_fstype = FSTYPE_TERM;
int trivfs_fsid = 0;		/* pid?? */
int trivfs_support_read = 1;
int trivfs_support_write = 1;
int trivfs_support_exec = 0;

int trivfs_allow_open = O_READ|O_WRITE;

int trivfs_protid_porttypes[] = {PT_TTY, PT_PTY};
int trivfs_cntl_porttypes[] = {PT_TTYCNTL, PT_PTYCNTL};
int trivfs_protid_nporttypes = 2;
int trivfs_cntl_nporttypes = 2;

mach_port_t async_icky_id;
mach_port_t async_id;
struct port_info *cttyid;
int foreground_id;

struct winsize window_size;

struct trivfs_control *blocked_open_control;

static void check_selects (void);
static void call_asyncs (void);

/* Called when last user goes away. */
void
last_close ()
{
  clear_queue (inputq);
  (*lowerhalf->drain_output) (HUPCL);
}

void
pi_create_hook (struct trivfs_protid *cred)
{
  if (cred->hook)
    ((struct protid_hook *)cred->hook)->refcnt++;
}
void (*trivfs_protid_create_hook) (struct trivfs_protid *) = pi_create_hook;

void
pi_destroy_hook (struct trivfs_protid *cred)
{
  if (cred->hook && !--((struct protid_hook *)cred->hook)->refcnt)
    free (cred->hook);
}
void (*trivfs_protid_destroy_hook) (struct trivfs_protid *) = pi_destroy_hook;

void
po_create_hook (struct trivfs_peropen *po)
{
  nperopens++;
  if (po->openmodes & O_ASYNC)
    ustate |= ICKY_ASYNC;
}
void (*trivfs_peropen_create_hook) (struct trivfs_peropen *) = po_create_hook;

void 
po_destroy_hook (struct trivfs_peropen *po)
{
  nperopens--;
  if (!nperopens)
    last_close ();
}
void (*trivfs_peropen_destroy_hook) (struct trivfs_peropen *) 
     = po_destroy_hook;
     
#if 0
error_t
check_open_block (struct trivfs_control *cntl, uid_t *uids, u_int nuids,
		  uid_t *gids, u_int ngids, int flags)
{
  if (!(flags & O_NONBLOCK) && blocked_open_control != cntl)
    {
      if (blocked_open_control)
	ports_done_with_port (blocked_open_control);
      blocked_open_control = cntl;
      ports_port_ref (blocked_open_control);
    }
  /* Check CLOCAL XXX */
  return (ustate & NOT_CONNECTED) ? EWOULDBLOCK : 0;
}
#else

/* Called from trivfs_S_fsys_getroot. */
error_t
check_open_block (struct trivfs_control *cntl, uid_t *uids, u_int nuids, 
		  uid_t *gids, u_int ngids, int flags)
{
  /* XXX This doesn't block like it ought to because trivfs
     doesn't actually support it right yet. */
  if (ustate & NOT_CONNECTED)
    (*lowerhalf->dtron)();

  if ((ustate & EXCLUSIVE_USE) && nperopens)
    return EBUSY;

  return 0;
}
#endif
error_t (*trivfs_check_open_hook) (struct trivfs_control *, uid_t *, u_int,
				uid_t *, u_int, int) = check_open_block;

/* Tell if CRED can do foreground terminal operations */
static inline int
fg_p (struct trivfs_protid *cred)
{
  struct protid_hook *hook = cred->hook;
  
  if (!hook || (ustate & NO_OWNER))
    return 1;
  
  if (hook->pid == foreground_id
      || hook->pgrp == -foreground_id)
    return 1;
  
  return 0;
}

/* Set WATCHCHARS to be a string of all the characters that
   need special processing in input. */
void
set_watchchars ()
{
  char *cp = watchchars;
  
  /* We can't have more characters than the limit (10, currently)
     in the declaration of watchchars above. */
  if (termstate.c_lflag & ICANON)
    {
      /* Break characters cause input to be immediately received
	 in ICANON mode.  Also, don't return data past the first
	 break character seen.  Also, don't actually return VEOF,
	 just discard it. */
      if (termstate.c_cc[VEOF] != _POSIX_VDISABLE)
	*cp++ = termstate.c_cc[VEOF];
      if (termstate.c_cc[VEOL] != _POSIX_VDISABLE)
	*cp++ = termstate.c_cc[VEOL];
      if (termstate.c_cc[VEOL2] != _POSIX_VDISABLE)
	*cp++ = termstate.c_cc[VEOL2];
      *cp++ = '\n';
    }
  
  if ((termstate.c_lflag & ISIG) 
      && (termstate.c_cc[VDSUSP] != _POSIX_VDISABLE))
    *cp++ = termstate.c_cc[VDSUSP];
  
  *cp = '\0';
}
  
/* Find the first occurrence in MEM (of length N) of ary character in
   ACCEPT; return zero of there is no such character. */
char *
mempbrk (const char *mem, int n, const char *accept)
{
  while (n--)
    {
      if (!strchr (accept, *mem))
	mem++;
      else
	return (char *)mem;
    }
  return 0;
}
  

/* Set up the termstate variable according to our defaults.
   The lower half has already been started, and may have
   set some bits in termstate, so don't smash them. */
void
init_user_state ()
{
  cttyid = ports_allocate_port (sizeof (struct port_info), PT_CTTYID);
  ports_port_ref (cttyid);

  ustate = NOT_CONNECTED | NO_OWNER;

  /* This is different from BSD: we don't turn on ISTRIP,
     and we use CS8 rather than CS7|PARENB.  */
  termstate.c_iflag |= BRKINT | ICRNL | IMAXBEL | IXON | IXANY;
  termstate.c_oflag |= OPOST | ONLCR | OXTABS;
  termstate.c_lflag |= ECHO | ICANON | ISIG | IEXTEN | ECHOE|ECHOKE|ECHOCTL;
  termstate.c_cflag |= CREAD | CS8 | HUPCL;
  
  /* Leave ispeed and ospeed as zero; they will be set by
     lowerhalf->set_bits at open time. */
  
  bcopy (ttydefchars, termstate.c_cc, NCCS);

  /* These bits must be valid. */
  if (!(termstate.c_cflag & CIGNORE))
    (*lowerhalf->set_bits)();
  
  set_watchchars ();
}

void
trivfs_modify_stat (struct stat *st)
{
  st->st_blksize = HIGH_WATER_MARK / 2;
  st->st_fstype = FSTYPE_TERM;
  st->st_fsid = 0;		/* ??? */
  st->st_ino = 0;		/* ??? */
  st->st_mode &= ~S_IFMT;
  st->st_mode |= S_IFCHR;
}

/* Implement term_getctty as described in <hurd/term.defs>. */
kern_return_t
S_term_getctty (mach_port_t arg,
		mach_port_t *id,
		mach_msg_type_name_t *idtype)
{
  struct trivfs_protid *cred = ports_check_port_type (arg, PT_TTY);
  error_t err;
  
  if (!cred)
    return EOPNOTSUPP;

  if (!(cred->po->openmodes & (O_READ|O_WRITE)))
    err = EBADF;
  else
    {
      *id = ports_get_right (cttyid);
      *idtype = MACH_MSG_TYPE_MAKE_SEND;
      err = 0;
    }
  ports_done_with_port (cred);
  return err;
}

/* Implement termctty_open_terminal as described in <hurd/term.defs>. */
kern_return_t
S_termctty_open_terminal (mach_port_t arg,
			  int flags,
			  mach_port_t *result,
			  mach_msg_type_name_t *resulttype)
{
  struct trivfs_protid *newcred;
  struct trivfs_peropen *newpo;
  struct port_info *pi;
  pi = ports_check_port_type (arg, PT_CTTYID);
  if (!pi)
    return EOPNOTSUPP;

  assert (pi == cttyid);

  {
    /* Copied from .../hurd/libtrivfs/fsys-getroot.c */
    newpo = malloc (sizeof (struct trivfs_peropen));
    newpo->refcnt = 1;
    newpo->cntl = termctl;
    newpo->openmodes = (flags & O_HURD
			& ~(O_CREAT|O_EXCL|O_NOLINK|O_NOTRANS));
    newpo->hook = 0;
    ports_port_ref (termctl);
    if (trivfs_peropen_create_hook)
      (*trivfs_peropen_create_hook) (newpo);
  }
  
  newcred = ports_allocate_port (sizeof (struct trivfs_protid), PT_TTY);
  io_restrict_auth (termctl->underlying, &newcred->realnode, 0, 0, 0, 0);
  newcred->isroot = 0;
  newcred->po = newpo;
  newcred->uids = malloc (0);
  newcred->gids = malloc (0);
  newcred->nuids = newcred->ngids = 0;
  newcred->hook = 0;
  if (trivfs_protid_create_hook)
    (*trivfs_protid_create_hook) (newcred);
  
  *result = ports_get_right (newcred);
  *resulttype = MACH_MSG_TYPE_MAKE_SEND;

  ports_done_with_port (pi);
  return 0;
}
  

/* Implement term_become_ctty as described in <hurd/term.defs>. */
kern_return_t
S_term_open_ctty (mach_port_t arg,
		    pid_t pid,
		    pid_t pgrp,
		    mach_port_t *newpt,
		    mach_msg_type_name_t *newpttype)
{
  struct trivfs_protid *cred = ports_check_port_type (arg, PT_TTY);
  struct trivfs_protid *newcred;
  struct protid_hook *hook;
  error_t err;
  
  if (!cred)
    return EOPNOTSUPP;

  if (!cred->po->openmodes & (O_READ|O_WRITE))
    err = EBADF;
  else
    {
      hook = malloc (sizeof (struct protid_hook));
      hook->pid = pid;
      hook->pgrp = pgrp;
      hook->refcnt = 1;

      /* Copied from .../hurd/libtrivfs/io-duplicate.c. */
      newcred = ports_allocate_port (sizeof (struct trivfs_protid), 
				     cred->pi.type);
      newcred->realnode = cred->realnode;
      newcred->isroot = cred->isroot;
      newcred->po = cred->po;
      newcred->po->refcnt++;
      newcred->uids = malloc (cred->nuids * sizeof (uid_t));
      newcred->gids = malloc (cred->ngids * sizeof (gid_t));
      bcopy (cred->uids, newcred->uids, cred->nuids * sizeof (uid_t));
      bcopy (cred->gids, newcred->gids, cred->ngids * sizeof (uid_t));
      newcred->nuids = cred->nuids;
      newcred->ngids = cred->ngids;
      mach_port_mod_refs (mach_task_self (), newcred->realnode, 
			  MACH_PORT_RIGHT_SEND, 1);
      
      newcred->hook = hook;

      *newpt = ports_get_right (newcred);
      *newpttype = MACH_MSG_TYPE_MAKE_SEND;
      err = 0;
    }
  ports_done_with_port (cred);
  return err;
}

/* Called for user writes to the terminal as described
   in <hurd/io.defs>. */
error_t
trivfs_S_io_write (struct trivfs_protid *cred,
		   mach_port_t reply,
		   mach_msg_type_name_t replytype,
		   char *data,
		   u_int datalen,
		   off_t offset,
		   int *amt)
{
  int i;
  int wroteany;

  if (!cred)
    return EOPNOTSUPP;
  if (cred->po->openmodes & O_WRITE == 0)
    return EBADF;

  if ((termstate.c_lflag & TOSTOP)
      && !fg_p (cred))
    return EBACKGROUND;
  
  if (ustate & NOT_CONNECTED)
    return EIO;

  if (ustate & OUTPUT_DROP)
    return 0;

  if (ustate & NO_OUTPUT)
    {
      struct write_req *wr;
      wr = malloc (sizeof (struct write_req) + datalen);
      
      if (cred->hook)
	{
	  wr->pid = ((struct protid_hook *)cred->hook)->pid;
	  wr->pgrp = ((struct protid_hook *)cred->hook)->pgrp;
	}
      else
	{
	  wr->pid = 0;
	  wr->pgrp = 0;
	}
      
      wr->reply = reply;
      wr->replytype = replytype;
      wr->user = cred;
      ports_port_ref (cred);
      wr->amount = datalen;

      bcopy (data, wr->data, datalen);
      wr->dp = wr->data;
      wr->stop = wr->data + datalen;
      
      wr->next = 0;
      if (pending_writes_tail)
	pending_writes_tail->next = wr;
      else
	pending_writes_head = wr;
      pending_writes_tail = wr;

      return MIG_NO_REPLY;
    }
  
  assert (!pending_writes_head);

  wroteany = 0;
  (*lowerhalf->start_batch)();
  for (i = 0; i < datalen; i++)
    if (ustate & NO_OUTPUT)
      {
	struct write_req *wr;

	if (wroteany)
	  nonecho_output ();
	(*lowerhalf->stop_batch)();

	trivfs_set_mtime (termctl);

	wr = malloc (sizeof (struct write_req) + datalen - i);

	if (cred->hook)
	  {
	    wr->pid = ((struct protid_hook *)cred->hook)->pid;
	    wr->pgrp = ((struct protid_hook *)cred->hook)->pgrp;
	  }
	else
	  {
	    wr->pid = 0;
	    wr->pgrp = 0;
	  }

	wr->reply = reply;
	wr->replytype = replytype;
	wr->user = cred;
	ports_port_ref (cred);
	wr->amount = datalen;

	bcopy (&data[i], wr->data, datalen - i);
	wr->dp = wr->data;
	wr->stop = wr->data + datalen - i;

	wr->next = 0;
	pending_writes_head = wr;
	pending_writes_tail = wr;

	*amt = datalen;
	return MIG_NO_REPLY;
      }
    else
      {
	output_character (data[i]);
	wroteany = 1;
      }

  if (wroteany)
    nonecho_output ();
  (*lowerhalf->stop_batch)();
  *amt = datalen;

  trivfs_set_mtime (termctl);
  return 0;
}

void
emit_pending_writes ()
{
  struct write_req *wr;
  int wroteany = 0;
  
  assert (!(ustate & OUTPUT_DROP) || !pending_writes_head);

  wr = pending_writes_head; 
  while (wr && !(ustate & NO_OUTPUT))
    {
      struct write_req *tmp;
  
      (*lowerhalf->start_batch)();
      while (wr->dp != wr->stop)
	if (ustate & NO_OUTPUT)
	  {
	    if (wroteany)
	      nonecho_output ();
	    (*lowerhalf->stop_batch)();
	    if (wroteany)
	      trivfs_set_mtime (termctl);
	    return;
	  }
	else
	  {
	    output_character (*wr->dp++);
	    wroteany = 1;
	  }
      if (wroteany)
	nonecho_output ();
      (*lowerhalf->stop_batch)();
      
      pending_writes_head = wr->next;
      if (wr == pending_writes_tail)
	pending_writes_tail = 0;
      io_write_reply (wr->reply, wr->replytype, 0, wr->amount);
      ports_done_with_port (wr->user);
      
      tmp = wr->next;
      free (wr);
      wr = tmp;
    }

  if (wroteany)
    trivfs_set_mtime (termctl);

  check_selects ();
  call_asyncs ();
}

/* Called by middle half to discard output */
void
discard_output ()
{
  struct write_req *wr, *tmp;

  ustate |= OUTPUT_DROP;
  
  /* Return all the pending write RPC's.  */
  wr = pending_writes_head;
  while (wr)
    {
      io_write_reply (wr->reply, wr->replytype, 0, wr->amount);
      ports_done_with_port (wr->user);
      tmp = wr->next;
      free (wr);
      wr = tmp;
    }
  pending_writes_head = pending_writes_tail = 0;
}

/* Called by middle half to stop output discard */
void
stop_discarding_output ()
{
  ustate &= ~OUTPUT_DROP;
}
     
/* Called by middle half when user has asked for output to be suspended. */
void
user_suspend_writing ()
{
  ustate |= OUTPUT_SUSP;
  (*lowerhalf->stop_sending)();
}

/* Called by middle half when user has asked for output to resume. */
void
user_resume_writing ()
{
  ustate &= ~OUTPUT_SUSP;
  (*lowerhalf->start_sending)();
  emit_pending_writes ();
}

/* Called by lower half when output should be stopped for a moment. */
void
stop_writing ()
{
  ustate |= OUTPUT_STOPPED;
}

/* Called by lower half when output should resume. */
void
start_writing ()
{
  ustate &= ~OUTPUT_STOPPED;
  
  emit_pending_writes ();
}

void
drain_finished (int code)
{
  switch (code)
    {
    default:
      return;
      
    case HUPCL:
      /* Drain following hangup */
      if (termstate.c_cflag & HUPCL)
	(*lowerhalf->dtrflicker)();
      return;

    case TIOCSETAW:
      /* Drain following TIOCSETAW or TIOCSETAF */
      if (ondrainstatevalid)
	{
	  int oldlflag = termstate.c_lflag;
	  termstate = ondrainstate;
	  if (!(termstate.c_cflag & CIGNORE))
	    (*lowerhalf->set_bits)();
	  if (oldlflag & ICANON)
	    {
	      if (!(termstate.c_lflag & ICANON))
		copy_rawq ();
	    }
	  else
	    {
	      if (termstate.c_lflag & ICANON)
		rescan_inputq ();
	    }
	  set_watchchars ();
	}
      ondrainstatevalid = 0;
    }
}

/* Called for user reads from the terminal. */
error_t
trivfs_S_io_read (struct trivfs_protid *cred,
		  mach_port_t reply,
		  mach_msg_type_name_t replytype,
		  char **data,
		  u_int *datalen,
		  off_t offset,
		  int amount)
{
  struct read_req *rr;

  if (!cred)
    return EOPNOTSUPP;
  if ((cred->po->openmodes & O_READ) == 0)
    return EBADF;
  if (!fg_p (cred))
    return EBACKGROUND;
  
  if (qsize (inputq))
    {
      char *brk;
      int brkeof = 0;
      
      if (amount > qsize (inputq))
	amount = qsize (inputq);

      if (watchchars[0])
	{
	  /* Look for special characters that need processing.  */
	  brk = mempbrk (inputq->qs, amount, watchchars);
	  if (brk)
	    {
	      /* If this isn't EOF, increment BRK.  */
	      if (!(termstate.c_lflag & ICANON) 
		  || !CCEQ (termstate.c_cc[VEOF], (cc_t) *brk))
		brk++;
	      else
		brkeof = 1;

	      /* In ICANON mode, only return up to the break */
	      if ((termstate.c_lflag & ICANON) && amount > brk - inputq->qs)
		amount = brk - inputq->qs;

	      /* XXX process VDSUSP here */
	    }
	}

      if (amount > *datalen)
	vm_allocate (mach_task_self (), (vm_address_t *) data, amount, 1);
      bcopy (inputq->qs, *data, amount);
      *datalen = amount;
      inputq->qs += amount;

      /* If the break was an EOF, then consume it here */
      if (brkeof)
	inputq->qs++;

      trivfs_set_atime (termctl);
      return 0;
    }
  
  if (ustate & NOT_CONNECTED)
    {
      /* Return EOF, Posix.1 7.1.1.10. */
      *datalen = 0;
      trivfs_set_atime (termctl);
      return 0;
    }

  rr = malloc (sizeof (struct read_req));
  
  if (cred->hook)
    {
      rr->pid = ((struct protid_hook *)cred->hook)->pid;
      rr->pgrp = ((struct protid_hook *)cred->hook)->pgrp;
    }
  else
    {
      rr->pid = 0;
      rr->pgrp = 0;
    }

  rr->reply = reply;
  rr->replytype = replytype;
  rr->user = cred;
  ports_port_ref (cred);
  rr->wanted = amount;
  
  rr->next = 0;
  if (pending_reads_tail)
    pending_reads_tail->next = rr;
  else
    pending_reads_head = rr;
  pending_reads_tail = rr;
  
  return MIG_NO_REPLY;
}

/* Called when data has been added to inputq. */
void
wakeup_readers ()
{
  struct read_req *rr, *tmp;
  
  rr = pending_reads_head;
  while (rr && qsize (inputq))
    {
      if (qsize (inputq))
	{
	  char *brk, brkeof = 0;

	  if (rr->wanted > qsize (inputq))
	    rr->wanted = qsize (inputq);
	  
	  /* Look for special characters that need processing */
	  if (watchchars[0])
	    {
	      brk = mempbrk (inputq->qs, rr->wanted, watchchars);
	      if (brk)
		{
		  /* If this isn't EOF, increment BRK */
		  if (!(termstate.c_lflag & ICANON) 
		      || !CCEQ (termstate.c_cc[VEOF], (cc_t) *brk))
		    brk++;
		  else
		    brkeof = 1;
		  
		  /* In ICANON mode, only return up to the break */
		  if ((termstate.c_lflag & ICANON) 
		      && rr->wanted > brk - inputq->qs)
		    rr->wanted = brk - inputq->qs;

		  /* XXX process VDSUSP here */
		}
	    }

	  trivfs_set_atime (termctl);
	  io_read_reply (rr->reply, rr->replytype, 0, inputq->qs,
			 rr->wanted);
	  inputq->qs += rr->wanted;

	  /* If the break was an eof, then consume it here */
	  if (brkeof)
	    inputq->qs++;
	}

      ports_done_with_port (rr->user);

      pending_reads_head = rr->next;
      if (rr == pending_reads_tail)
	pending_reads_tail = 0;
      tmp = rr->next;
      free (rr);
      rr = tmp;
    }

  check_selects ();
  call_asyncs ();
}

/* Immediately return pending read operation RR, freeing
   the struct completely. */
static void
abort_read (struct read_req *rr)
{
  io_read_reply (rr->reply, rr->replytype, EINTR, 0, 0);
  ports_done_with_port (rr->user);
  free (rr);
}

/* Immediately return pending write operation WR, freeing
   the struct completely. */
static void
abort_write (struct write_req *wr)
{
  int lefttowrite;
	  
  lefttowrite = wr->stop - wr->dp;

  if (lefttowrite == wr->amount)
    io_write_reply (wr->reply, wr->replytype, 
		    0, wr->amount - lefttowrite);
  else
    io_write_reply (wr->reply, wr->replytype, EINTR, 0);
  
  ports_done_with_port (wr->user);
  free (wr);
}
  

/* Immediately return all pending operations whose pid might be PID or
   whose pgrp might be -PID.  If PID is zero, then abort all pending RPCs. */
void
abort_rpcs (pid_t pid)
{
  struct read_req *rr, *prevr, *nxtr;
  struct write_req *wr, *prevw, *nxtw;
  
  if (!pid)
    return;

  rr = pending_reads_head;
  prevr = 0;
  while (rr)
    if (rr->pid == pid || rr->pgrp == -pid || !pid || (!rr->pid && !rr->pgrp))
      {
	nxtr = prevr ? prevr->next : pending_reads_head = rr->next;
	if (rr == pending_reads_tail)
	  pending_reads_tail = prevr;
	abort_read (rr);
	rr = nxtr;
      }
    else
      {
	prevr = rr;
	rr = rr->next;
      }
      
  wr = pending_writes_head;
  prevw = 0;
  while (wr)
    if (wr->pid == pid || wr->pgrp == -pid || !pid || (!wr->pid && !wr->pgrp))
      {
	nxtw = prevw ? prevw->next : pending_writes_head = wr->next;
	if (wr == pending_writes_tail)
	  pending_writes_tail = prevw;
	abort_write (wr);
	wr = nxtw;
      }
    else
      {
	prevw = wr;
	wr = wr->next;
      }
}

error_t
trivfs_S_io_readable (struct trivfs_protid *cred,
		      mach_port_t reply,
		      mach_msg_type_name_t replytype,
		      int *amt)
{
  if (!cred)
    return EOPNOTSUPP;
  if ((cred->po->openmodes & O_READ) == 0)
    return EBADF;
  *amt = qsize (inputq);
  return 0;
}

/* TIOCMODG ioctl -- Get modem state */
kern_return_t
S_tioctl_tiocmodg (io_t port,
		   int *state)
{
  struct trivfs_protid *cred = ports_check_port_type (port, PT_TTY);
  if (!cred)
    return EOPNOTSUPP;

  *state = (*lowerhalf->mdmstate)();
  ports_done_with_port (cred);
  return 0;
}

/* TIOCMODS ioctl -- Set modem state */
kern_return_t
S_tioctl_tiocmods (io_t port,
		   int state)
{
  struct trivfs_protid *cred = ports_check_port_type (port, PT_TTY);
  error_t err;
  if (!cred)
    return EOPNOTSUPP;

  if (!(cred->po->openmodes & (O_READ|O_WRITE)))
    err = EBADF;
  else
    {
      (*lowerhalf->mdmctl)(MDMCTL_SET, state);
      err = 0;
    }
  
  ports_done_with_port (cred);
  return err;
}

/* TIOCEXCL ioctl -- Set exclusive use */
kern_return_t
S_tioctl_tiocexcl (io_t port)
{
  struct trivfs_protid *cred = ports_check_port_type (port, PT_TTY);
  error_t err;
  if (!cred)
    return EOPNOTSUPP;

  if (!(cred->po->openmodes & (O_READ|O_WRITE)))
    err = EBADF;
  else
    {
      ustate |= EXCLUSIVE_USE;
      err = 0;
    }
  ports_done_with_port (cred);
  return err;
}

/* TIOCNXCL ioctl -- Clear exclusive use */
kern_return_t
S_tioctl_tiocnxcl (io_t port)
{
  struct trivfs_protid *cred = ports_check_port_type (port, PT_TTY);
  error_t err;
  
  if (!cred)
    return EOPNOTSUPP;
  if (!(cred->po->openmodes & (O_READ|O_WRITE)))
    err = EBADF;
  else
    {
      ustate &= ~EXCLUSIVE_USE;
      err = 0;
    }

  ports_done_with_port (cred);
  return err;
}

/* TIOCFLUSH ioctl -- Flush input, output, or both */
kern_return_t
S_tioctl_tiocflush (io_t port,
		    int flags)
{
  struct trivfs_protid *cred = ports_check_port_type (port, PT_TTY);
  error_t err;
  if (!cred)
    return EOPNOTSUPP;

  if (!(cred->po->openmodes & (O_READ|O_WRITE)))
    err =  EBADF;
  else
    {
      if (flags == 0)
	flags = O_READ|O_WRITE;

      /* Should flush input here if O_READ is set. */

      if (flags & O_WRITE)
	(*lowerhalf->drop_output)();
      err = 0;
    }

  ports_done_with_port (cred);
  return err;
}
  
/* TIOCGETA ioctl -- Get termios state */
kern_return_t
S_tioctl_tiocgeta (io_t port,
		   tcflag_t *modes,
		   cc_t *ccs,
		   speed_t *speeds)
{
  struct trivfs_protid *cred = ports_check_port_type (port, PT_TTY);
  if (!cred)
    return EOPNOTSUPP;

  modes[0] = termstate.c_iflag;
  modes[1] = termstate.c_oflag;
  modes[2] = termstate.c_cflag;
  modes[3] = termstate.c_lflag;
  bcopy (termstate.c_cc, ccs, NCCS);
  speeds[0] = termstate.__ispeed;
  speeds[1] = termstate.__ospeed;

  ports_done_with_port (cred);
  return 0;
}

/* TIOCSETA -- Set termios state */
kern_return_t
S_tioctl_tiocseta (io_t port,
		   tcflag_t *modes,
		   cc_t *ccs,
		   speed_t *speeds)
{
  struct trivfs_protid *cred = ports_check_port_type (port, PT_TTY);
  error_t err;
  int oldlflag;

  if (!cred)
    return EOPNOTSUPP;

  if (!(cred->po->openmodes & (O_READ|O_WRITE)))
    err =  EBADF;
  else if (!fg_p (cred))
    err = EBACKGROUND;
  else  
    {
      oldlflag = termstate.c_lflag;
      termstate.c_iflag = modes[0];
      termstate.c_oflag = modes[1];
      termstate.c_cflag = modes[2];
      termstate.c_lflag = modes[3];
      bcopy (ccs, termstate.c_cc, NCCS);
      termstate.__ispeed = speeds[0];
      termstate.__ospeed = speeds[1];
      if (!(termstate.c_cflag & CIGNORE))
	(*lowerhalf->set_bits)();
      if (oldlflag & ICANON)
	{
	  if (!(termstate.c_lflag & ICANON))
	    copy_rawq ();
	}
      else
	{
	  if (termstate.c_lflag & ICANON)
	    rescan_inputq ();
	}
      set_watchchars ();
      err = 0;
    }      

  ports_done_with_port (cred);
  return err;
}

/* Drain output, then set term state.  */
kern_return_t
S_tioctl_tiocsetaw (io_t port,
		    tcflag_t *modes,
		    cc_t *ccs,
		    speed_t *speeds)
{
  struct trivfs_protid *cred = ports_check_port_type (port, PT_TTY);
  error_t err;

  if (!cred)
    return EOPNOTSUPP;

  if (!(cred->po->openmodes & (O_READ|O_WRITE)))
    err =  EBADF;
  else if (!fg_p (cred))
    err = EBACKGROUND;
  else  
    {
      ondrainstate.c_iflag = modes[0];
      ondrainstate.c_oflag = modes[1];
      ondrainstate.c_cflag = modes[2];
      ondrainstate.c_lflag = modes[3];
      bcopy (ccs, ondrainstate.c_cc, NCCS);
      ondrainstate.__ispeed = speeds[0];
      ondrainstate.__ospeed = speeds[1];
      ondrainstatevalid = 1;
      (*lowerhalf->drain_output)(TIOCSETAW);
      err = 0;
    }
  
  ports_done_with_port (cred);
  return err;
}
  
/* Flush input, drain output, then set term state.  */
kern_return_t
S_tioctl_tiocsetaf (io_t port,
		    tcflag_t *modes,
		    cc_t *ccs,
		    speed_t *speeds)

{
  struct trivfs_protid *cred = ports_check_port_type (port, PT_TTY);
  error_t err;

  if (!cred)
    return EOPNOTSUPP;

  if (!(cred->po->openmodes & (O_READ|O_WRITE)))
    err =  EBADF;
  else if (!fg_p (cred))
    err = EBACKGROUND;
  else  
    {
      clear_queue (inputq);
      ondrainstate.c_iflag = modes[0];
      ondrainstate.c_oflag = modes[1];
      ondrainstate.c_cflag = modes[2];
      ondrainstate.c_lflag = modes[3];
      bcopy (ccs, ondrainstate.c_cc, NCCS);
      ondrainstate.__ispeed = speeds[0];
      ondrainstate.__ospeed = speeds[1];
      ondrainstatevalid = 1;
      (*lowerhalf->drain_output)(TIOCSETAW);
      err = 0;
    }
  
  ports_done_with_port (cred);
  return err;
}

/* TIOCGETD -- Return line discipline */
kern_return_t
S_tioctl_tiocgetd (io_t port,
		   int *disc)
{
  struct trivfs_protid *cred = ports_check_port_type (port, PT_TTY);
  if (!cred)
    return EOPNOTSUPP;

  *disc = TTYDISC;

  ports_done_with_port (cred);
  return 0;
}

/* TIOCSETD -- Set line discipline */
kern_return_t
S_tioctl_tiocsetd (io_t port,
		   int disc)
{
  struct trivfs_protid *cred = ports_check_port_type (port, PT_TTY);
  error_t err;
  if (!cred)
    return EOPNOTSUPP;

  if (!(cred->po->openmodes & (O_READ|O_WRITE)))
    err = EBADF;
  if (disc != TTYDISC)
    err = ENXIO;
  else
    err = 0;

  ports_done_with_port (cred);
  return 0;
}

/* TIOCDRAIN -- Wait for output to drain */
kern_return_t
S_tioctl_tiocdrain (io_t port)
{
  /* XXX */
  return EOPNOTSUPP;
}

/* TIOCSIG -- PTY master generate signal */
kern_return_t
S_tioctl_tiocsig (io_t port)
{
  return EOPNOTSUPP;
}

/* TIOCEXT -- PTY master enter external mode */
kern_return_t
S_tioctl_tiocext (io_t port)
{
  return EOPNOTSUPP;
}

/* TIOCUCNTL -- PTY set/clear usr cntl mode */
kern_return_t
S_tioctl_tiocucntl (io_t port,
		    int how)
{
  return EOPNOTSUPP;
}

/* TIOCSWINSZ -- Set window size */
kern_return_t
S_tioctl_tiocswinsz (io_t port,
		     struct winsize size)
{
  struct trivfs_protid *cred = ports_check_port_type (port, PT_TTY);
  error_t err;
  if (!cred)
    return EOPNOTSUPP;

  if (!(cred->po->openmodes & (O_READ|O_WRITE)))
    err = EBADF;
  else
    err = 0;

  ports_done_with_port (cred);

  if (!err && (size.ws_row - window_size.ws_row +
	       size.ws_col - window_size.ws_col +
	       size.ws_xpixel - window_size.ws_xpixel +
	       size.ws_ypixel - window_size.ws_ypixel) != 0)
    {
      /* The size is actually changing.  Record the new size and notify the
	 process group.  */
      window_size = size;
      send_signal (SIGWINCH);
    }

  return err;
}

/* TIOCGWINSZ -- Fetch window size */  
kern_return_t
S_tioctl_tiocgwinsz (io_t port,
		     struct winsize *size)
{
  struct trivfs_protid *cred = ports_check_port_type (port, PT_TTY);
  if (!cred)
    return EOPNOTSUPP;

  *size = window_size;

  ports_done_with_port (cred);
  return 0;
}

/* TIOCREMOTE -- PTY enter remote mode */
kern_return_t
S_tioctl_tiocremote (io_t port,
		     int how)
{
  return EOPNOTSUPP;
}

/* TIOCMGET -- Fetch all modem bits */
kern_return_t
S_tioctl_tiocmget (io_t port,
		   int *bits)
{
  struct trivfs_protid *cred = ports_check_port_type (port, PT_TTY);
  if (!cred)
    return EOPNOTSUPP;

  *bits = (*lowerhalf->mdmstate)();
  ports_done_with_port (cred);
  return 0;
}
  
/* TIOCMSET -- Set all modem bits */
kern_return_t
S_tioctl_tiocmset (io_t port,
		   int bits)
{
  struct trivfs_protid *cred = ports_check_port_type (port, PT_TTY);
  error_t err;
  if (!cred)
    return EOPNOTSUPP;

  if (!(cred->po->openmodes & (O_READ|O_WRITE)))
    err = EBADF;
  else
    {
      (*lowerhalf->mdmctl)(MDMCTL_SET, bits);
      err = 0;
    }
  
  ports_done_with_port (cred);
  return err;
}
  
/* TIOCMBIC -- Clear some modem bits */
kern_return_t
S_tioctl_tiocmbic (io_t port,
		   int bits)
{
  struct trivfs_protid *cred = ports_check_port_type (port, PT_TTY);
  error_t err;
  if (!cred)
    return EOPNOTSUPP;

  if (!(cred->po->openmodes & (O_READ|O_WRITE)))
    err = EBADF;
  else
    {
      (*lowerhalf->mdmctl)(MDMCTL_BIC, bits);
      err = 0;
    }
  
  ports_done_with_port (cred);
  return err;
}

/* TIOCMBIS -- Set some modem bits */
kern_return_t
S_tioctl_tiocmbis (io_t port,
		   int bits)
{
  struct trivfs_protid *cred = ports_check_port_type (port, PT_TTY);
  error_t err;
  if (!cred)
    return EOPNOTSUPP;

  if (!(cred->po->openmodes & (O_READ|O_WRITE)))
    err = EBADF;
  else
    {
      (*lowerhalf->mdmctl)(MDMCTL_BIS, bits);
      err = 0;
    }
  
  ports_done_with_port (cred);
  return err;
}

/* TIOCSTART and TIOCSTOP are in input.c. */

/* TIOCPKT -- PTY Master packet mode */
kern_return_t
S_tioctl_tiocpkt (io_t port,
		  int how)
{
  return EOPNOTSUPP;
}

/* TIOCSTI -- Simulate terminal input */
kern_return_t
S_tioctl_tiocsti (io_t port,
		  char c)
{
  struct trivfs_protid *cred = ports_check_port_type (port, PT_TTY);
  error_t err;
  if (!cred)
    return EOPNOTSUPP;

  /* BSD returns EACCES if this is not our controlling terminal,
     but we have no way to do that.  (And I don't think it actually
     provides any security there, either.) */

  if (!(cred->po->openmodes & O_READ))
    err = EPERM;
  else
    {
      input_character (c);
      err = 0;
    }
  
  ports_done_with_port (cred);
  return err;
}

/* TIOCOUTQ -- return output queue size */
kern_return_t
S_tioctl_tiocoutq (io_t port,
		   int *queue_size)
{
  struct trivfs_protid *cred = ports_check_port_type (port, PT_TTY);
  error_t err;
  if (!cred)
    return EOPNOTSUPP;

  if (!(cred->po->openmodes & (O_READ|O_WRITE)))
    err = EBADF;
  else
    {
      *queue_size = (*lowerhalf->outsize)();
      err = 0;
    }
  
  ports_done_with_port (cred);
  return err;
}
  
/* TIOCSPGRP -- set pgrp of terminal */
kern_return_t
S_tioctl_tiocspgrp (io_t port,
		    int pgrp)
{
  struct trivfs_protid *cred = ports_check_port_type (port, PT_TTY);
  error_t err;
  if (!cred)
    return EOPNOTSUPP;

  if (!(cred->po->openmodes & (O_READ|O_WRITE)))
    err = EBADF;
  else
    {
      ustate &= ~NO_OWNER;
      foreground_id = -pgrp;
      err = 0;
    }
  
  ports_done_with_port (cred);
  return err;
}
  
/* TIOCGPGRP --- fetch pgrp of terminal */
kern_return_t
S_tioctl_tiocgpgrp (io_t port,
		    int *pgrp)
{
  struct trivfs_protid *cred = ports_check_port_type (port, PT_TTY);
  if (!cred)
    return EOPNOTSUPP;

  if (ustate & NO_OWNER)
    return ENOTTY;		/* that's what BSD says... */
  else
    {
      *pgrp = - foreground_id;
      return 0;
    }
}

/* TIOCCDTR -- clear DTR */
kern_return_t
S_tioctl_tioccdtr (io_t port)
{
  struct trivfs_protid *cred = ports_check_port_type (port, PT_TTY);
  error_t err;
  if (!cred)
    return EOPNOTSUPP;

  if (!(cred->po->openmodes & (O_READ|O_WRITE)))
    err = EBADF;
  else
    {
      (*lowerhalf->mdmctl)(MDMCTL_BIC, TIOCM_DTR);
      err = 0;
    }
  
  ports_done_with_port (cred);
  return err;
}
  
/* TIOCSDTR -- set DTR */
kern_return_t
S_tioctl_tiocsdtr (io_t port)
{
  struct trivfs_protid *cred = ports_check_port_type (port, PT_TTY);
  error_t err;
  if (!cred)
    return EOPNOTSUPP;

  if (!(cred->po->openmodes & (O_READ|O_WRITE)))
    err = EBADF;
  else
    {
      (*lowerhalf->mdmctl)(MDMCTL_BIS, TIOCM_DTR);
      err = 0;
    }
  
  ports_done_with_port (cred);
  return err;
}

/* TIOCCBRK -- Clear break condition */
kern_return_t
S_tioctl_tioccbrk (io_t port)
{
  struct trivfs_protid *cred = ports_check_port_type (port, PT_TTY);
  error_t err;
  if (!cred)
    return EOPNOTSUPP;

  if (!(cred->po->openmodes & (O_READ|O_WRITE)))
    err = EBADF;
  else
    {
      (*lowerhalf->clear_break)();
      err = 0;
    }
  
  ports_done_with_port (cred);
  return err;
}

/* TIOCSBRK -- Set break condition */
kern_return_t
S_tioctl_tiocsbrk (io_t port)
{
  struct trivfs_protid *cred = ports_check_port_type (port, PT_TTY);
  error_t err;
  if (!cred)
    return EOPNOTSUPP;

  if (!(cred->po->openmodes & (O_READ|O_WRITE)))
    err = EBADF;
  else
    {
      (*lowerhalf->set_break)();
      err = 0;
    }
  
  ports_done_with_port (cred);
  return err;
}

error_t
trivfs_S_file_truncate (struct trivfs_protid *cred,
			off_t size)
{
  if (!cred)
    return EOPNOTSUPP;
  if ((cred->po->openmodes & O_WRITE) == 0)
    return EBADF;
  return 0;
}

error_t
trivfs_S_io_seek (struct trivfs_protid *cred,
		  off_t off,
		  int whence,
		  off_t *newp)
{
  return ESPIPE;
}

error_t
trivfs_S_io_get_openmodes (struct trivfs_protid *cred,
			   mach_port_t reply,
			   mach_msg_type_name_t replytype,
			   int *bits)
{
  if (!cred)
    return EOPNOTSUPP;
  *bits = cred->po->openmodes;
  return 0;
}

error_t
trivfs_S_io_set_all_openmodes (struct trivfs_protid *cred,
			       mach_port_t reply,
			       mach_msg_type_name_t replytype,
			       int bits)
{

  if (!cred)
    return EOPNOTSUPP;
  cred->po->openmodes &= ~HONORED_STATE_MODES;
  cred->po->openmodes |= (bits & HONORED_STATE_MODES);
  return 0;
}

error_t
trivfs_S_io_set_some_openmodes (struct trivfs_protid *cred,
			     mach_port_t reply,
			     mach_msg_type_name_t reply_type,
			     int bits)
{
  if (!cred)
    return EOPNOTSUPP;
  cred->po->openmodes |= (bits & HONORED_STATE_MODES);
  return 0;
}
  
error_t
trivfs_S_io_clear_some_openmodes (struct trivfs_protid *cred,
			       mach_port_t reply,
			       mach_msg_type_name_t reply_type,
			       int bits)
{
  if (!cred)
    return EOPNOTSUPP;
  cred->po->openmodes &= ~(bits & HONORED_STATE_MODES);
  return 0;
}

error_t
trivfs_S_io_mod_owner (struct trivfs_protid *cred,
		    mach_port_t reply,
		    mach_msg_type_name_t reply_type,
		    pid_t owner)
{
  if (!cred)
    return EOPNOTSUPP;
  ustate &= ~NO_OWNER;
  foreground_id = owner;
  return 0;
}

error_t
trivfs_S_io_get_owner (struct trivfs_protid *cred,
		    mach_port_t erply,
		    mach_msg_type_name_t reply_type,
		    pid_t *owner)
{
  if (!cred)
    return EOPNOTSUPP;
  if (ustate & NO_OWNER)
    return ENOTTY;
  *owner = foreground_id;
  return 0;
}

error_t
trivfs_S_io_get_async_icky (struct trivfs_protid *cred,
			    mach_port_t *id,
			    mach_msg_type_name_t *idtype)
{
  if (!cred)
    return EOPNOTSUPP;
  if (!(cred->po->openmodes & (O_READ|O_WRITE)))
    return EBADF;
  *id = async_icky_id;
  *idtype = MACH_MSG_TYPE_MAKE_SEND;
  return 0;
}

error_t
trivfs_S_io_async (struct trivfs_protid *cred,
		   mach_port_t notify,
		   mach_port_t *id,
		   mach_msg_type_name_t *idtype)
{
  struct async_req *ar;
  if (!cred)
    return EOPNOTSUPP;
  if (!(cred->po->openmodes & (O_READ|O_WRITE)))
    return EBADF;
  ar = malloc (sizeof (struct async_req));
  ar->notify = notify;
  ar->next = async_requests;
  async_requests = ar;
  *id = async_id;
  *idtype = MACH_MSG_TYPE_MAKE_SEND;
  return 0;
}

error_t
trivfs_S_io_select (struct trivfs_protid *cred,
		    mach_port_t reply,
		    mach_msg_type_name_t reply_type,
		    int type,
		    mach_port_t notify,
		    mach_msg_type_name_t notify_type,
		    int idtag,
		    int *result)
{
  if (!cred)
    return EOPNOTSUPP;

  /* We don't deal with SELECT_URG here.  */
  if (type & ~(SELECT_READ | SELECT_WRITE))
    return EINVAL;

  *result = 0;
  if (type == 0)
    return 0;

  if ((type & SELECT_READ) && qsize (inputq))
    *result |= SELECT_READ;
  if ((type & SELECT_WRITE)
      && !(ustate & NO_OUTPUT))
    *result |= SELECT_WRITE;
  
  if (*result)
    mach_port_deallocate (mach_task_self (), notify);
  else
    {
      struct select_req *sel = malloc (sizeof (struct select_req));
      sel->reply = notify;
      sel->replytype = notify_type;
      sel->type = type;
      sel->idtag = idtag;
      sel->next = pending_selects;
      pending_selects = sel;
    }
  
  return 0;
}

/* Run through the entire list of select messages;
   returning those that we can. */
static void
check_selects ()
{
  struct select_req *sel, **prevp;
  int state;
  
  state = 0;
  if (qsize (inputq))
    state |= SELECT_READ;
  if (!(ustate & NO_OUTPUT) || (ustate & OUTPUT_DROP))
    state |= SELECT_WRITE;

  if (!state)
    return;
  
  prevp = &pending_selects;
  sel = pending_selects;
  while (sel)
    if (state & sel->type)
      {
	struct select_req *tmp;
	nowait_io_select_done (sel->reply, sel->replytype,
			       state & sel->type, sel->idtag);
	tmp = sel;
	*prevp = sel->next;
	sel = sel->next;
	free (tmp);
      }
    else
      {
	prevp = &sel->next; 
	sel = sel->next;
      }
}

kern_return_t
trivfs_S_io_map (struct trivfs_protid *cred,
		 mach_port_t *rdobj,
		 mach_msg_type_name_t *rdtype,
		 mach_port_t *wrobj,
		 mach_msg_type_name_t *wrtype)
{
  return EOPNOTSUPP;
}

void
report_connect ()
{
  ustate &= ~NOT_CONNECTED;

#if 0
  if (blocked_open_control)
    {
      trivfs_complete_open (blocked_open_control, 1, 0);
      ports_done_with_port (blocked_open_control);
      blocked_open_control = 0;
    }
#endif  

  if (!(ustate & NO_OUTPUT))
    start_writing ();
  check_selects ();
  call_asyncs ();
}

void
report_open_error (error_t err)
{
#if 0
  if (blocked_open_control)
    {
      trivfs_complete_open (blocked_open_control, 1, err);
      ports_done_with_port (blocked_open_control);
      blocked_open_control = 0;
    }
#else
  abort ();
#endif
}

void
report_hangup ()
{
  ustate |= NOT_CONNECTED;
  
  if (!(termstate.c_cflag & CLOCAL))
    send_signal (SIGHUP);
}

/* Call all the scheduled async I/O handlers */
static void
call_asyncs ()
{
  struct async_req *ar, *nxt, **prevp;
  mach_port_t err;
  int didabort;

  if (!(ustate & ICKY_ASYNC) && !async_requests)
    return;
  
  /* These tests must be the same as for select above. */
  if (!qsize (inputq) && (ustate & NO_OUTPUT) && !(ustate & OUTPUT_DROP))
    return;
  
  if (async_requests)
    {
      abort_rpcs (0);
      didabort = 1;
    }
  else
    didabort = 0;

  if ((ustate & ICKY_ASYNC) && !(ustate & NO_OWNER))
    {
      if (!didabort)
	abort_rpcs (foreground_id);
      hurd_sig_post (foreground_id, SIGIO, async_icky_id);
    }
  
  for (ar = async_requests, prevp = &async_requests;
       ar;
       ar = nxt)
    {
      nxt = ar->next;
      err = nowait_sig_post (ar->notify, SIGIO, async_id);
      if (err == MACH_SEND_INVALID_DEST)
	{
	  /* Receiver died; remove the notification request.  */
	  *prevp = ar->next;
	  mach_port_deallocate (mach_task_self (), ar->notify);
	  free (ar);
	}
      else
	prevp = &ar->next;
    }
}

/* Send a signal to the current process (group) of the terminal. */
void
send_signal (int signo)
{
  mach_port_t right;

  if (!(ustate & NO_OWNER))
    {
      abort_rpcs (foreground_id);
      right = ports_get_right (cttyid);
      mach_port_insert_right (mach_task_self (), right, right,
			      MACH_MSG_TYPE_MAKE_SEND);
      hurd_sig_post (foreground_id, signo, right);
      mach_port_deallocate (mach_task_self (), right);
    }
}

error_t
trivfs_S_interrupt_operation (mach_port_t port)
{
  struct trivfs_protid *cred = ports_check_port_type (port, PT_TTY);
  struct read_req *rr, *prevr, *nxtr;
  struct write_req *wr, *prevw, *nxtw;

  if (!cred)
    return EOPNOTSUPP;
  
  rr = pending_reads_head;
  prevr = 0;
  while (rr)
    if (rr->user == cred)
      {
	nxtr = prevr ? prevr->next : pending_reads_head = rr->next;
	if (rr == pending_reads_tail)
	  pending_reads_tail = prevr;
	abort_read (rr);
	rr = nxtr;
      }
    else
      {
	prevr = rr;
	rr = rr->next;
      }
  
  wr = pending_writes_head;
  prevw = 0;
  while (wr)
    if (wr->user == cred)
      {
	nxtw = prevw ? prevw->next : pending_writes_head = wr->next;
	if (wr == pending_writes_tail)
	  pending_writes_tail = prevw;
	abort_write (wr);
	wr = nxtw;
      }
    else
      {
	prevw = wr;
	wr = wr->next;
      }
  ports_done_with_port (cred);
  return 0;
}
