summaryrefslogtreecommitdiff
path: root/console
diff options
context:
space:
mode:
authorMarcus Brinkmann <marcus@gnu.org>2002-06-05 02:00:32 +0000
committerMarcus Brinkmann <marcus@gnu.org>2002-06-05 02:00:32 +0000
commitcb387b25d318025c67c422d509253d824a7764f1 (patch)
tree671b4093bcce8ffe84524a4b52be00fb762274f6 /console
parent94d1c5884949b21303e66b80c00c1690ec0d9d60 (diff)
2002-06-05 Marcus Brinkmann <marcus@gnu.org>
* input.h: New file. * input.c: Likewise. * console.h: Likewise. * console.c: Likewise. * display.h: New development version. * display.c: Likewise. * Makefile (SRCS): Replace with files for new console server. (LCLHDRS): Likewise. (HURDLIBS): Likewise. (OBJS): Likewise.
Diffstat (limited to 'console')
-rw-r--r--console/ChangeLog13
-rw-r--r--console/Makefile20
-rw-r--r--console/console.c1171
-rw-r--r--console/console.h35
-rw-r--r--console/display.c524
-rw-r--r--console/display.h39
-rw-r--r--console/input.c277
-rw-r--r--console/input.h58
8 files changed, 1983 insertions, 154 deletions
diff --git a/console/ChangeLog b/console/ChangeLog
index 513802c6..5a14f195 100644
--- a/console/ChangeLog
+++ b/console/ChangeLog
@@ -1,5 +1,18 @@
2002-06-05 Marcus Brinkmann <marcus@gnu.org>
+ * input.h: New file.
+ * input.c: Likewise.
+ * console.h: Likewise.
+ * console.c: Likewise.
+ * display.h: New development version.
+ * display.c: Likewise.
+ * Makefile (SRCS): Replace with files for new console server.
+ (LCLHDRS): Likewise.
+ (HURDLIBS): Likewise.
+ (OBJS): Likewise.
+
+2002-06-05 Marcus Brinkmann <marcus@gnu.org>
+
* input.h: Renamed to ...
* input-drv.h: ... this.
* focus.c: Include "input-drv.h" instead "input.h".
diff --git a/console/Makefile b/console/Makefile
index a5de1e49..2e227269 100644
--- a/console/Makefile
+++ b/console/Makefile
@@ -22,13 +22,21 @@ dir := console
makemode := server
target = console
-SRCS = main.c vcons.c focus.c vga-display.c vga.c dynafont.c bdf.c
-LCLHDRS = focus.h input-drv.h vcons.h display-drv.h vga.h vga-hw.h dynafont.h bdf.h \
- mutations.h priv.h
-HURDLIBS = trivfs fshelp iohelp threads ports ihash shouldbeinlibc
-OBJS = $(subst .c,.o,$(SRCS)) tioctlServer.o
+SRCS = console.c display.c input.c
+LCLHDRS = console.h display.h input.h
-MIGSFLAGS += -imacros $(srcdir)/mutations.h
+HURDLIBS = netfs fshelp iohelp threads ports ihash shouldbeinlibc
+OBJS = $(subst .c,.o,$(SRCS))
+
+# This is the old monolithic version of the console server.
+#SRCS = main.c vcons.c focus.c vga-display.c vga.c dynafont.c bdf.c
+#LCLHDRS = focus.h input-drv.h vcons.h display-drv.h vga.h vga-hw.h \
+# dynafont.h bdf.h mutations.h priv.h
+#
+#HURDLIBS = trivfs fshelp iohelp threads ports ihash shouldbeinlibc
+#OBJS = $(subst .c,.o,$(SRCS)) tioctlServer.o
+#
+#MIGSFLAGS += -imacros $(srcdir)/mutations.h
include ../Makeconf
diff --git a/console/console.c b/console/console.c
new file mode 100644
index 00000000..57e93de6
--- /dev/null
+++ b/console/console.c
@@ -0,0 +1,1171 @@
+/* console.c -- A console server.
+ Copyright (C) 1997, 1999, 2002 Free Software Foundation, Inc.
+ Written by Miles Bader and Marcus Brinkmann.
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#include <hurd/netfs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+
+#include <argp.h>
+#include <error.h>
+#include <string.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <cthreads.h>
+#include <mcheck.h>
+
+#include <version.h>
+
+#include "console.h"
+#include "display.h"
+#include "input.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (console);
+
+char *netfs_server_name = "console";
+char *netfs_server_version = HURD_VERSION;
+int netfs_maxsymlinks = 16; /* Arbitrary. */
+
+
+struct vcons
+{
+ /* Protected by cons->lock. */
+ vcons_t next;
+ vcons_t prev;
+ /* We acquire one reference per netnode. */
+ int refcnt;
+
+ /* The following members remain constant over the lifetime of the
+ object and accesses don't need to be locked. */
+ int id;
+ char *name;
+ cons_t cons;
+ display_t display;
+ input_t input;
+
+ struct mutex lock;
+ /* Nodes in the filesystem referring to this virtual console. */
+ struct node *dir_node;
+ struct node *cons_node;
+ struct node *disp_node;
+ struct node *inpt_node;
+};
+
+struct cons
+{
+ /* The lock protects the console, all virtual consoles contained in
+ it and the reference counters. It also locks the
+ configuration. */
+ struct mutex lock;
+ vcons_t vcons_list;
+ /* The encoding. */
+ const char *encoding;
+
+ struct node *node;
+ mach_port_t underlying;
+ /* A template for the stat information of all nodes. */
+ struct stat stat_template;
+} mycons;
+cons_t cons = &mycons;
+
+/* Lookup the virtual console with number ID in the console CONS,
+ acquire a reference for it, and return it in R_VCONS. If CREATE is
+ true, the virtual console will be created if it doesn't exist yet.
+ If CREATE is true, and ID 0, the first free virtual console id is
+ used. */
+error_t
+vcons_lookup (cons_t cons, int id, int create, vcons_t *r_vcons)
+{
+ error_t err;
+ vcons_t previous_vcons = 0;
+ vcons_t vcons;
+
+ if (!id && !create)
+ return EINVAL;
+
+ mutex_lock (&cons->lock);
+ if (id)
+ {
+ if (cons->vcons_list && cons->vcons_list->id <= id)
+ {
+ previous_vcons = cons->vcons_list;
+ while (previous_vcons->next && previous_vcons->next->id <= id)
+ previous_vcons = previous_vcons->next;
+ if (previous_vcons->id == id)
+ {
+ previous_vcons->refcnt++;
+ mutex_unlock (&cons->lock);
+ *r_vcons = previous_vcons;
+ return 0;
+ }
+ }
+ else if (!create)
+ {
+ mutex_unlock (&cons->lock);
+ return ESRCH;
+ }
+ }
+ else
+ {
+ id = 1;
+ if (cons->vcons_list && cons->vcons_list->id == 1)
+ {
+ previous_vcons = cons->vcons_list;
+ while (previous_vcons && previous_vcons->id == id)
+ {
+ id++;
+ previous_vcons = previous_vcons->next;
+ }
+ }
+ }
+
+ vcons = calloc (1, sizeof (struct vcons));
+ if (!vcons)
+ {
+ mutex_unlock (&vcons->cons->lock);
+ return ENOMEM;
+ }
+ vcons->cons = cons;
+ vcons->refcnt = 1;
+ vcons->id = id;
+ asprintf (&vcons->name, "%i", id);
+ /* XXX Error checking. */
+
+ mutex_init (&vcons->lock);
+ err = display_create (&vcons->display, cons->encoding);
+ if (err)
+ {
+ free (vcons->name);
+ free (vcons);
+ mutex_unlock (&vcons->cons->lock);
+ return err;
+ }
+
+ err = input_create (&vcons->input, cons->encoding);
+ if (err)
+ {
+ display_destroy (vcons->display);
+ free (vcons->name);
+ free (vcons);
+ mutex_unlock (&vcons->cons->lock);
+ return err;
+ }
+
+ /* Insert the virtual console into the doubly linked list. */
+ if (previous_vcons)
+ {
+ vcons->prev = previous_vcons;
+ if (previous_vcons->next)
+ {
+ previous_vcons->next->prev = vcons;
+ vcons->next = previous_vcons->next;
+ }
+ previous_vcons->next = vcons;
+ }
+ else
+ {
+ if (cons->vcons_list)
+ {
+ cons->vcons_list->prev = vcons;
+ vcons->next = cons->vcons_list;
+ }
+ cons->vcons_list = vcons;
+ }
+ mutex_unlock (&cons->lock);
+ *r_vcons = vcons;
+ return 0;
+}
+
+/* Acquire an additional reference to the virtual console VCONS. */
+void
+vcons_ref (vcons_t vcons)
+{
+ cons_t cons = vcons->cons;
+
+ mutex_lock (&cons->lock);
+ vcons->refcnt++;
+ mutex_unlock (&cons->lock);
+}
+
+/* Release a reference to the virtual console VCONS. If this was the
+ last reference the virtual console is destroyed. */
+void
+vcons_release (vcons_t vcons)
+{
+ cons_t cons = vcons->cons;
+
+ mutex_lock (&cons->lock);
+ if (!--vcons->refcnt)
+ {
+ /* As we keep a reference for all input focus groups pointing to
+ the virtual console, and a reference for the active console,
+ we know that without references, this virtual console is
+ neither active nor used by any input group. */
+
+ if (vcons->prev)
+ vcons->prev->next = vcons->next;
+ else
+ cons->vcons_list = vcons->next;
+ if (vcons->next)
+ vcons->next->prev = vcons->prev;
+
+ /* XXX Destroy the state. */
+ display_destroy (vcons->display);
+ input_destroy (vcons->input);
+ free (vcons->name);
+ free (vcons);
+ }
+ mutex_unlock (&cons->lock);
+}
+
+struct netnode
+{
+ /* The root node points to the console object. */
+ cons_t cons;
+
+ /* All other nodes point to the virtual console object. */
+ vcons_t vcons;
+};
+
+typedef enum
+ {
+ VCONS_NODE_DIR = 0,
+ VCONS_NODE_CONSOLE,
+ VCONS_NODE_DISPLAY,
+ VCONS_NODE_INPUT
+ } vcons_node_type;
+
+/* Make a new virtual node. Always consumes the ports. */
+static error_t
+new_node (struct node **np, vcons_t vcons, vcons_node_type type)
+{
+ struct netnode *nn = calloc (1, sizeof *nn);
+ if (nn == 0)
+ return ENOMEM;
+
+ nn->vcons = vcons;
+ *np = netfs_make_node (nn);
+ if (*np == 0)
+ {
+ free (nn);
+ return ENOMEM;
+ }
+ (*np)->nn_stat = cons->stat_template;
+ (*np)->nn_translated = 0;
+
+ switch (type)
+ {
+ case VCONS_NODE_DIR:
+ (*np)->nn_stat.st_ino = vcons->id << 2;
+ (*np)->nn_stat.st_mode |= S_IFDIR;
+ (*np)->nn_stat.st_size = 0;
+ break;
+ case VCONS_NODE_CONSOLE:
+ (*np)->nn_stat.st_ino = (vcons->id << 2) + 1;
+ (*np)->nn_stat.st_mode |= S_IFCHR; /* Don't set nn_translated! */
+ (*np)->nn_stat.st_mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH);
+ (*np)->nn_stat.st_size = 0;
+ break;
+ case VCONS_NODE_DISPLAY:
+ (*np)->nn_stat.st_ino = (vcons->id << 2) + 2;
+ (*np)->nn_stat.st_mode |= S_IFREG;
+ (*np)->nn_stat.st_mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH);
+ (*np)->nn_stat.st_size = 2000 * sizeof (wchar_t); /* XXX */
+ break;
+ case VCONS_NODE_INPUT:
+ (*np)->nn_stat.st_ino = (vcons->id << 2) + 3;
+ (*np)->nn_stat.st_mode |= S_IFIFO;
+ (*np)->nn_stat.st_mode &= ~(S_IXUSR | S_IXGRP | S_IXOTH);
+ (*np)->nn_stat.st_size = 0;
+ break;
+ }
+
+ /* If the underlying node isn't a directory, propagate read permission to
+ execute permission since we need that for lookups. */
+ if (! S_ISDIR (cons->stat_template.st_mode)
+ && S_ISDIR ((*np)->nn_stat.st_mode))
+ {
+ if (cons->stat_template.st_mode & S_IRUSR)
+ (*np)->nn_stat.st_mode |= S_IXUSR;
+ if (cons->stat_template.st_mode & S_IRGRP)
+ (*np)->nn_stat.st_mode |= S_IXGRP;
+ if (cons->stat_template.st_mode & S_IROTH)
+ (*np)->nn_stat.st_mode |= S_IXOTH;
+ }
+
+ fshelp_touch (&(*np)->nn_stat, TOUCH_ATIME|TOUCH_MTIME|TOUCH_CTIME,
+ console_maptime);
+
+ return 0;
+}
+
+
+/* Node management. */
+
+/* Node NP has no more references; free all its associated
+ storage. */
+void
+netfs_node_norefs (struct node *np)
+{
+ vcons_t vcons = np->nn->vcons;
+
+ /* The root node does never go away. */
+ assert (!np->nn->cons && np->nn->vcons);
+
+ /* Avoid deadlock. */
+ spin_unlock (&netfs_node_refcnt_lock);
+
+ /* Find the back reference to ourself in the virtual console
+ structure, and delete it. */
+ mutex_lock (&vcons->lock);
+ spin_lock (&netfs_node_refcnt_lock);
+ if (np->references)
+ {
+ /* Someone else got a reference while we were attempting to go
+ away. This can happen in netfs_attempt_lookup. In this
+ case, just unlock the node and do nothing else. */
+ mutex_unlock (&vcons->lock);
+ mutex_unlock (&np->lock);
+ return;
+ }
+ if (np == vcons->dir_node)
+ vcons->dir_node = 0;
+ else if (np == vcons->cons_node)
+ vcons->cons_node = 0;
+ else if (np == vcons->disp_node)
+ vcons->disp_node = 0;
+ else
+ {
+ assert (np == vcons->inpt_node);
+ vcons->inpt_node = 0;
+ }
+ mutex_unlock (&vcons->lock);
+
+ /* Release our reference. */
+ vcons_release (vcons);
+
+ free (np->nn);
+ free (np);
+}
+
+/* Attempt to create a file named NAME in DIR for USER with MODE. Set
+ *NODE to the new node upon return. On any error, clear *NODE.
+ *NODE should be locked on success; no matter what, unlock DIR
+ before returning. */
+error_t
+netfs_attempt_create_file (struct iouser *user, struct node *dir,
+ char *name, mode_t mode, struct node **np)
+{
+ /* We create virtual consoles dynamically on the fly, so there is no
+ need for an explicit create operation. */
+ *np = 0;
+ mutex_unlock (&dir->lock);
+ return EOPNOTSUPP;
+}
+
+/* Node NODE is being opened by USER, with FLAGS. NEWNODE is nonzero
+ if we just created this node. Return an error if we should not
+ permit the open to complete because of a permission
+ restriction. */
+error_t
+netfs_check_open_permissions (struct iouser *user, struct node *node,
+ int flags, int newnode)
+{
+ error_t err = 0;
+ if (flags & O_READ)
+ err = fshelp_access (&node->nn_stat, S_IREAD, user);
+ if (!err && (flags & O_WRITE))
+ err = fshelp_access (&node->nn_stat, S_IWRITE, user);
+ if (!err && (flags & O_EXEC))
+ err = fshelp_access (&node->nn_stat, S_IEXEC, user);
+ return err;
+}
+
+/* This should attempt a utimes call for the user specified by CRED on node
+ NODE, to change the atime to ATIME and the mtime to MTIME. */
+error_t
+netfs_attempt_utimes (struct iouser *cred, struct node *node,
+ struct timespec *atime, struct timespec *mtime)
+{
+ error_t err = fshelp_isowner (&node->nn_stat, cred);
+ int flags = TOUCH_CTIME;
+
+ if (! err)
+ {
+ if (mtime)
+ {
+ node->nn_stat.st_mtime = mtime->tv_sec;
+ node->nn_stat.st_mtime_usec = mtime->tv_nsec / 1000;
+ }
+ else
+ flags |= TOUCH_MTIME;
+
+ if (atime)
+ {
+ node->nn_stat.st_atime = atime->tv_sec;
+ node->nn_stat.st_atime_usec = atime->tv_nsec / 1000;
+ }
+ else
+ flags |= TOUCH_ATIME;
+
+ fshelp_touch (&node->nn_stat, flags, console_maptime);
+ }
+ return err;
+}
+
+/* Return the valid access types (bitwise OR of O_READ, O_WRITE, and O_EXEC)
+ in *TYPES for file NODE and user CRED. */
+error_t
+netfs_report_access (struct iouser *cred, struct node *node, int *types)
+{
+ *types = 0;
+ if (fshelp_access (&node->nn_stat, S_IREAD, cred) == 0)
+ *types |= O_READ;
+ if (fshelp_access (&node->nn_stat, S_IWRITE, cred) == 0)
+ *types |= O_WRITE;
+ if (fshelp_access (&node->nn_stat, S_IEXEC, cred) == 0)
+ *types |= O_EXEC;
+ return 0;
+}
+
+/* Make sure that NP->nn_stat is filled with the most current
+ information. CRED identifies the user responsible for the
+ operation. NP is locked. */
+error_t
+netfs_validate_stat (struct node *np, struct iouser *cred)
+{
+ /* We are always uptodate. */
+ return 0;
+}
+
+/* This should sync the file NODE completely to disk, for the user
+ CRED. If WAIT is set, return only after sync is completely
+ finished. */
+error_t
+netfs_attempt_sync (struct iouser *cred, struct node *np, int wait)
+{
+ return 0;
+}
+
+
+/* Directory management. */
+
+/* Lookup NAME in DIR for USER; set *NODE to the found name upon
+ return. If the name was not found, then return ENOENT. On any
+ error, clear *NODE. (*NODE, if found, should be locked, this call
+ should unlock DIR no matter what.) */
+error_t
+netfs_attempt_lookup (struct iouser *user, struct node *dir,
+ char *name, struct node **node)
+{
+ error_t err;
+
+ *node = 0;
+ err = fshelp_access (&dir->nn_stat, S_IEXEC, user);
+ if (err)
+ goto out;
+
+ if (strcmp (name, ".") == 0)
+ {
+ /* Current directory -- just add an additional reference to DIR
+ and return it. */
+ netfs_nref (dir);
+ *node = dir;
+ goto out;
+ }
+
+ if (strcmp (name, "..") == 0)
+ {
+ /* Parent directory -- if this is the root directory, return
+ EAGAIN. Otherwise return the root node, because we know
+ that our hierarchy is only one level deep. */
+
+ if (dir->nn->cons)
+ err = EAGAIN;
+ else
+ {
+ netfs_nref (netfs_root_node);
+ *node = netfs_root_node;
+ }
+ goto out;
+ }
+
+ if (dir->nn->cons)
+ {
+ /* This is the root directory. Look up the desired virtual
+ console, creating it on the fly if necessary. */
+ vcons_t vcons;
+ int release_vcons = 0;
+ char *tail = NULL;
+ int id;
+ errno = 0;
+ id = strtol (name, &tail, 0);
+ if ((tail && *tail) || errno)
+ {
+ err = ENOENT;
+ goto out;
+ }
+ err = vcons_lookup (dir->nn->cons, id, 1, &vcons);
+ if (err == ESRCH || err == EINVAL)
+ err = ENOENT;
+ if (err)
+ goto out;
+
+ mutex_lock (&vcons->lock);
+ if (vcons->dir_node)
+ {
+ /* We already have a directory node for this virtual
+ console. Use that, acquire a reference for it, and drop
+ our extra reference to the virtual console. */
+ *node = vcons->dir_node;
+ netfs_nref (*node);
+ release_vcons = 1;
+ }
+ else
+ {
+ /* Create a new directory node, connsuming the reference to
+ the virtual console. */
+ err = new_node (node, vcons, VCONS_NODE_DIR);
+ if (!err)
+ vcons->dir_node = *node;
+ else
+ release_vcons = 1;
+ }
+ mutex_unlock (&vcons->lock);
+ if (release_vcons)
+ vcons_release (vcons);
+ }
+ else
+ {
+ /* This is a virtual console directory node. */
+ vcons_t vcons = dir->nn->vcons;
+ int ref_vcons = 0;
+ assert (dir == vcons->dir_node);
+
+ if (!strcmp (name, "console"))
+ {
+ mutex_lock (&vcons->lock);
+ if (vcons->cons_node)
+ {
+ *node = vcons->cons_node;
+ netfs_nref (*node);
+ }
+ else
+ {
+ err = new_node (node, vcons, VCONS_NODE_CONSOLE);
+ if (!err)
+ {
+ vcons->cons_node = *node;
+ ref_vcons = 1;
+ }
+ }
+ mutex_unlock (&vcons->lock);
+ }
+ else if (!strcmp (name, "display"))
+ {
+ mutex_lock (&vcons->lock);
+ if (vcons->disp_node)
+ {
+ *node = vcons->disp_node;
+ netfs_nref (*node);
+ }
+ else
+ {
+ err = new_node (node, vcons, VCONS_NODE_DISPLAY);
+ if (!err)
+ {
+ vcons->disp_node = *node;
+ ref_vcons = 1;
+ }
+ }
+ mutex_unlock (&vcons->lock);
+ }
+ else if (!strcmp (name, "input"))
+ {
+ mutex_lock (&vcons->lock);
+ if (vcons->inpt_node)
+ {
+ *node = vcons->inpt_node;
+ netfs_nref (*node);
+ }
+ else
+ {
+ err = new_node (node, vcons, VCONS_NODE_INPUT);
+ if (!err)
+ {
+ vcons->inpt_node = *node;
+ ref_vcons = 1;
+ }
+ }
+ mutex_unlock (&vcons->lock);
+ }
+ else
+ err = ENOENT;
+
+ if (ref_vcons)
+ vcons_ref (vcons);
+ }
+
+ if (!err)
+ fshelp_touch (&dir->nn_stat, TOUCH_ATIME, console_maptime);
+
+ out:
+ mutex_unlock (&dir->lock);
+ if (err)
+ *node = 0;
+ else
+ mutex_lock (&(*node)->lock);
+
+ return err;
+}
+
+/* Returned directory entries are aligned to blocks this many bytes long.
+ Must be a power of two. */
+#define DIRENT_ALIGN 4
+#define DIRENT_NAME_OFFS offsetof (struct dirent, d_name)
+/* Length is structure before the name + the name + '\0', all
+ padded to a four-byte alignment. */
+#define DIRENT_LEN(name_len) \
+ ((DIRENT_NAME_OFFS + (name_len) + 1 + (DIRENT_ALIGN - 1)) \
+ & ~(DIRENT_ALIGN - 1))
+
+/* Implement the netfs_get_dirents callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_get_dirents (struct iouser *cred, struct node *dir,
+ int first_entry, int num_entries, char **data,
+ mach_msg_type_number_t *data_len,
+ vm_size_t max_data_len, int *data_entries)
+{
+ error_t err;
+ int count = 0;
+ size_t size = 0; /* Total size of our return block. */
+ struct vcons *first_vcons, *vcons;
+
+ /* Add the length of a directory entry for NAME to SIZE and return true,
+ unless it would overflow MAX_DATA_LEN or NUM_ENTRIES, in which case
+ return false. */
+ int bump_size (const char *name)
+ {
+ if (num_entries == -1 || count < num_entries)
+ {
+ size_t new_size = size + DIRENT_LEN (strlen (name));
+ if (max_data_len > 0 && new_size > max_data_len)
+ return 0;
+ size = new_size;
+ count++;
+ return 1;
+ }
+ else
+ return 0;
+ }
+
+ if (!dir->nn->cons && !(dir == dir->nn->vcons->dir_node))
+ return ENOTDIR;
+
+ if (dir->nn->cons)
+ {
+ mutex_lock (&dir->nn->cons->lock);
+
+ /* Find the first entry. */
+ for (first_vcons = dir->nn->cons->vcons_list, count = 2;
+ first_vcons && first_entry > count;
+ first_vcons = first_vcons->next)
+ count++;
+
+ count = 0;
+ }
+
+ /* Make space for the `.' and `..' entries. */
+ if (first_entry == 0)
+ bump_size (".");
+ if (first_entry <= 1)
+ bump_size ("..");
+
+ if (dir->nn->cons)
+ {
+ /* See how much space we need for the result. */
+ for (vcons = first_vcons; vcons; vcons = vcons->next)
+ if (!bump_size (vcons->name))
+ break;
+ }
+ else
+ {
+ if (first_entry <= 2)
+ bump_size ("console");
+ if (first_entry <= 3)
+ bump_size ("display");
+ if (first_entry <= 4)
+ bump_size ("input");
+ }
+
+ /* Allocate it. */
+ *data = mmap (0, size, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ err = ((void *) *data == (void *) -1) ? errno : 0;
+
+ if (! err)
+ /* Copy out the result. */
+ {
+ char *p = *data;
+
+ int add_dir_entry (const char *name, ino_t fileno, int type)
+ {
+ if (num_entries == -1 || count < num_entries)
+ {
+ struct dirent hdr;
+ size_t name_len = strlen (name);
+ size_t sz = DIRENT_LEN (name_len);
+
+ if (sz > size)
+ return 0;
+ else
+ size -= sz;
+
+ hdr.d_fileno = fileno;
+ hdr.d_reclen = sz;
+ hdr.d_type = type;
+ hdr.d_namlen = name_len;
+
+ memcpy (p, &hdr, DIRENT_NAME_OFFS);
+ strcpy (p + DIRENT_NAME_OFFS, name);
+ p += sz;
+
+ count++;
+
+ return 1;
+ }
+ else
+ return 0;
+ }
+
+ *data_len = size;
+ *data_entries = count;
+
+ count = 0;
+
+ if (dir->nn->cons)
+ {
+ /* Add `.' and `..' entries. */
+ if (first_entry == 0)
+ add_dir_entry (".", 2, DT_DIR);
+ if (first_entry <= 1)
+ add_dir_entry ("..", 2, DT_DIR);
+
+ /* Fill in the real directory entries. */
+ for (vcons = first_vcons; vcons; vcons = vcons->next)
+ if (!add_dir_entry (vcons->name,
+ vcons->id << 2, DT_DIR))
+ break;
+ mutex_unlock (&dir->nn->cons->lock);
+ }
+ else
+ {
+ /* Add `.' and `..' entries. */
+ if (first_entry == 0)
+ add_dir_entry (".", dir->nn_stat.st_ino, DT_DIR);
+ if (first_entry <= 1)
+ add_dir_entry ("..", 2, DT_DIR);
+
+ if (first_entry <= 2)
+ add_dir_entry ("console", (dir->nn->vcons->id << 2) + 1, DT_REG);
+ if (first_entry <= 3)
+ add_dir_entry ("display", (dir->nn->vcons->id << 2) + 2, DT_REG);
+ if (first_entry <= 4)
+ add_dir_entry ("input", (dir->nn->vcons->id << 3) + 2, DT_FIFO);
+ }
+ }
+
+ fshelp_touch (&dir->nn_stat, TOUCH_ATIME, console_maptime);
+ return err;
+}
+
+/* This should sync the entire remote filesystem. If WAIT is set, return
+ only after sync is completely finished. */
+error_t
+netfs_attempt_syncfs (struct iouser *cred, int wait)
+{
+ return 0;
+}
+
+/* This should attempt a chmod call for the user specified by CRED on node
+ NODE, to change the owner to UID and the group to GID. */
+error_t
+netfs_attempt_chown (struct iouser *cred, struct node *node,
+ uid_t uid, uid_t gid)
+{
+ cons_t cons = node->nn->cons;
+ vcons_t vcons;
+ error_t err;
+
+ if (!cons)
+ return EOPNOTSUPP;
+
+ err = file_chown (cons->underlying, uid, gid);
+ if (err)
+ return err;
+
+ /* Change NODE's owner. */
+ node->nn_stat.st_uid = uid;
+ node->nn_stat.st_gid = gid;
+
+ mutex_lock (&cons->lock);
+ cons->stat_template.st_uid = uid;
+ cons->stat_template.st_gid = gid;
+
+ /* Change the owner of each leaf node. */
+ for (vcons = cons->vcons_list; vcons; vcons = vcons->next)
+ {
+ if (vcons->dir_node)
+ {
+ vcons->dir_node->nn_stat.st_uid = uid;
+ vcons->dir_node->nn_stat.st_gid = gid;
+ }
+ if (vcons->cons_node)
+ {
+ vcons->cons_node->nn_stat.st_uid = uid;
+ vcons->cons_node->nn_stat.st_gid = gid;
+ }
+ if (vcons->disp_node)
+ {
+ vcons->disp_node->nn_stat.st_uid = uid;
+ vcons->disp_node->nn_stat.st_gid = gid;
+ }
+ if (vcons->inpt_node)
+ {
+ vcons->inpt_node->nn_stat.st_uid = uid;
+ vcons->inpt_node->nn_stat.st_gid = gid;
+ }
+ }
+ mutex_unlock (&cons->lock);
+ fshelp_touch (&node->nn_stat, TOUCH_CTIME, console_maptime);
+ return err;
+}
+
+/* This should attempt a chauthor call for the user specified by CRED on node
+ NODE, to change the author to AUTHOR. */
+error_t
+netfs_attempt_chauthor (struct iouser *cred, struct node *node, uid_t author)
+{
+ cons_t cons = node->nn->cons;
+ vcons_t vcons;
+ error_t err;
+
+ if (!cons)
+ return EOPNOTSUPP;
+
+ err = file_chauthor (cons->underlying, author);
+ if (err)
+ return err;
+
+ /* Change NODE's author. */
+ node->nn_stat.st_author = author;
+
+ mutex_lock (&cons->lock);
+ cons->stat_template.st_author = author;
+
+ /* Change the author of each leaf node. */
+ for (vcons = cons->vcons_list; vcons; vcons = vcons->next)
+ {
+ if (vcons->dir_node)
+ vcons->dir_node->nn_stat.st_author = author;
+ if (vcons->cons_node)
+ vcons->cons_node->nn_stat.st_author = author;
+ if (vcons->disp_node)
+ vcons->disp_node->nn_stat.st_author = author;
+ if (vcons->inpt_node)
+ vcons->inpt_node->nn_stat.st_author = author;
+ }
+ mutex_unlock (&cons->lock);
+ fshelp_touch (&node->nn_stat, TOUCH_CTIME, console_maptime);
+ return err;
+}
+
+/* This should attempt a chmod call for the user specified by CRED on node
+ NODE, to change the mode to MODE. Unlike the normal Unix and Hurd meaning
+ of chmod, this function is also used to attempt to change files into other
+ types. If such a transition is attempted which is impossible, then return
+ EOPNOTSUPP. */
+error_t
+netfs_attempt_chmod (struct iouser *cred, struct node *node, mode_t mode)
+{
+ error_t err;
+
+ mode &= ~S_ITRANS;
+ if ((mode & S_IFMT) == 0)
+ mode |= (node->nn_stat.st_mode & S_IFMT);
+
+ if (!node->nn->cons || ((mode & S_IFMT) != (node->nn_stat.st_mode & S_IFMT)))
+ return EOPNOTSUPP;
+
+ err = file_chmod (node->nn->cons->underlying, mode & ~S_IFMT);
+ if (err)
+ return err;
+
+ node->nn_stat.st_mode = mode;
+ fshelp_touch (&node->nn_stat, TOUCH_CTIME, console_maptime);
+ return err;
+}
+
+
+/* The user must define this function. Attempt to turn locked node NP
+ (user CRED) into a symlink with target NAME. */
+error_t
+netfs_attempt_mksymlink (struct iouser *cred, struct node *np, char *name)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_mkdev (struct iouser *cred, struct node *np,
+ mode_t type, dev_t indexes)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_chflags (struct iouser *cred, struct node *np, int flags)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_set_size (struct iouser *cred, struct node *np, off_t size)
+{
+ vcons_t vcons = np->nn->vcons;
+
+ if (!vcons || np == vcons->dir_node
+ || np == vcons->disp_node)
+ return EOPNOTSUPP;
+
+ assert (np == vcons->cons_node || np == vcons->inpt_node);
+ return 0;
+}
+
+error_t
+netfs_attempt_statfs (struct iouser *cred, struct node *np, struct statfs *st)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_mkdir (struct iouser *user, struct node *dir,
+ char *name, mode_t mode)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_unlink (struct iouser *user, struct node *dir, char *name)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_rename (struct iouser *user, struct node *fromdir,
+ char *fromname, struct node *todir,
+ char *toname, int excl)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_rmdir (struct iouser *user,
+ struct node *dir, char *name)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_link (struct iouser *user, struct node *dir,
+ struct node *file, char *name, int excl)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_mkfile (struct iouser *user, struct node *dir,
+ mode_t mode, struct node **np)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_readlink (struct iouser *user, struct node *np, char *buf)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+netfs_attempt_read (struct iouser *cred, struct node *np,
+ off_t offset, size_t *len, void *data)
+{
+ error_t err = 0;
+ vcons_t vcons = np->nn->vcons;
+ if (!vcons || np == vcons->dir_node
+ || np == vcons->inpt_node)
+ return EOPNOTSUPP;
+
+ mutex_unlock (&np->lock);
+ if (np == vcons->cons_node)
+ {
+ ssize_t amt = input_dequeue (vcons->input,
+ /* cred->po->openstat & O_NONBLOCK */ 0,
+ data, *len);
+ if (amt == -1)
+ err = errno;
+ else
+ *len = amt;
+ }
+ else
+ {
+ /* Pass display content to caller. */
+ ssize_t amt;
+ assert (np == vcons->disp_node);
+ amt = display_read (vcons->display,
+ /* cred->po->openstat & O_NONBLOCK */ 0,
+ offset, data, *len);
+ if (amt == -1)
+ err = errno;
+ else
+ *len = amt;
+ }
+ mutex_lock (&np->lock);
+ return err;
+}
+
+error_t
+netfs_attempt_write (struct iouser *cred, struct node *np,
+ off_t offset, size_t *len, void *data)
+{
+ error_t err = 0;
+ vcons_t vcons = np->nn->vcons;
+ if (!vcons || np == vcons->dir_node
+ || np == vcons->disp_node)
+ return EOPNOTSUPP;
+
+ mutex_unlock (&np->lock);
+ if (np == vcons->cons_node)
+ {
+ /* The term server is writing to the console device. Feed the
+ data into the screen matrix display. */
+ int amt = display_output (vcons->display,
+ /* cred->po->openstat & O_NONBLOCK */ 0,
+ data, *len);
+ if (amt == -1)
+ err = errno;
+ else
+ *len = amt;
+ }
+ else
+ {
+ int amt;
+ /* The input driver is writing to the input device. Feed the
+ data into the input queue. */
+ assert (np == vcons->inpt_node);
+
+ amt = input_enqueue (vcons->input,
+ /* cred->po->openstat & O_NONBLOCK */ 1,
+ data, *len);
+ if (amt == -1)
+ err = errno;
+ else
+ *len = amt;
+ }
+ mutex_lock (&np->lock);
+ return err;
+}
+
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+ struct stat ul_stat;
+ struct netnode root_nn = { cons: cons, vcons: 0 };
+
+ mtrace();
+ struct argp argp
+ = { NULL, NULL, NULL,
+ "A translator that provides virtual console displays." };
+
+ /* Parse our command line arguments (all none of them). */
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ netfs_init ();
+
+ /* Create the root node (some attributes initialized below). */
+ netfs_root_node = netfs_make_node (&root_nn);
+ if (! netfs_root_node)
+ error (5, ENOMEM, "Cannot create root node");
+
+ err = maptime_map (0, 0, &console_maptime);
+ if (err)
+ error (6, err, "Cannot map time");
+
+ mutex_init (&cons->lock);
+ cons->encoding = "ISO-8859-1";
+ cons->node = netfs_root_node;
+ cons->underlying = netfs_startup (bootstrap, O_READ);
+ if (cons->underlying == MACH_PORT_NULL)
+ error (5, err, "Cannot get underlying node");
+
+ err = io_stat (cons->underlying, &ul_stat);
+ if (err)
+ error (6, err, "Cannot stat underlying node");
+
+ /* CONS.stat_template contains some fields that are inherited by all
+ nodes we create. */
+ cons->stat_template.st_uid = ul_stat.st_uid;
+ cons->stat_template.st_gid = ul_stat.st_gid;
+ cons->stat_template.st_author = ul_stat.st_author;
+ cons->stat_template.st_mode = (ul_stat.st_mode & ~S_IFMT & ~S_ITRANS);
+ cons->stat_template.st_fsid = getpid ();
+ cons->stat_template.st_nlink = 1;
+ cons->stat_template.st_fstype = FSTYPE_MISC;
+
+ /* Initialize the root node's stat information. */
+ netfs_root_node->nn_stat = cons->stat_template;
+ netfs_root_node->nn_stat.st_ino = 2;
+ netfs_root_node->nn_stat.st_mode |= S_IFDIR;
+ netfs_root_node->nn_translated = 0;
+
+ /* If the underlying node isn't a directory, propagate read permission to
+ execute permission since we need that for lookups. */
+ if (! S_ISDIR (ul_stat.st_mode))
+ {
+ if (ul_stat.st_mode & S_IRUSR)
+ netfs_root_node->nn_stat.st_mode |= S_IXUSR;
+ if (ul_stat.st_mode & S_IRGRP)
+ netfs_root_node->nn_stat.st_mode |= S_IXGRP;
+ if (ul_stat.st_mode & S_IROTH)
+ netfs_root_node->nn_stat.st_mode |= S_IXOTH;
+ }
+
+ fshelp_touch (&netfs_root_node->nn_stat, TOUCH_ATIME|TOUCH_MTIME|TOUCH_CTIME,
+ console_maptime);
+
+ netfs_server_loop (); /* Never returns. */
+
+ /*NOTREACHED*/
+ return 0;
+}
diff --git a/console/console.h b/console/console.h
new file mode 100644
index 00000000..3642bfe4
--- /dev/null
+++ b/console/console.h
@@ -0,0 +1,35 @@
+/* console.h -- Interfaces for the console server.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef CONSOLE_H
+#define CONSOLE_H
+
+#include <hurd/netfs.h>
+#include <rwlock.h>
+#include <maptime.h>
+
+/* Handy source of time. */
+volatile struct mapped_time_value *console_maptime;
+
+/* A handle for a console device. */
+typedef struct cons *cons_t;
+
+/* A handle for a virtual console device. */
+typedef struct vcons *vcons_t;
+
+#endif /* CONSOLE_H */
diff --git a/console/display.c b/console/display.c
index 363c9c09..d2c0b590 100644
--- a/console/display.c
+++ b/console/display.c
@@ -57,6 +57,20 @@ struct screen
};
typedef struct screen *screen_t;
+struct cursor
+{
+ /* The visibility of the cursor. */
+ int status;
+#define CURSOR_INVISIBLE 1
+#define CURSOR_STANDOUT 2
+
+ size_t x;
+ size_t y;
+ size_t saved_x;
+ size_t saved_y;
+};
+typedef struct cursor *cursor_t;
+
struct parse
{
/* The parsing state of output characters, needed to handle escape
@@ -76,47 +90,53 @@ struct parse
int params[PARSE_MAX_PARAMS];
int nparams;
};
+typedef struct parse *parse_t;
-struct cursor
+struct output
{
- /* The visibility of the cursor. */
- int status;
-#define CURSOR_INVISIBLE 1
-#define CURSOR_STANDOUT 2
+ /* The state of the conversion of output characters. */
+ iconv_t cd;
+ /* The output queue holds the characters that are to be outputted.
+ The conversion routine might refuse to handle some incomplete
+ multi-byte or composed character at the end of the buffer, so we
+ have to keep them around. */
+ int stopped;
+ struct condition resumed;
+ char *buffer;
+ size_t allocated;
+ size_t size;
+
+ /* The parsing state of output characters. */
+ struct parse parse;
+};
+typedef struct output *output_t;
- size_t x;
- size_t y;
- size_t saved_x;
- size_t saved_y;
+struct attr
+{
+ /* Current attribute. */
+ char current;
+ int fg;
+ int bg;
+ int def_fg;
+ int def_bg;
+ int reverse : 1;
+ int bold : 1;
+ int blink : 1;
+ int invisible : 1;
+ int dim : 1;
+ int underline : 1;
};
+typedef struct attr *attr_t;
struct display
{
/* The lock for the virtual console display structure. */
struct mutex lock;
- /* The state of the conversion of output characters. */
- iconv_t cd;
-
- struct parse parse;
struct screen screen;
struct cursor cursor;
-
- struct
- {
- /* Current attribute. */
- char current;
- int fg;
- int bg;
- int def_fg;
- int def_bg;
- int reverse : 1;
- int bold : 1;
- int blink : 1;
- int invisible : 1;
- int dim : 1;
- int underline : 1;
- } attr;
+ struct output output;
+ struct attr attr;
/* Indicates if OWNER_ID is initialized. */
int has_owner;
@@ -202,7 +222,7 @@ static void
screen_scroll_left (screen_t screen, size_t x, size_t y, size_t w, size_t h,
int amt, wchar_t chr, char attr)
{
- int y;
+ int i;
wchar_t *matrixp = screen->matrix + y * screen->width + x;
if (amt < 0)
@@ -210,9 +230,9 @@ screen_scroll_left (screen_t screen, size_t x, size_t y, size_t w, size_t h,
if (amt > w)
amt = w;
- for (y = 0; y < y + h; y++)
+ for (i = 0; i < y + h; i++)
{
- wmemmov (matrixp, matrixp + amt, w - amt);
+ wmemmove (matrixp, matrixp + amt, w - amt);
matrixp += screen->width;
}
screen_fill (screen, x + w - amt, y, amt, h, chr, attr);
@@ -222,7 +242,7 @@ static void
screen_scroll_right (screen_t screen, size_t x, size_t y, size_t w, size_t h,
int amt, wchar_t chr, char attr)
{
- int y;
+ int i;
wchar_t *matrixp = screen->matrix + y * screen->width + x;
if (amt < 0)
@@ -230,152 +250,131 @@ screen_scroll_right (screen_t screen, size_t x, size_t y, size_t w, size_t h,
if (amt > w)
amt = w;
- for (y = 0; y < y + h; y++)
+ for (i = 0; i < y + h; i++)
{
- wmemmov (matrixp + amt, matrixp, w - amt);
+ wmemmove (matrixp + amt, matrixp, w - amt);
matrixp += screen->width;
}
screen_fill (screen, x, y, amt, h, chr, attr);
}
-/* Create a new virtual console display, with the system encoding
- being ENCODING. */
-error_t
-display_create (display_t *r_display, const char *encoding)
+static error_t
+output_init (output_t output, const char *encoding)
{
- error_t err = 0;
- display_t display;
-
- *r_display = NULL;
- display = calloc (1, sizeof *display);
- if (!display)
- return ENOMEM;
-
- mutex_init (&display->lock);
- err = screen_init (&display->screen);
- if (err)
- {
- free (display);
- return err;
- }
+ condition_init (&output->resumed);
+ output->stopped = 0;
+ output->buffer = NULL;
+ output->allocated = 0;
+ output->size = 0;
/* WCHAR_T happens to be UCS-4 on the GNU system. */
- display->cd = iconv_open ("WCHAR_T", encoding);
- if (display->cd == (iconv_t) -1)
- {
- err = errno;
- screen_deinit (&display->screen);
- free (display);
- }
- *r_display = display;
- return err;
+ output->cd = iconv_open ("WCHAR_T", encoding);
+ if (output->cd == (iconv_t) -1)
+ return errno;
+ return 0;
}
-
-/* Destroy the display DISPLAY. */
-void
-display_destroy (display_t display)
+static void
+output_deinit (output_t output)
{
- iconv_close (display->cd);
- screen_deinit (&display->screen);
- free (display);
+ iconv_close (output->cd);
}
static void
-handle_esc_bracket_hl (display_t display, int code, int flag)
+handle_esc_bracket_hl (cursor_t cursor, int code, int flag)
{
switch (code)
{
case 34:
/* Cursor standout: <cnorm>, <cvvis>. */
if (flag)
- display->cursor.status |= CURSOR_STANDOUT;
+ cursor->status |= CURSOR_STANDOUT;
else
- display->cursor.status &= ~CURSOR_STANDOUT;
+ cursor->status &= ~CURSOR_STANDOUT;
/* XXX Flag cursor status change. */
break;
}
}
static void
-handle_esc_bracket_m (display_t display, int code)
+handle_esc_bracket_m (attr_t attr, int code)
{
switch (code)
{
case 0:
/* All attributes off: <sgr0>. */
- display->attr.fg = display->attr.def_fg;
- display->attr.bg = display->attr.def_bg;
- display->attr.reverse = display->attr.bold = display->attr.blink
- = display->attr.invisible = display->attr.dim
- = display->attr.underline = 0;
+ attr->fg = attr->def_fg;
+ attr->bg = attr->def_bg;
+ attr->reverse = attr->bold = attr->blink
+ = attr->invisible = attr->dim
+ = attr->underline = 0;
/* Cursor attributes aren't text attributes. */
break;
case 1:
/* Bold on: <bold>. */
- display->attr.bold = 1;
+ attr->bold = 1;
break;
case 2:
/* Dim on: <dim>. */
- display->attr.dim = 1;
+ attr->dim = 1;
break;
case 4:
/* Underline on: <smul>. */
- display->attr.underline = 1;
+ attr->underline = 1;
break;
case 5:
/* Blink on: <blink>. */
- display->attr.blink = 1;
+ attr->blink = 1;
break;
case 7:
/* Reverse video on: <rev>, <smso>. */
- display->attr.reverse = 1;
+ attr->reverse = 1;
break;
case 8:
/* Concealed on: <invis>. */
- display->attr.invisible = 1;
+ attr->invisible = 1;
break;
case 21:
/* Bold Off. */
- display->attr.bold = 0;
+ attr->bold = 0;
break;
case 22:
/* Dim off. */
- display->attr.dim = 0;
+ attr->dim = 0;
break;
case 24:
/* Underline off: <rmul>. */
- display->attr.underline = 0;
+ attr->underline = 0;
break;
case 25:
/* Blink off. */
- display->attr.blink = 0;
+ attr->blink = 0;
break;
case 27:
/* Reverse video off: <rmso>. */
- display->attr.reverse = 0;
+ attr->reverse = 0;
break;
case 28:
/* Concealed off. */
- display->attr.invisible = 0;
+ attr->invisible = 0;
break;
case 30 ... 37:
/* Set foreground color: <setaf>. */
- display->attr.fg = code - 30;
+ attr->fg = code - 30;
break;
case 39:
/* Default foreground color; ANSI?. */
- display->attr.fg = display->attr.def_fg;
+ attr->fg = attr->def_fg;
break;
case 40 ... 47:
/* Set background color: <setab>. */
- display->attr.bg = code - 40;
+ attr->bg = code - 40;
break;
case 49:
/* Default background color; ANSI?. */
- display->attr.bg = display->attr.def_bg;
+ attr->bg = attr->def_bg;
break;
}
/* XXX */
@@ -385,6 +384,7 @@ handle_esc_bracket_m (display_t display, int code)
static void
handle_esc_bracket (display_t display, char op)
{
+ parse_t parse = &display->output.parse;
int i;
static void limit_cursor (void)
@@ -407,13 +407,13 @@ handle_esc_bracket (display_t display, char op)
case 'H':
case 'f':
/* Cursor position: <cup>. */
- display->cursor.x = display->parse.params[1] - 1;
- display->cursor.y = display->parse.params[0] - 1;
+ display->cursor.x = parse->params[1] - 1;
+ display->cursor.y = parse->params[0] - 1;
limit_cursor ();
break;
case 'G':
/* Horizontal position: <hpa>. */
- display->cursor.x = display->parse.params[0] - 1;
+ display->cursor.x = parse->params[0] - 1;
limit_cursor ();
break;
case 'F':
@@ -422,7 +422,7 @@ handle_esc_bracket (display_t display, char op)
/* fall through */
case 'A':
/* Cursor up: <cuu>, <cuu1>. */
- display->cursor.y -= (display->parse.params[0] ?: 1);
+ display->cursor.y -= (parse->params[0] ?: 1);
limit_cursor ();
break;
case 'E':
@@ -431,17 +431,17 @@ handle_esc_bracket (display_t display, char op)
/* Fall through. */
case 'B':
/* Cursor down: <cud1>, <cud>. */
- display->cursor.y += (display->parse.params[0] ?: 1);
+ display->cursor.y += (parse->params[0] ?: 1);
limit_cursor ();
break;
case 'C':
/* Cursor right: <cuf1>, <cuf>. */
- display->cursor.x += (display->parse.params[0] ?: 1);
+ display->cursor.x += (parse->params[0] ?: 1);
limit_cursor ();
break;
case 'D':
/* Cursor left: <cub>, <cub1>. */
- display->cursor.x -= (display->parse.params[0] ?: 1);
+ display->cursor.x -= (parse->params[0] ?: 1);
limit_cursor ();
break;
case 's':
@@ -458,20 +458,20 @@ handle_esc_bracket (display_t display, char op)
break;
case 'h':
/* Reset mode. */
- for (i = 0; i < display->parse.nparams; i++)
- handle_esc_bracket_hl (display, display->parse.params[i], 0);
+ for (i = 0; i < parse->nparams; i++)
+ handle_esc_bracket_hl (&display->cursor, parse->params[i], 0);
break;
case 'l':
/* Set mode. */
- for (i = 0; i < display->parse.nparams; i++)
- handle_esc_bracket_hl (display, display->parse.params[i], 1);
+ for (i = 0; i < parse->nparams; i++)
+ handle_esc_bracket_hl (&display->cursor, parse->params[i], 1);
break;
case 'm':
- for (i = 0; i < display->parse.nparams; i++)
- handle_esc_bracket_m (display, display->parse.params[i]);
+ for (i = 0; i < parse->nparams; i++)
+ handle_esc_bracket_m (&display->attr, parse->params[i]);
break;
case 'J':
- switch (display->parse.params[0])
+ switch (parse->params[0])
{
case 0:
/* Clear to end of screen: <ed>. */
@@ -501,7 +501,7 @@ handle_esc_bracket (display_t display, char op)
}
break;
case 'K':
- switch (display->parse.params[0])
+ switch (parse->params[0])
{
case 0:
/* Clear to end of line: <el>. */
@@ -528,7 +528,7 @@ handle_esc_bracket (display_t display, char op)
screen_scroll_down (&display->screen, 0, display->cursor.y,
display->screen.width,
display->screen.height - display->cursor.y,
- display->parse.params[0] ?: 1,
+ parse->params[0] ?: 1,
L' ', display->attr.current);
break;
case 'M':
@@ -536,7 +536,7 @@ handle_esc_bracket (display_t display, char op)
screen_scroll_up (&display->screen, 0, display->cursor.y,
display->screen.width,
display->screen.height - display->cursor.y,
- display->parse.params[0] ?: 1,
+ parse->params[0] ?: 1,
L' ', display->attr.current);
break;
case '@':
@@ -544,7 +544,7 @@ handle_esc_bracket (display_t display, char op)
screen_scroll_right (&display->screen, display->cursor.x,
display->cursor.y,
display->screen.width - display->cursor.x, 1,
- display->parse.params[0] ?: 1,
+ parse->params[0] ?: 1,
L' ', display->attr.current);
break;
case 'P':
@@ -552,27 +552,27 @@ handle_esc_bracket (display_t display, char op)
screen_scroll_left (&display->screen, display->cursor.x,
display->cursor.y,
display->screen.width - display->cursor.x, 1,
- display->parse.params[0] ?: 1,
+ parse->params[0] ?: 1,
L' ', display->attr.current);
break;
case 'S':
/* Scroll up: <ind>, <indn>. */
screen_scroll_up (&display->screen, 0, 0,
display->screen.width, display->screen.height,
- display->parse.params[0] ?: 1,
+ parse->params[0] ?: 1,
L' ', display->attr.current);
break;
case 'T':
/* Scroll down: <ri>, <rin>. */
screen_scroll_down (&display->screen, 0, 0,
display->screen.width, display->screen.height,
- display->parse.params[0] ?: 1,
+ parse->params[0] ?: 1,
L' ', display->attr.current);
break;
case 'X':
/* Erase character(s): <ech>. */
screen_fill (&display->screen, display->cursor.x, display->cursor.y,
- display->parse.params[0] ?: 1, 1,
+ parse->params[0] ?: 1, 1,
L' ', display->attr.current);
break;
}
@@ -598,18 +598,20 @@ handle_esc_bracket_question_hl (display_t display, int code, int flag)
static void
handle_esc_bracket_question (display_t display, char op)
{
+ parse_t parse = &display->output.parse;
+
int i;
switch (op)
{
case 'h':
/* Reset mode. */
- for (i = 0; i < display->parse.nparams; ++i)
- handle_esc_bracket_question_hl (display, display->parse.params[i], 0);
+ for (i = 0; i < parse->nparams; ++i)
+ handle_esc_bracket_question_hl (display, parse->params[i], 0);
break;
case 'l':
/* Set mode. */
- for (i = 0; i < display->parse.nparams; ++i)
- handle_esc_bracket_question_hl (display, display->parse.params[i], 1);
+ for (i = 0; i < parse->nparams; ++i)
+ handle_esc_bracket_question_hl (display, parse->params[i], 1);
break;
}
}
@@ -618,6 +620,8 @@ handle_esc_bracket_question (display_t display, char op)
static void
display_output_one (display_t display, wchar_t chr)
{
+ parse_t parse = &display->output.parse;
+
void newline (void)
{
if (display->cursor.y < display->screen.height - 1)
@@ -643,7 +647,7 @@ display_output_one (display_t display, wchar_t chr)
}
}
- switch (display->parse.state)
+ switch (parse->state)
{
case STATE_NORMAL:
switch (chr)
@@ -687,7 +691,7 @@ display_output_one (display_t display, wchar_t chr)
/* XXX Flag cursor update. */
break;
case L'\033':
- display->parse.state = STATE_ESC;
+ parse->state = STATE_ESC;
break;
case L'\0':
/* Padding character: <pad>. */
@@ -702,7 +706,7 @@ display_output_one (display_t display, wchar_t chr)
+ display->cursor.x] = chr;
display->cursor.x++;
- if (display->cursor.x == display->screen.height)
+ if (display->cursor.x == display->screen.width)
{
display->cursor.x = 0;
newline ();
@@ -716,7 +720,7 @@ display_output_one (display_t display, wchar_t chr)
switch (chr)
{
case L'[':
- display->parse.state = STATE_ESC_BRACKET_INIT;
+ parse->state = STATE_ESC_BRACKET_INIT;
break;
case L'c':
/* Clear screen and home cursor: <clear>. */
@@ -725,44 +729,44 @@ display_output_one (display_t display, wchar_t chr)
L' ', display->attr.current);
display->cursor.x = display->cursor.y = 0;
/* XXX Flag cursor change. */
- display->parse.state = STATE_NORMAL;
+ parse->state = STATE_NORMAL;
break;
default:
/* Unsupported escape sequence. */
- display->parse.state = STATE_NORMAL;
+ parse->state = STATE_NORMAL;
break;
}
break;
case STATE_ESC_BRACKET_INIT:
- memset (&display->parse.params, 0, sizeof display->parse.params);
- display->parse.nparams = 0;
+ memset (&parse->params, 0, sizeof parse->params);
+ parse->nparams = 0;
if (chr == '?')
{
- display->parse.state = STATE_ESC_BRACKET_QUESTION;
+ parse->state = STATE_ESC_BRACKET_QUESTION;
break; /* Consume the question mark. */
}
else
- display->parse.state = STATE_ESC_BRACKET;
+ parse->state = STATE_ESC_BRACKET;
/* Fall through. */
case STATE_ESC_BRACKET:
case STATE_ESC_BRACKET_QUESTION:
if (chr >= '0' && chr <= '9')
- display->parse.params[display->parse.nparams]
- = display->parse.params[display->parse.nparams]*10 + chr - '0';
+ parse->params[parse->nparams]
+ = parse->params[parse->nparams]*10 + chr - '0';
else if (chr == ';')
{
- if (++(display->parse.nparams) >= PARSE_MAX_PARAMS)
- display->parse.state = STATE_NORMAL; /* too many */
+ if (++(parse->nparams) >= PARSE_MAX_PARAMS)
+ parse->state = STATE_NORMAL; /* too many */
}
else
{
- display->parse.nparams++;
- if (display->parse.state == STATE_ESC_BRACKET)
+ parse->nparams++;
+ if (parse->state == STATE_ESC_BRACKET)
handle_esc_bracket (display, chr);
else
handle_esc_bracket_question (display, chr);
- display->parse.state = STATE_NORMAL;
+ parse->state = STATE_NORMAL;
}
break;
default:
@@ -770,31 +774,29 @@ display_output_one (display_t display, wchar_t chr)
}
}
-
/* Output LENGTH bytes starting from BUFFER in the system encoding.
Set BUFFER and LENGTH to the new values. The exact semantics are
just as in the iconv interface. */
-error_t
-display_output (display_t display, char **buffer, size_t *length)
+static error_t
+display_output_some (display_t display, char **buffer, size_t *length)
{
#define CONV_OUTBUF_SIZE 256
error_t err = 0;
- mutex_lock (&display->lock);
while (!err && *length > 0)
{
size_t nconv;
wchar_t outbuf[CONV_OUTBUF_SIZE];
char *outptr = (char *) outbuf;
- size_t outsize = CONV_OUTBUF_SIZE;
+ size_t outsize = CONV_OUTBUF_SIZE * sizeof (wchar_t);
error_t saved_err;
int i;
- nconv = iconv (display->cd, buffer, length, &outptr, &outsize);
+ nconv = iconv (display->output.cd, buffer, length, &outptr, &outsize);
saved_err = errno;
/* First process all successfully converted characters. */
- for (i = 0; i < CONV_OUTBUF_SIZE - outsize; i++)
+ for (i = 0; i < CONV_OUTBUF_SIZE - outsize / sizeof (wchar_t); i++)
display_output_one (display, outbuf[i]);
if (nconv == (size_t) -1)
@@ -808,11 +810,52 @@ display_output (display_t display, char **buffer, size_t *length)
err = saved_err;
}
}
- mutex_unlock (&display->lock);
return err;
}
-
+/* Create a new virtual console display, with the system encoding
+ being ENCODING. */
+error_t
+display_create (display_t *r_display, const char *encoding)
+{
+ error_t err = 0;
+ display_t display;
+
+ *r_display = NULL;
+ display = calloc (1, sizeof *display);
+ if (!display)
+ return ENOMEM;
+
+ mutex_init (&display->lock);
+ err = screen_init (&display->screen);
+ if (err)
+ {
+ free (display);
+ return err;
+ }
+
+ err = output_init (&display->output, encoding);
+ if (err)
+ {
+ screen_deinit (&display->screen);
+ free (display);
+ }
+ *r_display = display;
+ return err;
+}
+
+
+/* Destroy the display DISPLAY. */
+void
+display_destroy (display_t display)
+{
+ output_deinit (&display->output);
+ screen_deinit (&display->screen);
+ free (display);
+}
+
+
+/* Return the dimensions of the display DISPLAY in *WINSIZE. */
void
display_getsize (display_t display, struct winsize *winsize)
{
@@ -823,3 +866,190 @@ display_getsize (display_t display, struct winsize *winsize)
winsize->ws_ypixel = 0;
mutex_unlock (&display->lock);
}
+
+
+/* Set the owner of the display DISPLAY to PID. The owner receives
+ the SIGWINCH signal when the terminal size changes. */
+error_t
+display_set_owner (display_t display, pid_t pid)
+{
+ mutex_lock (&display->lock);
+ display->has_owner = 1;
+ display->owner_id = pid;
+ mutex_unlock (&display->lock);
+ return 0;
+}
+
+
+/* Return the owner of the display DISPLAY in PID. If there is no
+ owner, return ENOTTY. */
+error_t
+display_get_owner (display_t display, pid_t *pid)
+{
+ error_t err = 0;
+ mutex_lock (&display->lock);
+ if (!display->has_owner)
+ err = ENOTTY;
+ else
+ *pid = display->owner_id;
+ mutex_unlock (&display->lock);
+ return err;
+}
+
+/* Output DATALEN characters from the buffer DATA on display DISPLAY.
+ The DATA must be supplied in the system encoding configured for
+ DISPLAY. The function returns the amount of bytes written (might
+ be smaller than DATALEN) or -1 and the error number in errno. If
+ NONBLOCK is not zero, return with -1 and set errno to EWOULDBLOCK
+ if operation would block for a long time. */
+ssize_t
+display_output (display_t display, int nonblock, char *data, size_t datalen)
+{
+ output_t output = &display->output;
+ error_t err;
+ char *buffer;
+ size_t buffer_size;
+ ssize_t amount;
+
+ error_t ensure_output_buffer_size (size_t new_size)
+ {
+ /* Must be a power of two. */
+#define OUTPUT_ALLOCSIZE 32
+
+ if (output->allocated < new_size)
+ {
+ char *new_buffer;
+ new_size = (new_size + OUTPUT_ALLOCSIZE - 1)
+ & ~(OUTPUT_ALLOCSIZE - 1);
+ new_buffer = realloc (output->buffer, new_size);
+ if (!new_buffer)
+ return ENOMEM;
+ output->buffer = new_buffer;
+ output->allocated = new_size;
+ }
+ return 0;
+ }
+
+ mutex_lock (&display->lock);
+ while (output->stopped)
+ {
+ if (nonblock)
+ {
+ mutex_unlock (&display->lock);
+ errno = EWOULDBLOCK;
+ return -1;
+ }
+ if (hurd_condition_wait (&output->resumed, &display->lock))
+ {
+ mutex_unlock (&display->lock);
+ errno = EINTR;
+ return -1;
+ }
+ }
+
+ if (output->size)
+ {
+ err = ensure_output_buffer_size (output->size + datalen);
+ if (err)
+ {
+ mutex_unlock (&display->lock);
+ errno = ENOMEM;
+ return -1;
+ }
+ buffer = output->buffer;
+ buffer_size = output->size;
+ memcpy (buffer + buffer_size, data, datalen);
+ buffer_size += datalen;
+ }
+ else
+ {
+ buffer = data;
+ buffer_size = datalen;
+ }
+ amount = buffer_size;
+ err = display_output_some (display, &buffer, &buffer_size);
+ amount -= buffer_size;
+
+ if (err && !amount)
+ {
+ mutex_unlock (&display->lock);
+ errno = err;
+ return err;
+ }
+ /* XXX What should be done with invalid characters etc? */
+ if (buffer_size)
+ {
+ /* If we used the caller's buffer DATA, the remaining bytes
+ might not fit in our internal output buffer. In this case we
+ can reallocate the buffer in VCONS without needing to update
+ OUTPUT (as it points into DATA). */
+ err = ensure_output_buffer_size (buffer_size);
+ if (err)
+ {
+ mutex_unlock (&display->lock);
+ return err;
+ }
+ memmove (output->buffer, buffer, buffer_size);
+ }
+ output->size = buffer_size;
+ amount += buffer_size;
+
+ mutex_unlock (&display->lock);
+ return amount;
+}
+
+ssize_t display_read (display_t display, int nonblock, off_t off,
+ char *data, size_t len)
+{
+ mutex_lock (&display->lock);
+ if (off + len > 2000 * sizeof(wchar_t))
+ len = 2000 * sizeof(wchar_t) - off;
+ memcpy (data, (char *) display->screen.matrix + off, len);
+ mutex_unlock (&display->lock);
+ return len;
+}
+
+/* Resume the output on the display DISPLAY. */
+void
+display_start_output (display_t display)
+{
+ mutex_lock (&display->lock);
+ if (display->output.stopped)
+ {
+ display->output.stopped = 0;
+ condition_broadcast (&display->output.resumed);
+ }
+ mutex_unlock (&display->lock);
+}
+
+
+/* Stop all output on the display DISPLAY. */
+void
+display_stop_output (display_t display)
+{
+ mutex_lock (&display->lock);
+ display->output.stopped = 1;
+ mutex_unlock (&display->lock);
+}
+
+
+/* Return the number of pending output bytes for DISPLAY. */
+size_t
+display_pending_output (display_t display)
+{
+ int output_size;
+ mutex_lock (&display->lock);
+ output_size = display->output.size;
+ mutex_unlock (&display->lock);
+ return output_size;
+}
+
+
+/* Flush the output buffer, discarding all pending data. */
+void
+display_discard_output (display_t display)
+{
+ mutex_lock (&display->lock);
+ display->output.size = 0;
+ mutex_unlock (&display->lock);
+}
diff --git a/console/display.h b/console/display.h
index 0043fb3e..6762e1b7 100644
--- a/console/display.h
+++ b/console/display.h
@@ -26,9 +26,46 @@
struct display;
typedef struct display *display_t;
+/* Create a new virtual console display, with the system encoding
+ being ENCODING. */
error_t display_create (display_t *r_display, const char *encoding);
+
+/* Destroy the display DISPLAY. */
void display_destroy (display_t display);
-error_t display_output (display_t display, char **buffer, size_t *length);
+
+/* Return the dimensions of the display DISPLAY in *WINSIZE. */
void display_getsize (display_t display, struct winsize *winsize);
+/* Set the owner of the display DISPLAY to PID. The owner receives
+ the SIGWINCH signal when the terminal size changes. */
+error_t display_set_owner (display_t display, pid_t pid);
+
+/* Return the owner of the display DISPLAY in PID. If there is no
+ owner, return ENOTTY. */
+error_t display_get_owner (display_t display, pid_t *pid);
+
+/* Output DATALEN characters from the buffer DATA on display DISPLAY.
+ The DATA must be supplied in the system encoding configured for
+ DISPLAY. The function returns the amount of bytes written (might
+ be smaller than DATALEN) or -1 and the error number in errno. If
+ NONBLOCK is not zero, return with -1 and set errno to EWOULDBLOCK
+ if operation would block for a long time. */
+ssize_t display_output (display_t display, int nonblock, char *data,
+ size_t datalen);
+
+ssize_t display_read (display_t display, int nonblock, off_t off,
+ char *data, size_t len);
+
+/* Resume the output on the display DISPLAY. */
+void display_start_output (display_t display);
+
+/* Stop all output on the display DISPLAY. */
+void display_stop_output (display_t display);
+
+/* Return the number of pending output bytes for DISPLAY. */
+size_t display_pending_output (display_t display);
+
+/* Flush the output buffer, discarding all pending data. */
+void display_discard_output (display_t display);
+
#endif /* DISPLAY_H */
diff --git a/console/input.c b/console/input.c
new file mode 100644
index 00000000..e27d30c8
--- /dev/null
+++ b/console/input.c
@@ -0,0 +1,277 @@
+/* input.c - Input component of a virtual console.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ 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 <iconv.h>
+#include <error.h>
+#include <string.h>
+#include <errno.h>
+#include <malloc.h>
+#include <sys/types.h>
+
+#include <cthreads.h>
+
+#include "input.h"
+
+struct input
+{
+ struct mutex lock;
+
+ struct condition data_available;
+ struct condition space_available;
+#define INPUT_QUEUE_SIZE 300
+ char buffer[INPUT_QUEUE_SIZE];
+ int full;
+ size_t size;
+
+ /* The state of the conversion of input characters. */
+ iconv_t cd;
+ /* The conversion routine might refuse to handle some incomplete
+ multi-byte or composed character at the end of the buffer, so we
+ have to keep them around. */
+ char *cd_buffer;
+ size_t cd_size;
+ size_t cd_allocated;
+};
+
+/* Create a new virtual console input queue, with the system encoding
+ being ENCODING. */
+error_t input_create (input_t *r_input, const char *encoding)
+{
+ input_t input = calloc (1, sizeof *input);
+ if (!input)
+ return ENOMEM;
+
+ mutex_init (&input->lock);
+ condition_init (&input->data_available);
+ condition_init (&input->space_available);
+
+ input->cd = iconv_open ("UTF-8", encoding);
+ if (input->cd == (iconv_t) -1)
+ {
+ free (input);
+ return errno;
+ }
+
+ *r_input = input;
+ return 0;
+}
+
+/* Destroy the input queue INPUT. */
+void input_destroy (input_t input)
+{
+ iconv_close (input->cd);
+ free (input);
+}
+
+/* Enter DATALEN characters from the buffer DATA into the input queue
+ INPUT. The DATA must be supplied in UTF-8 encoding (XXX UCS-4
+ would be nice, too, but it requires knowledge of endianess). The
+ function returns the amount of bytes written (might be smaller than
+ DATALEN) or -1 and the error number in errno. If NONBLOCK is not
+ zero, return with -1 and set errno to EWOULDBLOCK if operation
+ would block for a long time. */
+ssize_t input_enqueue (input_t input, int nonblock, char *data,
+ size_t datalen)
+{
+ error_t err;
+ int was_empty;
+ int enqueued = 0;
+ char *buffer;
+ size_t buffer_size;
+ ssize_t amount;
+ size_t nconv;
+ char *outbuf;
+ size_t outbuf_size;
+
+ error_t ensure_cd_buffer_size (size_t new_size)
+ {
+ /* Must be a power of two. */
+#define CD_ALLOCSIZE 32
+
+ if (input->cd_allocated < new_size)
+ {
+ char *new_buffer;
+ new_size = (new_size + CD_ALLOCSIZE - 1)
+ & ~(CD_ALLOCSIZE - 1);
+ new_buffer = realloc (input->cd_buffer, new_size);
+ if (!new_buffer)
+ return ENOMEM;
+ input->cd_buffer = new_buffer;
+ input->cd_allocated = new_size;
+ }
+ return 0;
+ }
+
+ mutex_lock (&input->lock);
+ was_empty = !input->size;
+
+ while (datalen)
+ {
+ /* Make sure we are ready for writing (or at least can make a
+ honest attempt at it). */
+ while (input->full)
+ {
+ if (nonblock)
+ {
+ err = EWOULDBLOCK;
+ goto out;
+ }
+ if (hurd_condition_wait (&input->space_available, &input->lock))
+ {
+ err = EINTR;
+ goto out;
+ }
+ was_empty = !input->size;
+ }
+
+ /* Prepare the input buffer for iconv. */
+ if (input->cd_size)
+ {
+ err = ensure_cd_buffer_size (input->cd_size + datalen);
+ if (err)
+ goto out;
+ buffer = input->cd_buffer;
+ buffer_size = input->cd_size;
+ memcpy (buffer + buffer_size, data, datalen);
+ buffer_size += datalen;
+ }
+ else
+ {
+ buffer = data;
+ buffer_size = datalen;
+ }
+ /* Prepare output buffer for iconv. */
+ outbuf = &input->buffer[input->size];
+ outbuf_size = INPUT_QUEUE_SIZE - input->size;
+
+ amount = buffer_size;
+ nconv = iconv (input->cd, &buffer, &buffer_size, &outbuf, &outbuf_size);
+ amount -= buffer_size;
+
+ /* Calculate buffer progress. */
+ enqueued += amount;
+ data = buffer;
+ datalen = buffer_size;
+ input->size = INPUT_QUEUE_SIZE - outbuf_size;
+
+ if (nconv == (size_t) -1)
+ {
+ if (errno == E2BIG)
+ {
+ /* There is not enough space for more data in the outbuf
+ buffer. Mark the buffer as full, awake waiting
+ readers and go to sleep (above). */
+ input->full = 1;
+ if (was_empty)
+ condition_broadcast (&input->data_available);
+ /* Prevent calling condition_broadcast again if nonblock. */
+ was_empty = 0;
+ }
+ else
+ break;
+ }
+ }
+
+ /* XXX What should be done with invalid characters etc? */
+ if (errno == EINVAL && datalen)
+ {
+ /* The conversion stopped because of an incomplete byte sequence
+ at the end of the buffer. */
+ /* If we used the caller's buffer DATA, the remaining bytes
+ might not fit in our internal output buffer. In this case we
+ can reallocate the buffer in INPUT without needing to update
+ CD_BUFFER (as it points into DATA). */
+ err = ensure_cd_buffer_size (datalen);
+ if (err)
+ {
+ mutex_unlock (&input->lock);
+ errno = err;
+ return enqueued ?: -1;
+ }
+ memmove (input->cd_buffer, data, datalen);
+ }
+
+ out:
+ if (enqueued)
+ {
+ if (was_empty)
+ condition_broadcast (&input->data_available);
+ }
+ else
+ errno = err;
+ mutex_unlock (&input->lock);
+ return enqueued ?: -1;
+}
+
+/* Remove DATALEN characters from the input queue and put them in the
+ buffer DATA. The data will be supplied in the local encoding. The
+ function returns the amount of bytes removed (might be smaller than
+ DATALEN) or -1 and the error number in errno. If NONBLOCK is not
+ zero, return with -1 and set errno to EWOULDBLOCK if operation
+ would block for a long time. */
+ssize_t input_dequeue (input_t input, int nonblock, char *data,
+ size_t datalen)
+{
+ size_t amount = datalen;
+
+ mutex_lock (&input->lock);
+ while (!input->size)
+ {
+ if (nonblock)
+ {
+ mutex_unlock (&input->lock);
+ errno = EWOULDBLOCK;
+ return -1;
+ }
+ if (hurd_condition_wait (&input->data_available, &input->lock))
+ {
+ mutex_unlock (&input->lock);
+ errno = EINTR;
+ return -1;
+ }
+ }
+
+ if (amount > input->size)
+ amount = input->size;
+ memcpy (data, input->buffer, amount);
+ memmove (input->buffer, input->buffer + amount, input->size - amount);
+ input->size -= amount;
+ if (amount && input->full)
+ {
+ input->full = 0;
+ condition_broadcast (&input->space_available);
+ }
+ mutex_unlock (&input->lock);
+ return amount;
+}
+
+
+/* Flush the input buffer, discarding all pending data. */
+void input_flush (input_t input)
+{
+ mutex_lock (&input->lock);
+ input->size = 0;
+ if (input->full)
+ {
+ input->full = 0;
+ condition_broadcast (&input->space_available);
+ }
+ mutex_unlock (&input->lock);
+}
diff --git a/console/input.h b/console/input.h
new file mode 100644
index 00000000..47de35e2
--- /dev/null
+++ b/console/input.h
@@ -0,0 +1,58 @@
+/* input.h - Interface to the input component of a virtual console.
+ Copyright (C) 2002 Free Software Foundation, Inc.
+ Written by Marcus Brinkmann.
+
+ 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. */
+
+#ifndef INPUT_H
+#define INPUT_H
+
+#include <errno.h>
+
+struct input;
+typedef struct input *input_t;
+
+/* Create a new virtual console input queue, with the system encoding
+ being ENCODING. */
+error_t input_create (input_t *r_input, const char *encoding);
+
+/* Destroy the input queue INPUT. */
+void input_destroy (input_t input);
+
+/* Enter DATALEN characters from the buffer DATA into the input queue
+ INPUT. The DATA must be supplied in UTF-8 encoding (XXX UCS-4
+ would be nice, too, but it requires knowledge of endianess). The
+ function returns the amount of bytes written (might be smaller than
+ DATALEN) or -1 and the error number in errno. If NONBLOCK is not
+ zero, return with -1 and set errno to EWOULDBLOCK if operation
+ would block for a long time. */
+ssize_t input_enqueue (input_t input, int nonblock, char *data,
+ size_t datalen);
+
+/* Remove DATALEN characters from the input queue and put them in the
+ buffer DATA. The data will be supplied in the local encoding. The
+ function returns the amount of bytes removed (might be smaller than
+ DATALEN) or -1 and the error number in errno. If NONBLOCK is not
+ zero, return with -1 and set errno to EWOULDBLOCK if operation
+ would block for a long time. */
+ssize_t input_dequeue (input_t input, int nonblock, char *data,
+ size_t datalen);
+
+/* Flush the input buffer, discarding all pending data. */
+void input_flush (input_t input);
+
+#endif /* INPUT_H */