(FILECREATED "12-Oct-84 23:08:24" {ERIS}<LISPCORE>SOURCES>COURIER.;6 76282  

      changes to:  (FNS COURIER.OPEN COURIER.EXECUTE.CALL \COURIER.OUTPUT.ABORTED \BULK.DATA.CLOSE 
			\BULK.DATA.STREAM \COURIER.CHECK.PROCEDURES \COURIER.CHECK.ERRORS 
			\COURIER.HANDLE.BULKDATA \COURIER.ATTENTIONFN)
		   (RECORDS \BULK.DATA.CONTINUATION)
		   (VARS COURIERCOMS)

      previous date: "21-Jul-84 23:40:59" {ERIS}<LISPCORE>SOURCES>COURIER.;4)


(* Copyright (c) 1983, 1984 by Xerox Corporation. All rights reserved.)

(PRETTYCOMPRINT COURIERCOMS)

(RPAQQ COURIERCOMS [(COMS (* Courier Remote Procedure Call Protocol.)
			  (DECLARE: DONTCOPY (CONSTANTS (COURIER.VERSION# 3))
				    (CONSTANTS (\COURIERMSG.CALL 0)
					       (\COURIERMSG.REJECT 1)
					       (\COURIERMSG.RETURN 2)
					       (\COURIERMSG.ABORT 3))
				    (CONSTANTS (\NS.WKS.Courier 5))
				    (MACROS \GET.COURIERPROGRAM \COURIER.QUALIFIED.NAMEP NULLORLISTP)
				    (RECORDS COURIERPGM COURIERFN COURIERERR \BULK.DATA.CONTINUATION 
					     COURIERREPSTREAM BULKDATAGENERATOR)
				    (GLOBALVARS DFNFLG LCASEFLG \COURIER.REP.DEVICE \BASEBYTESDEVICE)
				    (COMS (CONSTANTS (\EXPEDITED.LENGTH (IPLUS \XIPOVLEN 6 4))
						     \EXTYPE.EXPEDITED.COURIER)
					  (RECORDS EXPEDITEDXIP)))
			  (* Facilities for manipulating Courier definitions.)
			  (INITVARS (\COURIERPROGRAM (HARRAY 20))
				    \COURIER.REP.DEVICE)
			  (FNS COURIER.VERSION# COURIERPROGRAM \COURIER.PUTDEF \COURIER.CHECKDEF 
			       \COURIER.CHECK.PROCEDURES \COURIER.CHECK.ERRORS \COURIER.DELDEF 
			       \COURIER.GETDEF \GET.COURIERPROGRAM \DUMP.COURIERPROGRAMS 
			       \GET.COURIER.TYPE \GET.COURIER.DEFINITION)
			  (GLOBALVARS \COURIERPROGRAM)
			  (FILEPKGCOMS COURIERPROGRAMS)
			  (COMS (MACROS COURIER.FETCH COURIER.CREATE)
				(PROP INFO COURIER.FETCH COURIER.CREATE)
				(FNS \COURIER.RECORDTRAN))
			  (* Functions for calling Courier procedures.)
			  (FNS COURIER.OPEN \COURIER.WHENCLOSED COURIER.CALL COURIER.EXECUTE.CALL 
			       \COURIER.RESULTS \COURIER.HANDLE.BULKDATA \COURIER.HANDLE.ERROR 
			       \BULK.DATA.STREAM \COURIER.ATTENTIONFN \COURIER.OUTPUT.ABORTED 
			       \BULK.DATA.CLOSE \ABORT.BULK.DATA)
			  (FNS COURIER.EXPEDITED.CALL COURIER.EXECUTE.EXPEDITED.CALL 
			       \BUILD.EXPEDITED.XIP \SEND.EXPEDITED.XIP \COURIER.EXPEDITED.ARGS 
			       \MAKE.EXPEDITED.STREAM \COURIER.EOF \COURIER.EXPEDITED.OVERFLOW)
			  (FNS COURIER.BROADCAST.CALL \COURIER.BROADCAST.ON.NET)
			  (FNS COURIER.READ \COURIER.UNKNOWN.TYPE COURIER.READ.SEQUENCE 
			       COURIER.READ.STRING COURIER.WRITE COURIER.WRITE.SEQUENCE 
			       COURIER.WRITE.STRING \COURIER.TYPE.ERROR)
			  (FNS COURIER.READ.BULKDATA BULKDATA.GENERATOR BULKDATA.GENERATE.NEXT 
			       COURIER.WRITE.BULKDATA)
			  (FNS COURIER.READ.REP COURIER.WRITE.REP COURIER.WRITE.SEQUENCE.UNSPECIFIED 
			       \CWSU.DEFAULT COURIER.REP.LENGTH \MAKE.COURIER.REP.STREAM 
			       \COURIER.REP.BIN \COURIER.REP.BOUT)
			  (COMS (FNS COURIER.READ.NSADDRESS COURIER.WRITE.NSADDRESS)
				(PROP COURIERDEF NSADDRESS)))
		    (COMS (* Debugging)
			  (INITVARS (COURIERTRACEFILE)
				    (COURIERTRACEFLG)
				    (COURIERPRINTLEVEL (QUOTE (2 . 4)))
				    (NSWIZARDFLG))
			  (GLOBALVARS COURIERTRACEFLG COURIERTRACEFILE COURIERPRINTLEVEL NSWIZARDFLG)
			  (FNS COURIERTRACE \COURIER.TRACE))
		    (DECLARE: DONTEVAL@LOAD DOEVAL@COMPILE DONTCOPY COMPILERVARS
			      (ADDVARS (NLAMA \DUMP.COURIERPROGRAMS COURIERPROGRAM)
				       (NLAML)
				       (LAMA COURIER.EXPEDITED.CALL COURIER.CALL])



(* Courier Remote Procedure Call Protocol.)

(DECLARE: DONTCOPY 
(DECLARE: EVAL@COMPILE 

(RPAQQ COURIER.VERSION# 3)

(CONSTANTS (COURIER.VERSION# 3))
)

(DECLARE: EVAL@COMPILE 

(RPAQQ \COURIERMSG.CALL 0)

(RPAQQ \COURIERMSG.REJECT 1)

(RPAQQ \COURIERMSG.RETURN 2)

(RPAQQ \COURIERMSG.ABORT 3)

(CONSTANTS (\COURIERMSG.CALL 0)
	   (\COURIERMSG.REJECT 1)
	   (\COURIERMSG.RETURN 2)
	   (\COURIERMSG.ABORT 3))
)

(DECLARE: EVAL@COMPILE 

(RPAQQ \NS.WKS.Courier 5)

(CONSTANTS (\NS.WKS.Courier 5))
)

(DECLARE: EVAL@COMPILE 

(PUTPROPS \GET.COURIERPROGRAM MACRO ((PROGRAM)
				     (GETHASH PROGRAM \COURIERPROGRAM)))

(PUTPROPS \COURIER.QUALIFIED.NAMEP MACRO [OPENLAMBDA (X)
						     (AND (LISTP X)
							  (LITATOM (CDR X))
							  (LITATOM (CAR X])

(PUTPROPS NULLORLISTP MACRO (OPENLAMBDA (X)
					(OR (NULL X)
					    (LISTP X))))
)

[DECLARE: EVAL@COMPILE 

(RECORD COURIERPGM (VERSIONPAIR . COURIERDEFS)
		   (RECORD VERSIONPAIR (PROGRAM# VERSION#))
		   (PROPRECORD COURIERDEFS (TYPES PROCEDURES ERRORS INHERITS)))

(RECORD COURIERFN (FN# ARGS RETURNSNOISE RESULTS REPORTSNOISE ERRORS))

(RECORD COURIERERR (ERR# ARGS))

(RECORD \BULK.DATA.CONTINUATION (PROGRAM PROCEDURE PGMDEF PROCDEF NOERRORFLG INTERNALFLG))

(ACCESSFNS COURIERREPSTREAM ((CRWORDLIST (fetch F1 of DATUM)
					 (replace F1 of DATUM with NEWVALUE))
			     (CRNEXTBYTE (fetch F2 of DATUM)
					 (replace F2 of DATUM with NEWVALUE))
			     (CRLASTWORD (fetch F3 of DATUM)
					 (replace F3 of DATUM with NEWVALUE))))

(RECORD BULKDATAGENERATOR (BGITEMSLEFT BGSTREAM (BGPROGRAM . BGTYPE) . BGLASTSEGMENT?))
]

(DECLARE: DOEVAL@COMPILE DONTCOPY

(GLOBALVARS DFNFLG LCASEFLG \COURIER.REP.DEVICE \BASEBYTESDEVICE)
)

(DECLARE: EVAL@COMPILE 

(RPAQ \EXPEDITED.LENGTH (IPLUS \XIPOVLEN 6 4))

(RPAQQ \EXTYPE.EXPEDITED.COURIER 2)

(CONSTANTS (\EXPEDITED.LENGTH (IPLUS \XIPOVLEN 6 4))
	   \EXTYPE.EXPEDITED.COURIER)
)
[DECLARE: EVAL@COMPILE 

(ACCESSFNS EXPEDITEDXIP ((EXPEDITEDBASE (fetch (PACKETEXCHANGEXIP PACKETEXCHANGEBODY) of DATUM)))
			(BLOCKRECORD EXPEDITEDBASE ((LOWVERSION WORD)
				      (HIGHVERSION WORD)
				      (MSGTYPE WORD)
				      (TRANSACTIONID WORD)
				      (PROGRAM# FIXP)
				      (VERSION# WORD)
				      (PROCEDURE# WORD)
				      (ARG0 WORD)))
			[ACCESSFNS EXPEDITEDXIP ((EXPEDITEDMSGBODY (LOCF (fetch (EXPEDITEDXIP MSGTYPE)
									    of DATUM)))
				    (EXPEDITEDARGBASE (LOCF (fetch (EXPEDITEDXIP ARG0) of DATUM])
]
)



(* Facilities for manipulating Courier definitions.)


(RPAQ? \COURIERPROGRAM (HARRAY 20))

(RPAQ? \COURIER.REP.DEVICE NIL)
(DEFINEQ

(COURIER.VERSION#
  [LAMBDA NIL                                                (* bvm: " 2-May-84 12:27")

          (* * Returns number of the version of Courier we are running)


    COURIER.VERSION#])

(COURIERPROGRAM
  [NLAMBDA X                                                 (* bvm: "10-Jun-84 23:02")

          (* Define a Courier program and its associated types, constants, procedures, and errors. Syntax is 
	  (COURIERPROGRAM programName (programNumber versionNumber) TYPES (typeDeclarations ...) PROCEDURES 
	  (procedureDeclarations ...) ERRORS (errorDeclarations ...)) The TYPES, PROCEDURES, and ERRORS may appear in any 
	  order after the program number/version number pair.)


    (PUTDEF (CAR X)
	    (QUOTE COURIERPROGRAMS)
	    (CDR X])

(\COURIER.PUTDEF
  [LAMBDA (NAME TYPE DEFINITION)                             (* bvm: "10-Jun-84 23:17")

          (* * PUTDEF for type COURIERPROGRAMS -- also called by COURIERPROGRAM)


    (PROG (OLDINFO)
          (SETQ OLDINFO (GETHASH (SETQ NAME (\DTEST NAME (QUOTE LITATOM)))
				 \COURIERPROGRAM))
          [COND
	    ((NULL OLDINFO)
	      (MARKASCHANGED NAME TYPE (QUOTE DEFINED)))
	    ((AND OLDINFO (NOT (EQUAL OLDINFO DEFINITION)))
	      (COND
		((NEQ DFNFLG T)
		  (LISPXPRINT (LIST (QUOTE COURIER)
				    (QUOTE program)
				    NAME
				    (QUOTE redefined))
			      T T)))
	      (MARKASCHANGED NAME TYPE (QUOTE CHANGED]
          (/PUTHASH NAME DEFINITION \COURIERPROGRAM)
          (RETURN NAME])

(\COURIER.CHECKDEF
  [LAMBDA (NAME DEF)                                         (* bvm: "16-Jul-84 15:36")
    (COND
      ([OR (NLISTP (fetch (COURIERPGM VERSIONPAIR) of DEF))
	   (NOT (FIXP (fetch (COURIERPGM PROGRAM#) of DEF)))
	   (NOT (FIXP (fetch (COURIERPGM VERSION#) of DEF]
	(ERROR "Bad version specification in Courier def" NAME))
      (T (for TAIL on (fetch COURIERDEFS of DEF) by (CDDR TAIL)
	    do (SELECTQ (CAR TAIL)
			((TYPES INHERITS))
			(PROCEDURES (\COURIER.CHECK.PROCEDURES (CADR TAIL)))
			(ERRORS (\COURIER.CHECK.ERRORS (CADR TAIL)))
			(ERROR "Courier definition not understood" (CAR TAIL])

(\COURIER.CHECK.PROCEDURES
  [LAMBDA (DEFS)                                             (* bvm: "12-Oct-84 11:24")
    (for FNDEF in DEFS bind INFO
       unless
	[COND
	  ((NLISTP FNDEF)
	    NIL)
	  ((EQ (CAR FNDEF)
	       COMMENTFLG)                                   (* Comments ok)
	    T)
	  (T (SETQ INFO (CDR FNDEF))
	     (COND
	       ((AND (FIXP (fetch (COURIERFN FN#) of INFO))
		     (NULLORLISTP (fetch (COURIERFN ARGS) of INFO))
		     (LITATOM (fetch (COURIERFN RETURNSNOISE) of INFO))
		     (NULLORLISTP (fetch (COURIERFN RESULTS) of INFO))
		     (LITATOM (fetch (COURIERFN REPORTSNOISE) of INFO))
		     (NULLORLISTP (fetch (COURIERFN ERRORS) of INFO)))
                                                             (* nice new format)
		 T)
	       (T (PROG (ARGS RESULTS ERRORS N)
		        (RETURN (COND
				  ([while INFO
				      do (COND
					   [(NULL (CDR INFO))
					     (RETURN (FIXP (SETQ N (CAR INFO]
					   (T (SELECTQ (CAR INFO)
						       (ARGS (OR (NULLORLISTP (SETQ ARGS
										(CADR INFO)))
								 (RETURN)))
						       (RESULTS (OR (NULLORLISTP (SETQ RESULTS
										   (CADR INFO)))
								    (RETURN)))
						       (ERRORS (OR (NULLORLISTP (SETQ ERRORS
										  (CADR INFO)))
								   (RETURN)))
						       (RETURN))
					      (SETQ INFO (CDDR INFO]
				    (/RPLACD FNDEF
					     (create COURIERFN
						     FN# ← N
						     ARGS ← ARGS
						     RETURNSNOISE ←(QUOTE RETURNS)
						     RESULTS ← RESULTS
						     REPORTSNOISE ←(QUOTE REPORTS)
						     ERRORS ← ERRORS))
				    T]
       do (ERROR "Bad Courier Procedure definition" FNDEF])

(\COURIER.CHECK.ERRORS
  [LAMBDA (DEFS)                                             (* bvm: "12-Oct-84 11:24")
    (for ERRDEF in DEFS bind INFO unless [COND
					   ((NLISTP ERRDEF)
					     NIL)
					   ((EQ (CAR ERRDEF)
						COMMENTFLG)
                                                             (* Comments ok)
					     T)
					   (T (SETQ INFO (CDR ERRDEF))
					      (COND
						((AND (FIXP (fetch (COURIERERR ERR#) of INFO))
						      (NULLORLISTP (fetch (COURIERERR ARGS)
								      of INFO)))
                                                             (* nice new format)
						  T)
						(T (COND
						     ((AND (EQ (CAR INFO)
							       (QUOTE ARGS))
							   (NULLORLISTP (CADR INFO))
							   (FIXP (CADDR INFO)))
                                                             (* Old format)
						       (/RPLACD ERRDEF (create COURIERERR
									       ERR# ←(CADDR INFO)
									       ARGS ←(CADR INFO)))
						       T]
       do (ERROR "Bad Courier Error definition" ERRDEF])

(\COURIER.DELDEF
  [LAMBDA (NAME TYPE)                                        (* bvm: "15-Jun-84 15:34")
    (AND (EQ TYPE (QUOTE COURIERPROGRAMS))
	 (PUTHASH NAME NIL \COURIERPROGRAM])

(\COURIER.GETDEF
  [LAMBDA (NAME TYPE OPTIONS)                                (* bvm: " 4-Jul-84 15:44")
    (AND (EQ TYPE (QUOTE COURIERPROGRAMS))
	 (\GET.COURIERPROGRAM NAME])

(\GET.COURIERPROGRAM
  [LAMBDA (PROGRAM)                                          (* bvm: " 5-May-84 14:17")
    (GETHASH PROGRAM \COURIERPROGRAM])

(\DUMP.COURIERPROGRAMS
  [NLAMBDA NAMES                                             (* bvm: " 4-Jul-84 15:44")
                                                             (* Used by the COURIERPROGRAMS filepkgcom)
    (for PROGRAM in NAMES bind PGMDEF do (COND
					   ((SETQ PGMDEF (\GET.COURIERPROGRAM PROGRAM))
					     (TERPRI)        (* because if you have a really bold font, it lines up 
							     the bottoms, but you can get crowded into the line 
							     above.)
					     (PRIN1 "(COURIERPROGRAM ")
					     (COND
					       ((AND FONTCHANGEFLG PRETTYCOMFONT)
						 (CHANGEFONT PRETTYCOMFONT)))
					     (PRIN2 PROGRAM)
					     (COND
					       ((AND FONTCHANGEFLG PRETTYCOMFONT)
						 (CHANGEFONT DEFAULTFONT)))
					     (SPACES 1)
					     (PRIN2 (CAR PGMDEF))
                                                             (* Version pair)
					     (for TAIL on (CDR PGMDEF) by (CDDR TAIL)
						do (TAB 4)
						   (COND
						     ((AND FONTCHANGEFLG CLISPFONT)
						       (CHANGEFONT PRETTYCOMFONT)))
						   (PRIN2 (CAR TAIL))
						   (COND
						     ((AND FONTCHANGEFLG CLISPFONT)
						       (CHANGEFONT DEFAULTFONT)))
                                                             (* Property name)
						   (TAB 6)
						   (PRINTDEF (CADR TAIL)
							     6))
					     (PRIN1 (QUOTE %)))
					     (TERPRI))
					   (T (LISPXPRINT (APPEND (QUOTE (no COURIER definition for))
								  (LIST PROGRAM))
							  T T])

(\GET.COURIER.TYPE
  [LAMBDA (PROGRAMNAME TYPENAME)                             (* ecc " 7-JUL-83 14:34")
    (CAR (\GET.COURIER.DEFINITION PROGRAMNAME TYPENAME (QUOTE TYPES])

(\GET.COURIER.DEFINITION
  [LAMBDA (PROGRAM NAME TYPE PGMDEF)                         (* bvm: "16-Jul-84 15:35")
    (COND
      ((\COURIER.QUALIFIED.NAMEP NAME)
	(\GET.COURIER.DEFINITION (CAR NAME)
				 (CDR NAME)
				 TYPE))
      (T (OR (CDR (ASSOC NAME (LISTGET [CDR (OR PGMDEF (SETQ PGMDEF (\GET.COURIERPROGRAM PROGRAM]
				       TYPE)))
	     (for OTHERPROGRAM in (LISTGET (CDR (OR PGMDEF (\GET.COURIERPROGRAM PROGRAM)))
					   (QUOTE INHERITS))
		when [SETQ $$VAL (CDR (ASSOC NAME (LISTGET (CDR (\GET.COURIERPROGRAM OTHERPROGRAM))
							   TYPE]
		do                                           (* Is defined in an inherited program)
		   (RETURN $$VAL))
	     (ERROR (CONCAT "No " TYPE " definition for")
		    (LIST PROGRAM NAME])
)
(DECLARE: DOEVAL@COMPILE DONTCOPY

(GLOBALVARS \COURIERPROGRAM)
)
(PUTDEF (QUOTE COURIERPROGRAMS) (QUOTE FILEPKGCOMS) (QUOTE ((COM MACRO (X (E (\DUMP.COURIERPROGRAMS . 
									       X)))
								 CONTENTS NILL)
							    (TYPE DESCRIPTION "Courier programs" 
								  GETDEF \COURIER.GETDEF PUTDEF 
								  \COURIER.PUTDEF DELDEF 
								  \COURIER.DELDEF))))
(DECLARE: EVAL@COMPILE 

(PUTPROPS COURIER.FETCH MACRO (ARGS (\COURIER.RECORDTRAN ARGS (QUOTE FETCH))))

(PUTPROPS COURIER.CREATE MACRO (ARGS (\COURIER.RECORDTRAN ARGS (QUOTE CREATE))))
)

(PUTPROPS COURIER.FETCH INFO NOEVAL)

(PUTPROPS COURIER.CREATE INFO NOEVAL)
(DEFINEQ

(\COURIER.RECORDTRAN
  [LAMBDA (ARGS OP)                                          (* bvm: " 4-Jul-84 15:42")
    (PROG ((PROGRAM (CAR ARGS))
	   (REST (CDR ARGS))
	   TYPEDEF)
          [SETQ TYPEDEF (COND
	      ((NLISTP PROGRAM)
		(\GET.COURIER.TYPE PROGRAM (pop REST)))
	      ((\COURIER.QUALIFIED.NAMEP PROGRAM)
		(SETQ TYPEDEF (CDR PROGRAM))
		(\GET.COURIER.TYPE (SETQ PROGRAM (CAR PROGRAM))
				   TYPEDEF))
	      (T (GO ERROR]
      LP  (COND
	    ((NLISTP TYPEDEF)
	      (SETQ TYPEDEF (\GET.COURIER.TYPE PROGRAM TYPEDEF))
	      (GO LP))
	    [(NEQ (CAR TYPEDEF)
		  (QUOTE RECORD))
	      (COND
		((\COURIER.QUALIFIED.NAMEP TYPEDEF)
		  (SETQ TYPEDEF (\GET.COURIER.TYPE (SETQ PROGRAM (CAR TYPEDEF))
						   (CDR TYPEDEF)))
		  (GO LP))
		(T (GO ERROR]
	    (T (pop TYPEDEF)))
          (RETURN (SELECTQ OP
			   (FETCH                            (* FETCH FIELD of DATUM -
							     DATUM is a list of values, one for each field)
				  (bind (FIELD ←(pop REST))
					(FORM ←(CAR REST))
				     first [SELECTQ FORM
						    [(OF of)
                                                             (* Noise word)
						      [COND
							((AND (EQ FORM (QUOTE OF))
							      LCASEFLG)
							  (/RPLACA REST (QUOTE of]
						      (SETQ FORM (CAR (SETQ REST (CDR REST]
						    (COND
						      ((EQ FORM (QUOTE of))
                                                             (* Noise word)
							(SETQ FORM (CAR (SETQ REST (CDR REST]
					   (COND
					     ((CDR REST)     (* Too many args)
					       (GO ERROR)))
				     while TYPEDEF
				     do [COND
					  ((EQ (CAAR TYPEDEF)
					       FIELD)
					    (RETURN (LIST (QUOTE CAR)
							  FORM]
					(SETQ FORM (LIST (QUOTE CDR)
							 FORM))
					(SETQ TYPEDEF (CDR TYPEDEF))
				     finally (GO ERROR)))
			   [CREATE                           (* CREATE Field1 Value1 ... FieldN ValueN)
				   (CONS (QUOTE LIST)
					 (bind (TAIL ← REST)
					       X while TAIL
					    collect
					     [COND
					       ((NEQ (CAR TAIL)
						     (CAR (pop TYPEDEF)))
                                                             (* Fields not in order)
						 (GO ERROR))
					       (T (PROG1 (COND
							   [(EQ [SETQ X (CAR (SETQ TAIL (CDR TAIL]
								(QUOTE ←))
                                                             (* Noise token)
							     (CAR (SETQ TAIL (CDR TAIL]
							   (T X))
							 (SETQ TAIL (CDR TAIL]
					    finally (COND
						      (TYPEDEF (GO ERROR]
			   (GO ERROR)))
      ERROR
          (ERROR "Invalid Courier Record Access form" (CONS OP ARGS])
)



(* Functions for calling Courier procedures.)

(DEFINEQ

(COURIER.OPEN
  [LAMBDA (HOSTNAME SERVERTYPE NOERRORFLG NAME WHENCLOSEDFN)
                                                             (* bvm: "12-Oct-84 22:35")
                                                             (* Open a Courier connection to the specified host.)
    (RESETLST (PROG (ADDRESS STREAM LOW.VERSION HIGH.VERSION)
		    [COND
		      [[NOT (SETQ ADDRESS (COND
				((type? NSADDRESS HOSTNAME)
				  HOSTNAME)
				(HOSTNAME (LOOKUP.NS.SERVER HOSTNAME SERVERTYPE]
			(RETURN (AND (NOT NOERRORFLG)
				     (ERROR "Unknown host" HOSTNAME]
		      ([NULL (SETQ STREAM (SPP.OPEN ADDRESS \NS.WKS.Courier T NAME
						    (LIST (QUOTE CLOSEFN)
							  (CONS (FUNCTION \COURIER.WHENCLOSED)
								(MKLIST WHENCLOSEDFN]
			(RETURN (AND (NOT NOERRORFLG)
				     (ERROR "Host not responding" HOSTNAME]
		    (RESETSAVE NIL (LIST (FUNCTION \SPP.CLOSE.IF.ERROR)
					 STREAM))
		    (replace ENDOFSTREAMOP of STREAM with (FUNCTION \COURIER.EOF))
		    (SPP.DSTYPE STREAM \SPPDSTYPE.COURIER)
		    [COND
		      (COURIERTRACEFLG (printout COURIERTRACEFILE T "Opened " (OR NAME "")
						 " with "
						 (SPP.DESTADDRESS STREAM]
		    (PUTWORD STREAM COURIER.VERSION#)
		    (PUTWORD STREAM COURIER.VERSION#)
		    (SPP.SENDEOM STREAM)
		    (SETQ LOW.VERSION (GETWORD STREAM))
		    (SETQ HIGH.VERSION (GETWORD STREAM))
		    [COND
		      ((NOT (AND (ILEQ LOW.VERSION COURIER.VERSION#)
				 (ILEQ COURIER.VERSION# HIGH.VERSION)))
			(SPP.CLOSE STREAM)
			(RETURN (AND (NOT NOERRORFLG)
				     (ERROR "Server supports wrong version of Courier"
					    (LIST HOSTNAME LOW.VERSION HIGH.VERSION]
		    (RETURN STREAM])

(\COURIER.WHENCLOSED
  [LAMBDA (STREAM CON)                                       (* bvm: " 4-Jul-84 00:28")
    (COND
      (COURIERTRACEFLG (printout COURIERTRACEFILE .TAB0 0 "Closed with " (SPP.DESTADDRESS STREAM)
				 T])

(COURIER.CALL
  [LAMBDA ARGS                                               (* bvm: "16-Jul-84 15:38")

          (* Call a Courier procedure. -
	  (COURIER.CALL stream program-name procedure-name arg1 ... argN) -
	  Returns the result of the remote procedure, or a list of such results if it returns more than one.
	  A single flag NoErrorFlg can be optionally appended to the arglist -- If NoErrorFlg is NOERROR, return NIL if the 
	  Courier program aborts with an error; if RETURNERRORS, then return an expression (ERROR ERRNAME . args) on error.
	  If the Courier procedure takes a Bulk Data parameter, then the result of COURIER.CALL is a stream for the 
	  transfer. When the stream is closed, the results will be read and the functional argument that was supplied in the
	  call, if any, will be applied to the results.)


    (PROG ((STREAM (ARG ARGS 1))
	   (PROGRAM (ARG ARGS 2))
	   (PROCEDURE (ARG ARGS 3))
	   #ARGS ARGLIST NOERRORFLG PGMDEF PROCDEF ARGTYPES)
          (SETQ PGMDEF (OR (\GET.COURIERPROGRAM PROGRAM)
			   (ERROR "No such Courier program" PROGRAM)))
          (SETQ PROCDEF (\GET.COURIER.DEFINITION PROGRAM PROCEDURE (QUOTE PROCEDURES)
						 PGMDEF))
          [SETQ #ARGS (LENGTH (SETQ ARGTYPES (fetch (COURIERFN ARGS) of PROCDEF]
          (OR (SELECTQ (IDIFFERENCE ARGS #ARGS)
		       (3                                    (* Exactly right)
			  T)
		       (4                                    (* Extra arg is errorflg)
			  (SELECTQ (SETQ NOERRORFLG (ARG ARGS (IPLUS #ARGS 4)))
				   ((NOERROR RETURNERRORS T)
				     T)
				   NIL))
		       NIL)
	      (ERROR "Wrong number of arguments to Courier procedure" (CONS PROGRAM PROCEDURE)))
          (SETQ ARGLIST (for I from 4 to (IPLUS #ARGS 3) collect (ARG ARGS I)))
          (RETURN (COND
		    ((type? STREAM STREAM)
		      (COURIER.EXECUTE.CALL STREAM PROGRAM PGMDEF PROCEDURE PROCDEF ARGLIST ARGTYPES 
					    NOERRORFLG))
		    [(type? NSADDRESS STREAM)                (* Means to make a single call to this address)
		      (RESETLST (PROG ((STREAM (COURIER.OPEN STREAM NIL NOERRORFLG)))
				      (RETURN (COND
						(STREAM (RESETSAVE NIL (LIST (FUNCTION 
									       \SPP.RESETCLOSE)
									     STREAM))
							(COURIER.EXECUTE.CALL STREAM PROGRAM PGMDEF 
									      PROCEDURE PROCDEF 
									      ARGLIST ARGTYPES 
									      NOERRORFLG]
		    ((NULL NOERRORFLG)
		      (\ILLEGAL.ARG STREAM])

(COURIER.EXECUTE.CALL
  [LAMBDA (STREAM PROGRAM PGMDEF PROCEDURE PROCDEF ARGLIST ARGTYPES NOERRORFLG)
                                                             (* bvm: "12-Oct-84 22:36")

          (* Send the arguments for a Courier call to the remote program. Returns NIL if none of the formal parameters are of 
	  type BULK.DATA.SOURCE or BULK.DATA.SINK, otherwise returns a stream for the Bulk Data transfer.)


    (COND
      (COURIERTRACEFLG (\COURIER.TRACE (QUOTE CALL)
				       PROGRAM PROCEDURE ARGLIST)))
    (PROG (SOURCEFLG SINKFLG BULKDATAFN DATASTREAM)
          (SPP.DSTYPE STREAM \SPPDSTYPE.COURIER)
          (PUTWORD STREAM \COURIERMSG.CALL)
          (PUTWORD STREAM 0)                                 (* Transaction ID, ignored for now.)
          (PUTLONG STREAM (fetch (COURIERPGM PROGRAM#) of PGMDEF))
          (PUTWORD STREAM (fetch (COURIERPGM VERSION#) of PGMDEF))
          (PUTWORD STREAM (fetch (COURIERFN FN#) of PROCDEF))
          (for VALUE in ARGLIST as TYPE in ARGTYPES do (SELECTQ TYPE
								(BULK.DATA.SOURCE (SETQ SOURCEFLG T)
										  (SETQ BULKDATAFN 
										    VALUE)
										  (PUTWORD STREAM 1))
								(BULK.DATA.SINK (SETQ SINKFLG T)
										(SETQ BULKDATAFN 
										  VALUE)
										(PUTWORD STREAM 1))
								(COURIER.WRITE STREAM VALUE PROGRAM 
									       TYPE)))
          (SPP.SENDEOM STREAM)
          (CHECK (NOT (AND SOURCEFLG SINKFLG)))
          (RETURN (COND
		    ((AND (OR SOURCEFLG SINKFLG)
			  (SETQ DATASTREAM (\BULK.DATA.STREAM STREAM (COND
								(SINKFLG (QUOTE INPUT))
								(T (QUOTE OUTPUT)))
							      PROGRAM PROCEDURE PGMDEF PROCDEF 
							      NOERRORFLG BULKDATAFN)))
		      (COND
			(BULKDATAFN (\COURIER.HANDLE.BULKDATA DATASTREAM BULKDATAFN NOERRORFLG))
			(T                                   (* Return the stream to caller)
			   DATASTREAM)))
		    (T (\COURIER.RESULTS STREAM PROGRAM PGMDEF PROCEDURE PROCDEF NOERRORFLG])

(\COURIER.RESULTS
  [LAMBDA (STREAM PROGRAM PGMDEF PROCEDURE PROCDEF NOERRORFLG EXPEDITEDFLG)
                                                             (* bvm: "18-Jul-84 11:48")
    (PROG (MSGTYPE RESULT ARGS)
          (SETQ RESULT
	    (SELECTC (SETQ MSGTYPE (GETWORD STREAM))
		     [\COURIERMSG.RETURN (PROG ((RESULTTYPES (fetch (COURIERFN RESULTS) of PROCDEF)))
					       (GETWORD STREAM)
                                                             (* Skip the Transaction ID.)
					       (RETURN (COND
							 ((AND RESULTTYPES (NOT (CDR RESULTTYPES)))
                                                             (* Single-valued procedures return conventionally)
							   (COURIER.READ STREAM PROGRAM (CAR 
										      RESULTTYPES)))
							 (T (for TYPE in RESULTTYPES
							       collect (COURIER.READ STREAM PROGRAM 
										     TYPE]
		     [\COURIERMSG.ABORT
		       (PROG (NUMBER ERRORDEF)
			     (GETWORD STREAM)                (* Skip the Transaction ID.)
			     (SETQ NUMBER (GETWORD STREAM))
			     (RETURN (CONS (QUOTE ERROR)
					   (COND
					     [(SETQ ERRORDEF
						 (find ERR
						    in (OR (fetch (COURIERPGM ERRORS) of PGMDEF)
							   (for OTHER in (fetch (COURIERPGM INHERITS)
									    of PGMDEF)
							      when (SETQ $$VAL (fetch (COURIERPGM
											ERRORS)
										  of (
\GET.COURIERPROGRAM OTHER)))
							      do (RETURN $$VAL)))
						    suchthat (IEQP (fetch (COURIERERR ERR#)
								      of (CDR ERR))
								   NUMBER)))
					       (CONS (CAR ERRORDEF)
						     (for TYPE in (fetch (COURIERERR ARGS)
								     of (CDR ERRORDEF))
							collect (COURIER.READ STREAM PROGRAM TYPE]
					     (T (LIST NUMBER]
		     [\COURIERMSG.REJECT (GETWORD STREAM)    (* Skip the Transaction ID.)
					 (LIST (QUOTE ERROR)
					       (QUOTE REJECT)
					       (COURIER.READ
						 STREAM PROGRAM
						 (QUOTE (CHOICE (noSuchProgramNumber 0)
								(noSuchVersionNumber
								  1
								  (RECORD (lowest CARDINAL)
									  (highest CARDINAL)))
								(noSuchProcedureValue 2)
								(invalidArguments 3)
								(unspecifiedError 65535]
		     (ERROR "Unknown Courier message type" MSGTYPE)))
          (OR EXPEDITEDFLG (SPP.CLEAREOM STREAM))
          (COND
	    (COURIERTRACEFLG (\COURIER.TRACE (QUOTE RETURN)
					     PROGRAM PROCEDURE RESULT)))
          [COND
	    ((NEQ MSGTYPE \COURIERMSG.RETURN)
	      (COND
		((AND EXPEDITEDFLG (EQ (CADDR RESULT)
				       (QUOTE USE.COURIER)))
                                                             (* Special flag on expedited courier call saying to use 
							     regular Courier)
		  (SETQ RESULT (QUOTE USE.COURIER)))
		(T (SELECTQ NOERRORFLG
			    (RETURNERRORS)
			    (NIL (ERROR (CONCAT (COND
						  ((EQ (CAR (SETQ ARGS (CDR RESULT)))
						       (QUOTE REJECT))
						    (SETQ ARGS (CADR ARGS))
						    "Courier rejected call to ")
						  (T [SETQ ARGS (COND
							 ((CDR ARGS)
							   ARGS)
							 (T (CAR ARGS]
						     "Error in Courier procedure "))
						PROGRAM "." PROCEDURE)
					ARGS))
			    (PROGN (\COURIER.HANDLE.ERROR PROGRAM PROCEDURE RESULT)
				   (SETQ RESULT NIL]
          (RETURN RESULT])

(\COURIER.HANDLE.BULKDATA
  [LAMBDA (DATASTREAM BULKDATAFN NOERRORFLG)                 (* bvm: "12-Oct-84 16:10")

          (* * Called when a Courier call has a bulkdata argument. BULKDATAFN is a function to apply to the bulk data stream.
	  If it returns a non-NIL result, that is returned as the value of the Courier call, ignoring the Courier results, if 
	  any. As a special case, a BULKDATAFN of (Program . Type) interprets the bulk data stream as a 
	  "Stream of Program.Type")


    (PROG ([BULKRESULTS (ERSETQ (COND
				  ((AND (LISTP BULKDATAFN)
					(NEQ (CAR BULKDATAFN)
					     (QUOTE LAMBDA)))
                                                             (* Special case, interpret as a type)
				    (COURIER.READ.BULKDATA DATASTREAM (CAR BULKDATAFN)
							   (CDR BULKDATAFN)
							   T))
				  (T (APPLY* BULKDATAFN DATASTREAM]
	   MAINRESULTS)
          [SETQ MAINRESULTS (\BULK.DATA.CLOSE DATASTREAM (AND (NULL BULKRESULTS)
							      (OR NOERRORFLG T]
          (RETURN (OR (CAR BULKRESULTS)
		      MAINRESULTS])

(\COURIER.HANDLE.ERROR
  [LAMBDA (PROGRAM PROCEDURE ERRORARGS)                      (* bvm: "27-Jun-84 23:05")
    (COND
      (NSWIZARDFLG (printout PROMPTWINDOW .TAB0 0 "Error in Courier program " PROGRAM ", procedure " 
			     PROCEDURE ": " ERRORARGS])

(\BULK.DATA.STREAM
  [LAMBDA (STREAM MODE PROGRAM PROCEDURE PGMDEF PROCDEF NOERRORFLG INTERNALFLG)
                                                             (* bvm: "12-Oct-84 17:13")

          (* Return a specialized version of an SPP stream suitable for sending or receiving a Bulk Data object.
	  Uses the Bulk Data device, which redefines the EOFP and CLOSE functions. Save the program, procedure, and result 
	  function in the stream record for use by \BULK.DATA.CLOSE.)


    (PROG ((CON (GETSPPCON STREAM))
	   SUBSTREAM NEXTPKT)
          [COND
	    ((EQ MODE (QUOTE INPUT))                         (* Preview the incoming stream to see if there's any 
							     data there)
	      (COND
		((NOT (SETQ NEXTPKT (\GETSPP CON NIL T)))    (* Connection died)
		  (RETURN NIL))
		((NEQ (fetch (SPPXIP DSTYPE) of NEXTPKT)
		      \SPPDSTYPE.BULKDATA)                   (* Bulkdata not coming, must be error)
		  (RETURN NIL))
		((fetch (SPPXIP ATTENTION) of NEXTPKT)       (* Immediately aborted, must be nothing coming)
		  (\GETSPP CON)                              (* Eat the packet)
		  (RETURN NIL]
          (SETQ SUBSTREAM (OR (fetch SPPSUBSTREAM of CON)
			      (create STREAM
				      DEVICE ← \SPP.BULKDATA.DEVICE)))
          (replace SPP.CONNECTION of SUBSTREAM with CON)
          (replace BULK.DATA.CONTINUATION of SUBSTREAM
	     with (create \BULK.DATA.CONTINUATION
			  PROGRAM ← PROGRAM
			  PROCEDURE ← PROCEDURE
			  PGMDEF ← PGMDEF
			  PROCDEF ← PROCDEF
			  NOERRORFLG ← NOERRORFLG
			  INTERNALFLG ← INTERNALFLG))
          (replace SPPEOFP of SUBSTREAM with NIL)
          (replace ACCESS of SUBSTREAM with MODE)
          (replace SPPSUBSTREAM of CON with SUBSTREAM)
          (replace SPPATTENTIONFN of CON with (FUNCTION \COURIER.ATTENTIONFN))
          (COND
	    (COURIERTRACEFLG (\COURIER.TRACE (QUOTE BEGIN.BULK.DATA)
					     PROGRAM PROCEDURE)))
          (SPP.DSTYPE SUBSTREAM \SPPDSTYPE.BULKDATA)
          (RETURN SUBSTREAM])

(\COURIER.ATTENTIONFN
  [LAMBDA (STREAM BYTE DSTYPE)                               (* bvm: "12-Oct-84 16:16")

          (* * Called when attention packet received on input STREAM. If we are currently writing bulkdata, this is an abort, 
	  so arrange to kill the writer)


    (PROG (CON)
          (RETURN (COND
		    ((AND (EQ BYTE 1)
			  (EQ DSTYPE \SPPDSTYPE.BULKDATA))   (* Bulk data stream truncation signal)
		      (COND
			((AND (SETQ CON (GETSPPCON STREAM))
			      (SETQ STREAM (fetch SPPSUBSTREAM of CON))
			      (WRITEABLE STREAM))
			  (replace SPPOUTPUTABORTEDFN of CON with (FUNCTION \COURIER.OUTPUT.ABORTED))
			  (replace SPPOUTPUTABORTEDP of CON with T)))
		      (COND
			(NSWIZARDFLG (printout PROMPTWINDOW .TAB0 0 
					       "[Remote host aborted data transfer]")))
		      T])

(\COURIER.OUTPUT.ABORTED
  [LAMBDA (OUTSTREAM)                                        (* bvm: "12-Oct-84 17:54")
                                                             (* Called when attempt is made to write data on 
							     OUTSTREAM when output has been aborted)
    (PROG (FILENAME CONTINUATION RESULT)
          (COND
	    [(AND (SETQ CONTINUATION (fetch BULK.DATA.CONTINUATION of OUTSTREAM))
		  (NOT (fetch INTERNALFLG of CONTINUATION)))
                                                             (* This was a standalone bulkdata stream)
	      (SETQ RESULT (\BULK.DATA.CLOSE OUTSTREAM (QUOTE RETURNERRORS)))
	      (COND
		((AND (SETQ FILENAME (fetch FULLFILENAME of OUTSTREAM))
		      (EQ (CADDR RESULT)
			  (QUOTE MediumFull)))
		  (LISPERROR "FILE SYSTEM RESOURCES EXCEEDED" FILENAME))
		(T (ERROR (CONCAT "Output aborted: " (CADR RESULT)
				  " -- "
				  (CADDR RESULT))
			  (OR FILENAME OUTSTREAM]
	    (T                                               (* Inside of \COURIER.HANDLE.BULKDATA)
	       (ERROR!])

(\BULK.DATA.CLOSE
  [LAMBDA (STREAM ABORTFLG)                                  (* bvm: "12-Oct-84 17:54")

          (* Close a Bulk Data stream after the transfer has taken place. If a result function was specified in COURIER.CALL, 
	  call it on the stream and the result or list of results.)


    (PROG ((CON (GETSPPCON STREAM))
	   (CONTINUATION (fetch BULK.DATA.CONTINUATION of STREAM))
	   ABORTFLG)
          (replace SPPATTENTIONFN of CON with NIL)
          (COND
	    ((NULL (fetch SPPSUBSTREAM of CON))              (* This stream has already been closed.
							     We don't want to try to read the Courier results twice)
	      (RETURN)))
          [COND
	    (COURIERTRACEFLG (\COURIER.TRACE (QUOTE END.BULK.DATA)
					     (fetch PROGRAM of CONTINUATION)
					     (fetch PROCEDURE of CONTINUATION]
          (COND
	    [(WRITEABLE STREAM)
	      (COND
		(ABORTFLG (SPP.SENDATTENTION STREAM 1))
		(T (SPP.SENDEOM STREAM]
	    ((NOT (\EOFP STREAM))                            (* Closing before all the data has been read -- abort 
							     the transfer.)
	      (OR ABORTFLG (SETQ ABORTFLG T))
	      (\ABORT.BULK.DATA STREAM)))
          (replace SPPINPKT of CON with NIL)                 (* This stream is closing; make sure there aren't any 
							     dangling pointers into the middle of ether packets.)
          (replace CBUFPTR of STREAM with NIL)
          (replace CBUFSIZE of STREAM with 0)
          (RETURN
	    (CAR (ERSETQ (RESETLST (RESETSAVE NIL (LIST [FUNCTION (LAMBDA (CON)
							    (replace SPPSUBSTREAM of CON
							       with NIL)
							    (COND
							      (RESETSTATE (SPP.CLOSE (fetch 
										   SPPINPUTSTREAM
											of CON)
										     T]
							CON))

          (* The result of the Courier call may be an error which the user should see; however, we still need to clean up the 
	  substream, so we wrap it in this RESETLST.)


				   (PROG ((COURIERSTREAM (fetch SPPINPUTSTREAM of CON)))
				         [COND
					   (ABORTFLG (RESETSAVE
						       NIL
						       (LIST [FUNCTION (LAMBDA (STREAM)
								 (replace ENDOFSTREAMOP of STREAM
								    with (FUNCTION \COURIER.EOF]
							     COURIERSTREAM))
						     (replace ENDOFSTREAMOP of STREAM
							with (FUNCTION ERROR!]
				         (RETURN (\COURIER.RESULTS COURIERSTREAM
								   (fetch PROGRAM of CONTINUATION)
								   (fetch PGMDEF of CONTINUATION)
								   (fetch PROCEDURE of CONTINUATION)
								   (fetch PROCDEF of CONTINUATION)
								   (OR ABORTFLG
								       (fetch NOERRORFLG
									  of CONTINUATION])

(\ABORT.BULK.DATA
  [LAMBDA (STREAM)                                           (* bvm: " 4-Jul-84 00:30")
    (PROG (EPKT)
          (do                                                (* Empty queue of waiting packets without blocking.)
	      (replace COFFSET of STREAM with (fetch CBUFSIZE of STREAM)) repeatwhile (
\SPP.PREPARE.INPUT STREAM 0))
          (COND
	    ((fetch SPPEOFP of STREAM)                       (* We've already received the last packet of the Bulk 
							     Data transfer.)
	      )
	    (T 

          (* Abort the bulk data stream by sending an Attention packet with a 1 in it. WARNING: if the EOM bit is set in 
	  this packet, the NS fileserver will crash.)


	       (SPP.SENDATTENTION STREAM 1)

          (* (while (\SPP.PREPARE.INPUT STREAM SPP.USER.TIMEOUT) do (* Ignore any remaining bulk data packets -- there 
	  shouldn't be many if the other end is obeying the protocol.)))


	       ])
)
(DEFINEQ

(COURIER.EXPEDITED.CALL
  [LAMBDA ARGS                                               (* bvm: "16-Jul-84 15:39")

          (* * Like COURIER.CALL but tries to use "expedited" calls. The first two args are the address and socket# to talk 
	  to, rather than a single open Courier stream. Remaining args are identical. If expedited version fails, a regular 
	  courier call is executed. Bulk data is prohibited)


    (PROG ((ADDRESS (ARG ARGS 1))
	   (SOCKET# (ARG ARGS 2))
	   (PROGRAM (ARG ARGS 3))
	   (PROCEDURE (ARG ARGS 4))
	   #ARGS ARGLIST NOERRORFLG PGMDEF PROCDEF ARGTYPES)
          (SETQ PGMDEF (OR (\GET.COURIERPROGRAM PROGRAM)
			   (ERROR "No such Courier program" PROGRAM)))
          (SETQ PROCDEF (\GET.COURIER.DEFINITION PROGRAM PROCEDURE (QUOTE PROCEDURES)
						 PGMDEF))
          [SETQ #ARGS (LENGTH (SETQ ARGTYPES (fetch (COURIERFN ARGS) of PROCDEF]
          [COND
	    ([for TYPE in ARGTYPES thereis (OR (EQ TYPE (QUOTE BULK.DATA.SINK))
					       (EQ TYPE (QUOTE BULK.DATA.SOURCE]
	      (ERROR "Can't transfer bulk data with expedited call" (CONS PROGRAM PROCEDURE]
          (OR (SELECTQ (IDIFFERENCE ARGS #ARGS)
		       (4                                    (* Exactly right)
			  T)
		       (5                                    (* Extra arg is errorflg)
			  (SELECTQ (SETQ NOERRORFLG (ARG ARGS (IPLUS #ARGS 5)))
				   ((NOERROR RETURNERRORS T)
				     T)
				   NIL))
		       NIL)
	      (ERROR "Wrong number of arguments to Courier procedure" (CONS PROGRAM PROCEDURE)))
          (SETQ ARGLIST (for I from 5 to (IPLUS #ARGS 4) collect (ARG ARGS I)))
          (RETURN (COURIER.EXECUTE.EXPEDITED.CALL ADDRESS SOCKET# PROGRAM PGMDEF PROCEDURE PROCDEF 
						  ARGLIST ARGTYPES NOERRORFLG])

(COURIER.EXECUTE.EXPEDITED.CALL
  [LAMBDA (ADDRESS SOCKET# PROGRAM PGMDEF PROCEDURE PROCDEF ARGLIST ARGTYPES NOERRORFLG)
                                                             (* bvm: "27-Jun-84 12:59")

          (* * Attempts the actual expedited call)


    (COND
      (COURIERTRACEFLG (\COURIER.TRACE (QUOTE CALL)
				       PROGRAM PROCEDURE ARGLIST)))
    (RESETLST (PROG ((NSOC (OPENNSOCKET))
		     XIP STREAM RESULT)
		    (RESETSAVE NIL (LIST (QUOTE CLOSENSOCKET)
					 NSOC))
		    (SETQ XIP (CREATE.PACKET.EXCHANGE.XIP NSOC ADDRESS SOCKET# 
							  \EXTYPE.EXPEDITED.COURIER))
		    (OR (\BUILD.EXPEDITED.XIP XIP PROGRAM PGMDEF PROCDEF ARGLIST ARGTYPES)
			(GO USECOURIER))
		    (COND
		      ((NEQ (SETQ RESULT (\SEND.EXPEDITED.XIP XIP NSOC PROGRAM PGMDEF PROCEDURE 
							      PROCDEF NOERRORFLG))
			    (QUOTE USE.COURIER))
			(RETURN RESULT)))
		USECOURIER
		    (RETURN (COND
			      ((SETQ STREAM (COURIER.OPEN ADDRESS NIL NOERRORFLG (QUOTE COURIER)))
                                                             (* Use regular courier)
				(RESETSAVE NIL (LIST (FUNCTION \SPP.RESETCLOSE)
						     STREAM))
				(COURIER.EXECUTE.CALL STREAM PROGRAM PGMDEF PROCEDURE PROCDEF ARGLIST 
						      ARGTYPES NOERRORFLG])

(\BUILD.EXPEDITED.XIP
  [LAMBDA (XIP PROGRAM PGMDEF PROCDEF ARGLIST)               (* bvm: " 4-Jul-84 15:41")
    (PROG (STREAM)
          (replace (EXPEDITEDXIP LOWVERSION) of XIP with (replace (EXPEDITEDXIP HIGHVERSION)
							    of XIP with (COURIER.VERSION#)))
          (replace (EXPEDITEDXIP MSGTYPE) of XIP with \COURIERMSG.CALL)
          (replace (EXPEDITEDXIP TRANSACTIONID) of XIP with 0)
                                                             (* Transaction ID, ignored for now.)
          (replace (EXPEDITEDXIP PROGRAM#) of XIP with (fetch (COURIERPGM PROGRAM#) of PGMDEF))
          (replace (EXPEDITEDXIP VERSION#) of XIP with (fetch (COURIERPGM VERSION#) of PGMDEF))
          (replace (EXPEDITEDXIP PROCEDURE#) of XIP with (fetch (COURIERFN FN#) of PROCDEF))
          [replace XIPLENGTH of XIP with (COND
					   (ARGLIST (SETQ STREAM (\MAKE.EXPEDITED.STREAM
							XIP
							(QUOTE OUTPUT)))
						    (OR (\COURIER.EXPEDITED.ARGS STREAM PROGRAM 
										 ARGLIST
										 (fetch (COURIERFN
											  ARGS)
										    of PROCDEF))
							(RETURN))
						    (fetch COFFSET of STREAM))
					   (T (IPLUS \XIPOVLEN
						     (UNFOLD (IPLUS 3 (INDEXF (fetch (EXPEDITEDXIP
										       ARG0)
										 of T)))
							     BYTESPERWORD]
          (RETURN XIP])

(\SEND.EXPEDITED.XIP
  [LAMBDA (XIP NSOC PROGRAM PGMDEF PROCEDURE PROCDEF NOERRORFLG #TRIES)
                                                             (* bvm: " 4-Jul-84 15:40")

          (* * Sends XIP, which is in the form of an expedited courier call, and awaits a response on NSOC.
	  The call is to PROCEDURE of PROGRAM. If there is no response, or the remote element responds with the USE.COURIER 
	  error, returns USE.COURIER else the actual result (which could be NIL))


    (bind (TIMER ←(SETUPTIMER 0))
	  (EVENT ←(NSOCKETEVENT NSOC))
	  (ID ←(fetch PACKETEXCHANGEID of XIP))
	  IXIP to (OR #TRIES \MAXETHERTRIES)
       do (SENDXIP NSOC XIP)
	  (SETUPTIMER \ETHERTIMEOUT TIMER)
	  [COND
	    ((until (TIMEREXPIRED? TIMER) when (PROGN (AWAIT.EVENT EVENT TIMER T)
						      (SETQ IXIP (GETXIP NSOC)))
		do (SELECTC (fetch XIPTYPE of IXIP)
			    [\XIPT.EXCHANGE (COND
					      ((AND (IEQP (fetch PACKETEXCHANGEID of IXIP)
							  ID)
						    (ILEQ (fetch (EXPEDITEDXIP LOWVERSION)
							     of IXIP)
							  (COURIER.VERSION#))
						    (IGEQ (fetch (EXPEDITEDXIP HIGHVERSION)
							     of IXIP)
							  (COURIER.VERSION#))
						    (SELECTC (fetch (EXPEDITEDXIP MSGTYPE)
								of IXIP)
							     ((LIST \COURIERMSG.RETURN 
								    \COURIERMSG.REJECT 
								    \COURIERMSG.ABORT)
							       T)
							     NIL))
						(RETURN T]
			    [\XIPT.ERROR (COND
					   ((EQ (fetch ERRORXIPCODE of IXIP)
						\XIPE.NOSOCKET)
                                                             (* Not responding to calls on this socket)
					     (RELEASE.XIP IXIP)
					     (RETURN (QUOTE USE.COURIER]
			    NIL)
		   (RELEASE.XIP IXIP))
	      (RETURN (PROG1 (\COURIER.RESULTS (\MAKE.EXPEDITED.STREAM IXIP (QUOTE INPUT))
					       PROGRAM PGMDEF PROCEDURE PROCDEF NOERRORFLG T)
			     (RELEASE.XIP IXIP]
       finally (RETURN (QUOTE USE.COURIER])

(\COURIER.EXPEDITED.ARGS
  [LAMBDA (STREAM PROGRAM ARGLIST ARGTYPES)                  (* bvm: "15-Jun-84 12:00")

          (* * Store the args for an expedited call into packet addressed by STREAM. Returns T on success.
	  Failure is indicated by a RETFROM this fn with value NIL)


    (for VALUE in ARGLIST as TYPE in ARGTYPES do (COURIER.WRITE STREAM VALUE PROGRAM TYPE))
    T])

(\MAKE.EXPEDITED.STREAM
  [LAMBDA (XIP ACCESS OSTREAM)                               (* bvm: "27-Jun-84 11:07")

          (* * Makes a STREAM to access the contents of XIP as an expedited courier message body. We use the BASEBYTES 
	  device for simplicity. All the operations we actually need are BIN, BOUT, BLOCKIN and BLOCKOUT)


    (PROG ([STREAM (OR OSTREAM (NCREATE (QUOTE STREAM]
	   END)
          (replace DEVICE of STREAM with \BASEBYTESDEVICE)
          (replace ACCESS of STREAM with ACCESS)
          (replace CBUFPTR of STREAM with (fetch (XIP XIPBASE) of XIP))
          [replace COFFSET of STREAM with (IPLUS \XIPOVLEN (UNFOLD 3 BYTESPERWORD)
						 (COND
						   ((EQ ACCESS (QUOTE INPUT))
                                                             (* For COURIER.RESULTS)
						     (SETQ END (fetch XIPLENGTH of XIP))
						     (UNFOLD (INDEXF (fetch (EXPEDITEDXIP MSGTYPE)
									of T))
							     BYTESPERWORD))
						   (T        (* For COURIER.EXPEDITED.ARGS)
						      (SETQ END (IPLUS \MAX.XIPDATALENGTH \XIPOVLEN))
						      (UNFOLD (INDEXF (fetch (EXPEDITEDXIP ARG0)
									 of T))
							      BYTESPERWORD]
          (replace EOFFSET of STREAM with (replace CBUFSIZE of STREAM with END))
          [COND
	    ((EQ ACCESS (QUOTE INPUT))                       (* Will cause error if COURIER.RESULTS tries to read 
							     more than was sent -- should never happen)
	      (replace ENDOFSTREAMOP of STREAM with (FUNCTION \COURIER.EOF)))
	    (T                                               (* Invoked if COURIER.EXPEDITED.ARGS tries to write more
							     than will fit in the packet)
	       (replace (BASEBYTESTREAM WRITEXTENSIONFN) of STREAM with (FUNCTION 
								      \COURIER.EXPEDITED.OVERFLOW]
          (RETURN STREAM])

(\COURIER.EOF
  [LAMBDA (STREAM)                                           (* bvm: "15-Jun-84 11:56")

          (* * Called if we attempt to read beyond the end of a courier response)


    (ERROR "Unexpected end of stream while reading Courier response"])

(\COURIER.EXPEDITED.OVERFLOW
  [LAMBDA (STREAM)                                           (* bvm: " 4-Jul-84 15:41")

          (* * Called when \COURIER.EXPEDITED.ARGS tries to write beyond the end of the packet)


    (COND
      (NSWIZARDFLG (printout PROMPTWINDOW T "[Expedited call did not fit in one packet]")))
    (RETFROM (FUNCTION \COURIER.EXPEDITED.ARGS)
	     NIL])
)
(DEFINEQ

(COURIER.BROADCAST.CALL
  [LAMBDA (DESTSOCKET# PROGRAM PROCEDURE ARGS RESULTFN NETHINT MESSAGE)
                                                             (* bvm: "16-Jul-84 15:39")

          (* Performs expanding ring broadcast for Courier PROCEDURE applied to ARGS. If RESULTFN is given, it is applied to
	  the results of the courier call, and its result is returned, unless it is NIL, in which case the broadcast 
	  continues. NETHINT is a net or list of nets that are expected to have the desired server. If omitted, or if no 
	  server on those nets responds, broadcast starts with the connected net and expands outward)


    (RESETLST (PROG ((PGMDEF (OR (\GET.COURIERPROGRAM PROGRAM)
				 (ERROR "No such Courier program" PROGRAM)))
		     PROCDEF SKT EPKT ROUTINGTABLE RESULT NEARBYNETS)
		    (DECLARE (SPECVARS NEARBYNETS))          (* For \MAP.ROUTING.TABLE)
		    (SETQ PROCDEF (\GET.COURIER.DEFINITION PROGRAM PROCEDURE (QUOTE PROCEDURES)
							   PGMDEF))
		    [RESETSAVE NIL (LIST (QUOTE CLOSENSOCKET)
					 (SETQ SKT (OPENNSOCKET]
		    (SETQ EPKT (CREATE.PACKET.EXCHANGE.XIP SKT BROADCASTNSHOSTNUMBER DESTSOCKET# 
							   \EXTYPE.EXPEDITED.COURIER))
		    (OR (\BUILD.EXPEDITED.XIP EPKT PROGRAM PGMDEF PROCDEF ARGS)
			(ERROR "Could not build broadcast for servers packet" (CONS PROGRAM PROCEDURE)
			       ))
		    [COND
		      ([COND
			  ((NOT NETHINT)
			    NIL)
			  ((FIXP NETHINT)
			    (SETQ RESULT (\COURIER.BROADCAST.ON.NET NETHINT SKT EPKT PROGRAM PGMDEF 
								    PROCEDURE PROCDEF RESULTFN 
								    MESSAGE)))
			  ((LISTP NETHINT)
			    (for NET in NETHINT
			       thereis (SETQ RESULT
					 (\COURIER.BROADCAST.ON.NET NET SKT EPKT PROGRAM PGMDEF 
								    PROCEDURE PROCDEF RESULTFN 
								    MESSAGE]
                                                             (* Found server on hinted net)
			)
		      (T (SETQ NEARBYNETS (CONS))
			 [\MAP.ROUTING.TABLE \NS.ROUTING.TABLE
					     (FUNCTION (LAMBDA (RT)
                                                             (* Gather up info about what nets are nearby in order of
							     hop count)
						 (PROG ((HOPS (fetch (ROUTING RTHOPCOUNT)
								 of RT)))
						       (COND
							 ((ILEQ HOPS 5)
							   (for (TAIL ← NEARBYNETS)
							      while
							       (AND (CDR TAIL)
								    (ILESSP (CAR (CADR TAIL))
									    HOPS))
							      do (SETQ TAIL (CDR TAIL))
							      finally
							       (push (CDR TAIL)
								     (LIST HOPS (fetch (ROUTING
											 RTNET#)
										   of RT]
			 [COND
			   ((OR (NULL (CDR NEARBYNETS))
				(NEQ (CAR (CADR NEARBYNETS))
				     0))                     (* Include local net)
			     (push (CDR NEARBYNETS)
				   (LIST 0 0]
			 (COND
			   ([NOT (find PAIR in (CDR NEARBYNETS) suchthat (SETQ RESULT
									   (\COURIER.BROADCAST.ON.NET
									     (CADR PAIR)
									     SKT EPKT PROGRAM PGMDEF 
									     PROCEDURE PROCDEF 
									     RESULTFN MESSAGE]

          (* Try once more, just in case we didn't wait long enough on the last guy. The previous tries overlapped each 
	  other, and we need to wait a bit to give the last one equal time)


			     (SETQ RESULT (\COURIER.BROADCAST.ON.NET (CADR (CADR NEARBYNETS))
								     SKT EPKT PROGRAM PGMDEF 
								     PROCEDURE PROCDEF RESULTFN]
		    (RETURN RESULT])

(\COURIER.BROADCAST.ON.NET
  [LAMBDA (NET NSOC XIP PROGRAM PGMDEF PROCEDURE PROCDEF RESULTFN MESSAGE)
                                                             (* bvm: "27-Jun-84 14:38")
    (replace XIPDESTNET of XIP with NET)
    (COND
      (MESSAGE (printout PROMPTWINDOW .TAB0 0 "[Looking for " MESSAGE " on net " .I0.8 NET "]")))
    (PROG ((RESULT (\SEND.EXPEDITED.XIP XIP NSOC PROGRAM PGMDEF PROCEDURE PROCDEF T 2)))
          (RETURN (COND
		    ((EQ RESULT (QUOTE USE.COURIER))
		      NIL)
		    (RESULTFN (APPLY* RESULTFN RESULT))
		    (T RESULT])
)
(DEFINEQ

(COURIER.READ
  [LAMBDA (STREAM PROGRAM TYPE)                              (* bvm: "19-Jul-84 13:03")
    (PROG (X)
          (RETURN (COND
		    [(LITATOM TYPE)
		      (SELECTQ TYPE
			       (BOOLEAN (NEQ 0 (GETWORD STREAM)))
			       ((CARDINAL UNSPECIFIED)
				 (GETWORD STREAM))
			       (INTEGER (SIGNED (GETWORD STREAM)
						BITSPERWORD))
			       ((LONGCARDINAL LONGINTEGER)
				 (GETLONG STREAM))
			       (STRING (COURIER.READ.STRING STREAM))
			       (TIME (ALTO.TO.LISP.DATE (GETLONG STREAM)))
			       (COND
				 ((SETQ X (GETPROP TYPE (QUOTE COURIERDEF)))
                                                             (* User-defined type)
				   (APPLY* (CAR X)
					   STREAM PROGRAM TYPE))
				 ((SETQ X (\GET.COURIER.TYPE PROGRAM TYPE))
				   (COURIER.READ STREAM PROGRAM X))
				 (T (\COURIER.UNKNOWN.TYPE PROGRAM TYPE]
		    [(AND (LISTP TYPE)
			  (LITATOM (CAR TYPE)))
		      (SELECTQ (CAR TYPE)
			       (ENUMERATION (bind (ITEM ←(GETWORD STREAM)) for DEF
					       in (CDR TYPE) do [COND
								  ((IEQP ITEM (CADR DEF))
								    (RETURN (CAR DEF]
					       finally (RETURN ITEM)))
			       (ARRAY (bind (BASETYPE ←(CADDR TYPE)) to (CADR TYPE)
					 collect (COURIER.READ STREAM PROGRAM BASETYPE)))
			       [SEQUENCE                     (* We ignore the maximum length of the sequence.)
					 (COURIER.READ.SEQUENCE STREAM PROGRAM (OR (CADDR TYPE)
										   (CADR TYPE]
			       [RECORD (for NAMEANDTYPE in (CDR TYPE) collect (COURIER.READ
										STREAM PROGRAM
										(CADR NAMEANDTYPE]
			       [NAMEDRECORD                  (* Expanded form for backward compatibility)
					    (for NAMEANDTYPE in (CDR TYPE)
					       collect (LIST (CAR NAMEANDTYPE)
							     (COURIER.READ STREAM PROGRAM
									   (CADR NAMEANDTYPE]
			       [CHOICE (bind (WHICH ←(GETWORD STREAM)) for DEF in (CDR TYPE)
					  do                 (* DEF = (tag choice# type); type = NIL is shorthand for
							     type null record)
					     [COND
					       ((IEQP WHICH (CADR DEF))
						 (RETURN (CONS (CAR DEF)
							       (AND (CADDR DEF)
								    (LIST (COURIER.READ STREAM 
											PROGRAM
											(CADDR DEF]
					  finally (RETURN (LIST WHICH (QUOTE ???]
			       (COND
				 ((LITATOM (CDR TYPE))       (* Qualified name)
				   (COURIER.READ STREAM (CAR TYPE)
						 (CDR TYPE)))
				 ((SETQ X (GETPROP (CAR TYPE)
						   (QUOTE COURIERDEF)))
				   (APPLY* (CAR X)
					   STREAM PROGRAM TYPE))
				 (T (\COURIER.UNKNOWN.TYPE PROGRAM TYPE]
		    (T (\COURIER.UNKNOWN.TYPE PROGRAM TYPE])

(\COURIER.UNKNOWN.TYPE
  [LAMBDA (PROGRAM TYPE)                                     (* bvm: "27-Jun-84 15:36")
    (ERROR "Unknown Courier Type" (COND
	     (PROGRAM (CONS PROGRAM TYPE))
	     (T TYPE])

(COURIER.READ.SEQUENCE
  [LAMBDA (STREAM PROGRAM BASETYPE)                          (* bvm: "27-Jun-84 15:16")

          (* * Reads a Courier SEQUENCE, returning it as a list of objects of type BASETYPE)


    (to (GETWORD STREAM) collect (COURIER.READ STREAM PROGRAM BASETYPE])

(COURIER.READ.STRING
  [LAMBDA (STREAM)                                           (* bvm: " 1-May-84 12:25")
    (PROG ((LENGTH (GETWORD STREAM))
	   STRING)
          (SETQ STRING (ALLOCSTRING LENGTH))
          (\BINS STREAM (fetch (STRINGP BASE) of STRING)
		 (fetch (STRINGP OFFST) of STRING)
		 LENGTH)
          (COND
	    ((ODDP LENGTH)
	      (BIN STREAM)))
          (RETURN STRING])

(COURIER.WRITE
  [LAMBDA (STREAM ITEM PROGRAM TYPE)                         (* bvm: "21-Jul-84 16:07")
    (PROG (X)
          (COND
	    [(LITATOM TYPE)
	      (SELECTQ TYPE
		       [BOOLEAN (PUTWORD STREAM (COND
					   (ITEM 1)
					   (T 0]
		       ((CARDINAL UNSPECIFIED)
			 (PUTWORD STREAM ITEM))
		       (INTEGER (PUTWORD STREAM (UNSIGNED ITEM BITSPERWORD)))
		       ((LONGCARDINAL LONGINTEGER)
			 (PUTLONG STREAM ITEM))
		       (STRING (COURIER.WRITE.STRING STREAM ITEM))
		       (TIME (PUTLONG STREAM (LISP.TO.ALTO.DATE ITEM)))
		       (COND
			 ((SETQ X (GETPROP TYPE (QUOTE COURIERDEF)))
                                                             (* User-defined type)
			   (APPLY* (CADR X)
				   STREAM ITEM PROGRAM TYPE))
			 ((SETQ X (\GET.COURIER.TYPE PROGRAM TYPE))
			   (COURIER.WRITE STREAM ITEM PROGRAM X))
			 (T (\COURIER.UNKNOWN.TYPE PROGRAM TYPE]
	    [(AND (LISTP TYPE)
		  (LITATOM (CAR TYPE)))
	      (SELECTQ (CAR TYPE)
		       [ENUMERATION                          (* Keys can be either atoms, for fast lookup, or 
							     strings, to save atom space)
				    (PUTWORD STREAM (OR [CADR (OR (ASSOC ITEM (CDR TYPE))
								  (find X in (CDR TYPE)
								     bind (KEY ←(MKSTRING ITEM))
								     suchthat (STREQUAL KEY
											(CAR X]
							(\COURIER.TYPE.ERROR ITEM (QUOTE ENUMERATION]
		       [ARRAY (PROG ((SIZE (CADR TYPE))
				     (BASETYPE (CADDR TYPE)))
				    (COND
				      ((NOT (IEQP SIZE (LENGTH ITEM)))
					(\COURIER.TYPE.ERROR ITEM TYPE)))
				    (for X in ITEM do (COURIER.WRITE STREAM X PROGRAM BASETYPE]
		       [SEQUENCE                             (* We ignore the maximum length of the sequence.)
				 (COURIER.WRITE.SEQUENCE STREAM ITEM PROGRAM (OR (CADDR TYPE)
										 (CADR TYPE]
		       [RECORD (for NAMEANDTYPE in (CDR TYPE) as VALUE in ITEM
				  do (COURIER.WRITE STREAM VALUE PROGRAM (CADR NAMEANDTYPE]
		       [NAMEDRECORD                          (* Old style)
				    (for NAMEANDTYPE in (CDR TYPE) as NAMEANDVALUE in ITEM
				       do [COND
					    ((NEQ (CAR NAMEANDTYPE)
						  (CAR NAMEANDVALUE))
					      (\COURIER.TYPE.ERROR ITEM (CAR TYPE]
					  (COURIER.WRITE STREAM (CADR NAMEANDVALUE)
							 PROGRAM
							 (CADR NAMEANDTYPE]
		       [CHOICE (PROG [(WHICH (OR (ASSOC (CAR ITEM)
							(CDR TYPE))
						 (\COURIER.TYPE.ERROR ITEM (QUOTE CHOICE]
				     (PUTWORD STREAM (CADR WHICH))
				     (COND
				       ((CADDR WHICH)
					 (COURIER.WRITE STREAM (CADR ITEM)
							PROGRAM
							(CADDR WHICH]
		       (COND
			 ((LITATOM (CDR TYPE))               (* Qualified name)
			   (COURIER.WRITE STREAM ITEM (CAR TYPE)
					  (CDR TYPE)))
			 ((SETQ X (GETPROP (CAR TYPE)
					   (QUOTE COURIERDEF)))
                                                             (* User-defined type)
			   (APPLY* (CADR X)
				   STREAM ITEM PROGRAM TYPE))
			 (T (\COURIER.UNKNOWN.TYPE PROGRAM TYPE]
	    (T (\COURIER.UNKNOWN.TYPE PROGRAM TYPE])

(COURIER.WRITE.SEQUENCE
  [LAMBDA (STREAM ITEMLIST PROGRAM TYPE)                     (* bvm: " 4-Jul-84 15:13")
    (PROG ((BASETYPE TYPE))
          (COND
	    [(EQ (CAR (LISTP ITEMLIST))
		 (QUOTE INTERPRETATION))                     (* This is how to write a (SEQUENCE UNSPECIFIED) without
							     running it through COURIER.WRITE.REP first.
							     ITEMLIST = (INTERPRETATION type value))
	      (COND
		((NEQ BASETYPE (QUOTE UNSPECIFIED))
		  (\COURIER.TYPE.ERROR ITEMLIST TYPE))
		(T (SETQ BASETYPE (CADR ITEMLIST))
		   (COURIER.WRITE.SEQUENCE.UNSPECIFIED STREAM (CADDR ITEMLIST)
						       (COND
							 [(LISTP BASETYPE)
							   (PROG1 (CAR BASETYPE)
								  (SETQ BASETYPE (CDR BASETYPE]
							 (T PROGRAM))
						       BASETYPE]
	    ((NULL ITEMLIST)
	      (PUTWORD STREAM 0))
	    ((LISTP ITEMLIST)
	      (PUTWORD STREAM (LENGTH ITEMLIST))
	      (for X in ITEMLIST do (COURIER.WRITE STREAM X PROGRAM BASETYPE)))
	    (T (\COURIER.TYPE.ERROR ITEMLIST TYPE])

(COURIER.WRITE.STRING
  [LAMBDA (STREAM STRING)                                    (* bvm: "12-Jun-84 11:23")
    (PROG [(LENGTH (NCHARS (OR (STRINGP STRING)
			       (SETQ STRING (MKSTRING STRING]
          (PUTWORD STREAM LENGTH)
          (\BOUTS STREAM (fetch (STRINGP BASE) of STRING)
		  (fetch (STRINGP OFFST) of STRING)
		  LENGTH)
          (COND
	    ((ODDP LENGTH)
	      (BOUT STREAM 0])

(\COURIER.TYPE.ERROR
  [LAMBDA (ITEM TYPE)                                        (* bvm: " 3-Jul-84 17:53")
    (ERROR (CONCAT "Arg not of Courier type " TYPE)
	   ITEM])
)
(DEFINEQ

(COURIER.READ.BULKDATA
  [LAMBDA (STREAM PROGRAM TYPE DONTCLOSE)                    (* bvm: "27-Jun-84 15:17")

          (* Read a Bulk Data object which is a stream of the specified type. This can be done by declaring the stream type 
	  in Courier, as is done in the protocol specs, but that causes COURIER.READ to produce a deeply nested structure.
	  Instead, this function returns a list of objects making up the stream. See the Bulk Data Transfer spec.)



          (* * Closes STREAM on exit unless DONTCLOSE is true. If STREAM is not a stream, returns it directly, presumably an
	  error from COURIER.CALL)


    (COND
      [(type? STREAM STREAM)
	(PROG1 (bind LASTSEGMENT? join (PROGN [SETQ LASTSEGMENT? (NOT (ZEROP (GETWORD STREAM]
					      (COURIER.READ.SEQUENCE STREAM PROGRAM TYPE))
		  repeatuntil LASTSEGMENT?)
	       (OR DONTCLOSE (CLOSEF STREAM]
      (T                                                     (* An error return from COURIER.CALL -- pass it thru)
	 STREAM])

(BULKDATA.GENERATOR
  [LAMBDA (STREAM PROGRAM TYPE)                              (* bvm: "19-Jul-84 11:40")

          (* Produces a generator for reading from STREAM a Courier "Stream of PROGRAM.TYPE". The value returned from this 
	  function is an object to pass to BULKDATA.GENERATE.NEXT to retrieve the next item from the stream.)


    (create BULKDATAGENERATOR
	    BGSTREAM ← STREAM
	    BGPROGRAM ← PROGRAM
	    BGTYPE ← TYPE
	    BGLASTSEGMENT? ← NIL
	    BGITEMSLEFT ← 0])

(BULKDATA.GENERATE.NEXT
  [LAMBDA (GENSTATE)                                         (* bvm: "19-Jul-84 11:34")
                                                             (* Returns the next item from bulkdata generator 
							     GENSTATE, updating the state.
							     Returns NIL when generator exhausted)
    (PROG ((STREAM (fetch BGSTREAM of GENSTATE))
	   (CNT (fetch BGITEMSLEFT of GENSTATE)))
      LP  (COND
	    ((NEQ CNT 0)                                     (* Middle of a segment)
	      (replace BGITEMSLEFT of GENSTATE with (SUB1 CNT)))
	    ((fetch BGLASTSEGMENT? of GENSTATE)              (* Finished last segment)
	      (RETURN NIL))
	    (T                                               (* Finished a segment, get the next)
	       (COND
		 ((NEQ (GETWORD STREAM)
		       0)
		   (replace BGLASTSEGMENT? of GENSTATE with T)))
	       (SETQ CNT (GETWORD STREAM))
	       (GO LP)))
          (RETURN (COURIER.READ STREAM (fetch BGPROGRAM of GENSTATE)
				(fetch BGTYPE of GENSTATE])

(COURIER.WRITE.BULKDATA
  [LAMBDA (STREAM ITEMLIST PROGRAM TYPE)                     (* bvm: " 4-Jul-84 15:24")

          (* Writes ITEMLIST as a Bulk Data object which is a stream of the specified type, i.e., ITEMLIST is interpreted as
	  a list of (PROGRAM . TYPE) objects. Returns NIL)



          (* * Format a little strange: a succession of SEQUENCE's, the last of which is flagged as the final sequence.
	  In theory, one could send the entire list, up to 65535 items, as a single sequence, but maybe that overloads some 
	  processors, so break it up into smaller chunks)


    (PROG ((LEN (LENGTH ITEMLIST))
	   (TAIL ITEMLIST)
	   SEGMENTLENGTH)
          (do (PUTWORD STREAM (COND
			 ((IGREATERP LEN 100)                (* Don't try to write too long segments)
			   (SETQ SEGMENTLENGTH 100)          (* Not last segment)
			   0)
			 (T (SETQ SEGMENTLENGTH LEN)
			    1)))
	      (PUTWORD STREAM SEGMENTLENGTH)
	      (to SEGMENTLENGTH do (COURIER.WRITE STREAM (pop TAIL)
						  PROGRAM TYPE))
	      (SETQ LEN (IDIFFERENCE LEN SEGMENTLENGTH)) repeatwhile TAIL])
)
(DEFINEQ

(COURIER.READ.REP
  [LAMBDA (LIST.OF.WORDS PROGRAM TEMPLATE)                   (* bvm: "14-Jun-84 15:08")
                                                             (* Like COURIER.READ but "reads" from a list of integers
							     corresponding to the words in the Courier 
							     representation.)
    (COURIER.READ (\MAKE.COURIER.REP.STREAM LIST.OF.WORDS)
		  PROGRAM TEMPLATE])

(COURIER.WRITE.REP
  [LAMBDA (VALUE PROGRAM TYPE)                               (* bvm: "14-Jun-84 16:15")
    (PROG ((STREAM (\MAKE.COURIER.REP.STREAM)))
          (COURIER.WRITE STREAM VALUE PROGRAM TYPE)
          (COND
	    ((fetch CRNEXTBYTE of STREAM)
	      (\BOUT STREAM 0)))
          (RETURN (fetch CRWORDLIST of STREAM])

(COURIER.WRITE.SEQUENCE.UNSPECIFIED
  [LAMBDA (STREAM ITEM PROGRAM TYPE)                         (* bvm: "21-Jul-84 16:07")

          (* * Write ITEM on STREAM as a (SEQUENCE UNSPECIFIED) interpreted as a (PROGRAM . TYPE); this means figuring out 
	  how long ITEM is so we can write the appropriate word count before sending ITEM)


    (PROG (X FN)
          (COND
	    [(LITATOM TYPE)
	      (SELECTQ TYPE
		       [BOOLEAN (PUTWORD STREAM 1)
				(PUTWORD STREAM (COND
					   (ITEM 1)
					   (T 0]
		       ((CARDINAL UNSPECIFIED)
			 (PUTWORD STREAM 1)
			 (PUTWORD STREAM ITEM))
		       (INTEGER (PUTWORD STREAM 1)
				(PUTWORD STREAM (UNSIGNED ITEM BITSPERWORD)))
		       ((LONGCARDINAL LONGINTEGER)
			 (PUTWORD STREAM 2)
			 (PUTLONG STREAM ITEM))
		       [STRING (PROG [(LENGTH (NCHARS (OR (STRINGP ITEM)
							  (SETQ ITEM (MKSTRING ITEM]
				     (PUTWORD STREAM (ADD1 (FOLDHI LENGTH BYTESPERWORD)))
				     (PUTWORD STREAM LENGTH)
				     (\BOUTS STREAM (fetch (STRINGP BASE) of ITEM)
					     (fetch (STRINGP OFFST) of ITEM)
					     LENGTH)
				     (COND
				       ((ODDP LENGTH)
					 (BOUT STREAM 0]
		       (TIME (PUTWORD STREAM 2)
			     (PUTLONG STREAM (LISP.TO.ALTO.DATE ITEM)))
		       (COND
			 ((SETQ X (GETPROP TYPE (QUOTE COURIERDEF)))
                                                             (* User-defined type)
			   (GO USERTYPE))
			 ((SETQ X (\GET.COURIER.TYPE PROGRAM TYPE))
			   (COURIER.WRITE.SEQUENCE.UNSPECIFIED STREAM ITEM PROGRAM X))
			 (T (\CWSU.DEFAULT STREAM ITEM PROGRAM TYPE]
	    [(AND (LISTP TYPE)
		  (LITATOM (CAR TYPE)))
	      (SELECTQ (CAR TYPE)
		       (ENUMERATION (PUTWORD STREAM 1)
				    (COURIER.WRITE STREAM ITEM PROGRAM TYPE))
		       [(ARRAY SEQUENCE RECORD NAMEDRECORD CHOICE)
			 (PROG ((LENGTH (COURIER.REP.LENGTH ITEM PROGRAM TYPE)))
			       (COND
				 (LENGTH (PUTWORD STREAM LENGTH)
					 (COURIER.WRITE STREAM ITEM PROGRAM TYPE))
				 (T (\CWSU.DEFAULT STREAM ITEM PROGRAM TYPE]
		       (COND
			 ((LITATOM (CDR TYPE))               (* Qualified name)
			   (COURIER.WRITE.SEQUENCE.UNSPECIFIED STREAM ITEM (CAR TYPE)
							       (CDR TYPE)))
			 ((SETQ X (GETPROP (CAR TYPE)
					   (QUOTE COURIERDEF)))
                                                             (* User-defined type)
			   (GO USERTYPE))
			 (T (\CWSU.DEFAULT STREAM ITEM PROGRAM TYPE]
	    (T (\COURIER.UNKNOWN.TYPE PROGRAM TYPE)))
          (RETURN)
      USERTYPE                                               (* X = (readFn writeFn lengthFn writeSequenceFn))
          (COND
	    ((SETQ FN (CADDDR X))
	      (APPLY* FN STREAM ITEM PROGRAM TYPE))
	    ([AND (SETQ FN (CADDR X))
		  (OR (FIXP FN)
		      (SETQ FN (APPLY* FN ITEM PROGRAM TYPE]
                                                             (* Says how long it is)
	      (PUTWORD STREAM FN)
	      (APPLY* (CADR X)
		      STREAM ITEM PROGRAM TYPE))
	    (T (\CWSU.DEFAULT STREAM ITEM PROGRAM TYPE])

(\CWSU.DEFAULT
  [LAMBDA (STREAM ITEM PROGRAM TYPE)                         (* bvm: " 1-Jul-84 18:05")
    (COURIER.WRITE STREAM (COURIER.WRITE.REP ITEM PROGRAM TYPE)
		   NIL
		   (QUOTE (SEQUENCE UNSPECIFIED])

(COURIER.REP.LENGTH
  [LAMBDA (ITEM PROGRAM TYPE)                                (* bvm: " 3-Jul-84 17:37")

          (* * Returns the number of words that the Courier rep of ITEM as a (PROGRAM . TYPE) would occupy or NIL if we 
	  can't easily figure it out)


    (PROG (X)
          (RETURN (COND
		    [(LITATOM TYPE)
		      (SELECTQ TYPE
			       ((BOOLEAN CARDINAL INTEGER UNSPECIFIED)
				 1)
			       ((LONGCARDINAL LONGINTEGER TIME)
				 2)
			       (STRING (ADD1 (FOLDHI (NCHARS ITEM)
						     BYTESPERWORD)))
			       (COND
				 [(SETQ X (GETPROP TYPE (QUOTE COURIERDEF)))
                                                             (* User-defined type)
				   (AND (SETQ X (CADDR X))
					(OR (FIXP X)
					    (APPLY* X ITEM PROGRAM TYPE]
				 ((SETQ X (\GET.COURIER.TYPE PROGRAM TYPE))
				   (COURIER.REP.LENGTH ITEM PROGRAM X]
		    [(AND (LISTP TYPE)
			  (LITATOM (CAR TYPE)))
		      (SELECTQ (CAR TYPE)
			       (ENUMERATION 1)
			       [ARRAY (for X in ITEM bind (BASETYPE ←(CADDR TYPE))
					 sum (OR (COURIER.REP.LENGTH X PROGRAM BASETYPE)
						 (RETURN]
			       [SEQUENCE (for X in ITEM bind (BASETYPE ←(OR (CADDR TYPE)
									    (CADR TYPE)))
					    sum (OR (COURIER.REP.LENGTH X PROGRAM BASETYPE)
						    (RETURN))
					    finally          (* Count the word which is the sequence length)
						    (RETURN (ADD1 $$VAL]
			       [RECORD (for NAMEANDTYPE in (CDR TYPE) as VALUE in ITEM
					  do (OR (COURIER.REP.LENGTH VALUE PROGRAM (CADR NAMEANDTYPE))
						 (RETURN]
			       [NAMEDRECORD (for NAMEANDTYPE in (CDR TYPE) as NAMEANDVALUE
					       in ITEM do (OR (COURIER.REP.LENGTH (CADR NAMEANDVALUE)
										  PROGRAM
										  (CADR NAMEANDTYPE))
							      (RETURN]
			       [CHOICE (PROG ([WHICH (OR (ASSOC (CAR ITEM)
								(CDR TYPE))
							 (\COURIER.TYPE.ERROR ITEM (QUOTE CHOICE]
					      N)
					     (RETURN (COND
						       ((SETQ N (COND
							     ((CADDR WHICH)
							       (COURIER.REP.LENGTH (CADR ITEM)
										   PROGRAM
										   (CADDR WHICH)))
							     (T 0)))
							 (ADD1 N]
			       (COND
				 ((LITATOM (CDR TYPE))       (* Qualified name)
				   (COURIER.REP.LENGTH ITEM (CAR TYPE)
						       (CDR TYPE)))
				 ((SETQ X (GETPROP (CAR TYPE)
						   (QUOTE COURIERDEF)))
                                                             (* User-defined type)
				   (AND (SETQ X (CADDR X))
					(OR (FIXP X)
					    (APPLY* X ITEM PROGRAM TYPE]
		    (T (\COURIER.UNKNOWN.TYPE PROGRAM TYPE])

(\MAKE.COURIER.REP.STREAM
  [LAMBDA (LIST.OF.WORDS)                                    (* bvm: "15-Jun-84 11:54")

          (* * Makes a STREAM whose BIN operation produces bytes from LIST.OF.WORDS or whose BOUT operation produces a list 
	  of words in the stream's CRWORDLIST field (can only use stream for one or the other, of course))


    (PROG [(STREAM (NCREATE (QUOTE STREAM]
          (replace DEVICE of STREAM with (OR \COURIER.REP.DEVICE
					     (PROGN (SETQ \COURIER.REP.DEVICE (NCREATE (QUOTE FDEV)))
						    (replace BLOCKIN of \COURIER.REP.DEVICE
						       with (FUNCTION \NONPAGEDBINS))
						    (replace BLOCKOUT of \COURIER.REP.DEVICE
						       with (FUNCTION \NONPAGEDBOUTS))
						    \COURIER.REP.DEVICE)))
          (replace ACCESSBITS of STREAM with BothBits)
          (replace STRMBINFN of STREAM with (FUNCTION \COURIER.REP.BIN))
          (replace STRMBOUTFN of STREAM with (FUNCTION \COURIER.REP.BOUT))
          (replace ENDOFSTREAMOP of STREAM with (FUNCTION \COURIER.EOF))
          (replace CRWORDLIST of STREAM with LIST.OF.WORDS)
          (RETURN STREAM])

(\COURIER.REP.BIN
  [LAMBDA (STREAM)                                           (* bvm: "14-Jun-84 16:06")
    (PROG ((X (fetch CRNEXTBYTE of STREAM)))
          (RETURN (COND
		    (X (replace CRNEXTBYTE of STREAM with NIL)
		       X)
		    (T (SETQ X (OR (pop (fetch CRWORDLIST of STREAM))
				   (ERROR "Courier stream prematurely terminated")))
		       (replace CRNEXTBYTE of STREAM with (fetch LOBYTE of X))
		       (fetch HIBYTE of X])

(\COURIER.REP.BOUT
  [LAMBDA (STREAM BYTE)                                      (* bvm: "14-Jun-84 16:13")
    (PROG ((X (fetch CRNEXTBYTE of STREAM))
	   TAIL)
          (COND
	    (X (SETQ X (create WORD
			       HIBYTE ← X
			       LOBYTE ← BYTE))
	       [replace CRLASTWORD of STREAM with (COND
						    [(SETQ TAIL (fetch CRLASTWORD of STREAM))
						      (CDR (RPLACD TAIL (CONS X]
						    (T (replace CRWORDLIST of STREAM
							  with (LIST X]
	       (replace CRNEXTBYTE of STREAM with NIL))
	    (T (replace CRNEXTBYTE of STREAM with BYTE])
)
(DEFINEQ

(COURIER.READ.NSADDRESS
  [LAMBDA (STREAM)                                           (* bvm: "12-Jun-84 11:41")
                                                             (* Read a standard NSADDRESS from the next 6 words of 
							     STREAM)
    (PROG ((ADDR (create NSADDRESS)))
          (\BINS STREAM ADDR 0 (UNFOLD \#WDS.NSADDRESS BYTESPERWORD))
          (RETURN ADDR])

(COURIER.WRITE.NSADDRESS
  [LAMBDA (STREAM ADDR)                                      (* bvm: "12-Jun-84 11:45")
    (\BOUTS STREAM (\DTEST ADDR (QUOTE NSADDRESS))
	    0
	    (UNFOLD \#WDS.NSADDRESS BYTESPERWORD])
)

(PUTPROPS NSADDRESS COURIERDEF (COURIER.READ.NSADDRESS COURIER.WRITE.NSADDRESS 6))



(* Debugging)


(RPAQ? COURIERTRACEFILE )

(RPAQ? COURIERTRACEFLG )

(RPAQ? COURIERPRINTLEVEL (QUOTE (2 . 4)))

(RPAQ? NSWIZARDFLG )
(DECLARE: DOEVAL@COMPILE DONTCOPY

(GLOBALVARS COURIERTRACEFLG COURIERTRACEFILE COURIERPRINTLEVEL NSWIZARDFLG)
)
(DEFINEQ

(COURIERTRACE
  [LAMBDA (FLG REGION)                                       (* ecc " 4-AUG-83 15:13")
    (if (NULL FLG)
	then (if (ACTIVEWP COURIERTRACEFILE)
		 then (CLOSEW COURIERTRACEFILE))
	     (SETQ COURIERTRACEFILE T)
	     (SETQ COURIERTRACEFLG NIL)
      else (if (NOT (ACTIVEWP COURIERTRACEFILE))
	       then (SETQ COURIERTRACEFILE (CREATEW REGION "Courier Trace Window")))
	   [WINDOWPROP COURIERTRACEFILE (QUOTE BUTTONEVENTFN)
		       (FUNCTION (LAMBDA (WINDOW)
			   (if (LASTMOUSESTATE (NOT UP))
			       then (\CHANGE.ETHER.TRACING WINDOW (QUOTE COURIERTRACEFLG]
	   [WINDOWPROP COURIERTRACEFILE (QUOTE CLOSEFN)
		       (FUNCTION (LAMBDA (WINDOW)
			   (if (EQ WINDOW COURIERTRACEFILE)
			       then (SETQ COURIERTRACEFLG NIL)
				    (SETQ COURIERTRACEFILE T]
	   (DSPFONT (FONTCREATE (QUOTE GACHA)
				8)
		    COURIERTRACEFILE)
	   (SETQ COURIERTRACEFLG FLG)
	   (DSPSCROLL T COURIERTRACEFILE)
	   (TOTOPW COURIERTRACEFILE)
	   COURIERTRACEFILE])

(\COURIER.TRACE
  [LAMBDA (EVENT PROGRAM PROCEDURE ARGUMENTS)                (* bvm: "22-Jun-84 17:16")
    (SELECTQ EVENT
	     (CALL (printout COURIERTRACEFILE .TAB0 0 PROGRAM "." PROCEDURE "[")
		   [COND
		     (ARGUMENTS (COND
				  ((EQ COURIERTRACEFLG (QUOTE PEEK))
				    (printout COURIERTRACEFILE (QUOTE --)))
				  (T (for X in ARGUMENTS bind (FIRSTTIME ← T)
					do (COND
					     (FIRSTTIME (SETQ FIRSTTIME NIL))
					     (T (SPACES 1 COURIERTRACEFILE)))
					   (LVLPRIN2 X COURIERTRACEFILE (CAR COURIERPRINTLEVEL)
						     (CDR COURIERPRINTLEVEL]
		   (printout COURIERTRACEFILE (QUOTE %])))
	     [RETURN (printout COURIERTRACEFILE " => ")
		     (COND
		       [(EQ COURIERTRACEFLG (QUOTE PEEK))
			 (printout COURIERTRACEFILE (COND
				     ((CDR (LISTP ARGUMENTS))
				       (QUOTE --))
				     (T "&"]
		       (T (LVLPRINT ARGUMENTS COURIERTRACEFILE (CAR COURIERPRINTLEVEL)
				    (CDR COURIERPRINTLEVEL]
	     [BEGIN.BULK.DATA (printout COURIERTRACEFILE (COND
					  ((EQ COURIERTRACEFLG (QUOTE PEEK))
					    (QUOTE {))
					  (T "{bulk data"]
	     (END.BULK.DATA (printout COURIERTRACEFILE (QUOTE })))
	     (SHOULDNT])
)
(DECLARE: DONTEVAL@LOAD DOEVAL@COMPILE DONTCOPY COMPILERVARS 

(ADDTOVAR NLAMA \DUMP.COURIERPROGRAMS COURIERPROGRAM)

(ADDTOVAR NLAML )

(ADDTOVAR LAMA COURIER.EXPEDITED.CALL COURIER.CALL)
)
(PUTPROPS COURIER COPYRIGHT ("Xerox Corporation" 1983 1984))
(DECLARE: DONTCOPY
  (FILEMAP (NIL (6328 14649 (COURIER.VERSION# 6338 . 6553) (COURIERPROGRAM 6555 . 7132) (\COURIER.PUTDEF
 7134 . 7867) (\COURIER.CHECKDEF 7869 . 8549) (\COURIER.CHECK.PROCEDURES 8551 . 10438) (
\COURIER.CHECK.ERRORS 10440 . 11598) (\COURIER.DELDEF 11600 . 11793) (\COURIER.GETDEF 11795 . 11984) (
\GET.COURIERPROGRAM 11986 . 12141) (\DUMP.COURIERPROGRAMS 12143 . 13672) (\GET.COURIER.TYPE 13674 . 
13861) (\GET.COURIER.DEFINITION 13863 . 14647)) (15315 18002 (\COURIER.RECORDTRAN 15325 . 18000)) (
18057 37768 (COURIER.OPEN 18067 . 19922) (\COURIER.WHENCLOSED 19924 . 20161) (COURIER.CALL 20163 . 
22682) (COURIER.EXECUTE.CALL 22684 . 24799) (\COURIER.RESULTS 24801 . 28131) (\COURIER.HANDLE.BULKDATA
 28133 . 29283) (\COURIER.HANDLE.ERROR 29285 . 29554) (\BULK.DATA.STREAM 29556 . 31762) (
\COURIER.ATTENTIONFN 31764 . 32674) (\COURIER.OUTPUT.ABORTED 32676 . 33846) (\BULK.DATA.CLOSE 33848 . 
36765) (\ABORT.BULK.DATA 36767 . 37766)) (37769 47388 (COURIER.EXPEDITED.CALL 37779 . 39581) (
COURIER.EXECUTE.EXPEDITED.CALL 39583 . 40867) (\BUILD.EXPEDITED.XIP 40869 . 42336) (
\SEND.EXPEDITED.XIP 42338 . 44346) (\COURIER.EXPEDITED.ARGS 44348 . 44771) (\MAKE.EXPEDITED.STREAM 
44773 . 46720) (\COURIER.EOF 46722 . 46991) (\COURIER.EXPEDITED.OVERFLOW 46993 . 47386)) (47389 51468 
(COURIER.BROADCAST.CALL 47399 . 50873) (\COURIER.BROADCAST.ON.NET 50875 . 51466)) (51469 59881 (
COURIER.READ 51479 . 54183) (\COURIER.UNKNOWN.TYPE 54185 . 54395) (COURIER.READ.SEQUENCE 54397 . 54700
) (COURIER.READ.STRING 54702 . 55118) (COURIER.WRITE 55120 . 58235) (COURIER.WRITE.SEQUENCE 58237 . 
59272) (COURIER.WRITE.STRING 59274 . 59698) (\COURIER.TYPE.ERROR 59700 . 59879)) (59882 63696 (
COURIER.READ.BULKDATA 59892 . 60938) (BULKDATA.GENERATOR 60940 . 61443) (BULKDATA.GENERATE.NEXT 61445
 . 62553) (COURIER.WRITE.BULKDATA 62555 . 63694)) (63697 72761 (COURIER.READ.REP 63707 . 64124) (
COURIER.WRITE.REP 64126 . 64489) (COURIER.WRITE.SEQUENCE.UNSPECIFIED 64491 . 67521) (\CWSU.DEFAULT 
67523 . 67750) (COURIER.REP.LENGTH 67752 . 70395) (\MAKE.COURIER.REP.STREAM 70397 . 71611) (
\COURIER.REP.BIN 71613 . 72124) (\COURIER.REP.BOUT 72126 . 72759)) (72762 73398 (
COURIER.READ.NSADDRESS 72772 . 73172) (COURIER.WRITE.NSADDRESS 73174 . 73396)) (73756 75996 (
COURIERTRACE 73766 . 74787) (\COURIER.TRACE 74789 . 75994)))))
STOP