;; Doesn't need to be on your computer in order for you to use it.
;; In this contingency, recite the following line of prayer (semicolon omitted)
;printf "\n" | nc sdf.org 70
;; And repeat, following the prompts and referring to RFC1436.
(defmethod shared-initialize :after ((obj nc) names &key address port &allow-other-keys) "
Instantiates obj based on :address and :port.
"
(declare (ignore names args))
(multiple-value-bind (2way stat proc)
(ext:run-program "nc" `(,address ,port) :wait nil)
(assert (null stat))
(setf (2way obj) 2way (proc obj) proc)))
(defun visit (address &optional (port 70)) "
(visit address &optional (port 70))
For internal use.
Instantiates a nc process with the address and port.
Returns a closure which consumes an item-specifier.
Consider using bind-nl instead.
"
(let* ((nc (make-instance 'nc :allow-other-keys t :address address :port (format nil "~d" port)))
(in (two-way-stream-input-stream (2way nc)))
(out (two-way-stream-output-stream (2way nc))))
(labels ((readline-stat () (handler-case
(values (let ((ch (read-char-no-hang in)))
(when ch
(with-output-to-string (*standard-output*)
(princ ch) (princ (read-line in)))))
(ext:external-process-status (proc nc)))
(end-of-file (print "eof") (terpri))))
(specify-item (item-specifier)
(format out "~a~%" item-specifier)
(force-output out)
#'readline-stat))
(values #'specify-item))))
(defvar *nl*)
(defun bind-nl (&key (address "sdf.org") (port 70) (spec "")) "
Despite its name,
bind-nl (&key (address \"sdf.org\") (port 70) (spec \"\"))
Setfs (symbol-function '*nl*)
to (funcall (visit address port) spec)
Thereafter,
(*nl*) -> (values (when line) ext-stat)
For some reasonable meanings of those values.
ext-stat is probably :running but might not be.
"
(let* ((item-specifier (visit address port))
(readline-stat (funcall item-specifier spec)))
(setf (symbol-function '*nl*) readline-stat)))
(defun plain (list &optional (stream t)) "
plain (list &optional (stream t))
Princs a list line by line.
"
(format stream "~{~a~^~%~}~%" list))
(defun nconc-nls (&optional list) "
nconc-nls (&optional list)
Calls *nl* to get lines. With a slow enough connection and fast fingers,
this might need to be called multiple times to finish reading an item.
This is like this because I found some servers don't close gopher connections.
If called with the optional list, nconcs to it.
"
(nconc list (loop for line = (*nl*) while line collect line)))