diff options
Diffstat (limited to 'console')
-rw-r--r-- | console/Makefile | 47 | ||||
-rw-r--r-- | console/README.UTF8 | 143 | ||||
-rw-r--r-- | console/console.c | 2093 | ||||
-rw-r--r-- | console/display.c | 2138 | ||||
-rw-r--r-- | console/display.h | 84 | ||||
-rw-r--r-- | console/hurd.ti | 164 | ||||
-rw-r--r-- | console/input.c | 277 | ||||
-rw-r--r-- | console/input.h | 58 | ||||
-rw-r--r-- | console/motd.UTF8 | 4 | ||||
-rw-r--r-- | console/mutations.h | 27 | ||||
-rw-r--r-- | console/pager.c | 221 | ||||
-rw-r--r-- | console/pager.h | 47 | ||||
-rw-r--r-- | console/priv.h | 37 |
13 files changed, 5340 insertions, 0 deletions
diff --git a/console/Makefile b/console/Makefile new file mode 100644 index 00000000..3eb4252e --- /dev/null +++ b/console/Makefile @@ -0,0 +1,47 @@ +# +# Copyright (C) 2002, 2012 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. + +dir := console +makemode := server + +target = console + +SRCS = console.c display.c pager.c input.c +LCLHDRS = display.h pager.h input.h priv.h mutations.h +DIST_FILES = hurd.ti motd.UTF8 + +MIGSTUBS = notifyServer.o tioctlServer.o fs_notifyUser.o + +HURDLIBS = netfs fshelp iohelp pager threads ports ihash shouldbeinlibc +OBJS = $(sort $(SRCS:.c=.o) $(MIGSTUBS)) + +MIGSFLAGS += -imacros $(srcdir)/mutations.h + +# 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/README.UTF8 b/console/README.UTF8 new file mode 100644 index 00000000..91901e1b --- /dev/null +++ b/console/README.UTF8 @@ -0,0 +1,143 @@ + +The console server supports any encoding supported by iconv, but uses +Unicode internally. The default encoding is ISO8859-1, another useful +variant is UTF-8. To configure the console server to use UTF-8 you +have to use the `--encoding' argument: + +# settrans -fg /dev/vcs /hurd/console --encoding=UTF-8 + +If you actually try this, you will notice two problems: + +1. You can not enter the letters in your locale, because the keyboard +doesn't have the right layout. Keyboard maps come later. For now, +you have to help yourself with the direct input with RightAlt. Maybe +I will put a simple compose key feature in the pc_kbd driver, so that +some western locales can be used more easily. + +2. If you bother to look up the unicode hex code and enter it with +AltGr, the font can not display it! If you are using the ncursesw +driver, do you use it while you are logged in from a working UTF-8 +terminal? If not, then this is your problem. An ncurses driver for +non-UTF-8 terminals is on the TODO list. But if you use the VGA +driver, you need to load a different font. + +This is because by default, the vga driver just reads the VGA card +memory and takes the font that is stored there. This font has a +limited characterset (256 characters, many graphical symbols among +that), so you won't get more than a few western characters with that. + +Unicode support +=============== + +But you want it all. You want to read Middle Old English. You want +to read Thai. Your Korean spam. Georgian script. Hebrew. And you +can have it. + +You need a Unicode font. There are good ones provided by Markus Kuhn, +the UCS fonts. Get them here: + +http://www.cl.cam.ac.uk/~mgk25/download/ucs-fonts.tar.gz + +(See also the web page at http://www.cl.cam.ac.uk/~mgk25/ucs-fonts.html). + +Now, load the font by providing it with the --font option to the vga +driver. I suggest only the 8x13 and the 9x15 fonts, but feel free to +try others, too. Note that the VGA text mode can not really display 9 +pixel wide characters. But as most characters have the ninth column +empty, and the VGA text mode can display an empty column between two +adjacent character cells, this trick allows us to display most of the +9x15 font correctly. So you won't notice a difference until you come +to very broad characters or special symbols, where you will see that +the last column is cut off. (BTW, I wrote the dynafont code carefully +to still support horizontal line graphic characters properly in 9 +pixel wide fonts. This is done by exploiting some special modes in +the VGA hardware. This is why in 512 (256) glyph mode and 9 pixel +wide fonts, you are limited to 448 (224) normal characters: 64 (32) +slots are reserved for the horizontal line graphic characters so they +are drawn continuously.) + +So, try the following: + +# console -d vga --font 8x13.bdf -d pc_kbd -d generic_speaker /dev/vcs + +or + +# console -d vga --font 9x15.bdf -d pc_kbd -d generic_speaker /dev/vcs + +If you are satisfied, copy your default font to +/lib/hurd/fonts/vga-system.bdf, where it will be picked up +automatically in favor to the graphic card's font. + +More about fonts +================ + +While we are talking about fonts, try also the 8x13O font with +--font-italic and 8x13B or 9x15B font with --font-bold. You can save +them in /lib/hurd/fonts/vga-system-bold.bdf and +/lib/hurd/fonts/vga-system-italic.bdf, too. + +To activate those fonts on your virtual console, try the following: + +# echo `tput sitm`Hello slanted world.`tput ritm` + +and + +# echo `tput gsbom`Hello bold world.`tput grbom` + +I hope you like what you see. Imagine this in emacs font-lock mode. + + +Unicode, finally +================ + +There are a few more steps necessary to make your Unicode environment +ready: + +Add a Unicode locale to /etc/locale.gen, and generate the locale +information for that! For example, I am living in Germany, and +normally use de_DE with the encoding ISO8859-1. My Unicode locale is +de_DE.UTF-8, so I am adding that to /etc/locale.gen: + +de_DE.UTF-8 UTF-8 + +and rerun locale-gen: + +# locale-gen + +See also /share/i18n/SUPPORTED. You can also do this more conveniently with + +# dpkg-reconfigure locales + +Once you generated this, make it your default locale: + +# export LANG=de_DE.UTF-8 + +If you have also loaded the unicode font above, you are set up. Try +for example to view the examples/ files in the ucs-fonts package with +less. + +# less fonts/examples/UTF_8-demo.txt + +You should see most of that file with the 9x15 font (a bit less with +the 8x13 font). + +You should be able to do the above process with other encodings than +UTF-8. But you should _always_ use a Unicode font, because the +console client uses Unicode internally for everything. + +Application specific notes +========================== + +If you enter unicode characters at the shell, libreadline loses track +of the number of characters displayed (it is not aware of multi-byte +encodings like UTF-8). This is fixed in readline 4.3 (which is not +yet in Debian). + +If you use mutt, install mutt-utf8. For lynx, edit /etc/lynx.cfg, +making sure that CHARACTER_SET is set to utf-8. + +If you use other applications, try to search with google for +"application-name utf8" or "application-name unicode". Often you find +what you need. The issues are the same for the GNU/Hurd and GNU/Linux +systems, so most of the information can be shared, except how to setup +the system console to support Unicode, of course. diff --git a/console/console.c b/console/console.c new file mode 100644 index 00000000..0b1f42c4 --- /dev/null +++ b/console/console.c @@ -0,0 +1,2093 @@ +/* console.c -- A console server. + + Copyright (C) 1997, 1999, 2002, 2003, 2007, 2008, 2010 + 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 <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <unistd.h> +#include <locale.h> + +#include <argp.h> +#include <argz.h> +#include <error.h> +#include <string.h> +#include <fcntl.h> +#include <dirent.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <rwlock.h> +#include <maptime.h> +#include <cthreads.h> + +#include <version.h> + +#include <mach.h> +#include <hurd/netfs.h> +#include <hurd/ioctl_types.h> +/* We include console.h for the color numbers. */ +#include <hurd/console.h> + +#include "display.h" +#include "input.h" + +#include "fs_notify_U.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. */ + +/* Handy source of time. */ +volatile struct mapped_time_value *console_maptime; + +#define DEFAULT_ENCODING "ISO-8859-1" +#define DEFAULT_INTENSITY CONS_ATTR_INTENSITY_NORMAL +#define DEFAULT_UNDERLINED 0 +#define DEFAULT_BLINKING 0 +#define DEFAULT_REVERSED 0 +#define DEFAULT_CONCEALED 0 +#define DEFAULT_WIDTH 80 +#define DEFAULT_HEIGHT 25 +#define DEFAULT_LINES 50 +/* Stringification of a macro. */ +#define STRX(s) #s +#define STR(s) STRX(s) + +/* For the help output. */ +#define DEFAULT_ATTRIBUTE_NAME "normal" +#define DEFAULT_FOREGROUND CONS_COLOR_WHITE +/* For the help output. */ +#define DEFAULT_FOREGROUND_NAME "white" +#define DEFAULT_BACKGROUND CONS_COLOR_BLACK +/* For the help output. */ +#define DEFAULT_BACKGROUND_NAME "black" + + +/* A handle for a console device. */ +typedef struct cons *cons_t; + +/* A handle for a virtual console device. */ +typedef struct vcons *vcons_t; + +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; +}; + +/* Pending directory modification requests. */ +struct modreq +{ + mach_port_t port; + struct modreq *next; +}; + +struct cons +{ + /* The lock protects the console, all virtual consoles contained in + it and the reference counters. It also locks the configuration + parameters. */ + struct mutex lock; + vcons_t vcons_list; + /* The encoding. */ + char *encoding; + /* Default attributes. */ + conchar_attr_t attribute; + + /* Requester of directory modification notifications. */ + struct modreq *dirmod_reqs; + unsigned int dirmod_tick; + + struct node *node; + mach_port_t underlying; + /* A template for the stat information of all nodes. */ + struct stat stat_template; + + /* The amount of lines, width and height. */ + unsigned int lines; + unsigned int width; + unsigned int height; +}; + + +/* Requires CONS to be locked. */ +static void +cons_notice_dirchange (cons_t cons, dir_changed_type_t type, char *name) +{ + error_t err; + struct modreq **preq = &cons->dirmod_reqs; + + cons->dirmod_tick++; + while (*preq) + { + struct modreq *req = *preq; + + err = dir_changed (req->port, cons->dirmod_tick, type, name); + if (err && err != MACH_SEND_TIMEOUT) + { + /* Remove notify port. */ + *preq = req->next; + mach_port_deallocate (mach_task_self (), req->port); + free (req); + } + else + preq = &req->next; + } +} + + +/* 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 (&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 ?: DEFAULT_ENCODING, + cons->attribute, cons->lines, cons->width, + cons->height); + if (err) + { + free (vcons->name); + free (vcons); + mutex_unlock (&cons->lock); + return err; + } + + err = input_create (&vcons->input, cons->encoding ?: DEFAULT_ENCODING); + if (err) + { + display_destroy (vcons->display); + free (vcons->name); + free (vcons); + mutex_unlock (&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; + } + cons_notice_dirchange (cons, DIR_CHANGED_NEW, vcons->name); + + 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; + + cons_notice_dirchange (cons, DIR_CHANGED_UNLINK, vcons->name); + + /* 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 = vcons->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 = display_get_size (vcons->display); + 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 (vcons->cons->stat_template.st_mode) + && S_ISDIR ((*np)->nn_stat.st_mode)) + { + if (vcons->cons->stat_template.st_mode & S_IRUSR) + (*np)->nn_stat.st_mode |= S_IXUSR; + if (vcons->cons->stat_template.st_mode & S_IRGRP) + (*np)->nn_stat.st_mode |= S_IXGRP; + if (vcons->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_mtim = *mtime; + else + flags |= TOUCH_MTIME; + + if (atime) + node->nn_stat.st_atim = *atime; + 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 = NULL; + struct 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; + } + 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); + } + } + + if (dir->nn->cons) + mutex_unlock(&dir->nn->cons->lock); + + 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 = *len; + assert (np == vcons->disp_node); + + if (offset + amt > np->nn_stat.st_size) + amt = np->nn_stat.st_size - offset; + if (amt < 0) + amt = 0; + else + amt = display_read (vcons->display, + /* cred->po->openstat & O_NONBLOCK */ 0, + offset, data, amt); + 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; +} + + +/* Implement io_map as described in <hurd/io.defs>. */ +kern_return_t +netfs_S_io_map (struct protid *cred, + memory_object_t *rdobj, + mach_msg_type_name_t *rdtype, + memory_object_t *wrobj, + mach_msg_type_name_t *wrtype) +{ + int flags; + struct node *np; + vcons_t vcons; + + if (!cred) + return EOPNOTSUPP; + + np = cred->po->np; + vcons = np->nn->vcons; + if (!vcons || np != vcons->disp_node) + return EOPNOTSUPP; + + *wrobj = *rdobj = MACH_PORT_NULL; + + flags = cred->po->openstat & (O_READ | O_WRITE); + + mutex_lock (&np->lock); + switch (flags) + { + case O_READ | O_WRITE: + *wrobj = *rdobj = display_get_filemap (vcons->display, + VM_PROT_READ | VM_PROT_WRITE); + if (*wrobj == MACH_PORT_NULL) + goto error; + mach_port_mod_refs (mach_task_self (), *rdobj, MACH_PORT_RIGHT_SEND, 1); + break; + case O_READ: + *rdobj = display_get_filemap (vcons->display, VM_PROT_READ); + if (*rdobj == MACH_PORT_NULL) + goto error; + break; + case O_WRITE: + *wrobj = display_get_filemap (vcons->display, VM_PROT_WRITE); + if (*wrobj == MACH_PORT_NULL) + goto error; + break; + } + mutex_unlock (&np->lock); + + *rdtype = MACH_MSG_TYPE_MOVE_SEND; + *wrtype = MACH_MSG_TYPE_MOVE_SEND; + + return 0; + + error: + mutex_unlock (&np->lock); + return errno; +} + + +kern_return_t +netfs_S_dir_notice_changes (struct protid *cred, mach_port_t notify) +{ + error_t err; + cons_t cons; + struct modreq **preq; + struct modreq *req; + + if (!cred) + return EOPNOTSUPP; + + cons = cred->po->np->nn->cons; + if (!cons) + return EOPNOTSUPP; + + mutex_lock (&cons->lock); + /* We have to prevent that we accumulate dead-names in the + notification list. They are cleaned up in cons_notice_dirchange, + but that is not called often enough, so we also clean them up + here. This way, the maximum of dead-names will never exceed the + prior maximum of active clients. The better way would be to + request dead-name notifications, XXX. */ + preq = &cons->dirmod_reqs; + + while (*preq) + { + mach_port_type_t type; + req = *preq; + + err = mach_port_type (mach_task_self (), req->port, &type); + if (!err && type == MACH_PORT_TYPE_DEAD_NAME) + { + /* Remove notify port. */ + *preq = req->next; + mach_port_deallocate (mach_task_self (), req->port); + free (req); + } + else + preq = &req->next; + } + + err = dir_changed (notify, cons->dirmod_tick, DIR_CHANGED_NULL, ""); + if (err) + { + mutex_unlock (&cons->lock); + return err; + } + req = malloc (sizeof (struct modreq)); + if (!req) + { + mutex_unlock (&cons->lock); + return errno; + } + req->port = notify; + req->next = cons->dirmod_reqs; + cons->dirmod_reqs = req; + mutex_unlock (&cons->lock); + return 0; +} + +kern_return_t +netfs_S_file_notice_changes (struct protid *cred, mach_port_t notify) +{ + struct node *np; + vcons_t vcons; + + if (!cred) + return EOPNOTSUPP; + + np = cred->po->np; + vcons = np->nn->vcons; + if (!vcons || np != vcons->disp_node) + return EOPNOTSUPP; + + return display_notice_changes (vcons->display, notify); +} + + +static const char *color_names[CONS_COLOR_MAX + 1] = + { + [CONS_COLOR_BLACK] = "black", + [CONS_COLOR_RED] = "red", + [CONS_COLOR_GREEN] = "green", + [CONS_COLOR_YELLOW] = "yellow", + [CONS_COLOR_BLUE] = "blue", + [CONS_COLOR_MAGENTA] = "magenta", + [CONS_COLOR_CYAN] = "cyan", + [CONS_COLOR_WHITE] = "white" + }; + +static const struct argp_option options[] = +{ + { "foreground", 'f', "COLOR", 0, "Set foreground color to" + " COLOR (default `" DEFAULT_FOREGROUND_NAME "')" }, + { "background", 'b', "COLOR", 0, "Set background color to" + " COLOR (default `" DEFAULT_BACKGROUND_NAME "')" }, + { "attribute", 'a', "ATTR[,...]", 0, "Set further default attributes" + " (default `" DEFAULT_ATTRIBUTE_NAME "')" }, + { "encoding", 'e', "NAME", 0, "Set encoding of virtual consoles to" + " NAME (default `" DEFAULT_ENCODING "')" }, + { "width", 'w', "WIDTH", 0, "Set width to WIDTH (default `" + STR(DEFAULT_WIDTH) "')" }, + { "height", 'h', "HEIGHT", 0, "Set height to HEIGHT (default `" + STR(DEFAULT_HEIGHT) "')" }, + { "lines", 'l', "LINES", 0, "Set amount of scrollback lines to LINES " + "(default `" STR(DEFAULT_LINES) "')" }, + {0} +}; + +static error_t +parse_color (const char *name, int *number) +{ + if (isdigit (*name)) + { + long int nr; + char *tail; + + errno = 0; + + nr = strtol (name, &tail, 0); + if (errno || *tail || nr < 0 || nr > CONS_COLOR_MAX) + return EINVAL; + *number = nr; + return 0; + } + else + { + int i; + for (i = 0; i <= CONS_COLOR_MAX; i++) + if (!strcmp (color_names[i], name)) + { + *number = i; + return 0; + } + return EINVAL; + } +} + +static error_t +parse_attributes (const char *name, conchar_attr_t *attr) +{ + while (*name) + { + int value = 1; + + if (!strncmp (name, "not-", 4)) + { + value = 0; + name += 4; + } + + if (!strncmp (name, "normal", 6)) + { + name += 6; + if (value != 1) + return EINVAL; + attr->intensity = CONS_ATTR_INTENSITY_NORMAL; + } + else if (!strncmp (name, "bright", 6)) + { + name += 6; + if (value != 1) + return EINVAL; + attr->intensity = CONS_ATTR_INTENSITY_BOLD; + } + else if (!strncmp (name, "dim", 3)) + { + name += 3; + if (value != 1) + return EINVAL; + attr->intensity = CONS_ATTR_INTENSITY_DIM; + } + else if (!strncmp (name, "underlined", 10)) + { + name += 10; + attr->underlined = value; + } + else if (!strncmp (name, "blinking", 8)) + { + name += 8; + attr->blinking = value; + } + else if (!strncmp (name, "concealed", 9)) + { + name += 9; + attr->concealed = value; + } + else if (!strncmp (name, "italic", 6)) + { + name += 6; + attr->italic = value; + } + else if (!strncmp (name, "bold", 4)) + { + name += 4; + attr->bold = value; + } + else + return EINVAL; + + if (name[0] == ',') + name++; + else if (name[0] != '\0') + return EINVAL; + } + return 0; +} + +static error_t +parse_opt (int opt, char *arg, struct argp_state *state) +{ + cons_t cons = state->input ?: netfs_root_node->nn->cons; + error_t err; + int color = 0; + char *tail; + + switch (opt) + { + default: + return ARGP_ERR_UNKNOWN; + case ARGP_KEY_SUCCESS: + case ARGP_KEY_ERROR: + break; + + case ARGP_KEY_INIT: + mutex_lock (&cons->lock); + break; + + case ARGP_KEY_FINI: + mutex_unlock (&cons->lock); + break; + + case 'f': + err = parse_color (arg, &color); + cons->attribute.fgcol = color; + if (err) + argp_error (state, "Invalid color name: %s", arg); + break; + + case 'b': + err = parse_color (arg, &color); + cons->attribute.bgcol = color; + if (err) + argp_error (state, "Invalid color name: %s", arg); + break; + + case 'a': + err = parse_attributes (arg, &cons->attribute); + if (err) + argp_error (state, "Invalid attribute specifier: %s", arg); + break; + + case 'l': + errno = 0; + cons->lines = strtoul (arg, &tail, 0); + if (tail == NULL || tail == arg || *tail != '\0') + argp_error (state, "LINES is not a number: %s", arg); + if (errno) + argp_error (state, "Overflow in argument LINES %s", arg); + break; + + case 'w': + errno = 0; + cons->width = strtoul (arg, &tail, 0); + if (tail == NULL || tail == arg || *tail != '\0') + argp_error (state, "WIDTH is not a number: %s", arg); + if (errno) + argp_error (state, "Overflow in argument WIDTH %s", arg); + break; + + case 'h': + errno = 0; + cons->height = strtoul (arg, &tail, 0); + if (tail == NULL || tail == arg || *tail != '\0') + argp_error (state, "HEIGHT is not a number: %s", arg); + if (errno) + argp_error (state, "Overflow in argument HEIGHT %s", arg); + break; + + case 'e': + /* XXX Check validity of encoding. Can we perform all necessary + conversions? */ + { + char *new = strdup (arg); + if (!new) + return ENOMEM; + if (cons->encoding) + free (cons->encoding); + cons->encoding = new; + } + break; + } + return 0; +} + +/* Return an argz string describing the current options. Fill *ARGZ + with a pointer to newly malloced storage holding the list and *LEN + to the length of that storage. */ +error_t +netfs_append_args (char **argz, size_t *argz_len) +{ + error_t err = 0; + cons_t cons = netfs_root_node->nn->cons; + /* The longest possible is 61 characters long: + "normal,not-underlined,not-blinking,not-reversed,not-concealed". */ + char attr_str[80] = "--attribute="; + char *attr = &attr_str[12]; + char *attrp = attr; + + if (cons->encoding && strcmp (cons->encoding, DEFAULT_ENCODING)) + { + char *buf; + if (asprintf (&buf, "--encoding=%s", cons->encoding) < 0) + err = ENOMEM; + else + err = argz_add (argz, argz_len, buf); + + } + if (!err && cons->attribute.fgcol != DEFAULT_FOREGROUND) + { + char *buf; + if (asprintf (&buf, "--foreground=%s", + color_names[cons->attribute.fgcol]) < 0) + err = ENOMEM; + else + err = argz_add (argz, argz_len, buf); + + } + if (!err && cons->attribute.bgcol != DEFAULT_BACKGROUND) + { + char *buf; + if (asprintf (&buf, "--background=%s", + color_names[cons->attribute.bgcol]) < 0) + err = ENOMEM; + else + err = argz_add (argz, argz_len, buf); + } + if (!err && cons->lines != DEFAULT_LINES) + { + char *buf; + if (asprintf (&buf, "--lines=%d", cons->lines) < 0) + err = ENOMEM; + else + err = argz_add (argz, argz_len, buf); + } + if (!err && cons->width != DEFAULT_WIDTH) + { + char *buf; + if (asprintf (&buf, "--width=%d", cons->lines) < 0) + err = ENOMEM; + else + err = argz_add (argz, argz_len, buf); + } + if (!err && cons->height != DEFAULT_HEIGHT) + { + char *buf; + if (asprintf (&buf, "--height=%d", cons->height) < 0) + err = ENOMEM; + else + err = argz_add (argz, argz_len, buf); + } + if (!err && cons->attribute.intensity != DEFAULT_INTENSITY) + { + if (attrp != attr) + *(attrp++) = ','; + switch (cons->attribute.intensity) + { + case CONS_ATTR_INTENSITY_NORMAL: + attrp = stpcpy (attrp, "normal"); + break; + case CONS_ATTR_INTENSITY_BOLD: + attrp = stpcpy (attrp, "bold"); + break; + case CONS_ATTR_INTENSITY_DIM: + attrp = stpcpy (attrp, "dim"); + break; + } + } + if (!err && cons->attribute.underlined != DEFAULT_UNDERLINED) + { + if (attrp != attr) + *(attrp++) = ','; + if (!cons->attribute.underlined) + attrp = stpcpy (attrp, "not-"); + attrp = stpcpy (attrp, "underlined"); + } + if (!err && cons->attribute.blinking != DEFAULT_BLINKING) + { + if (attrp != attr) + *(attrp++) = ','; + if (!cons->attribute.blinking) + attrp = stpcpy (attrp, "not-"); + attrp = stpcpy (attrp, "blinking"); + } + if (!err && cons->attribute.reversed != DEFAULT_REVERSED) + { + if (attrp != attr) + *(attrp++) = ','; + if (!cons->attribute.reversed) + attrp = stpcpy (attrp, "not-"); + attrp = stpcpy (attrp, "reversed"); + } + if (!err && cons->attribute.concealed != DEFAULT_CONCEALED) + { + if (attrp != attr) + *(attrp++) = ','; + if (!cons->attribute.concealed) + attrp = stpcpy (attrp, "not-"); + attrp = stpcpy (attrp, "concealed"); + } + if (!err && attrp != attr) + err = argz_add (argz, argz_len, attr_str); + + return err; +} + + +kern_return_t +S_tioctl_tiocflush (struct protid *cred, int queue_selector) +{ + struct node *np; + vcons_t vcons; + + if (!cred) + return EOPNOTSUPP; + if (!(cred->po->openstat & (O_READ | O_WRITE))) + return EBADF; + + np = cred->po->np; + vcons = np->nn->vcons; + if (!vcons || np != vcons->cons_node) + return EOPNOTSUPP; + + if (!queue_selector) + queue_selector = O_READ | O_WRITE; + + if (queue_selector & O_READ) + input_flush (vcons->input); + if (queue_selector & O_WRITE) + display_discard_output (vcons->display); + + return 0; +} + +kern_return_t +S_tioctl_tiocgwinsz (struct protid *cred, struct winsize *size) +{ + struct node *np; + vcons_t vcons; + + if (!cred) + return EOPNOTSUPP; + + np = cred->po->np; + vcons = np->nn->vcons; + if (!vcons || np != vcons->cons_node) + return EOPNOTSUPP; + + display_getsize (vcons->display, size); + return 0; +} + +kern_return_t +S_tioctl_tiocstart (struct protid *cred) +{ + struct node *np; + vcons_t vcons; + + if (!cred) + return EOPNOTSUPP; + if (!(cred->po->openstat & (O_READ | O_WRITE))) + return EBADF; + + np = cred->po->np; + vcons = np->nn->vcons; + if (!vcons || np != vcons->cons_node) + return EOPNOTSUPP; + + display_start_output (vcons->display); + return 0; +} + +kern_return_t +S_tioctl_tiocstop (struct protid *cred) +{ + struct node *np; + vcons_t vcons; + + if (!cred) + return EOPNOTSUPP; + if (!(cred->po->openstat & (O_READ | O_WRITE))) + return EBADF; + + np = cred->po->np; + vcons = np->nn->vcons; + if (!vcons || np != vcons->cons_node) + return EOPNOTSUPP; + + display_stop_output (vcons->display); + return 0; +} + + +kern_return_t +S_tioctl_tiocoutq (struct protid *cred, int *queue_size) +{ + struct node *np; + vcons_t vcons; + + if (!cred) + return EOPNOTSUPP; + if (!(cred->po->openstat & (O_READ | O_WRITE))) + return EBADF; + + np = cred->po->np; + vcons = np->nn->vcons; + if (!vcons || np != vcons->cons_node) + return EOPNOTSUPP; + + *queue_size = display_pending_output (vcons->display); + return 0; +} + +kern_return_t +S_tioctl_tiocspgrp (struct protid *cred, int pgrp) +{ + struct node *np; + vcons_t vcons; + + if (!cred) + return EOPNOTSUPP; + if (!(cred->po->openstat & (O_READ | O_WRITE))) + return EBADF; + + np = cred->po->np; + vcons = np->nn->vcons; + if (!vcons || np != vcons->cons_node) + return EOPNOTSUPP; + + display_set_owner (vcons->display, -pgrp); + return 0; +} + +kern_return_t +S_tioctl_tiocgpgrp (struct protid *cred, int *pgrp) +{ + error_t err; + struct node *np; + vcons_t vcons; + + if (!cred) + return EOPNOTSUPP; + if (!(cred->po->openstat & (O_READ | O_WRITE))) + return EBADF; + + np = cred->po->np; + vcons = np->nn->vcons; + if (!vcons || np != vcons->cons_node) + return EOPNOTSUPP; + + err = display_get_owner (vcons->display, pgrp); + if (!err) + *pgrp = -*pgrp; + + return err; +} + +kern_return_t +S_tioctl_tiocmodg (io_t port, int *state) +{ + return EOPNOTSUPP; +} + +kern_return_t +S_tioctl_tiocmods (io_t port, int state) +{ + return EOPNOTSUPP; +} + +kern_return_t +S_tioctl_tiocexcl (io_t port) +{ + return EOPNOTSUPP; +} + +kern_return_t +S_tioctl_tiocnxcl (io_t port) +{ + return EOPNOTSUPP; +} + +kern_return_t +S_tioctl_tiocgeta (io_t port, tcflag_t *modes, cc_t *ccs, speed_t *speeds) +{ + return EOPNOTSUPP; +} + +kern_return_t +S_tioctl_tiocseta (io_t port, tcflag_t *modes, cc_t *ccs, speed_t *speeds) +{ + return EOPNOTSUPP; +} + +kern_return_t +S_tioctl_tiocsetaw (io_t port, tcflag_t *modes, cc_t *ccs, speed_t *speeds) +{ + return EOPNOTSUPP; +} + +kern_return_t +S_tioctl_tiocsetaf (io_t port, tcflag_t *modes, cc_t *ccs, + speed_t *speeds) +{ + return EOPNOTSUPP; +} + +kern_return_t +S_tioctl_tiocgetd (io_t port, int *disc) +{ + return EOPNOTSUPP; +} + +kern_return_t +S_tioctl_tiocsetd (io_t port, int disc) +{ + return EOPNOTSUPP; +} + +kern_return_t +S_tioctl_tiocdrain (io_t port) +{ + return EOPNOTSUPP; +} + +kern_return_t +S_tioctl_tiocmget (io_t port, int *bits) +{ + return EOPNOTSUPP; +} + +kern_return_t +S_tioctl_tiocmset (io_t port, int bits) +{ + return EOPNOTSUPP; +} + +kern_return_t +S_tioctl_tiocsig (io_t port, int sig) +{ + return EOPNOTSUPP; +} + +kern_return_t +S_tioctl_tiocext (io_t port, int mode) +{ + return EOPNOTSUPP; +} + +kern_return_t +S_tioctl_tiocucntl (io_t port, int mode) + +{ + return EOPNOTSUPP; +} + +kern_return_t +S_tioctl_tiocswinsz (struct protid *cred, struct winsize size) +{ + return EOPNOTSUPP; +} + +kern_return_t +S_tioctl_tiocremote (struct protid *cred, int how) +{ + return EOPNOTSUPP; +} + +kern_return_t +S_tioctl_tiocmbic (struct protid *cred, int bits) +{ + return EOPNOTSUPP; +} + +kern_return_t +S_tioctl_tiocmbis (struct protid *cred, int bits) +{ + return EOPNOTSUPP; +} + +kern_return_t +S_tioctl_tiocpkt (struct protid *cred, int mode) +{ + return EOPNOTSUPP; +} + +kern_return_t +S_tioctl_tiocsti (struct protid *cred, char c) +{ + return EOPNOTSUPP; +} + +kern_return_t +S_tioctl_tioccdtr (struct protid *cred) +{ + return EOPNOTSUPP; +} + +kern_return_t +S_tioctl_tiocsdtr (struct protid *cred) +{ + return EOPNOTSUPP; +} + +kern_return_t +S_tioctl_tioccbrk (struct protid *cred) +{ + return EOPNOTSUPP; +} + +kern_return_t +S_tioctl_tiocsbrk (struct protid *cred) +{ + return EOPNOTSUPP; +} + + +int +console_demuxer (mach_msg_header_t *inp, + mach_msg_header_t *outp) +{ + extern int netfs_demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp); + extern int tioctl_server (mach_msg_header_t *inp, mach_msg_header_t *outp); + + return (netfs_demuxer (inp, outp) + || tioctl_server (inp, outp)); +} + +const struct argp netfs_std_runtime_argp = + { options, parse_opt, NULL, + "A translator that provides virtual consoles." }; + +int +main (int argc, char **argv) +{ + error_t err; + mach_port_t bootstrap; + struct stat ul_stat; + cons_t cons; + struct netnode root_nn = { vcons: 0 }; + + cons = malloc (sizeof (struct cons)); + if (!cons) + error (1, ENOMEM, "Cannot create console structure"); + mutex_init (&cons->lock); + cons->encoding = NULL; + cons->width = DEFAULT_WIDTH; + cons->height = DEFAULT_HEIGHT; + cons->lines = DEFAULT_LINES; + cons->attribute.intensity = DEFAULT_INTENSITY; + cons->attribute.underlined = DEFAULT_UNDERLINED; + cons->attribute.blinking = DEFAULT_BLINKING; + cons->attribute.reversed = DEFAULT_REVERSED; + cons->attribute.concealed = DEFAULT_CONCEALED; + cons->attribute.fgcol = DEFAULT_FOREGROUND; + cons->attribute.bgcol = DEFAULT_BACKGROUND; + cons->vcons_list = NULL; + cons->dirmod_reqs = NULL; + cons->dirmod_tick = 0; + root_nn.cons = cons; + + /* Parse our command line arguments. */ + argp_parse (&netfs_std_runtime_argp, argc, argv, 0, 0, cons); + + setlocale (LC_CTYPE, "C.UTF-8"); + + task_get_bootstrap_port (mach_task_self (), &bootstrap); + + netfs_init (); + + display_init (); + + /* Create the root node (some attributes initialized below). */ + netfs_root_node = netfs_make_node (&root_nn); + if (! netfs_root_node) + error (2, ENOMEM, "Cannot create root node"); + + err = maptime_map (0, 0, &console_maptime); + if (err) + error (3, err, "Cannot map time"); + + cons->node = netfs_root_node; + cons->underlying = netfs_startup (bootstrap, O_READ); + if (cons->underlying == MACH_PORT_NULL) + error (4, 0, "Cannot get underlying node"); + + err = io_stat (cons->underlying, &ul_stat); + if (err) + error (5, 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); + + do + { + ports_manage_port_operations_multithread (netfs_port_bucket, + console_demuxer, + 1000 * 60 * 2, + 1000 * 60 * 10, + 0); + err = netfs_shutdown (0); + } + while (err); + + exit (err); +} diff --git a/console/display.c b/console/display.c new file mode 100644 index 00000000..e807c50f --- /dev/null +++ b/console/display.c @@ -0,0 +1,2138 @@ +/* display.c - The display component of a virtual console. + Copyright (C) 1999 Kalle Olavi Niemitalo (emu.c from colortext 0.3). + Copyright (C) 2002, 2003, 2010 Free Software Foundation, Inc. + Written by Marcus Brinkmann and Kalle Olavi Niemitalo. + + 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 <stddef.h> +#include <errno.h> +#include <unistd.h> +#include <wchar.h> +#include <iconv.h> +#include <argp.h> +#include <string.h> +#include <assert.h> +#include <error.h> + +#include <cthreads.h> + +#include <hurd.h> +#include <hurd/ports.h> +#include <hurd/console.h> + +#ifndef __STDC_ISO_10646__ +#error It is required that wchar_t is UCS-4. +#endif + +#include "display.h" +#include "pager.h" + + +struct changes +{ + uint32_t flags; + struct + { + uint32_t col; + uint32_t row; + uint32_t status; + } cursor; + struct + { + uint32_t cur_line; + uint32_t scr_lines; + } screen; + + uint32_t bell_audible; + uint32_t bell_visible; + + off_t start; + off_t end; + +#define DISPLAY_CHANGE_CURSOR_POS 0x0001 +#define DISPLAY_CHANGE_CURSOR_STATUS 0x0002 +#define DISPLAY_CHANGE_SCREEN_CUR_LINE 0x0004 +#define DISPLAY_CHANGE_SCREEN_SCR_LINES 0x0008 +#define DISPLAY_CHANGE_BELL_AUDIBLE 0x0010 +#define DISPLAY_CHANGE_BELL_VISIBLE 0x0020 +#define DISPLAY_CHANGE_FLAGS 0x0030 +#define DISPLAY_CHANGE_MATRIX 0x0040 + unsigned int which; +}; + +struct cursor +{ + uint32_t saved_x; + uint32_t saved_y; +}; +typedef struct cursor *cursor_t; + +struct scrolling_region +{ + uint32_t top; + uint32_t bottom; +}; + +struct parse +{ + /* The parsing state of output characters, needed to handle escape + character sequences. */ + enum + { + STATE_NORMAL = 0, + /* An escape character has just been parsed. */ + STATE_ESC, + STATE_ESC_BRACKET_INIT, + STATE_ESC_BRACKET, + STATE_ESC_BRACKET_QUESTION, + STATE_ESC_BRACKET_RIGHT_ANGLE + } state; + + /* How many parameters an escape sequence may have. */ +#define PARSE_MAX_PARAMS 10 + int params[PARSE_MAX_PARAMS]; + int nparams; +}; +typedef struct parse *parse_t; + +struct output +{ + /* 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; + +struct attr +{ + conchar_attr_t attr_def; + conchar_attr_t current; + /* True if in alternate character set (ASCII graphic) mode. */ + unsigned int altchar; +}; +typedef struct attr *attr_t; + +/* Pending directory and file modification requests. */ +struct modreq +{ + mach_port_t port; + struct modreq *next; + /* If the port should have been notified, but it was blocking, we + set this. */ + int pending; +}; + +/* For each display, a notification port is created to which the + kernel sends message accepted notifications. */ +struct notify +{ + struct port_info pi; + struct display *display; +}; + +struct display +{ + /* The lock for the virtual console display structure. */ + struct mutex lock; + + /* Indicates if OWNER_ID is initialized. */ + int has_owner; + /* Specifies the ID of the process that should receive the WINCH + signal for this virtual console. */ + int owner_id; + + /* The pending changes. */ + struct changes changes; + + /* The state of the virtual console. */ + /* The saved cursor position. */ + struct cursor cursor; + /* The output queue and parser state. */ + struct output output; + /* The current video attributes. */ + struct attr attr; + /* Non-zero if we are in insert mode. */ + int insert_mode; + /* Scrolling region. */ + struct scrolling_region csr; + + struct cons_display *user; + + /* The pager for the USER member. */ + struct user_pager user_pager; + + /* A list of ports to send file change notifications to. */ + struct modreq *filemod_reqs; + /* Those ports which currently have a pending notification. */ + struct modreq *filemod_reqs_pending; + /* The notify port. */ + struct notify *notify_port; +}; + + +/* The bucket and class for notification messages. */ +static struct port_bucket *notify_bucket; +static struct port_class *notify_class; + +#define msgh_request_port msgh_remote_port +#define msgh_reply_port msgh_local_port + +/* SimpleRoutine file_changed */ +kern_return_t +nowait_file_changed (mach_port_t notify_port, natural_t tickno, + file_changed_type_t change, + off_t start, off_t end, mach_port_t notify) +{ + typedef struct + { + mach_msg_header_t Head; + mach_msg_type_t ticknoType; + natural_t tickno; + mach_msg_type_t changeType; + file_changed_type_t change; + mach_msg_type_t startType; + loff_t start; + mach_msg_type_t endType; + loff_t end; + } Request; + union + { + Request In; + } Mess; + register Request *InP = &Mess.In; + + static const mach_msg_type_t ticknoType = { + /* msgt_name = */ 2, + /* msgt_size = */ 32, + /* msgt_number = */ 1, + /* msgt_inline = */ TRUE, + /* msgt_longform = */ FALSE, + /* msgt_deallocate = */ FALSE, + /* msgt_unused = */ 0 + }; + + static const mach_msg_type_t changeType = { + /* msgt_name = */ 2, + /* msgt_size = */ 32, + /* msgt_number = */ 1, + /* msgt_inline = */ TRUE, + /* msgt_longform = */ FALSE, + /* msgt_deallocate = */ FALSE, + /* msgt_unused = */ 0 + }; + + static const mach_msg_type_t startType = { + /* msgt_name = */ 11, + /* msgt_size = */ 64, + /* msgt_number = */ 1, + /* msgt_inline = */ TRUE, + /* msgt_longform = */ FALSE, + /* msgt_deallocate = */ FALSE, + /* msgt_unused = */ 0 + }; + + static const mach_msg_type_t endType = { + /* msgt_name = */ 11, + /* msgt_size = */ 64, + /* msgt_number = */ 1, + /* msgt_inline = */ TRUE, + /* msgt_longform = */ FALSE, + /* msgt_deallocate = */ FALSE, + /* msgt_unused = */ 0 + }; + + InP->ticknoType = ticknoType; + InP->tickno = tickno; + InP->changeType = changeType; + InP->change = change; + InP->startType = startType; + InP->start = start; + InP->endType = endType; + InP->end = end; + + InP->Head.msgh_bits = MACH_MSGH_BITS(19, 0); + /* msgh_size passed as argument. */ + InP->Head.msgh_request_port = notify_port; + InP->Head.msgh_reply_port = MACH_PORT_NULL; + InP->Head.msgh_seqno = 0; + InP->Head.msgh_id = 20501; + + if (notify == MACH_PORT_NULL) + return mach_msg (&InP->Head, MACH_SEND_MSG | MACH_MSG_OPTION_NONE, + 64, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + else + return mach_msg (&InP->Head, MACH_SEND_MSG | MACH_SEND_NOTIFY, + 64, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, + notify); +} + +/* Free the list of modification requests MR */ +static void +free_modreqs (struct modreq *mr) +{ + struct modreq *tmp; + for (; mr; mr = tmp) + { + mach_port_t old; + /* Cancel the dead-name notification. */ + mach_port_request_notification (mach_task_self (), mr->port, + MACH_NOTIFY_DEAD_NAME, 0, + MACH_PORT_NULL, + MACH_MSG_TYPE_MAKE_SEND_ONCE, &old); + mach_port_deallocate (mach_task_self (), old); + + /* Deallocate the user's port. */ + mach_port_deallocate (mach_task_self (), mr->port); + tmp = mr->next; + free (mr); + } +} + +/* A port deleted notification is generated when we deallocate the + user's notify port before it is dead. */ +error_t +do_mach_notify_port_deleted (mach_port_t notify, mach_port_t name) +{ + /* As we cancel the dead-name notification before deallocating the + port, this should not happen. */ + assert (0); +} + +/* We request dead name notifications for the user ports. */ +error_t +do_mach_notify_dead_name (mach_port_t notify, mach_port_t dead_name) +{ + struct notify *notify_port = ports_lookup_port (notify_bucket, + notify, notify_class); + struct display *display; + struct modreq **preq; + struct modreq *req; + + if (!notify_port) + return EOPNOTSUPP; + + display = notify_port->display; + mutex_lock (&display->lock); + + /* Find request in pending queue. */ + preq = &display->filemod_reqs_pending; + while (*preq && (*preq)->port != dead_name) + preq = &(*preq)->next; + if (! *preq) + { + /* Find request in queue. */ + preq = &display->filemod_reqs; + while (*preq && (*preq)->port != dead_name) + preq = &(*preq)->next; + } + + if (*preq) + { + req = *preq; + *preq = req->next; + + mach_port_deallocate (mach_task_self (), req->port); + free (req); + } + mutex_unlock (&display->lock); + + /* Drop gratuitous extra reference that the notification creates. */ + mach_port_deallocate (mach_task_self (), dead_name); + + return 0; +} + +void do_mach_notify_port_destroyed (void) { assert (0); } + +error_t +do_mach_notify_no_senders (mach_port_t port, mach_port_mscount_t count) +{ + return ports_do_mach_notify_no_senders (port, count); +} + +kern_return_t +do_mach_notify_send_once (mach_port_t notify) +{ + return 0; +} + +kern_return_t +do_mach_notify_msg_accepted (mach_port_t notify, mach_port_t send) +{ + struct notify *notify_port = ports_lookup_port (notify_bucket, + notify, notify_class); + struct display *display; + struct modreq **preq; + struct modreq *req; + + if (!notify_port) + return EOPNOTSUPP; + + /* If we deallocated the send right in display_destroy before the + notification was created. We have nothing to do in this + case. */ + if (!send) + { + assert(0); + ports_port_deref (notify_port); + return 0; + } + + display = notify_port->display; + mutex_lock (&display->lock); + /* Find request in pending queue. */ + preq = &display->filemod_reqs_pending; + while (*preq && (*preq)->port != send) + preq = &(*preq)->next; + /* If we don't find the request, it was destroyed in + display_destroy. In this case, there is nothing left to do + here. */ + if (! *preq) + { + assert(0); + mutex_unlock (&display->lock); + ports_port_deref (notify_port); + return 0; + } + req = *preq; + + if (req->pending) + { + error_t err; + /* A request was desired while we were blocking. Send it now + and stay in pending queue. */ + req->pending = 0; + err = nowait_file_changed (req->port, 0, FILE_CHANGED_WRITE, -1, -1, + notify); + if (err && err != MACH_SEND_WILL_NOTIFY) + { + mach_port_t old; + *preq = req->next; + mutex_unlock (&display->lock); + + /* Cancel the dead-name notification. */ + mach_port_request_notification (mach_task_self (), req->port, + MACH_NOTIFY_DEAD_NAME, 0, + MACH_PORT_NULL, + MACH_MSG_TYPE_MAKE_SEND_ONCE, &old); + mach_port_deallocate (mach_task_self (), old); + + mach_port_deallocate (mach_task_self (), req->port); + free (req); + ports_port_deref (notify_port); + return err; + } + if (err == MACH_SEND_WILL_NOTIFY) + { + mutex_unlock (&display->lock); + return 0; + } + /* The message was successfully queued, fall through. */ + } + /* Remove request from pending queue. */ + *preq = req->next; + /* Insert request into active queue. */ + req->next = display->filemod_reqs; + display->filemod_reqs = req; + mutex_unlock (&display->lock); + ports_port_deref (notify_port); + return 0; +} + +/* A top-level function for the notification thread that just services + notification messages. */ +static void +service_notifications (any_t arg) +{ + struct port_bucket *notify_bucket = arg; + extern int notify_server (mach_msg_header_t *inp, mach_msg_header_t *outp); + + for (;;) + ports_manage_port_operations_one_thread (notify_bucket, + notify_server, + 1000 * 60 * 10); +} + +error_t +display_notice_changes (display_t display, mach_port_t notify) +{ + error_t err; + struct modreq *req; + mach_port_t notify_port; + mach_port_t old; + + mutex_lock (&display->lock); + err = nowait_file_changed (notify, 0, FILE_CHANGED_NULL, 0, 0, + MACH_PORT_NULL); + if (err) + { + mutex_unlock (&display->lock); + return err; + } + + req = malloc (sizeof (struct modreq)); + if (!req) + { + mutex_unlock (&display->lock); + return errno; + } + + notify_port = ports_get_right (display->notify_port); + + /* Request dead-name notification for the user's port. */ + err = mach_port_request_notification (mach_task_self (), notify, + MACH_NOTIFY_DEAD_NAME, 0, + notify_port, + MACH_MSG_TYPE_MAKE_SEND_ONCE, &old); + if (err) + { + free (req); + mutex_unlock (&display->lock); + return err; + } + assert (old == MACH_PORT_NULL); + + req->port = notify; + req->pending = 0; + req->next = display->filemod_reqs; + display->filemod_reqs = req; + mutex_unlock (&display->lock); + return 0; +} + +/* Requires DISPLAY to be locked. */ +static void +display_notice_filechange (display_t display) +{ + error_t err; + struct modreq *req = display->filemod_reqs_pending; + struct modreq **preq = &display->filemod_reqs; + mach_port_t notify_port = ports_get_right (display->notify_port); + + while (req) + { + req->pending = 1; + req = req->next; + } + + while (*preq) + { + req = *preq; + + err = nowait_file_changed (req->port, 0, FILE_CHANGED_WRITE, -1, -1, + notify_port); + if (err) + { + /* Remove notify port. */ + *preq = req->next; + + if (err == MACH_SEND_WILL_NOTIFY) + { + req->next = display->filemod_reqs_pending; + display->filemod_reqs_pending = req; + } + else + { + mach_port_t old; + + /* Cancel the dead-name notification. */ + mach_port_request_notification (mach_task_self (), req->port, + MACH_NOTIFY_DEAD_NAME, 0, + MACH_PORT_NULL, 0, &old); + mach_port_deallocate (mach_task_self (), old); + mach_port_deallocate (mach_task_self (), req->port); + free (req); + } + } + else + preq = &req->next; + } +} + +static void +display_flush_filechange (display_t display, unsigned int type) +{ + struct cons_display *user = display->user; + cons_change_t *next = &user->changes._buffer[user->changes.written + % _CONS_CHANGES_LENGTH]; + int notify = 0; + int bump_written = 0; + + if (type & DISPLAY_CHANGE_MATRIX + && display->changes.which & DISPLAY_CHANGE_MATRIX) + { + notify = 1; + next->matrix.start = display->changes.start; + next->matrix.end = display->changes.end; + user->changes.written++; + next = &user->changes._buffer[user->changes.written + % _CONS_CHANGES_LENGTH]; + display->changes.which &= ~DISPLAY_CHANGE_MATRIX; + } + + memset (next, 0, sizeof (cons_change_t)); + next->what.not_matrix = 1; + + if (type & DISPLAY_CHANGE_CURSOR_POS + && display->changes.which & DISPLAY_CHANGE_CURSOR_POS + && (display->changes.cursor.col != user->cursor.col + || display->changes.cursor.row != user->cursor.row)) + { + notify = 1; + next->what.cursor_pos = 1; + bump_written = 1; + display->changes.which &= ~DISPLAY_CHANGE_CURSOR_POS; + } + + if (type & DISPLAY_CHANGE_CURSOR_STATUS + && display->changes.which & DISPLAY_CHANGE_CURSOR_STATUS + && display->changes.cursor.status != user->cursor.status) + { + notify = 1; + next->what.cursor_status = 1; + bump_written = 1; + display->changes.which &= ~DISPLAY_CHANGE_CURSOR_STATUS; + } + + if (type & DISPLAY_CHANGE_SCREEN_CUR_LINE + && display->changes.which & DISPLAY_CHANGE_SCREEN_CUR_LINE + && display->changes.screen.cur_line != user->screen.cur_line) + { + notify = 1; + next->what.screen_cur_line = 1; + bump_written = 1; + display->changes.which &= ~DISPLAY_CHANGE_SCREEN_CUR_LINE; + } + + if (type & DISPLAY_CHANGE_SCREEN_SCR_LINES + && display->changes.which & DISPLAY_CHANGE_SCREEN_SCR_LINES + && display->changes.screen.scr_lines != user->screen.scr_lines) + { + notify = 1; + next->what.screen_scr_lines = 1; + bump_written = 1; + display->changes.which &= ~DISPLAY_CHANGE_SCREEN_SCR_LINES; + } + + if (type & DISPLAY_CHANGE_BELL_AUDIBLE + && display->changes.which & DISPLAY_CHANGE_BELL_AUDIBLE + && display->changes.bell_audible != user->bell.audible) + { + notify = 1; + next->what.bell_audible = 1; + bump_written = 1; + display->changes.which &= ~DISPLAY_CHANGE_BELL_AUDIBLE; + } + + if (type & DISPLAY_CHANGE_BELL_VISIBLE + && display->changes.which & DISPLAY_CHANGE_BELL_VISIBLE + && display->changes.bell_visible != user->bell.visible) + { + notify = 1; + next->what.bell_visible = 1; + bump_written = 1; + display->changes.which &= ~DISPLAY_CHANGE_BELL_VISIBLE; + } + + if (type & DISPLAY_CHANGE_FLAGS + && display->changes.which & DISPLAY_CHANGE_FLAGS + && display->changes.flags != user->flags) + { + notify = 1; + next->what.flags = 1; + bump_written = 1; + display->changes.which &= ~DISPLAY_CHANGE_FLAGS; + } + + if (bump_written) + user->changes.written++; + if (notify) + display_notice_filechange (display); +} + +/* Record a change in the matrix ringbuffer. */ +static void +display_record_filechange (display_t display, off_t start, off_t end) +{ + if (!(display->changes.which & DISPLAY_CHANGE_MATRIX)) + { + display->changes.start = start; + display->changes.end = end; + display->changes.which |= DISPLAY_CHANGE_MATRIX; + } + else + { + off_t size = display->user->screen.width * display->user->screen.lines; + off_t rotate = display->changes.start; + off_t old_end = display->changes.end; + int disjunct = 0; + + /* First rotate the buffer to reduce the number of cases. */ + old_end -= rotate; + if (old_end < 0) + old_end += size; + start -= rotate; + if (start < 0) + start += size; + end -= rotate; + if (end < 0) + end += size; + + /* Now the old region starts at 0 and ends at OLD_END. Try to + merge in the new region if it overlaps or touches the old + one. */ + if (start <= end) + { + if (start <= old_end + 1) + { + start = 0; + if (old_end > end) + end = old_end; + } + else + { + if (end == size - 1) + end = old_end; + else + disjunct = 1; + } + } + else + { + if (start <= old_end + 1) + { + start = 0; + end = size - 1; + } + else + { + if (old_end > end) + end = old_end; + } + } + /* Now reverse the rotation. */ + start += rotate; + if (start >= size) + start -= size; + end += rotate; + if (end >= size) + end -= size; + + if (disjunct) + { + /* The regions are disjunct, so we have to flush the old + changes. */ + display_flush_filechange (display, DISPLAY_CHANGE_MATRIX); + display->changes.which |= DISPLAY_CHANGE_MATRIX; + } + display->changes.start = start; + display->changes.end = end; + } +} + + +static void +conchar_memset (conchar_t *conchar, wchar_t chr, conchar_attr_t attr, + size_t size) +{ + int i; + + for (i = 0; i < size; i++) + { + conchar->chr = chr; + conchar->attr = attr; + conchar++; + } +} + + +static error_t +user_create (display_t display, uint32_t width, uint32_t height, + uint32_t lines, wchar_t chr, conchar_attr_t attr) +{ + error_t err; + struct cons_display *user; + int npages = (round_page (sizeof (struct cons_display) + sizeof (conchar_t) + * width * lines)) / vm_page_size; + + err = user_pager_create (&display->user_pager, npages, &display->user); + if (err) + return err; + + user = display->user; + user->magic = CONS_MAGIC; + user->version = CONS_VERSION_MAJ << CONS_VERSION_MAJ_SHIFT | CONS_VERSION_AGE; + user->changes.buffer = offsetof (struct cons_display, changes._buffer) + / sizeof (uint32_t); + user->changes.length = _CONS_CHANGES_LENGTH; + user->screen.width = width; + user->screen.height = height; + user->screen.lines = lines; + user->screen.cur_line = 0; + user->screen.scr_lines = 0; + user->screen.matrix = sizeof (struct cons_display) / sizeof (uint32_t); + user->cursor.col = 0; + user->cursor.row = 0; + user->cursor.status = CONS_CURSOR_NORMAL; + conchar_memset (user->_matrix, chr, attr, + user->screen.width * user->screen.lines); + return 0; +} + +static void +user_destroy (display_t display) +{ + user_pager_destroy (&display->user_pager, display->user); +} + + +static void +screen_fill (display_t display, size_t col1, size_t row1, size_t col2, + size_t row2, wchar_t chr, conchar_attr_t attr) +{ + struct cons_display *user = display->user; + off_t start = ((user->screen.cur_line % user->screen.lines) + row1) + * user->screen.width + col1; + off_t end = ((user->screen.cur_line % user->screen.lines) + row2) + * user->screen.width + col2; + off_t size = user->screen.width * user->screen.lines; + + if (start >= size && end >= size) + { + start -= size; + end -= size; + } + + if (end < size) + { + conchar_memset (user->_matrix + start, chr, attr, end - start + 1); + display_record_filechange (display, start, end); + } + else + { + conchar_memset (user->_matrix + start, chr, attr, size - start); + conchar_memset (user->_matrix, chr, attr, end - size + 1); + display_record_filechange (display, start, end - size); + } +} + +static void +screen_shift_left (display_t display, size_t col1, size_t row1, size_t col2, + size_t row2, size_t shift, wchar_t chr, conchar_attr_t attr) +{ + struct cons_display *user = display->user; + off_t start = ((user->screen.cur_line % user->screen.lines) + row1) + * user->screen.width + col1; + off_t end = ((user->screen.cur_line % user->screen.lines) + row2) + * user->screen.width + col2; + off_t size = user->screen.width * user->screen.lines; + + if (start >= size && end >= size) + { + start -= size; + end -= size; + } + + if (start + shift <= end) + { + /* Use a loop to copy the data. Using wmemmove and wmemset on + the chunks is tiresome, as there are many cases. */ + off_t src = start + shift; + off_t dst = start; + + while (src <= end) + user->_matrix[dst++ % size] = user->_matrix[src++ % size]; + while (dst <= end) + { + user->_matrix[dst % size].chr = chr; + user->_matrix[dst++ % size].attr = attr; + } + + display_record_filechange (display, start, end); + } + else + screen_fill (display, col1, row1, col2, row2, chr, attr); +} + +static void +screen_shift_right (display_t display, size_t col1, size_t row1, size_t col2, + size_t row2, size_t shift, + wchar_t chr, conchar_attr_t attr) +{ + struct cons_display *user = display->user; + off_t start = ((user->screen.cur_line % user->screen.lines) + row1) + * user->screen.width + col1; + off_t end = ((user->screen.cur_line % user->screen.lines) + row2) + * user->screen.width + col2; + off_t size = user->screen.width * user->screen.lines; + + if (start >= size && end >= size) + { + start -= size; + end -= size; + } + + if (start + shift <= end) + { + /* Use a loop to copy the data. Using wmemmove and wmemset on + the chunks is tiresome, as there are many cases. */ + off_t src = end - shift; + off_t dst = end; + + while (src >= start) + user->_matrix[dst-- % size] = user->_matrix[src-- % size]; + while (dst >= start) + { + user->_matrix[dst % size].chr = chr; + user->_matrix[dst-- % size].attr = attr; + } + + display_record_filechange (display, start, end); + } + else + screen_fill (display, col1, row1, col2, row2, chr, attr); +} + + +static error_t +output_init (output_t output, const char *encoding) +{ + 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. */ + output->cd = iconv_open ("WCHAR_T", encoding); + if (output->cd == (iconv_t) -1) + return errno; + return 0; +} + +static void +output_deinit (output_t output) +{ + iconv_close (output->cd); +} + + +static void +handle_esc_bracket_hl (display_t display, int code, int flag) +{ + switch (code) + { + case 4: /* ECMA-48 <SMIR>, <RMIR>. */ + /* Insert mode: <smir>, <rmir>. */ + display->insert_mode = flag ? 1 : 0; + break; + case 34: + /* Cursor standout: <cnorm>, <cvvis>. */ + if (flag) + display->user->cursor.status = CONS_CURSOR_NORMAL; + else + display->user->cursor.status = CONS_CURSOR_VERY_VISIBLE; + /* XXX Flag cursor status change. */ + break; + } +} + +static void +handle_esc_bracket_m (attr_t attr, int code) +{ + switch (code) + { + case 0: + /* All attributes off: <sgr0>. */ + attr->current = attr->attr_def; + attr->altchar = 0; + break; + case 1: + /* Bold on: <bold>. */ + attr->current.intensity = CONS_ATTR_INTENSITY_BOLD; + break; + case 2: + /* Dim on: <dim>. */ + attr->current.intensity = CONS_ATTR_INTENSITY_DIM; + break; + case 3: + /* Italic on: <sitm>. */ + attr->current.italic = 1; + break; + case 4: + /* Underline on: <smul>. */ + attr->current.underlined = 1; + break; + case 5: + /* (Slow) blink on: <blink>. */ + attr->current.blinking = 1; + break; + case 7: + /* Reverse video on: <rev>, <smso>. */ + attr->current.reversed = 1; + break; + case 8: + /* Concealed on: <invis>. */ + attr->current.concealed = 1; + break; + case 10: + /* Alternate character set mode off: <rmacs>. */ + attr->altchar = 0; + break; + case 11: + /* Alternate character set mode on: <smacs>. */ + attr->altchar = 1; + break; + case 21: + /* Normal intensity (switch off bright). */ + attr->current.intensity = CONS_ATTR_INTENSITY_NORMAL; + break; + case 22: + /* Normal intensity (switch off dim). */ + attr->current.intensity = CONS_ATTR_INTENSITY_NORMAL; + break; + case 23: + /* Italic off: <ritm>. */ + attr->current.italic = 0; + break; + case 24: + /* Underline off: <rmul>. */ + attr->current.underlined = 0; + break; + case 25: + /* Blink off. */ + attr->current.blinking = 0; + break; + case 27: + /* Reverse video off: <rmso>. */ + attr->current.reversed = 0; + break; + case 28: + /* Concealed off. */ + attr->current.concealed = 0; + break; + case 30 ... 37: + /* Set foreground color: <setaf>. */ + attr->current.fgcol = code - 30; + break; + case 39: + /* Default foreground color; ANSI?. */ + attr->current.fgcol = attr->attr_def.fgcol; + break; + case 40 ... 47: + /* Set background color: <setab>. */ + attr->current.bgcol = code - 40; + break; + case 49: + /* Default background color; ANSI?. */ + attr->current.bgcol = attr->attr_def.bgcol; + break; + } +} + +static +void limit_cursor (display_t display) +{ + struct cons_display *user = display->user; + + if (user->cursor.col >= user->screen.width) + user->cursor.col = user->screen.width - 1; + else if (user->cursor.col < 0) + user->cursor.col = 0; + + if (user->cursor.row >= user->screen.height) + user->cursor.row = user->screen.height - 1; + else if (user->cursor.row < 0) + user->cursor.row = 0; + + /* XXX Flag cursor change. */ +} + + +static void +linefeed (display_t display) +{ + struct cons_display *user = display->user; + + if (display->csr.top == 0 && display->csr.bottom >= user->screen.height - 1) + { + /* No scrolling region active, do the normal scrolling activity. */ + if (user->cursor.row < user->screen.height - 1) + { + user->cursor.row++; + /* XXX Flag cursor update. */ + } + else + { + user->screen.cur_line++; + + screen_fill (display, 0, user->screen.height - 1, + user->screen.width - 1, user->screen.height - 1, + L' ', display->attr.current); + if (user->screen.scr_lines < + user->screen.lines - user->screen.height) + user->screen.scr_lines++; + /* XXX Flag current line change. */ + /* XXX Possibly flag change of length of scroll back buffer. */ + } + } + else + { + /* With an active scrolling region, never actually scroll. Just + shift the scrolling region if necessary. */ + if (user->cursor.row != display->csr.bottom + && user->cursor.row < user->screen.height - 1) + { + user->cursor.row++; + /* XXX Flag cursor update. */ + } + else if (user->cursor.row == display->csr.bottom) + screen_shift_left (display, 0, display->csr.top, + user->screen.width - 1, display->csr.bottom, + user->screen.width, + L' ', display->attr.current); + } +} + +static void +horizontal_tab (display_t display) +{ + struct cons_display *user = display->user; + + user->cursor.col = (user->cursor.col | 7) + 1; + if (user->cursor.col >= user->screen.width) + { + user->cursor.col = 0; + linefeed (display); + } + /* XXX Flag cursor update. */ +} + +static void +handle_esc_bracket (display_t display, char op) +{ + struct cons_display *user = display->user; + parse_t parse = &display->output.parse; + int i; + + switch (op) + { + case 'H': /* ECMA-48 <CUP>. */ + case 'f': /* ECMA-48 <HVP>. */ + /* Cursor position: <cup>. */ + user->cursor.col = (parse->params[1] ?: 1) - 1; + user->cursor.row = (parse->params[0] ?: 1) - 1; + limit_cursor (display); + break; + case 'G': /* ECMA-48 <CHA>. */ + case '`': /* ECMA-48 <HPA>. */ + case '\'': /* VT100. */ + /* Horizontal cursor position: <hpa>. */ + user->cursor.col = (parse->params[0] ?: 1) - 1; + limit_cursor (display); + break; + case 'a': /* ECMA-48 <HPR>. */ + /* Horizontal cursor position relative. */ + user->cursor.col += (parse->params[1] ?: 1) - 1; + limit_cursor (display); + break; + case 'd': /* ECMA-48 <VPA>. */ + /* Vertical cursor position: <vpa>. */ + user->cursor.row = (parse->params[0] ?: 1) - 1; + limit_cursor (display); + break; + case 'F': /* ECMA-48 <CPL>. */ + /* Beginning of previous line. */ + user->cursor.col = 0; + /* Fall through. */ + case 'A': /* ECMA-48 <CUU>. */ + case 'k': /* ECMA-48 <VPB>. */ + /* Cursor up: <cuu>, <cuu1>. */ + user->cursor.row -= (parse->params[0] ?: 1); + limit_cursor (display); + break; + case 'E': /* ECMA-48 <CNL>. */ + /* Beginning of next line. */ + user->cursor.col = 0; + /* Fall through. */ + case 'B': /* ECMA-48 <CUD>. */ + case 'e': /* ECMA-48 <VPR>. */ + /* Cursor down: <cud1>, <cud>. */ + user->cursor.row += (parse->params[0] ?: 1); + limit_cursor (display); + break; + case 'C': /* ECMA-48 <CUF>. */ + /* Cursor right: <cuf1>, <cuf>. */ + user->cursor.col += (parse->params[0] ?: 1); + limit_cursor (display); + break; + case 'D': /* ECMA-48 <CUB>. */ + /* Cursor left: <cub>, <cub1>. */ + user->cursor.col -= (parse->params[0] ?: 1); + limit_cursor (display); + break; + case 'l': + /* Reset mode. */ + for (i = 0; i < parse->nparams; i++) + handle_esc_bracket_hl (display, parse->params[i], 0); + break; + case 'h': + /* Set mode. */ + for (i = 0; i < parse->nparams; i++) + handle_esc_bracket_hl (display, parse->params[i], 1); + break; + case 'm': /* ECMA-48 <SGR>. */ + for (i = 0; i < parse->nparams; i++) + handle_esc_bracket_m (&display->attr, parse->params[i]); + break; + case 'J': /* ECMA-48 <ED>. */ + switch (parse->params[0]) + { + case 0: + /* Clear to end of screen: <ed>. */ + screen_fill (display, user->cursor.col, user->cursor.row, + user->screen.width - 1, user->screen.height - 1, + L' ', display->attr.current); + break; + case 1: + /* Clear to beginning of screen. */ + screen_fill (display, 0, 0, + user->cursor.col, user->cursor.row, + L' ', display->attr.current); + break; + case 2: + /* Clear entire screen. */ + screen_fill (display, 0, 0, + user->screen.width - 1, user->screen.height - 1, + L' ', display->attr.current); + break; + } + break; + case 'K': /* ECMA-48 <EL>. */ + switch (parse->params[0]) + { + case 0: + /* Clear to end of line: <el>. */ + screen_fill (display, user->cursor.col, user->cursor.row, + user->screen.width - 1, user->cursor.row, + L' ', display->attr.current); + break; + case 1: + /* Clear to beginning of line: <el1>. */ + screen_fill (display, 0, user->cursor.row, + user->cursor.col, user->cursor.row, + L' ', display->attr.current); + break; + case 2: + /* Clear entire line. */ + screen_fill (display, 0, user->cursor.row, + user->screen.width - 1, user->cursor.row, + L' ', display->attr.current); + break; + } + break; + case 'L': /* ECMA-48 <IL>. */ + /* Insert line(s): <il1>, <il>. */ + screen_shift_right (display, 0, user->cursor.row, + user->screen.width - 1, + (user->cursor.row <= display->csr.bottom) + ? display->csr.bottom : user->screen.height - 1, + (parse->params[0] ?: 1) * user->screen.width, + L' ', display->attr.current); + break; + case 'M': /* ECMA-48 <DL>. */ + /* Delete line(s): <dl1>, <dl>. */ + screen_shift_left (display, 0, user->cursor.row, + user->screen.width - 1, + (user->cursor.row <= display->csr.bottom) + ? display->csr.bottom : user->screen.height - 1, + (parse->params[0] ?: 1) * user->screen.width, + L' ', display->attr.current); + break; + case '@': /* ECMA-48 <ICH>. */ + /* Insert character(s): <ich1>, <ich>. */ + screen_shift_right (display, user->cursor.col, user->cursor.row, + user->screen.width - 1, user->cursor.row, + parse->params[0] ?: 1, + L' ', display->attr.current); + break; + case 'P': /* ECMA-48 <DCH>. */ + /* Delete character(s): <dch1>, <dch>. */ + screen_shift_left (display, user->cursor.col, user->cursor.row, + user->screen.width - 1, user->cursor.row, + parse->params[0] ?: 1, + L' ', display->attr.current); + break; + case 'r': /* VT100: Set scrolling region. */ + if (!parse->params[1]) + { + display->csr.top = 0; + display->csr.bottom = user->screen.height - 1; + } + else + { + if (parse->params[1] <= user->screen.height + && parse->params[0] < parse->params[1]) + { + display->csr.top = parse->params[0] ? parse->params[0] - 1 : 0; + display->csr.bottom = parse->params[1] - 1; + user->cursor.col = 0; + user->cursor.row = 0; + /* XXX Flag cursor change. */ + } + } + break; + case 'S': /* ECMA-48 <SU>. */ + /* Scroll up: <ind>, <indn>. */ + screen_shift_left (display, 0, display->csr.top, + user->screen.width - 1, display->csr.bottom, + (parse->params[0] ?: 1) * user->screen.width, + L' ', display->attr.current); + break; + case 'T': /* ECMA-48 <SD>. */ + /* Scroll down: <ri>, <rin>. */ + screen_shift_right (display, 0, display->csr.top, + user->screen.width - 1, display->csr.bottom, + (parse->params[0] ?: 1) * user->screen.width, + L' ', display->attr.current); + break; + case 'X': /* ECMA-48 <ECH>. */ + /* Erase character(s): <ech>. */ + { + int col = user->cursor.col; + if (parse->params[0] - 1 > 0) + col += parse->params[0] - 1; + if (col > user->screen.width - 1) + col = user->screen.width - 1; + + screen_fill (display, user->cursor.col, user->cursor.row, + col, user->cursor.row, L' ', display->attr.current); + } + break; + case 'I': /* ECMA-48 <CHT>. */ + /* Horizontal tab. */ + if (!parse->params[0]) + parse->params[0] = 1; + while (parse->params[0]--) + horizontal_tab (display); + break; + case 'Z': /* ECMA-48 <CBT>. */ + /* Cursor backward tabulation: <cbt>. */ + if (parse->params[0] > user->screen.height * (user->screen.width / 8)) + { + user->cursor.col = 0; + user->cursor.row = 0; + } + else + { + int i = parse->params[0] ?: 1; + + while (i--) + { + if (user->cursor.col == 0) + { + if (user->cursor.row == 0) + break; + else + { + user->cursor.col = user->screen.width - 1; + user->cursor.row--; + } + } + else + user->cursor.col--; + user->cursor.col &= ~7; + } + } + } +} + + +static void +handle_esc_bracket_question_hl (display_t display, int code, int flag) +{ + switch (code) + { + case 25: + /* Cursor invisibility: <civis>, <cnorm>. */ + if (flag) + display->user->cursor.status = CONS_CURSOR_NORMAL; + else + display->user->cursor.status = CONS_CURSOR_INVISIBLE; + /* XXX Flag cursor status change. */ + break; + case 1000: + /* XTerm mouse tracking. */ + if (flag) + display->user->flags |= CONS_FLAGS_TRACK_MOUSE; + else + display->user->flags &= ~CONS_FLAGS_TRACK_MOUSE; + /* XXX Flag flags change. */ + break; + } +} + + +static void +handle_esc_bracket_question (display_t display, char op) +{ + parse_t parse = &display->output.parse; + + int i; + switch (op) + { + case 'l': + /* Reset mode. */ + for (i = 0; i < parse->nparams; ++i) + handle_esc_bracket_question_hl (display, parse->params[i], 0); + break; + case 'h': + /* Set mode. */ + for (i = 0; i < parse->nparams; ++i) + handle_esc_bracket_question_hl (display, parse->params[i], 1); + break; + } +} + + +static void +handle_esc_bracket_right_angle_hl (display_t display, int code, int flag) +{ + switch (code) + { + case 1: + /* Bold: <gsbom>, <grbom>. This is a GNU extension. */ + if (flag) + display->attr.current.bold = 1; + else + display->attr.current.bold = 0; + break; + } +} + + +static void +handle_esc_bracket_right_angle (display_t display, char op) +{ + parse_t parse = &display->output.parse; + + int i; + switch (op) + { + case 'l': + /* Reset mode. */ + for (i = 0; i < parse->nparams; ++i) + handle_esc_bracket_right_angle_hl (display, parse->params[i], 0); + break; + case 'h': + /* Set mode. */ + for (i = 0; i < parse->nparams; ++i) + handle_esc_bracket_right_angle_hl (display, parse->params[i], 1); + break; + } +} + + +static wchar_t +altchar_to_ucs4 (wchar_t chr) +{ + /* Alternative character set frobbing. */ + switch (chr) + { + case L'+': + return CONS_CHAR_RARROW; + case L',': + return CONS_CHAR_LARROW; + case L'-': + return CONS_CHAR_UARROW; + case L'.': + return CONS_CHAR_DARROW; + case L'0': + return CONS_CHAR_BLOCK; + case L'I': + return CONS_CHAR_LANTERN; + case L'`': + return CONS_CHAR_DIAMOND; + case L'a': + return CONS_CHAR_CKBOARD; + case L'f': + return CONS_CHAR_DEGREE; + case L'g': + return CONS_CHAR_PLMINUS; + case L'h': + return CONS_CHAR_BOARD; + case L'j': + return CONS_CHAR_LRCORNER; + case L'k': + return CONS_CHAR_URCORNER; + case L'l': + return CONS_CHAR_ULCORNER; + case L'm': + return CONS_CHAR_LLCORNER; + case L'n': + return CONS_CHAR_PLUS; + case L'o': + return CONS_CHAR_S1; + case L'p': + return CONS_CHAR_S3; + case L'q': + return CONS_CHAR_HLINE; + case L'r': + return CONS_CHAR_S7; + case L's': + return CONS_CHAR_S9; + case L't': + return CONS_CHAR_LTEE; + case L'u': + return CONS_CHAR_RTEE; + case L'v': + return CONS_CHAR_BTEE; + case L'w': + return CONS_CHAR_TTEE; + case L'x': + return CONS_CHAR_VLINE; + case L'y': + return CONS_CHAR_LEQUAL; + case L'z': + return CONS_CHAR_GEQUAL; + case L'{': + return CONS_CHAR_PI; + case L'|': + return CONS_CHAR_NEQUAL; + case L'}': + return CONS_CHAR_STERLING; + case L'~': + return CONS_CHAR_BULLET; + default: + return chr; + } +} + +/* Display must be locked. */ +static void +display_output_one (display_t display, wchar_t chr) +{ + struct cons_display *user = display->user; + parse_t parse = &display->output.parse; + + switch (parse->state) + { + case STATE_NORMAL: + switch (chr) + { + case L'\r': + /* Carriage return: <cr>. */ + if (user->cursor.col) + { + user->cursor.col = 0; + /* XXX Flag cursor update. */ + } + break; + case L'\n': + /* Line feed. */ + linefeed (display); + break; + case L'\b': + /* Backspace. */ + if (user->cursor.col > 0 || user->cursor.row > 0) + { + if (user->cursor.col > 0) + user->cursor.col--; + else + { + /* This implements the <bw> functionality. */ + user->cursor.col = user->screen.width - 1; + user->cursor.row--; + } + /* XXX Flag cursor update. */ + } + break; + case L'\t': + /* Horizontal tab: <ht> */ + horizontal_tab (display); + break; + case L'\033': + parse->state = STATE_ESC; + break; + case L'\0': + /* Padding character: <pad>. */ + break; + case L'\a': + /* Audible bell. */ + user->bell.audible++; + break; + default: + { + int line; + int idx; + + if (user->cursor.col >= user->screen.width) + { + user->cursor.col = 0; + linefeed (display); + } + + line = (user->screen.cur_line + user->cursor.row) + % user->screen.lines; + idx = line * user->screen.width + user->cursor.col; + int width, i; + + width = wcwidth (chr); + if (width < 0) + width = 1; + + if (display->insert_mode + && user->cursor.col < user->screen.width - width) + { + /* If in insert mode, do the same as <ich1>. */ + screen_shift_right (display, user->cursor.col, + user->cursor.row, + user->screen.width - 1, user->cursor.row, + width, L' ', display->attr.current); + } + + if (display->attr.altchar) + chr = altchar_to_ucs4 (chr); + + for (i = 0; i < width; i++) + { + if (user->cursor.col >= user->screen.width) + break; + user->_matrix[idx+i].chr = chr; + user->_matrix[idx+i].attr = display->attr.current; + user->cursor.col++; + chr |= CONS_WCHAR_CONTINUED; + } + + if (i > 0) + display_record_filechange (display, idx, idx + i - 1); + + if (user->cursor.col > user->screen.width) + { + user->cursor.col = 0; + linefeed (display); + } + } + break; + } + break; + + case STATE_ESC: + parse->state = STATE_NORMAL; + switch (chr) + { + case L'[': + parse->state = STATE_ESC_BRACKET_INIT; + break; + case L'M': /* ECMA-48 <RIS>. */ + /* Reset: <rs2>. */ + display->attr.current = display->attr.attr_def; + display->attr.altchar = 0; + display->insert_mode = 0; + display->csr.top = 0; + display->csr.bottom = user->screen.height - 1; + user->cursor.status = CONS_CURSOR_NORMAL; + /* Fall through. */ + case L'c': + /* Clear screen and home cursor: <clear>. */ + screen_fill (display, 0, 0, + user->screen.width - 1, user->screen.height - 1, + L' ', display->attr.current); + user->cursor.col = user->cursor.row = 0; + /* XXX Flag cursor change. */ + break; + case L'E': /* ECMA-48 <NEL>. */ + /* Newline. */ + user->cursor.col = 0; + linefeed (display); + break; + case L'7': /* VT100: Save cursor and attributes. */ + /* Save cursor position: <sc>. */ + display->cursor.saved_x = user->cursor.col; + display->cursor.saved_y = user->cursor.row; + break; + case L'8': /* VT100: Restore cursor and attributes. */ + /* Restore cursor position: <rc>. */ + user->cursor.col = display->cursor.saved_x; + user->cursor.row = display->cursor.saved_y; + /* In case the screen was larger before: */ + limit_cursor (display); + break; + case L'g': + /* Visible bell. */ + user->bell.visible++; + break; + default: + /* Unsupported escape sequence. */ + break; + } + break; + + case STATE_ESC_BRACKET_INIT: + memset (&parse->params, 0, sizeof parse->params); + parse->nparams = 0; + if (chr == '?') + { + parse->state = STATE_ESC_BRACKET_QUESTION; + break; /* Consume the question mark. */ + } + else if (chr == '>') + { + parse->state = STATE_ESC_BRACKET_RIGHT_ANGLE; + break; /* Consume the right angle. */ + } + else + parse->state = STATE_ESC_BRACKET; + /* Fall through. */ + case STATE_ESC_BRACKET: + case STATE_ESC_BRACKET_QUESTION: + case STATE_ESC_BRACKET_RIGHT_ANGLE: + if (chr >= '0' && chr <= '9') + parse->params[parse->nparams] + = parse->params[parse->nparams]*10 + chr - '0'; + else if (chr == ';') + { + if (++(parse->nparams) >= PARSE_MAX_PARAMS) + parse->state = STATE_NORMAL; /* too many */ + } + else + { + parse->nparams++; + if (parse->state == STATE_ESC_BRACKET) + handle_esc_bracket (display, chr); + else if (parse->state == STATE_ESC_BRACKET_RIGHT_ANGLE) + handle_esc_bracket_right_angle (display, chr); + else + handle_esc_bracket_question (display, chr); + parse->state = STATE_NORMAL; + } + break; + default: + abort (); + } +} + +/* 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. */ +static error_t +display_output_some (display_t display, char **buffer, size_t *length) +{ +#define CONV_OUTBUF_SIZE 256 + error_t err = 0; + + display->changes.cursor.col = display->user->cursor.col; + display->changes.cursor.row = display->user->cursor.row; + display->changes.cursor.status = display->user->cursor.status; + display->changes.screen.cur_line = display->user->screen.cur_line; + display->changes.screen.scr_lines = display->user->screen.scr_lines; + display->changes.bell_audible = display->user->bell.audible; + display->changes.bell_visible = display->user->bell.visible; + display->changes.flags = display->user->flags; + display->changes.which = ~DISPLAY_CHANGE_MATRIX; + + while (!err && *length > 0) + { + size_t nconv; + wchar_t outbuf[CONV_OUTBUF_SIZE]; + char *outptr = (char *) outbuf; + size_t outsize = CONV_OUTBUF_SIZE * sizeof (wchar_t); + error_t saved_err; + int i; + + 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 / sizeof (wchar_t); i++) + display_output_one (display, outbuf[i]); + + if (nconv == (size_t) -1) + { + /* Conversion is not completed, look for recoverable + errors. */ +#define UNICODE_REPLACEMENT_CHARACTER ((wchar_t) 0xfffd) + if (saved_err == EILSEQ) + { + assert (*length); + (*length)--; + (*buffer)++; + display_output_one (display, UNICODE_REPLACEMENT_CHARACTER); + } + else if (saved_err == EINVAL) + /* This is only an unfinished byte sequence at the end of + the input buffer. */ + break; + else if (saved_err != E2BIG) + err = saved_err; + } + } + + display_flush_filechange (display, ~0); + return err; +} + + +/* Forward declaration. */ +void display_destroy_complete (void *pi); + +void +display_init (void) +{ + user_pager_init (); + + /* Create the notify bucket, and start to serve notifications. */ + notify_bucket = ports_create_bucket (); + if (! notify_bucket) + error (5, errno, "Cannot create notify bucket"); + notify_class = ports_create_class (display_destroy_complete, NULL); + if (! notify_class) + error (5, errno, "Cannot create notify class"); + + cthread_detach (cthread_fork ((cthread_fn_t) service_notifications, + (any_t) notify_bucket)); +} + + +/* Create a new virtual console display, with the system encoding + being ENCODING. */ +error_t +display_create (display_t *r_display, const char *encoding, + conchar_attr_t def_attr, unsigned int lines, + unsigned int width, unsigned int height) +{ + error_t err = 0; + display_t display; + + *r_display = NULL; + display = calloc (1, sizeof *display); + if (!display) + return ENOMEM; + + err = ports_create_port (notify_class, notify_bucket, sizeof (struct notify), + &display->notify_port); + if (err) + { + free (display); + return err; + } + display->notify_port->display = display; + + mutex_init (&display->lock); + display->attr.attr_def = def_attr; + display->attr.current = display->attr.attr_def; + display->csr.bottom = height - 1; + + err = user_create (display, width, height, lines, L' ', + display->attr.current); + if (err) + { + ports_destroy_right (display->notify_port); + free (display); + return err; + } + + err = output_init (&display->output, encoding); + if (err) + { + user_destroy (display); + ports_destroy_right (display->notify_port); + free (display); + } + *r_display = display; + return err; +} + + +/* Destroy the display DISPLAY. */ +void +display_destroy (display_t display) +{ + mutex_lock (&display->lock); + if (display->filemod_reqs_pending) + { + free_modreqs (display->filemod_reqs_pending); + display->filemod_reqs_pending = NULL; + } + if (display->filemod_reqs) + { + free_modreqs (display->filemod_reqs); + display->filemod_reqs = NULL; + } + ports_destroy_right (display->notify_port); + output_deinit (&display->output); + user_destroy (display); + mutex_unlock (&display->lock); + + /* We can not free the display structure here, because it might + still be needed by pending modification requests when msg + accepted notifications are handled. So we have to wait until all + notifications have arrived and the notify port is completely + deallocated, which will invoke display_destroy_complete + below. */ +} + + +/* Complete destruction of the display DISPLAY. */ +void +display_destroy_complete (void *pi) +{ + struct display *display = ((struct notify *) pi)->display; + free (display); +} + + +/* Return the dimension of the display in bytes. */ +off_t +display_get_size (display_t display) +{ + return sizeof (struct cons_display) + + (sizeof (conchar_t) * display->user->screen.width + * display->user->screen.lines); +} + + +/* Return the dimensions of the display DISPLAY in *WINSIZE. */ +void +display_getsize (display_t display, struct winsize *winsize) +{ + mutex_lock (&display->lock); + winsize->ws_row = display->user->screen.height; + winsize->ws_col = display->user->screen.width; + winsize->ws_xpixel = 0; + 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; + } + 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); + memcpy (data, ((char *) display->user) + 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); + } + display->changes.flags = display->user->flags; + display->changes.which = DISPLAY_CHANGE_FLAGS; + display->user->flags &= ~CONS_FLAGS_SCROLL_LOCK; + display_flush_filechange (display, DISPLAY_CHANGE_FLAGS); + 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; + display->changes.flags = display->user->flags; + display->changes.which = DISPLAY_CHANGE_FLAGS; + display->user->flags |= CONS_FLAGS_SCROLL_LOCK; + display_flush_filechange (display, DISPLAY_CHANGE_FLAGS); + 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); +} + + +mach_port_t +display_get_filemap (display_t display, vm_prot_t prot) +{ + mach_port_t memobj; + mutex_lock (&display->lock); + memobj = user_pager_get_filemap (&display->user_pager, prot); + mutex_unlock (&display->lock); + return memobj; +} diff --git a/console/display.h b/console/display.h new file mode 100644 index 00000000..d64cf69e --- /dev/null +++ b/console/display.h @@ -0,0 +1,84 @@ +/* display.h - Interface to the display component of a virtual console. + Copyright (C) 2002, 2003 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 DISPLAY_H +#define DISPLAY_H + +#include <sys/ioctl.h> + +struct display; +typedef struct display *display_t; + +void display_init (void); + +/* Create a new virtual console display, with the system encoding + being ENCODING and the default colors being FOREGROUND and BACKGROUND. */ +error_t +display_create (display_t *r_display, const char *encoding, + conchar_attr_t def_attr, unsigned int lines, + unsigned int width, unsigned int height); + + +/* Destroy the display DISPLAY. */ +void display_destroy (display_t display); + +/* Return the dimension of the display in bytes. */ +off_t display_get_size (display_t display); + +/* 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); + +mach_port_t display_get_filemap (display_t display, vm_prot_t prot); + +ssize_t display_read (display_t display, int nonblock, off_t off, + char *data, size_t len); + +error_t display_notice_changes (display_t display, mach_port_t notify); + +/* 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/hurd.ti b/console/hurd.ti new file mode 100644 index 00000000..2508482a --- /dev/null +++ b/console/hurd.ti @@ -0,0 +1,164 @@ +hurd|The GNU Hurd console server, +# Over-all properties. +# We use 8-bit characters + km, +# Although we don't do XON/XOFF, we don't want padding characters. + xon, +# Hard reset. + rs1=\EM, + +# Note about compatibility to vt100: We don't specify <xenl>, as we +# don't have the eat_newline_glitch. We don't support setting or +# removing tab stops (hts/tbc). + +# Cursor related capabilities. + +# Moving the cursor. +# We have automatic margins. + am, +# We wrap around the left edge. + bw, +# Carriage return and newline. + cr=^M, nel=^M^J, +# Move cursor to home position (to position P1, P2). + home=\E[H, cup=\E[%i%p1%d;%p2%dH, +# Move cursor one character (P1 characters) backwards. +# We use ^H instead \E[D for cub1, as only ^H implements <bw> and it +# is one byte instead three. + cub1=^H, cub=\E[%p1%dD, +# Move cursor one line (P1 lines) downwards. + cud1=\E[B, cud=\E[%p1%dB, +# Move cursor one character (P1 characters) forwards. + cuf1=\E[C, cuf=\E[%p1%dC, +# Move cursor one line (P1 lines) upwards. + cuu1=\E[A, cuu=\E[%p1%dA, +# Set horizontal (vertical) cursor position to P1. + hpa=\E[%i%p1%dG, vpa=\E[%i%p1%dd, +# Save (restore) cursor position. + sc=\E7, rc=\E8, +# Set the scrolling region to lines P1 to P2. + csr=\E[%i%p1%d;%p2%dr, + +# Modifying cursor attributes. +# Make cursor invisible, very visible or normal. + civis=\E[?25l, cvvis=\E[34l, cnorm=\E[?25h, + +# Tabulator stops. +# We have tabulator stops every eight rows. + it#8, +# Move cursor to next tabulator stop. + ht=^I, +# Move cursor to previous tabulator stop. + cbt=\E[Z, +# XXX When we implement this. +# Set tab stop in the current column of every row. +# hts=\EH, +# Delete all tab stops. +# tbc=\E[3g, + + +# Screen editing capabilities. +# Clear screen. + clear=\Ec, +# Clear to end of screen. + ed=\E[J, +# Clear to end (beginning) of line. + el=\E[K, el1=\E[1K, + +# Insert one character (P1 characters). +# <ich1> not included because we have insert mode. +# ich1=\E[@, + ich=\E[%p1%d@, +# Enter (leave) insert mode. + smir=\E[4h, rmir=\E[4l, +# It is save to move when in insert mode. + mir, +# Delete one character (P1 characters). + dch1=\E[P, dch=\E[%p1%dP, +# Erase the next N characters. + ech=\E[%p1%dX, +# Insert one line (P1 lines). + il1=\E[L, il=\E[%p1%dL, +# Delete one line (P1 lines). + dl1=\E[M, dl=\E[%p1%dM, +# Scroll the whole screen one line (P1 lines) upwards. We don't use +# ^J, because this could put things into the scrollback buffer. + ind=\E[S, indn=\E[%p1%dS, +# Scroll the whole screen one line (P1 lines) downwards. + rin=\E[%p1%dT, ri=\E[T, + + +# Bell capabilities. +# Audible bell. + bel=^G, +# Flash the screen (visible bell). + flash=\Eg, + + +# Keycodes for special keys. +# Backspace key. + kbs=\177, +# Keycode for left, down, right and up arrow key. + kcub1=\EOD, kcud1=\EOB, kcuf1=\EOC, kcuu1=\EOA, +# Keycodes for function keys. + kf1=\EOP, kf2=\EOQ, kf3=\EOR, kf4=\EOS, kf5=\E[15~, + kf6=\E[17~, kf7=\E[18~, kf8=\E[19~, kf9=\E[20~, + kf10=\E[21~, kf11=\E[23~, kf12=\E[24~, kf13=\E[25~, + kf14=\E[26~, kf15=\E[28~, kf16=\E[29~, kf17=\E[31~, + kf18=\E[32~, kf19=\E[33~, kf20=\E[34~, +# Keycode for backtab key. + kcbt=\E[Z, +# Keycode for suspend key. + kspd=^Z, +# Keycode for home (insert, delete, end) key. + khome=\E[1~, kich1=\E[2~, kdch1=\E[3~, kend=\E[4~, +# Keycode for previous (next) page key. + kpp=\E[5~, knp=\E[6~, +# Keycode for center of keypad area. + kb2=\E[G, +# Mouse event has occurred. + kmous=\E[M, + +# Text attribute capabilities. + acsc=++\,\,--..00ii``aaffgghhjjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~, + +# Color support. +# We erase the screen with the current background color. + bce, +# Number of colors and color pairs at the same time. + colors#8, + pairs#64, +# Video attributes colliding with color. +# ORed: A_STANDOUT 1, A_UNDERLINE 2, A_REVERSE 4, A_BLINK 8, A_DIM 16, +# A_BOLD 32, A_INVIS 64 +# We don't define this as we do our own display optimization, +# depending on the display driver. Alternatively, we could provide +# different terminfo entries. +# ncv#18, +# Set background (foreground) color. + setab=\E[4%p1%dm, setaf=\E[3%p1%dm, +# Set default color pair to its original value. + op=\E[39;49m, + +# Video attributes. +# Overstrikes are erasable with a blank. + eo, +# It is save to move when in standout mode. + msgr, +# Enable dim (blinking, bold, invisible, reverse) attribute. + dim=\E[2m, blink=\E[5m, bold=\E[1m, invis=\E[8m, rev=\E[7m, +# Enable (disable) standout mode. + smso=\E[7m, rmso=\E[27m, +# Enable (disable) underline mode. + smul=\E[4m, rmul=\E[24m, +# Enable (disable) italic mode. + sitm=\E[3m, ritm=\E[23m, +# Enable (disable) real bold (not intensity bright) mode. This is a +# GNU extension. + gsbom=\E[>1h, grbom=\E[>1l, +# Enable (disable) alternative character set. + smacs=\E[11m, rmacs=\E[10m, +# Set all attributes. + sgr=\E[0%?%p1%t;7%;%?%p2%t;4%;%?%p3%t;7%;%?%p4%t;5%;%?%p5%t;2%;%?%p6%t;1%;%?%p7%t;8%;%?%p9%t;11%;m, +# Reset all attributes. + sgr0=\E[0m, diff --git a/console/input.c b/console/input.c new file mode 100644 index 00000000..14879d07 --- /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 (encoding, "UTF-8"); + 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 = 0; + 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 */ diff --git a/console/motd.UTF8 b/console/motd.UTF8 new file mode 100644 index 00000000..40a104be --- /dev/null +++ b/console/motd.UTF8 @@ -0,0 +1,4 @@ + + + This is the GNU Hurd. Welcome. + diff --git a/console/mutations.h b/console/mutations.h new file mode 100644 index 00000000..a8183fc0 --- /dev/null +++ b/console/mutations.h @@ -0,0 +1,27 @@ +/* mutations.h - Automagic type transformation for MiG interfaces. + 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. */ + +/* Only CPP macro definitions should go in this file. */ + +#define IO_INTRAN protid_t begin_using_protid_port (io_t) +#define IO_DESTRUCTOR end_using_protid_port (protid_t) + +#define TIOCTL_IMPORTS import "priv.h"; + diff --git a/console/pager.c b/console/pager.c new file mode 100644 index 00000000..092a8820 --- /dev/null +++ b/console/pager.c @@ -0,0 +1,221 @@ +/* pager.c - The pager for the display 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 <errno.h> +#include <assert.h> +#include <error.h> + +#include <sys/mman.h> +#include <cthreads.h> + +#include <hurd.h> +#include <hurd/pager.h> +#include <hurd/console.h> + +#include "pager.h" + + +struct user_pager_info +{ + size_t memobj_npages; + vm_address_t memobj_pages[0]; +}; + + +/* We need a separate bucket for the pager ports. */ +static struct port_bucket *pager_bucket; + + +/* Implement the pager_clear_user_data callback from the pager library. */ +void +pager_clear_user_data (struct user_pager_info *upi) +{ + int idx; + + for (idx = 0; idx < upi->memobj_npages; idx++) + if (upi->memobj_pages[idx]) + vm_deallocate (mach_task_self (), upi->memobj_pages[idx], vm_page_size); + free (upi); +} + + +error_t +pager_read_page (struct user_pager_info *upi, vm_offset_t page, + vm_address_t *buf, int *writelock) +{ + /* XXX clients should get a read only object. */ + *writelock = 0; + + if (upi->memobj_pages[page / vm_page_size] != (vm_address_t) NULL) + { + *buf = upi->memobj_pages[page / vm_page_size]; + upi->memobj_pages[page / vm_page_size] = (vm_address_t) NULL; + } + else + *buf = (vm_address_t) mmap (0, vm_page_size, PROT_READ|PROT_WRITE, + MAP_ANON, 0, 0); + return 0; +} + + +error_t +pager_write_page (struct user_pager_info *upi, vm_offset_t page, + vm_address_t buf) +{ + assert (upi->memobj_pages[page / vm_page_size] == (vm_address_t) NULL); + upi->memobj_pages[page / vm_page_size] = buf; + return 0; +} + + +error_t +pager_unlock_page (struct user_pager_info *pager, + vm_offset_t address) +{ + assert (!"unlocking requested on unlocked page"); + return 0; +} + + +/* Tell how big the file is. */ +error_t +pager_report_extent (struct user_pager_info *upi, + vm_address_t *offset, + vm_size_t *size) +{ + *offset = 0; + *size = upi->memobj_npages * vm_page_size; + return 0; +} + + +void +pager_dropweak (struct user_pager_info *upi) +{ +} + + +/* A top-level function for the paging thread that just services paging + requests. */ +static void +service_paging_requests (any_t arg) +{ + struct port_bucket *pager_bucket = arg; + for (;;) + ports_manage_port_operations_multithread (pager_bucket, + pager_demuxer, + 1000 * 60 * 2, + 1000 * 60 * 10, 0); +} + + +/* Initialize the pager for the display component. */ +void +user_pager_init (void) +{ + /* Create the pager bucket, and start to serve paging requests. */ + pager_bucket = ports_create_bucket (); + if (! pager_bucket) + error (5, errno, "Cannot create pager bucket"); + + /* Make a thread to service paging requests. */ + cthread_detach (cthread_fork ((cthread_fn_t) service_paging_requests, + (any_t) pager_bucket)); +} + + +/* Create a new pager in USER_PAGER with NPAGES pages, and return a + mapping to the memory in *USER. */ +error_t +user_pager_create (struct user_pager *user_pager, unsigned int npages, + struct cons_display **user) +{ + error_t err; + struct user_pager_info *upi; + + upi = calloc (1, sizeof (struct user_pager_info) + + sizeof (vm_address_t) * npages); + if (!upi) + return errno; + + upi->memobj_npages = npages; + + /* XXX Are the values 1 and MEMORY_OBJECT_COPY_DELAY correct? */ + user_pager->pager = pager_create (upi, pager_bucket, + 1, MEMORY_OBJECT_COPY_DELAY); + if (!user_pager->pager) + { + free (upi); + return errno; + } + user_pager->memobj = pager_get_port (user_pager->pager); + ports_port_deref (user_pager->pager); + + mach_port_insert_right (mach_task_self (), user_pager->memobj, + user_pager->memobj, MACH_MSG_TYPE_MAKE_SEND); + + err = vm_map (mach_task_self (), + (vm_address_t *) user, + (vm_size_t) npages * vm_page_size, + (vm_address_t) 0, + 1 /* ! (flags & MAP_FIXED) */, + user_pager->memobj, 0 /* (vm_offset_t) offset */, + 0 /* ! (flags & MAP_SHARED) */, + VM_PROT_READ | VM_PROT_WRITE, + VM_PROT_READ | VM_PROT_WRITE, + VM_INHERIT_NONE); + if (err) + { + /* UPI will be cleaned up by libpager. */ + mach_port_deallocate (mach_task_self (), user_pager->memobj); + return err; + } + + return 0; +} + + +/* Destroy the pager USER_PAGER and the mapping at USER. */ +void +user_pager_destroy (struct user_pager *user_pager, struct cons_display *user) +{ + /* The pager will be deallocated by libpager. */ + vm_deallocate (mach_task_self (), (vm_offset_t) user, + pager_get_upi (user_pager->pager)->memobj_npages + * vm_page_size); + mach_port_deallocate (mach_task_self (), user_pager->memobj); +} + + +/* Allocate a reference for the memory object backing the pager + USER_PAGER with protection PROT and return it. */ +mach_port_t +user_pager_get_filemap (struct user_pager *user_pager, vm_prot_t prot) +{ + error_t err; + + /* Add a reference for each call, the caller will deallocate it. */ + err = mach_port_mod_refs (mach_task_self (), user_pager->memobj, + MACH_PORT_RIGHT_SEND, +1); + assert_perror (err); + + return user_pager->memobj; +} + diff --git a/console/pager.h b/console/pager.h new file mode 100644 index 00000000..974db961 --- /dev/null +++ b/console/pager.h @@ -0,0 +1,47 @@ +/* pager.h - Interface to the pager for display 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 PAGER_H +#define PAGER_H + +struct user_pager +{ + struct pager *pager; + memory_object_t memobj; +}; + +/* Initialize the pager for the display component. */ +void user_pager_init (void); + +/* Create a new pager in USER_PAGER with NPAGES pages, and return a + mapping to the memory in *USER. */ +error_t user_pager_create (struct user_pager *user_pager, unsigned int npages, + struct cons_display **user); + +/* Destroy the pager USER_PAGER and the mapping at USER. */ +void user_pager_destroy (struct user_pager *user_pager, + struct cons_display *user); + +/* Allocate a reference for the memory object backing the pager + USER_PAGER with protection PROT and return it. */ +mach_port_t user_pager_get_filemap (struct user_pager *user_pager, + vm_prot_t prot); + +#endif /* PAGER_H_ */ diff --git a/console/priv.h b/console/priv.h new file mode 100644 index 00000000..c03bbc46 --- /dev/null +++ b/console/priv.h @@ -0,0 +1,37 @@ +/* + Copyright (C) 1995, 1996, 2001, 2007 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 <hurd/hurd_types.h> + +#include <hurd/netfs.h> + +static inline struct protid * __attribute__ ((unused)) +begin_using_protid_port (file_t port) +{ + return ports_lookup_port (netfs_port_bucket, port, netfs_protid_class); +} + +static inline void __attribute__ ((unused)) +end_using_protid_port (struct protid *cred) +{ + if (cred) + ports_port_deref (cred); +} |