/*
 * dpsXclient.c -- Implementation of the Display PostScript Client Library.
 *
 * (c) Copyright 1988-1994 Adobe Systems Incorporated.
 * All rights reserved.
 * 
 * Permission to use, copy, modify, distribute, and sublicense this software
 * and its documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notices appear in all copies and that
 * both those copyright notices and this permission notice appear in
 * supporting documentation and that the name of Adobe Systems Incorporated
 * not be used in advertising or publicity pertaining to distribution of the
 * software without specific, written prior permission.  No trademark license
 * to use the Adobe trademarks is hereby granted.  If the Adobe trademark
 * "Display PostScript"(tm) is used to describe this software, its
 * functionality or for any other purpose, such use shall be limited to a
 * statement that this software works in conjunction with the Display
 * PostScript system.  Proper trademark attribution to reflect Adobe's
 * ownership of the trademark shall be given whenever any such reference to
 * the Display PostScript system is made.
 * 
 * ADOBE MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THE SOFTWARE FOR
 * ANY PURPOSE.  IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
 * ADOBE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
 * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NON- INFRINGEMENT OF THIRD PARTY RIGHTS.  IN NO EVENT SHALL ADOBE BE LIABLE
 * TO YOU OR ANY OTHER PARTY FOR ANY SPECIAL, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER WHETHER IN AN ACTION OF CONTRACT,
 * NEGLIGENCE, STRICT LIABILITY OR ANY OTHER ACTION ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.  ADOBE WILL NOT
 * PROVIDE ANY TRAINING OR OTHER SUPPORT FOR THE SOFTWARE.
 * 
 * Adobe, PostScript, and Display PostScript are trademarks of Adobe Systems
 * Incorporated which may be registered in certain jurisdictions
 * 
 * Author:  Adobe Systems Incorporated
 */
/* $XFree86$ */

#include <stdlib.h>
#include <unistd.h>	/* sleep() */
#include <stdio.h>
#include <string.h>
#include <ctype.h>

#ifdef VMS
/* Xlib does not like UNIX defined to any value under VMS. */
#undef UNIX
#include <decw$include/X.h>
#include <decw$include/Xlib.h>

#else /* VMS */
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#endif /* VMS */

#include "DPS/XDPSlib.h"
#include "DPS/XDPS.h"

#include "publictypes.h"
#include "DPS/dpsclient.h"
#include "dpsprivate.h"

#include "dpsXpriv.h"
#include "DPS/dpsXclient.h"

#include "dpsdict.h"
#include "DPS/dpsexcept.h"

#include "dpsXint.h"

static DPSPrivContext FindPrivContext (
  Display  *	dpy,
  long int	cid)
{
  DPSPrivSpace ss;
  DPSPrivContext cc;

  for (ss = spaces;  ss != NIL;  ss = ss->next)
    for (cc = ss->firstContext;  cc != NIL;  cc = cc->next)
      if (cc->cid == cid && ((XDPSPrivContext) cc->wh)->dpy == dpy)
	return (cc);
  return (NIL);
}

DPSContext XDPSFindContext (
  Display  *	dpy,
  int		cid)
{
  return ((DPSContext) FindPrivContext (dpy, cid));
}

DPSContext DPSContextFromContextID(
  DPSContext ctxt,
  int contextID,
  DPSTextProc textProc,
  DPSErrorProc errorProc)
{
  DPSPrivSpace ss;
  Display *dpy = ((XDPSPrivContext) ((DPSPrivContext) ctxt)->wh)->dpy;
  DPSPrivContext c, cc = FindPrivContext (dpy, contextID);
  DPSPrivContext oldc = (DPSPrivContext)ctxt;
  
  if (cc != NIL) return (DPSContext)cc;

  c = (DPSPrivContext)DPScalloc(sizeof(DPSPrivContextRec), 1);
  if (!c) return NIL;
  *c = *oldc;
  ss = (DPSPrivSpace)c->space;
  
  if (textProc) c->textProc = textProc;
  if (errorProc) c->errorProc = errorProc;

  c->eofReceived = false;
  c->cid = contextID;
  c->buf = c->outBuf = c->objBuf = NIL;
  c->chainParent = c->chainChild = NIL;
  
  c->nBufChars = c->nOutBufChars = c->nObjBufChars = 0;

  c->next = ss->firstContext;
  DPSAssert(c->next != c);
  ss->firstContext = c;

  /* Note: there's no way to determine whether the new context id was obtained
  ** as a result of a fork operation or from another application.  so, it must
  ** be assumed that the application is the creator of the new context.
  ** Otherwise, it would have called the XDPSContextFromSharedID.
  */
  c->creator = true;
  c->zombie = false;
  c->numstringOffsets = NULL;

  DPSIncludePrivContext(
    (XDPSPrivContext) c->wh, (DPSContext)c, c->cid, ss->sid, DPSclientPrintProc);

  return (DPSContext)c;
}

boolean DPSPrivateCheckWait(
    DPSContext ctxt)
{
    DPSPrivContext cc = (DPSPrivContext) ctxt;

    if (!cc->creator || cc->zombie) {
	DPSSafeSetLastNameIndex(ctxt);
	if (cc->errorProc != NIL) {
	    (*cc->errorProc) (ctxt, cc->zombie ? dps_err_deadContext :
			                         dps_err_invalidAccess,
			      (unsigned long) ctxt, 0);
	}
	return true;
    }
    return false;
}

static void procFlushContext(
  DPSContext ctxt)
{
  DPSPrivContext c = (DPSPrivContext) ctxt;
  XDPSLFlush (((XDPSPrivContext) c->wh)->dpy);
  if (ctxt->chainChild != NIL) DPSFlushContext(ctxt->chainChild);
}

/* ARGSUSED */
static Bool FindDPSEvent(
  Display *dpy,
  XEvent *event,
  char *arg)
{
  return XDPSIsDPSEvent(event);
}

static void procAwaitReturnValues(DPSContext ctxt)
{
  DPSPrivContext c = (DPSPrivContext)ctxt;
 
  XDPSPrivContext xwh = (XDPSPrivContext) c->wh;
  XEvent ev;

  /* Output currently goes only to creator! */
  if (!c->creator)
    {
    DPSSafeSetLastNameIndex(ctxt);
    c->resultTable = NIL;
    c->resultTableLength = 0;
    if (c->errorProc != NIL)
      (*c->errorProc) (ctxt, dps_err_invalidAccess, 0, 0);
    return;
    }
  if (c->resultTable != NIL)
    {
    DPSCheckInitClientGlobals();

    if (XDPSLGetWrapWaitingFlag(xwh->dpy)) {
	DPSSafeSetLastNameIndex(ctxt);
	c->resultTable = NIL;
	c->resultTableLength = 0;
	if (c->errorProc != NIL)
		(*c->errorProc) (ctxt, dps_err_recursiveWait,
				 (unsigned long) xwh->dpy, 0);
	return;
    }
    XDPSLSetWrapWaitingFlag(xwh->dpy, True);

  DURING
    DPSFlushContext(ctxt);
    while (c->resultTable != NIL)
      {
      /* We may block indefinitely if the context is frozen or it
	 somehow needs more input. */
      if (c->zombie)
	{
	DPSSafeSetLastNameIndex(ctxt);
	c->resultTable = NIL;
	c->resultTableLength = 0;
	if (c->errorProc != NIL)
	  (*c->errorProc) (ctxt, dps_err_deadContext, (unsigned long) c, 0);
	XDPSLSetWrapWaitingFlag(xwh->dpy, False);
	E_RTRN_VOID;
	}
      
      /* Someone could conceivably change the event delivery mode in the
	 middle of this...best to check every time */

      if (XDPSLGetPassEventsFlag(xwh->dpy)) {
	  XIfEvent(xwh->dpy, &ev, FindDPSEvent, (char *) NULL);
	  if (!XDPSDispatchEvent(&ev)) DPSCantHappen();
      } else DPSSendPostScript((XDPSPrivContext) c->wh, DPSclientPrintProc,
			       c->cid, NIL, 0, NIL);
    }
  HANDLER
    XDPSLSetWrapWaitingFlag(xwh->dpy, False);
    RERAISE;
  END_HANDLER

  XDPSLSetWrapWaitingFlag(xwh->dpy, False);

  }

 /* update space's name map.
       space->lastNameIndex is the highest index known to be known to the
	 server for this space.
       c->lastNameIndex is the highest index sent so far to the context
 */

  if (((DPSPrivSpace)(c->space))->lastNameIndex < c->lastNameIndex)
    ((DPSPrivSpace)(c->space))->lastNameIndex = c->lastNameIndex;
}

void DPSinnerProcWriteData(
  DPSContext ctxt,
  char *buf,
  unsigned int count)
{
  DPSPrivContext c = (DPSPrivContext) ctxt;
  
  /* ASSERT: safe to call with chain */

  /* No local buffering */
  DPSSendPostScript ((XDPSPrivContext) c->wh, DPSclientPrintProc,
		     c->cid, buf, count, NIL);
} /* DPSinnerProcWriteData */

static void procResetContext(DPSContext ctxt)
{
  DPSPrivContext c = (DPSPrivContext) ctxt;
  int currStatus;
  register XDPSPrivContext xwh = (XDPSPrivContext) c->wh;
  int retries = 0;
  int backoff = 2;
  
  /* First make sure context isn't frozen, try to unfreeze. */

#define DPS_SLEEP_SECS 2

  while((currStatus = XDPSLGetStatus(xwh->dpy, xwh->cxid)) == PSFROZEN)
    {
    XDPSLNotifyContext(xwh->dpy, xwh->cxid, PSUNFREEZE);
    sleep(DPS_SLEEP_SECS);
      /* Okay if context is PSRUNNING, since the EOF will
         be handled at the next PSNEEDSINPUT */
    }

  /* Remove events from Xlib Qs before sending the reset request. */
  XDPSForceEvents (xwh->dpy);
  
  if (currStatus == PSSTATUSERROR)
    /* +++ report error? */;
  else  /* Didn't become zombie. */
    {
    currStatus = 0;
    XDPSLReset(xwh->dpy, xwh->cxid);
    XDPSLFlush(xwh->dpy);
    /* Be optmistic for the first try. Assume the app set up a status mask
       correctly, we should get a status event without asking the
       server for status. */
    
    XDPSForceEvents(xwh->dpy);
    currStatus = c->statusFromEvent;

    while (currStatus != PSNEEDSINPUT && currStatus != PSZOMBIE)
      {
      if (currStatus == PSFROZEN)
          XDPSLNotifyContext(xwh->dpy, xwh->cxid, PSUNFREEZE);
      if (retries > backoff)
        {
        /* Optimism failed.  App probably didn't set up a status mask.
           Ask the server for status. */
        currStatus = XDPSLGetStatus(xwh->dpy, xwh->cxid);
        retries = 0;
        backoff = (backoff > 30) ? 2 : backoff + 1;
        continue;
        }
      else
        ++retries;
      sleep(DPS_SLEEP_SECS);
      XDPSForceEvents(xwh->dpy);
      currStatus = c->statusFromEvent;
      }
    }
  
  c->eofReceived = false;
}

void DPSPrivateDestroyContext(DPSContext ctxt)
{
  DPSPrivContext c = (DPSPrivContext)ctxt;
  DPSPrivSpace s = (DPSPrivSpace) c->space;

  if (c->creator)
    DPSSendTerminate((XDPSPrivContext) c->wh, c->cid, DPSclientPrintProc);
  else 
    XDPSSetStatusMask(ctxt, 0, XDPSL_ALL_EVENTS, 0);  /* Stop status events */
  /* Don't free the space's wh out from under it */
  if (c->wh != s->wh) free(c->wh);
}

void DPSPrivateDestroySpace(DPSSpace space)
{
    DPSPrivSpace ss = (DPSPrivSpace) space;

    if (ss->creator) DPSSendDestroySpace((XDPSPrivContext) ss->wh, ss->sid,
					 DPSclientPrintProc);

    free (ss->wh);
}

boolean DPSCheckShared(DPSPrivContext ctxt)
{
  return ctxt->creator == false && ctxt->resultTable != NIL;
  /* let procAwaitReturnValues generate error */
}

/* ARGSUSED */
void DPSServicePostScript(boolean (*returnControl)(void))
{
} /* DPSServicePostScript */

void DPSHandleBogusError(DPSContext ctxt, char *prefix, char *suffix)
{
    char *buf = "bogus error output from context";
    DPSDefaultPrivateHandler(ctxt, dps_err_warning,
			     (long unsigned int)buf, 0, prefix, suffix);
}

void DPSDefaultPrivateHandler(
  DPSContext ctxt,
  DPSErrorCode errorCode,
  long unsigned int arg1,
  long unsigned int arg2,
  char *prefix,
  char *suffix)
{

  DPSTextProc textProc = DPSGetCurrentTextBackstop();

  switch (errorCode) {
    case dps_err_invalidAccess:
      if (textProc != NIL)
	{
	char m[100];
	(void) sprintf (m, "%sInvalid context access.%s", prefix, suffix);
	(*textProc) (ctxt, m, strlen (m));
	}
	break;
    case dps_err_encodingCheck:
      if (textProc != NIL)
	{
	char m[100];
	(void) sprintf (m, "%sInvalid name/program encoding: %d/%d.%s",
			prefix, (int) arg1, (int) arg2, suffix);
	(*textProc) (ctxt, m, strlen (m));
	}
	break;
    case dps_err_closedDisplay:
      if (textProc != NIL)
	{
	char m[100];
	(void) sprintf (m, "%sBroken display connection %d.%s",
			prefix, (int) arg1, suffix);
	(*textProc) (ctxt, m, strlen (m));
	}
	break;
    case dps_err_deadContext:
      if (textProc != NIL)
	{
	char m[100];
	(void) sprintf (m, "%sDead context 0x0%x.%s", prefix,
			(int) arg1, suffix);
	(*textProc) (ctxt, m, strlen (m));
	}
	break;
    case dps_err_warning:
      if (textProc != NIL)
        {
        char *warn = (char *)arg1;
        char *msg = "%% DPS Client Library Warning:\n   ";
        (*textProc)(ctxt, msg, strlen(msg));
        (*textProc)(ctxt, warn, strlen(warn));
        msg = "\n";
        (*textProc)(ctxt, msg, strlen(msg));
        /* flush convention */
        (*textProc)(ctxt, msg, 0);
        }
        break;
    case dps_err_fatal:
      if (textProc != NIL)
        {
        char *fatal = (char *)arg1;
        char *msg = "%% DPS Client Library Fatal Internal Error:\n   ";
        (*textProc)(ctxt, msg, strlen(msg));
        (*textProc)(ctxt, fatal, strlen(fatal));
        msg = ".\nAborting ...\n";
        (*textProc)(ctxt, msg, strlen(msg));
        /* flush convention */
        (*textProc)(ctxt, msg, 0);
        abort();
        }
        break;
    case dps_err_recursiveWait:
      if (textProc != NIL)
	{
	char m[100];
	(void) sprintf (m,
			"%sRecursive wait for return values, display 0x%x.%s",
			prefix, (int) arg1, suffix);
	(*textProc) (ctxt, m, strlen (m));
	}
	break;
  }
}

void DPSInitPrivateSpaceFields(DPSPrivSpace s)
{
    s->creator = true;
}

void DPSInitPrivateContextFields(DPSPrivContext c, DPSPrivSpace s)
{
    c->creator = true;
    c->zombie = false;
    if (!s->creator) {
	c->procs = XDPSconvProcs;
	c->nameEncoding = dps_strings;
    }
}

void  DPSInitPrivateTextContextFields(DPSPrivContext c, DPSPrivSpace s)
{
    c->creator = true;
    c->zombie = false;
    c->space = (DPSSpace) s;
    c->next = s->firstContext;
    s->firstContext = c;
}
  
long int DPSLastUserObjectIndex = 0;

long int DPSNewUserObjectIndex (void)
{
  return (DPSLastUserObjectIndex++);
}

void XDPSSetProcs (void)
{
  DPSCheckInitClientGlobals ();
  if (!textCtxProcs)
    {
    textCtxProcs = (DPSProcs) DPScalloc (sizeof (DPSProcsRec), 1);
    DPSInitCommonTextContextProcs(textCtxProcs);
    DPSInitSysNames();
    }
  if (!ctxProcs)
    {
    ctxProcs = (DPSProcs) DPScalloc (sizeof (DPSProcsRec), 1);
    DPSInitCommonContextProcs(ctxProcs);
    DPSInitPrivateContextProcs(ctxProcs);
    }
  if (!XDPSconvProcs)
    XDPSconvProcs = (DPSProcs) DPScalloc (sizeof (DPSProcsRec), 1);
  if (!XDPSrawProcs)
    XDPSrawProcs = ctxProcs;
  *XDPSconvProcs = *ctxProcs;
  XDPSconvProcs->BinObjSeqWrite = textCtxProcs->BinObjSeqWrite;
  XDPSconvProcs->WriteStringChars = textCtxProcs->WriteStringChars;
  XDPSconvProcs->WritePostScript = textCtxProcs->WritePostScript;
  XDPSconvProcs->WriteNumString = textCtxProcs->WriteNumString;
}

void DPSInitPrivateContextProcs(DPSProcs p)
{
    p->FlushContext = procFlushContext;
    p->ResetContext = procResetContext;
    p->AwaitReturnValues = procAwaitReturnValues;
}

DPSContext XDPSCreateSimpleContext (
  Display	*dpy,
  Drawable	draw,
  GC		gc,
  int		x,
  int		y,
  DPSTextProc	textProc,
  DPSErrorProc	errorProc,
  DPSSpace	space)
{
  XDPSPrivContext xwh = XDPSCreatePrivContextRec (dpy, draw, gc, x, y,
  						  0, DefaultStdCMap,
						  DefaultStdCMap, 0, false);
  DPSContext newCtxt;
  
  if (xwh == NIL)
    return (NIL);
  else
    {
    newCtxt = DPSCreateContext ((char *) xwh, textProc, errorProc, space);
    if (newCtxt == NIL)
      free ((char *) xwh);
    return (newCtxt);
    }
}


DPSContext XDPSCreateContext (
  Display		*dpy,
  Drawable		draw,
  GC			gc,
  int			x,
  int			y,
  unsigned int		eventmask,
  XStandardColormap	*grayramp,
  XStandardColormap	*ccube,
  int			actual,
  DPSTextProc		textProc,
  DPSErrorProc		errorProc,
  DPSSpace		space)
{
  XDPSPrivContext xwh = XDPSCreatePrivContextRec (dpy, draw, gc, x, y,
  						  eventmask, grayramp,
						  ccube, actual, false);
  DPSContext newCtxt;
  
  if (xwh == NIL)
    return (NIL);
  else
    {
    newCtxt = DPSCreateContext ((char *) xwh, textProc, errorProc, space);
    if (newCtxt == NIL)
      free ((char *) xwh);
    return (newCtxt);
    }
}

DPSContext XDPSCreateSecureContext (
  Display		*dpy,
  Drawable		draw,
  GC			gc,
  int			x,
  int			y,
  unsigned int		eventmask,
  XStandardColormap	*grayramp,
  XStandardColormap	*ccube,
  int			actual,
  DPSTextProc		textProc,
  DPSErrorProc		errorProc,
  DPSSpace		space)
{
  XDPSPrivContext xwh = XDPSCreatePrivContextRec (dpy, draw, gc, x, y,
  						  eventmask, grayramp,
						  ccube, actual, true);
  DPSContext newCtxt;
  
  if (xwh == NIL)
    return (NIL);
  else
    {
    newCtxt = DPSCreateContext ((char *) xwh, textProc, errorProc, space);
    if (newCtxt == NIL)
      free ((char *) xwh);
    return (newCtxt);
    }
}


DPSContext XDPSContextFromSharedID (dpy, cid, textProc, errorProc)
  Display	  *dpy;
  ContextPSID	  cid;
  DPSTextProc	  textProc;
  DPSErrorProc	  errorProc;
{
  DPSPrivContext	c;
  DPSPrivSpace		s; 
  ContextXID		cxid;
  SpaceXID		sxid;
  XDPSPrivContext	xwh;

  if (DPSInitialize () != 0)
    return (NIL);

  c = FindPrivContext (dpy, cid);
  if (c != NIL)
    return ((DPSContext) c);

  xwh = XDPSCreatePrivContextRec (dpy, 0, 0, 0, 0, 0, NIL, NIL, 0, false);
  if (xwh == NIL)
    return (NIL);
  else if (XDPSLIDFromContext (dpy, cid, &cxid, &sxid) != 1)
    {
    free ((char *) xwh);
    return (NIL);
    }
  xwh->cxid = cxid;

  if (spaceProcs == NIL)
    {
    spaceProcs = (DPSSpaceProcs) DPScalloc (sizeof (DPSSpaceProcsRec), 1);
    DPSInitCommonSpaceProcs(spaceProcs);
    }

  s = spaces;
  while (s != NIL)
    if ((SpaceXID)s->sid == sxid && ((XDPSPrivContext) s->wh)->dpy == dpy)
      break;
    else
      s = s->next;

  if (s == NIL)	  /* Create new space record. */
    {
    s = (DPSPrivSpace) DPScalloc (sizeof (DPSPrivSpaceRec), 1);
    s->procs = spaceProcs;
    s->lastNameIndex = -1;
    s->sid = sxid;
    s->wh = (char *) xwh;
    s->creator = false;
    s->next = spaces;
    spaces = s;
    }

  c = (DPSPrivContext) DPScalloc (sizeof (DPSPrivContextRec), 1);
  c->space = (DPSSpace) s;
  c->procs = XDPSconvProcs;
  c->textProc = textProc;
  c->errorProc = errorProc;
  c->programEncoding = DPSDefaultProgramEncoding;
  c->nameEncoding = dps_strings;
  c->next = s->firstContext;
  s->firstContext = c;
  c->lastNameIndex = s->lastNameIndex;
  c->cid = cid;
  c->numstringOffsets = NULL;
  c->creator = false;
  c->zombie = false;
  c->numFormat = XDPSNumFormat (dpy);
  c->wh = (char *) xwh;
  
  xwh->ctxt = (DPSContext) c;

  return ((DPSContext) c);
}


void DPSChangeEncoding (ctxt, newProgEncoding, newNameEncoding)
  DPSContext	     ctxt;
  DPSProgramEncoding newProgEncoding;
  DPSNameEncoding    newNameEncoding;
{
  if (ctxt->programEncoding != newProgEncoding ||
      ctxt->nameEncoding != newNameEncoding)
    {
      DPSPrivContext cc = (DPSPrivContext) ctxt;
      DPSPrivSpace   ss = (DPSPrivSpace) (cc->space);
      
      if ((!cc->creator || !ss->creator) && newNameEncoding != dps_strings)
	{
	DPSSafeSetLastNameIndex(ctxt);
	if (cc->errorProc != NIL)
	  (*cc->errorProc) (ctxt, dps_err_encodingCheck,
			    (unsigned long) newNameEncoding,
			    (unsigned long) newProgEncoding);
	return;
	}
      if (ctxt->procs == textCtxProcs)
        {
        ctxt->programEncoding = newProgEncoding;
        ctxt->nameEncoding = newNameEncoding;
        }
      else
        XDPSSetContextEncoding (ctxt, newProgEncoding, newNameEncoding);
    }
}


DPSSpace XDPSSpaceFromSharedID (dpy, sid)
  Display	*dpy;
  SpaceXID	sid;
{
  DPSPrivSpace		s;
  XDPSPrivContext	xwh;

  if (DPSInitialize () != 0)
    return (NIL);

  if (spaceProcs == NIL)
    {
    spaceProcs = (DPSSpaceProcs) DPScalloc (sizeof (DPSSpaceProcsRec), 1);
    DPSInitCommonSpaceProcs(spaceProcs);
    }

  s = spaces;
  while (s != NIL)
    if ((SpaceXID)s->sid == sid && ((XDPSPrivContext) s->wh)->dpy == dpy)
      break;
    else
      s = s->next;

  if (s == NIL)	  /* Create new space record. */
    {
    xwh = XDPSCreatePrivContextRec (dpy, 0, 0, 0, 0, 0, NIL, NIL, 0, false);
    if (xwh == NIL)
      return (NIL);

    s = (DPSPrivSpace) DPScalloc (sizeof (DPSPrivSpaceRec), 1);
    s->procs = spaceProcs;
    s->lastNameIndex = -1;
    s->sid = sid;
    s->wh = (char *) xwh;
    s->creator = false;
    s->next = spaces;
    spaces = s;
    }

  return ((DPSSpace) s);
}


void XDPSUnfreezeContext (ctxt)
  DPSContext ctxt;
{
  XDPSPrivContext wh = (XDPSPrivContext) (((DPSPrivContext) ctxt)->wh);
  
  if (wh != NIL && wh->cxid != 0)
    XDPSSendUnfreeze (wh->dpy, wh->cxid);
}


ContextXID XDPSXIDFromContext (Pdpy, ctxt)
  Display    **Pdpy;
  DPSContext ctxt;
{
  XDPSPrivContext  xwh = (XDPSPrivContext) (((DPSPrivContext) ctxt)->wh);
  
  if (xwh == NIL || xwh->cxid == 0)
    {
    *Pdpy = NULL;
    return (0);
    }
  else
    {
    *Pdpy = xwh->dpy;
    return (xwh->cxid);
    }
}


SpaceXID XDPSXIDFromSpace (Pdpy, space)
  Display    **Pdpy;
  DPSSpace   space;
{
  DPSPrivSpace	   ss = (DPSPrivSpace) space;
  XDPSPrivContext  xwh = (XDPSPrivContext) ss->wh;
  
  if (xwh != NIL && xwh->dpy != NULL)
    {
    *Pdpy = xwh->dpy;
    return (ss->sid);
    }
  else
    {
    *Pdpy = NULL;
    return (0);
    }
}


DPSContext XDPSContextFromXID (dpy, cxid)
  Display    *dpy;
  ContextXID cxid;
{
  DPSPrivContext c;
  DPSPrivSpace	 ss;
  
  for (ss = spaces;  ss != NIL;  ss = ss->next)
    if (((XDPSPrivContext) ss->wh)->dpy == dpy)
      for (c = ss->firstContext;  c != NIL;  c = c->next)
	if (((XDPSPrivContext) c->wh)->cxid == cxid)
	  return ((DPSContext) c);

  return (NIL);
}


DPSSpace XDPSSpaceFromXID (dpy, sxid)
  Display    *dpy;
  SpaceXID   sxid;
{
  DPSPrivSpace  ss;
  
  for (ss = spaces;  ss != NIL;  ss = ss->next)
    if ((SpaceXID)ss->sid == sxid && ((XDPSPrivContext) ss->wh)->dpy == dpy)
      return ((DPSSpace) ss);

  return (NIL);
}


XDPSStatusProc XDPSRegisterStatusProc (ctxt, statusProc)
  DPSContext	 ctxt;
  XDPSStatusProc statusProc;
{
  DPSPrivContext c = (DPSPrivContext) ctxt;
  XDPSStatusProc old = c->statusProc;
  
  if (c->wh != NIL) c->statusProc = statusProc;
  return old;
}


XDPSReadyProc XDPSRegisterReadyProc (ctxt, readyProc)
  DPSContext	 ctxt;
  XDPSReadyProc readyProc;
{
  DPSPrivContext c = (DPSPrivContext) ctxt;
  XDPSReadyProc old = c->readyProc;
  
  if (c->wh != NIL) c->readyProc = readyProc;
  return old;
}


void XDPSSetStatusMask(ctxt, enableMask, disableMask, nextMask)
  DPSContext ctxt;
  unsigned long enableMask, disableMask, nextMask;
{
  XDPSPrivContext xwh = (XDPSPrivContext) (((DPSPrivContext) ctxt)->wh);
  
  if (xwh != NIL && xwh->cxid != 0)
    XDPSLSetStatusMask(xwh->dpy, xwh->cxid, enableMask, disableMask, nextMask);
}


int XDPSGetContextStatus(ctxt)
  DPSContext ctxt;
{
  DPSPrivContext  c = (DPSPrivContext) ctxt;
  XDPSPrivContext xwh = (XDPSPrivContext) c->wh;
  
  if (xwh != NIL && xwh->cxid != 0)
    return (XDPSLGetStatus(xwh->dpy, xwh->cxid));
  else
    return (0);
}

void XDPSNotifyWhenReady(ctxt, i0, i1, i2, i3)
    DPSContext ctxt;
    int i0, i1, i2, i3;
{
    DPSPrivContext  c = (DPSPrivContext) ctxt;
    XDPSPrivContext xwh = (XDPSPrivContext) c->wh;
    int i[4];

    i[0] = i0;
    i[1] = i1;
    i[2] = i2;
    i[3] = i3;
    
    XDPSLNotifyWhenReady(xwh->dpy, xwh->cxid, i);
}

void XDPSStatusEventHandler (e)
  XDPSLStatusEvent *e;
{
  DPSPrivContext  c = (DPSPrivContext) XDPSContextFromXID(e->display, e->cxid);
  
  if (c == NIL)
    return;
  
  c->statusFromEvent = e->status;
  if (e->status == PSZOMBIE)
    {
    c->zombie = true;
    if (c->resultTable != NIL)  /* Currently waiting for results */
      XDPSQuitBlocking = true;
    }
  
  if (c->statusProc != NIL)
    (*(c->statusProc)) ((DPSContext) c, e->status);
}

void XDPSReadyEventHandler (e)
  XDPSLReadyEvent *e;
{
  DPSPrivContext  c = (DPSPrivContext) XDPSContextFromXID(e->display, e->cxid);
  
  if (c == NIL)
    return;
  
  if (c->readyProc != NIL)
    (*(c->readyProc)) ((DPSContext) c, e->val);
}



void DPSWarnProc(
    DPSContext ctxt,
    char *msg)
{
    DPSErrorProc ep;

    if (DPSInitialize() != 0) return;
    ep = DPSGetCurrentErrorBackstop();
    if (ep == NULL) ep = DPSDefaultErrorProc;
    (*ep)(ctxt, dps_err_warning, (long unsigned int)msg, 0);
}

void DPSFatalProc(
    DPSContext ctxt,
    char *msg)
{
    DPSErrorProc ep;

    if (DPSInitialize() != 0) return;
    ep = DPSGetCurrentErrorBackstop();
    if (ep == NULL) ep = DPSDefaultErrorProc;
    (*ep)(ctxt, dps_err_fatal, (long unsigned int)msg, 0);
}

void DPSCantHappen(void)
{
  static int locked = 0;
  char *msg = "assertion failure or DPSCantHappen";
  if (locked > 0) abort();
  ++locked;
  DPSFatalProc((DPSContext)NULL, msg);
  /* Fatal proc shouldn't return, but client can override and do anything. */
  --locked;
}


/* Procedures for delayed event dispatching */

DPSEventDelivery XDPSSetEventDelivery(
    Display *dpy,
    DPSEventDelivery newMode)
{
    Bool old = XDPSLGetPassEventsFlag(dpy);

    switch (newMode) {
	case dps_event_pass_through:
	    XDPSLSetPassEventsFlag(dpy, True);
	    break;
	case dps_event_internal_dispatch:
	    XDPSLSetPassEventsFlag(dpy, False);
	    break;
	default:
	    break;
    }

    if (old) return dps_event_pass_through;
    else return dps_event_internal_dispatch;
}

Bool XDPSIsStatusEvent(
    XEvent *event,
    DPSContext *ctxt,
    int *status)
{
    Display *d = event->xany.display;
    XExtCodes *c = XDPSLGetCodes(d);
    XDPSLStatusEvent *se = (XDPSLStatusEvent *) event;

    if (c == NULL) return False;	/* Not inited on that display;
					   must be False */

    if (!c->first_event)		/* Check CSDPS first */
        {
	if (XDPSLGetCSDPSFakeEventType(d, event) == csdps_status)
	    {				/* Check CSDPS first */
	    XDPSLGetCSDPSStatus(d, event, (void **)ctxt, status);
	    return True;
	    }
	else
	    return False;
	}

    if (event->type != c->first_event + PSEVENTSTATUS) return False;

    if (ctxt != NULL) *ctxt = XDPSContextFromXID(d, se->cxid);
    if (status != NULL) *status = se->status;
    return True;
}

Bool XDPSIsOutputEvent(
    XEvent *event)
{
    Display *d = event->xany.display;
    XExtCodes *c = XDPSLGetCodes(d);
    CSDPSFakeEventTypes t;

    if (c == NULL) return False;	/* Not inited on that display;
					   must be False */

    if (!c->first_event)		/* Check CSDPS first */
        {
	if ((t = XDPSLGetCSDPSFakeEventType(d, event)) == csdps_output
	  || t == csdps_output_with_len)
	    return True;
	else
	    return False;
        }

    return event->type == c->first_event + PSEVENTOUTPUT;
}

Bool XDPSIsDPSEvent(
    XEvent *event)
{
    Display *d = event->xany.display;
    XExtCodes *c = XDPSLGetCodes(d);

    if (c == NULL) return False;	/* Not inited on that display;
					   must be False */

    if (!c->first_event)		/* Check CSDPS first */
        {
	if (XDPSLGetCSDPSFakeEventType(d, event) != csdps_not)
	    return True;
	else
	    return False;
	}

    return event->type >= c->first_event &&
	   event->type <  c->first_event + NPSEVENTS;
}

Bool XDPSDispatchEvent(
    XEvent *event)
{
    Display *d = event->xany.display;
    XExtCodes *c = XDPSLGetCodes(d);
    CSDPSFakeEventTypes t;

    if (c == NULL) return False;	/* Not inited on that display;
					   must be False */

    if (!c->first_event)		/* Check CSDPS first */
        {
	if ((t = XDPSLGetCSDPSFakeEventType(d, event)) != csdps_not) 
	    return(XDPSLDispatchCSDPSFakeEvent(d, event, t));
	else
	    return False;
        }

    if (event->type == c->first_event + PSEVENTSTATUS) {
	XDPSLCallStatusEventHandler(d, event);
    } else if (event->type == c->first_event + PSEVENTOUTPUT) {
	XDPSLCallOutputEventHandler(d, event);
    } else if (event->type == c->first_event + PSEVENTREADY) {
	XDPSLCallReadyEventHandler(d, event);
    } else return False;
    return True;
}

/* L2-DPS/PROTO 9 addition */
Bool XDPSIsReadyEvent(
    XEvent *event,
    DPSContext *ctxt,
    int *val)
{
    Display *d = event->xany.display;
    XExtCodes *c = XDPSLGetCodes(d);
    XDPSLReadyEvent *re = (XDPSLReadyEvent *) event;

    if (c == NULL) return False;        /* Not inited on that display;
                                           must be False */

    if (!c->first_event)		/* Check CSDPS first */
        {
	if (XDPSLGetCSDPSFakeEventType(d, event) == csdps_ready)
	    {
	    XDPSLGetCSDPSReady(d, event, (void **)ctxt, val);
	    return True;
	    }
	else
	    return False;
	}

    if (event->type != c->first_event + PSEVENTREADY) return False;

    if (ctxt != NULL) *ctxt = XDPSContextFromXID(d, re->cxid);
    if (val != NULL) {
        val[0] = re->val[0];
        val[1] = re->val[1];
        val[2] = re->val[2];
        val[4] = re->val[3];
    }
    return True;
}

int XDPSGetProtocolVersion(
    Display *dpy)
{
    return XDPSLGetVersion(dpy);
}
