dpsclient.c   [plain text]


/*
 * dpsclient.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: xc/lib/dps/dpsclient.c,v 1.3 2000/09/26 15:56:59 tsi Exp $ */

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>

#include "dpsXpriv.h"
#include "DPS/dpsclient.h"
#include "dpsprivate.h"
#include "dpsdict.h"
#include "DPS/dpsexcept.h"
#include "dpsassert.h"

#ifdef XDPS
#include "dpsXint.h"
#endif /* XDPS */

#if defined(SVR4) || defined(SYSV) || defined(SystemV)
#define os_bcopy(f,t,c) memcpy(t,f,c)
#else
#define os_bcopy(f,t,c) bcopy(f,t,c)
#endif

#if !IEEEFLOAT
extern void IEEEHighToNative(/* FloatRep *from, real *to */);
/* Converts from IEEE high-byte-first real to native real. */

extern void NativeToIEEEHigh(/* real *from, FloatRep *to */);
/* Converts from native real to IEEE high-byte-first real. */

extern void IEEELowToNative(/* FloatRep *from, real *to */);
/* Converts from IEEE low-byte-first real to native real. */

extern void NativeToIEEELow(/* real *from, FloatRep *to */);
/* Converts from native real to IEEE low-byte-first real. */
#endif /* !IEEEFLOAT */

typedef union { /* 32 bit number representations */
  unsigned int u;
  int i;
  float f;
  unsigned char bytes[4];
    /* raw bytes, in whatever order they appear in memory */
  } Swap32Rec;

typedef union { /* 16 bit number representations */
  unsigned short u;
  short i;
  unsigned char bytes[2];
  } Swap16Rec;
 
#if SWAPBITS
#define Copy4SrcLo(src, dst)\
  (((Swap32Rec *) dst)->bytes[0] = ((Swap32Rec *) src)->bytes[0], \
   ((Swap32Rec *) dst)->bytes[1] = ((Swap32Rec *) src)->bytes[1], \
   ((Swap32Rec *) dst)->bytes[2] = ((Swap32Rec *) src)->bytes[2], \
   ((Swap32Rec *) dst)->bytes[3] = ((Swap32Rec *) src)->bytes[3] )
#define Copy4SrcHi(src, dst)\
  (((Swap32Rec *) dst)->bytes[0] = ((Swap32Rec *) src)->bytes[3], \
   ((Swap32Rec *) dst)->bytes[1] = ((Swap32Rec *) src)->bytes[2], \
   ((Swap32Rec *) dst)->bytes[2] = ((Swap32Rec *) src)->bytes[1], \
   ((Swap32Rec *) dst)->bytes[3] = ((Swap32Rec *) src)->bytes[0] )
#define Copy2SrcLo(src, dst)\
  (((Swap16Rec *) dst)->bytes[0] = ((Swap16Rec *) src)->bytes[0], \
   ((Swap16Rec *) dst)->bytes[1] = ((Swap16Rec *) src)->bytes[1] )
#define Copy2SrcHi(src, dst)\
  (((Swap16Rec *) dst)->bytes[0] = ((Swap16Rec *) src)->bytes[1], \
   ((Swap16Rec *) dst)->bytes[1] = ((Swap16Rec *) src)->bytes[0] )
#else /* SWAPBITS */
#define Copy4SrcHi(src, dst)\
  (((Swap32Rec *) dst)->bytes[0] = ((Swap32Rec *) src)->bytes[0], \
   ((Swap32Rec *) dst)->bytes[1] = ((Swap32Rec *) src)->bytes[1], \
   ((Swap32Rec *) dst)->bytes[2] = ((Swap32Rec *) src)->bytes[2], \
   ((Swap32Rec *) dst)->bytes[3] = ((Swap32Rec *) src)->bytes[3] )
#define Copy4SrcLo(src, dst)\
  (((Swap32Rec *) dst)->bytes[0] = ((Swap32Rec *) src)->bytes[3], \
   ((Swap32Rec *) dst)->bytes[1] = ((Swap32Rec *) src)->bytes[2], \
   ((Swap32Rec *) dst)->bytes[2] = ((Swap32Rec *) src)->bytes[1], \
   ((Swap32Rec *) dst)->bytes[3] = ((Swap32Rec *) src)->bytes[0] )
#define Copy2SrcHi(src, dst)\
  (((Swap16Rec *) dst)->bytes[0] = ((Swap16Rec *) src)->bytes[0], \
   ((Swap16Rec *) dst)->bytes[1] = ((Swap16Rec *) src)->bytes[1] )
#define Copy2SrcLo(src, dst)\
  (((Swap16Rec *) dst)->bytes[0] = ((Swap16Rec *) src)->bytes[1], \
   ((Swap16Rec *) dst)->bytes[1] = ((Swap16Rec *) src)->bytes[0] )
#endif /* SWAPBITS */
 

#define DPS_ERR_TAG 250

#define CONTEXTBUFFERSIZE 256
#define MAXQUEUEDBUFFERS 5

DPSContext DPSGlobalContext;
  
Globals DPSglobals = NULL;

char *DPScalloc(integer e, integer n)
{
  char *p;
  while (!(p = (char *)calloc(e, n))) {
    DPSOutOfMemory();
    }
  return p;
}



void DPSSafeSetLastNameIndex(DPSContext ctxt)
{
  /* we're about to call the error handler, so roll back the
     lastNameIndex to the last known valid index */
  DPSCheckInitClientGlobals();
  if (ctxt != dummyCtx && ctxt->space != NIL)
    ((DPSPrivContext)ctxt)->lastNameIndex = ((DPSPrivSpace)(ctxt->space))->lastNameIndex;
}

void DPSCheckInitClientGlobals(void)
{
  if (!DPSglobals) {
    DPSglobals = (Globals)DPScalloc(sizeof(GlobalsRec), 1);
    globLastNameIndex = -1;
    }
}

/**************************************************************/
/* Procedures that support the DPSCreateContext context procs */
/**************************************************************/

/* ARGSUSED */
static void ReleaseInput(
  char *unused, char *buffer)
{
  ContextBuffer cb = (ContextBuffer)buffer;
  if (cb == NIL) return;
  cb->next = contextBuffers;
  contextBuffers = cb;
  queuedBuffers--;
}

/* ARGSUSED */
static void StuffResultVal(
  DPSContext ctxt,
  DPSResults result,
  integer tag,
  DPSBinObj obj)
{

  integer type = obj->attributedType & ~DPS_EXEC;
  integer nObjs = 1;

  /* check if array */
  if (type == DPS_ARRAY) {
    nObjs = obj->length;
    if (nObjs < 1) return;
    if (result->count == -1 && nObjs != 1) {
      DPSSafeSetLastNameIndex(ctxt);
      if (ctxt->errorProc != NIL)
	(*ctxt->errorProc)(ctxt, dps_err_resultTypeCheck, (long unsigned)obj, 0);
      return;
      }
    obj = (DPSBinObj) ((char *)obj + obj->val.arrayVal);
    type = obj->attributedType & ~DPS_EXEC;
    }

  do {
    integer bump = 0;
    if (result->count == 0) return;
    switch (result->type) {
      case dps_tBoolean:
	if (type == DPS_BOOL) {
	  int *b = (int *) result->value;
	  *b = (int) obj->val.booleanVal;
	  bump = sizeof(int);
	  }
	break;
      case dps_tFloat: {
	float *f = (float *) result->value;
	if (type == DPS_REAL) {
	  *f = obj->val.realVal;
	  bump = sizeof(float);
	  }
	else if (type == DPS_INT) {
	  *f = (float) obj->val.integerVal;
	  bump = sizeof(float);
	  }
	break;
	}
      case dps_tDouble: {
	double *d = (double *) result->value;
	if (type == DPS_REAL) {
	  *d = (double) obj->val.realVal;
	  bump = sizeof(double);
	  }
	else if (type == DPS_INT) {
	  *d = (double) obj->val.integerVal;
	  bump = sizeof(double);
	  }
	break;
	}
      case dps_tShort:
	if (type == DPS_INT) {
	  short *s = (short *) result->value;
	  *s = (short) obj->val.integerVal;
	  bump = sizeof(short);
	  }
	break;
      case dps_tUShort:
	if (type == DPS_INT) {
	  unsigned short *us = (unsigned short *) result->value;
	  *us = (unsigned short) obj->val.integerVal;
	  bump = sizeof(unsigned short);
	  }
	break;
      case dps_tInt:
	if (type == DPS_INT) {
	  int *i = (int *) result->value;
	  *i = (int) obj->val.integerVal;
	  bump = sizeof(int);
	  }
	break;
      case dps_tUInt:
	if (type == DPS_INT) {
	  unsigned int *ui = (unsigned int *) result->value;
	  *ui = (unsigned int) obj->val.integerVal;
	  bump = sizeof(unsigned int);
	  }
	break;
      case dps_tLong:
	if (type == DPS_INT) {
	  long int *li = (long int *) result->value;
	  *li = obj->val.integerVal;
	  bump = sizeof(long int);
	  }
	break;
      case dps_tULong:
	if (type == DPS_INT) {
	  unsigned long *u = (unsigned long *) result->value;
	  *u = (unsigned long) obj->val.integerVal;
	  bump = sizeof(unsigned long);
	  }
	break;
      case dps_tChar:
      case dps_tUChar:
	if (nObjs != 1) {
	  DPSSafeSetLastNameIndex(ctxt);
	  if (ctxt->errorProc != NIL)
	    (*ctxt->errorProc)(ctxt, dps_err_resultTypeCheck, (long unsigned)obj, 0);
	  }
	else if (type == DPS_STRING) {
	  if (result->count == -1) {
	    /* char * result, copy first, ignore subsequent, null terminate */
	    os_bcopy(((integer)(obj->val.stringVal)) + (char *)obj,
		     result->value, obj->length);
	    (result->value)[obj->length] = '\0';
	    result->count = 0;
	    }
	  else {
	    unsigned slen;
	    if (result->count >= (int) obj->length) {
	      /* copy entire string into char array */
	      slen = obj->length;
	      }
	    else if (result->count > 0) {
	      /* copy partial string into char array */
	      slen = result->count;
	      }
	    else return;  /* ignore string result, no room left */
	    os_bcopy(((integer)(obj->val.stringVal)) + (char *)obj,
		     result->value, slen);
	    result->value += slen;
	    result->count -= slen;
	    }
	  return;
	  }
	break;

      default:
	DPSSafeSetLastNameIndex(ctxt);
	if (ctxt->errorProc != NIL)
	  (*ctxt->errorProc)(ctxt, dps_err_resultTypeCheck, (long unsigned)obj, 0);
      } /* switch (result->type) */

    if (bump == 0) {
      DPSSafeSetLastNameIndex(ctxt);
      if (ctxt->errorProc != NIL)
	(*ctxt->errorProc)(ctxt, dps_err_resultTypeCheck, (long unsigned)obj, 0);
      return;
      }
    if (result->count != -1) {
      result->count--;
      result->value += bump;
      }
    obj += 1;
    nObjs--;
    } while (nObjs > 0); /* do */

} /* StuffResultVal */


static void NumFormatFromTokenType(
  unsigned char t,
  DPSNumFormat *numFormat)
{
  switch (t) {
    case DPS_HI_IEEE:
      *numFormat = dps_ieee;
      break;
    case DPS_LO_IEEE:
      *numFormat = dps_ieee;
      break;
    case DPS_HI_NATIVE:
      *numFormat = dps_native;
      break;
    case DPS_LO_NATIVE:
      *numFormat = dps_native;
      break;
    default: DPSCantHappen();
    }
}
 

#if !IEEEFLOAT
/* called to deal with results from the server */
static void ConvSeqInPlace(nObjs, currObj, base, tokenType)
  integer nObjs;
  DPSBinObj currObj;
  char *base;
  unsigned char tokenType;
{
  DPSNumFormat numFormat;
  
  NumFormatFromTokenType(tokenType, &numFormat);
  
  while (nObjs--) {
    unsigned char t = currObj->attributedType & 0x07f;
    integer i;
    switch (t) {
      case DPS_REAL: {
	float f;
	if (numFormat == dps_ieee) {
	  if (DPSDefaultByteOrder == dps_hiFirst)
	    IEEEHighToNative(&currObj->val.realVal, &f);
	  else
	    IEEELowToNative(&currObj->val.realVal, &f);
	  }
	else break; /* switch */
	currObj->val.realVal = f;
	break;
	}
      case DPS_ARRAY:
	if (currObj->length > 0)
	  ConvSeqInPlace(currObj->length, (DPSBinObj)(base + currObj->val.arrayVal), base, tokenType);
	break;
      case DPS_NAME:
      case DPS_STRING:
	break;
      default:;
      } /* end switch */
    ++currObj;
    } /* end while */
}
#endif /* !IEEEFLOAT */
  
boolean DPSKnownContext(DPSContext ctxt)
{
  DPSPrivContext cc, c = (DPSPrivContext) ctxt;
  DPSPrivSpace ss;
  for (ss = spaces; ss != NIL; ss = ss->next)
    for (cc = ss->firstContext; cc != NIL; cc = cc->next)
      if (cc == c) return true;
  return false;
}

boolean DPSKnownSpace(DPSSpace space)
{
  DPSPrivSpace ss, s = (DPSPrivSpace) space;
  for (ss = spaces; ss != NIL; ss = ss->next)
    if (ss == s) return true;
  return false;
}

void DPSclientPrintProc (
  DPSContext ctxt,
  char	     *buf,
  unsigned    nch)
{
  DPSPrivContext cc = (DPSPrivContext) ctxt;

#define DPS_SEQ_MIN 2

  DPSCheckInitClientGlobals();
  if (cc == NIL) cc = (DPSPrivContext)dummyCtx;
  if (cc == NIL) return;
  
  if (nch == 0) { /* this is an EOF */
    DPSAssertWarn(buf == NIL, cc, "non-nil output buffer with 0 length");
    cc->eofReceived = true;
    if (cc->objBuf) {
      /* we were buffering; drop buffered chars on the floor */
      free(cc->objBuf);
      cc->objBuf = NIL;
      cc->nObjBufChars = 0;
      }
    }
  while (nch > 0) {
    char *oldBuf = NIL;
    unsigned oldNch = 0;
    unsigned n;
    if (cc->objBuf) { /* we're buffering */
      unsigned long int m;
      char *b = cc->objBuf + cc->nObjBufChars;
      integer minSize;
      while (cc->nObjBufChars < DPS_SEQ_MIN) {
	if (nch == 0) return;
	*b++ = *buf++;
	++cc->nObjBufChars;
	--nch;
	}
      b = cc->objBuf;
      minSize = (*(b+1) == 0) ? DPS_EXT_HEADER_SIZE : DPS_HEADER_SIZE;
      if (cc->nObjBufChars < minSize) {
	if (nch + cc->nObjBufChars < (unsigned) minSize) {
	  os_bcopy(buf, b + cc->nObjBufChars, nch);
	  cc->nObjBufChars += nch;
	  return;
	  }
	else {
	  os_bcopy(buf, b + cc->nObjBufChars, minSize - cc->nObjBufChars);
	  buf += minSize - cc->nObjBufChars;
	  nch -= minSize - cc->nObjBufChars;
	  cc->nObjBufChars = minSize;
	  }
	}
      
      if (minSize == DPS_HEADER_SIZE) {
	unsigned short *sizeP = (unsigned short *)(cc->objBuf+2);
	m = *sizeP;
	}
      else {
	unsigned long *extSizeP = (unsigned long *)(cc->objBuf+4);
	m = *extSizeP;
	}

      /* here with m = BOS total length in bytes, b = cc->objBuf */
      cc->objBuf = (char *)realloc(b, m);

      if (nch + cc->nObjBufChars < m) {
	os_bcopy(buf, cc->objBuf + cc->nObjBufChars, nch);
	cc->nObjBufChars += nch;
	return;
	}
      else {
	os_bcopy(buf, cc->objBuf + cc->nObjBufChars, m - cc->nObjBufChars);
	buf += m - cc->nObjBufChars;
	nch -= m - cc->nObjBufChars;
	cc->nObjBufChars = m;
	}
      /* we're here only if cc->objBuf contains a complete BOS */
      oldBuf = buf;
      oldNch = nch;
      buf = cc->objBuf;
      nch = cc->nObjBufChars;
      cc->objBuf = NIL;
      cc->nObjBufChars = 0;
      } /* if we're buffering */

    /* dispose of any plain text.  If no binary conversion, all output
       is plain text */
    if (cc->contextFlags & DPS_FLAG_NO_BINARY_CONVERSION) n = nch;
    else {
	for (n = 0; n < nch &&
	  ((unsigned char) buf[n] < 128 || (unsigned char) buf[n] > 159); n++);
    }
    if ((n > 0) && (cc->textProc != NIL)) {
	(*cc->textProc)((DPSContext)cc, buf, n);
    }
    buf += n;
    nch -= n;

    if (nch != 0) {
      /* here with the next binary object sequence from a server */
      DPSExtendedBinObjSeq bos;
      DPSExtendedBinObjSeqRec bosRec;
      DPSBinObj firstObj;
      unsigned t;
      unsigned long int m;
      unsigned minSize;
      
      if (nch < DPS_SEQ_MIN) {
	/* gotta buffer it */
	DPSAssertWarn(nch == 1 && !oldBuf, cc, "illegal binary output from context (oldBuf)");
	cc->objBuf = (char *)DPScalloc(DPS_EXT_HEADER_SIZE, 1);
	cc->nObjBufChars = nch;
	*cc->objBuf = *buf;
	return;
	}
      /* check for quadbyte alignment */
      if ((long int)buf & (MIN_POINTER_ALIGN - 1)) {
        /* not aligned, we gotta copy the buffer */
        /* we assert that we can't have an oldBuf if we're not aligned,
           since we only get an oldBuf if we copied to a new buffer,
           and we have already tested nch to be at least DPS_SEQ_MIN */
        DPSAssertWarn(!oldBuf && nch > 1, cc, "return values garbled (oldBuf||nch<DPS_SEQ_MIN");
        /* copy DPS_SEQ_MIN bytes, so we can use existing buffering code */
        cc->objBuf = (char *)DPScalloc(DPS_EXT_HEADER_SIZE, 1);
        cc->nObjBufChars = DPS_SEQ_MIN;
        os_bcopy(buf, cc->objBuf, cc->nObjBufChars);
        buf += DPS_SEQ_MIN;
        nch -= DPS_SEQ_MIN;
        /* now go to top of loop and go through the buffer update code */
        continue;
        }
      bos = (DPSExtendedBinObjSeq) buf;
      t = bos->tokenType;
      minSize = (bos->escape == 0) ? DPS_EXT_HEADER_SIZE : DPS_HEADER_SIZE;
      if (nch < minSize) {
	/* gotta buffer it */
	char *tb;
	DPSAssertWarn(!oldBuf, cc, "return values garbled (oldBuf)");
	tb = cc->objBuf = (char *)DPScalloc(minSize, 1);
	cc->nObjBufChars = nch;
	while (nch--) *tb++ = *buf++;
	return;
	}
      else if (minSize == DPS_HEADER_SIZE) {
	/* this is not an extended BOS */
	DPSBinObjSeq seqHead = (DPSBinObjSeq) buf;
	bos = &bosRec;
	bos->tokenType = t;
	bos->nTopElements = seqHead->nTopElements;
	bos->length = seqHead->length;
	firstObj = &(seqHead->objects[0]);
	}
      else firstObj = &(bos->objects[0]);
      m = bos->length;
      if (nch < m) {
	/* gotta buffer it */
	DPSAssertWarn(!oldBuf, cc, "return values garbled (oldBuf&&nch<m");
	cc->objBuf = (char *)DPScalloc(bos->length, 1);
	cc->nObjBufChars = nch;
	os_bcopy(buf, cc->objBuf, nch);
	return;
	}
      DPSAssertWarn(bos->nTopElements == 1, cc, "illegal binary output detected (bos->nTopElements!=1)");
#if !IEEEFLOAT
      if (t != DPS_DEF_TOKENTYPE) 
	ConvSeqInPlace(1, firstObj, firstObj, bos->tokenType);
#endif /* !IEEEFLOAT */
      t = firstObj->tag;
      if (t == DPS_ERR_TAG) {
	cc->resultTable = NIL;
	DPSSafeSetLastNameIndex((DPSContext)cc);
	DURING
	  if (cc->errorProc != NIL)
	    (*cc->errorProc)((DPSContext)cc, dps_err_ps, (unsigned long)buf, m);
	HANDLER
	  if (oldBuf) free(buf);
	  RERAISE;
	END_HANDLER
	}
      else { /* dispatch this result */
	if (!cc->resultTable || t > cc->resultTableLength) {
	  if (cc->chainParent == NIL && cc->errorProc != NIL) {
	    DPSSafeSetLastNameIndex((DPSContext)cc);
	    (*cc->errorProc)((DPSContext)cc, dps_err_resultTagCheck, (unsigned long)buf, m);
	    }
	  }
	else if (t == cc->resultTableLength) {
	  cc->resultTable = NIL;
	  }
	else {
	  StuffResultVal((DPSContext)cc, &cc->resultTable[t], t, firstObj);
	  }
	}
      if (!oldBuf)
	buf += m;
      nch -= m;
      } /* if (nch != 0) ... the next binary object sequence from a server */
    if (oldBuf) {
      DPSAssertWarn(nch == 0, cc, "some return values/data lost (nch)");
      free(buf);
      buf = oldBuf;
      nch = oldNch;
      }
    } /* while (nch > 0) */

} /* DPSclientPrintProc */

/**************************************/
/* Context procs for DPSCreateContext */
/**************************************/


static void procWaitContext(DPSContext ctxt)
{
  typedef struct {
    unsigned char tokenType;
    unsigned char topLevelCount;
    unsigned short int nBytes;

    DPSBinObjGeneric obj0;
    DPSBinObjGeneric obj1;
    DPSBinObjGeneric obj2;
    DPSBinObjGeneric obj3;
    } DPSQ;
  static DPSQ dpsF = {
    DPS_DEF_TOKENTYPE, 4, sizeof(DPSQ),
    {DPS_LITERAL|DPS_INT, 0, 0, -23},	/* arbitrary int */
    {DPS_LITERAL|DPS_INT, 0, 0, 0},	/* termination tag = 0 */
    {DPS_EXEC|DPS_NAME, 0, DPSSYSNAME, 119},	/* printobject */
    {DPS_EXEC|DPS_NAME, 0, DPSSYSNAME, 70},	/* flush */
    }; /* DPSQ */
  DPSResultsRec DPSR;

  if (DPSPrivateCheckWait(ctxt)) return;

  ctxt->resultTable = &DPSR;	    /* must be non-null for handler to work */
  ctxt->resultTableLength = 0;  /* same value as termination tag */
  DPSBinObjSeqWrite(ctxt, (char *) &dpsF,sizeof(DPSQ));
  DPSAwaitReturnValues(ctxt);
}

static void procUpdateNameMap(DPSContext ctxt)
{
  integer i;
  DPSPrivContext c = (DPSPrivContext) ctxt;
  DPSPrivSpace s = (DPSPrivSpace) ctxt->space;
  DPSContext children = ctxt->chainChild;

  /* unlink context from chain temporarily, so DPSPrintf can be called */
  if (children != NIL) ctxt->chainChild = NIL;
  DURING
    for (i = s->lastNameIndex+1; i <= globLastNameIndex; i++)
      DPSPrintf(ctxt, "%d /%s defineusername\n", i, userNames[i]);
  HANDLER
    if (children != NIL) ctxt->chainChild = children;
    RERAISE;
  END_HANDLER
  c->lastNameIndex = globLastNameIndex;
  if (children != NIL) {
    /* update any children */
    ctxt->chainChild = children;
    DPSUpdateNameMap(ctxt->chainChild);
  }
}

static void procWriteData(
  DPSContext ctxt,
  char *buf,
  unsigned int count)
{
  /* safe to call with chain */
  DPSinnerProcWriteData(ctxt, buf, count);
  if (ctxt->chainChild != NIL) DPSWriteData(ctxt->chainChild, buf, count);
}

static void procBinObjSeqWrite(
  DPSContext ctxt,
  char *buf,
  unsigned int count)
{
  if (((DPSPrivContext)ctxt)->lastNameIndex < globLastNameIndex) DPSUpdateNameMap(ctxt);
  DPSinnerProcWriteData(ctxt, buf, count);
  if (ctxt->chainChild != NIL) DPSBinObjSeqWrite(ctxt->chainChild, buf, count);
}

static void procWriteStringChars(
  DPSContext ctxt,
  char *buf,
  unsigned int count)
{
  DPSinnerProcWriteData(ctxt, buf, count);
  if (ctxt->chainChild != NIL) DPSWriteStringChars(ctxt->chainChild, buf, count);
}

static void procWritePostScript(
  DPSContext ctxt,
  char *buf,
  unsigned int count)
{
  DPSinnerProcWriteData(ctxt, buf, count);
  if (ctxt->chainChild != NIL) DPSWritePostScript(ctxt->chainChild, buf, count);
}

static void innerProcWriteNumstring(
    DPSContext ctxt,
    DPSDefinedType type,
    char *data,
    unsigned int size,
    int scale,
    void (*writeProc)(DPSContext, char *, unsigned))
{
    unsigned char HNumHeader[4];
    register int i;
#define NBUFSIZE 10
    int ibuf[NBUFSIZE];		/* This needs to be a 32 bit datatype */

    HNumHeader[0] = 149;
    switch (type) {
	case dps_tLong:
	    HNumHeader[1] = (DPS_DEF_TOKENTYPE % 2) * 128 + scale;
	    break;

	case dps_tInt:
	    HNumHeader[1] = ((sizeof(int) >= 4) ? 0 : 32)
                + ((DPS_DEF_TOKENTYPE % 2) * 128) + scale;
	    break;

	case dps_tShort:
	    HNumHeader[1] = 32 + ((DPS_DEF_TOKENTYPE % 2) * 128) + scale;
	    break;

	case dps_tFloat:
	    HNumHeader[1] = 48 + ((DPS_DEF_TOKENTYPE % 2) * 128)
                + ((DPS_DEF_TOKENTYPE >= 130) ? 1 : 0);
	    break;

	default:
	    break;
    }

    HNumHeader[(DPS_DEF_TOKENTYPE % 2) ? 2 : 3] = (unsigned char) size;
    HNumHeader[(DPS_DEF_TOKENTYPE % 2) ? 3 : 2] = (unsigned char) (size >> 8);

    (*writeProc)(ctxt, (char *)HNumHeader, 4);

    switch (type) {
	case dps_tLong:
	    if (sizeof(long) == 4) {
		(*writeProc)(ctxt, (char *) data, size * 4);
	    } else {
		while (size > 0) {
		    for (i = 0; i < NBUFSIZE && (unsigned) i < size; i++) {
			ibuf[i] = ((long *) data)[i];
		    }
		    (*writeProc)(ctxt, (char *) ibuf,
				 4 * (size < NBUFSIZE ? size : NBUFSIZE));
		    size -= NBUFSIZE;
		}
	    }
	    break;

	case dps_tInt:
	    (*writeProc)(ctxt, (char *) data, size * sizeof(int));
	    break;

	case dps_tShort:
	    (*writeProc)(ctxt, (char *) data, size * sizeof(short));
	    break;

	case dps_tFloat:
	    (*writeProc)(ctxt, (char *) data, size * sizeof(float));

	default:
	    break;
    }
} /* innerProcWriteNumstring */

static void procWriteNumstring(
    DPSContext ctxt,
    DPSDefinedType type,
    char *data,
    unsigned int size,
    int scale)
{
  innerProcWriteNumstring(ctxt, type, data, size, scale, DPSinnerProcWriteData);
  if (ctxt->chainChild != NIL) DPSWriteNumString(ctxt->chainChild, type, data, size, scale);
}

static void writeTypedObjectArray(
  DPSContext ctxt,
  DPSDefinedType type,
  char *array,
  unsigned int length)
{

#define DPSMAX_SEQ 10		 
  unsigned int i;
  DPSPrivContext c = (DPSPrivContext)(ctxt);
  static DPSBinObjGeneric bboolObj[DPSMAX_SEQ] = {
	{DPS_LITERAL | DPS_BOOL, 0,0,0},
	{DPS_LITERAL | DPS_BOOL, 0,0,0},
	{DPS_LITERAL | DPS_BOOL, 0,0,0},
	{DPS_LITERAL | DPS_BOOL, 0,0,0},
	{DPS_LITERAL | DPS_BOOL, 0,0,0},
	{DPS_LITERAL | DPS_BOOL, 0,0,0},
	{DPS_LITERAL | DPS_BOOL, 0,0,0},
	{DPS_LITERAL | DPS_BOOL, 0,0,0},
	{DPS_LITERAL | DPS_BOOL, 0,0,0},
	{DPS_LITERAL | DPS_BOOL, 0,0,0},
	};
  static DPSBinObjReal rrealObj[DPSMAX_SEQ] = {
	{DPS_LITERAL | DPS_REAL, 0,0,0},
	{DPS_LITERAL | DPS_REAL, 0,0,0},
	{DPS_LITERAL | DPS_REAL, 0,0,0},
	{DPS_LITERAL | DPS_REAL, 0,0,0},
	{DPS_LITERAL | DPS_REAL, 0,0,0},
	{DPS_LITERAL | DPS_REAL, 0,0,0},
	{DPS_LITERAL | DPS_REAL, 0,0,0},
	{DPS_LITERAL | DPS_REAL, 0,0,0},
	{DPS_LITERAL | DPS_REAL, 0,0,0},
	{DPS_LITERAL | DPS_REAL, 0,0,0},
	};
  static DPSBinObjGeneric iintObj[DPSMAX_SEQ] = {
	{DPS_LITERAL | DPS_INT, 0,0,0},
	{DPS_LITERAL | DPS_INT, 0,0,0},
	{DPS_LITERAL | DPS_INT, 0,0,0},
	{DPS_LITERAL | DPS_INT, 0,0,0},
	{DPS_LITERAL | DPS_INT, 0,0,0},
	{DPS_LITERAL | DPS_INT, 0,0,0},
	{DPS_LITERAL | DPS_INT, 0,0,0},
	{DPS_LITERAL | DPS_INT, 0,0,0},
	{DPS_LITERAL | DPS_INT, 0,0,0},
	{DPS_LITERAL | DPS_INT, 0,0,0},
	};
    
  if (DPSCheckShared(c)) return;

  switch (type) {
    case dps_tChar:
    case dps_tUChar:
      DPSCantHappen();
      break;

    case dps_tBoolean: {
      while (length > 0) {
	for (i = 0; i < MIN(DPSMAX_SEQ, length); i++) {
	  bboolObj[i].val = *((int *)array);
	  array += sizeof(int);
	  }
	DPSWritePostScript(ctxt, (char *) bboolObj, sizeof(DPSBinObjGeneric) * i);
	length -= i;
	}
      break;
      }

    case dps_tFloat: {
      while (length > 0) {
	for (i = 0; i < MIN(DPSMAX_SEQ, length); i++) {
	  rrealObj[i].realVal = *((float *)array);
	  array += sizeof(float);
	  }
	DPSWritePostScript(ctxt, (char *) rrealObj, sizeof(DPSBinObjReal) * i);
	length -= i;
	}
      break;
      }

    case dps_tDouble: {
      while (length > 0) {
	for (i = 0; i < MIN(DPSMAX_SEQ, length); i++) {
	  rrealObj[i].realVal = *((double *)array);
	  array += sizeof(double);
	  }
	DPSWritePostScript(ctxt, (char *) rrealObj, sizeof(DPSBinObjReal) * i);
	length -= i;
	}
      break;
      }

    case dps_tShort: {
      while (length > 0) {
	for (i = 0; i < MIN(DPSMAX_SEQ, length); i++) {
	  iintObj[i].val = *((short *)array);
	  array += sizeof(short);
	  }
	DPSWritePostScript(ctxt, (char *) iintObj, sizeof(DPSBinObjGeneric) * i);
	length -= i;
	}
      break;
      }

    case dps_tUShort: {
      while (length > 0) {
	for (i = 0; i < MIN(DPSMAX_SEQ, length); i++) {
	  iintObj[i].val = *((unsigned short *)array);
	  array += sizeof(unsigned short);
	  }
	DPSWritePostScript(ctxt, (char *) iintObj, sizeof(DPSBinObjGeneric) * i);
	length -= i;
	}
      break;
      }

    case dps_tInt: {
      while (length > 0) {
	for (i = 0; i < MIN(DPSMAX_SEQ, length); i++) {
	  iintObj[i].val = *((int *)array);
	  array += sizeof(int);
	  }
	DPSWritePostScript(ctxt, (char *) iintObj, sizeof(DPSBinObjGeneric) * i);
	length -= i;
	}
      break;
      }

    case dps_tUInt: {
      while (length > 0) {
	for (i = 0; i < MIN(DPSMAX_SEQ, length); i++) {
	  iintObj[i].val = *((unsigned int *)array);
	  array += sizeof(unsigned int);
	  }
	DPSWritePostScript(ctxt, (char *) iintObj, sizeof(DPSBinObjGeneric) * i);
	length -= i;
	}
      break;
      }

    case dps_tLong: {
      while (length > 0) {
	for (i = 0; i < MIN(DPSMAX_SEQ, length); i++) {
	  iintObj[i].val = *((long *)array);
	  array += sizeof(long);
	  }
	DPSWritePostScript(ctxt, (char *) iintObj, sizeof(DPSBinObjGeneric) * i);
	length -= i;
	}
      break;
      }

    case dps_tULong: {
      while (length > 0) {
	for (i = 0; i < MIN(DPSMAX_SEQ, length); i++) {
	  iintObj[i].val = *((unsigned long *)array);
	  array += sizeof(unsigned long);
	  }
	DPSWritePostScript(ctxt, (char *) iintObj, sizeof(DPSBinObjGeneric) * i);
	length -= i;
	}
      break;


      }

    default:;
    }
} /* writeTypedObjectArray */

static void procDestroyContext(DPSContext ctxt)
{
  DPSPrivContext c = (DPSPrivContext)ctxt, cc, prev;
  DPSPrivSpace ss = (DPSPrivSpace)(c->space);

  if (c->buf != NIL) {
    ContextBuffer b = (ContextBuffer)(c->buf);
    b->next = contextBuffers;
    contextBuffers = b;
    c->buf = NIL;
    }
  if (c->objBuf != NIL) {
    free(c->objBuf);
    c->objBuf = NIL;
    }

  DPSUnchainContext(ctxt);

  prev = NIL;
  DPSAssert(ss != NIL);
  for (cc = ss->firstContext; (cc != NIL) && (cc != c); cc = cc->next)
    prev = cc;
  DPSAssert(cc != NIL);
  DPSAssert(cc != prev);
  if (prev == NIL) ss->firstContext = cc->next;
  else {
    prev->next = cc->next;
    DPSAssert(prev->next != prev);
    }
 
  DPSPrivateDestroyContext(ctxt);
  free(c);
}

static void procDestroySpace(DPSSpace space)
{
  DPSPrivSpace ns, prevS, ss = (DPSPrivSpace)space;
  integer sid = ss->sid;

  while (ss->firstContext != NIL)
    DPSDestroyContext((DPSContext)(ss->firstContext));
  
  prevS = NIL;
  for (ns = spaces; (ns != NIL) && (ns->sid != sid); ns = ns->next)
    prevS = ns;
  DPSAssert(ns != NIL);
  DPSAssert(ns == ss);
  if (prevS == NIL) spaces = ns->next;
  else {
    prevS->next = ns->next;
    DPSAssert(prevS->next != prevS);
    }
  
  DPSPrivateDestroySpace(space);
  free(ss);
}

static void procInterrupt(DPSContext ctxt)
{
  DPSPrivContext c = (DPSPrivContext)ctxt;
  DPSSendInterrupt((XDPSPrivContext)c->wh, c->cid, DPSclientPrintProc);
  if (ctxt->chainChild != NIL) DPSInterruptContext(ctxt->chainChild);
}


/****************************************************************/
/* Procedures that support the DPSCreateTextContext context procs */
/****************************************************************/

/* precondition: (c >= 129 && c <= 159), which means that c is the
   first byte of either a binary token or a binary object sequence */

static boolean IsBinaryToken(unsigned char c)
{
  return (c != DPS_HI_IEEE && c != DPS_LO_IEEE
	  && c != DPS_HI_NATIVE && c != DPS_LO_NATIVE);
}

/* returns the number of initial bytes of a given goody that are
   required to figure out how many bytes are needed for the entire goody */
   
static unsigned GetHdrNBytes(unsigned char *t)
{
  if (!IsBinaryToken(*t)) {
    if (*(++t) == 0) return DPS_EXT_HEADER_SIZE;
    else return DPS_HEADER_SIZE;
    }
  switch (*t) {
    case 137: 
    case 142:
      return(2);
    case 143:
    case 144:
      return(3);
    case 149:
      return(4);
    default:
      if (*t > 149 && *t < 160) return(1); /* unassigned */
      else return(1);
    }
}


static void WriteHomogeneousArrayAsASCII(
  DPSContext ctxt,
  register unsigned char *buf)
{
  Swap32Rec n32;
  Swap16Rec n16;
  register unsigned char *b;

  unsigned char r = *buf++;
  float f;
  boolean hi = (r < 128);
  
  if (!hi) r -= 128;
  DPSPrintf(ctxt, "[ ");
  b = n16.bytes;
  if (hi) {Copy2SrcHi(buf, b);} else {Copy2SrcLo(buf, b);}
  buf += 2;
  if (r < 32)		/* 32-bit fixed */
	  while (n16.u--) {
	      b = n32.bytes;
	      if (hi) {Copy4SrcHi(buf, b);} else {Copy4SrcLo(buf, b);}
	      if (r == 0)
		      DPSPrintf(ctxt, "%d ", n32.i);
	      else {
		  f = n32.i;  /* convert to float */
		  n32.f = f / (1 << r);  /* scale */
		  DPSPrintf(ctxt, "%g ", n32.f);
	      }
	      buf += 4;
	  }
  else if (r < 48) {	/* 16-bit fixed */
      unsigned l = n16.u;
      r -= 32;
      while (l--) {
	  b = n16.bytes;
	  if (hi) {Copy2SrcHi(buf, b);} else {Copy2SrcLo(buf, b);}
	  if (r == 0)
		  DPSPrintf(ctxt, "%d ", n16.i);
	  else {
	      f = n16.i; /* convert to float */
	      n32.f = f / (1 << r);  /* scale */
	      DPSPrintf(ctxt, "%g ", n32.f);
	  } 
	  buf += 2;
      }
  }
  else if (r == 48)	/* 32-bit IEEE */
	  while (n16.u--) {
#if IEEEFLOAT
	      b = n32.bytes;
	      if (hi) {Copy4SrcHi(buf, b);} else {Copy4SrcLo(buf, b);}
#else /* IEEEFLOAT */
	      if (hi) IEEEHighToNative(buf, &n32.f);
	      else IEEELowToNative(buf, &n32.f);
#endif /* IEEEFLOAT */
	      DPSPrintf(ctxt, "%g ", n32.f);
	      buf += 4;
	  }
  else if (r == 49)	  /* 32-bit native */
	  while (n16.u--) {
	      b = n32.bytes;
	      *b++ = *buf++,*b++ = *buf++,*b++ = *buf++,*b = *buf++;
	      DPSPrintf(ctxt, "%g ", n32.f);
	  }
  else DPSCantHappen();
      DPSPrintf(ctxt, "\n] ");
} /* WriteHomogeneousArrayAsASCII */

/* returns the number of bytes needed for the entire goody. buf points
   to enough initial bytes of the goody to figure this out (see above). */
   
static integer GetNBytes(register unsigned char *buf)
{
  unsigned short int nBytes;
  register unsigned char *r = (unsigned char *)&nBytes;
  
  switch (*buf) {
      case DPS_HI_IEEE:
      case DPS_HI_NATIVE:
	  if (*(buf+1) == 0) {
	    unsigned int nb;
	    r = (unsigned char *)&nb;
	    buf += 4;
	    Copy4SrcHi(buf, r);
	    return(nb);
	    }
	  else {
	    buf += 2;
	    Copy2SrcHi(buf, r);
	    }
	  break;
      case DPS_LO_IEEE:
      case DPS_LO_NATIVE:
	  if (*(buf+1) == 0) {
	    unsigned int nb;
	    r = (unsigned char *)&nb;
	    buf += 4;
	    Copy4SrcLo(buf, r);
	    return(nb);
	    }
	  else {
	    buf += 2;
	    Copy2SrcLo(buf, r);
	    }
	  break;
      case 132:
      case 133:
      case 138:
      case 139:
      case 140:
	nBytes = 5; break;
      case 134:
      case 135:
	nBytes = 3; break;
      case 136:
      case 141:
      case 145:
      case 146:
      case 147:
      case 148:
	nBytes = 2; break;
      case 137: {
	unsigned int l = *(buf+1);
	if (l < 32) {nBytes = 6; break;}
	if (l < 48) {nBytes = 4; break;}
	if (l-128 < 32) {nBytes = 6; break;}
	if (l-128 < 48) {nBytes = 4; break;}
	DPSCantHappen();
	}
      case 142:
	nBytes = *(buf+1);
	nBytes += 2;
	break;
      case 143: 
	buf++;
	Copy2SrcHi(buf, r);
	nBytes += 3;
	break;
      case 144: 
	buf++;
	Copy2SrcLo(buf, r);
	nBytes += 3;
	break;
      case 149:  {
	unsigned char scale = *(buf+1);
	buf += 2;
	if (scale < 128)
	  Copy2SrcHi(buf, r);
	else {
	  scale -= 128;
	  Copy2SrcLo(buf, r);
	  }
	if (scale < 32)
	  nBytes *= 4;
	else if (scale < 48)
	  nBytes *= 2;
	else
	  nBytes *= 4;
	nBytes += 4;
	break;
	}
      default: nBytes = 1; /* unassigned */
      }
  return(nBytes);
}
  
static void WriteSeqAsAscii(
  DPSContext ctxt,
  char *base,
  DPSBinObj currObj,
  unsigned int nObjs,
  unsigned char tokenType,
  int *numstringOffsets)
{
  integer nLineObjs = 0;
  DPSNumFormat numFormat;
  float f;
  long int i;
  unsigned short int length;
  
  /* NOTE: It's ok to call DPSPrintf (which calls DPSWritePostScript)
     from here since we are only sending text, so there's no problem
     with re-entering DPSWritePostScript.  Caller guarantees
     that this context has no children. */
  
  NumFormatFromTokenType(tokenType, &numFormat);
   
  while (nObjs--) {
    unsigned char type = currObj->attributedType & ~DPS_EXEC;
    boolean lit = ((currObj->attributedType & DPS_EXEC) == 0);

    switch (type) {
      case DPS_NULL:
	break;
      case DPS_BOOL:
	i = currObj->val.booleanVal;
	if (i)
	  DPSPrintf(ctxt, "true ");
	else
	  DPSPrintf(ctxt, "false ");
	break;
      case DPS_INT:
	i = currObj->val.integerVal;
	DPSPrintf(ctxt, "%d ", i);
	break;
      case DPS_REAL:
#if IEEEFLOAT
	f = currObj->val.realVal;
#else /* IEEEFLOAT */
	if (numFormat != DPSDefaultNumFormat)
#if SWAPBITS
	    IEEELowToNative(&currObj->val.realVal, &f);
#else /* SWAPBITS */
	    IEEEHighToNative(&currObj->val.realVal, &f);
#endif /* SWAPBITS */
	else
	  f = currObj->val.realVal;
#endif /* IEEEFLOAT */

	DPSPrintf(ctxt, "%g ", f);
	break;
      case DPS_NAME: {
	char *p = 0;
	integer index;
	
	index = currObj->val.nameVal;
	length = currObj->length;
	  
	if (lit) DPSPrintf(ctxt, "/");

	if (length == DPSSYSNAME) { /* system name index */
	    if (index <= DPS_LAST_COMMON_SYSNAME) {
	      if (!lit && (ctxt->contextFlags & DPS_FLAG_USE_ABBREVS)) {
		  p = DPSGetSysnameAbbrev(index);
		  if (p == NULL) p = DPSSysNames[index];
	      } else p = DPSSysNames[index];
	    } else if (DPS_FIRST_AUX_SYSNAME <= index
		     && index <= DPS_LAST_AUX_SYSNAME)
	      p = DPSSysNamesAux[index - DPS_FIRST_AUX_SYSNAME];
	    else DPSCantHappen();
	    length = strlen(p);
	    }
	else if (length == 0) { /* user name index */
	    p = DPSNameFromIndex(index);
	    length = strlen(p);
	    }
	else
	    p = base + index;

	DPSWriteData(ctxt, p, length);
	DPSPrintf(ctxt, " ");
	break; 
	}
      case DPS_ARRAY:
	DPSPrintf(ctxt, (lit ? "[ " : "{ "));
	WriteSeqAsAscii(
	  ctxt,
	  base,
	  (DPSBinObj)(base+currObj->val.arrayVal),
	  currObj->length,
	  tokenType, numstringOffsets);
	DPSPrintf(ctxt, (lit ? " ] " : " } "));
	break;
      case DPS_MARK:
	if (lit)
	  DPSPrintf(ctxt, "/mark ");
	else
	  DPSPrintf(ctxt, "mark ");
	break;
      case DPS_STRING: {
	char *p;
	int j;
	i = currObj->val.stringVal;
	length = currObj->length;
	p = base + i;
	if (numstringOffsets != NULL) {
	    for (j = 2; j < numstringOffsets[1] &&
		 numstringOffsets[j] != i; j++) ;
	    if (numstringOffsets[j] == i) {
		DPSAssert(*(unsigned char *) p++ == 149);
		WriteHomogeneousArrayAsASCII(ctxt, (unsigned char *) p);
		break;
	    }
	}
	DPSPrintf(ctxt, "(");
#ifdef VMS
	WriteVMSStringBody(ctxt, p, length); /* write this */
#else /* VMS */
	/* render string bytes correctly */
	while (length--) {
	  char c = *p++;
	  if (c == '(' || c == ')' || c == '\\')
	    DPSPrintf(ctxt, "\\%c", c);
	  else if (c == '\n')
	    DPSPrintf(ctxt, "\\n");
	  else if (!(isascii(c) && isprint(c)))
	    DPSPrintf(ctxt, "\\%03.3o", (unsigned char) c);
	  else DPSWriteData(ctxt, &c, 1);
	  }
#endif /* VMS */
	DPSPrintf(ctxt, ") ");
	break;
	}
      default: DPSCantHappen();
      }

    currObj++;
    if (++nLineObjs == 15) {
      nLineObjs = 0;
      DPSPrintf(ctxt, "\n ");
      }
    } /* end while */

    DPSPrintf(ctxt, "\n");

} /* WriteSeqAsAscii */

static void ConvertAndWriteSeqAsData(
  DPSContext ctxt,
  char *bosBuf,
  int pass)
{
  DPSPrivContext cc = (DPSPrivContext) ctxt;
  DPSExtendedBinObjSeq bos;
  DPSExtendedBinObjSeqRec bosRec;
  DPSNumFormat numFormat;
  unsigned int offset, firstCharOffset;
  unsigned int nameOffset;
  unsigned int len = 0;
  DPSBinObj currObj;
  integer headSize;
  char *seqBase;
  
  if (*(bosBuf+1) != 0) {
    /* not an extended BOS */
    DPSBinObjSeq seqHead = (DPSBinObjSeq) bosBuf;
    bos = &bosRec;
    bos->tokenType = seqHead->tokenType;
    bos->escape = seqHead->nTopElements;
    bos->nTopElements = seqHead->nTopElements;
    bos->length = seqHead->length;
    currObj = &(seqHead->objects[0]);
    seqBase = (char *) &(seqHead->objects[0]);
    headSize = DPS_HEADER_SIZE;
    }
  else {
    bos = (DPSExtendedBinObjSeq) bosBuf;
    currObj = &(bos->objects[0]);
    seqBase = (char *) &(bos->objects[0]);
    headSize = DPS_EXT_HEADER_SIZE;
    }
  firstCharOffset = bos->length - headSize;
  nameOffset = firstCharOffset;
  
  /* Caller guarantees that this context has no children,
     so it is okay to call DPSWriteData */
  
  NumFormatFromTokenType(bos->tokenType, &numFormat);
    
  /* Pass 0: If we're expanding name indices, find all name objects,
	     lookup their strings, sum the lengths.
	     Write the modified sequence header regardless.
     Pass 1: we're converting indices to strings and/or
	     converting numbers.  Write each modified object, and
	     subsidiary strings.
     Pass 2: Find all name objects, lookup and write the strings. */
  
  if (pass == 0 && ctxt->nameEncoding != dps_strings) 
    /* we're just converting numbers, so skip while loop */
    offset = firstCharOffset;
  else
    offset = 0;
  
  while (offset < firstCharOffset) {
    DPSBinObjRec newObj;
    unsigned char type = (currObj->attributedType & ~DPS_EXEC);
    
    newObj = *currObj;
    
#if !IEEEFLOAT
    if (type == DPS_REAL) {
	if (numFormat != cc->numFormat) {
	  if (numFormat == dps_ieee) {
	    if (DPSDefaultByteOrder == dps_loFirst)
	      IEEELowToNative(&currObj->val.realVal, &newObj.val.realVal);
	    else
	      IEEEHighToNative(&currObj->val.realVal, &newObj.val.realVal);
	    }  
	  else { /* numFormat is native */
	    if (DPSDefaultByteOrder == dps_loFirst)
	      NativeToIEEELow(&currObj->val.realVal, &newObj.val.realVal);
	    else
	      NativeToIEEEHigh(&currObj->val.realVal, &newObj.val.realVal);
	    }
	  }
      }	     
#endif /* !IEEEFLOAT */

    if (type == DPS_STRING && newObj.length > 0) {
      /* keep track of where strings start */
      firstCharOffset = ((unsigned) newObj.val.stringVal < firstCharOffset)
	? newObj.val.stringVal : firstCharOffset;
      }
    if (type == DPS_NAME) {
      if (newObj.length == DPSSYSNAME) { /* system name index, never expand to string body */
	  if (pass != 1) goto next_obj;
	  }
      else if (newObj.length == 0) { /* user name index */
	  register char *p = DPSNameFromIndex(newObj.val.nameVal);
	  switch (pass) {
	    case 0: len += strlen(p); goto next_obj;
	    case 1: 
	      if (ctxt->nameEncoding == dps_strings) {
		newObj.length = strlen(p);
		newObj.val.nameVal = nameOffset;
		nameOffset += newObj.length;
	      }
	      break;
	    case 2: DPSWriteData(ctxt, p, strlen(p)); goto next_obj;
	    default:;
	    }
	  }
      else { /* name is already a string */
	  /* keep track of where strings start */
	  firstCharOffset = ((unsigned) newObj.val.nameVal < firstCharOffset)
	    ? newObj.val.nameVal : firstCharOffset;
	}
      } /* end if type == DPS_NAME */
    if (pass == 1) {
      DPSWriteData(ctxt, (char *) &newObj, sizeof(newObj)); 
      }  

next_obj:	  
    offset += sizeof(newObj);
    ++currObj;
    } /* end while */
    
  /* finish pass */
  switch (pass) {
    case 0: {
      unsigned char t;
      /* write modified seqHead */
      if (DPSDefaultByteOrder == dps_hiFirst && cc->numFormat == dps_ieee)
	t = DPS_HI_IEEE;
      else if (DPSDefaultByteOrder == dps_loFirst && cc->numFormat == dps_ieee) 
	t = DPS_LO_IEEE;
      else if (DPSDefaultByteOrder == dps_hiFirst && cc->numFormat == dps_native)
	t = DPS_HI_NATIVE;
      else 
	t = DPS_LO_NATIVE;
      DPSWriteData(ctxt, (char *) &t, 1);
      if (headSize == DPS_HEADER_SIZE) {
	unsigned short int nBytes;
	unsigned char c = bos->nTopElements;
	/* write top level count */
	DPSWriteData(ctxt, (char *) &c, 1);
	/* write nBytes */
	nBytes = (ctxt->nameEncoding == dps_strings) ? bos->length + len :
	   bos->length;
	DPSWriteData(ctxt, (char *)&nBytes, 2);
	}
      else {
	unsigned int nBytes;
	/* write escape code & top level count */
	DPSWriteData(ctxt, (char *)&bos->escape, 3);
	/* write nBytes */
	nBytes = (ctxt->nameEncoding == dps_strings) ? bos->length + len :
	   bos->length;
	DPSWriteData(ctxt, (char *)&nBytes, 4);
	}
      break;
      }
    case 1: {
      char *stringStart = seqBase + firstCharOffset; 
      DPSWriteData(ctxt, stringStart, (bos->length - headSize - firstCharOffset));
      break;
      }
    default:;
    }
    
} /* ConvertAndWriteSeqAsData */

#define MIN16 -32768
#define MAX16 32767

/* TestHomogeneous will return a non-negative representation code 'r'
   if all of the array elements are "integers", or all are "reals".
   Will return -1 for any other case. */
   
static integer TestHomogeneous(
  DPSBinObj aryObj,
  unsigned short length,
  DPSNumFormat numFormat)
{
  integer tmp, r = -1;
  
  while (length--) {
    switch (aryObj->attributedType & ~DPS_EXEC) {
      case DPS_INT:
#if SWAPBITS
	tmp = (aryObj->val.integerVal < MIN16
	       || aryObj->val.integerVal > MAX16) ? 128 : 128+32;
#else /* SWAPBITS */
	tmp = (aryObj->val.integerVal < MIN16
	       || aryObj->val.integerVal > MAX16) ? 0 : 32;
#endif /* SWAPBITS */
	if ((r == -1) || ((r & 0x07F) == 32 && (tmp & 0x07F) == 0))
	  r = tmp; /* first element, or was 16-bit => bump to 32-bit */
	else if ((r & 0x07F) == 0 && (tmp & 0x07F) == 32)
	  goto bump_obj;  /* is 32-bit => stay 32-bit */
	else if (r != tmp)
	  return(-1);
	/* else fall thru, r == tmp */
	break;
      case DPS_REAL:
#if SWAPBITS
	tmp = (numFormat == dps_ieee) ? 128+48 : 128+49;
#else /* SWAPBITS */
	tmp = (numFormat == dps_ieee) ? 48 : 49;
#endif /* SWAPBITS */
	if (r == -1)
	  r = tmp;
	else if (r != tmp) return(-1);
	break;
      default: return(-1);
      }
bump_obj:
    ++aryObj;
    }
  return(r);
} /* TestHomogeneous */

static void WriteSeqAsTokens(
  DPSContext ctxt,
  char *base,
  DPSBinObj currObj,
  unsigned int nObjs,
  unsigned char tokenType,
  int *numstringOffsets)
{
  int nLineObjs = 0;
  DPSNumFormat numFormat;
  unsigned short length;
  Swap32Rec n32;
  Swap16Rec n16;
  unsigned char c;

#define PB(byte) c = (byte),DPSWriteData(ctxt, (char *) &c, 1)
#if SWAPBITS
#define WTT(byte) PB((byte)+1)
#else /* SWAPBITS */
#define WTT(byte) PB(byte)
#endif /* SWAPBITS */

  NumFormatFromTokenType(tokenType, &numFormat);
   
  while (nObjs--) {
    unsigned char type = currObj->attributedType & ~DPS_EXEC;
    boolean lit = ((currObj->attributedType & DPS_EXEC) == 0);

    switch (type) {
      case DPS_NULL:
	break;
      case DPS_BOOL:
	PB(141);			/* boolean */
	if (currObj->val.booleanVal)
	  PB(true);
	else
	  PB(false);
	break;
      case DPS_INT:
	n32.i = currObj->val.integerVal;
	if (n32.i < MIN16 || n32.i > MAX16) {
	  WTT(132);			/* 32-bit int */
	  DPSWriteData(ctxt, (char *) n32.bytes, 4);
	  }
	else if (n32.i < -128 || n32.i > 127) {
	  WTT(134);			/* 16-bit int */
	  n16.i = n32.i;
	  DPSWriteData(ctxt, (char *) n16.bytes, 2);
	  }
	else {
	  WTT(136);			/* 8-bit int */
	  PB(n32.i);
	  }
	break;
      case DPS_REAL:
#if IEEEFLOAT
	WTT(138);			/* 32-bit IEEE float */
#else /* IEEEFLOAT */
	if (numFormat != DPSDefaultNumFormat)
	  /* then it must be IEEE */
	  WTT(138);			/* 32-bit IEEE float */
	else
	  PB(140);			/* 32-bit native float */
#endif /* IEEEFLOAT */

	DPSWriteData(ctxt, (char *) &currObj->val.realVal, 4);
	break;
      case DPS_NAME: {
	char *p = 0;
	integer index = currObj->val.nameVal;
	
	length = currObj->length;
	if (length == DPSSYSNAME) {/* system name index */
	    if (index >= 0 && index < 256) {
	      if (lit)
		PB(145);		/* literal system name */
	      else
		PB(146);		/* exec. system name */
	      PB(index);
	      goto next_obj;
	      }
	    else if (DPS_FIRST_AUX_SYSNAME <= index
		     && index <= DPS_LAST_AUX_SYSNAME)
	      p = DPSSysNamesAux[index - DPS_FIRST_AUX_SYSNAME];
	    else DPSCantHappen();
	    length = strlen(p);
	    }
	else if (length == 0) { /* user name index */
	    if (ctxt->nameEncoding == dps_indexed && index < 256) {
	      if (lit)
		PB(147);		/* literal user name index */
	      else
		PB(148);		/* executable user name index */
	      PB(index);
	      goto next_obj;
	      }
	    else {
	      p = DPSNameFromIndex(index);
	      length = strlen(p);
	      }
	    }
	else
	  p = base + index;
      if (lit) DPSPrintf(ctxt, "/");
	DPSWriteData(ctxt, p, length);
	DPSPrintf(ctxt, " ");
	break; 
	}
      case DPS_ARRAY: {
	DPSBinObj aryObj = (DPSBinObj)(base+currObj->val.arrayVal);
	integer r;
	length = currObj->length;
	if (lit && (r = TestHomogeneous(aryObj, length, numFormat)) != -1) {
	  PB(149);			/* homogeneous number array */
	  PB(r);
	  DPSWriteData(ctxt, (char *) &length, 2);
	  if (r > 127) r -= 128;
	  while (length--) {
	    switch (r) {
	      case 0:
		DPSWriteData(ctxt, (char *) &aryObj->val.integerVal, 4);
		break;
	      case 32:
		n16.i = aryObj->val.integerVal;
		DPSWriteData(ctxt, (char *) n16.bytes, 2);
		break;
	      case 48:
	      case 49:
		DPSWriteData(ctxt, (char *) &aryObj->val.realVal, 4);
		break;
	      default: DPSCantHappen();
	      }
	    ++aryObj;
	    }
	  }
	else { 
	  DPSPrintf(ctxt, (lit ? "[ " : "{ "));
	  WriteSeqAsTokens(ctxt, base, aryObj, length, tokenType,
			   numstringOffsets);
	  DPSPrintf(ctxt, (lit ? " ] " : " } "));
	  }
	break; }
      case DPS_MARK:
	if (lit)
	  DPSPrintf(ctxt, "/mark ");
	else
	  DPSPrintf(ctxt, "mark ");
	break;
      case DPS_STRING: {
	char *p = base + currObj->val.stringVal;
	int i;
	if (numstringOffsets != NULL) {
	    for (i = 2; i < numstringOffsets[1] &&
		 numstringOffsets[i] != currObj->val.stringVal; i++) ;
	    if (numstringOffsets[i] == currObj->val.stringVal) {
		DPSAssert(*(unsigned char *) p == 149);
		DPSWriteData(ctxt, p, length);
		break;
	    }
	}
	length = currObj->length;
	if (length < 256) {
	  PB(142);			/* short string */
	  PB(length);
	  }
	else {
	  WTT(143);			/* long string */
	  DPSWriteData(ctxt, (char *) &length, 2);
	  }
	DPSWriteData(ctxt, p, length);
	break;
	}
      default: DPSCantHappen();
      }
next_obj:
    ++currObj;
    if (++nLineObjs == 15) {
      nLineObjs = 0;
      DPSPrintf(ctxt, "\n ");
      }
    } /* end while */

    DPSPrintf(ctxt, "\n");
} /* WriteSeqAsTokens */

static void WriteTokenAsAscii(
  DPSContext ctxt,
  register unsigned char *buf)
{
  Swap32Rec n32;
  Swap16Rec n16;
  register unsigned char *b;

  switch (*buf++) {
    case 132:	 /* 32-bit int, hi */
      b = n32.bytes;
      Copy4SrcHi(buf, b);
      DPSPrintf(ctxt, "%d ", n32.i);
      break;
    case 133:	 /* 32-bit int, lo */
      b = n32.bytes;
      Copy4SrcLo(buf, b);
      DPSPrintf(ctxt, "%d ", n32.i);
      break;
    case 134:	 /* 16-bit int, hi */
      b = n16.bytes;
      Copy2SrcHi(buf, b);
      DPSPrintf(ctxt, "%d ", n16.i);
      break;
    case 135:	 /* 16-bit int, lo */
      b = n16.bytes;
      Copy2SrcLo(buf, b);
      DPSPrintf(ctxt, "%d ", n16.i);
      break;
    case 136:	 /* 8-bit int, signed */
      n32.i = (char) *buf;
      DPSPrintf(ctxt, "%d ", n32.i);
      break;
    case 137: {  /* 16 or 32-bit fixed */
	unsigned char r = *buf++;
	float f = 0.0;
	boolean hi = (r < 128);
	
	if (!hi) r -= 128;
	if (r < 32) {		/* 32-bit */
	  b = n32.bytes;
	  if (hi) {Copy4SrcHi(buf, b);} else {Copy4SrcLo(buf, b);}
	  if (r == 0) {
	    DPSPrintf(ctxt, "%d ", n32.i);
	    break;
	    }
	  else {
	    f = n32.i;  /* convert to float */
	    goto do_scale;
	    }
	  }
	else if (r < 48) {	/* 16-bit */
	  b = n16.bytes;
	  if (hi) {Copy2SrcHi(buf, b);} else {Copy2SrcLo(buf, b);};
	  if (r == 0) {
	    DPSPrintf(ctxt, "%d ", n16.i);
	    break;
	    }
	  else {
	    r -= 32;
	    f = n16.i;  /* convert to float */
	    goto do_scale;
	    }
	  }
	else DPSCantHappen();
do_scale:
	n32.f = f / (1 << r);  /* scale */
	DPSPrintf(ctxt, "%g ", n32.f);
	break;
	}
    case 138:	 /* 32-bit IEEE float, hi */
#if IEEEFLOAT
      b = n32.bytes;
      Copy4SrcHi(buf, b);
#else /* IEEEFLOAT */
      IEEEHighToNative(buf, &n32.f);
#endif /* IEEEFLOAT */
      DPSPrintf(ctxt, "%g ", n32.f);
      break;
    case 139:	 /* 32-bit IEEE float, lo */
#if IEEEFLOAT
      b = n32.bytes;
      Copy4SrcLo(buf, b);
#else /* IEEEFLOAT */
      IEEELowToNative(buf, &n32.f);
#endif /* IEEEFLOAT */
      DPSPrintf(ctxt, "%g ", n32.f);
      break;
    case 140:	 /* 32-bit native float */
      b = n32.bytes;
      *b++ = *buf++,*b++ = *buf++,*b++ = *buf++,*b = *buf;
      DPSPrintf(ctxt, "%g ", n32.f);
      break;
    case 141:	 /* boolean */
      if (*buf)
	DPSPrintf(ctxt, "true ");
      else
	DPSPrintf(ctxt, "false ");
      break;
    case 142:	 /* short string */
      DPSPrintf(ctxt, "(");
      n16.u = *buf++;
      goto share_str_code;
    case 143:	 /* long string, hi */
      b = n16.bytes;
      Copy2SrcHi(buf, b);
      DPSPrintf(ctxt, "(");
      buf += 2;
      goto share_str_code;
    case 144:	 /* long string, lo */
      b = n16.bytes;
      Copy2SrcLo(buf, b);
      DPSPrintf(ctxt, "(");
      buf += 2;
share_str_code:
#ifdef VMS
      WriteVMSStringBody(ctxt, buf, n16.u); /* write this */
#else /* VMS */
      /* render string bytes correctly */
      while (n16.u--) {
	unsigned char c = *buf++;
	if (c == '(' || c == ')' || c == '\\')
	  DPSPrintf(ctxt, "\\%c", c);
	else if (c == '\n')
	  DPSPrintf(ctxt, "\\n");
	else if (!(isascii(c) && isprint(c)))
	  DPSPrintf(ctxt, "\\%03.3o", c);
	else DPSWriteData(ctxt, (char *) &c, 1);
	}
#endif /* VMS */
      DPSPrintf(ctxt, ") ");
      break;
    case 145:	 /* literal system name index */
      DPSPrintf(ctxt, "/%s ", DPSSysNames[*buf]);
      break;
    case 146:	 /* executable system name index */
      DPSPrintf(ctxt, "%s ", DPSSysNames[*buf]);
      break;
    case 147:	/* literal user name index */
      DPSPrintf(ctxt, "/%s ", DPSNameFromIndex(*buf));
      break;
    case 148:	/* executable user name index */
      DPSPrintf(ctxt, "%s ", DPSNameFromIndex(*buf));
      break;
    case 149: {  /* homogeneous number array */
      WriteHomogeneousArrayAsASCII(ctxt, buf);
      break;
      }
    default:; /* unassigned */
    }
} /* WriteTokenAsAscii */
  

/* WriteEntireGoody converts an entire binary token or binary object
   sequence as specified by ctxt's encoding parameters. Write the
   converted bytes via DPSWriteData.  buf points to the complete goody. */
   
static void WriteEntireGoody(
  DPSContext ctxt,
  unsigned char *buf,
  int *numstringOffsets)
{

  DPSExtendedBinObjSeq bos = (DPSExtendedBinObjSeq) buf;
  DPSExtendedBinObjSeqRec bosRec;
  DPSBinObj currObj;
  DPSPrivContext cc = (DPSPrivContext) ctxt;
  
  if (IsBinaryToken(*buf)) {
     /* only supported conversion is binary token to ASCII */
     WriteTokenAsAscii(ctxt, buf);
     if (numstringOffsets) numstringOffsets[1] = 2;
     return;
     }
     
  if (bos->escape != 0) {
    /* not extended BOS */
    DPSBinObjSeq seqHead = (DPSBinObjSeq) buf;
    bos = &bosRec;
    bos->tokenType = seqHead->tokenType;
    bos->escape = seqHead->nTopElements;
    bos->nTopElements = seqHead->nTopElements;
    bos->length = seqHead->length;
    currObj = &(seqHead->objects[0]);
    }
  else currObj = &(bos->objects[0]);

  switch (ctxt->programEncoding) {
    case dps_binObjSeq:
      if (ctxt->nameEncoding == dps_strings) {
	  /* takes three passes to do conversions */
	  ConvertAndWriteSeqAsData(ctxt, (char *) buf, 0);
	  ConvertAndWriteSeqAsData(ctxt, (char *) buf, 1);
	  ConvertAndWriteSeqAsData(ctxt, (char *) buf, 2);
	}
      else if (bos->tokenType != DPS_DEF_TOKENTYPE
	       || cc->numFormat != DPSDefaultNumFormat) {
	  /* first pass just writes modified seqHead */
	  ConvertAndWriteSeqAsData(ctxt, (char *) buf, 0);
	  /* second pass converts numbers and writes the sequence */
	  ConvertAndWriteSeqAsData(ctxt, (char *) buf, 1);
	}
      else DPSWriteData(ctxt, (char *) buf, bos->length);
      break;
    case dps_ascii: 
    case dps_encodedTokens: {
      
      if (ctxt->programEncoding == dps_ascii)
        {
	WriteSeqAsAscii(
	  ctxt, (char *)currObj, currObj, bos->nTopElements,
	  bos->tokenType, numstringOffsets);
	}
      else
	WriteSeqAsTokens(
	  ctxt, (char *)currObj, currObj, bos->nTopElements,
	  bos->tokenType, numstringOffsets);
      DPSWriteData(ctxt, "\n", 1);
      break;
      }
    default:;
    }
  if (numstringOffsets) numstringOffsets[1] = 2;
} /* WriteEntireGoody */


/**************************************/
/* Context procs for DPSCreateTextContext */
/**************************************/

static void textWriteData(DPSContext ctxt, char *buf, unsigned int nch)
{
  (*ctxt->textProc)(ctxt, buf, nch);
  if (ctxt->chainChild != NIL) DPSWriteData(ctxt->chainChild, buf, nch);
}

static void textFlushContext(DPSContext ctxt)
{
  if (ctxt->chainChild != NIL) DPSFlushContext(ctxt->chainChild);
}

static void textInterruptContext(DPSContext ctxt)
{
  if (ctxt->chainChild != NIL) DPSInterruptContext(ctxt->chainChild);
}

static void textDestroyContext(DPSContext ctxt)
{
  DPSPrivContext c = (DPSPrivContext)ctxt;

  DPSUnchainContext(ctxt);

  free(c);
}

static void textInnerWritePostScript(
  DPSContext ctxt, char *buf, unsigned int nch)
{
  DPSPrivContext cc = (DPSPrivContext)ctxt;
  while (nch > 0) {
    char *oldBuf = NIL;
    integer oldNch = 0;
    unsigned n;
    if (cc->outBuf) { /* we're buffering */
      unsigned m;
      integer bst;
      if (!IsBinaryToken(cc->outBuf[0]) && cc->nOutBufChars < DPS_SEQ_MIN) {
	char *tb = cc->outBuf + cc->nOutBufChars;
	integer nn = DPS_SEQ_MIN - cc->nOutBufChars;
	DPSAssert(nn == 1);
	cc->nOutBufChars += nn;
	nch -= nn;
	*tb++ = *buf++;
	}
      bst = GetHdrNBytes((unsigned char *) cc->outBuf);
	/* # bytes needed to determine size */
      if (cc->nOutBufChars < bst) {
	char *b = cc->outBuf;
	if (nch + cc->nOutBufChars < (unsigned) bst) {
	  os_bcopy(buf, cc->outBuf + cc->nOutBufChars, nch);
	  cc->nOutBufChars += nch;
	  return;
	  }
	os_bcopy(buf, b + cc->nOutBufChars, bst - cc->nOutBufChars);
	buf += bst - cc->nOutBufChars;
	nch -= bst - cc->nOutBufChars;
	cc->nOutBufChars = bst;
	m = GetNBytes((unsigned char *) cc->outBuf);
	cc->outBuf = (char *)DPScalloc(m, 1);
	os_bcopy(b, cc->outBuf, bst);
	free(b);
	}
      else m = GetNBytes((unsigned char *) cc->outBuf);

      /* here with size of entire goody in m and outBuf set up */
      if (nch + cc->nOutBufChars < m) {
	os_bcopy(buf, cc->outBuf + cc->nOutBufChars, nch);
	cc->nOutBufChars += nch;
	return;
	}
      os_bcopy(buf, cc->outBuf + cc->nOutBufChars, m - cc->nOutBufChars);
      buf += m - cc->nOutBufChars;
      nch -= m - cc->nOutBufChars;
      cc->nOutBufChars = m;
      oldBuf = buf;
      oldNch = nch;
      buf = cc->outBuf;
      nch = cc->nOutBufChars;
      cc->outBuf = NIL;
      cc->nOutBufChars = 0;
      } /* if (cc->outBuf) */

    /* dispose of any plain text.  If no binary conversion, all output
       is plain text */
    if (cc->contextFlags & DPS_FLAG_NO_BINARY_CONVERSION) n = nch;
    else {
	for (n = 0; n < nch &&
	  ((unsigned char) buf[n] < 128 || (unsigned char) buf[n] > 159); n++);
    }
    if (n > 0) {
      /* Assumes below that any error proc called uses dpsexcept.h
	 if it rips control away */
      DURING
	DPSWriteData((DPSContext)cc, buf, n);
      HANDLER
	if (oldBuf) free(buf);
	RERAISE;
      END_HANDLER
      }
    buf += n;
    nch -= n;

    if (nch != 0) {
      /* here with the next binary object sequence or encoded token */
      unsigned m = 0;
      integer bst;
      if (!IsBinaryToken(buf[0]) && nch < DPS_SEQ_MIN) {
	/* gotta buffer it */
	DPSAssertWarn(nch == 1 && !oldBuf, cc, "problem converting binary token/sequence (nch!=1||oldBuf)");
	cc->outBuf = (char *)DPScalloc(DPS_EXT_HEADER_SIZE, 1);
	cc->nOutBufChars = nch;
	cc->outBuf[0] = *buf;
	return;
	}
      bst = GetHdrNBytes((unsigned char *) buf);
      if (nch < (unsigned)bst || nch < (m = GetNBytes((unsigned char *) buf))) {
	/* gotta buffer it */
	DPSAssertWarn(!oldBuf, cc, "problem converting binary token/sequence (oldBuf)");
	if (nch < (unsigned) bst) {
	  cc->outBuf = (char *)DPScalloc(bst, 1);
	  }
	else {
	  cc->outBuf = (char *)DPScalloc(m, 1);
	  }
	cc->nOutBufChars = nch;
	os_bcopy(buf, cc->outBuf, nch);
	return;
	}

      /* Assumes below that any error proc called uses dpsexcept.h
	 if it rips control away */
      DURING
	WriteEntireGoody(ctxt, (unsigned char *) buf, cc->numstringOffsets);
      HANDLER
	if (oldBuf) {
	  DPSAssertWarn(nch == m, cc, "some converted PostScript language may be lost during error recovery (nch!=m)");
	  free(buf);
	  }
	RERAISE;
      END_HANDLER

      if (oldBuf) {
	DPSAssertWarn(nch == m, cc, "some converted PostScript language may be lost (nch!=m)");
	free(buf);
	buf = oldBuf;
	nch = oldNch;
	oldBuf = NIL;
	}
      else {
	buf += m;
	nch -= m;
	}
      } /* if (nch != 0) */
    } /* while (nch > 0) */
} /* textInnerWritePostScript */

static void textWritePostScript(
  DPSContext ctxt, char *buf, unsigned int nch)
{
  DPSContext children = ctxt->chainChild;
  /* disconnect temporarily so that high level procs can
     be called safely */
  if (children != NIL) ctxt->chainChild = NIL;
  DURING
  textInnerWritePostScript(ctxt, buf, nch);
  HANDLER
    if (children != NIL) ctxt->chainChild = children;
    RERAISE;
  END_HANDLER
  if (children != NIL) {
    ctxt->chainChild = children;
    DPSWritePostScript(ctxt->chainChild, buf, nch);
    }
}

static void textWriteStringChars(
  DPSContext ctxt, char *buf, unsigned int nch)
{
  DPSContext children = ctxt->chainChild;

  if (DPSCheckShared((DPSPrivContext)ctxt)) return;
  /* disconnect temporarily so that high level procs can
     be called safely */
  if (children != NIL) ctxt->chainChild = NIL;
  DURING
  textInnerWritePostScript(ctxt, buf, nch);
  HANDLER
    if (children != NIL) ctxt->chainChild = children;
    RERAISE;
  END_HANDLER
  if (children != NIL) {
    ctxt->chainChild = children;
    DPSWriteStringChars(ctxt->chainChild, buf, nch);
    }
}

static void textBinObjSeqWrite(
  DPSContext ctxt, char *buf, unsigned int nch)
{
  DPSContext children = ctxt->chainChild;
  DPSPrivContext c = (DPSPrivContext) ctxt;

  if (DPSCheckShared(c)) return;
  if (c->lastNameIndex < globLastNameIndex)
    DPSUpdateNameMap(ctxt);
  /* disconnect temporarily so that high level procs can
     be called safely */
  if (children != NIL) ctxt->chainChild = NIL;
  DURING
  textInnerWritePostScript(ctxt, buf, nch);
  HANDLER
    if (children != NIL) ctxt->chainChild = children;
    RERAISE;
  END_HANDLER
  if (children != NIL) {
    ctxt->chainChild = children;
    DPSBinObjSeqWrite(ctxt->chainChild, buf, nch);
    }
}

static void textWriteNumstring(
    DPSContext ctxt,
    DPSDefinedType type,
    char *data,
    unsigned int size,
    int scale)
{
    DPSPrivContext cc = (DPSPrivContext) ctxt;
#define BUFFER_GROW 10

    if (cc->contextFlags & DPS_FLAG_CONVERT_NUMSTRINGS) {
	if (cc->numstringOffsets == NULL) {
	    cc->numstringOffsets = (int *) DPScalloc(sizeof(int),
						     BUFFER_GROW + 2);
	    cc->numstringOffsets[0] = BUFFER_GROW + 2;	/* Current size */
	    cc->numstringOffsets[1] = 2;		/* Next slot */
	} else if (cc->numstringOffsets[1] >= cc->numstringOffsets[0]) {
	    cc->numstringOffsets[0] += BUFFER_GROW;
	    cc->numstringOffsets =
		    (int *) realloc(cc->numstringOffsets,
				    sizeof(int)* cc->numstringOffsets[0]);
	}

	/* Subtract 4 because of binary object sequence header */
	cc->numstringOffsets[cc->numstringOffsets[1]] = cc->nOutBufChars - 4;
	cc->numstringOffsets[1] += 1;
    }

    innerProcWriteNumstring(ctxt, type, data, size, scale, textInnerWritePostScript);
#undef BUFFER_GROW
} /* textWriteNumstring */

/*********************/
/* Public procedures */
/*********************/

/********************************************/
/* Public procs for dealing with user names */

char *DPSNameFromIndex(long int index)
{
  if (DPSglobals == NIL || index < 0 || index > globLastNameIndex
      || userNameDict == NIL)
    return NIL;
  return (char *) userNames[index];
}

void DPSMapNames(
  DPSContext ctxt,
  unsigned int nNames,
  char **names,
  int **indices)
{
  unsigned i;
  char *last = names[0];

  DPSCheckInitClientGlobals();

#define USERNAMEDICTLENGTH 100

  if (userNameDict == NIL) {
    userNameDict = DPSCreatePSWDict(USERNAMEDICTLENGTH);
    userNames = (char **)DPScalloc(sizeof(char *), USERNAMEDICTLENGTH);
    userNamesLength = USERNAMEDICTLENGTH;
    }

  for (i = 0; i < nNames; i++) {
    integer j;
    char *n = names[i];
    DPSContext c;
    
    if (n == NIL)
      n = last;
    else
      last = n;
    DPSAssert(n != NIL);
    if (strlen(n) > 128) {
      DPSSafeSetLastNameIndex(ctxt);
      (*ctxt->errorProc)(ctxt, dps_err_nameTooLong, (unsigned long) n, strlen(n));
      return;
      }
    j = DPSWDictLookup(userNameDict, n);
    if (j >= 0) {
      *(indices[i]) = j;
      /* handle the case where another context in another space has
	 defined this name */
      if (((DPSPrivContext)ctxt)->lastNameIndex < j) 
	DPSUpdateNameMap(ctxt);
      }
    else {
      /* handle other cases where another context in another
	 space has defined names */
      if (((DPSPrivContext)ctxt)->lastNameIndex < globLastNameIndex)
	DPSUpdateNameMap(ctxt);
      globLastNameIndex++;
      if (((globLastNameIndex + 1) > userNamesLength)) {
	char **t = (char **)DPScalloc(sizeof(char *),
				      userNamesLength + USERNAMEDICTLENGTH);
	for (j = 0; j < userNamesLength; j++) {
	  t[j] = userNames[j];
	  }
	free(userNames);
	userNames = t;
	userNamesLength += USERNAMEDICTLENGTH;
	}
      userNames[globLastNameIndex] = n;
      DPSWDictEnter(userNameDict, n, globLastNameIndex);
      *(indices[i]) = globLastNameIndex;
      DPSPrintf(ctxt, "%d /%s defineusername\n", globLastNameIndex, n);
      for (c = ctxt; c != NIL; c = c->chainChild)
	((DPSPrivContext)c)->lastNameIndex = globLastNameIndex;
      }
    } /* for */
}
     
/**********************/
/* Other public procs */

void DPSDefaultErrorProc(
  DPSContext ctxt,
  DPSErrorCode errorCode,
  long unsigned int arg1, long unsigned int arg2)
{

  DPSTextProc textProc = DPSGetCurrentTextBackstop();
  
  char *prefix = "%%[ Error: ";
  char *suffix = " ]%%\n";

  char *infix = "; OffendingCommand: ";
  char *nameinfix = "User name too long; Name: ";
  char *contextinfix = "Invalid context: ";
  char *taginfix = "Unexpected wrap result tag: ";
  char *typeinfix = "Unexpected wrap result type; tag: ";

  switch (errorCode) {
    case dps_err_ps: {
      char *buf = (char *)arg1;
      DPSBinObj ary = (DPSBinObj) (buf+DPS_HEADER_SIZE);
      DPSBinObj elements;
      char *error, *errorName;
      integer errorCount, errorNameCount;
      boolean resyncFlg;
      
      if ((ary->attributedType & 0x7f) != DPS_ARRAY 
        || ary->length != 4) {
	DPSHandleBogusError(ctxt, prefix, suffix);
        }

      elements = (DPSBinObj)(((char *) ary) + ary->val.arrayVal);

      errorName = (char *)(((char *) ary) + elements[1].val.nameVal);
      errorNameCount = elements[1].length;

      error = (char *)(((char *) ary) + elements[2].val.nameVal);
      errorCount = elements[2].length;

      resyncFlg = elements[3].val.booleanVal;

      if (textProc != NIL) {
	(*textProc)(ctxt, prefix, strlen(prefix));
	(*textProc)(ctxt, errorName, errorNameCount);
	(*textProc)(ctxt, infix, strlen(infix));
	(*textProc)(ctxt, error, errorCount);
	(*textProc)(ctxt, suffix, strlen(suffix));
	}
      if (resyncFlg && (ctxt != dummyCtx) && (ctxt != NULL)) {
#if 0	/* Postpone the raise 'til later to avoid RAISEing through Xlib */
	RAISE(dps_err_ps, (char *) ctxt);
	DPSCantHappen();
#else
	DPSPrivContext cc = (DPSPrivContext) ctxt;
	cc->resyncing = true;
#endif
        }
      break;
      }
    case dps_err_nameTooLong:
      if (textProc != NIL) {
	char *buf = (char *)arg1;
	(*textProc)(ctxt, prefix, strlen(prefix));
	(*textProc)(ctxt, nameinfix, strlen(nameinfix));
	(*textProc)(ctxt, buf, arg2);
	(*textProc)(ctxt, suffix, strlen(suffix));
	}
      break;
    case dps_err_invalidContext:
      if (textProc != NIL) {
	char m[100];
	(void) sprintf(m, "%s%s%ld%s", prefix, contextinfix, arg1, suffix);
	(*textProc)(ctxt, m, strlen(m));
	}
      break;
    case dps_err_resultTagCheck:
      if (textProc != NIL) {
	char m[100];
	unsigned char tag = *((unsigned char *) arg1+1);
	(void) sprintf(m, "%s%s%d%s", prefix, taginfix, tag, suffix);
	(*textProc)(ctxt, m, strlen(m));
	}
      break;
    case dps_err_resultTypeCheck:
      if (textProc != NIL) {
	char m[100];
	unsigned char tag = *((unsigned char *) arg1+1);
	(void) sprintf(m, "%s%s%d%s", prefix, typeinfix, tag, suffix);
	(*textProc)(ctxt, m, strlen(m));
	}
      break;
    default:
	DPSDefaultPrivateHandler(ctxt, errorCode, arg1, arg2, prefix, suffix);
	break;
    }
} /* DPSDefaultErrorProc */

void DPSCheckRaiseError(DPSContext c)
{
    DPSPrivContext cc = (DPSPrivContext) c;
    if (cc != NULL && cc->resyncing) {
	cc->resyncing = false;
	RAISE(dps_err_ps, (char *) c);
	DPSCantHappen();
    }
}

/**************************************/
/* Public procs for creating contexts */

/* dps_err_invalidAccess is now defined for all clients. */

static void textAwaitReturnValues(DPSContext ctxt)
{
  (*ctxt->errorProc)(ctxt, dps_err_invalidAccess, 0, 0);
}

static void Noop(void)
{
}
  
DPSContext DPSCreateTextContext(
  DPSTextProc textProc, DPSErrorProc errorProc)
{
  DPSPrivContext c;

  if (DPSInitialize () != 0) return((DPSContext) NIL);
  if (!textCtxProcs) {
    textCtxProcs = (DPSProcs)DPScalloc(sizeof(DPSProcsRec), 1);
    DPSInitCommonTextContextProcs(textCtxProcs);
    DPSInitSysNames();
    }

  c = (DPSPrivContext)DPScalloc(sizeof(DPSPrivContextRec), 1);
  c->textProc = textProc;
  c->procs = textCtxProcs;
  c->textProc = textProc;
  c->errorProc = errorProc;
  c->programEncoding = dps_ascii;
  c->nameEncoding = dps_strings;  /* don't write user name indices on a file */
  c->contextFlags = DPS_FLAG_CONVERT_NUMSTRINGS; /* Convert by default */
  c->numFormat = DPSDefaultNumFormat;
  c->numstringOffsets = NULL;
  c->lastNameIndex = -1;
 
  /* Setup a dummy space */
  if (textSpace == NIL)
    {
    textSpace = (DPSPrivSpace) DPScalloc(sizeof (DPSPrivSpaceRec), 1);
    textSpace->procs = (DPSSpaceProcs) DPScalloc(sizeof (DPSSpaceProcsRec), 1);
    textSpace->procs->DestroySpace = (DPSSpaceProc) Noop;
    textSpace->lastNameIndex = -1;
    DPSInitPrivateSpaceFields(textSpace);
    }
  c->space = (DPSSpace) textSpace;

  DPSInitPrivateTextContextFields(c, textSpace);
  return (DPSContext)c;
} /* DPSCreateTextContext */


static DPSContext CreateDummyContext(void)
{
  DPSPrivContext c;

  DPSCheckInitClientGlobals();
  if (!dummyCtxProcs) {
    dummyCtxProcs = (DPSProcs)DPScalloc(sizeof(DPSProcsRec), 1);
    dummyCtxProcs->BinObjSeqWrite = (DPSContextBufProc) Noop;
    dummyCtxProcs->WriteTypedObjectArray = (DPSContextTypedArrayProc) Noop;
    dummyCtxProcs->WriteStringChars = (DPSContextBufProc) Noop;
    dummyCtxProcs->WritePostScript = (DPSContextBufProc) Noop;
    dummyCtxProcs->WriteData = (DPSContextBufProc) Noop;
    dummyCtxProcs->FlushContext = (DPSContextProc) Noop;
    dummyCtxProcs->ResetContext = (DPSContextProc) Noop;
    dummyCtxProcs->WaitContext = (DPSContextProc) Noop;
    dummyCtxProcs->UpdateNameMap = (DPSContextProc) Noop;
    dummyCtxProcs->AwaitReturnValues = (DPSContextProc) Noop;
    dummyCtxProcs->Interrupt = (DPSContextProc) Noop;
    dummyCtxProcs->DestroyContext = (DPSContextProc) Noop;
    dummyCtxProcs->WriteNumString = (DPSWriteNumStringProc) Noop;
    }

  c = (DPSPrivContext)DPScalloc(sizeof(DPSPrivContextRec), 1);
  c->procs = dummyCtxProcs;
  c->programEncoding = DPSDefaultProgramEncoding;
  c->nameEncoding = DPSDefaultNameEncoding; /* don't care */
  c->numFormat = DPSDefaultNumFormat;
  c->lastNameIndex = -1;
  c->numstringOffsets = NULL;

  return (DPSContext)c;
} /* CreateDummyContext */

void DPSSetTextBackstop(DPSTextProc textProc)
{
  DPSCheckInitClientGlobals();
  if (!dummyCtx) dummyCtx = CreateDummyContext();
  dummyCtx->textProc = textProc;
}

DPSTextProc DPSGetCurrentTextBackstop(void)
{
  DPSCheckInitClientGlobals();
  if (dummyCtx == NIL) return NIL;
  else return dummyCtx->textProc;
}

void DPSSetErrorBackstop(DPSErrorProc errorProc)
{
  DPSCheckInitClientGlobals();
  if (!dummyCtx) dummyCtx = CreateDummyContext();
  dummyCtx->errorProc = errorProc;
}

DPSErrorProc DPSGetCurrentErrorBackstop(void)
{
  DPSCheckInitClientGlobals();
  if (dummyCtx == NIL) return NIL;
  else return dummyCtx->errorProc;
}

static void NoteInitFailure(DPSContext ctxt, char *buf, long unsigned length)
{
  initFailed = -1;
}

int DPSInitialize(void)
{
  DPSCheckInitClientGlobals();
  if (!clientStarted) {
    clientStarted = true;
    initFailed = 0;
    DPSInitClient(NoteInitFailure, ReleaseInput); /* may call DPSCreateContext */
    /* textProc will not be used unless DPS initialization fails */
    }
  return initFailed;
}

DPSContext DPSCreateContext(
  char *wh,
  DPSTextProc textProc,
  DPSErrorProc errorProc,
  DPSSpace space)
{
  
  DPSPrivSpace ss;
  DPSPrivContext c;

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

  if (!ctxProcs) {
    ctxProcs = (DPSProcs)DPScalloc(sizeof(DPSProcsRec), 1);
    ctxProcs->BinObjSeqWrite = procBinObjSeqWrite;
    ctxProcs->WriteTypedObjectArray = writeTypedObjectArray;
    ctxProcs->WriteStringChars = procWriteStringChars;
    ctxProcs->WritePostScript = procWritePostScript;
    ctxProcs->WriteData = procWriteData;
    ctxProcs->UpdateNameMap = procUpdateNameMap;
    ctxProcs->Interrupt = procInterrupt;
    ctxProcs->WriteNumString = (DPSWriteNumStringProc) procWriteNumstring;
    }
  if (!spaceProcs) {
    spaceProcs = (DPSSpaceProcs)DPScalloc(sizeof(DPSSpaceProcsRec), 1);
    DPSInitCommonSpaceProcs(spaceProcs);
    }
  
  ss = (DPSPrivSpace)space;

  if (ss == NIL) {
    ss = (DPSPrivSpace)DPScalloc(sizeof(DPSPrivSpaceRec), 1);
    ss->procs = spaceProcs;
    ss->lastNameIndex = -1;
    ss->next = spaces;
    DPSAssert(ss->next != ss);
    spaces = ss;
    DPSInitPrivateSpaceFields(ss);
    }

  if (ss->wh == NIL) ss->wh = wh; /* KLUDGE to support DPSSendDestroySpace */

  c = (DPSPrivContext)DPScalloc(sizeof(DPSPrivContextRec), 1);
  c->procs = ctxProcs;
  c->wh = wh;
  c->textProc = textProc;
  c->errorProc = errorProc;
  c->programEncoding = DPSDefaultProgramEncoding;
  c->nameEncoding = DPSDefaultNameEncoding;
  c->lastNameIndex = -1;
  c->space = (DPSSpace)ss;
  c->numstringOffsets = NULL;
  
  c->next = ss->firstContext;
  DPSAssert(c->next != c);
  ss->firstContext = c;

  DPSInitPrivateContextFields(c, ss);

  c->numFormat = DPSCreatePrivContext(
	(XDPSPrivContext)wh,
	(DPSContext)c,
	(ContextPSID *)&c->cid,
	(SpaceXID *)&ss->sid,
	(space == NIL), DPSclientPrintProc);
  if (c->numFormat ==  (DPSNumFormat) -1)
    { /* can't create the context */
    if (space == NIL) {
      spaces = ss->next;
      free(ss);
      }
    else ss->firstContext = c->next;
    free(c);
    return NIL;
    }
  else return (DPSContext)c;
} /* DPSCreateContext */

char *DPSSetWh(
  DPSContext ctxt,
  char *newWh)
{
  DPSPrivContext cc = (DPSPrivContext) ctxt;
  char *tmp = cc->wh;
  cc->wh = newWh;
  return(tmp);
}

/*
   The chainParent field is non-NIL if this context automatically receives
   a copy of the PostScript code sent to the referenced (parent) context.
     
   The chainChild field is non-NIL if this context automatically sends along
   to the referenced (child) context a copy of any PostScript code received.
*/
int DPSChainContext(DPSContext parent, DPSContext child)
{
  DPSContext cc = child->chainChild;

  if (child->chainParent != NIL)
    return -1; /* report an error */

  /* insert new child between parent and existing children */
  child->chainChild = parent->chainChild;
  if (parent->chainChild != NIL) {
    DPSAssertWarn(parent->chainChild->chainParent == parent, (DPSPrivContext)parent, "attempting to chain context on invalid chain");
    child->chainChild->chainParent = child;
    }
  child->chainParent = parent;
  parent->chainChild = child;
  /* if child has children, recursively chain them */
  if (cc != NIL) {
    cc->chainParent = NIL;
    (void) DPSChainContext(child, cc);
    }
  return 0;
}

void DPSUnchainContext(DPSContext ctxt)
{
  DPSContext p = ctxt->chainParent;
  DPSContext c = ctxt->chainChild;

  if (p != NIL) { /* remove ctxt from parent's chain */
    DPSAssertWarn(p->chainChild == ctxt, (DPSPrivContext)p, "attempting to unchain context from wrong chain (parent)");
    p->chainChild = c;
    ctxt->chainParent = NIL;
    }
  if (c != NIL) { /* remove ctxt's child (if any) from ctxt's chain */
   DPSAssertWarn(c->chainParent == ctxt, (DPSPrivContext)c, "attempting to unchain context from wrong chain (child)");
    c->chainParent = p;
    ctxt->chainChild = NIL;
    }
}

/****************/
/* Veneer procs */

void DPSAwaitReturnValues(DPSContext ctxt)
{
  (*(ctxt)->procs->AwaitReturnValues)((ctxt));
}

void DPSDestroyContext(DPSContext ctxt)
{
  (*(ctxt)->procs->DestroyContext)((ctxt));
}

void DPSDestroySpace(DPSSpace spc)
{
  (*(spc)->procs->DestroySpace)((spc));
}

void DPSFlushContext(DPSContext ctxt)
{
  (*(ctxt)->procs->FlushContext)((ctxt));
}

DPSContext DPSGetCurrentContext(void) { return DPSGlobalContext; }

void DPSInterruptContext(DPSContext ctxt)
{
  (*(ctxt)->procs->Interrupt)((ctxt));
}

DPSContext DPSPrivCurrentContext(void) { return DPSGlobalContext; }

void DPSResetContext(DPSContext ctxt)
{
  (*(ctxt)->procs->ResetContext)((ctxt));
}

void DPSSetResultTable(
  register DPSContext ctxt,
  DPSResults tbl,
  unsigned int len)
{
  (ctxt)->resultTable = (tbl);
  (ctxt)->resultTableLength = (len);
}

void DPSSetContext(
  DPSContext ctxt)
{
  DPSGlobalContext = ctxt;
}

void DPSUpdateNameMap(
  DPSContext ctxt)
{
  (*(ctxt)->procs->UpdateNameMap)((ctxt));
}

void DPSWaitContext(
  DPSContext ctxt)
{
  (*(ctxt)->procs->WaitContext)(ctxt);
}

void DPSBinObjSeqWrite(
  DPSContext ctxt,
  char *buf,
  unsigned int count)
{
  (*(ctxt)->procs->BinObjSeqWrite)((ctxt), (buf), (count));
}

void DPSWriteData(
  DPSContext ctxt,
  char *buf,
  unsigned int count)
{
  (*(ctxt)->procs->WriteData)((ctxt), (buf), (count));
}

void DPSWritePostScript(
  DPSContext ctxt,
  char *buf,
  unsigned int count)
{
  (*(ctxt)->procs->WritePostScript)((ctxt), (buf), (count));
}

void DPSWriteStringChars(
  DPSContext ctxt,
  char *buf,
  unsigned int count)
{
  (*(ctxt)->procs->WriteStringChars)((ctxt), (buf), (count));
}

void DPSWriteNumString(ctxt, type, data, size, scale)
    DPSContext ctxt;
    DPSDefinedType type;
    char *data;
    unsigned int size;
    int scale;
{
   (*(ctxt)->procs->WriteNumString)((ctxt), (type), (data), (size), (scale));
}

void DPSWriteTypedObjectArray(ctxt, type, array, length)
  DPSContext ctxt;
  DPSDefinedType type;
  char *array;
  unsigned int length; {
  (*(ctxt)->procs->WriteTypedObjectArray)((ctxt), (type), (array), (length));
  }
  
void DPSInitCommonTextContextProcs(DPSProcs p)
{
    p->BinObjSeqWrite = textBinObjSeqWrite;
    p->WriteTypedObjectArray = writeTypedObjectArray;
    p->WriteStringChars = textWriteStringChars;
    p->WritePostScript = textWritePostScript;
    p->WriteData = textWriteData;
    p->FlushContext = textFlushContext;
    p->ResetContext = (DPSContextProc) Noop;
    p->WaitContext = (DPSContextProc) Noop;
    p->UpdateNameMap = procUpdateNameMap;
    p->AwaitReturnValues = textAwaitReturnValues;
    p->Interrupt = textInterruptContext;
    p->DestroyContext = textDestroyContext;
    p->WriteNumString = (DPSWriteNumStringProc) textWriteNumstring;
}

void DPSInitCommonContextProcs(DPSProcs p)
{
    p->BinObjSeqWrite = procBinObjSeqWrite;
    p->WriteTypedObjectArray = writeTypedObjectArray;
    p->WriteStringChars = procWriteStringChars;
    p->WritePostScript = procWritePostScript;
    p->WaitContext = procWaitContext;
    p->DestroyContext = procDestroyContext;
    p->WriteData = procWriteData;
    p->UpdateNameMap = procUpdateNameMap;
    p->Interrupt = procInterrupt;
    p->WriteNumString = (DPSWriteNumStringProc) procWriteNumstring;
}

void DPSInitCommonSpaceProcs(DPSSpaceProcs p)
{
    p->DestroySpace = procDestroySpace;
}
    
void DPSSetNumStringConversion(ctxt, flag)
    DPSContext ctxt;
    int flag;
{
    if (flag) ctxt->contextFlags |= DPS_FLAG_CONVERT_NUMSTRINGS;
    else ctxt->contextFlags &= ~DPS_FLAG_CONVERT_NUMSTRINGS;
}

void DPSSetWrapSynchronization(ctxt, flag)
    DPSContext ctxt;
    int flag;
{
    if (flag) ctxt->contextFlags |= DPS_FLAG_SYNC;
    else ctxt->contextFlags &= ~DPS_FLAG_SYNC;
}

void DPSSuppressBinaryConversion(ctxt, flag)
    DPSContext ctxt;
    int flag;
{
    if (flag) ctxt->contextFlags |= DPS_FLAG_NO_BINARY_CONVERSION;
    else ctxt->contextFlags &= ~DPS_FLAG_NO_BINARY_CONVERSION;
}

void DPSSetAbbrevMode(ctxt, flag)
    DPSContext ctxt;
    int flag;
{
    if (flag) ctxt->contextFlags |= DPS_FLAG_USE_ABBREVS;
    else ctxt->contextFlags &= ~DPS_FLAG_USE_ABBREVS;
}

DPSContextExtensionRec *DPSGetContextExtensionRec(ctxt, extensionId)
    DPSContext ctxt;
    int extensionId;
{
    DPSContextExtensionRec *r = ctxt->extension;

    while (r != NULL && r->extensionId != extensionId) r = r->next;
    return r;
}

void DPSAddContextExtensionRec(ctxt, rec)
    DPSContext ctxt;
    DPSContextExtensionRec *rec;
{
    rec->next = ctxt->extension;
    ctxt->extension = rec;
}

DPSContextExtensionRec *DPSRemoveContextExtensionRec(ctxt, extensionId)
    DPSContext ctxt;
    int extensionId;
{
    DPSContextExtensionRec *rret, **r = &ctxt->extension;


    while (*r != NULL && (*r)->extensionId != extensionId) {
	r = &(*r)->next;
    }
    rret = *r;
    if (*r != NULL) *r = (*r)->next;
    return rret;
}

int DPSGenerateExtensionRecID(void)
{
    static int id = 1;

    return id++;
}

DPSContextType DPSGetContextType(ctxt)
    DPSContext ctxt;
{
    DPSPrivContext c = (DPSPrivContext) ctxt;

    if (c->procs == textCtxProcs) return dps_context_text;
    else return dps_context_execution;
}