(FILECREATED "20-NOV-83 20:35:41" {PHYLUM}<LISP>LIBRARY>RS232EXEC.;28 35940  

      changes to:  (VARS RS232EXECCOMS)
		   (FNS RS232EXEC.SERVER \RS232EXEC.CHECK.MODEMANSWER)

      previous date: "14-NOV-83 16:22:30" {PHYLUM}<LISP>LIBRARY>RS232EXEC.;27)


(* Copyright (c) 1983 by Xerox Corporation)

(PRETTYCOMPRINT RS232EXECCOMS)

(RPAQQ RS232EXECCOMS ((FILES (SYSLOAD)
			     RS232)
		      (DECLARE: EVAL@COMPILE DONTCOPY (P (OR (GETMACROPROP (QUOTE 
									     RS232INITIALIZECHECK)
									   COMPILERMACROPROPS)
							     (LOADFROM (QUOTE RS232)))))
		      (LOCALVARS . T)
		      (DECLARE: EVAL@COMPILE (RECORDS RS232COMMANDLSTENTRY RS232COMMANDUSERSENTRY))
		      (ADDVARS (RS232EXEC.USERS NIL)
			       (RS232EXEC.HOSTS)
			       (RS232EXEC.DIRECTORIES LISPUSERS LISP LISP>LIBRARY LISP>FUGUE 
						      LISP>CURRENT))
		      (INITVARS (RS232EXEC.DEFAULTLOGFILE (UNPACKFILENAME 
								   "<LISP>COMMANDSERVER>LOGSHEET"))
				(\RS232EXEC.WHITEBT (MAKEBITTABLE (CHARCODE (SPACE TAB NULL))))
				(\RS232EXEC.TIMEOUT.mins 3)
				(\RS232EXEC.TIMER (SETUPTIMER 0)))
		      (GLOBALVARS RS232EXEC.USERS RS232EXEC.HOSTS RS232EXEC.DIRECTORIES 
				  \RS232EXEC.WHITEBT \RS232EXEC.TIMER)
		      (SPECVARS \RS232EXEC.TIMEOUT.mins 1MIN.tics MODEMANSWERTEST)
		      (FNS RS232EXEC.ADDUSER RS232EXEC.DELUSER RS232EXEC.SERVER 
			   \RS232EXEC.CHECK.MODEMANSWER \RS232EXECSERVER.TERPRI 
			   \RS232EXEC.LOGFILE.NOTE RS232READ&ECHO.LINE \RS232.DSPRUBOUTCHAR 
			   \RS232COMMAND.EVAL \RS232COMMAND.TERM \RS232COMMAND.SPEED 
			   \RS232COMMAND.LOGIN \RS232COMMAND.LOGOUT \RS232COMMAND.CONN)
		      (ALISTS (RS232COMMANDSLST EVAL TERMINAL QUIT SPEED LOGIN CONN PASSWORD LOGOUT))
		      (COMS (* Should be elsewhere)
			    (EXPORT (DECLARE: EVAL@COMPILE DONTCOPY (MACROS STRINGNCHARS 
									    \FILLINSTRPTR 
									    CHARCODE.DIGIT.WEIGHT)))
			    (GLOBALRESOURCES \STRINGTRIM.BITTABLE)
			    (MACROS STRINGTRIM \STRINGTRIM.OLDPTR STRINGLEFTTRIM STRINGRIGHTTRIM)
			    (FNS MATCHSTRING.FROM.LIST FIND.MAXIMAL.SUBSTRING STRINGTRIM 
				 STRINGLEFTTRIM STRINGRIGHTTRIM \STRINGTRIMAUX \STRINGTRIMAUX1))
		      (DECLARE: DONTEVAL@LOAD DOEVAL@COMPILE DONTCOPY COMPILERVARS
				(ADDVARS (NLAMA)
					 (NLAML)
					 (LAMA \RS232EXEC.LOGFILE.NOTE)))))
(FILESLOAD (SYSLOAD)
	   RS232)
(DECLARE: EVAL@COMPILE DONTCOPY 
(OR (GETMACROPROP (QUOTE RS232INITIALIZECHECK)
		  COMPILERMACROPROPS)
    (LOADFROM (QUOTE RS232)))
)
(DECLARE: DOEVAL@COMPILE DONTCOPY

(LOCALVARS . T)
)
(DECLARE: EVAL@COMPILE 
[DECLARE: EVAL@COMPILE 

(RECORD RS232COMMANDLSTENTRY (COMMANDNAME COMMANDFN COMMANDDOC COMMANDNEEDSLOGIN SHORTSCANP))

(RECORD RS232COMMANDUSERSENTRY (USERID CRYPTWORD WHEELP))
]
)

(ADDTOVAR RS232EXEC.USERS NIL)

(ADDTOVAR RS232EXEC.HOSTS )

(ADDTOVAR RS232EXEC.DIRECTORIES LISPUSERS LISP LISP>LIBRARY LISP>FUGUE LISP>CURRENT)

(RPAQ? RS232EXEC.DEFAULTLOGFILE (UNPACKFILENAME "<LISP>COMMANDSERVER>LOGSHEET"))

(RPAQ? \RS232EXEC.WHITEBT (MAKEBITTABLE (CHARCODE (SPACE TAB NULL))))

(RPAQ? \RS232EXEC.TIMEOUT.mins 3)

(RPAQ? \RS232EXEC.TIMER (SETUPTIMER 0))
(DECLARE: DOEVAL@COMPILE DONTCOPY

(ADDTOVAR GLOBALVARS RS232EXEC.USERS RS232EXEC.HOSTS RS232EXEC.DIRECTORIES \RS232EXEC.WHITEBT 
	  \RS232EXEC.TIMER)
)
(DECLARE: DOEVAL@COMPILE DONTCOPY

(SPECVARS \RS232EXEC.TIMEOUT.mins 1MIN.tics MODEMANSWERTEST)
)
(DEFINEQ

(RS232EXEC.ADDUSER
  (LAMBDA (NAME PASSWORD WHEELP)                             (* JonL "10-OCT-83 17:38")

          (* Someday, this database should be kept on a file, and any entries/deletions to this internal list should be 
	  reflected in an update to the database file.)


    (PROG ((UNAME (U-CASE (MKATOM NAME)))
	   USERINFO)
          (OR (SETQ USERINFO (FASSOC UNAME RS232EXEC.USERS))
	      (push RS232EXEC.USERS (SETQ USERINFO (create RS232COMMANDUSERSENTRY
							   USERID ← UNAME))))
          (replace CRYPTWORD of USERINFO with (AND PASSWORD (HASH.PASSWORD PASSWORD)))
          (replace WHEELP of USERINFO with WHEELP)
          (RETURN T))))

(RS232EXEC.DELUSER
  (LAMBDA (NAME)                                             (* JonL "10-OCT-83 17:40")
    (PROG ((UNAME (U-CASE (MKATOM NAME)))
	   USERINFO)
          (if (SETQ USERINFO (FASSOC UNAME RS232EXEC.USERS))
	      then (SETQ RS232EXEC.USERS (DREMOVE USERINFO RS232EXEC.USERS))
		   (RETURN T)))))

(RS232EXEC.SERVER
  (LAMBDA (LOGFILE MODEMANSWERTEST LISTENINGSPEED)           (* JonL "20-NOV-83 20:34")
    (DECLARE (SPECVARS LOGFILE MODEMANSWERTEST LISTENINGSPEED))
    (RS232INITIALIZECHECK)
    (\RS232INSURE.LINEBUFFER 580)                            (* Input buffer is at least as big as the data portion 
							     of an ethernet packet.)
    (OR (FIXP LISTENINGSPEED)
	(SETQ LISTENINGSPEED 1200))
    (AND (PROG1 RS232EXEC.DEFAULTLOGFILE                     (* Comment PPLossage))
	 (NLISTP RS232EXEC.DEFAULTLOGFILE)
	 (SETQ RS232EXEC.DEFAULTLOGFILE (UNPACKFILENAME RS232EXEC.DEFAULTLOGFILE)))
    (RESETLST (RESETSAVE RECLAIMWAIT MAX.SMALLP)
	      (RESETSAVE NIL (LIST (QUOTE CNDIR)
				   (DIRECTORYNAME T)))
	      (PROG ((PROMPTMSG "←")
		     (TIMEOUT.tics (ITIMES \RS232EXEC.TIMEOUT.mins 60 \RCLKSECOND))
		     (1MIN.tics (ITIMES 60 \RCLKSECOND))
		     (10SECS.tics (ITIMES 10 \RCLKSECOND))
		     (3SECS.tics (ITIMES 3 \RCLKSECOND))
		     (SUCCESSIVE.LONGRINGS 0)
		     (SPEEDFLIPFLOPS 0)
		     (RS232LOSTCHARFN (FUNCTION (LAMBDA (X)
					  (if (EQ X (QUOTE FramingError))
					      then (OR MODEMANSWERTEST (HELP 
								     "Connections at wrong speed"))
						   (PROG ((POS (OR (STKPOS (QUOTE RS232READ&ECHO.LINE)
									   )
								   (STKPOS (QUOTE 
								     \RS232EXEC.CHECK.MODEMANSWER)))))
						         (if POS
							     then (RETFROM POS (QUOTE FramingError))))
					      )
					  (SETUPTIMER 0 \RS232DING.BOX)
                                                             (* To stop that abominable flashing)
					  (\RS232DING))))
		     LOGGEDINP WHEELP DISPLAYTERMP CLINE COMM COMMWORDP NCHARS I)
		    (DECLARE (SPECVARS TIMEOUT.tics 1MIN.tics 10SECS.tics 3SECS.tics 
				       SUCCESSIVE.LONGRINGS LOGGEDINP WHEELP DISPLAYTERMP 
				       RS232LOSTCHARFN))
		    (if LOGFILE
			then (OR (EQ T LOGFILE)
				 (type? STREAM LOGFILE)
				 (SETQ LOGFILE (if (OR (LITATOM LOGFILE)
						       (STRINGP LOGFILE))
						   then (PACKFILENAME (NCONC (UNPACKFILENAME LOGFILE)
									     RS232EXEC.DEFAULTLOGFILE)
								      )
						 else T)))
			     (SETQ LOGFILE (if (EQ T LOGFILE)
					       then (GETSTREAM T (QUOTE OUTPUT))
					     elseif (OR (OPENP LOGFILE (SETQ I (QUOTE APPEND)))
							(OPENP LOGFILE (SETQ I (QUOTE OUTPUT))))
					       then (GETSTREAM LOGFILE I)
					     else (OPENSTREAM LOGFILE (QUOTE APPEND)
							      (QUOTE OLD/NEW))))
			     (printout LOGFILE T T 
				       "Commencement of RS232 CommandServer Service is at "
				       (GDATE)
				       T))
		BEGINLISTENING
		    (SETQ LOGGEDINP (SETQ WHEELP (SETQ DISPLAYTERMP NIL)))
		    (FRPTQ 8 (RECLAIM))
		    (CNDIR (QUOTE {DSK}))
		    (RS232INIT LISTENINGSPEED 8 NIL NIL (AND MODEMANSWERTEST (QUOTE (DTR RTS))))
		    (if MODEMANSWERTEST
			then (SETQ SPEEDFLIPFLOPS 0)
			     (SELECTQ (\RS232EXEC.CHECK.MODEMANSWER)
				      (NIL (GO GETLINE))
				      (TIMEREXPIRED? (SETQ CLINE (QUOTE TIMEREXPIRED?))
						     (GO ANALYZELINE))
				      (RESTART (GO BEGINLISTENING))
				      ((FramingError)
					(GO FLIPSPEED))
				      (SHOULDNT (QUOTE \RS232EXEC.CHECK.MODEMANSWER)))
		      else (GO GETLINE))
		FLIPSPEED
		    (if (IGREATERP (add SPEEDFLIPFLOPS 1)
				   4)
			then (\RS232EXEC.LOGFILE.NOTE 24 "Connection failure (wrong speed?)")
			     (RS232MODEMHANGUP)
			     (GO BEGINLISTENING))
		    (APPLY (FUNCTION RS232INIT)
			   (CONS (SELECTQ (fetch BAUDRATE of RS232INIT)
					  (300 1200)
					  (1200 300)
					  (SHOULDNT))
				 (CDR RS232INIT)))
		GETLINE
		    (SETQ CLINE (RS232READ&ECHO.LINE PROMPTMSG TIMEOUT.tics DISPLAYTERMP NIL NIL 
						     RS232COMMANDSLST))
		ANALYZELINE
		    (if (NOT (STRINGP CLINE))
			then (if (LISTP CLINE)
				 then                        (* Command with SHORTSCANP has already found its 
							     COMMANDLSTENTRY)
				      (SETQ COMM CLINE)
				      (SETQ CLINE)
				      (\RS232.CHECKUART)
				      (GO RUNCOMMAND))
			     (SELECTQ CLINE
				      ((TIMEREXPIRED?)
					(if (AND (PROG1 MODEMANSWERTEST 
                                                             (* Comment PPLossage))
						 (OR (EQ MODEMANSWERTEST (QUOTE PEEK))
						     (RS232MODEMSTATUSP (QUOTE DSR))))
					    then (\RS232EXECSERVER.TERPRI)
						 (SETQ I
						   (CONCAT (PROG1 "Auto LOGOUT " 
                                                             (* Comment PPLossage))
							   (if LOGGEDINP
							       then (if WHEELP
									then "of Wheel "
								      else "of User ")
							     else "")
							   (OR LOGGEDINP "")
							   " (idle for more than "
							   (MKSTRING \RS232EXEC.TIMEOUT.mins)
							   " minutes)"))
						 (RS232WRITECHARS I)
						 (\RS232EXECSERVER.TERPRI)
						 (RS232WRITEBYTE (CHARCODE ↑G)
								 T)
						 (RS232MODEMHANGUP)
						 (\RS232EXEC.LOGFILE.NOTE 24 I))
					(GO BEGINLISTENING))
				      ((RS232CLEARBUFFER)
					(\RS232EXECSERVER.TERPRI)
					(RS232WRITECHARS 
						   "Input buffer capacity exceeded -- try again!")
					(RS232CLEARBUFFER (QUOTE INPUT))
					(GO GETLINE))
				      ((ABORT)
					(RS232WRITESTRING "   XXX ")
					(GO GETLINE))
				      ((FramingError)
					(GO FLIPSPEED))
				      (PROGN (\RS232EXECSERVER.TERPRI)
					     (RS232WRITECHARS 
			 "Shouldn't Happen -- RS232COMMAND.SERVER   Please report to LispSupport")
					     (GO GETLINE)))
		      else (SETQ SPEEDFLIPFLOPS 0))          (* Trim "white space" chars off right end of line -- 
							     RS232READ&ECHO.LINE doesn't allow them to accumulate on 
							     the left)
		    (\RS232.CHECKUART)
		    (SETQ CLINE (STRINGRIGHTTRIM CLINE \RS232EXEC.WHITEBT))
		    (\RS232.CHECKUART)
		    (if (ZEROP (SETQ NCHARS (STRINGNCHARS CLINE)))
			then (GO GETLINE)
		      elseif (PROG1 (SETQ I (STRPOSL \RS232EXEC.WHITEBT CLINE))
				    (\RS232.CHECKUART))
			then                                 (* Delimit the initial word of CLINE by index I)
			     (add I -1))
		    (SETQ COMM (ASSOC (MATCHSTRING.FROM.LIST (U-CASE (SUBSTRING CLINE 1 I))
							     RS232COMMANDSLST T)
				      RS232COMMANDSLST))
		    (if (NULL (AND COMM (fetch (RS232COMMANDLSTENTRY COMMANDNAME) of COMM)))
			then                                 (* Looks like he doesn't know what he's doing)
			     (RS232WRITECHARS " ??" T)
			     (GO GETLINE)
		      elseif (AND (NOT LOGGEDINP)
				  (fetch (RS232COMMANDLSTENTRY COMMANDNEEDSLOGIN) of COMM))
			then (RS232WRITECHARS " Not logged in ??" T)
			     (GO GETLINE))
		    (SETQ CLINE (AND I (SETQ I (PROG1 (STRPOSL \RS232EXEC.WHITEBT CLINE (ADD1 I)
							       T)
						      (\RS232.CHECKUART)))
				     (PROG1 (SUBSTRING CLINE I NIL CLINE)
					    (\RS232.CHECKUART))))
		    (\RS232EXECSERVER.TERPRI)
		RUNCOMMAND
		    (SETQ COMM (NLSETQ (APPLY* (fetch (RS232COMMANDLSTENTRY COMMANDFN) of COMM)
					       CLINE)))
		    (if COMM
			then (if (EQ (CAR COMM)
				     (QUOTE \RS232COMMAND.LOGOUT))
				 then                        (* Special-case for this one)
				      (GO BEGINLISTENING))
			     (RS232WRITESTRING " ==> ")
			     (RS232WRITESTRING (MKSTRING (CAR COMM)))
		      elseif (NOT (AND (EQ 17 (CAR (SETQ COMM (PROG1 (ERRORN)
								     (\RS232.CHECKUART)))))
				       (EQ (CAADR COMM)
					   (QUOTE NOERROR))))
			then                                 (* Note that (ERROR (QUOTE NOERROR)) elects not to come 
							     here)
			     (RS232WRITESTRING " [Error: ")
			     (if (EQ 17 (CAR COMM))
				 then (RS232WRITESTRING (MKSTRING (CAADR COMM)))
				      (RS232WRITESTRING "  ")
				      (RS232WRITESTRING (MKSTRING (CDADR COMM)
								  T))
			       else (RS232WRITESTRING (ERRORSTRING (CAR COMM)))
				    (RS232WRITESTRING "  ")
				    (RS232WRITESTRING (MKSTRING (CADR COMM)
								T)))
			     (RS232WRITESTRING " ]"))        (* Remember, GETLINE will call \RS232EXECSERVER.TERPRI 
							     which will force output.)
		    (GO GETLINE)))))

(\RS232EXEC.CHECK.MODEMANSWER
  (LAMBDA NIL
    (DECLARE (USEDFREE MODEMANSWERTEST TIMEOUT.tics 10SECS.tics 3SECS.tics LOGFILE 
		       SUCCESSIVE.LONGRINGS))                (* JonL "20-NOV-83 20:23")
                                                             (* Wait until we hear someone who has dialed-in, and 
							     whom the modem has answered.)
    (repeatuntil (SELECTQ MODEMANSWERTEST
			  ((RING READY RINGREADY)
			    (RS232MODEMSTATUSP (SELECTQ MODEMANSWERTEST
							(RING (QUOTE RI))
							(READY (QUOTE DSR))
							(RINGREADY (QUOTE (OR RI DSR)))
							NIL)))
			  (PEEK (RS232PEEKBYTE))
			  (\ILLEGAL.ARG MODEMANSWERTEST))
       do (\RS232CHECK.BLOCK))
    (PROG NIL
          (if (AND (NEQ MODEMANSWERTEST (QUOTE PEEK))
		   (RS232MODEMSTATUSP (QUOTE RI)))
	      then                                           (* Ascertain that the phone has stopped ringing)
		   (AND (during 10SECS.tics timerUnits (QUOTE TICKS) usingTimer \RS232EXEC.TIMER
			   when (NOT (RS232MODEMSTATUSP (QUOTE RI))) do (RETURN)
			   finally (if (ILEQ 10 (add SUCCESSIVE.LONGRINGS 1))
				       then (HELP "Modem wedged in RI state?")
				     else (\RS232EXEC.LOGFILE.NOTE 0 
					  "****** Modem continues RI for longer than 10 seconds.")
					  (RS232MODEMHANGUP)
					  (RETURN T)))
			(RETURN (QUOTE RESTART))))
          (SETQ SUCCESSIVE.LONGRINGS 0)
          (\RS232EXEC.LOGFILE.NOTE 24 "Dial-in detected"))))

(\RS232EXECSERVER.TERPRI
  (LAMBDA NIL                                                (* JonL "11-JUL-83 18:23")

          (* Here is the place which ought to be sensitive to CR/LF -- to install some padding if necessary.
	  Currently, we just delay long enough for Interlisp-D's display printout routines to do their thing.)


    (RS232WRITEBYTE (CHARCODE CR)
		    T)
    (\RS232CHECK.BLOCK 10)
    (RS232WRITEBYTE (CHARCODE LF)
		    T)
    (\RS232CHECK.BLOCK 40)
    T))

(\RS232EXEC.LOGFILE.NOTE
  (LAMBDA NARGS
    (DECLARE (SPECVARS LOGFILE))                             (* JonL "15-SEP-83 13:36")
    (if LOGFILE
	then (FRESHLINE LOGFILE)
	     (PRIN1 (GDATE))
	     (TAB (ARG NARGS 1)
		  0 LOGFILE)
	     (for I from 2 to NARGS do (PRIN1 (ARG NARGS I)
					      LOGFILE)))))

(RS232READ&ECHO.LINE
  (LAMBDA (PROMPTSTRING TIMEOUT.tics DISPLAYTERMP WHITESPACETERMINS? NOECHOP COMMANDS.ALIST)
                                                             (* JonL "10-OCT-83 17:51")
    (DECLARE (SPECVARS DISPLAYTERMP))
    (\RS232.CHECKUART)
    (PROG ((NCHARS 0)
	   (LINEBUFFERSIZE 256)
	   C CLINE COMM COMMWORDP RUBBING?)
          (DECLARE (SPECVARS RUBBING?))
      BEGIN
          (\RS232INSURE.LINEBUFFER LINEBUFFERSIZE)
          (SETQ CLINE \RS232STRPTR)
          (SETQ LINEBUFFERSIZE (STRINGNCHARS CLINE))
          (OR (IGEQ LINEBUFFERSIZE 256)
	      (SHOULDNT "Line Buffer size"))
          (change (STRINGNCHARS CLINE)
		  (SETQ NCHARS 0))
          (\RS232EXECSERVER.TERPRI)
          (RS232WRITESTRING PROMPTSTRING T)
          (SETQ RUBBING?)
          (SETQ COMMWORDP COMMANDS.ALIST)
      GETNEXTBYTE
          (if (NULL (SETQ C (RS232READBYTE TIMEOUT.tics (QUOTE TICS))))
	      then                                           (* Flush him if he's idle too long)
		   (RETURN (QUOTE TIMEREXPIRED?)))
      CHECKBYTE
          (SELCHARQ C
		    ((↑A BS)
		      (if (AND RUBBING? (NOT DISPLAYTERMP)
			       (ZEROP NCHARS))
			  then (SETQ RUBBING?)
			       (if (NOT NOECHOP)
				   then (RS232WRITEBYTE (CHARCODE \)
							T)))
		      (if (NOT (ZEROP NCHARS))
			  then (add NCHARS -1)
			       (SETQ C (GLC CLINE))
			       (if (NOT NOECHOP)
				   then (OR (\RS232.DSPRUBOUTCHAR C DISPLAYTERMP)
					    (PROGN           (* This, just to share code with the reprint from ↑R)
						   (SETQ RUBBING? T)
						   (SETQ C (CHARCODE ↑R))
						   (GO CHECKBYTE))))
			       (if (ZEROP NCHARS)
				   then (if (AND RUBBING? (NOT DISPLAYTERMP))
					    then (SETQ RUBBING?)
						 (RS232WRITEBYTE (CHARCODE \)
								 T))
					(GO BEGIN)))
		      (GO GETNEXTBYTE))
		    ((↑Q)
		      (if DISPLAYTERMP
			  then                               (* Someday, figure out something better to do here.)
			       NIL)
		      (GO BEGIN))
		    ((↑R)
		      (if (AND (NOT NOECHOP)
			       (OR RUBBING? (NEQ 0 NCHARS)))
			  then (if DISPLAYTERMP
				   then                      (* Someday, figure out something better to do here.)
				   )
			       (\RS232EXECSERVER.TERPRI)
			       (RS232WRITESTRING PROMPTSTRING)
			       (RS232WRITESTRING CLINE))
		      (SETQ RUBBING?)
		      (RS232FORCEOUTPUT)
		      (GO GETNEXTBYTE))
		    ((↑V)
		      (if (NULL (SETQ C (RS232READBYTE TIMEOUT.tics (QUOTE TICS))))
			  then (RETURN (QUOTE TIMEREXPIRED?))))
		    ((?)
		      (if COMMANDS.ALIST
			  then (for X in COMMANDS.ALIST when (OR (PROG1 LOGGEDINP 
                                                             (* Comment PPLossage))
								 (NOT (fetch (RS232COMMANDLSTENTRY
									       COMMANDNEEDSLOGIN)
									 of X)))
				  do (\RS232EXECSERVER.TERPRI)
				     (RS232WRITESTRING (fetch (RS232COMMANDLSTENTRY COMMANDNAME)
							  of X))
				     (RS232WRITEBYTE (CHARCODE SPACE))
				     (RS232WRITESTRING (OR (fetch (RS232COMMANDLSTENTRY COMMANDDOC)
							      of X)
							   " ")
						       T))
			       (PROGN                        (* This, just to share code with the reprint from ↑R)
				      (SETQ RUBBING? T)
				      (SETQ C (CHARCODE ↑R))
				      (GO CHECKBYTE))))
		    ((ESC)
		      (if COMMWORDP
			  then                               (* Try command completion)
			       (if (SETQ COMM (MATCHSTRING.FROM.LIST CLINE COMMANDS.ALIST T))
				   then (PROG ((MAYBECOMM? COMM)
					       (M (fetch (STRINGP LENGTH) of CLINE))
					       N)
					      (if (LISTP MAYBECOMM?)
						  then (SETQ MAYBECOMM? (FIND.MAXIMAL.SUBSTRING
							   MAYBECOMM?)))
					      (OR (STRINGP MAYBECOMM?)
						  (SETQ MAYBECOMM? (MKSTRING MAYBECOMM?)))
					      (change (STRINGNCHARS CLINE)
						      (SETQ NCHARS (SETQ N (STRINGNCHARS MAYBECOMM?)))
						      )
					      (if (NEQ N M)
						  then (SETQ MAYBECOMM? (SUBSTRING MAYBECOMM?
										   (ADD1 M)
										   NIL MAYBECOMM?))
						       (RPLSTRING CLINE (ADD1 M)
								  MAYBECOMM?)
						       (RS232WRITECHARS MAYBECOMM?)))
					(if (AND (NLISTP COMM)
						 (SETQ C (ASSOC (MATCHSTRING.FROM.LIST CLINE 
										   COMMANDS.ALIST T)
								COMMANDS.ALIST))
						 (fetch (RS232COMMANDLSTENTRY SHORTSCANP)
						    of C))
					    then (RETURN C)
					  elseif (NLISTP COMM)
					    then (change (STRINGNCHARS CLINE)
							 (add NCHARS 1))
						 (RPLCHARCODE CLINE NCHARS (CHARCODE SPACE))
						 (RS232WRITEBYTE (CHARCODE SPACE))
					  else               (* Lose! Didn't make a perfect match!)
					       (RS232WRITEBYTE (CHARCODE ↑G)))
				 else (RS232WRITEBYTE (CHARCODE ↑G)))
			       (RS232FORCEOUTPUT)
			       (GO GETNEXTBYTE)))
		    ((EOL CR LF)
		      (RETURN CLINE))
		    ((DEL)
		      (RETURN (QUOTE ABORT)))
		    (if (ZEROP (\SYNCODE \RS232EXEC.WHITEBT C))
			then                                 (* Don't worry much about non-white-space characters)
		      elseif (NOT COMMANDS.ALIST)
			then (if WHITESPACETERMINS?
				 then (if (ZEROP NCHARS)
					  then (GO GETNEXTBYTE)
					else (RETURN CLINE)))
		      elseif (ZEROP NCHARS)
			then                                 (* Don't put white-space characters at the beginning of 
							     a command line)
			     (GO GETNEXTBYTE)
		      elseif (NEQ C (CHARCODE NULL))
			then                                 (* Most "white space" characters will terminate a word.)
			     (if COMMWORDP
				 then                        (* Try once to see if this is an "early" quitter)
				      (if (AND (SETQ COMMWORDP (ASSOC (MATCHSTRING.FROM.LIST CLINE 
										   COMMANDS.ALIST T)
								      COMMANDS.ALIST))
					       (fetch (RS232COMMANDLSTENTRY SHORTSCANP) of COMMWORDP))
					  then (RETURN COMMWORDP))
				      (SETQ COMMWORDP))))
          (\RS232.CHECKUART)
          (if RUBBING?
	      then (OR NOECHOP (RS232WRITEBYTE (CHARCODE \)))
		   (SETQ RUBBING?))
          (\PUTBASEBYTE \RS232LINEBUFFER NCHARS C)
          (change (STRINGNCHARS CLINE)
		  (add NCHARS 1))
          (if (IGEQ NCHARS LINEBUFFERSIZE)
	      then (RETURN (QUOTE RS232CLEARBUFFER)))        (* Now for the normal echo of his typed-in character)
          (OR NOECHOP (RS232WRITEBYTE C T))
          (GO GETNEXTBYTE))))

(\RS232.DSPRUBOUTCHAR
  (LAMBDA (C DISPLAYTERMP)                                   (* JonL "14-SEP-83 19:05")
    (DECLARE (SPECVARS RUBBING?))
    (if DISPLAYTERMP
	then (RS232WRITECHARS "")
	     (RS232WRITEBYTE C T)
	     T
      else (if (NOT RUBBING?)
	       then (RS232WRITEBYTE (SETQ RUBBING? (CHARCODE \))))
	   (RS232WRITEBYTE (CHCON1 C)
			   T))))

(\RS232COMMAND.EVAL
  (LAMBDA (STR)                                              (* JonL "14-SEP-83 20:03")
    (DECLARE (GLOBALVARS \STRINGOFDS)
	     (SPECVARS WHEELP))
    (if (NOT WHEELP)
	then (ERROR "EVAL not available to non-WHEELs."))
    (if (AND (STRINGP STR)
	     (NOT (ZEROP (NCHARS STR))))
	then (EVAL (PROG1 (READ STR)
			  (PUTHASH STR NIL \STRINGOFDS)))
      else (ERROR " ??"))))

(\RS232COMMAND.TERM
  (LAMBDA (STR)                                              (* JonL "10-OCT-83 17:47")
                                                             (* DISPLAYTERMP should be a FVAR bound by 
							     RS232COMMAND.SERVER)
    (DECLARE (USEDFREE DISPLAYTERMP))
    ((LAMBDA (I)
	(if (OR (NOT (STRINGP STR))
		(ZEROP (NCHARS STR))
		(AND (FMEMB (NTHCHARCODE STR 1)
			    (CHARCODE (D d)))
		     (FMEMB (NTHCHARCODE STR 1)
			    (CHARCODE (E e)))
		     (NULL (STRPOSL \RS232EXEC.WHITEBT STR))))
	    then                                             (* Latter AND is a quick test for "DEscribe")
		 NIL
	  else                                               (* Next AND is a quick test for "DIsplay")
	       (SETQ DISPLAYTERMP (AND (FMEMB (NTHCHARCODE STR 1)
					      (CHARCODE (D d)))
				       (FMEMB (NTHCHARCODE STR 1)
					      (CHARCODE (I i)))
				       (NULL (STRPOSL \RS232EXEC.WHITEBT STR)))))
	(if DISPLAYTERMP
	    then " DISPLAY"
	  else " STANDARD")))))

(\RS232COMMAND.SPEED
  (LAMBDA (STR)                                              (* JonL "10-OCT-83 17:47")
    (if (AND (STRINGP STR)
	     (NEQ 0 (NCHARS STR)))
	then (PROG ((SPEED 0)
		    (RADIX 10))
	           (SETQ STR (STRINGTRIM STR \RS232EXEC.WHITEBT))
	           (if (EQ (NTHCHARCODE STR (NCHARS STR))
			   (CHARCODE Q))
		       then (GLC STR)
			    (SETQ RADIX 8))
	           (for C instring STR do (SETQ SPEED (IPLUS (ITIMES RADIX SPEED)
							     (CHARCODE.DIGIT.WEIGHT C))))
	           (APPLY (FUNCTION RS232INIT)
			  (CONS SPEED (CDR RS232INIT)))
	           (AND MODEMANSWERTEST (RS232MODEMCONTROL (QUOTE (DTR RTS))))))
    (MKSTRING (fetch (RS232CHARACTERISTICS BAUDRATE) of RS232INIT))))

(\RS232COMMAND.LOGIN
  (LAMBDA (NAME)                                             (* JonL "10-OCT-83 17:52")
    (DECLARE (USEDFREE DISPLAYTERMP))
    (\RS232.CHECKUART)
    (PROG (UNAME USERINFO CRYPTWORD PASSW I)
          (DECLARE (SPECVARS LOGGEDINP WHEELP))
          (repeatuntil (AND (STRINGP NAME)
			    (NOT (ZEROP (STRINGNCHARS NAME))))
	     do (SETQ NAME (RS232READ&ECHO.LINE "Login: (name?) " 1MIN.tics DISPLAYTERMP
						(CHARCODE (SPACE TAB))))
		(AND (LITATOM NAME)
		     (SELECTQ NAME
			      (TIMEREXPIRED? (ERROR (QUOTE Time% out)))
			      (ABORT (RS232WRITESTRING "   XXX ")
				     (ERROR (QUOTE NOERROR)))
			      NIL)))
          (SETQ NAME (STRINGTRIM NAME \RS232EXEC.WHITEBT))
          (\RS232.CHECKUART)
          (if (SETQ I (PROG1 (STRPOSL \RS232EXEC.WHITEBT NAME)
			     (\RS232.CHECKUART)))
	      then                                           (* Since white-space characters have been trimmed of 
							     left and right ends, then this must be an embedded 
							     "space")
		   (PROG ((J (STRPOSL \RS232EXEC.WHITEBT NAME I T)))
		         (\RS232.CHECKUART)
		         (if J
			     then (SETQ PASSW (SUBSTRING NAME J (PROG1 (STRPOSL \RS232EXEC.WHITEBT 
										NAME J)
								       (\RS232.CHECKUART))))
				  (\RS232.CHECKUART)
				  (SETQ NAME (SUBSTRING NAME 1 (SUB1 I)
							NAME))
			   else (SHOULDNT (QUOTE \RS232COMMAND.LOGIN)))))
          (\RS232.CHECKUART)
          (SETQ UNAME (U-CASE (MKATOM NAME)))
          (\RS232.CHECKUART)
          (if (NULL (SETQ USERINFO (FASSOC UNAME RS232EXEC.USERS)))
	      then (ERROR NAME "Unknown user name"))
          (until (STRINGP PASSW)
	     do (SETQ PASSW (RS232READ&ECHO.LINE "  (Password) " 1MIN.tics DISPLAYTERMP
						 (CHARCODE (SPACE TAB))
						 T))
		(AND (LITATOM NAME)
		     (SELECTQ NAME
			      (TIMEREXPIRED? (ERROR (QUOTE Time% out)))
			      (ABORT (RS232WRITESTRING "   XXX ")
				     (ERROR (QUOTE NOERROR)))
			      NIL)))
          (if (OR (NULL (SETQ I (fetch (RS232COMMANDUSERSENTRY CRYPTWORD) of USERINFO)))
		  (EQUAL I (HASH.PASSWORD (U-CASE PASSW))))
	      then (SETQ LOGGEDINP UNAME)
	    else (ERROR " wrong password"))
          (SETQ WHEELP (fetch (RS232COMMANDUSERSENTRY WHEELP) of USERINFO))
          (\RS232EXEC.LOGFILE.NOTE 24 (if WHEELP
					  then "Wheel "
					else "User ")
				   UNAME " logged in")
          (RETURN (QUOTE OK)))))

(\RS232COMMAND.LOGOUT
  (LAMBDA NIL                                                (* JonL "10-OCT-83 17:52")
    (\RS232EXECSERVER.TERPRI)
    (RS232WRITECHARS "Bye, bye.")
    (\RS232EXECSERVER.TERPRI)
    (AND MODEMANSWERTEST (RS232MODEMHANGUP))
    (\RS232EXEC.LOGFILE.NOTE 24 (if LOGGEDINP
				    then (if WHEELP
					     then "LOGOUT of Wheel "
					   else "LOGOUT of user ")
				  else "LOGOUT ")
			     (OR LOGGEDINP ""))
    (QUOTE \RS232COMMAND.LOGOUT)))

(\RS232COMMAND.CONN
  (LAMBDA (STR)                                              (* JonL "10-OCT-83 17:45")
    (PROG (HOST DIR)
          (if (AND (STRINGP STR)
		   (NOT (ZEROP (NCHARS STR))))
	      then (PROG ((L (UNPACKFILENAME STR)))
		         (SETQ HOST (LISTGET L (QUOTE HOST)))
		         (SETQ DIR (LISTGET L (QUOTE DIRECTORY))))
	    else (RETURN (DIRECTORYNAME T)))
          (if (NULL HOST)
	    elseif (EQ HOST (QUOTE DSK))
	      then                                           (* Temporary kludge since ALTO file system doesn't 
							     support local directories)
		   (SETQ DIR)
	    elseif (NOT (FMEMB HOST RS232EXEC.HOSTS))
	      then (ERROR HOST " not a permitted host"))
          (if (NULL DIR)
	    elseif (NOT (FMEMB DIR RS232EXEC.DIRECTORIES))
	      then (ERROR DIR " not a permitted directory"))
          (RETURN (CNDIR (if (AND HOST DIR)
			     then (PACKFILENAME (QUOTE HOST)
						HOST
						(QUOTE DIRECTORY)
						DIR)
			   else (OR HOST DIR (QUOTE {DSK}))))))))
)

(ADDTOVAR RS232COMMANDSLST (EVAL \RS232COMMAND.EVAL "{s-expression}" T)
			   (TERMINAL \RS232COMMAND.TERM "{DISPLAY or not}")
			   (QUIT \RS232COMMAND.LOGOUT " | Quit and Logout.")
			   (SPEED \RS232COMMAND.SPEED "{change RS232 speed -- decimal number}")
			   (LOGIN \RS232COMMAND.LOGIN "Login for Command service" NIL T)
			   (CONN \RS232COMMAND.CONN 
			     "Connect to directory (on local file server), or to {DSK} if no arg"
				 T)
			   (PASSWORD \RS232COMMAND.PASSW "Change the logged-in user's password" T T)
			   (LOGOUT \RS232COMMAND.LOGOUT " | Quit and Logout."))



(* Should be elsewhere)

(* FOLLOWING DEFINITIONS EXPORTED)


(DECLARE: EVAL@COMPILE DONTCOPY 
(DECLARE: EVAL@COMPILE 

(PUTPROPS STRINGNCHARS MACRO (= . NCHARS))

(PUTPROPS STRINGNCHARS DMACRO ((STR)
  (ffetch (STRINGP LENGTH) of STR)))

(PUTPROPS \FILLINSTRPTR DMACRO (OPENLAMBDA (STRPTR STRBASE STROFFST STRLENGTH)
  (replace (STRINGP BASE) of STRPTR with STRBASE)
  (freplace (STRINGP OFFST) of STRPTR with STROFFST)
  (freplace (STRINGP LENGTH) of STRPTR with STRLENGTH)
  STRPTR))

(PUTPROPS CHARCODE.DIGIT.WEIGHT MACRO (OPENLAMBDA (C)
  (if (OR (ILESSP C (CHARCODE 0))
	  (IGREATERP C (CHARCODE 9)))
      then (ERROR (CHARACTER C)
		  "Bad digit")
    else (IDIFFERENCE C (CHARCODE 0)))))
)
)


(* END EXPORTED DEFINITIONS)


(RPAQQ \STRINGTRIM.BITTABLE NIL)
(DECLARE: DOEVAL@COMPILE DONTCOPY

(ADDTOVAR GLOBALVARS \STRINGTRIM.BITTABLE)
)
(DECLARE: DOEVAL@COMPILE DONTCOPY 
(PUTDEF (QUOTE \STRINGTRIM.BITTABLE)
	(QUOTE GLOBALRESOURCES)
	(QUOTE (\ALLOCBLOCK (CONSTANT (FOLDHI (EXPT 2 BITSPERBYTE)
					      BITSPERCELL)))))
)
(DECLARE: EVAL@COMPILE 

(PUTPROPS STRINGTRIM MACRO (X
  (if (ILESSP 2 (LENGTH X))
      then (CONS (QUOTE \STRINGTRIM.OLDPTR)
		 X)
    else (QUOTE IGNOREMACRO))))

(PUTPROPS \STRINGTRIM.OLDPTR MACRO (OPENLAMBDA (STR CHARSET OLDPTR)
  (STRINGLEFTTRIM (STRINGRIGHTTRIM STR CHARSET OLDPTR)
		  CHARSET OLDPTR)))

(PUTPROPS STRINGLEFTTRIM MACRO ((STR CHARSET . OLDPTR)
  (\STRINGTRIMAUX STR CHARSET T . OLDPTR)))

(PUTPROPS STRINGRIGHTTRIM MACRO ((STR CHARSET . OLDPTR)
  (\STRINGTRIMAUX STR CHARSET NIL . OLDPTR)))
)
(DEFINEQ

(MATCHSTRING.FROM.LIST
  (LAMBDA (STR L ALISTFLG)                                   (* JonL "16-SEP-83 15:10")

          (* Searches the car's of L for a string for which STR is an initial substring. Returns the unique best match, if 
	  there is one, or a list of all matches if there is more than one (with the exact match at the beginning, if there 
	  is one) or NIL if there are no matches.)


    (\RS232.CHECKUART)
    (if (NOT (STRINGP STR))
	then (SETQ STR (MKSTRING STR))
	     (\RS232.CHECKUART))
    (for Y in L bind ITEM EXACTMATCH FIRSTPARTIALMATCH MULTIPLES TESTSTR STRPTR (#CHARS.STR
		       ←(STRINGNCHARS STR))
       do (SETQ ITEM (if ALISTFLG
			 then (CAR Y)
		       else Y))
	  (if (NOT (STRINGP (SETQ TESTSTR ITEM)))
	      then (if (NULL STRPTR)
		       then (SETQ STRPTR (ALLOCSTRING 0))
			    (\RS232.CHECKUART))
		   (SETQ TESTSTR (SUBSTRING TESTSTR 1 NIL STRPTR))
		   (\RS232.CHECKUART))
	  (if (PROG1 (STRPOS STR TESTSTR 1 NIL T)
		     (\RS232.CHECKUART))
	      then (if (EQ #CHARS.STR (STRINGNCHARS TESTSTR))
		       then                                  (* AHA! An exact match!)
			    (SETQ EXACTMATCH ITEM)
		     elseif (NOT FIRSTPARTIALMATCH)
		       then (SETQ FIRSTPARTIALMATCH ITEM)
		     elseif (NULL MULTIPLES)
		       then (SETQ MULTIPLES (LIST ITEM FIRSTPARTIALMATCH))
		     else (push MULTIPLES ITEM)
			  (\RS232.CHECKUART)))
       finally (RETURN (PROG1 (if (NULL MULTIPLES)
				  then (OR EXACTMATCH FIRSTPARTIALMATCH)
				else (if EXACTMATCH
					 then (push MULTIPLES EXACTMATCH))
				     MULTIPLES)
			      (\RS232.CHECKUART))))))

(FIND.MAXIMAL.SUBSTRING
  (LAMBDA (L)                                                (* JonL "15-SEP-83 14:40")
    (if (NLISTP L)
	then (ALLOCSTRING 0)
      else ((LAMBDA (MINLENGTHWORD MINLENGTH N)
	       (for X in (CDR L) when (IGREATERP MINLENGTH (SETQ N (NCHARS X)))
		  do (SETQ MINLENGTHWORD X)
		     (SETQ MINLENGTH N))
	       (for I to MINLENGTH until (PROG2 (SETQ N (NTHCHARCODE MINLENGTHWORD I))
						(find X in L suchthat (NEQ N (NTHCHARCODE X I))))
		  do NIL finally (RETURN (SUBSTRING MINLENGTHWORD 1 (SUB1 I)))))
	     (CAR L)
	     (NCHARS (CAR L))))))

(STRINGTRIM
  (LAMBDA (STR CHARSET OLDPTR)                               (* JonL "27-JAN-83 19:29")
    (OR (STRINGP OLDPTR)
	(AND OLDPTR (\ILLEGAL.ARG OLDPTR))
	(SETQ OLDPTR (ALLOCSTRING 0)))
    (\MACRO.MX (STRINGTRIM STR CHARSET OLDPTR))))

(STRINGLEFTTRIM
  (LAMBDA (STR CHARSET OLDPTR)                               (* JonL "27-JAN-83 19:23")
    (\MACRO.MX (STRINGLEFTTRIM STR CHARSET OLDPTR))))

(STRINGRIGHTTRIM
  (LAMBDA (STR CHARSET OLDPTR)                               (* JonL "27-JAN-83 19:23")
    (\MACRO.MX (STRINGRIGHTTRIM STR CHARSET OLDPTR))))

(\STRINGTRIMAUX
  (LAMBDA (STR CHARSET LEFTP OLDPTR)                         (* JonL "14-SEP-83 21:39")
    (SETQ STR (\DTEST STR (QUOTE STRINGP)))
    (if (NOT (STRINGP OLDPTR))
	then (SETQ OLDPTR (ALLOCSTRING 0)))
    (PROG ((CHARTABLEP (type? CHARTABLE CHARSET))
	   (BASE (fetch (STRINGP BASE) of STR))
	   (OFFST (fetch (STRINGP OFFST) of STR))
	   (STRLEN (fetch (STRINGP LENGTH) of STR))
	   (INCREMENT (if LEFTP
			  then 1
			else -1))
	   (#STRIPPED 0))
          (GLOBALRESOURCE (\STRINGTRIM.BITTABLE)
			  (if (NOT CHARTABLEP)
			      then (\ZEROBYTES \STRINGTRIM.BITTABLE 0 (CONSTANT (SUB1 (EXPT 2 
										      BITSPERBYTE))))
				   (if (LISTP CHARSET)
				       then (for J in CHARSET do (\STRINGTRIMAUX1 
									     \STRINGTRIM.BITTABLE J))
				     elseif (STRINGP CHARSET)
				       then (for J instring CHARSET do (\STRINGTRIMAUX1 
									     \STRINGTRIM.BITTABLE J))
				     else (\ILLEGAL.ARG CHARSET)))
			  (for old #STRIPPED from 0 to (SUB1 STRLEN) as J
			     from (if LEFTP
				      then OFFST
				    else (IPLUS OFFST STRLEN -1))
			     by INCREMENT when (ZEROP (if CHARTABLEP
							  then (\SYNCODE CHARSET (\GETBASEBYTE BASE J)
									 )
							else (\GETBASEBIT \STRINGTRIM.BITTABLE
									  (\GETBASEBYTE BASE J))))
			     do                              (* So not all the characters of the string were excluded
							     by the test)
				(\FILLINSTRPTR OLDPTR BASE (if LEFTP
							       then (IPLUS OFFST #STRIPPED)
							     else OFFST)
					       (IDIFFERENCE STRLEN #STRIPPED))
				(RETURN)
			     finally (\FILLINSTRPTR OLDPTR NIL 0 0))))
    OLDPTR))

(\STRINGTRIMAUX1
  (LAMBDA (TABLE J)                                          (* JonL "27-JAN-83 19:32")
    (\PUTBASEBIT TABLE (if (CHARCODEP J)
			   then J
			 else (CHCON1 J))
		 1)))
)
(DECLARE: DONTEVAL@LOAD DOEVAL@COMPILE DONTCOPY COMPILERVARS 

(ADDTOVAR NLAMA )

(ADDTOVAR NLAML )

(ADDTOVAR LAMA \RS232EXEC.LOGFILE.NOTE)
)
(PUTPROPS RS232EXEC COPYRIGHT ("Xerox Corporation" 1983))
(DECLARE: DONTCOPY
  (FILEMAP (NIL (3370 28415 (RS232EXEC.ADDUSER 3380 . 4090) (RS232EXEC.DELUSER 4092 . 4421) (
RS232EXEC.SERVER 4423 . 12650) (\RS232EXEC.CHECK.MODEMANSWER 12652 . 14163) (\RS232EXECSERVER.TERPRI 
14165 . 14657) (\RS232EXEC.LOGFILE.NOTE 14659 . 15004) (RS232READ&ECHO.LINE 15006 . 21663) (
\RS232.DSPRUBOUTCHAR 21665 . 22058) (\RS232COMMAND.EVAL 22060 . 22490) (\RS232COMMAND.TERM 22492 . 
23539) (\RS232COMMAND.SPEED 23541 . 24305) (\RS232COMMAND.LOGIN 24307 . 26823) (\RS232COMMAND.LOGOUT 
26825 . 27337) (\RS232COMMAND.CONN 27339 . 28413)) (30680 35705 (MATCHSTRING.FROM.LIST 30690 . 32429) 
(FIND.MAXIMAL.SUBSTRING 32431 . 33073) (STRINGTRIM 33075 . 33329) (STRINGLEFTTRIM 33331 . 33500) (
STRINGRIGHTTRIM 33502 . 33673) (\STRINGTRIMAUX 33675 . 35494) (\STRINGTRIMAUX1 35496 . 35703)))))
STOP