1
0
mirror of https://github.com/PDP-10/its.git synced 2026-02-24 16:17:26 +00:00
Files
PDP-10.its/src/math/kermit.170
2018-06-16 07:09:24 -07:00

1141 lines
35 KiB
Common Lisp
Executable File
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
;;; -*- Mode: LISP; Syntax: Common-Lisp; Package: (KERMIT LISP) -*-
;;; Common Lisp KERMIT
;;; Authorship Information:
;;; Mark David (LMI) Original version, using KERMIT.C as a guide
;;; George Carrette (LMI) Various enhancements
;;; Mark Ahlstrom (Honeywell) Port to 3600 (edits marked with ";1;")
;;; Jonathan Rees (MIT) Major total rewrite for Maclisp/Common Lisp
;;; Don't make any attempt to understand this code without having access
;;; to the Kermit Protocol Manual, Frank Da Cruz, Columbia University,
;;; 5th edition.
;;; This code is written in Common Lisp, but will run in Maclisp given
;;; a Common Lisp compatibility mode (see AI: MATH; COMMON >). To
;;; port to an arbitrary CL implementation, one must supply
;;; appropriate low-level primitives for doing read-with-timeout and
;;; no-echo terminal I/O.
;;; I think the main thing making it not yet usable outside of Maclisp
;;; is that it doesn't convert CR/LF into NEWLINE on the way in and out.
;;;+ Bugs
;;; What to do about the control-C's at the end of ITS files?
;;; There's apparently no way to distinguish them from legitimate control-C's.
;;; Right now they're interpreted exactly the same as end of file.
;;;
;;; The receive packet size rightly ought to be about 93 or so, but
;;; for some reason can't RECEIVE packets bigger than about 64 bytes.
;;; For the life of me I can't figure out why. Debug this some day.
;;; [And due to a bug in Kermit-9000, I have set the packet size down
;;; to 50 (probably should be 32) to try to make SURE...]
;;; The low level 3600 hacks are undebugged!!
;;;-
#+Maclisp
(EVAL-WHEN (EVAL LOAD COMPILE)
(OR (GET 'COMMON 'LOADEDP) (LOAD '((DSK MATH) COMMON))))
(IN-PACKAGE 'KERMIT :USE '(LISP))
(SHADOW '(LOG))
(EXPORT '(SERVE
SET-LINE
GET-FILE
SEND-FILE
FINISH
BYE))
(DEFVAR *KERMIT-VERSION*
#+Maclisp (STATUS OPSYS)
#+Symbolics "Symbolics"
#-(or Maclisp Symbolics) (LISP-IMPLEMENTATION-TYPE))
#+Maclisp
(PROGN 'COMPILE
(DECLARE (*LEXPR SEND-ERROR-PACKET INIT-SERIAL-I/O))
(DECLARE (FIXNUM (SERIAL-WRITE-CHAR FIXNUM))
(FIXNUM (SERIAL-READ-CHAR))))
;;; Fundamental constants
(DEFCONSTANT *SOH* (CODE-CHAR 1) "start of header")
(DEFCONSTANT *MAX-PACKET-SIZE* (- 128. 32.) "maximum packet size")
;;; The following three values merely need to answer true to FAILP
;;; and not be legal packet types.
(DEFCONSTANT *TIMED-OUT* (CODE-CHAR 2))
(DEFCONSTANT *LENGTH-ERROR* (CODE-CHAR 3))
(DEFCONSTANT *CHECKSUM-ERROR* (CODE-CHAR 4))
(DEFCONSTANT *BOGUS-CHARACTER* (CODE-CHAR 5))
;;; If you want Kermit to be re-entrant you'll have to specbind everything
;;; from here on down, excpting the state machine variables which are
;;; bound by RUN-STATE-MACHINE.
;;; Global parameters, user-adjustable
(DEFVAR *IMAGE?* NIL "T means 8-bit mode")
(DEFVAR *DEBUG?* NIL "T means supply debugging info as you run")
(DEFVAR *VERSION-NUMBERS?* NIL "T means include version numbers in file names")
(DEFVAR *MAX-RETRY-COUNT* 10. "times to retry a packet")
(DEFVAR *MIN-TIMEOUT* 2 "minimum timeout interval in seconds")
(DEFVAR *SERVER-TIMEOUT* 30 "timeout interval when in server mode")
(DEFVAR *SERIAL-INPUT* NIL "input stream for serial line")
(DEFVAR *SERIAL-OUTPUT* NIL "output stream for serial line")
(DEFVAR *COMMENTARY-STREAM* *TERMINAL-IO* "stream for messages to user")
(DEFVAR *LOG* NIL "log file")
;;; Packet reception parameters (see KPM page 26):
(DEFVAR *RECEIVE-PACKET-SIZE* 50 "maximum packet size") ;see bug remark, above
(DEFVAR *MY-TIMEOUT* 10 "seconds after which I time out")
(DEFVAR *MY-PAD* 0 "number of padding characters I need")
(DEFVAR *MY-PAD-CHAR* #\SPACE "char I want as a padding char")
(DEFVAR *MY-EOL* #\RETURN "my kind of return char")
(DEFVAR *MY-QUOTE* #\# "my quote character")
;;; Packet transmission parameters:
(DEFVAR *SEND-PACKET-SIZE* 80 "maximum send packet size") ;MAXL
(DEFVAR *TIMEOUT* 15 "timeout for remote host on sends") ;TIME
(DEFVAR *PAD* 0 "how much padding to send") ;NPAD
(DEFVAR *PAD-CHAR* #\SPACE "padding character to send") ;PADC
(DEFVAR *EOL* #\RETURN "end-of-line character to send") ;EOL
(DEFVAR *QUOTE* #\# "quote character in outgoing data") ;QCTL
;;; Packet buffers, globally allocated for no very good reason
(DEFPARAMETER *RECEIVE-PACKET*
(MAKE-ARRAY *MAX-PACKET-SIZE* :ELEMENT-TYPE 'STRING-CHAR :FILL-POINTER 0)
"receive packet buffer")
(DEFPARAMETER *SEND-PACKET*
(MAKE-ARRAY *MAX-PACKET-SIZE* :ELEMENT-TYPE 'STRING-CHAR :FILL-POINTER 0)
"send packet buffer")
;;; State machine variables, bound by RUN-STATE-MACHINE
(DEFVAR *SERVER-MODE* NIL "true if in server mode")
(DEFVAR *FILES-TO-SEND* '() "list of source/dest filename pairs")
(DEFVAR *COMMAND* NIL "generic or other command to execute")
(DEFVAR *SEQUENCE-NUMBER* 0 "the packet number")
(DEFVAR *RETRY-COUNT* 0 "times this packet retried")
(DEFVAR *STREAM* NIL "file pointer for current disk file")
(DEFVAR *DISCARD* NIL "T if current file is to be discarded")
;;; Utility routines & macros
#-(OR Maclisp Symbolics) ;COMMON > defines this for Maclisp
(DEFMACRO DEFSUBST (NAME ARGS &BODY BODY)
`(PROGN 'COMPILE
(DEFUN ,NAME ,ARGS ,@BODY)
(PROCLAIM '(INLINE ,NAME))))
#+Symbolics
(DEFMACRO DEFSUBST (NAME ARGS &BODY BODY)
`(SCL:DEFSUBST ,NAME ,ARGS ,@BODY))
#-Maclisp
(PROGN 'COMPILE
(DEFSUBST STRING-ELT (S I) ;Not the same as CHAR!
(AREF (THE STRING S) I))
(DEFSUBST SET-STRING-ELT (S I C)
(SETF (AREF (THE STRING S) I) C))
(DEFMACRO UNKLUDGIFY-STRING (S)
S)
(DEFUN QUIT () NIL)
) ;psilcaM-#
(DEFMACRO MESSAGE (&REST CRUFT)
`(IF (OR (NOT *SERVER-MODE*) *DEBUG?* *LOG*)
(FORMAT *COMMENTARY-STREAM* ,@CRUFT)))
(DEFMACRO DEBUG-MESSAGE (&REST CRUFT)
`(IF *DEBUG?*
(FORMAT *DEBUG-IO* ,@CRUFT)))
(DEFMACRO STRING-APPEND (&REST STRINGS)
`(CONCATENATE 'STRING ,@STRINGS))
#-Maclisp
(PROGN 'COMPILE
;;; Character-set-independent ASCII character conversion
(DEFPARAMETER ASCII-CHARS
(CONCATENATE 'VECTOR
'(#\NULL)
(MAKE-LIST 7)
'(#\BACKSPACE #\TAB #\NEWLINE NIL #\PAGE #\RETURN)
(MAKE-LIST 18)
" !\"#$%&'()*+,-./0123456789:;<=>?"
"@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
"`abcdefghijklmnopqrstuvwxyz{|}~"
'(#\RUBOUT)))
(DEFUN ASCII->CHAR (N)
(OR (SVREF ASCII-CHARS N)
(CODE-CHAR N)))
(DEFPARAMETER NATIVE-CHARS
(LET ((TABLE (MAKE-HASH-TABLE :TEST 'EQUAL :SIZE 128))
(LEAST 10000000)
(GREATEST -10000000))
(DO ((I 0 (+ I 1)))
((>= I (LENGTH ASCII-CHARS))
(LET ((V (MAKE-ARRAY (+ (- GREATEST LEAST) 1))))
(DO ((I LEAST (+ I 1)))
((> I GREATEST) (CONS LEAST V))
(SETF (SVREF V (- I LEAST)) (GETHASH I TABLE)))))
(LET ((CH (SVREF ASCII-CHARS I)))
(WHEN CH
(LET ((CODE (CHAR-CODE CH)))
(SETQ LEAST (MIN LEAST CODE))
(SETQ GREATEST (MAX GREATEST CODE))
(SETF (GETHASH CODE TABLE) I)))))))
(DEFUN CHAR->ASCII (CHAR)
(LET ((INDEX (- (CHAR-CODE CHAR) (CAR NATIVE-CHARS))))
(IF (OR (< INDEX 0)
(>= INDEX (LENGTH (CDR NATIVE-CHARS))))
(CHAR-CODE CHAR)
(OR (SVREF (CDR NATIVE-CHARS) INDEX)
(CHAR-CODE CHAR)))))
)
#+Maclisp
(PROGN 'COMPILE
(DEFMACRO CHAR->ASCII (C) C)
(DEFMACRO ASCII->CHAR (N) N)
)
;;; INTEGER->GRAPHIC: converts a number to a graphic character by adding
;;; 32 and deasciizing.
;;; GRAPHIC->INTEGER: undoes INTEGER->GRAPHIC.
;;; CTL: converts between control characters and printable characters by
;;; toggling the control bit (i.e. ^a becomes a and a becomes ^a).
(DEFSUBST INTEGER->GRAPHIC (N)
(ASCII->CHAR (+ N 32.)))
(DEFSUBST GRAPHIC->INTEGER (CH)
(- (CHAR->ASCII CH) 32.))
(DEFSUBST CTL (CH)
(ASCII->CHAR (LOGXOR (CHAR->ASCII CH) #o100)))
(DEFSUBST COMPUTE-FINAL-CHECKSUM (CHKSUM)
(LOGAND (+ (ASH (LOGAND CHKSUM #o0300) -6) CHKSUM) #o077))
;;; ACKP, NACKP, ERRP, FAILP
;;; Predicates applied to packet type returned by RECEIVE-PACKET:
;;; an ACK (Y), a NACK (N), an error message (E), or a failed
;;; packet transmission (timeout, bad checksum, or illegal length).
(DEFSUBST ACKP (TYPE)
(CHAR= TYPE #\Y))
(DEFSUBST NACKP (TYPE)
(CHAR= TYPE #\N))
(DEFSUBST ERRP (TYPE)
(CHAR= TYPE #\E))
(DEFSUBST FAILP (TYPE)
(CHAR< TYPE #\SPACE))
;;; Top-level entry routines
;;; Specify a terminal line [e.g. (SET-LINE "TTA1:")]:
(DEFUN SET-LINE (LINE)
(INIT-SERIAL-I/O LINE))
;;; Server mode:
(DEFUN SERVE () ;See LIBDOC; TTY >
(FORMAT *COMMENTARY-STREAM*
"~&Entering KERMIT server mode.~
~%Control-G throws to Lisp top level, (SERVE) starts serving again.~%")
;; Kludge!!! Logging isn't done right at all. Ought to use broadcast stream
;; so that logging works even when not in server mode.
(IF *LOG*
(FORMAT *LOG* "~2&----- Entering server mode -----~&"))
(UNWIND-PROTECT
(LET ((*COMMENTARY-STREAM* (OR *LOG* *COMMENTARY-STREAM*))
(*DEBUG-IO* (OR *LOG* *DEBUG-IO*)))
(LET ((STATUS (RUN-STATE-MACHINE #'RECEIVE-SERVER-IDLE T '() NIL)))
(IF (EQ STATUS 'LOGOUT) (QUIT) STATUS)))
(IF *LOG* (FORMAT *LOG* "~&----- Leaving server mode -----~&"))))
;;; Send a file:
(DEFUN SEND-FILE (LOCAL-FILESPEC &OPTIONAL REMOTE-FILESPEC)
(LET ((XFER-LIST (MAKE-TRANSFER-LIST LOCAL-FILESPEC REMOTE-FILESPEC)))
(COND ((NULL XFER-LIST)
(MESSAGE "~&No files match this specification: ~S" LOCAL-FILESPEC))
(T
(RUN-STATE-MACHINE #'SEND-INIT NIL XFER-LIST NIL)))))
;;; Get a file:
(DEFUN GET-FILE (REMOTE-SPEC &OPTIONAL LOCAL-SPEC)
(RUN-STATE-MACHINE #'SEND-MISC
NIL
'()
`(GET ,REMOTE-SPEC ,LOCAL-SPEC)))
;;; Receive file: useless, but included for completeness.
(DEFUN RECEIVE-FILE (&OPTIONAL LOCAL-SPEC)
(RUN-STATE-MACHINE #'RECEIVE-INIT
NIL
'()
`(GET NIL ,LOCAL-SPEC)))
;;; Tell remote server to go away:
(DEFUN FINISH ()
(RUN-STATE-MACHINE #'SEND-MISC NIL '() '(FINISH)))
(DEFUN BYE ()
(RUN-STATE-MACHINE #'SEND-MISC NIL '() '(BYE)))
(DEFUN LOG (&OPTIONAL PATH)
(COND ((NULL PATH)
(COND ((NULL *LOG*)
(FORMAT T "~&There is no log file to close."))
(T
(FORMAT T "~&Closing log file ~s" (TRUENAME *LOG*))
(LISP:CLOSE *LOG*)
(SETQ *LOG* NIL))))
((NULL *LOG*)
(SETQ *LOG* (LISP:OPEN PATH :DIRECTION :OUTPUT))
(FORMAT T "~&Logging on ~s" (TRUENAME *LOG*)))))
;;; The KERMIT state machine
(DEFUN RUN-STATE-MACHINE (STATE *SERVER-MODE*
*FILES-TO-SEND*
*COMMAND*)
(IF (NULL *SERIAL-INPUT*) (INIT-SERIAL-I/O))
(LET ((TEMP (SERIAL-STATE)))
(UNWIND-PROTECT
(PROGN (DISABLE-ECHOING TEMP)
(FLUSH-INPUT)
(LISP:CATCH 'DONE
(DO ()
(NIL)
(LISP:CATCH 'ABORT
(UNWIND-PROTECT
(LET ((*SEQUENCE-NUMBER* 0)
(*RETRY-COUNT* 0)
(*DISCARD* NIL)
(*TIMEOUT* *TIMEOUT*))
(DO ((STATE STATE (FUNCALL STATE)))
(NIL)
;; (DEBUG-MESSAGE "~&State = ~S~&" STATE)
))
(COND (*STREAM*
(LISP:CLOSE *STREAM* :ABORT T)
(SETQ *STREAM* NIL)))))
(IF (NOT *SERVER-MODE*) (RETURN 'ABORTED)))))
(SET-SERIAL-STATE TEMP))))
(DEFMACRO DEFSTATE (NAME &REST BODY)
`(DEFUN ,NAME () ,@BODY))
;(DEFMACRO GOTO-STATE (STATE)
; `#',STATE)
(defmacro goto-state (state)
`(%goto-state #',state))
(defun %goto-state (state)
(if (null state) (error "null state") state))
(DEFUN ABORT ()
(DEBUG-MESSAGE "~&Aborting.")
(LISP:THROW 'ABORT NIL))
(DEFUN COMPLETE ()
(IF *SERVER-MODE*
(GOTO-STATE RECEIVE-SERVER-IDLE)
(LISP:THROW 'DONE 'COMPLETE)))
(DEFSUBST NEXT-SEQUENCE-NUMBER ()
(LOGAND (1+ *SEQUENCE-NUMBER*) #o77))
(DEFSUBST PREVIOUS-SEQUENCE-NUMBER ()
(LOGAND (+ *SEQUENCE-NUMBER* #o77) #o77))
(DEFUN BUMP-SEQUENCE-NUMBER ()
(SETQ *SEQUENCE-NUMBER* (NEXT-SEQUENCE-NUMBER))
(SETQ *RETRY-COUNT* 0))
(DEFUN BUMP-RETRY-COUNT ()
(INCF *RETRY-COUNT*)
(COND ((> *RETRY-COUNT* *MAX-RETRY-COUNT*)
(SEND-ERROR-PACKET "aborting after ~D retries" *MAX-RETRY-COUNT*)
(ABORT))))
;;; Rec_Server_Idle -- Server idle, waiting for a message
(DEFSTATE RECEIVE-SERVER-IDLE
(SETQ *SEQUENCE-NUMBER* 0)
(SETQ *TIMEOUT* *SERVER-TIMEOUT*) ;will get reset later
(MULTIPLE-VALUE-BIND (TYPE NUM DATA)
(RECEIVE-PACKET)
(COND ((FAILP TYPE)
(SEND-NACK 0)
(GOTO-STATE RECEIVE-SERVER-IDLE))
((ERRP TYPE)
;; Error has already been reported, so don't worry about it.
(GOTO-STATE RECEIVE-SERVER-IDLE))
((NOT (= NUM 0))
(SEND-ERROR-PACKET "bad sequence number - ~S" NUM)
(GOTO-STATE RECEIVE-SERVER-IDLE))
((CHAR= TYPE #\I)
;; Initialize & send ACK
(INITIALIZE-PARAMETERS DATA)
(GOTO-STATE RECEIVE-SERVER-IDLE))
((CHAR= TYPE #\S)
;; Remote host wants to send a file; we should prepare to receive.
(INITIALIZE-PARAMETERS DATA)
(BUMP-SEQUENCE-NUMBER)
(SETQ *COMMAND* '(GET NIL NIL))
(GOTO-STATE RECEIVE-FILE-HEADER))
((CHAR= TYPE #\R)
;; Remote host wants to receive a file, i.e. we should send it.
(SETQ *FILES-TO-SEND*
(MAKE-TRANSFER-LIST (UNKLUDGIFY-STRING DATA) NIL))
(COND ((NULL *FILES-TO-SEND*)
(SEND-ERROR-PACKET "no files matching this specification")
(GOTO-STATE RECEIVE-SERVER-IDLE))
(T
(DEBUG-MESSAGE "~&Files to send: ~S" *FILES-TO-SEND*)
(GOTO-STATE SEND-INIT))))
((CHAR= TYPE #\G)
(COND ((< (LISP:LENGTH DATA) 1)
(SEND-ERROR-PACKET "ill-formed generic command packet")
(GOTO-STATE RECEIVE-SERVER-IDLE))
(T
(PERFORM-GENERIC-COMMAND DATA))))
(T
(SEND-ERROR-PACKET "unimplemented server command - ~C" TYPE)
(GOTO-STATE RECEIVE-SERVER-IDLE)))))
(DEFUN PERFORM-GENERIC-COMMAND (DATA)
(CASE (STRING-ELT DATA 0)
((#\F) ;Finished
(SEND-ACK *SEQUENCE-NUMBER*)
(LISP:THROW 'DONE 'FINISHED))
((#\L) ;Logout
(SEND-ACK *SEQUENCE-NUMBER*)
(LISP:THROW 'DONE 'LOGOUT))
((#\C) ;Change working directory
;; Data looks something like
;; C$directory#password (I think)
;; where $ is the length of the directory name (! = 1, etc.)
;; and # is the length of the password.
;; This has only been tested with Kermit-32 (on VMS), and I haven't
;; checked the protocol manual to see what's really supposed to
;; happen. -- JAR 3 March 1988
(IF (> (LISP:LENGTH DATA) 3)
(LET ((END (MIN (LISP:LENGTH DATA)
(+ 2 (GRAPHIC->INTEGER (AREF DATA 1))))))
(DO ((I 2 (1+ I))
(L '() (CONS (AREF DATA I) L)))
((>= I END)
(LET ((DIR (COERCE (REVERSE L) 'STRING)))
(MESSAGE "~&Directory ~S" DIR)
(SETQ *DEFAULT-PATHNAME-DEFAULTS* (PATHNAME DIR))
(SEND-ACK 0)))))
(SEND-ERROR-PACKET "missing directory name or ill-formed packet - ~A"
(UNKLUDGIFY-STRING DATA)))
(GOTO-STATE RECEIVE-SERVER-IDLE))
;; Unimplemented: Directory, diskUsage,
;; dEletefile, Type, Rename, Kopy, Whoisloggedin,
;; sendMessage, runProgram, Journal, Variable
(T
(SEND-ERROR-PACKET "unimplemented generic command - ~A"
(UNKLUDGIFY-STRING DATA))
(GOTO-STATE RECEIVE-SERVER-IDLE))))
;;; Rec_Init - entry point for non-server RECEIVE command (pretty useless)
;;; *COMMAND* should be (GET remote local)
(DEFSTATE RECEIVE-INIT
(SETQ *SEQUENCE-NUMBER* 0)
(MULTIPLE-VALUE-BIND (TYPE NUM DATA)
(RECEIVE-PACKET)
(COND ((AND (CHAR= TYPE #\S) (= NUM 0))
(INITIALIZE-PARAMETERS DATA)
(BUMP-SEQUENCE-NUMBER)
(GOTO-STATE RECEIVE-FILE-HEADER))
((FAILP TYPE)
(SEND-NACK 0)
(BUMP-RETRY-COUNT)
(GOTO-STATE RECEIVE-INIT))
(T
(SEND-NACK 0)
(ABORT)))))
;;; Rec_File -- look for a file header or EOT message
;;; *COMMAND* should be (GET remote local)
(DEFSTATE RECEIVE-FILE-HEADER
(MULTIPLE-VALUE-BIND (TYPE NUM PACKET)
(RECEIVE-PACKET)
(COND ((AND (CHAR= TYPE #\F) (= NUM *SEQUENCE-NUMBER*))
;; File header; ignore it if a local name was specified.
(LET* ((REMOTE (UNKLUDGIFY-STRING PACKET))
(LOCAL (OR (CADDR *COMMAND*) (CADR *COMMAND*)))
(LOCAL (IF LOCAL
(MERGE-PATHNAMES (FLUSH-WILDCARDS LOCAL) REMOTE)
REMOTE)))
(COND ((SETQ *STREAM* (MAYBE-OPEN-FOR-OUTPUT LOCAL))
(MESSAGE "~&Receiving ~A as ~A"
REMOTE
(LISP:NAMESTRING LOCAL))
(SEND-ACK NUM)
(BUMP-SEQUENCE-NUMBER)
(GOTO-STATE RECEIVE-DATA))
(T
(MESSAGE "~&Can't create ~S" LOCAL)
(SEND-ERROR-PACKET "Can't create ~S" LOCAL)
(ABORT)))))
((AND (CHAR= TYPE #\B) (= NUM *SEQUENCE-NUMBER*))
;; End of transmission
(SEND-ACK NUM)
(COMPLETE))
((AND (CHAR= TYPE #\S) (= NUM (PREVIOUS-SEQUENCE-NUMBER)))
;; Send init
(SEND-PARAMETERS #\Y NUM)
(BUMP-RETRY-COUNT)
(GOTO-STATE RECEIVE-FILE-HEADER))
((AND (CHAR= TYPE #\Z) (= NUM (PREVIOUS-SEQUENCE-NUMBER)))
;; End of file
(SEND-ACK NUM)
(BUMP-RETRY-COUNT)
(GOTO-STATE RECEIVE-FILE-HEADER))
((FAILP TYPE)
;; Timeout
(SEND-NACK *SEQUENCE-NUMBER*)
(GOTO-STATE RECEIVE-FILE-HEADER))
(T
(SEND-ERROR-PACKET "error receiving file header")
(ABORT)))))
;;; Rec_Data - Receive data up to end of file
(DEFSTATE RECEIVE-DATA
(MULTIPLE-VALUE-BIND (TYPE NUM DATA)
(RECEIVE-PACKET)
(COND ((AND (CHAR= TYPE #\D) (= NUM *SEQUENCE-NUMBER*))
(EMPTY-BUFFER DATA)
(SEND-ACK NUM)
(BUMP-SEQUENCE-NUMBER)
(GOTO-STATE RECEIVE-DATA))
((AND (OR (CHAR= TYPE #\D) (CHAR= TYPE #\F))
(= NUM (PREVIOUS-SEQUENCE-NUMBER)))
(SEND-ACK NUM)
(BUMP-RETRY-COUNT)
(GOTO-STATE RECEIVE-DATA))
((AND (CHAR= TYPE #\Z) (= NUM *SEQUENCE-NUMBER*))
(LET (#+Maclisp
(IO-LOSSAGE #'(LAMBDA IGNORE
(SEND-ERROR-PACKET "error closing file")
(ABORT))))
(COND ((OR (< (LISP:LENGTH DATA) 1)
(NOT (CHAR= (STRING-ELT DATA 0) #\D)))
(MESSAGE "~&File received successfully: ~A"
(LISP:NAMESTRING *STREAM*))
(LISP:CLOSE *STREAM*))
(T
;; Discard
(MESSAGE "~&File discarded: ~A"
(LISP:NAMESTRING *STREAM*))
(LISP:CLOSE *STREAM* :ABORT T)))
(SEND-ACK NUM)
(SETQ *STREAM* NIL)
(BUMP-SEQUENCE-NUMBER)
(GOTO-STATE RECEIVE-FILE-HEADER)))
((FAILP TYPE)
(SEND-NACK *SEQUENCE-NUMBER*)
(BUMP-RETRY-COUNT)
(GOTO-STATE RECEIVE-DATA))
(T
(IF (NOT (ERRP TYPE))
(SEND-ERROR-PACKET "error during data reception"))
(ABORT)))))
;;; Send_Init - entry for SEND command (or remote kermit's GET)
(DEFSTATE SEND-INIT
(SETQ *SEQUENCE-NUMBER* 0)
(SEND-PARAMETERS #\S 0)
(MULTIPLE-VALUE-BIND (REPLY NUM PACKET)
(RECEIVE-PACKET)
(COND ((AND (ACKP REPLY) (= *SEQUENCE-NUMBER* NUM))
(DECODE-PARAMETERS PACKET) ;Check and set defaults
(BUMP-SEQUENCE-NUMBER)
(GOTO-STATE OPEN-FILE))
((ERRP REPLY)
(ABORT))
(T
(GOTO-STATE SEND-INIT)))))
;;; Open_File
(DEFSTATE OPEN-FILE
(COND ((NULL *FILES-TO-SEND*)
(GOTO-STATE SEND-BREAK))
(T
(LET ((LOCAL-FILENAME (CAAR *FILES-TO-SEND*)))
(COND ((NOT (SETQ *STREAM* (MAYBE-OPEN-FOR-INPUT LOCAL-FILENAME)))
;; This ain't right, but what's to do for it??
(MESSAGE "~&Can't open file ~A" LOCAL-FILENAME)
(POP *FILES-TO-SEND*)
(SETQ *DISCARD* T)
(GOTO-STATE SEND-EOF))
(T
(GOTO-STATE SEND-FILE-HEADER)))))))
;;; Send_File
(DEFUN NORMAL-ACKP (TYPE NUM)
(OR (AND (ACKP TYPE) (= NUM *SEQUENCE-NUMBER*))
(AND (NACKP TYPE) (= NUM (NEXT-SEQUENCE-NUMBER)))))
(DEFUN NORMAL-NACKP (TYPE)
(OR (FAILP TYPE)
(NACKP TYPE)))
(DEFSTATE SEND-FILE-HEADER
(SEND-PACKET #\F *SEQUENCE-NUMBER*
(KERMITIFY-PATHNAME (CDAR *FILES-TO-SEND*)
*VERSION-NUMBERS?*))
(MULTIPLE-VALUE-BIND (REPLY NUM IGNORE)
(RECEIVE-PACKET)
IGNORE
(COND ((NORMAL-ACKP REPLY NUM)
(BUMP-SEQUENCE-NUMBER)
(GOTO-STATE SEND-DATA))
((NORMAL-NACKP REPLY)
(BUMP-RETRY-COUNT)
(GOTO-STATE SEND-FILE-HEADER))
(T
(IF (NOT (ERRP REPLY))
(SEND-ERROR-PACKET "error sending file header"))
(ABORT)))))
;;; Send_Data -- Send contents of file
(DEFSTATE SEND-DATA
(IF (= *RETRY-COUNT* 0)
(FILL-BUFFER *SEND-PACKET*))
(COND ((= (LISP:LENGTH *SEND-PACKET*) 0)
(GOTO-STATE SEND-EOF))
(T
(SEND-PACKET #\D *SEQUENCE-NUMBER* *SEND-PACKET*)
(MULTIPLE-VALUE-BIND (REPLY NUM PACKET)
(RECEIVE-PACKET)
(COND ((NORMAL-ACKP REPLY NUM)
(BUMP-SEQUENCE-NUMBER)
(COND ((AND (> (LISP:LENGTH PACKET) 0)
(OR (CHAR= (STRING-ELT PACKET 0) #\X)
(CHAR= (STRING-ELT PACKET 0) #\Z)))
(SETQ *DISCARD* T)
(GOTO-STATE SEND-EOF))
(T
(GOTO-STATE SEND-DATA))))
((NORMAL-NACKP REPLY)
(BUMP-RETRY-COUNT)
(GOTO-STATE SEND-DATA))
(T
(IF (NOT (ERRP REPLY))
(SEND-ERROR-PACKET "error during data transmission"))
(ABORT)))))))
;;; Send_EOF -- Send end of file indicator
(DEFSTATE SEND-EOF
(SEND-PACKET #\Z *SEQUENCE-NUMBER* (IF *DISCARD* "Z" NIL))
(MULTIPLE-VALUE-BIND (REPLY NUM IGNORE)
(RECEIVE-PACKET)
IGNORE
(COND ((NORMAL-ACKP REPLY NUM)
(MESSAGE "~&File sent successfully: ~A~%"
(TRUENAME *STREAM*))
(CLOSE *STREAM*)
(SETQ *STREAM* NIL)
(POP *FILES-TO-SEND*)
(BUMP-SEQUENCE-NUMBER)
(SETQ *DISCARD* NIL)
(GOTO-STATE OPEN-FILE))
((NORMAL-NACKP REPLY)
(GOTO-STATE SEND-EOF))
(T
(SETQ *DISCARD* NIL)
(IF (NOT (ERRP REPLY))
(SEND-ERROR-PACKET "error during EOF transmission"))
(ABORT)))))
;;; Send_Break - End of transaction
(DEFSTATE SEND-BREAK
(SEND-PACKET #\B *SEQUENCE-NUMBER* NIL)
(MULTIPLE-VALUE-BIND (REPLY NUM IGNORE)
(RECEIVE-PACKET)
IGNORE
(COND ((OR (AND (ACKP REPLY) (= NUM *SEQUENCE-NUMBER*))
(AND (NACKP REPLY) (= NUM 0)))
(COMPLETE))
((OR (AND (NACKP REPLY) (= NUM *SEQUENCE-NUMBER*))
(FAILP REPLY))
(GOTO-STATE SEND-BREAK))
(T
(IF (NOT (ERRP REPLY))
(SEND-ERROR-PACKET "error during break transmission"))
(ABORT)))))
;;; Send_Gen_Cmd - Command to server
(DEFSTATE SEND-MISC
(CASE (CAR *COMMAND*)
((FINISH) (SEND-PACKET #\G 0 "F"))
((LOGOUT) (SEND-PACKET #\G 0 "L"))
((GET)
(SEND-PACKET #\R 0 (KERMITIFY-PATHNAME (CADR *COMMAND*)
*VERSION-NUMBERS?*)))
(OTHERWISE (ERROR "weird command in SEND-MISC" *COMMAND* 'FAIL-ACT)))
(MULTIPLE-VALUE-BIND (TYPE NUM DATA)
(RECEIVE-PACKET)
(COND ((AND (ACKP TYPE) (= NUM 0))
(MESSAGE "~&OK")
(COMPLETE))
((AND (CHAR= TYPE #\S) (= NUM 0) (>= (LISP:LENGTH DATA) 6))
(DECODE-PARAMETERS DATA)
(SEND-PARAMETERS #\Y 0)
(BUMP-SEQUENCE-NUMBER)
(GOTO-STATE RECEIVE-FILE-HEADER))
((NORMAL-NACKP TYPE)
(BUMP-RETRY-COUNT)
(GOTO-STATE SEND-MISC))
(T
(IF (NOT (ERRP TYPE))
(SEND-ERROR-PACKET "error during command transmission"))
(ABORT)))))
;;; Packet sending and receiving
(DEFUN SEND-ERROR-PACKET (&REST CRUFT)
(LET* ((FROB (LISP:APPLY #'FORMAT NIL CRUFT))
(FROB (FORMAT NIL "Kermit/~A: ~A" *KERMIT-VERSION* FROB)))
(DEBUG-MESSAGE "~&Sending error packet: ~A" FROB)
(SEND-PACKET #\E *SEQUENCE-NUMBER* FROB)))
(DEFUN SEND-ACK (NUM)
(SEND-PACKET #\Y NUM NIL))
(DEFUN SEND-NACK (NUM)
(SEND-PACKET #\N NUM NIL))
;;; Remember remote parameters and ackowledge with ours
(DEFUN INITIALIZE-PARAMETERS (DATA)
(DECODE-PARAMETERS DATA)
(SEND-PARAMETERS #\Y *SEQUENCE-NUMBER*))
(DEFUN DECODE-PARAMETERS (DATA)
(SETQ *SEND-PACKET-SIZE* (GRAPHIC->INTEGER (STRING-ELT DATA 0)))
(SETQ *TIMEOUT* (GRAPHIC->INTEGER (STRING-ELT DATA 1)))
(SETQ *PAD* (GRAPHIC->INTEGER (STRING-ELT DATA 2)))
(SETQ *PAD-CHAR* (CTL (STRING-ELT DATA 3)))
(SETQ *EOL* (ASCII->CHAR (GRAPHIC->INTEGER (STRING-ELT DATA 4))))
(SETQ *QUOTE* (STRING-ELT DATA 5)))
(DEFUN SEND-PARAMETERS (TYPE NUM)
(LET ((PACKET *SEND-PACKET*))
(SET-STRING-ELT PACKET 0 (INTEGER->GRAPHIC *RECEIVE-PACKET-SIZE*))
(SET-STRING-ELT PACKET 1 (INTEGER->GRAPHIC *MY-TIMEOUT*))
(SET-STRING-ELT PACKET 2 (INTEGER->GRAPHIC *MY-PAD*))
(SET-STRING-ELT PACKET 3 (CTL *MY-PAD-CHAR*))
(SET-STRING-ELT PACKET 4 (INTEGER->GRAPHIC (CHAR->ASCII *MY-EOL*)))
(SET-STRING-ELT PACKET 5 *MY-QUOTE*)
(SETF (FILL-POINTER PACKET) 6)
(SEND-PACKET TYPE NUM PACKET)))
;;; SEND-PACKET
;;; TYPE -- a number, the type of packet this is.
;;; NUM -- a number, the packet-number of this packet.
;;; DATA -- a string, i.e. an art-string type of array, the data of this pkt.
(DEFUN SEND-PACKET (TYPE NUM DATA)
(LET ((CHKSUM 0)
(LEN (IF DATA (LISP:LENGTH DATA) 0)))
(DECLARE (FIXNUM CHKSUM LEN))
(DEBUG-MESSAGE "~&Sending packet: ")
(DO ((I 0 (1+ I)))
((>= I *PAD*))
(SERIAL-WRITE-CHAR *PAD-CHAR*))
(SERIAL-WRITE-CHAR *SOH*)
(INCF CHKSUM (SERIAL-WRITE-CHAR (INTEGER->GRAPHIC (+ LEN 3))))
(INCF CHKSUM (SERIAL-WRITE-CHAR (INTEGER->GRAPHIC NUM)))
(INCF CHKSUM (SERIAL-WRITE-CHAR TYPE))
(IF DATA
(DO ((I 0 (1+ I)))
((>= I LEN))
(DECLARE (FIXNUM I))
(INCF CHKSUM (SERIAL-WRITE-CHAR (STRING-ELT DATA I)))))
(SERIAL-WRITE-CHAR (INTEGER->GRAPHIC (COMPUTE-FINAL-CHECKSUM CHKSUM)))
(SERIAL-WRITE-CHAR *EOL*)
(FORCE-OUTPUT *SERIAL-OUTPUT*)
(DEBUG-MESSAGE "~&type = ~3C, num = ~3D, len = ~3D~& Data = ~A"
TYPE NUM LEN (IF DATA (UNKLUDGIFY-STRING DATA) ""))))
;;; RECEIVE-PACKET
;;; Values returned are, in order:
;;; TYPE -- a character (fixnum), in {#\A, #\S, ...}. E.g. #\A means "abort".
;;; NUM -- a number, the packet-number of this packet.
;;; DATA -- a string, the data of this packet, which is as many
;;; characters as appropriate/desired for this type of packet.
;;; Many callers need only one (usually the type) value.
(DEFUN RECEIVE-PACKET ()
(LISP:CATCH 'TIMEOUT
(LET ((ALARMCLOCK #'(LAMBDA (IGNORE) IGNORE
(DEBUG-MESSAGE "~&RECEIVE-PACKET timed out")
(LISP:THROW 'TIMEOUT *TIMED-OUT*))))
(DECLARE (SPECIAL ALARMCLOCK))
(UNWIND-PROTECT
(PROGN
(ALARMCLOCK 'TIME (MAX *TIMEOUT* *MIN-TIMEOUT*))
(REALLY-RECEIVE-PACKET))
(ALARMCLOCK 'TIME NIL)))))
(DEFUN REALLY-RECEIVE-PACKET ()
(LET ((CH 0) (TYPE 0) (I 0) (CCHKSUM 0) (RCHKSUM 0) (LEN 0) (NUM 0)
(DATA *RECEIVE-PACKET*))
(DECLARE (FIXNUM TYPE I CCHKSUM RCHKSUM LEN NUM)
#+Maclisp (FIXNUM CH))
(PROG ()
CONTINUE
(SETQ CH (SERIAL-READ-CHAR))
(COND ((NOT (CHAR= CH *SOH*))
(DEBUG-MESSAGE "~&Noise: ")
(GO CONTINUE)))
(DEBUG-MESSAGE "~&Receiving packet: ")
(SETQ CH (SERIAL-READ-CHAR))
(IF (CHAR= CH *SOH*) (GO CONTINUE))
(SETQ CCHKSUM (CHAR->ASCII CH)) ;OK, start checksum
(SETQ LEN (- (GRAPHIC->INTEGER CH) 3)) ;Get character count
;; (if *debug?* (format t "<~d>" len))
(COND ((OR (< LEN 0) (> LEN (- *MAX-PACKET-SIZE* 3)))
(DEBUG-MESSAGE "~&RECEIVE-PACKET got illegal packet length: ~D"
LEN)
(RETURN (VALUES *LENGTH-ERROR* NUM DATA))))
(SETQ CH (SERIAL-READ-CHAR))
(IF (CHAR= CH *SOH*) (GO CONTINUE))
(INCF CCHKSUM (CHAR->ASCII CH)) ;OK, update checksum
(SETQ NUM (GRAPHIC->INTEGER CH)) ;Get packet number
(SETQ CH (SERIAL-READ-CHAR))
(IF (CHAR= CH *SOH*) (GO CONTINUE))
(INCF CCHKSUM (CHAR->ASCII CH)) ;OK, update checksum
(SETQ TYPE CH) ;Get packet type
(DEBUG-MESSAGE "type = ~3C, num = ~3D, len = ~3D~%"
TYPE NUM LEN)
(SETQ I 0)
READ-THE-PACKET
(COND ((< I LEN)
(SETQ CH (SERIAL-READ-CHAR))
(IF (CHAR= CH *SOH*) (GO CONTINUE))
(INCF CCHKSUM (CHAR->ASCII CH)) ;OK, update checksum
(SET-STRING-ELT DATA I CH)
(INCF I)
(GO READ-THE-PACKET)))
;; (SET-STRING-ELT DATA LEN 0) ;Mark end of data
;; ^^ unnecessary!?
(SETF (FILL-POINTER DATA) LEN)
(DEBUG-MESSAGE " Data = ~A" (UNKLUDGIFY-STRING DATA))
(SETQ CH (SERIAL-READ-CHAR))
(SETQ RCHKSUM (GRAPHIC->INTEGER CH)) ;OK, get checksum
(SETQ CH (SERIAL-READ-CHAR))
(IF (CHAR= CH *SOH*) (GO CONTINUE)) ;OK, get eol char and toss it
;Safe!
(SETQ CCHKSUM (COMPUTE-FINAL-CHECKSUM CCHKSUM))
(COND ((NOT (= CCHKSUM RCHKSUM))
(DEBUG-MESSAGE "RECEIVE-PACKET received bad checksum (expected ~A, got ~A)"
RCHKSUM CCHKSUM)
;; Corruption, oh no!
(RETURN (VALUES *CHECKSUM-ERROR* NUM DATA)))
(T
;; Else checksum ok, 'uncorrupted'.
(IF (ERRP TYPE)
(MESSAGE "~&Aborting with following error from remote host:~% ~S~%"
(UNKLUDGIFY-STRING DATA)))
(RETURN (VALUES TYPE NUM DATA)))))))
;;; Serial I/O. Everything here is pretty implementation-dependent, or at
;;; least tunable for speed.
#-Maclisp
(DEFUN ALARMCLOCK (WHICH SECONDS) WHICH SECONDS
;; Call the value of global variable ALARMCLOCK when SECONDS seconds
;; have passed. If SECONDS is NIL then disable the alarm.
'UNIMPLEMENTED)
(DEFUN INIT-SERIAL-I/O (&OPTIONAL IN (OUT IN))
#-Maclisp
(PROGN (SETQ *SERIAL-INPUT*
(OR (OPEN IN :DIRECTION :INPUT) *TERMINAL-IO*))
(SETQ *SERIAL-OUTPUT*
(OR (OPEN OUT :DIRECTION :OUTPUT) *TERMINAL-IO*)))
#+Maclisp
(PROGN (IF *SERIAL-INPUT* (TERMINATE-SERIAL-I/O))
(SETQ *SERIAL-INPUT*
(OPEN `((,(OR IN 'TTY))) '(TTY SINGLE IMAGE IN)))
(SETQ *SERIAL-OUTPUT*
(OPEN `((,(OR IN 'TTY))) '(TTY BLOCK IMAGE OUT)))))
#+DEC ;; For testing purposes
(DEFUN MBX (&OPTIONAL IN (OUT IN))
(SETQ *EOL* #\NEWLINE)
(SETQ *MY-EOL* #\NEWLINE)
(SETQ *SERIAL-INPUT* (OPEN-MAILBOX IN))
(SETQ *SERIAL-OUTPUT*
(IF (EQUAL OUT IN)
*SERIAL-INPUT*
(OPEN-MAILBOX OUT)))
(VALUES (NAMESTRING *SERIAL-INPUT*) (NAMESTRING *SERIAL-OUTPUT*)))
(DEFUN TERMINATE-SERIAL-I/O ()
(IF (NOT (EQ *SERIAL-INPUT* *TERMINAL-IO*))
(CLOSE *SERIAL-INPUT*))
(IF (NOT (EQ *SERIAL-OUTPUT* *TERMINAL-IO*))
(CLOSE *SERIAL-OUTPUT*))
(SETQ *SERIAL-INPUT* NIL *SERIAL-OUTPUT* NIL))
(DEFUN SERIAL-STATE ()
#-Maclisp
NIL
#+Maclisp
(SYSCALL 3. 'TTYGET *SERIAL-INPUT*))
(DEFUN SET-SERIAL-STATE (TEMP)
#-Maclisp
TEMP
#+Maclisp
(SYSCALL 0. 'TTYSET *SERIAL-INPUT* (CAR TEMP) (CADR TEMP)))
(DEFUN DISABLE-ECHOING (TEMP)
#-Maclisp
TEMP
#+Maclisp
(SYSCALL 0. 'TTYSET *SERIAL-INPUT*
(LOGAND (CAR TEMP) #o070707070707)
(LOGAND (CADR TEMP) #o070707070707)))
;(DEFUN ENABLE-ECHOING (TEMP)
; (SYSCALL 0. 'TTYSET *SERIAL-INPUT*
; (LOGIOR (CAR TEMP) #o202020202020)
; (LOGIOR (CADR TEMP) #o202020200020)))
(DEFUN SERIAL-READ-CHAR ()
#-(or Maclisp Symbolics)
;; This is supposed to read without echoing.
(READ-CHAR *SERIAL-INPUT*)
#+Maclisp
(LET ((CH (+TYI *SERIAL-INPUT*)))
(IF *IMAGE?* CH (LOGAND CH #o177)))
#+Symbolics
(PROGN (ZL:SEND *SERIAL-INPUT* :INPUT-WAIT NIL
#'ZL:TIME-ELAPSED-P
(FLOOR (* (MAX *TIMEOUT* *MIN-TIMEOUT*) 60))
(ZL:TIME))
(IF (ZL:SEND *TERMINAL-IO* :LISTEN)
(LET ((CH (ZL:SEND *TERMINAL-IO* :TYI)))
(CODE-CHAR (ZL:CHAR-CODE CH) (ZL:CHAR-BITS CH)))
(LISP:THROW 'TIMEOUT *TIMED-OUT*))))
(DEFUN SERIAL-WRITE-CHAR (CH)
#-(or Maclisp Symbolics)
(WRITE-CHAR CH *SERIAL-OUTPUT*)
#+Maclisp
(+TYO CH *SERIAL-OUTPUT*)
#+Symbolics
(ZL:SEND *SERIAL-OUTPUT* :TYO (CHAR->ASCII CH)) ;?
(CHAR->ASCII CH)) ;for checksums
(DEFUN FLUSH-INPUT ()
(CLEAR-INPUT *SERIAL-INPUT*))
;;; Implementation-dependent fast character file I/O:
#-Maclisp
(PROGN 'COMPILE
(DEFSUBST +TYI (STREAM)
(READ-CHAR STREAM NIL NIL))
(DEFSUBST +TYO (CH STREAM)
(WRITE-CHAR CH STREAM))
(DEFSUBST END-OF-FILE-P (C) ;Called only on value of +TYI
(NULL C))
)
;;; File I/O
;;; Put a pathname into Kermit standard form.
(DEFUN KERMITIFY-PATHNAME (PATHNAME VERSIONP)
(LET* ((NAME (STRINGIFY-COMPONENT (PATHNAME-NAME PATHNAME)))
(TYPE (STRINGIFY-COMPONENT (PATHNAME-TYPE PATHNAME)))
(VERSION (STRINGIFY-COMPONENT (PATHNAME-VERSION PATHNAME))))
(COND ((AND VERSIONP VERSION)
(STRING-APPEND NAME "." (OR TYPE "") "." VERSION))
(TYPE
(STRING-APPEND NAME "." TYPE))
(T NAME))))
(DEFUN STRINGIFY-COMPONENT (X)
(COND ((EQ X ':WILD) "*")
((EQ X ':NEWEST) "0")
((EQ X ':OLDEST) "-1")
((NUMBERP X) (FORMAT NIL "~d" X))
(T X)))
;;; Use by SEND-FILE and by SERVE to service remote GET requests.
;;; Returns a list of pairs (<local-pathname> . <remote-filespec>).
(DEFUN MAKE-TRANSFER-LIST (LOCAL-FILESPEC REMOTE-FILESPEC)
#+Maclisp (DECLARE (SPECIAL REMOTE-FILESPEC)) ;No closures
(LET* ((PATH (PATHNAME LOCAL-FILESPEC))
(PATH (IF (NULL (PATHNAME-VERSION PATH))
(MAKE-PATHNAME :DEFAULTS PATH :VERSION :NEWEST)
PATH))
(PATHS (LISP:DIRECTORY PATH)))
(MAPCAR #'(LAMBDA (PATH)
(CONS PATH (IF REMOTE-FILESPEC
(MERGE-PATHNAMES REMOTE-FILESPEC PATH
(PATHNAME-VERSION PATH))
PATH)))
PATHS)))
;;; Opening files...
(DEFUN MAYBE-OPEN-FOR-INPUT (FILESPEC)
(LISP:OPEN FILESPEC :DIRECTION :INPUT :IF-DOES-NOT-EXIST NIL))
(DEFUN MAYBE-OPEN-FOR-OUTPUT (FILESPEC)
(LISP:OPEN FILESPEC :DIRECTION :OUTPUT :IF-EXISTS NIL))
;;; Get around vagaries of file merging.
(DEFUN FLUSH-WILDCARDS (PATHNAME)
(MAKE-PATHNAME :HOST (PATHNAME-HOST PATHNAME)
:DEVICE (NOT-WILD (PATHNAME-DEVICE PATHNAME))
:DIRECTORY (NOT-WILD (PATHNAME-DIRECTORY PATHNAME))
:NAME (NOT-WILD (PATHNAME-NAME PATHNAME))
:TYPE (NOT-WILD (PATHNAME-TYPE PATHNAME))))
(DEFUN NOT-WILD (COMPONENT)
(IF (OR (EQ COMPONENT :WILD)
(EQUAL COMPONENT "*"))
NIL COMPONENT))
;;; Write a packet buffer into the currently open local output file.
(DEFUN EMPTY-BUFFER (BUFFER)
"Put data from an incoming packet into a local disk file."
(LET ((LEN (LISP:LENGTH BUFFER)))
(DO ((I 0 (1+ I)))
((>= I LEN) BUFFER)
(LET ((CH (STRING-ELT BUFFER I)))
(DECLARE (FIXNUM CH))
(COND ((CHAR= CH *MY-QUOTE*)
(SETQ CH (STRING-ELT BUFFER (INCF I)))
(IF (NOT (CHAR= CH #|(LOGAND CH #o177)|# *MY-QUOTE*))
(SETQ CH (CTL CH)))))
;; (IF (NOT *IMAGE?*) (SETQ CH (LOGAND CH #o177)))
(+TYO CH *STREAM*)))))
;;; Fill a packet buffer from the currently open local input file.
;;; Returns the number of characters stored into the packet buffer.
;;; A return value of zero means that the end of the file has been reached.
;;; There are four ways to fill a buffer:
;;; 1. kermit default: 7-bit, quote all control characters.
;;; 3. image mode: send everything through with no conversion, except for
;;; quoting the quote character.
;;; 2, 4: lose mode: excised by JAR.
(DEFUN FILL-BUFFER (BUFFER)
"Fill buffer with the outgoing data from the file *STREAM* points to.
Only control quoting is done; 8-bit and repeat count prefixes are not handled."
;;1; Changed 6 to 7!! See lmbugs.doc file item #14.
(LET ((INDEX 0)
(LIMIT (- *SEND-PACKET-SIZE* 7)))
(DECLARE (FIXNUM LIMIT INDEX))
(DO ()
((>= INDEX LIMIT))
(LET ((C (+TYI *STREAM*)))
#+Maclisp (DECLARE (FIXNUM C))
(COND ((END-OF-FILE-P C) ;???
(RETURN NIL))
(T (LET ((N (CHAR->ASCII C)))
(COND ((CHAR= C *QUOTE*)
(SET-STRING-ELT BUFFER INDEX *QUOTE*)
(INCF INDEX)
(SET-STRING-ELT BUFFER INDEX C)
(INCF INDEX))
((GRAPHIC-CHAR-P C)
;; Regular character
(SET-STRING-ELT BUFFER INDEX C)
(INCF INDEX))
((AND (NOT *IMAGE?*)
(> N #o177))
;; Weird character. Don't send anything for it.
(MESSAGE "~&The character ~C [~N octal] couldn't be translated to ASCII."
C N))
;; Deal with newline characters?
(T (SET-STRING-ELT BUFFER INDEX *QUOTE*)
(INCF INDEX)
(SET-STRING-ELT BUFFER
INDEX
(IF (OR *IMAGE?* (>= N 32.))
C
(CTL C)))
(INCF INDEX))))))))
(SETF (FILL-POINTER BUFFER) INDEX)
INDEX))
#+Symbolics
(defvar cl-readtable (copy-readtable nil))
#+Symbolics
(defun zl-user::setup ()
(zl:setq-standard-value *readtable* cl-readtable)
(zl:setq-standard-value *package* (find-package 'kermit)))