summaryrefslogtreecommitdiff
path: root/term
diff options
context:
space:
mode:
Diffstat (limited to 'term')
-rw-r--r--term/Makefile45
-rw-r--r--term/devio.c554
-rw-r--r--term/main.c147
-rw-r--r--term/munge.c725
-rw-r--r--term/term.h264
-rw-r--r--term/users.c1659
6 files changed, 3394 insertions, 0 deletions
diff --git a/term/Makefile b/term/Makefile
new file mode 100644
index 00000000..40c06ba4
--- /dev/null
+++ b/term/Makefile
@@ -0,0 +1,45 @@
+#
+# Copyright (C) 1995 Free Software Foundation, Inc.
+# Written by Michael I. Bushnell, p/BSG.
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd 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 Hurd 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
+
+dir := term
+makemode := server
+
+target = term
+SRCS = devio.c munge.c users.c main.c
+LCLHDRS = term.h
+
+OBJS = $(subst .c,.o,$(SRCS)) termServer.o device_replyServer.o tioctlServer.o ourmsgUser.o ../libtrivfs/libtrivfs.a ../libports/libports.a ../libihash/libihash.a ../libthreads/libthreads.a
+
+include ../Makeconf
+
+# The reason for -Dout= is to prevent errors for get_init_port,
+# get_init_ports, get_init_int, get_init_ints, get_dtable, and get_fd.
+# We don't use those, so we're safe in breaking them.
+ourmsg_U.h ourmsgUser.c: ourmsg.defs ../hurd/hurd_types.defs
+ $(CPP) $(CPPFLAGS) -Droutine=simpleroutine -Dout= $< \
+ | $(MIGCOM) -prefix nowait_ -server /dev/null \
+ -user ourmsgUser.c -header ourmsg_U.h
+ mv ourmsg_U.h tmp.h
+ sed -e 's/_msg_user_/_ourmsg_user/' < tmp.h > ourmsg_U.h
+ rm -f tmp.h
+
+ourmsg.defs: $(includedir)/hurd/msg.defs
+ rm -f $@
+ ln -s $< $@
diff --git a/term/devio.c b/term/devio.c
new file mode 100644
index 00000000..e24e34fb
--- /dev/null
+++ b/term/devio.c
@@ -0,0 +1,554 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd 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 Hurd 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "term.h"
+#include <device/device.h>
+#include <device/device_request.h>
+#include <hurd/ports.h>
+#include <string.h>
+#include <sys/types.h>
+#include <hurd.h>
+#include <stdio.h>
+
+#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 <device/tty_status.h>
+
+/* This flag is set if there is an outstanding device_write. */
+static int output_pending;
+
+/* This flag is set if there is an outstanding device_read. */
+static int input_pending;
+
+/* This flag is set if there is an outstanding device_open. */
+static int open_pending;
+
+static char pending_output[IO_INBAND_MAX];
+static int npending_output;
+
+static struct port_class *phys_reply_class;
+
+/* The Mach device_t representing the terminal. */
+static device_t phys_device = MACH_PORT_NULL;
+
+/* The ports we get replies on for device calls. */
+static mach_port_t phys_reply_writes = MACH_PORT_NULL;
+static mach_port_t phys_reply = MACH_PORT_NULL;
+
+/* The port-info structs. */
+static struct port_info *phys_reply_writes_pi;
+static struct port_info *phys_reply_pi;
+
+static device_t device_master;
+
+static void init_devio (void) __attribute__ ((constructor));
+static void
+init_devio ()
+{
+ mach_port_t host_priv;
+ errno = get_privileged_ports (&host_priv, &device_master);
+ if (errno)
+ {
+ perror ("Getting priviliged ports");
+ exit (1);
+ }
+ mach_port_deallocate (mach_task_self (), host_priv);
+ phys_reply_class = ports_create_class (0, 0);
+}
+
+/* XXX Convert a real speed to a bogus Mach speed. Return
+ -1 if the real speed was bogus, else 0. */
+static int
+real_speed_to_bogus_speed (int rspeed, int *bspeed)
+{
+ switch (rspeed)
+ {
+ case 0:
+ *bspeed = B0;
+ break;
+ case 50:
+ *bspeed = B50;
+ break;
+ case 75:
+ *bspeed = B75;
+ break;
+ case 110:
+ *bspeed = B110;
+ break;
+ case 134:
+ *bspeed = B134;
+ break;
+ case 150:
+ *bspeed = B150;
+ break;
+ case 200:
+ *bspeed = B200;
+ break;
+ case 300:
+ *bspeed = B300;
+ break;
+ case 600:
+ *bspeed = B600;
+ break;
+ case 1200:
+ *bspeed = B1200;
+ break;
+ case 1800:
+ *bspeed = B1800;
+ break;
+ case 2400:
+ *bspeed = B2400;
+ break;
+ case 4800:
+ *bspeed = B4800;
+ break;
+ case 9600:
+ *bspeed = B9600;
+ break;
+ case 19200:
+ *bspeed = EXTA;
+ break;
+ case 28400:
+ *bspeed = EXTB;
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+/* XXX Convert a bogus speed to a real speed. */
+static int
+bogus_speed_to_real_speed (int bspeed)
+{
+ switch (bspeed)
+ {
+ case B0:
+ default:
+ return 0;
+ case B50:
+ return 50;
+ case B75:
+ return 75;
+ case B110:
+ return 110;
+ case B134:
+ return 134;
+ case B150:
+ return 150;
+ case B200:
+ return 200;
+ case B300:
+ return 300;
+ case B600:
+ return 600;
+ case B1200:
+ return 1200;
+ case B1800:
+ return 1800;
+ case B2400:
+ return 2400;
+ case B4800:
+ return 4800;
+ case B9600:
+ return 9600;
+ case EXTA:
+ return 19200;
+ case EXTB:
+ return 38400;
+ }
+}
+
+
+/* If there are characters on the output queue and no
+ pending output requests, then send them. */
+void
+start_output ()
+{
+ char *cp;
+ int size;
+ error_t err;
+
+ size = qsize (outputq);
+
+ if (!size || output_pending)
+ return;
+
+ /* Copy characters onto PENDING_OUTPUT, not bothering
+ those already there. */
+
+ if (size + npending_output > IO_INBAND_MAX)
+ size = IO_INBAND_MAX - npending_output;
+
+ cp = pending_output + npending_output;
+ npending_output += size;
+
+ while (size--)
+ *cp++ = dequeue (outputq);
+
+ /* Submit all the outstanding characters to the device. */
+ /* The D_NOWAIT flag does not, in fact, prevent blocks. Instead,
+ it merely causes D_WOULD_BLOCK errors when carrier is down...
+ whee.... */
+ err = device_write_request_inband (phys_device, phys_reply_writes, D_NOWAIT,
+ 0, pending_output, npending_output);
+
+ if (err == MACH_SEND_INVALID_DEST)
+ desert_dtr ();
+ else if (!err)
+ output_pending = 1;
+}
+
+error_t
+device_write_reply_inband (mach_port_t replypt,
+ error_t return_code,
+ int amount)
+{
+ if (replypt != phys_reply_writes)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+
+ output_pending = 0;
+
+ if (return_code == 0)
+ {
+ if (amount >= npending_output)
+ {
+ npending_output = 0;
+ condition_broadcast (outputq->wait);
+ }
+ else
+ {
+ /* Copy the characters that didn't get output
+ to the front of the array. */
+ npending_output -= amount;
+ memmove (pending_output, pending_output + amount, npending_output);
+ }
+ start_output ();
+ }
+ else if (return_code == D_WOULD_BLOCK)
+ /* Carrier has dropped. */
+ desert_dtr ();
+ else
+ start_output ();
+
+ mutex_unlock (&global_lock);
+ return 0;
+}
+
+error_t
+device_read_reply_inband (mach_port_t replypt,
+ error_t error_code,
+ char *data,
+ u_int datalen)
+{
+ int i, flush;
+ error_t err;
+
+ if (replypt != phys_reply)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+
+ input_pending = 0;
+
+ if (!error_code && (termstate.c_cflag & CREAD))
+ for (i = 0; i < datalen; i++)
+ {
+ flush = input_character (data[i]);
+ if (flush)
+ break;
+ }
+ else if (error_code == D_WOULD_BLOCK)
+ {
+ desert_dtr ();
+ mutex_unlock (&global_lock);
+ return 0;
+ }
+
+ /* D_NOWAIT does not actually prevent blocks; it merely causes
+ D_WOULD_BLOCK errors when carrier drops. */
+ err = device_read_request_inband (phys_device, phys_reply, D_NOWAIT,
+ 0, vm_page_size);
+ if (err)
+ desert_dtr ();
+ else
+ input_pending = 1;
+
+ mutex_unlock (&global_lock);
+ return 0;
+}
+
+void
+set_break ()
+{
+ device_set_status (phys_device, TTY_SET_BREAK, 0, 0);
+}
+
+void
+clear_break ()
+{
+ device_set_status (phys_device, TTY_CLEAR_BREAK, 0, 0);
+}
+
+void
+abandon_physical_output ()
+{
+ int val = D_WRITE;
+
+ mach_port_deallocate (mach_task_self (), phys_reply_writes);
+ ports_reallocate_port (phys_reply_writes_pi);
+ phys_reply_writes = ports_get_right (phys_reply_writes_pi);
+ mach_port_insert_right (mach_task_self (), phys_reply_writes,
+ phys_reply_writes, MACH_MSG_TYPE_MAKE_SEND);
+
+ device_set_status (phys_device, TTY_FLUSH, &val, TTY_FLUSH_COUNT);
+ npending_output = 0;
+ output_pending = 0;
+}
+
+int
+pending_output_size ()
+{
+ /* Unfortunately, there's no way to get the amount back from Mach
+ that has actually been written from this... */
+ return npending_output;
+}
+
+void
+desert_dtr ()
+{
+ /* This will work, because we set the TF_HUPCLS bit earlier. */
+ device_close (phys_device);
+ mach_port_deallocate (mach_task_self (), phys_device);
+ phys_device = MACH_PORT_NULL;
+
+ mach_port_deallocate (mach_task_self (), phys_reply);
+ mach_port_deallocate (mach_task_self (), phys_reply_writes);
+ phys_reply = phys_reply_writes = MACH_PORT_NULL;
+
+ ports_port_deref (phys_reply_pi);
+ ports_port_deref (phys_reply_writes_pi);
+ phys_reply_pi = phys_reply_writes_pi = 0;
+
+ report_carrier_off ();
+}
+
+error_t
+assert_dtr ()
+{
+ error_t err;
+
+ if (open_pending || (phys_device != MACH_PORT_NULL))
+ return 0;
+
+ assert (phys_reply == MACH_PORT_NULL);
+ assert (phys_reply_pi == 0);
+ phys_reply_pi = ports_allocate_port (term_bucket, sizeof (struct port_info),
+ phys_reply_class);
+ phys_reply = ports_get_right (phys_reply_pi);
+ mach_port_insert_right (mach_task_self (), phys_reply, phys_reply,
+ MACH_MSG_TYPE_MAKE_SEND);
+
+ err = device_open_request (device_master, phys_reply,
+ D_READ|D_WRITE, pterm_name);
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), phys_reply);
+ phys_reply = MACH_PORT_NULL;
+ ports_port_deref (phys_reply_pi);
+ phys_reply_pi = 0;
+ }
+ return err;
+}
+
+
+kern_return_t
+device_open_reply (mach_port_t replyport,
+ int returncode,
+ mach_port_t device)
+{
+ struct tty_status ttystat;
+ int count = TTY_STATUS_COUNT;
+ error_t err;
+
+ if (replyport != phys_reply)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+
+ open_pending = 0;
+
+ if (returncode != 0)
+ {
+ /* Bogus. */
+ report_carrier_on ();
+ report_carrier_off ();
+
+ mach_port_deallocate (mach_task_self (), phys_reply);
+ phys_reply = MACH_PORT_NULL;
+ ports_port_deref (phys_reply_pi);
+ phys_reply_pi = 0;
+ mutex_unlock (&global_lock);
+ return 0;
+ }
+
+ assert (phys_device == MACH_PORT_NULL);
+ assert (phys_reply_writes == MACH_PORT_NULL);
+ assert (phys_reply_writes_pi == 0);
+ phys_device = device;
+ phys_reply_writes_pi = ports_allocate_port (term_bucket,
+ sizeof (struct port_info),
+ phys_reply_class);
+ phys_reply_writes = ports_get_right (phys_reply_writes_pi);
+ mach_port_insert_right (mach_task_self (), phys_reply_writes,
+ phys_reply_writes, MACH_MSG_TYPE_MAKE_SEND);
+
+ device_get_status (phys_device, TTY_STATUS,
+ (dev_status_t)&ttystat, &count);
+ ttystat.tt_breakc = 0;
+ ttystat.tt_flags = TF_ANYP | TF_LITOUT | TF_NOHANG | TF_HUPCLS;
+ device_set_status (phys_device, TTY_STATUS,
+ (dev_status_t)&ttystat, TTY_STATUS_COUNT);
+
+ err = device_read_request_inband (phys_device, phys_reply, D_NOWAIT,
+ 0, vm_page_size);
+ input_pending = 1;
+ report_carrier_on ();
+ if (err)
+ desert_dtr ();
+
+ mutex_unlock (&global_lock);
+
+ return 0;
+}
+
+/* Adjust physical state on the basis of the terminal state.
+ Where it isn't possible, mutate terminal state to match
+ reality. */
+void
+set_bits ()
+{
+ struct tty_status ttystat;
+ int cnt = TTY_STATUS_COUNT;
+
+ assert (!(termstate.c_cflag & CIGNORE));
+
+ if (phys_device == MACH_PORT_NULL)
+ return;
+
+ /* Find the current state. */
+ device_get_status (phys_device, TTY_STATUS, (dev_status_t) &ttystat, &cnt);
+ if (termstate.__ispeed)
+ real_speed_to_bogus_speed (termstate.__ispeed, &ttystat.tt_ispeed);
+ if (termstate.__ospeed)
+ real_speed_to_bogus_speed (termstate.__ospeed, &ttystat.tt_ospeed);
+
+ /* Try and set it. */
+ device_set_status (phys_device, TTY_STATUS,
+ (dev_status_t) &ttystat, TTY_STATUS_COUNT);
+
+ /* And now make termstate match reality. */
+ cnt = TTY_STATUS_COUNT;
+ device_get_status (phys_device, TTY_STATUS, (dev_status_t) &ttystat, &cnt);
+ termstate.__ispeed = bogus_speed_to_real_speed (ttystat.tt_ispeed);
+ termstate.__ospeed = bogus_speed_to_real_speed (ttystat.tt_ospeed);
+
+ /* Mach forces us to use the normal stop bit convention:
+ two bits at 110 bps; 1 bit otherwise. */
+ if (termstate.__ispeed == 110)
+ termstate.c_cflag |= CSTOPB;
+ else
+ termstate.c_cflag &= ~CSTOPB;
+
+ /* Mach only supports 8 bit channels. So wark the CSIZE to conform. */
+ termstate.c_cflag = ((termstate.c_cflag & ~CSIZE)
+ | ((termstate.c_cflag & PARENB) ? CS7 : CS8));
+}
+
+void
+mdmctl (int how, int bits)
+{
+ int oldbits, newbits;
+ int cnt;
+ if ((how == MDMCTL_BIS) || (how == MDMCTL_BIC))
+ {
+ cnt = TTY_MODEM_COUNT;
+ device_get_status (phys_device, TTY_MODEM,
+ (dev_status_t) &oldbits, &cnt);
+ if (cnt < TTY_MODEM_COUNT)
+ oldbits = 0; /* what else can we do? */
+ }
+
+ if (how == MDMCTL_BIS)
+ newbits = (oldbits | bits);
+ else if (how == MDMCTL_BIC)
+ newbits = (oldbits &= ~bits);
+ else
+ newbits = bits;
+
+ device_set_status (phys_device, TTY_MODEM,
+ (dev_status_t) &newbits, TTY_MODEM_COUNT);
+}
+
+int
+mdmstate ()
+{
+ int bits, cnt;
+
+ cnt = TTY_MODEM_COUNT;
+ device_get_status (phys_device, TTY_MODEM, (dev_status_t) &bits, &cnt);
+ if (cnt != TTY_MODEM_COUNT)
+ return 0;
+ else
+ return bits;
+}
+
+/* Unused stubs */
+kern_return_t
+device_read_reply (mach_port_t port,
+ kern_return_t retcode,
+ io_buf_ptr_t data,
+ mach_msg_type_number_t cnt)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+device_write_reply (mach_port_t replyport,
+ kern_return_t retcode,
+ int written)
+{
+ return EOPNOTSUPP;
+}
diff --git a/term/main.c b/term/main.c
new file mode 100644
index 00000000..b1e384c3
--- /dev/null
+++ b/term/main.c
@@ -0,0 +1,147 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd 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 Hurd 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "term.h"
+#include <hurd.h>
+#include <fcntl.h>
+#include <hurd/trivfs.h>
+#include <stdio.h>
+#include <hurd/fsys.h>
+#include <string.h>
+
+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;
+
+struct port_class *trivfs_protid_portclasses[1];
+struct port_class *trivfs_cntl_portclasses[1];
+int trivfs_protid_nportclasses = 1;
+int trivfs_cntl_nportclasses = 1;
+
+int
+demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp)
+{
+ extern int term_server (mach_msg_header_t *, mach_msg_header_t *);
+ extern int tioctl_server (mach_msg_header_t *, mach_msg_header_t *);
+ extern int device_reply_server (mach_msg_header_t *, mach_msg_header_t *);
+
+ return (trivfs_demuxer (inp, outp)
+ || term_server (inp, outp)
+ || tioctl_server (inp, outp)
+ || device_reply_server (inp, outp));
+}
+
+int
+main (int argc, char **argv)
+{
+ file_t file;
+ mach_port_t ctlport, bootstrap;
+
+ term_bucket = ports_create_bucket ();
+
+ tty_cntl_class = ports_create_class (trivfs_clean_cntl, 0);
+ tty_class = ports_create_class (trivfs_clean_protid, 0);
+ cttyid_class = ports_create_class (0, 0);
+
+ trivfs_protid_portclasses[0] = tty_class;
+ trivfs_cntl_portclasses[0] = tty_cntl_class;
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+
+ if (bootstrap == MACH_PORT_NULL)
+ {
+ if (argc != 3)
+ {
+ fprintf (stderr, "Usage: term mach-dev-name ttyname\n");
+ exit (1);
+ }
+ pterm_name = argv[1];
+ nodename = argv[2];
+
+ /* Install control port in filesystem */
+ file = file_name_lookup (nodename, O_CREAT|O_NOTRANS, 0666);
+ if (file == MACH_PORT_NULL)
+ {
+ perror (nodename);
+ exit (1);
+ }
+
+ ctlport = trivfs_handle_port (file, tty_cntl_class, term_bucket,
+ tty_class, term_bucket);
+ termctl = ports_lookup_port (term_bucket, ctlport, tty_cntl_class);
+ assert (termctl);
+
+ errno = file_set_translator (file, 0, FS_TRANS_EXCL | FS_TRANS_SET,
+ 0, 0, 0, ctlport, MACH_MSG_TYPE_MAKE_SEND);
+ if (errno)
+ {
+ perror ("setting translator");
+ exit (1);
+ }
+ }
+ else
+ {
+ if (argc < 2 || argc > 3)
+ {
+ fprintf (stderr, "Usage (as translator): term mach-dev-name [ttyname]\n");
+ exit (1);
+ }
+ pterm_name = argv[1];
+ nodename = (argv[2] ?: "");
+
+ ctlport = trivfs_handle_port (MACH_PORT_NULL, tty_cntl_class,
+ term_bucket, tty_class, term_bucket);
+ errno = fsys_startup (bootstrap, ctlport, MACH_MSG_TYPE_MAKE_SEND,
+ &file);
+ if (errno)
+ {
+ perror ("Starting translator");
+ exit (1);
+ }
+ termctl = ports_lookup_port (term_bucket, ctlport, 0);
+ assert (termctl);
+ termctl->underlying = file;
+ }
+
+ bzero (&termstate, sizeof (termstate));
+ termflags = NO_CARRIER;
+ output_psize = 0;
+ mutex_init (&global_lock);
+
+ inputq = create_queue (256, 100, 300);
+ rawq = create_queue (256, 100, 300);
+ outputq = create_queue (256, 100, 300);
+
+ condition_init (&carrier_alert);
+ condition_init (&select_alert);
+ condition_implies (inputq->wait, &select_alert);
+ condition_implies (outputq->wait, &select_alert);
+
+ /* Launch */
+ ports_manage_port_operations_multithread (term_bucket, demuxer, 0, 0,
+ 0, MACH_PORT_NULL);
+
+ return 0;
+}
+
diff --git a/term/munge.c b/term/munge.c
new file mode 100644
index 00000000..31f0bdee
--- /dev/null
+++ b/term/munge.c
@@ -0,0 +1,725 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd 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 Hurd 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "term.h"
+#include <termios.h>
+#include <unistd.h>
+#include <signal.h>
+#include <ctype.h>
+#include <string.h>
+
+/* Number of characters in the rawq which have been
+ echoed continuously without intervening output. */
+int echo_qsize;
+
+/* Where the output_psize was when echo_qsize was last 0. */
+int echo_pstart;
+
+/* Actually drop character onto output queue. This should be the
+ only place where we actually enqueue characters on the output queue;
+ it is responsible for keeping track of cursor positions. */
+inline void
+poutput (int c)
+{
+ if (termflags & FLUSH_OUTPUT)
+ return; /* never mind */
+
+ if ((c >= ' ') && (c < '\177'))
+ output_psize++;
+ else if (c == '\r')
+ output_psize = 0;
+ else if (c == '\t')
+ output_psize += (output_psize + 8) % 8;
+ else if (c == '\b')
+ output_psize--;
+
+ enqueue (&outputq, c);
+}
+
+/* Place C on output queue, doing normal output processing.
+ This is used for write, never for echo. */
+void
+output_character (int c)
+{
+ int oflag = termstate.c_oflag;
+
+ /* One might think we should turn of INHDERASE here, but, no
+ in U*x it is only turned off by echoed characters.
+ See echo_char in input.c. */
+ if (oflag & OPOST)
+ {
+ /* Characters we write specially */
+ if ((oflag & ONLCR) && c == '\n')
+ {
+ poutput ('\r');
+ poutput ('\n');
+ }
+ else if ((oflag & OXTABS) && c == '\t')
+ {
+ poutput (' ');
+ while (output_psize % 8)
+ poutput (' ');
+ }
+ else if ((oflag & ONOEOT) && c == CHAR_EOT)
+ ;
+ else if ((oflag & OTILDE) && c == '~')
+ {
+ poutput ('\\');
+ poutput ('`');
+ }
+ else if ((oflag & OLCASE) && isalpha (c))
+ {
+ if (isupper (c))
+ poutput ('\\');
+ else
+ c = toupper (c);
+ poutput (c);
+ }
+ else
+ poutput (c);
+ }
+ else
+ poutput (c);
+ echo_qsize = 0;
+ echo_pstart = output_psize;
+}
+
+/* Report the width of character C as printed by output_character,
+ if output_psize were at LOC. . */
+int
+output_width (int c, int loc)
+{
+ int oflag = termstate.c_oflag;
+
+ if (oflag & OPOST)
+ {
+ if ((oflag & OTILDE) && c == '~')
+ return 2;
+ if ((oflag & OLCASE) && isalpha (c) && isupper (c))
+ return 2;
+ }
+ if (c == '\t')
+ {
+ int n = loc + 1;
+ while (n % 8)
+ n++;
+ return n;
+ }
+ if ((c >= ' ') && (c < '\177'))
+ return 1;
+ return 0;
+}
+
+
+
+/* For ICANON mode, this holds the edited line. */
+struct queue *rawq;
+
+/* For each character in this table, if the element is set, then
+ the character is EVEN */
+char const char_parity[] =
+{
+ 1, 0, 0, 1, 0, 1, 1, 0, /* nul - bel */
+ 0, 1, 1, 0, 1, 0, 0, 1, /* bs - si */
+ 0, 1, 1, 0, 1, 0, 0, 1, /* dle - etb */
+ 1, 0, 0, 1, 0, 1, 1, 0, /* can - us */
+ 0, 1, 1, 0, 1, 0, 0, 1, /* sp - ' */
+ 1, 0, 0, 1, 0, 1, 1, 0, /* ( - / */
+ 1, 0, 0, 1, 0, 1, 1, 0, /* 0 - 7 */
+ 0, 1, 1, 0, 1, 0, 0, 1, /* 8 - ? */
+ 0, 1, 1, 0, 1, 0, 0, 1, /* @ - G */
+ 1, 0, 0, 1, 0, 1, 1, 0, /* H - O */
+ 1, 0, 0, 1, 0, 1, 1, 0, /* P - W */
+ 0, 1, 1, 0, 1, 0, 0, 1, /* X - _ */
+ 1, 0, 0, 1, 0, 1, 1, 0, /* ` - g */
+ 0, 1, 1, 0, 1, 0, 0, 1, /* h - o */
+ 0, 1, 1, 0, 1, 0, 0, 1, /* p - w */
+ 1, 0, 0, 1, 0, 1, 1, 0, /* x - del */
+};
+#define checkevenpar(c) (((c)&0x80) \
+ ? !char_parity[(c)&0x7f] \
+ : char_parity[(c)&0x7f])
+#define checkoddpar(c) (((c)&0x80) \
+ ? char_parity[(c)&0x7f] \
+ : !char_parity[(c)&0x7f])
+
+
+
+/* These functions are used by both echo and erase code */
+
+/* Tell if we should echo a character at all */
+static inline int
+echo_p (char c, int quoted)
+{
+ return ((termstate.c_lflag & ECHO)
+ || (c == '\n' && (termstate.c_lflag & ECHONL) && !quoted));
+}
+
+/* Tell if this character deserves double-width cntrl processing */
+static inline int
+echo_double (char c, int quoted)
+{
+ return (iscntrl (c) && (termstate.c_lflag & ECHOCTL)
+ && !((c == '\n' || c == '\t') && !quoted));
+}
+
+/* Do a single C-h SPC C-h sequence */
+static inline void
+write_erase_sequence ()
+{
+ poutput ('\b');
+ poutput (' ');
+ poutput ('\b');
+}
+
+/* Echo a single character to the output */
+/* If this is an echo of a character which is being hard-erased,
+ set hderase. If this is a newline or tab which should not receive
+ their normal special processing, set quoted. */
+static void
+echo_char (char c, int hderase, int quoted)
+{
+ echo_qsize++;
+
+ if (echo_p (c, quoted))
+ {
+ if (!hderase && (termflags & INSIDE_HDERASE))
+ {
+ poutput ('/');
+ termflags &= ~INSIDE_HDERASE;
+ echo_qsize = 0;
+ echo_pstart = output_psize;
+ }
+
+ if (hderase && !(termflags & INSIDE_HDERASE))
+ {
+ poutput ('\\');
+ termflags |= INSIDE_HDERASE;
+ }
+
+ /* Control characters that should use caret-letter */
+ if (echo_double (c, quoted))
+ {
+ poutput ('^');
+ poutput (c + ('A' - CHAR_SOH));
+ }
+ else
+ poutput (c);
+ }
+}
+
+
+/* Re-echo the current rawq preceded by the VREPRINT char. */
+static inline void
+reprint_line ()
+{
+ short *cp;
+
+ if (termstate.c_cc[VREPRINT] != _POSIX_VDISABLE)
+ echo_char (termstate.c_cc[VREPRINT], 0, 0);
+ else
+ echo_char (CHAR_DC3, 0, 0);
+ echo_char ('\n', 0, 0);
+
+ echo_qsize = 0;
+ echo_pstart = output_psize;
+
+ for (cp = rawq->cs; cp != rawq->ce; cp++)
+ echo_char (unquote_char (*cp), 0, char_quoted_p (*cp));
+}
+
+/* Erase a single character off the end of the rawq, and delete
+ it from the screen appropriately. Set ERASE_CHAR if this
+ is being done by the VERASE character, to that character. */
+static void
+erase_1 (char erase_char)
+{
+ int quoted;
+ char c;
+ quoted_char cq;
+
+ if (qsize (rawq) == 0)
+ return;
+
+ cq = queue_erase (rawq);
+ c = unquote_char (cq);
+ quoted = char_quoted_p (cq);
+
+ if (!echo_p (c, quoted))
+ return;
+
+ /* The code for WERASE knows that we echo erase_char iff
+ !ECHOPRT && !ECHO. */
+
+ if (echo_qsize--)
+ {
+ if (termstate.c_lflag & ECHOPRT)
+ echo_char (c, 1, quoted);
+ else if (!(termstate.c_lflag & ECHOE) && erase_char)
+ echo_char (erase_char, 0, 0);
+ else
+ {
+ int nerase;
+
+ if (echo_double (c, quoted))
+ nerase = 2;
+ else if (c == '\t')
+ {
+ quoted_char *cp;
+ int loc = echo_pstart;
+
+ for (cp = rawq->ce - echo_qsize; cp != rawq->ce; cp++)
+ loc += (echo_double (unquote_char (*cp), char_quoted_p (*cp))
+ ? 2
+ : output_width (*cp, loc));
+ nerase = output_psize - loc;
+ }
+ else
+ nerase = output_width (c, output_psize);
+
+ while (nerase--)
+ write_erase_sequence ();
+ }
+ if (echo_qsize == 0)
+ assert (echo_pstart == output_psize);
+ }
+ else
+ reprint_line ();
+}
+
+
+/* Place newly input character C on the input queue. If all remaining
+ pending input characters should be dropped, return 1; else return
+ 0. */
+int
+input_character (int c)
+{
+ int lflag = termstate.c_lflag;
+ int iflag = termstate.c_iflag;
+ int cflag = termstate.c_cflag;
+ cc_t *cc = termstate.c_cc;
+ struct queue **qp = (lflag & ICANON) ? &rawq : &inputq;
+ int flush = 0;
+
+ /* Handle parity errors */
+ if ((iflag & INPCK)
+ && ((cflag & PARODD) ? checkoddpar (c) : checkevenpar (c)))
+ {
+ if (iflag & IGNPAR)
+ goto alldone;
+ else if (iflag & PARMRK)
+ {
+ enqueue_quote (qp, CHAR_USER_QUOTE);
+ enqueue_quote (qp, '\0');
+ enqueue_quote (qp, c);
+ goto alldone;
+ }
+ else
+ c = 0;
+ }
+
+ /* Check to see if we should send IXOFF */
+ if ((iflag & IXOFF)
+ && !qavail (*qp)
+ && (cc[VSTOP] != _POSIX_VDISABLE))
+ {
+ poutput (cc[VSTOP]);
+ termflags |= SENT_VSTOP;
+ }
+
+ /* Character mutations */
+ if (!(iflag & ISTRIP) && (iflag & PARMRK) && (c == CHAR_USER_QUOTE))
+ enqueue_quote (qp, CHAR_USER_QUOTE); /* cause doubling */
+
+ if (iflag & ISTRIP)
+ c &= 0x7f;
+
+ /* Handle LNEXT right away */
+ if (termflags & LAST_LNEXT)
+ {
+ enqueue_quote (qp, c);
+ echo_char (c, 0, 1);
+ termflags &= ~LAST_LNEXT;
+ }
+
+ /* Mutate ILCASE */
+ if ((iflag & ILCASE) && isalpha(c))
+ {
+ if (termflags & LAST_SLASH)
+ erase_1 (0); /* remove the slash from input */
+ else
+ c = isupper(c) ? tolower (c) : c;
+ }
+
+ /* IEXTEN control chars */
+ if (lflag & IEXTEN)
+ {
+ if (CCEQ (cc[VLNEXT], c))
+ {
+ if (lflag & ECHO)
+ {
+ poutput ('^');
+ poutput ('\b');
+ }
+ termflags |= LAST_LNEXT;
+ goto alldone;
+ }
+
+ if (CCEQ (cc[VDISCARD], c))
+ {
+ if (termflags & FLUSH_OUTPUT)
+ termflags &= ~FLUSH_OUTPUT;
+ else
+ {
+ drop_output ();
+ termflags |= FLUSH_OUTPUT;
+ }
+ }
+ }
+
+ /* Signals */
+ if (lflag & ISIG)
+ {
+ if (CCEQ (cc[VINTR], c) || CCEQ (cc[VQUIT], c))
+ {
+ if (!(lflag & NOFLSH))
+ {
+ drop_output ();
+ flush = 1;
+ }
+ echo_char (c, 0, 0);
+ send_signal (CCEQ (cc[VINTR], c) ? SIGINT : SIGQUIT);
+ goto alldone;
+ }
+
+ if (CCEQ (cc[VSUSP], c))
+ {
+ if (!(lflag & NOFLSH))
+ flush = 1;
+ echo_char (c, 0, 0);
+ send_signal (SIGTSTP);
+ goto alldone;
+ }
+ }
+
+ /* IXON */
+ if (iflag & IXON)
+ {
+ if (CCEQ (cc[VSTOP], c))
+ {
+ termflags |= USER_OUTPUT_SUSP;
+
+ if (!(CCEQ(cc[VSTART], c))) /* toggle if VSTART == VSTOP */
+ /* Alldone code always turns off USER_OUTPUT_SUSP. */
+ goto alldone;
+
+ return flush;
+ }
+ if (CCEQ (cc[VSTART], c))
+ goto alldone;
+ }
+
+ /* Newline and carriage-return frobbing */
+ if (c == '\r')
+ {
+ if (iflag & ICRNL)
+ c = '\n';
+ else if (iflag & IGNCR)
+ goto alldone;
+ }
+ else if ((c == '\n') && (iflag & INLCR))
+ c = '\r';
+
+ /* Canonical mode processing */
+ if (lflag & ICANON)
+ {
+ if (CCEQ (cc[VERASE], c))
+ {
+ if (qsize(rawq))
+ erase_1 (c);
+ if (!(termflags & LAST_SLASH)
+ || !(lflag & IEXTEN))
+ goto alldone;
+ }
+
+ if (CCEQ (cc[VKILL], c))
+ {
+ if (!(termflags & LAST_SLASH)
+ || !(lflag & IEXTEN))
+ {
+ if ((lflag & ECHOKE) && !(lflag & ECHOPRT)
+ && (echo_qsize == qsize (rawq)))
+ {
+ while (output_psize > echo_pstart)
+ write_erase_sequence ();
+ }
+ else
+ {
+ echo_char (c, 0, 0);
+ if ((lflag & ECHOK) || (lflag & ECHOKE))
+ echo_char ('\n', 0, 0);
+ }
+ clear_queue (rawq);
+ echo_qsize = 0;
+ echo_pstart = output_psize;
+ termflags &= ~(LAST_SLASH|LAST_LNEXT|INSIDE_HDERASE);
+ goto alldone;
+ }
+ else
+ erase_1 (0); /* remove \ */
+ }
+
+ if (CCEQ (cc[VWERASE], c))
+ {
+ /* If we are not going to echo the erase, then
+ echo a WERASE character right now. (If we
+ passed it to erase_1; it would echo it multiple
+ times.) */
+ if (!(lflag & (ECHOPRT|ECHOE)))
+ echo_char (cc[VWERASE], 0, 1);
+
+ /* Erase whitespace */
+ while (qsize (rawq) && isblank (unquote_char (rawq->ce[-1])))
+ erase_1 (0);
+
+ /* Erase word. */
+ if (lflag & ALTWERASE)
+ /* For ALTWERASE, we erase back to the first blank */
+ while (qsize (rawq) && !isblank (unquote_char (rawq->ce[-1])))
+ erase_1 (0);
+ else
+ /* For regular WERASE, we erase back to the first nonalpha/_ */
+ while (qsize (rawq) && !isblank (unquote_char (rawq->ce[-1]))
+ && (isalnum (unquote_char (rawq->ce[-1]))
+ || (unquote_char (rawq->ce[-1]) != '_')))
+ erase_1 (0);
+
+ goto alldone;
+ }
+
+ if (CCEQ (cc[VREPRINT], c) && (lflag & IEXTEN))
+ {
+ reprint_line ();
+ goto alldone;
+ }
+
+ if (CCEQ (cc[VSTATUS], c) && (lflag & ISIG) && (lflag & IEXTEN))
+ {
+ send_signal (SIGINFO);
+ goto alldone;
+ }
+ }
+
+ /* Now we have a character intended as input. See if it will fit. */
+ if (!qavail (*qp))
+ {
+ if (iflag & IMAXBEL)
+ poutput ('\a');
+ else
+ {
+ /* Drop everything */
+ drop_output ();
+ flush = 1;
+ }
+ goto alldone;
+ }
+
+ /* Echo */
+ echo_char (c, 0, 0);
+ if (CCEQ (cc[VEOF], c) && (lflag & ECHO))
+ {
+ /* Special bizzare echo processing for VEOF character. */
+ int n;
+ n = echo_double (c, 0) ? 2 : output_width (c, output_psize);
+ while (n--)
+ poutput ('\b');
+ }
+
+ /* Put character on input queue */
+ enqueue (qp, c);
+
+ /* Check for break characters in canonical input processing */
+ if (lflag & ICANON)
+ {
+ if (CCEQ (cc[VEOL], c)
+ || CCEQ (cc[VEOL2], c)
+ || CCEQ (cc[VEOF], c)
+ || c == '\n')
+ /* Make input available */
+ while (qsize (rawq))
+ enqueue (&inputq, dequeue (rawq));
+ }
+
+alldone:
+ /* Restart output */
+ if ((iflag & IXANY) || (CCEQ (cc[VSTART], c)))
+ termflags &= ~USER_OUTPUT_SUSP;
+ start_output ();
+
+ return flush;
+}
+
+
+/* This is called by the lower half when a break is received. */
+void
+input_break ()
+{
+ struct queue **qp = termstate.c_lflag & ICANON ? &rawq : &inputq;
+
+ /* Don't do anything if IGNBRK is set. */
+ if (termstate.c_iflag & IGNBRK)
+ return;
+
+ /* If BRKINT is set, then flush queues and send SIGINT. */
+ if (termstate.c_iflag & BRKINT)
+ {
+ drop_output ();
+ /* XXX drop pending input How?? */
+ send_signal (SIGINT);
+ return;
+ }
+
+ /* A break is then read as a null byte; marked specially if PARMRK is set. */
+ if (termstate.c_iflag & PARMRK)
+ {
+ enqueue_quote (qp, CHAR_USER_QUOTE);
+ enqueue_quote (qp, '\0');
+ }
+ enqueue_quote (qp, '\0');
+}
+
+/* Called when a character is recived with a framing error. */
+void
+input_framing_error (int c)
+{
+ struct queue **qp = termstate.c_lflag & ICANON ? &rawq : &inputq;
+
+ /* Ignore it if IGNPAR is set. */
+ if (termstate.c_iflag & IGNPAR)
+ return;
+
+ /* If PARMRK is set, pass it specially marked. */
+ if (termstate.c_iflag & PARMRK)
+ {
+ enqueue_quote (qp, CHAR_USER_QUOTE);
+ enqueue_quote (qp, '\0');
+ enqueue_quote (qp, c);
+ }
+ else
+ /* Otherwise, it looks like a null. */
+ enqueue_quote (qp, '\0');
+}
+
+/* Copy the characters in RAWQ to the end of INPUTQ and clear RAWQ. */
+void
+copy_rawq ()
+{
+ while (qsize (rawq))
+ enqueue (&inputq, dequeue (rawq));
+}
+
+/* Process all the characters in INPUTQ as if they had just been read. */
+void
+rescan_inputq ()
+{
+ short *buf;
+ int i, n;
+
+ n = qsize (inputq);
+ buf = alloca (n * sizeof (quoted_char));
+ bcopy (inputq->cs, buf, n * sizeof (quoted_char));
+ clear_queue (inputq);
+
+ for (i = 0; i < n; i++)
+ input_character (unquote_char (buf[i]));
+}
+
+void
+drop_output ()
+{
+ clear_queue (outputq);
+ abandon_physical_output ();
+}
+
+error_t
+drain_output ()
+{
+ int cancel = 0;
+
+ while ((qsize (outputq) || pending_output_size ())
+ && (!(termflags & NO_CARRIER) || (termstate.c_cflag & CLOCAL))
+ && !cancel)
+ cancel = hurd_condition_wait (outputq->wait, &global_lock);
+
+ return cancel ? EINTR : 0;
+}
+
+/* Create and return a new queue. */
+struct queue *
+create_queue (int size, int lowat, int hiwat)
+{
+ struct queue *q;
+
+ q = malloc (sizeof (struct queue) + size * sizeof (quoted_char));
+ q->susp = 0;
+ q->lowat = lowat;
+ q->hiwat = hiwat;
+ q->cs = q->ce = q->array;
+ q->arraylen = size;
+ q->wait = malloc (sizeof (struct condition));
+ condition_init (q->wait);
+ return q;
+}
+
+/* Make Q able to have more characters added to it. */
+struct queue *
+reallocate_queue (struct queue *q)
+{
+ int len;
+ struct queue *newq;
+
+ len = qsize (q);
+
+ if (len < q->arraylen / 2)
+ {
+ /* Shift the characters to the front of
+ the queue. */
+ memmove (q->array, q->cs, len * sizeof (quoted_char));
+ q->cs = q->array;
+ q->ce = q->cs + len;
+ }
+ else
+ {
+ /* Make the queue twice as large. */
+ newq = malloc (sizeof (struct queue)
+ + q->arraylen * 2 * sizeof (quoted_char));
+ newq->susp = q->susp;
+ newq->lowat = q->lowat;
+ newq->hiwat = q->hiwat;
+ newq->cs = newq->array;
+ newq->ce = newq->array + len;
+ newq->arraylen = q->arraylen * 2;
+ newq->wait = q->wait;
+ memmove (newq->array, q->cs, len * sizeof (quoted_char));
+ free (q);
+ q = newq;
+ }
+ return q;
+}
diff --git a/term/term.h b/term/term.h
new file mode 100644
index 00000000..a55f7027
--- /dev/null
+++ b/term/term.h
@@ -0,0 +1,264 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd 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 Hurd 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <cthreads.h>
+#include <assert.h>
+
+#undef MDMBUF
+#undef ECHO
+#undef TOSTOP
+#undef FLUSHO
+#undef PENDIN
+#undef NOFLSH
+#include <termios.h>
+
+#define CHAR_SOH '\001' /* C-a */
+#define CHAR_EOT '\004' /* C-d */
+#define CHAR_DC3 '\022' /* C-r */
+#define CHAR_USER_QUOTE '\377' /* break quoting, etc. */
+
+/* XXX These belong in <termios.h> */
+#define ILCASE (1 << 14)
+#define OLCASE (1 << 9)
+#define OTILDE (1 << 10)
+
+/* used in mdmctl device call */
+#define MDMCTL_BIS 0
+#define MDMCTL_BIC 1
+#define MDMCTL_SET 2
+
+/* Directly user-visible state */
+struct termios termstate;
+
+/* Other state with the following bits: */
+long termflags;
+
+#define USER_OUTPUT_SUSP 0x00000001 /* user has suspended output */
+#define TTY_OPEN 0x00000002 /* someone has us open */
+#define LAST_SLASH 0x00000004 /* last input char was \ */
+#define LAST_LNEXT 0x00000008 /* last input char was VLNEXT */
+#define INSIDE_HDERASE 0x00000010 /* inside \.../ hardcopy erase pair */
+#define SENT_VSTOP 0x00000020 /* we've sent VSTOP to IXOFF peer */
+#define FLUSH_OUTPUT 0x00000040 /* user wants output flushed */
+#define NO_CARRIER 0x00000080 /* carrier is absent */
+#define EXCL_USE 0x00000100 /* user accessible exclusive use */
+#define NO_OWNER 0x00000200 /* there is no foreground_id */
+#define ICKY_ASYNC 0x00000400 /* some user has set O_ASYNC */
+
+/* PHYSICAL position of the terminal cursor */
+int output_psize;
+
+/* Global lock */
+struct mutex global_lock;
+
+/* Wakeup when NO_CARRIER turns off */
+struct condition carrier_alert;
+
+/* Wakeup for select */
+struct condition select_alert;
+
+/* Bucket for all our ports. */
+struct port_bucket *term_bucket;
+
+/* Port class for tty control ports */
+struct port_class *tty_cntl_class;
+
+/* Port class for tty I/O ports */
+struct port_class *tty_class;
+
+/* Port class for ctty ID ports */
+struct port_class *cttyid_class;
+
+/* Trivfs control structure for the tty */
+struct trivfs_control *termctl;
+
+/* Filename for this terminal */
+char *nodename;
+
+/* Mach device name for this terminal */
+char *pterm_name;
+
+/* The queues we use */
+struct queue *inputq, *rawq, *outputq;
+
+
+/* Character queues */
+#define QUEUE_QUOTE_MARK 0xf000
+typedef short quoted_char;
+struct queue
+{
+ int susp;
+ int lowat;
+ int hiwat;
+ short *cs, *ce;
+ int arraylen;
+ struct condition *wait;
+ quoted_char array[0];
+};
+
+struct queue *create_queue (int size, int lowat, int hiwat);
+
+/* Return the number of characters in Q. */
+extern inline int
+qsize (struct queue *q)
+{
+ return q->ce - q->cs;
+}
+
+/* Return nonzero if characters can be added to Q. */
+extern inline int
+qavail (struct queue *q)
+{
+ return !q->susp;
+}
+
+/* Flush all the characters from Q. */
+extern inline int
+clear_queue (struct queue *q)
+{
+ q->susp = 0;
+ q->cs = q->ce = q->array;
+ condition_broadcast (q->wait);
+}
+
+
+/* Return the next character off Q; leave the quoting bit on. */
+extern inline quoted_char
+dequeue_quote (struct queue *q)
+{
+ int beep = 0;
+
+ assert (qsize (q));
+ if (q->susp && (qsize (q) < q->lowat))
+ {
+ q->susp = 0;
+ beep = 1;
+ }
+ if (qsize (q) == 1)
+ beep = 1;
+ if (beep)
+ condition_broadcast (q->wait);
+ return *q->cs++;
+}
+
+/* Return the next character off Q. */
+extern inline char
+dequeue (struct queue *q)
+{
+ return dequeue_quote (q) & ~QUEUE_QUOTE_MARK;
+}
+
+struct queue *reallocate_queue (struct queue *);
+
+/* Add C to *QP. */
+extern inline void
+enqueue_internal (struct queue **qp, quoted_char c)
+{
+ struct queue *q = *qp;
+
+ if (q->ce - q->array == q->arraylen)
+ q = *qp = reallocate_queue (q);
+
+ *q->ce++ = c;
+
+ if (qsize (q) == 1)
+ condition_broadcast (q->wait);
+
+ if (!q->susp && (qsize (q) > q->hiwat))
+ q->susp = 1;
+}
+
+/* Add C to *QP. */
+extern inline void
+enqueue (struct queue **qp, char c)
+{
+ enqueue_internal (qp, c);
+}
+
+/* Add C to *QP, marking it with a quote. */
+extern inline void
+enqueue_quote (struct queue **qp, char c)
+{
+ enqueue_internal (qp, c | QUEUE_QUOTE_MARK);
+}
+
+/* Return the unquoted version of a quoted_char. */
+extern inline char
+unquote_char (quoted_char c)
+{
+ return c & ~QUEUE_QUOTE_MARK;
+}
+
+/* Tell if a quoted_char is actually quoted. */
+extern inline int
+char_quoted_p (quoted_char c)
+{
+ return c & QUEUE_QUOTE_MARK;
+}
+
+/* Remove the most recently enqueue character from Q; leaving
+ the quote mark on. */
+extern inline short
+queue_erase (struct queue *q)
+{
+ short answer;
+ int beep = 0;
+
+ assert (qsize (q));
+ answer = *--q->ce;
+ if (q->susp && (qsize (q) < q->lowat))
+ {
+ q->susp = 0;
+ beep = 1;
+ }
+ if (qsize (q) == 0)
+ beep = 1;
+ if (beep)
+ condition_broadcast (q->wait);
+ return answer;
+}
+
+
+/* Functions devio is supposed to call */
+int input_character (int);
+void report_carrier_on (void);
+void report_carrier_off (void);
+
+
+/* Other decls */
+void drop_output (void);
+void send_signal (int);
+error_t drain_output ();
+void output_character (int);
+void copy_rawq (void);
+void rescan_inputq (void);
+
+
+/* exported by the devio interface */
+void start_output (void);
+void set_break (void);
+void clear_break (void);
+void abandon_physical_output (void);
+int pending_output_size (void);
+error_t assert_dtr (void);
+void desert_dtr (void);
+void set_bits (void);
+void mdmctl (int, int);
+int mdmstate (void);
diff --git a/term/users.c b/term/users.c
new file mode 100644
index 00000000..4eb61be8
--- /dev/null
+++ b/term/users.c
@@ -0,0 +1,1659 @@
+/*
+ Copyright (C) 1995 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd 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 Hurd 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "term.h"
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <string.h>
+#include <fcntl.h>
+#include <hurd/trivfs.h>
+#include <cthreads.h>
+#include <hurd.h>
+#include "ourmsg_U.h"
+
+#undef ECHO
+#undef MDMBUF
+#undef TOSTOP
+#undef FLUSHO
+#undef PENDIN
+#undef NOFLSH
+
+#include "term_S.h"
+#include "tioctl_S.h"
+#include <sys/ioctl.h>
+
+#define TTYDEFCHARS
+#include <sys/ttydefaults.h>
+
+/* Count of active opens */
+int nperopens;
+
+/* io_async requests */
+struct async_req
+{
+ mach_port_t notify;
+ struct async_req *next;
+};
+struct async_req *async_requests;
+
+mach_port_t async_icky_id;
+mach_port_t async_id;
+struct port_info *cttyid;
+int foreground_id;
+
+struct winsize window_size;
+
+static void call_asyncs (void);
+
+/* Attach this on the hook of any protid that is a ctty. */
+struct protid_hook
+{
+ int refcnt;
+ pid_t pid, pgrp;
+};
+
+static error_t
+open_hook (struct trivfs_control *cntl,
+ uid_t *uids, u_int nuids,
+ uid_t *gids, u_int ngids,
+ int flags)
+{
+ int cancel = 0;
+ error_t err;
+
+ mutex_lock (&global_lock);
+
+ if (!(termflags & TTY_OPEN))
+ {
+ bzero (&termstate, sizeof termstate);
+
+ /* 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;
+
+ bcopy (ttydefchars, termstate.c_cc, NCCS);
+ }
+ else if (termflags & EXCL_USE)
+ {
+ mutex_unlock (&global_lock);
+ return EBUSY;
+ }
+
+ /* Wait for carrier to turn on. */
+ while (((termflags & NO_CARRIER) && !(termstate.c_cflag & CLOCAL))
+ && !(flags & O_NONBLOCK)
+ && !cancel)
+ {
+ err = assert_dtr ();
+ if (err)
+ {
+ mutex_unlock (&global_lock);
+ return err;
+ }
+ cancel = hurd_condition_wait (&carrier_alert, &global_lock);
+ }
+
+ if ((termflags & NO_CARRIER) && !(termstate.c_cflag & CLOCAL))
+ {
+ mutex_unlock (&global_lock);
+ return EWOULDBLOCK;
+ }
+ if (cancel)
+ {
+ mutex_unlock (&global_lock);
+ return EINTR;
+ }
+
+ termflags |= TTY_OPEN;
+ if (!(termstate.c_cflag & CIGNORE))
+ set_bits ();
+
+ mutex_unlock (&global_lock);
+ return 0;
+}
+error_t (*trivfs_check_open_hook) (struct trivfs_control *, uid_t *,
+ u_int, uid_t *, u_int, int)
+ = open_hook;
+
+static error_t
+pi_create_hook (struct trivfs_protid *cred)
+{
+ mutex_lock (&global_lock);
+ if (cred->hook)
+ ((struct protid_hook *)cred->hook)->refcnt++;
+ mutex_unlock (&global_lock);
+
+ return 0;
+}
+error_t (*trivfs_protid_create_hook) (struct trivfs_protid *) = pi_create_hook;
+
+static void
+pi_destroy_hook (struct trivfs_protid *cred)
+{
+ mutex_lock (&global_lock);
+ if (cred->hook && !--((struct protid_hook *)cred->hook)->refcnt)
+ free (cred->hook);
+ mutex_unlock (&global_lock);
+}
+void (*trivfs_protid_destroy_hook) (struct trivfs_protid *) = pi_destroy_hook;
+
+static error_t
+po_create_hook (struct trivfs_peropen *po)
+{
+ mutex_lock (&global_lock);
+ nperopens++;
+ if (po->openmodes & O_ASYNC)
+ termflags |= ICKY_ASYNC;
+ mutex_unlock (&global_lock);
+ return 0;
+}
+error_t (*trivfs_peropen_create_hook) (struct trivfs_peropen *) =
+ po_create_hook;
+
+static void
+po_destroy_hook (struct trivfs_peropen *po)
+{
+ mutex_lock (&global_lock);
+ nperopens--;
+ if (!nperopens)
+ {
+ /* Empty queues */
+ clear_queue (inputq);
+ clear_queue (rawq);
+ drain_output ();
+
+ /* Possibly drop carrier */
+ if ((termstate.c_cflag & HUPCL) || (termflags & NO_CARRIER))
+ desert_dtr ();
+
+ termflags &= ~TTY_OPEN;
+ }
+ mutex_unlock (&global_lock);
+}
+void (*trivfs_peropen_destroy_hook) (struct trivfs_peropen *)
+ = po_destroy_hook;
+
+/* 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 || (termflags & NO_OWNER))
+ return 1;
+
+ if (hook->pid == foreground_id
+ || hook->pgrp == -foreground_id)
+ return 1;
+
+ return 0;
+}
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ st->st_blksize = 512;
+ st->st_fstype = FSTYPE_TERM;
+ st->st_fsid = getpid ();
+ 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_lookup_port (term_bucket,
+ arg, tty_class);
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+
+ 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_port_deref (cred);
+ mutex_unlock (&global_lock);
+ 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)
+{
+ error_t err;
+ mach_port_t new_realnode;
+ struct trivfs_protid *newcred;
+ struct port_info *pi = ports_lookup_port (term_bucket, arg, cttyid_class);
+
+ if (!pi)
+ return EOPNOTSUPP;
+
+ assert (pi == cttyid);
+
+ err = io_restrict_auth (termctl->underlying, &new_realnode, 0, 0, 0, 0);
+
+ if (!err)
+ {
+ err = trivfs_open (termctl, 0, 0, 0, 0, flags, new_realnode, &newcred);
+ if (!err)
+ {
+ *result = ports_get_right (newcred);
+ *resulttype = MACH_MSG_TYPE_MAKE_SEND;
+ ports_port_deref (newcred);
+ }
+ }
+
+ ports_port_deref (pi);
+ return err;
+}
+
+/* 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)
+{
+ error_t err;
+ struct trivfs_protid *newcred;
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket, arg, tty_class);
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+
+ if (!cred->po->openmodes & (O_READ|O_WRITE))
+ {
+ mutex_unlock (&global_lock);
+ err = EBADF;
+ }
+ else
+ {
+ mutex_unlock (&global_lock);
+ err = trivfs_protid_dup (cred, &newcred);
+
+ if (!err)
+ {
+ struct protid_hook *hook = malloc (sizeof (struct protid_hook));
+
+ hook->pid = pid;
+ hook->pgrp = pgrp;
+ hook->refcnt = 1;
+
+ if (newcred->hook)
+ /* We inherited CRED's hook, get rid of our ref to it. */
+ pi_destroy_hook (newcred);
+ newcred->hook = hook;
+
+ *newpt = ports_get_right (newcred);
+ *newpttype = MACH_MSG_TYPE_MAKE_SEND;
+
+ ports_port_deref (newcred);
+ }
+ }
+
+ ports_port_deref (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 cancel;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+
+ /* Check for errors first. */
+
+ if ((cred->po->openmodes & O_WRITE) == 0)
+ {
+ mutex_unlock (&global_lock);
+ return EBADF;
+ }
+
+ if ((termstate.c_lflag & TOSTOP) && !fg_p (cred))
+ {
+ mutex_unlock (&global_lock);
+ return EBACKGROUND;
+ }
+
+ if ((termflags & NO_CARRIER) && !(termstate.c_cflag & CLOCAL))
+ {
+ mutex_unlock (&global_lock);
+ return EIO;
+
+ }
+
+ cancel = 0;
+ for (i = 0; i < datalen; i++)
+ {
+ while (!qavail (outputq) && !cancel)
+ cancel = hurd_condition_wait (outputq->wait, &global_lock);
+ if (cancel)
+ break;
+
+ output_character (data[i]);
+ }
+
+ *amt = i;
+
+ start_output ();
+
+ trivfs_set_mtime (termctl);
+
+ call_asyncs ();
+
+ mutex_unlock (&global_lock);
+
+ return ((cancel && datalen && !*amt) ? EINTR : 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)
+{
+ int cancel;
+ int i, max;
+ char *cp;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+
+ if ((cred->po->openmodes & O_READ) == 0)
+ {
+ mutex_unlock (&global_lock);
+ return EBADF;
+ }
+
+ if (!fg_p (cred))
+ {
+ mutex_unlock (&global_lock);
+ return EBACKGROUND;
+ }
+
+ while (!qsize (inputq))
+ {
+ if ((termflags & NO_CARRIER) && !(termstate.c_cflag & CLOCAL))
+ {
+ /* Return EOF, Posix.1 7.1.1.10. */
+ mutex_unlock (&global_lock);
+ *datalen = 0;
+ return 0;
+ }
+
+ if (cred->po->openmodes & O_NONBLOCK)
+ {
+ mutex_unlock (&global_lock);
+ return EWOULDBLOCK;
+ }
+
+ if (hurd_condition_wait (inputq->wait, &global_lock))
+ {
+ mutex_unlock (&global_lock);
+ return EINTR;
+ }
+ }
+
+ max = (amount < qsize (inputq)) ? amount : qsize (inputq);
+
+ if (max > *datalen)
+ vm_allocate (mach_task_self (), (vm_address_t *)data, max, 1);
+
+ cancel = 0;
+ cp = *data;
+ for (i = 0; i < max; i++)
+ {
+ char c = dequeue (inputq);
+
+ /* Unless this is EOF, add it to the response. */
+ if (!(termstate.c_lflag & ICANON)
+ || !CCEQ (termstate.c_cc[VEOF], c))
+ *cp++ = c;
+
+ /* If this is a break character, then finish now. */
+ if ((termstate.c_lflag & ICANON)
+ && (c == '\n'
+ || CCEQ (termstate.c_cc[VEOF], c)
+ || CCEQ (termstate.c_cc[VEOL], c)
+ || CCEQ (termstate.c_cc[VEOL2], c)))
+ break;
+
+ /* If this is the delayed suspend character, then signal now. */
+ if ((termstate.c_lflag & ISIG)
+ && CCEQ (termstate.c_cc[VDSUSP], c))
+ {
+ /* The CANCEL flag is being used here to tell the return
+ below to make sure we don't signal EOF on a VDUSP that
+ happens at the front of a line. */
+ send_signal (SIGTSTP);
+ cancel = 1;
+ break;
+ }
+ }
+
+ *datalen = cp - *data;
+
+ mutex_unlock (&global_lock);
+
+ call_asyncs ();
+
+ return !*datalen && cancel ? EINTR : 0;
+}
+
+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;
+
+ mutex_lock (&global_lock);
+ if ((cred->po->openmodes & O_READ) == 0)
+ return EBADF;
+ *amt = qsize (inputq);
+ mutex_unlock (&global_lock);
+
+ return 0;
+}
+
+/* TIOCMODG ioctl -- Get modem state */
+kern_return_t
+S_tioctl_tiocmodg (io_t port,
+ int *state)
+{
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket,
+ port, tty_class);
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+ *state = mdmstate ();
+ mutex_unlock (&global_lock);
+
+ ports_port_deref (cred);
+ return 0;
+}
+
+/* TIOCMODS ioctl -- Set modem state */
+kern_return_t
+S_tioctl_tiocmods (io_t port,
+ int state)
+{
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket, port,
+ tty_class);
+ error_t err;
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ mdmctl (MDMCTL_SET, state);
+ err = 0;
+ }
+
+ mutex_unlock (&global_lock);
+
+ ports_port_deref (cred);
+ return err;
+}
+
+/* TIOCEXCL ioctl -- Set exclusive use */
+kern_return_t
+S_tioctl_tiocexcl (io_t port)
+{
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket, port,
+ tty_class);
+ error_t err;
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ termflags |= EXCL_USE;
+ err = 0;
+ }
+
+ mutex_unlock (&global_lock);
+ ports_port_deref (cred);
+ return err;
+}
+
+/* TIOCNXCL ioctl -- Clear exclusive use */
+kern_return_t
+S_tioctl_tiocnxcl (io_t port)
+{
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket,
+ port, tty_class);
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ termflags &= ~EXCL_USE;
+ err = 0;
+ }
+
+ mutex_unlock (&global_lock);
+ ports_port_deref (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_lookup_port (term_bucket, port,
+ tty_class);
+ error_t err;
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ if (flags == 0)
+ flags = O_READ|O_WRITE;
+
+ if (flags & O_READ)
+ clear_queue (inputq);
+
+ if (flags & O_WRITE)
+ drop_output ();
+
+ err = 0;
+ }
+
+ mutex_unlock (&global_lock);
+ ports_port_deref (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_lookup_port (term_bucket, port,
+ tty_class);
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+ 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;
+ mutex_unlock (&global_lock);
+
+ ports_port_deref (cred);
+ return 0;
+}
+
+/* Common code for the varios TIOCSET* commands. */
+static error_t
+set_state (io_t port,
+ tcflag_t *modes,
+ cc_t *ccs,
+ speed_t *speeds,
+ int draino,
+ int flushi)
+{
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket, port,
+ tty_class);
+ error_t err;
+ int oldlflag;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else if (!fg_p (cred))
+ err = EBACKGROUND;
+ else
+ {
+ if (draino)
+ {
+ err = drain_output ();
+ if (err)
+ {
+ mutex_unlock (&global_lock);
+ ports_port_deref (cred);
+ return err;
+ }
+ }
+
+ if (flushi)
+ clear_queue (inputq);
+
+ 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))
+ set_bits ();
+ if (oldlflag & ICANON)
+ {
+ if (!(termstate.c_lflag & ICANON))
+ copy_rawq ();
+ }
+ else
+ {
+ if (termstate.c_lflag & ICANON)
+ rescan_inputq ();
+ }
+ err = 0;
+ }
+
+ mutex_unlock (&global_lock);
+
+ ports_port_deref (cred);
+ return err;
+}
+
+
+
+/* TIOCSETA -- Set termios state */
+kern_return_t
+S_tioctl_tiocseta (io_t port,
+ tcflag_t *modes,
+ cc_t *ccs,
+ speed_t *speeds)
+{
+ return set_state (port, modes, ccs, speeds, 0, 0);
+}
+
+/* Drain output, then set term state. */
+kern_return_t
+S_tioctl_tiocsetaw (io_t port,
+ tcflag_t *modes,
+ cc_t *ccs,
+ speed_t *speeds)
+{
+ return set_state (port, modes, ccs, speeds, 1, 0);
+}
+
+/* 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)
+
+{
+ return set_state (port, modes, ccs, speeds, 1, 1);
+}
+
+/* TIOCGETD -- Return line discipline */
+kern_return_t
+S_tioctl_tiocgetd (io_t port,
+ int *disc)
+{
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket, port,
+ tty_class);
+ if (!cred)
+ return EOPNOTSUPP;
+
+ *disc = 0;
+
+ ports_port_deref (cred);
+ return 0;
+}
+
+/* TIOCSETD -- Set line discipline */
+kern_return_t
+S_tioctl_tiocsetd (io_t port,
+ int disc)
+{
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket,
+ port, tty_class);
+ error_t err;
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ mutex_unlock (&global_lock);
+
+ if (disc != 0)
+ err = ENXIO;
+ else
+ err = 0;
+
+ ports_port_deref (cred);
+ return 0;
+}
+
+/* TIOCDRAIN -- Wait for output to drain */
+kern_return_t
+S_tioctl_tiocdrain (io_t port)
+{
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket,
+ port, tty_class);
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & O_WRITE))
+ {
+ mutex_unlock (&global_lock);
+ return EBADF;
+ }
+
+ err = drain_output ();
+ mutex_unlock (&global_lock);
+ return err;
+}
+
+/* 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_lookup_port (term_bucket, port,
+ tty_class);
+ error_t err;
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ err = 0;
+
+ ports_port_deref (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);
+ }
+
+ mutex_unlock (&global_lock);
+
+ return err;
+}
+
+/* TIOCGWINSZ -- Fetch window size */
+kern_return_t
+S_tioctl_tiocgwinsz (io_t port,
+ struct winsize *size)
+{
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket, port,
+ tty_class);
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+ *size = window_size;
+ mutex_unlock (&global_lock);
+
+ ports_port_deref (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_lookup_port (term_bucket, port,
+ tty_class);
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+ *bits = mdmstate ();
+ mutex_unlock (&global_lock);
+
+ ports_port_deref (cred);
+ return 0;
+}
+
+/* TIOCMSET -- Set all modem bits */
+kern_return_t
+S_tioctl_tiocmset (io_t port,
+ int bits)
+{
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket, port,
+ tty_class);
+ error_t err;
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ mdmctl (MDMCTL_SET, bits);
+ err = 0;
+ }
+
+ mutex_unlock (&global_lock);
+ ports_port_deref (cred);
+ return err;
+}
+
+/* TIOCMBIC -- Clear some modem bits */
+kern_return_t
+S_tioctl_tiocmbic (io_t port,
+ int bits)
+{
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket, port,
+ tty_class);
+ error_t err;
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ mdmctl (MDMCTL_BIC, bits);
+ err = 0;
+ }
+ mutex_unlock (&global_lock);
+
+ ports_port_deref (cred);
+ return err;
+}
+
+/* TIOCMBIS -- Set some modem bits */
+kern_return_t
+S_tioctl_tiocmbis (io_t port,
+ int bits)
+{
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket, port,
+ tty_class);
+ error_t err;
+ if (!cred)
+ return EOPNOTSUPP;
+ mutex_lock (&global_lock);
+
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ mdmctl (MDMCTL_BIS, bits);
+ err = 0;
+ }
+ mutex_unlock (&global_lock);
+ ports_port_deref (cred);
+ return err;
+}
+
+/* TIOCSTART -- start output as if VSTART were typed */
+kern_return_t
+S_tioctl_tiocstart (io_t port)
+{
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket, port,
+ tty_class);
+ error_t err;
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ termflags &= ~USER_OUTPUT_SUSP;
+ err = 0;
+ }
+ mutex_unlock (&global_lock);
+
+ ports_port_deref (cred);
+ return err;
+}
+
+/* TIOCSTOP -- stop output as if VSTOP were typed */
+kern_return_t
+S_tioctl_tiocstop (io_t port)
+{
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket,
+ port, tty_class);
+ error_t err;
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ termflags |= USER_OUTPUT_SUSP;
+ err = 0;
+ }
+ mutex_unlock (&global_lock);
+
+ ports_port_deref (cred);
+ return err;
+}
+
+/* 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_lookup_port (term_bucket,
+ port, tty_class);
+ error_t err;
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+
+ /* 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;
+ }
+ mutex_unlock (&global_lock);
+
+ ports_port_deref (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_lookup_port (term_bucket, port,
+ tty_class);
+ error_t err;
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ *queue_size = qsize (outputq) + pending_output_size ();
+ err = 0;
+ }
+ mutex_unlock (&global_lock);
+
+ ports_port_deref (cred);
+ return err;
+}
+
+/* TIOCSPGRP -- set pgrp of terminal */
+kern_return_t
+S_tioctl_tiocspgrp (io_t port,
+ int pgrp)
+{
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket, port,
+ tty_class);
+ error_t err;
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ termflags &= ~NO_OWNER;
+ foreground_id = -pgrp;
+ err = 0;
+ }
+ mutex_unlock (&global_lock);
+
+ ports_port_deref (cred);
+ return err;
+}
+
+/* TIOCGPGRP --- fetch pgrp of terminal */
+kern_return_t
+S_tioctl_tiocgpgrp (io_t port,
+ int *pgrp)
+{
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket, port,
+ tty_class);
+ error_t ret;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+ if (termflags & NO_OWNER)
+ ret = ENOTTY; /* that's what BSD says... */
+ else
+ {
+ *pgrp = - foreground_id;
+ ret = 0;
+ }
+ mutex_unlock (&global_lock);
+
+ ports_port_deref (cred);
+ return ret;
+}
+
+/* TIOCCDTR -- clear DTR */
+kern_return_t
+S_tioctl_tioccdtr (io_t port)
+{
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket, port,
+ tty_class);
+ error_t err;
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ mdmctl (MDMCTL_BIC, TIOCM_DTR);
+ err = 0;
+ }
+ mutex_unlock (&global_lock);
+
+ ports_port_deref (cred);
+ return err;
+}
+
+/* TIOCSDTR -- set DTR */
+kern_return_t
+S_tioctl_tiocsdtr (io_t port)
+{
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket, port,
+ tty_class);
+ error_t err;
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ mdmctl (MDMCTL_BIS, TIOCM_DTR);
+ err = 0;
+ }
+ mutex_unlock (&global_lock);
+
+ ports_port_deref (cred);
+ return err;
+}
+
+/* TIOCCBRK -- Clear break condition */
+kern_return_t
+S_tioctl_tioccbrk (io_t port)
+{
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket, port,
+ tty_class);
+ error_t err;
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ clear_break ();
+ err = 0;
+ }
+ mutex_unlock (&global_lock);
+
+ ports_port_deref (cred);
+ return err;
+}
+
+/* TIOCSBRK -- Set break condition */
+kern_return_t
+S_tioctl_tiocsbrk (io_t port)
+{
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket,
+ port, tty_class);
+ error_t err;
+ if (!cred)
+ return EOPNOTSUPP;
+
+ mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ err = EBADF;
+ else
+ {
+ set_break ();
+ err = 0;
+ }
+ mutex_unlock (&global_lock);
+
+ ports_port_deref (cred);
+ return err;
+}
+
+error_t
+trivfs_S_file_truncate (struct trivfs_protid *cred,
+ off_t size)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ mutex_lock (&global_lock);
+ if ((cred->po->openmodes & O_WRITE) == 0)
+ {
+ mutex_unlock (&global_lock);
+ return EBADF;
+ }
+ mutex_unlock (&global_lock);
+ 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;
+ mutex_lock (&global_lock);
+ *bits = cred->po->openmodes;
+ mutex_unlock (&global_lock);
+ return 0;
+}
+
+#define HONORED_STATE_MODES (O_APPEND|O_ASYNC|O_FSYNC|O_NONBLOCK|O_NOATIME)
+
+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;
+ mutex_lock (&global_lock);
+ cred->po->openmodes &= ~HONORED_STATE_MODES;
+ cred->po->openmodes |= (bits & HONORED_STATE_MODES);
+ mutex_unlock (&global_lock);
+ 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;
+ mutex_lock (&global_lock);
+ cred->po->openmodes |= (bits & HONORED_STATE_MODES);
+ mutex_unlock (&global_lock);
+ 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;
+ mutex_lock (&global_lock);
+ cred->po->openmodes &= ~(bits & HONORED_STATE_MODES);
+ mutex_unlock (&global_lock);
+ 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;
+ mutex_lock (&global_lock);
+ termflags &= ~NO_OWNER;
+ foreground_id = owner;
+ mutex_unlock (&global_lock);
+ 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;
+ mutex_lock (&global_lock);
+ if (termflags & NO_OWNER)
+ {
+ mutex_unlock (&global_lock);
+ return ENOTTY;
+ }
+ *owner = foreground_id;
+ mutex_unlock (&global_lock);
+ 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;
+ mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ {
+ mutex_unlock (&global_lock);
+ return EBADF;
+ }
+ *id = async_icky_id;
+ *idtype = MACH_MSG_TYPE_MAKE_SEND;
+ mutex_unlock (&global_lock);
+ 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;
+ mutex_lock (&global_lock);
+ if (!(cred->po->openmodes & (O_READ|O_WRITE)))
+ {
+ mutex_unlock (&global_lock);
+ 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;
+ mutex_unlock (&global_lock);
+ 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,
+ int *idtag)
+{
+ int available;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ /* We don't deal with SELECT_URG here. */
+ if (*type & ~(SELECT_READ | SELECT_WRITE))
+ return EINVAL;
+
+ available = 0;
+ if (*type == 0)
+ return 0;
+
+ mutex_lock (&global_lock);
+
+ while (1)
+ {
+ if ((*type & SELECT_READ) && qsize (inputq))
+ available |= SELECT_READ;
+ if ((*type & SELECT_WRITE) && qavail (outputq))
+ available |= SELECT_WRITE;
+
+ if (available)
+ {
+ *type = available;
+ mutex_unlock (&global_lock);
+ return 0;
+ }
+
+ hurd_condition_wait (&select_alert, &global_lock);
+ }
+}
+
+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;
+}
+
+/* Call all the scheduled async I/O handlers */
+static void
+call_asyncs ()
+{
+ struct async_req *ar, *nxt, **prevp;
+ mach_port_t err;
+
+ /* If no I/O is possible or nobody wants async
+ messages, don't bother further. */
+ if ((!(termflags & ICKY_ASYNC) && !async_requests)
+ || (!qsize (inputq) && !qavail (outputq)))
+ return;
+
+ if ((termflags & ICKY_ASYNC) && !(termflags & NO_OWNER))
+ hurd_sig_post (foreground_id, SIGIO, async_icky_id);
+
+ for (ar = async_requests, prevp = &async_requests;
+ ar;
+ ar = nxt)
+ {
+ nxt = ar->next;
+ err = nowait_msg_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 (!(termflags & NO_OWNER))
+ {
+ 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_lookup_port (term_bucket, port,
+ tty_class);
+ if (!cred)
+ return EOPNOTSUPP;
+
+ ports_interrupt_rpc (cred);
+
+ ports_port_deref (cred);
+ return 0;
+}
+
+void
+report_carrier_off ()
+{
+ termflags |= NO_CARRIER;
+ if (!(termstate.c_cflag & CLOCAL))
+ send_signal (SIGHUP);
+}
+
+void
+report_carrier_on ()
+{
+ termflags &= ~NO_CARRIER;
+ condition_broadcast (&carrier_alert);
+}
+
+kern_return_t
+S_term_get_nodename (io_t arg,
+ char *name)
+{
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket, arg,
+ tty_class);
+ if (!cred)
+ return EOPNOTSUPP;
+
+ strcpy (name, nodename);
+
+ ports_port_deref (cred);
+ return 0;
+}
+
+kern_return_t
+S_term_set_nodename (io_t arg,
+ char *name)
+{
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket, arg,
+ tty_class);
+ if (!cred)
+ return EOPNOTSUPP;
+ ports_port_deref (cred);
+
+ if (strcmp (name, nodename))
+ return EINVAL;
+
+ return 0;
+}
+
+kern_return_t
+S_term_set_filenode (io_t arg,
+ file_t filenode)
+{
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket, arg,
+ tty_class);
+ if (!cred)
+ return EOPNOTSUPP;
+ ports_port_deref (cred);
+
+ return EINVAL;
+}
+
+kern_return_t
+S_term_get_bottom_type (io_t arg,
+ int *ttype)
+{
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket,
+ arg, tty_class);
+ if (!cred)
+ return EOPNOTSUPP;
+
+ ports_port_deref (cred);
+ *ttype = TERM_ON_MACHDEV;
+ return 0;
+}
+
+kern_return_t
+S_term_on_machdev (io_t arg,
+ device_t machdev)
+{
+ struct trivfs_protid *cred = ports_lookup_port (term_bucket, arg,
+ tty_class);
+ if (!cred)
+ return EOPNOTSUPP;
+ ports_port_deref (cred);
+ return EINVAL;
+}
+
+kern_return_t
+S_term_on_hurddev (io_t arg,
+ io_t hurddev)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+S_term_on_pty (io_t arg,
+ mach_port_t *master)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *cntl, int flags)
+{
+ return EBUSY;
+}