#ifndef      lint
static char sccsid[] = "@(#)SunOSI.c 9.20 88/02/10";
#endif

/*
 * Copyright (c) 1987 by Sun Microsystems, Inc.
 */

/*-
	Sun operating system interface routines

	SunOSI.c, Tue Jul 22 15:12:10 1986

		James Gosling,
		Sun Microsystems
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <netdb.h>
#include "PostScript.h"
#include "input.h"
#include "cscript.h"
#include <sgtty.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <cursor.h>
#include <sys/wait.h>
#include <values.h>
#include <ctype.h>
#ifdef sparc
#include <vfork.h>
#endif

#ifdef	TemporaryInitHack
#undef	pr_open
#include <sun/fbio.h>
#include <sundev/kbd.h>
#include <sundev/kbio.h>
#include <sunwindow/rect.h>
#include <sunwindow/rectlist.h>
#include <sunwindow/win_screen.h>
#include <sunwindow/win_struct.h>
#include <sunwindow/cms.h>
#include <sunwindow/win_input.h>
#include <sys/dir.h>

/* FIXME: XXX Remove references to RepairColorMap */

int             vsdev;		/* FD of event source (sunwindows window) */

struct scene   *CurrentInputScene;	/* XXX - AAAAAAARGGGHHHHH! */

static char     my_hostname[40];

static u_char   sun_red[256], sun_green[256], sun_blue[256];

static void      (*sig_TSTP_cache) ();
static void      (*sig_CONT_cache) ();
static void      (*sig_IO_cache) ();
static void      (*sig_VTALRM_cache) ();
static void      (*sig_WINCH_cache) ();

static void     sun_win_change();
static void     sun_pause();
static void     sun_resume();
static void     sun_sigio();
static void     sun_sigvtalrm();

static void     setcolors();
static void     remember_colors();
static void     sun_set_parent_name();
extern void     sun_set_shared_locking_info();
static void     set_timeout_alarm();

static void     sun_enable_kbd_input();
static void     sun_enable_pick_input();
static void     sun_disable_pick_input();
static void     sun_enter_news_region();
static void     sun_exit_news_region();

static short    sun_cursor_zone;/* = 16 */

/*
 * 16 is the width of a normal cursor (16). This is not completely safe but
 * should work for 90% of the cases.
 */
static int      sun_created_root;
static int      sun_placeholderfd;

static enum kbd_translation {
    UNENCODED_KEYBOARD, NORMAL_KEYBOARD
}               sun_current_keyboard;

static struct timeval
                sun_cached_input_timeout;

static          ttysw_saveparms();
static          we_setptyparms();
#endif	TemporaryInitHack

int	        news_pid;	/* process id of NeWS server */

int	        raw_keyboard,	/* is translation desired? */
                raw_mouse;

struct body	*motion_keyword,
		*but1_keyword,
		*but2_keyword,
		*but3_keyword,
		*raw_motion_keyword,
		*raw_but1_keyword,
		*raw_but2_keyword,
		*raw_but3_keyword,
		*up_keyword,
		*down_keyword;

/*
 * This routine interferrs with load in dload.c.  Load currently blocks
 * SIGCHLD this interaction may need to be fixed someday.
 */
static
reaper()
{
    union wait  status;
    register    pid;

    while ((pid = wait3(&status, WNOHANG, 0)) > 0)
	if (verbose)
	    fprintf(stderr, "Reaped %d, rv=0%o\n", pid, status.w_retcode);
}

/*
 * This is disgusting!  It is absolutly essential that we get rid of all the
 * win_* hacks!
 */
static
startkeyboardandmouse(ee)
    register struct execution_environment *ee;
{
    extern char *getenv();
    register int i;
    struct screen sc;
    char       *parentname;

    if (vsdev > 0)
	return;
    sig_IO_cache = signal(SIGIO, sun_sigio);
    sig_WINCH_cache = signal(SIGWINCH, sun_win_change);
    set_timeout_alarm();
    /* See if SunWindows running */
    if ((parentname = getenv("WINDOW_PARENT"))) {
	extern int  sun_left,
	            sun_top,
	            sun_width,
	            sun_height;
	extern char *sun_fb_name;
	int         parentfd;
	Rect        r;
	char       *p2name;
	int         p2fd;
	Rect        r2;
	struct screen screen;

	/* Determine screen dimensions */
	if ((parentfd = open(parentname, O_RDONLY, 0)) < 0) {
	    fprintf(stderr, "Can't open parent %s\n", parentname);
	    ee->error_code = ioerror_error_code;
	    return;
	}
	/* See if parent is on the same screen as sun_fb_name */
	win_screenget(parentfd, &screen);
	if (strcmp(sun_fb_name, screen.scr_fbname) != 0) {
	    close(parentfd);
	    goto NewScreen;
	}
	win_getrect(parentfd, &r);
	/* See if want partial screen capibility */
	if (sun_left != 0 || sun_top != 0 || sun_width > 0 || sun_height > 0) {
	    rect_construct(&r, sun_left, sun_top, sun_width, sun_height);
	    /*
	     * See if running under overall by testing for gfx window that
	     * covers the entire screen.
	     */
	}
	else if ((p2name = getenv("WINDOW_GFX"))) {
	    if ((p2fd = open(p2name, O_RDONLY, 0)) < 0) {
		fprintf(stderr, "Can't open parent %s\n", p2name);
		ee->error_code = ioerror_error_code;
		close(parentfd);
		return;
	    }
	    win_getrect(p2fd, &r2);
	    if (rect_equal(&r, &r2)) {
		/* Make the gfx window the parent */
		close(parentfd);
		parentfd = p2fd;
		parentname = p2name;
		/* Handle stopped server */
		sig_TSTP_cache = signal(SIGTSTP, sun_pause);
		sig_CONT_cache = signal(SIGCONT, sun_resume);
	    }
	}
	/* Allocate new window */
	if ((vsdev = win_getnewwindow()) < 0)
	    goto NoWin;
	/* Set window's dimensions */
	win_setrect(vsdev, &r);
	/* Install window as top child of parent */
	win_setlink(vsdev, WL_PARENT, win_fdtonumber(parentfd));
	win_setlink(vsdev, WL_OLDERSIB, win_getlink(parentfd, WL_TOPCHILD));
	/* Set window's colors to match servers */
	remember_colors();
	setcolors();
	/* Make visible */
	win_insert(vsdev);
	close(parentfd);
	win_setmouseposition(vsdev, r.r_width / 2, r.r_height / 2);
	cs_movecursor(r.r_width / 2, r.r_height / 2);
    }
    else {
	extern      (*RepairColorMap) ();
	extern char *sun_fb_name;

NewScreen:
	bzero((caddr_t) &sc, sizeof sc);
	strncpy(sc.scr_fbname, sun_fb_name, SCR_NAMESIZE);
	{
	    extern char *sun_fb_name;
	    struct pixrect *pr = pr_open(sun_fb_name);

	    if (pr) {
		if (pr->pr_depth > 1)
		    /*
		     * Set "only use one plane group" flag so SunView on NeWS
		     * programs don't try to use alternate plane groups that
		     * NeWS isn't managing.
		     */
		    sc.scr_flags |= SCR_8BITCOLORONLY;
		pr_destroy(pr);
	    }
	}
	/* Running alone */
	if ((vsdev = win_screennew(&sc)) < 0)
	    goto NoWin;
	sun_created_root = 1;
	/* Set window's colors to match servers */
	/* Remove references to RepairColorMap */
	if (RepairColorMap)	/* win_screennew gratuitously trashes the
				 * color map */
	    (*RepairColorMap) ();
	remember_colors();
	setcolors();
	/*
	 * Steal a window for the tool slot allocator & stash its name in the
	 * environment
	 */
	if ((sun_placeholderfd = win_getnewwindow()) == -1) {
	    (void) fprintf(stderr,
			   "No window available for placing open windows\n");
	    perror("PS");
	}
	else {
	    char        name[WIN_NAMESIZE];

	    (void) win_fdtoname(sun_placeholderfd, name);
	    (void) setenv("WMGR_ENV_PLACEHOLDER", name);
	}
    }
    /* Blank out cursor */
    zap_cursor(vsdev);
    /*
     * Set up name of vsdev in environment so that SunWindows based programs
     * that are run under NeWS are children of the NeWS window.
     */
    sun_set_parent_name(vsdev);
    /* Setup tty parameters for all terminal emulators that will start */
    {
	int         tty_fd;
	tty_fd = open("/dev/tty", O_RDWR, 0);
	if (tty_fd < 0)
	    (void) ttysw_saveparms(2);	/* Try stderr */
	else {
	    (void) ttysw_saveparms(tty_fd);
	    (void) close(tty_fd);
	}
    }
    /* Set up shared memory locking mechanism */
    sun_set_shared_locking_info(vsdev);

    add_selectable_file(vsdev, 1);
    if (vsdev > maximum_fd)
	maximum_fd = vsdev + 1;
    {
	/* XXX - this should go in the expressinterest stuff */
	struct inputmask im;

	/* Set kbd mask */
	sun_enable_kbd_input(NORMAL_KEYBOARD);
	/* Set pick mask */
	sun_enable_pick_input();
    }
    if (fcntl(vsdev, F_SETFL, O_NDELAY | FASYNC) < 0
	    || fcntl(vsdev, F_SETFD, 1) < 0) {
	ee->error_code = ioerror_error_code;
	return;
    }
    sun_enter_news_region();
    return;
NoWin:
    fprintf(stderr, "Couldn't get base SunWindow\n");
    exit(1);
}

static void
sun_set_parent_name(windowfd)
    int         windowfd;
{
    char        name[WIN_NAMESIZE];

    /*
     * Set up name of windowfd in environment so that SunWindows based
     * programs that are run under NeWS are children of the NeWS window.
     */
    win_fdtoname(windowfd, name);
    we_setparentwindow(name);
}

/* Remember the current state of the colormap */
static void
remember_colors()
{
    extern char *sun_fb_name;
    struct pixrect *pr;

    pr = pr_open(sun_fb_name);
    pr_getcolormap(pr, 0, 256, sun_red, sun_green, sun_blue);
    pr_destroy(pr);
}

/* Set window's colors to match servers */
static void
setcolors()
{
    struct colormapseg cms;
    struct cms_map cmap;

    /* Init cms to zeros */
    bzero((caddr_t) &cms, sizeof(cms));
    /* Make window a colormap hog */
    cms.cms_addr = 0;
    cms.cms_size = 256;
    cmap.cm_red = sun_red;
    cmap.cm_green = sun_green;
    cmap.cm_blue = sun_blue;
    if (!sun_created_root) {
	/*
	 * Use a non-monochrome colormap segment when there are existing
	 * SunView windows so as to not trash the color state. Colors will
	 * flash when moving between NeWS and SunView, which is better then
	 * trashing existing windows.
	 */
	sprintf(cms.cms_name, "NeWS");
    }
    else {
	/*
	 * Set the monochrome colormap segment to be the colormap hog so as to
	 * reduce the flashing when moving from NeWS to SunView.
	 */
	sprintf(cms.cms_name, "monochrome");
    }
    win_setcms(vsdev, &cms, &cmap);
}

static void
sun_win_change(sig, code, scp)
    int         sig,
                code;
    struct sigcontext *scp;
{
    /* Just used to kick sched out of select(2) */
    if (sig_WINCH_cache != SIG_DFL && sig_WINCH_cache != SIG_IGN)
	(void) sig_WINCH_cache(sig, code, scp);
    fcntl(vsdev, F_SETFL, O_NDELAY | FASYNC);
}

static void
sun_pause(sig, code, scp)
    int         sig,
                code;
    struct sigcontext *scp;
{
    win_remove(vsdev);
    if (sig_TSTP_cache != SIG_DFL && sig_TSTP_cache != SIG_IGN)
	(void) sig_TSTP_cache(sig, code, scp);
    else
	kill(getpid(), SIGSTOP);
}

static void
sun_resume(sig, code, scp)
    int         sig,
                code;
    struct sigcontext *scp;
{
    win_insert(vsdev);
    if (sig_CONT_cache != SIG_DFL && sig_CONT_cache != SIG_IGN)
	(void) sig_CONT_cache(sig, code, scp);
}

static void
sun_sigio(sig, code, scp)
    int             sig, code;
    struct sigcontext *scp;
{
    extern int      io_ready;

    io_ready = 1;
}

void
make_async(fd)
    int             fd;
{
    register unsigned flags;

    if (flags = fcntl(fd, F_GETFL) == -1) {
	perror("getflags");
	return;
    }
    flags |= FASYNC;
    if (fcntl(fd, F_SETFL, flags) == -1)
	perror("setflags");
/*     async_files[fd+1] = 1; */
}

static void
sun_sigvtalrm(sig, code, scp)
    int             sig, code;
    struct sigcontext *scp;
{
    extern int      timeout_ticks, timeout_value;

    if (--timeout_ticks == 0) {
        timeout_ticks = timeout_value;
        if (current_process)
            current_process->error_code = timeout_error_code;
    }
}

static void
set_timeout_alarm()
{
    struct itimerval    val, oval;

    sig_VTALRM_cache = signal(SIGVTALRM, sun_sigvtalrm);
    val.it_interval.tv_sec = 1;
    val.it_interval.tv_usec = 0;
    val.it_value.tv_sec = 1;
    val.it_value.tv_usec = 0;
    if (setitimer(ITIMER_VIRTUAL, &val, &oval)) {
        perror("set_timeout_alarm");
    }
}


static fract scene_clipping_matrix[3][2];

static void
init_scene_clipping_matrix()
{				/* {fracti(1), 0, 0, fracti(1), 0, 0} */
    register fract *ptr = &scene_clipping_matrix[0][0];

    *ptr++ = fracti(1);
    *ptr++ = 0;
    *ptr++ = 0;
    *ptr++ = fracti(1);
    *ptr++ = 0;
    *ptr = 0;
}

/* This routine unacceptably reaches around the CScript interface */
damage_everything(cv)
    register struct canvas *cv;
{
    if (cv == 0)
	return;
    if (!cv->transparent && cv->mapped)
	cv_extend_damage(cv, cv->outerclip);
    if (cv->hasopaque)
	for (cv = cv->topchild; cv; cv = cv->prev)
	    if ((!cv->transparent && cv->mapped)
		    || cv->hasopaque)
		damage_everything(cv);
}

void
sun_set_scene_clipping(sc, rl_sw_visible)
    struct scene *sc;
    struct rectlist *rl_sw_visible;
{
    struct rectlist rl_sw_invisible;	/* NeWS window invisible */
    struct rectlist rl_clip;	/* Root window clipping */
    struct rectlist rl_remove;
    struct rectlist rl_zone;
    static struct graphics_context *gc;
    register struct rectnode *rn;
    register struct rectnode *rn_pr;
    struct rect r;
    extern struct rect sun_wl_rect;
    struct canvas *cv_under_mouse;

    /* Initialize rectlists */
    rl_clip = rl_zone = rl_remove = rl_sw_invisible = rl_null;
    /*
     * May clobber the NeWS cursor (the lock removed the SunWindows cursor),
     * ee below at cv_under_mouse.
     */
    /* Establish new graphics context & reinit the transformation */
    if (gc == 0)
	gc = cs_newcontext(sc->canvases);
    cs_setmatrix(gc, scene_clipping_matrix);
    /* Set enable plane if have multiple plane groups */
    sun_init_plane_groups(sc->display, rl_sw_visible);
    /* If completely visible then set single rect */
    if (rl_equalrect(&sun_wl_rect, rl_sw_visible)) {
	r = sun_wl_rect;
	/* Add rectangle to path */
	cs_moveto(gc, r.r_left, r.r_top);
	cs_rlineto(gc, r.r_width, 0);
	cs_rlineto(gc, 0, r.r_height);
	cs_rlineto(gc, -r.r_width, 0);
	cs_closepath(gc);
    }
    else {
	/* Determine invisible portion of NeWS window */
	rl_initwithrect(&sun_wl_rect, &rl_remove);
	rl_difference(&rl_remove, rl_sw_visible, &rl_sw_invisible);
	rl_free(&rl_remove);
	/* Initialize rl_clip */
	rl_copy(rl_sw_visible, &rl_clip);
	/* Cycle through clipping list (invisible portions) */
	for (rn = rl_sw_invisible.rl_head; rn; rn = rn->rn_next) {
	    /*
	     * Expand invisible portion of NeSW window to allow for cursor
	     * buffer zone.
	     */
	    r = rn->rn_rect;
	    rect_marginadjust(&r, sun_cursor_zone);
	    /* Remove this cursor zone from root clipping */
	    rl_initwithrect(&r, &rl_remove);
	    rl_difference(&rl_clip, &rl_remove, &rl_clip);
	    rl_free(&rl_remove);
	    /* Intersect cursor zone with NeWS window visible */
	    rl_rectintersection(&r, rl_sw_visible, &rl_zone);
	    /* Clear the border zone with low level rop */
	    for (rn_pr = rl_zone.rl_head; rn_pr;
		    rn_pr = rn_pr->rn_next)
		pr_rop(sc->display,
		       rn_pr->rn_rect.r_left, rn_pr->rn_rect.r_top - 1,
		       rn_pr->rn_rect.r_width,
		       rn_pr->rn_rect.r_height + 1,
		       PIX_SRC, 0, 0, 0);
	    rl_free(&rl_zone);
	}
	/* Clean up unexplained line at bottom of screen */
	pr_rop(sc->display,
	       0, rect_bottom(&sun_wl_rect) - 1,
	       sun_wl_rect.r_width, 2, PIX_SRC, 0, 0, 0);
	rl_free(&rl_sw_invisible);
	/* Try to reduce the number of regions */
	rl_coalesce(&rl_clip);
	/* Cycle through root canvas new clipping list and make path */
	for (rn = rl_clip.rl_head; rn; rn = rn->rn_next) {
	    r = rn->rn_rect;
	    /* Add rectangle to path */
	    cs_moveto(gc, r.r_left, sun_wl_rect.r_height - r.r_top);
	    cs_rlineto(gc, r.r_width, 0);
	    cs_rlineto(gc, 0, -r.r_height);
	    cs_rlineto(gc, -r.r_width, 0);
	    cs_closepath(gc);
	}
	rl_free(&rl_clip);
    }

    /*	We need to reshape the root canvas, but we can't use
		cs_setcanvasshape(gc, sc->canvases);
	here because it makes assumptions about the integrity of the
	screen.  Since we can't save anything, doing the reshape by
	hand is relatively easy. All of this code should really be
	somewhere in CScript. */
    {
	register struct canvas *cv = sc->canvases;
	register struct shape *outline;
	outline = cv_pathtoshape(gc, ~0);
	if (outline == 0) {	/* Reshaping to empty path */
	    qtalloc(outline, (struct shape *));
	    outline->refcnt = 1;
	    outline->pos.x = outline->pos.y = 0;
	    outline->size.x = outline->size.y = 0;
	    outline->is_rect = 1;
	    outline->trapset = 0;
	}
	cv_invalidate_pixrects(cv);
	cv_invalidate_clip(cv, 1);
	cs_newpath(gc);
	sh_decref(cv->outerclip);
	cv->outerclip = outline;
	damage_everything(cv);
    }
    /*
     * Claim that the cursor is down if the cursor collides with new clipped
     * part of screen.  This still leave cursor turds when the cursor is in
     * the boundary between NeWS and SunWindows. But, that the way it goes
     * with this hack.
     */
    cv_under_mouse = cs_locatecanvasinscene(CurrentInputScene,
		 cfloorfr(cv_cursor.ri.pos.x), cfloorfr(cv_cursor.ri.pos.y));
    if (CanvasUnderMouse && !cv_under_mouse)
	cv_cursor.up = 0;
    CheckCrossings(last_event_time);
}

static
setkeyboardtranslation_primitive(ee)
    register struct execution_environment *ee;
{
    register struct object *optop = ee->optop - 1;
    register enum kbd_translation desired;

    if (optop->type != boolean_type) {
	ee->error_code = typecheck_error_code;
	return;
    }
    desired = optop->value.fixed ? NORMAL_KEYBOARD  :  UNENCODED_KEYBOARD;
        if (!setkeyboardtranslation(desired)) {
	ee->error_code = rangecheck_error_code;
	return;
    }
    raw_keyboard = (desired == UNENCODED_KEYBOARD);
    ee->optop--;
}

static
setkeyboardtranslation(arg)
    enum kbd_translation arg;
{
    register int    kbd_fd;
    int             current, desired, ret = 1;

#ifdef TR_UNTRANS_EVENT
    if ((kbd_fd = open("/dev/kbd", O_RDONLY, 0)) < 0)
	return 0;
    if (ioctl(kbd_fd, KIOCGTRANS, (caddr_t) &current)) {
	ret = 0;
	goto Return;
    }
    switch (current) {
      case TR_ASCII:
	if (arg == NORMAL_KEYBOARD)
	    goto Return;
	desired = TR_NONE;
	break;
      case TR_EVENT:
	if (arg == NORMAL_KEYBOARD)
	    goto Return;
	desired = TR_UNTRANS_EVENT;
	break;
      case TR_NONE:
	if (arg == UNENCODED_KEYBOARD)
	    goto Return;
	desired = TR_ASCII;
	break;
      case TR_UNTRANS_EVENT:
	if (arg == UNENCODED_KEYBOARD)
	    goto Return;
	desired = TR_EVENT;
	break;
      default:
	ret = 0;
	goto Return;
    }
    if (ioctl(kbd_fd, KIOCTRANS, (caddr_t) &desired)) {
	ret = 0;
	goto Return;
    }
    sun_current_keyboard = arg;
    switch (desired) {
      case TR_NONE:
	break;
      case TR_ASCII:
	break;
      case TR_EVENT:
	sun_enable_kbd_input(NORMAL_KEYBOARD);
	break;
      case TR_UNTRANS_EVENT:
	sun_enable_kbd_input(UNENCODED_KEYBOARD);
	break;
    }
Return:
    close(kbd_fd);
    return ret;
#else
    return 0;
#endif
}

static void
sun_enable_kbd_input(encoding)
    enum kbd_translation encoding;
{
    struct inputmask im;

    input_imnull(&im);
    win_setinputcodebit(&im, KBD_REQUEST);
    win_setinputcodebit(&im, KBD_USE);
    win_setinputcodebit(&im, KBD_DONE);
    im.im_flags = IM_ASCII | IM_META;
    if (encoding == UNENCODED_KEYBOARD)
	im.im_flags |= IM_NEGASCII;
    win_set_kbd_mask(vsdev, &im, NULL, WIN_NULLLINK);
}

static void
sun_enable_pick_input()
{
    struct inputmask im;

    input_imnull(&im);
    win_setinputcodebit(&im, LOC_MOVE);
    win_setinputcodebit(&im, MS_LEFT);
    win_setinputcodebit(&im, MS_MIDDLE);
    win_setinputcodebit(&im, MS_RIGHT);
    /* Include LOC_WINENTER & LOC_WINEXIT for cursor maintainence */
    win_setinputcodebit(&im, LOC_WINEXIT);
    win_setinputcodebit(&im, LOC_WINENTER);
    im.im_flags = IM_NEGEVENT | IM_INTRANSIT;
    win_set_pick_mask(vsdev, &im, NULL, WIN_NULLLINK);
}

/* Called when exit NeWS window */
static void
sun_disable_pick_input()
{
    /*
     * Turns out that we should get mouse motion even when the mouse is over a
     * SunView application so that NeWS gets awaken when the clipping of the
     * root canvas changes (just restricting the clipping doesn't generate a
     * SIGWINCH) [if go back to LOC_WINEXIT & LOC_WINENTER, make sure include
     * IM_INTRANSIT].
     */
    sun_enable_pick_input();
}

static
getkeyboardtranslation(ee)
    register struct execution_environment *ee;
{
    register struct object *optop = ee->optop - 1;
    register int kbd_fd;
    int         result;

#ifdef KIOCGTRANS
    if ((kbd_fd = open("/dev/kbd", O_RDONLY, 0)) < 0) {
	ee->error_code = ioerror_error_code;
	return;
    }
    if (ioctl(kbd_fd, KIOCGTRANS, (caddr_t) &result)) {
	ee->error_code = ioerror_error_code;
	return;
    }
    close(kbd_fd);
    switch (result) {
      case TR_NONE:
      case TR_UNTRANS_EVENT:
	result = 0;
      case TR_ASCII:
      case TR_EVENT:
	result = 1;
    }
#else
    result = 0;
#endif
    set_typed_object(ee->optop, boolean_type);
    ee->optop->value.fixed = result;
    ee->optop++;
}

static
keyboardtype(ee)
    register struct execution_environment *ee;
{
    register struct object *optop = ee->optop - 1;
    register int kbd_fd;
    int         result;

#ifdef KIOCTYPE
    if ((kbd_fd = open("/dev/kbd", O_RDONLY, 0)) < 0) {
	ee->error_code = ioerror_error_code;
	return;
    }
    if (ioctl(kbd_fd, KIOCTYPE, (caddr_t) &result)) {
	ee->error_code = ioerror_error_code;
	return;
    }
    close(kbd_fd);
#else
    result = 0;
#endif

    set_fixed_object(ee->optop, fracti(result));
    ee->optop++;
}


static
getmousetranslation(ee)
    register struct execution_environment *ee;
{
    set_typed_object(ee->optop, boolean_type);
    ee->optop->value.fixed = !raw_mouse;
    ee->optop++;
}

static
setmousetranslation(ee)
    register struct execution_environment *ee;
{

#ifdef WS_SCALE_MAX_COUNT
    register struct object *optop = ee->optop - 1;
    static Ws_scale_list old_scaling,
                scaling;

    if (optop->type != boolean_type) {
	ee->error_code = typecheck_error_code;
	return;
    }
    if (raw_mouse) {
	if (optop->value.fixed) {
	    scaling = old_scaling;
	    if (win_set_scaling(vsdev, &scaling)) {
		perror("set mouse parameters");
		ee->error_code = typecheck_error_code;
		return;
	    }
	    raw_mouse = 0;
	}
	else {
	    if (win_get_scaling(vsdev, &old_scaling)) {
		perror("read mouse parameters");
		ee->error_code = typecheck_error_code;
		return;
	    }
	    scaling.scales[0].ceiling = -1;
	    scaling.scales[0].factor = 1;
	    if (win_set_scaling(vsdev, &scaling)) {
		perror("set mouse parameters");
		ee->error_code = typecheck_error_code;
		return;
	    }
	    raw_mouse = 1;
	}
    }
    ee->optop--;
#endif
}

void
setcursorposition(x, y)
    int         x,
                y;		/* integer framebuffer coordinates */
{
    cs_movecursor(x, y);
    CheckCrossings(last_event_time);
    win_setmouseposition(vsdev, x, y);
}

open_socket(name, inf, outf)
    char       *name;
    PSFILE	**inf, **outf;
{
    register char *p = name;
    struct sockaddr_in addr;
    int         con = 0;
    int         lis = 0;
    register    fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    char	*hostname;
    long	strtol();

    bzero((char *)&addr, sizeof(addr));
    addr.sin_family = AF_INET;
    if (*p == 'l')
	lis++, p++;
    else if (*p == 'c')
	con++, p++;
    if (*p)
	if ((addr.sin_port = htons((short)strtol(p, &hostname, 10))) == 0) {
    bad_socket:
	    close(fd);
	}
	else {
	    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, 0, 0);
	    if (con) {
		struct	hostent *hp;

		if (*hostname == '\0')
		    hostname = "localhost";
		if (!isalpha(*hostname))    /* skip separator character */
		    hostname++;
		hp = gethostbyname(hostname);
		if (hp == NULL)
		    goto bad_socket;
		bcopy(hp->h_addr, (char *)&addr.sin_addr, hp->h_length);
		if (addr.sin_port == 0 || connect(fd, &addr, sizeof addr))
		    goto bad_socket;
		if (fcntl(fd, F_SETFL, FNDELAY) == -1)
		    perror("socket fcntl");
		*inf = psio_fdopen(fd, "r");
		*outf = psio_fdopen(fd, "w");
	    }
	    else {
		addr.sin_addr.s_addr = INADDR_ANY;
		if (bind(fd, &addr, sizeof addr) < 0 
			    || lis && listen(fd, 3) < 0)
		    goto bad_socket;
		*inf = psio_fdopen(fd, "r");
	    }
	}
}

fract
currenttime(tv)
    struct timeval *tv;
{
    fract       ret;
    struct timeval now;
    long        sec,
                usec;

    if (tv == 0) {
	tv = &now;
	gettimeofday(tv, 0);
    }
    sec = tv->tv_sec - startup_time.tv_sec;
    usec = tv->tv_usec - startup_time.tv_usec;
    if (usec < 0) {
	usec += 1000000;
	sec -= 1;
    }
    ret = sec * ((1 << 16) / 60) /* fraction(sec, 60) */ ;
    ret += usec / (60000000 >> 16) /* fraction((usec / 1000), 60000) */ ;
    return (ret);
}

EventsHaveSelected()
{
    /* Some event has happened */
#define	INPBUFSIZE	64
    struct inputevent sunevents[INPBUFSIZE];
    register struct inputevent
               *eventp,
               *limit;
    register int n,
                m;

    if (vsdev < 0)
	return;
    if ((n = read(vsdev, sunevents, sizeof sunevents)) < 0) {
	if (errno != EWOULDBLOCK) {
	    /* XXX - do something creative here! */
	    return;
	}
    }
    for (eventp = sunevents, limit = sunevents - 1 + n / sizeof sunevents[0];
	    eventp <= limit; eventp++) {
	if (eventp < limit &&
		eventp[0].ie_code == LOC_MOVE &&
		eventp[1].ie_code == LOC_MOVE)
	    continue;
	ProcessInputEvent(eventp);
    }
}

static void
sun_enter_news_region()
{
    struct timeval	tv;

    win_get_event_timeout(vsdev, &sun_cached_input_timeout);
    timerclear(&tv);
    win_set_event_timeout(vsdev, &tv);

    /* Do fullblown mouse/cursor tracking.  Reset colormap for the case	  *
     * of running SunView binaries on top of raw NeWS, in which you get	  *
     * colormap entry 1 (currently red) messed up.			  */
    sun_enable_pick_input();
    cs_cursorenable();
    setcolors();
    /* If the SunWindows environment is in cursor follows mouse mode,	  *
     * then switch translation quickly, before KBD_* events get to us.	  */
    if (sun_kbd_follows_ms() && raw_keyboard)
	setkeyboardtranslation(UNENCODED_KEYBOARD);

    fcntl(vsdev, F_SETFL, O_NDELAY | FASYNC);
}

static void
sun_exit_news_region()
{
    win_set_event_timeout(vsdev, &sun_cached_input_timeout);

    /* Since the mouse is not going to be doing stuff over the NeWS	  *
     * window, disable reading of mouse input and adjusting the NeWS	  *
     * cursor.  If we don't disable mouse input then NeWS will be	  *
     * tracking mouse motion even when a SunView application is above	  *
     * NeWS, due to the event propagation mechanism of SunWindows.	  */
    sun_disable_pick_input();
    cs_cursordown();
    cs_cursordisable();
    /* Keyboard translation follows same rules as enter	*/
    if (sun_kbd_follows_ms() && raw_keyboard)
	setkeyboardtranslation(NORMAL_KEYBOARD);
}

static
ProcessInputEvent(se)
    struct inputevent *se;
{
    register struct body *b;
    register int    code;
    register enum {
	unknown, keystroke, motion
    }		    class;
    struct object   name;

    switch (se->ie_code) {
    case LOC_MOVE:
	class = motion;
	set_typed_bodied_object(&name, keyword_type, 
		raw_mouse ? raw_motion_keyword : motion_keyword);
	break;
    case MS_LEFT:
	set_typed_bodied_object(&name, keyword_type, 
		raw_mouse ? raw_but1_keyword : but1_keyword);
	class = keystroke;
	break;
    case MS_MIDDLE:
	set_typed_bodied_object(&name, keyword_type, 
		raw_mouse ? raw_but2_keyword : but2_keyword);
	class = keystroke;
	break;
    case MS_RIGHT:
	set_typed_bodied_object(&name, keyword_type, 
		raw_mouse ? raw_but3_keyword : but3_keyword);
	class = keystroke;
	break;
    default:
	if ((code = se->ie_code) > META_LAST)
	    class = unknown;
	else {
	    class = keystroke;
	    if (raw_keyboard)
		code += DEVIDKBD;
	}
	set_fixed_object(&name, fracti(code));
	break;
/*  Following SunWindows events mean the cursor (or focus) entered
 *  or left NeWS (in SunView1 compatibility mode).  This requires
 *  adjusting keyboard translation, cursor-tracking, input timeouts,
 *  and (eventually) maintaining keyboard state across the transition.
 *  None of these events is interesting to a NeWS client.
 */
    case LOC_WINENTER:
	sun_enter_news_region();
	return;
    case LOC_WINEXIT:
	sun_exit_news_region(); 
	return;
    case KBD_REQUEST:	/* ignore; a KBD_USE will follow immediately */
	return;
    case KBD_USE:
	if (raw_keyboard && sun_current_keyboard != UNENCODED_KEYBOARD)
	    setkeyboardtranslation(UNENCODED_KEYBOARD);
	return;
    case KBD_DONE:
	if (raw_keyboard && sun_current_keyboard != NORMAL_KEYBOARD) {
	    setkeyboardtranslation(NORMAL_KEYBOARD);
	}
	return;
    }
    b = createevent();
    b->body.event.pos.x = fracti(se->ie_locx);
    b->body.event.pos.y = fracti(se->ie_locy);
    b->body.event.name = name;
    b->body.event.time = currenttime(&(se->ie_time));
    switch (class) {
      case keystroke:
	set_typed_bodied_object(&b->body.event.action, keyword_type,
		   (se->ie_flags & IE_NEGEVENT ? up_keyword : down_keyword));
	break;
      case motion:
	if (raw_mouse) {
	    static int  lastx,
	                lasty;
	    register int newx,
	                newy;

	    newx = se->ie_locx;
	    newy = se->ie_locy;
	    b->body.event.pos.x = cv_cursor.ri.pos.x + fracti(newx - lastx);
	    b->body.event.pos.y = cv_cursor.ri.pos.y + fracti(newy - lasty);
	    lastx = newx;
	    lasty = newy;
	}
	else {
	    cs_movecursor(se->ie_locx, se->ie_locy);
	    CheckCrossings(b->body.event.time);
	}			/* FALL THROUGH  */
    default:
	clear_object(&(b->body.event.action));
    }
    enqueueEvent(b);
}

/* Determine if the SunWindows environment is in cursor follows mouse mode */
static
sun_kbd_follows_ms()
{
     Firm_event fe;
     int shifts;

     win_get_focus_event(vsdev, &fe, &shifts);
     return (fe.id == LOC_WINENTER);
}

static
getenv_primitive(ee)
    register struct execution_environment *ee;
{
    register struct object *optop = ee->optop;
    char       *res;
    extern char *getenv();
    /* Rely on auto zero */
    static char *name;
    static unsigned int   name_size;

    if (optop[-1].type != string_type) {
	ee->error_code = typecheck_error_code;
	return;
    }
    if (optop[-1].value.substring.length >= name_size) {
	if (name)
	    free(name);
	name_size = optop[-1].value.substring.length + 50;
	name = (char *) snoopalloc("SunOSI.c", name_size);
    }
    bcopy(body_of(optop - 1)->body.string.chars + optop[-1].value.substring.start,
	  name, optop[-1].value.substring.length);
    name[optop[-1].value.substring.length] = 0;
    res = getenv(name);
    if (res == 0) {
	ee->error_code = undefined_error_code;
	return;
    }
    object_decref(optop - 1);
    optop[-1] = make_string(strlen(res), res);
}

static
putenv_primitive(ee)
    register struct execution_environment *ee;
{
    register struct object *optop = ee->optop;
    /* Rely on auto zero */
    static char *name;
    static char *value;
    static unsigned int   name_size;
    static unsigned int   value_size;

    if (optop[-2].type != string_type || optop[-1].type != string_type) {
	ee->error_code = typecheck_error_code;
	return;
    }
    if (optop[-2].value.substring.length >= name_size) {
	if (name)
	    free(name);
	name_size = optop[-2].value.substring.length + 50;
	name = (char *) snoopalloc("SunOSI.c", name_size);
    }
    if (optop[-1].value.substring.length >= value_size) {
	if (value)
	    free(value);
	value_size = optop[-1].value.substring.length + 50;
	value = (char *) snoopalloc("SunOSI.c", value_size);
    }
    bcopy(body_of(optop - 2)->body.string.chars + optop[-2].value.substring.start,
	  name, optop[-2].value.substring.length);
    name[optop[-2].value.substring.length] = 0;
    bcopy(body_of(optop - 1)->body.string.chars + optop[-1].value.substring.start,
	  value, optop[-1].value.substring.length);
    value[optop[-1].value.substring.length] = 0;
    object_decref(optop - 2);
    object_decref(optop - 1);
    ee->optop -= 2;
    putenv(name, value);
}

static
getsocketlocaladdress(ee)
    register struct execution_environment *ee;
{
    register struct object *optop = ee->optop;
    char        hname[50];
    register struct hostent *h;
    register    fd;
    struct sockaddr_in addr;
    int         namelen;
    if (optop[-1].type != file_type) {
	ee->error_code = typecheck_error_code;
	return;
    }
    if (body_of(&optop[-1])->body.file.inbuf == 0) {
	ee->error_code = invalidaccess_error_code;
	return;
    }
    fd = psio_fileno(body_of(&optop[-1])->body.file.inbuf);
    namelen = sizeof addr;
    if (getsockname(fd, &addr, &namelen) < 0) {
	ee->error_code = invalidaccess_error_code;
	return;
    }
    h = gethostbyname(my_hostname);
    if (h)
	sprintf(hname, "%u.%u;%s",
		*(int *) h->h_addr,
		addr.sin_port,
		h->h_name);
    else
	sprintf(hname, "%u.%u;localhost",
		addr.sin_addr.s_addr, addr.sin_port);
    object_decref(optop - 1);
    optop[-1] = make_string(strlen(hname), hname);
}

static
getsocketpeername(ee)
    register struct execution_environment *ee;
{
    register struct object *optop = ee->optop;
    register struct hostent *h;
    register    fd;
    struct sockaddr_in addr;
    int         namelen;
    if (optop[-1].type != file_type) {
	ee->error_code = typecheck_error_code;
	return;
    }
    if (body_of(&optop[-1])->body.file.inbuf == 0) {
	ee->error_code = invalidaccess_error_code;
	return;
    }
    fd = psio_fileno(body_of(&optop[-1])->body.file.inbuf);
    namelen = sizeof addr;
    if (getpeername(fd, &addr, &namelen) < 0
	    || (h = gethostbyaddr(&addr.sin_addr, sizeof addr.sin_addr, addr.sin_family)) == 0) {
	ee->error_code = invalidaccess_error_code;
	return;
    }
    object_decref(optop - 1);
    optop[-1] = make_string(strlen(h->h_name), h->h_name);
}



static
localhostname(ee)
    register struct execution_environment *ee;
{
    *ee->optop++ = make_string(strlen(my_hostname), my_hostname);
}

extern char **environ;

osi_spawn_process(sp, len)
    register char  *sp;
{
    char            combuf[400];
    char           *args[100];
    register char  *dp;
    register char **ap;
    register        cnt;
    int             err = 0;
    struct itimerval new_t, old_t;

    cs_cursorup();
    for (dp = sp, cnt = len; --cnt >= 0; dp++)
	switch (*dp) {
	  case '!':
	  case '"':
	  case '$':
	  case '&':
	  case '(':
	  case ')':
	  case '*':
	  case ';':
	  case '<':
	  case '>':
	  case '?':
	  case '[':
	  case '\'':
	  case '\\':
	  case ']':
	  case '`':
	  case '{':
	  case '|':
	  case '}':
	  case '~':
	    cnt = -1;
	}

    ap = args;
    if (cnt == -1) {		/* no special characters, don't invoke the
				 * shell */
	dp = combuf;
	while (len > 0 && ap < &args[sizeof args / sizeof args[0] - 1]) {
	    *ap++ = dp;
	    while (dp < combuf + sizeof combuf - 1 && *sp > ' ' && --len >= 0)
		*dp++ = *sp++;
	    *dp++ = 0;
	    while (*sp <= ' ' && --len >= 0)
		sp++;
	}
    } else {
	static char    *shell;

	strncpy(combuf, sp, len);
	combuf[len] = 0;
	if (shell == 0 && (shell = (char *) getenv("SHELL")) == 0)
	    shell = "/bin/sh";
	*ap++ = shell;
	*ap++ = "-c";
	*ap++ = combuf;
    }

    *ap++ = 0;
    timerclear(&new_t.it_value);
    timerclear(&new_t.it_interval);
    if (setitimer(ITIMER_VIRTUAL, &new_t, &old_t)) {
	perror("clear timeout_alarm");
    }
    if (vfork() == 0) {
	setpgrp(0, getpid());
	close(0);
	open("/dev/null", 2);
	dup2(0, 1);
/* Leave stderr alone so we see errors on the console -deh */
#ifdef notdef
	dup2(0, 2);
#endif
	execvp(args[0], args, environ);
	/*
	 * Beware: I'm depending on vfork semantics to propagate the error
	 * code.
	 */
	err = 1;
	_exit(0252);
    }
    if (setitimer(ITIMER_VIRTUAL, &old_t, &new_t)) {
	perror("set_timeout_alarm");
    }
    return err;
}

#ifndef HAVEPUTENV
putenv(name, value)
    char       *name,
               *value;
{
    register char *p;
    register    len;
    register char **ap;
    register char **new;
    register char *buf;
    static      alloced;

    len = strlen(name);
    buf = (char *) snoopalloc("SunOSI.c", len + strlen(value) + 2);
    sprintf(buf, "%s=%s", name, value);
    for (ap = environ; *ap; ap++)
	if (strncmp(*ap, name, len) == 0) {
	    *ap = buf;
	    return;
	}
    len = ap - environ;
    new = (char **) snoopalloc("SunOSI.c", (len + 2) * sizeof(char *));
    bcopy(environ, new, len * sizeof(char *));
    new[len] = buf;
    new[len + 1] = 0;
    if (alloced)
	free(environ);
    environ = new;
}

#endif

/*
 * ttysw_saveparms and we_setptyparms have been copied the suntool libaray:
 * "@(#)ttysw_stty.c 10.5 86/09/03 Copyr 1983 Sun Micro";
 */

/*
 * Ttysw parameter setting mechanism using given tty settings.
 */

/*
 * Determine ttyfd tty settings and cache in environment.
 */
static
ttysw_saveparms(ttyfd)
    int         ttyfd;
{
    int         ldisc,
                localmodes;
    struct sgttyb mode;
    struct tchars tchars;
    struct ltchars ltchars;

    /*
     * Get line discipline.
     */
    (void) ioctl(ttyfd, TIOCGETD, &ldisc);
    /*
     * Get tty parameters
     */
    (void) ioctl(ttyfd, TIOCGETP, &mode);
    /*
     * Get local modes
     */
    (void) ioctl(ttyfd, TIOCLGET, &localmodes);
    /*
     * Get terminal characters
     */
    (void) ioctl(ttyfd, TIOCGETC, &tchars);
    /*
     * Get local special characters
     */
    (void) ioctl(ttyfd, TIOCGLTC, &ltchars);
    /*
     * Write environment variable
     */
    (void) we_setptyparms(ldisc, localmodes, &mode, &tchars, &ltchars);
}

#define	WE_TTYPARMS	"WINDOW_TTYPARMS"
#define	WE_TTYPARMSLEN	120

/*
 * Save tty settings in environment.
 */
static
we_setptyparms(ldisc, localmodes, mode, tchars, ltchars)
    int         ldisc,
                localmodes;
    struct sgttyb *mode;
    struct tchars *tchars;
    struct ltchars *ltchars;
{
    char        str[WE_TTYPARMSLEN];

    str[0] = '\0';
    /*
     * %c cannot be used to write the character valued fields because they
     * often have a value of \0.
     */
    (void) sprintf(str, "%D,%D,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
		   ldisc, localmodes,
	     mode->sg_ispeed, mode->sg_ospeed, mode->sg_erase, mode->sg_kill,
		   mode->sg_flags,
		   tchars->t_intrc, tchars->t_quitc, tchars->t_startc,
		   tchars->t_stopc, tchars->t_eofc, tchars->t_brkc,
		   ltchars->t_suspc, ltchars->t_dsuspc, ltchars->t_rprntc,
		   ltchars->t_flushc, ltchars->t_werasc, ltchars->t_lnextc);
    (void) setenv(WE_TTYPARMS, str);
}

static
/*
 * Enumerate all the directories in the font path and push the names of all
 * font family files onto the PostScript stack.  Font family files are
 * identified by their extension: .ffam, .ffa or .ff
 */
enumeratefontdicts(ee)
    register struct execution_environment *ee;
{
    extern char *cs_fontpath();
    register char *p,
               *s,
               *d;
    for (s = cs_fontpath(); *s; s = p + 1) {
	char        buf[300];
	DIR        *dir;
	register struct direct *ent;
	d = buf;
	p = s;
	while (*p != ':' && *p)
	    *d++ = *p++;
	if (d == buf)
	    *d++ = '.';
	*d++ = 0;
	if (dir = opendir(buf)) {
	    while (ent = readdir(dir)) {
		register    n = ent->d_namlen;
		char        fullname[300];
		register char *ffs;
		d = &ent->d_name[n];
		while (--n >= 0 && *--d != '.');
		if (n < 0 || ent->d_namlen - n < 3 || ent->d_namlen - n > 5)
		    continue;
		for (ffs = ".ffam"; n < ent->d_namlen; n++)
		    if (*d++ != *ffs++)
			break;
		if (n != ent->d_namlen)
		    continue;
		if (ee->optop >= ee->overflow && grow_stack(ee, 1) < 0) {
		    ee->error_code = stackoverflow_error_code;
		    closedir(dir);
		    return;
		}
		sprintf(fullname, "%s/%.*s", buf, ent->d_namlen, ent->d_name);
		*ee->optop = make_string(strlen(fullname), fullname);
		ee->optop++;
	    }
	    closedir(dir);
	}
	if (*p == 0)
	    return 0;
    }
}

/* OSI specific clean executed just before NeWS exits */
restore_keyboard()
{
    register    i;
    char        map[256];

    if (vsdev < 0)
	return;
    setkeyboardtranslation(NORMAL_KEYBOARD);
    raw_keyboard = 0;
    for (i = 0; i < 256;)
	map[i++] = 255, map[i++] = 0;
    if (CurrentInputScene)
	pr_putcolormap(CurrentInputScene->display, 0, 256, map, map, map);
    /* See if other SunWindows on NeWS */
    if (sun_created_root &&
	    win_getlink(vsdev, WL_TOPCHILD) != WIN_NULLLINK) {
	/*
	 * Destroy screen sends SIGTERMs to all existing windows and wouldn''t
	 * let any windows install themselves in the window tree. Calling
	 * process of win_screedestroy is spared SIGTERM.
	 */
	(void) win_screendestroy(vsdev);
    }
}

#ifndef DEBUG
static
CatastrophicSignalHandler(sig, code, scp)
	int sig;
	int code;
	struct sigcontext *scp;
{
    restore_keyboard();

    write(2, "NeWS: unexpected ", 17);
    switch (sig) {
    case SIGBUS: write(2, "SIGBUS ", 7); break;
    case SIGSEGV: write(2, "SIGSEGV ", 8); break;
    }
    write(2, "signal received\n", 16);

    abort(0);
    exit(1);
}

#endif

initialize_SunOSI()
{
    vsdev = -1;
    news_pid = getpid();
    raw_keyboard = 0;
    raw_mouse = 0;
    init_scene_clipping_matrix();
    sun_cursor_zone = 16;
    motion_keyword = get_name("MouseDragged", -1);
    but1_keyword = get_name("LeftMouseButton", -1);
    but2_keyword = get_name("MiddleMouseButton", -1);
    but3_keyword = get_name("RightMouseButton", -1);
    raw_motion_keyword = get_name("RawMouseDragged", -1);
    raw_but1_keyword = get_name("RawLeftMouseButton", -1);
    raw_but2_keyword = get_name("RawMiddleMouseButton", -1);
    raw_but3_keyword = get_name("RawRightMouseButton", -1);
    up_keyword = get_name("UpTransition", -1);
    down_keyword = get_name("DownTransition", -1);
    define_operator("getkeyboardtranslation", getkeyboardtranslation, 0, 0, 1);
    define_operator("getmousetranslation", getmousetranslation, 0, 0, 1);
    define_operator("keyboardtype", keyboardtype, 0, 0, 1);
    define_operator("setkeyboardtranslation", setkeyboardtranslation_primitive, 0, 1, 0);
    define_operator("setmousetranslation", setmousetranslation, 0, 1, 0);
    define_operator("startkeyboardandmouse", startkeyboardandmouse, 0, 0, 0);
    define_operator("getenv", getenv_primitive, 0, 1, 1);
    define_operator("putenv", putenv_primitive, 0, 2, 0);
    define_operator("localhostname", localhostname, 0, 0, 1);
    define_operator("getsocketpeername", getsocketpeername, 0, 1, 1);
    define_operator("getsocketlocaladdress", getsocketlocaladdress, 0, 1, 1);
    define_operator("enumeratefontdicts", enumeratefontdicts, 0, 0, 0);
    signal(SIGCHLD, reaper);
    signal(SIGPIPE, SIG_IGN);
    signal(SIGFPE, SIG_IGN);

#ifndef DEBUG
    signal(SIGBUS, CatastrophicSignalHandler);
    signal(SIGSEGV, CatastrophicSignalHandler);
#endif

    gethostname(my_hostname, sizeof my_hostname);
    my_hostname[sizeof my_hostname - 1] = 0;
}