iThis is a major rework. - clic - Clic is an command line interactive client for gopher written in Common LISP Err bitreich.org 70 hgit clone git://bitreich.org/clic/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/clic/ URL:git://bitreich.org/clic/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/clic/ bitreich.org 70 1Log /scm/clic/log.gph bitreich.org 70 1Files /scm/clic/files.gph bitreich.org 70 1Refs /scm/clic/refs.gph bitreich.org 70 1Tags /scm/clic/tag bitreich.org 70 1README /scm/clic/file/README.md.gph bitreich.org 70 1LICENSE /scm/clic/file/LICENSE.gph bitreich.org 70 i--- Err bitreich.org 70 1commit cb7a05e3d9dc9bb1f7c4f894282857ca75efabd0 /scm/clic/commit/cb7a05e3d9dc9bb1f7c4f894282857ca75efabd0.gph bitreich.org 70 1parent 76bab87e5cb9401fcc55750cb1f720acc5123339 /scm/clic/commit/76bab87e5cb9401fcc55750cb1f720acc5123339.gph bitreich.org 70 hAuthor: Solene Rapenne URL:mailto:solene@perso.pw bitreich.org 70 iDate: Sat, 11 Nov 2017 00:30:27 +0000 Err bitreich.org 70 i Err bitreich.org 70 iThis is a major rework. Err bitreich.org 70 i Err bitreich.org 70 i- Sbcl now use a link to "extension.so" to get information about the height of the terminal, ecl default to a hardcoded value. Err bitreich.org 70 i- Response from server is now buffered instead of being displayed for each line received Err bitreich.org 70 i- when the display is more than the terminal height, break display loop Err bitreich.org 70 i Err bitreich.org 70 i- output to file is broken because it currently ask to display next page... Err bitreich.org 70 i Err bitreich.org 70 iDiffstat: Err bitreich.org 70 i M Makefile | 10 +++++++--- Err bitreich.org 70 i M TODO | 5 +++-- Err bitreich.org 70 i M clic.lisp | 264 +++++++++++++++++++------------ Err bitreich.org 70 i A extension.c | 8 ++++++++ Err bitreich.org 70 i M make-binary.lisp | 8 ++++++-- Err bitreich.org 70 i M test.lisp | 2 ++ Err bitreich.org 70 i Err bitreich.org 70 i6 files changed, 189 insertions(+), 108 deletions(-) Err bitreich.org 70 i--- Err bitreich.org 70 1diff --git a/Makefile b/Makefile /scm/clic/file/Makefile.gph bitreich.org 70 i@@ -10,12 +10,16 @@ MANDIR = ${PREFIX}/share/man/man1 Err bitreich.org 70 i Err bitreich.org 70 i all: ${BIN} Err bitreich.org 70 i Err bitreich.org 70 i-${BIN}: clic.lisp Err bitreich.org 70 i+${BIN}: clic.lisp make-binary.lisp Err bitreich.org 70 i ${LISP} --load make-binary.lisp Err bitreich.org 70 i Err bitreich.org 70 i-standalone: clic.lisp Err bitreich.org 70 i+standalone: clic.lisp extension make-binary.lisp Err bitreich.org 70 i ${MAKE} -e LISP=sbcl Err bitreich.org 70 i Err bitreich.org 70 i+extension: extension.c Err bitreich.org 70 i+ cc -fPIC -c extension.c Err bitreich.org 70 i+ ld -shared -o extension.so extension.o Err bitreich.org 70 i+ Err bitreich.org 70 i install: ${BIN} Err bitreich.org 70 i @echo installing executable to "${DESTDIR}${PREFIX}/bin" Err bitreich.org 70 i @mkdir -p "${DESTDIR}${BINDIR}" Err bitreich.org 70 i@@ -27,7 +31,7 @@ uninstall: Err bitreich.org 70 i @rm -f "${DESTDIR}${BINDIR}/${BIN}" Err bitreich.org 70 i Err bitreich.org 70 i clean: Err bitreich.org 70 i- rm -f "${BIN}" clic.o clic.eclh clic.cxx bookmark-test Err bitreich.org 70 i+ rm -f "${BIN}" clic.o clic.eclh clic.cxx bookmark-test extension.so Err bitreich.org 70 i Err bitreich.org 70 i test: clean all Err bitreich.org 70 i @sh run-test.sh ${LISP} Err bitreich.org 70 1diff --git a/TODO b/TODO /scm/clic/file/TODO.gph bitreich.org 70 i@@ -8,6 +8,7 @@ Err bitreich.org 70 i Err bitreich.org 70 i * CODE Err bitreich.org 70 i Err bitreich.org 70 i-- TODO find a better color scheme Err bitreich.org 70 i+- TODO remove the pagination if using stdout or not in shell Err bitreich.org 70 i+- TODO make the c-termsize working on ecl Err bitreich.org 70 i - DONE use CLOS to store data Err bitreich.org 70 i-- TODO store the whole page and deal with it later (but not if it's not a 0 or 1 type) Err bitreich.org 70 i+- DONE store the whole page and deal with it later Err bitreich.org 70 1diff --git a/clic.lisp b/clic.lisp /scm/clic/file/clic.lisp.gph bitreich.org 70 i@@ -5,11 +5,32 @@ Err bitreich.org 70 i #+ecl Err bitreich.org 70 i (require 'sockets)) Err bitreich.org 70 i Err bitreich.org 70 i+;;;; C binding to get terminal informations Err bitreich.org 70 i+;;;; SBCL only Err bitreich.org 70 i+#+sbcl Err bitreich.org 70 i+(progn Err bitreich.org 70 i+ (load-shared-object "./extension.so") Err bitreich.org 70 i+ (declaim (inline termsize)) Err bitreich.org 70 i+ (sb-alien:define-alien-routine "termsize" int) Err bitreich.org 70 i+ (defun c-termsize () Err bitreich.org 70 i+ "return terminal height" Err bitreich.org 70 i+ (sb-alien:with-alien ((res int (termsize)))))) Err bitreich.org 70 i+ Err bitreich.org 70 i+#+ecl Err bitreich.org 70 i+(progn Err bitreich.org 70 i+ "we don't do C binding with ecl" Err bitreich.org 70 i+ (defun c-termsize() Err bitreich.org 70 i+ 40)) Err bitreich.org 70 i+;;;; END C binding Err bitreich.org 70 i+ Err bitreich.org 70 i ;; structure to store links Err bitreich.org 70 i (defstruct location host port type uri) Err bitreich.org 70 i Err bitreich.org 70 i ;;;; BEGIN GLOBAL VARIABLES Err bitreich.org 70 i Err bitreich.org 70 i+;;; array of lines in buffer Err bitreich.org 70 i+(defparameter *buffer* nil) Err bitreich.org 70 i+ Err bitreich.org 70 i ;;; a list containing the last viewed pages Err bitreich.org 70 i (defparameter *history* '()) Err bitreich.org 70 i Err bitreich.org 70 i@@ -82,7 +103,7 @@ Err bitreich.org 70 i "Used to display gopher response with color one line at a time" Err bitreich.org 70 i (let ((line-type (subseq line 0 1)) Err bitreich.org 70 i (infos (split (subseq line 1) #\Tab))) Err bitreich.org 70 i- Err bitreich.org 70 i+ Err bitreich.org 70 i ;; see RFC 1436 Err bitreich.org 70 i ;; section 3.8 Err bitreich.org 70 i (when (and Err bitreich.org 70 i@@ -156,72 +177,40 @@ Err bitreich.org 70 i ;; I image Err bitreich.org 70 i (check "I" 'unimplemented))))) Err bitreich.org 70 i Err bitreich.org 70 i-(defun getpage(host port uri &optional (type "1")) Err bitreich.org 70 i+ Err bitreich.org 70 i+(defun getpage(host port uri) Err bitreich.org 70 i "connect and display" Err bitreich.org 70 i+ Err bitreich.org 70 i+ ;; we reset the buffer Err bitreich.org 70 i+ (setf *buffer* Err bitreich.org 70 i+ (make-array 200 Err bitreich.org 70 i+ :fill-pointer 0 Err bitreich.org 70 i+ :initial-element nil Err bitreich.org 70 i+ :adjustable t)) Err bitreich.org 70 i Err bitreich.org 70 i- (let ((here (make-location :host host :port port :uri uri :type type))) Err bitreich.org 70 i- Err bitreich.org 70 i- ;; goes to the history ! Err bitreich.org 70 i- (push here *history*) Err bitreich.org 70 i+ ;; we prepare informations about the connection Err bitreich.org 70 i+ (let* ((address (sb-bsd-sockets:get-host-by-name host)) Err bitreich.org 70 i+ (host (car (sb-bsd-sockets:host-ent-addresses address))) Err bitreich.org 70 i+ (socket (make-instance 'sb-bsd-sockets:inet-socket :type :stream :protocol :tcp))) Err bitreich.org 70 i Err bitreich.org 70 i- ;; we reset the links table ONLY if we have a new folder Err bitreich.org 70 i- (when (string= "1" type) Err bitreich.org 70 i- (setf *links* (make-hash-table))) Err bitreich.org 70 i- Err bitreich.org 70 i- (when *offline* Err bitreich.org 70 i- (ensure-directories-exist (concatenate 'string Err bitreich.org 70 i- "history/" Err bitreich.org 70 i- (location-host here) Err bitreich.org 70 i- "/" Err bitreich.org 70 i- (location-uri here) Err bitreich.org 70 i- "/"))) Err bitreich.org 70 i- Err bitreich.org 70 i+ (sb-bsd-sockets:socket-connect socket host port) Err bitreich.org 70 i Err bitreich.org 70 i- ;; we prepare informations about the connection Err bitreich.org 70 i- (let* ((address (sb-bsd-sockets:get-host-by-name host)) Err bitreich.org 70 i- (host (car (sb-bsd-sockets:host-ent-addresses address))) Err bitreich.org 70 i- (socket (make-instance 'sb-bsd-sockets:inet-socket :type :stream :protocol :tcp))) Err bitreich.org 70 i+ ;; we open a stream for input/output Err bitreich.org 70 i+ (let ((stream (sb-bsd-sockets:socket-make-stream socket :input t :output t))) Err bitreich.org 70 i Err bitreich.org 70 i- (sb-bsd-sockets:socket-connect socket host port) Err bitreich.org 70 i+ ;; sending the request here Err bitreich.org 70 i+ ;; if the selector is 1 we omit it Err bitreich.org 70 i+ (format stream "~a~%" uri) Err bitreich.org 70 i+ (force-output stream) Err bitreich.org 70 i Err bitreich.org 70 i- ;; we open a stream for input/output Err bitreich.org 70 i- (let ((stream (sb-bsd-sockets:socket-make-stream socket :input t :output t))) Err bitreich.org 70 i- Err bitreich.org 70 i- ;; sending the request here Err bitreich.org 70 i- ;; if the selector is 1 we omit it Err bitreich.org 70 i- (format stream "~a~%" uri) Err bitreich.org 70 i- (force-output stream) Err bitreich.org 70 i- Err bitreich.org 70 i- (let ((save-offline (if *offline* Err bitreich.org 70 i- (open (concatenate 'string Err bitreich.org 70 i- "history/" Err bitreich.org 70 i- (location-host here) Err bitreich.org 70 i- "/" Err bitreich.org 70 i- (location-uri here) Err bitreich.org 70 i- (location-type here)) Err bitreich.org 70 i- :direction :output Err bitreich.org 70 i- :if-does-not-exist :create Err bitreich.org 70 i- :if-exists :supersede) Err bitreich.org 70 i- nil))) Err bitreich.org 70 i- Err bitreich.org 70 i- ;; for each line we receive we display it Err bitreich.org 70 i- (loop for line = (read-line stream nil nil) Err bitreich.org 70 i- while line do Err bitreich.org 70 i- (when save-offline Err bitreich.org 70 i- (format save-offline "~a~%" line)) Err bitreich.org 70 i- (cond Err bitreich.org 70 i- ((string= "1" type) Err bitreich.org 70 i- (formatted-output line)) Err bitreich.org 70 i- ((string= "0" type) Err bitreich.org 70 i- (format t "~a~%" line)))) Err bitreich.org 70 i- (and save-offline (close save-offline))))))) Err bitreich.org 70 i+ ;; for each line we receive we display it Err bitreich.org 70 i+ (loop for line = (read-line stream nil nil) Err bitreich.org 70 i+ while line Err bitreich.org 70 i+ do Err bitreich.org 70 i+ (vector-push line *buffer*))))) Err bitreich.org 70 i+ Err bitreich.org 70 i+ Err bitreich.org 70 i Err bitreich.org 70 i-(defun visit(destination) Err bitreich.org 70 i- "visit a location" Err bitreich.org 70 i- (getpage (location-host destination) Err bitreich.org 70 i- (location-port destination) Err bitreich.org 70 i- (location-uri destination) Err bitreich.org 70 i- (location-type destination))) Err bitreich.org 70 i Err bitreich.org 70 i (defun g(key) Err bitreich.org 70 i "browse to the N-th link" Err bitreich.org 70 i@@ -271,7 +260,6 @@ Err bitreich.org 70 i (location-type bookmark) Err bitreich.org 70 i (location-uri bookmark)) Err bitreich.org 70 i 'file line-number)))) Err bitreich.org 70 i- Err bitreich.org 70 i (defun help-shell() Err bitreich.org 70 i "show help for the shell" Err bitreich.org 70 i (format t "number : go to link n~%") Err bitreich.org 70 i@@ -282,47 +270,7 @@ Err bitreich.org 70 i (format t "help : show this help~%") Err bitreich.org 70 i (format t "x or q : exit the shell, go back to REPL~%")) Err bitreich.org 70 i Err bitreich.org 70 i-(defun shell() Err bitreich.org 70 i- "Shell for user interaction" Err bitreich.org 70 i- (format t "clic => ") Err bitreich.org 70 i- (force-output) Err bitreich.org 70 i Err bitreich.org 70 i- ;; we loop until X or Q is typed Err bitreich.org 70 i- (loop for user-input = (format nil "~a" (read nil nil)) Err bitreich.org 70 i- while (not (or Err bitreich.org 70 i- (string= "X" user-input) Err bitreich.org 70 i- (string= "Q" user-input))) Err bitreich.org 70 i- do Err bitreich.org 70 i- (cond Err bitreich.org 70 i- Err bitreich.org 70 i- ;; show help Err bitreich.org 70 i- ((string= "HELP" user-input) Err bitreich.org 70 i- (help-shell)) Err bitreich.org 70 i- Err bitreich.org 70 i- ;; bookmark current link Err bitreich.org 70 i- ((string= "A" user-input) Err bitreich.org 70 i- (add-bookmark)) Err bitreich.org 70 i- Err bitreich.org 70 i- ;; show bookmarks Err bitreich.org 70 i- ((string= "B" user-input) Err bitreich.org 70 i- (show-bookmarks)) Err bitreich.org 70 i- Err bitreich.org 70 i- ;; go to previous page Err bitreich.org 70 i- ((string= "P" user-input) Err bitreich.org 70 i- (p)) Err bitreich.org 70 i- Err bitreich.org 70 i- ;; show history Err bitreich.org 70 i- ((string= "H" user-input) Err bitreich.org 70 i- (format t "~{~a~%~}" *history*)) Err bitreich.org 70 i- Err bitreich.org 70 i- ;; follow a link Err bitreich.org 70 i- (t Err bitreich.org 70 i- ;; we ignore error in case of bad input Err bitreich.org 70 i- ;; just do nothing Err bitreich.org 70 i- (ignore-errors Err bitreich.org 70 i- (g (parse-integer user-input))))) Err bitreich.org 70 i- (format t "clic => ") Err bitreich.org 70 i- (force-output))) Err bitreich.org 70 i Err bitreich.org 70 i (defun parse-url(url) Err bitreich.org 70 i "parse a gopher url and return a location" Err bitreich.org 70 i@@ -364,6 +312,120 @@ Err bitreich.org 70 i #+ecl Err bitreich.org 70 i (car (last (cdr (si::command-args))))) Err bitreich.org 70 i Err bitreich.org 70 i+ Err bitreich.org 70 i+ Err bitreich.org 70 i+(defun user-input(input) Err bitreich.org 70 i+ (cond Err bitreich.org 70 i+ ;; show help Err bitreich.org 70 i+ ((string= "HELP" input) Err bitreich.org 70 i+ (help-shell)) Err bitreich.org 70 i+ Err bitreich.org 70 i+ ;; bookmark current link Err bitreich.org 70 i+ ((string= "A" input) Err bitreich.org 70 i+ (add-bookmark)) Err bitreich.org 70 i+ Err bitreich.org 70 i+ ;; show bookmarks Err bitreich.org 70 i+ ((string= "B" input) Err bitreich.org 70 i+ (show-bookmarks)) Err bitreich.org 70 i+ Err bitreich.org 70 i+ ;; go to previous page Err bitreich.org 70 i+ ((string= "P" input) Err bitreich.org 70 i+ (p)) Err bitreich.org 70 i+ Err bitreich.org 70 i+ ;; exit Err bitreich.org 70 i+ ((or (string= "X" input) Err bitreich.org 70 i+ (string= "Q" input)) Err bitreich.org 70 i+ (quit)) Err bitreich.org 70 i+ Err bitreich.org 70 i+ Err bitreich.org 70 i+ ;; show history Err bitreich.org 70 i+ ((string= "H" input) Err bitreich.org 70 i+ (format t "~{~a~%~}" *history*)) Err bitreich.org 70 i+ Err bitreich.org 70 i+ ;; follow a link Err bitreich.org 70 i+ (t Err bitreich.org 70 i+ ;; we ignore error in case of bad input Err bitreich.org 70 i+ ;; just do nothing Err bitreich.org 70 i+ (ignore-errors Err bitreich.org 70 i+ (g (parse-integer input)))))) Err bitreich.org 70 i+ Err bitreich.org 70 i+(defun display-buffer(type) Err bitreich.org 70 i+ "display the buffer" Err bitreich.org 70 i+ (let ((rows (c-termsize))) Err bitreich.org 70 i+ (let ((input nil)) Err bitreich.org 70 i+ (loop for line across *buffer* Err bitreich.org 70 i+ counting line into row Err bitreich.org 70 i+ do Err bitreich.org 70 i+ (when (= row (- rows 3)) ; -1 for text - 1 for input and -1 for can't remember Err bitreich.org 70 i+ (setf row 0) Err bitreich.org 70 i+ (format t "~a------- press enter to next or a shell command ---------~a~%" Err bitreich.org 70 i+ (get-color 'cyan) Err bitreich.org 70 i+ (get-color 'white)) Err bitreich.org 70 i+ (let ((first-input (read-char))) Err bitreich.org 70 i+ (when (not (or (char= #\NewLine first-input) Err bitreich.org 70 i+ (char= #\Space first-input))) Err bitreich.org 70 i+ (unread-char first-input) Err bitreich.org 70 i+ (let ((input-text (format nil "~a" (read)))) Err bitreich.org 70 i+ (setf input input-text) Err bitreich.org 70 i+ (loop-finish))))) Err bitreich.org 70 i+ (cond Err bitreich.org 70 i+ ((string= "1" type) Err bitreich.org 70 i+ (formatted-output line)) Err bitreich.org 70 i+ ((string= "0" type) Err bitreich.org 70 i+ (format t "~a~%" line)))) Err bitreich.org 70 i+ (when input Err bitreich.org 70 i+ (user-input input))))) Err bitreich.org 70 i+ Err bitreich.org 70 i+(defun visit(destination) Err bitreich.org 70 i+ "visit a location" Err bitreich.org 70 i+ Err bitreich.org 70 i+ (getpage (location-host destination) Err bitreich.org 70 i+ (location-port destination) Err bitreich.org 70 i+ (location-uri destination)) Err bitreich.org 70 i+ Err bitreich.org 70 i+ ;; we reset the links table ONLY if we have a new folder Err bitreich.org 70 i+ (when (string= "1" (location-type destination)) Err bitreich.org 70 i+ (setf *links* (make-hash-table))) Err bitreich.org 70 i+ Err bitreich.org 70 i+ ;; goes to the history ! Err bitreich.org 70 i+ (push destination *history*) Err bitreich.org 70 i+ Err bitreich.org 70 i+ (display-buffer (location-type destination)) Err bitreich.org 70 i+ Err bitreich.org 70 i+ Err bitreich.org 70 i+ (when *offline* Err bitreich.org 70 i+ (let ((path (concatenate 'string Err bitreich.org 70 i+ "history/" (location-host destination) Err bitreich.org 70 i+ "/" (location-uri destination) "/"))) Err bitreich.org 70 i+ (ensure-directories-exist path) Err bitreich.org 70 i+ Err bitreich.org 70 i+ (with-open-file Err bitreich.org 70 i+ (save-offline (concatenate Err bitreich.org 70 i+ 'string path (location-type destination)) Err bitreich.org 70 i+ :direction :output Err bitreich.org 70 i+ :if-does-not-exist :create Err bitreich.org 70 i+ :if-exists :supersede) Err bitreich.org 70 i+ Err bitreich.org 70 i+ (loop for line in *buffer* Err bitreich.org 70 i+ while line Err bitreich.org 70 i+ do Err bitreich.org 70 i+ (format save-offline "~a~%" line)))))) Err bitreich.org 70 i+ Err bitreich.org 70 i+(defun shell() Err bitreich.org 70 i+ "Shell for user interaction" Err bitreich.org 70 i+ (format t "clic => ") Err bitreich.org 70 i+ (force-output) Err bitreich.org 70 i+ Err bitreich.org 70 i+ ;; we loop until X or Q is typed Err bitreich.org 70 i+ (loop for input = (format nil "~a" (read nil nil)) Err bitreich.org 70 i+ while (not (or Err bitreich.org 70 i+ (string= "X" input) Err bitreich.org 70 i+ (string= "Q" input))) Err bitreich.org 70 i+ do Err bitreich.org 70 i+ (user-input input) Err bitreich.org 70 i+ (format t "clic => ") Err bitreich.org 70 i+ (force-output))) Err bitreich.org 70 i+ Err bitreich.org 70 i (defun main() Err bitreich.org 70 i "fetch argument, display page and go to shell if type is 1" Err bitreich.org 70 i (let ((destination Err bitreich.org 70 1diff --git a/extension.c b/extension.c /scm/clic/file/extension.c.gph bitreich.org 70 i@@ -0,0 +1,8 @@ Err bitreich.org 70 i+#include Err bitreich.org 70 i+ Err bitreich.org 70 i+int termsize() Err bitreich.org 70 i+{ Err bitreich.org 70 i+ struct winsize w; Err bitreich.org 70 i+ ioctl(0,TIOCGWINSZ, &w); Err bitreich.org 70 i+ return(w.ws_row); Err bitreich.org 70 i+} Err bitreich.org 70 1diff --git a/make-binary.lisp b/make-binary.lisp /scm/clic/file/make-binary.lisp.gph bitreich.org 70 i@@ -14,10 +14,14 @@ Err bitreich.org 70 i (require 'sb-bsd-sockets) Err bitreich.org 70 i (sb-ext:disable-debugger) Err bitreich.org 70 i (load "clic.lisp") Err bitreich.org 70 i+ #+sb-core-compression Err bitreich.org 70 i+ (sb-ext:save-lisp-and-die "clic" Err bitreich.org 70 i+ :executable t Err bitreich.org 70 i+ :compression 5 Err bitreich.org 70 i+ :toplevel 'main) Err bitreich.org 70 i+ #-sb-core-compression Err bitreich.org 70 i (sb-ext:save-lisp-and-die "clic" Err bitreich.org 70 i :executable t Err bitreich.org 70 i- #+sb-core-compression Err bitreich.org 70 i- (values :compression 5) Err bitreich.org 70 i :toplevel 'main)) Err bitreich.org 70 i Err bitreich.org 70 i (format t "INFO => Compilation done (or at least it should)~%") Err bitreich.org 70 1diff --git a/test.lisp b/test.lisp /scm/clic/file/test.lisp.gph bitreich.org 70 i@@ -13,6 +13,7 @@ Err bitreich.org 70 i (g 1) Err bitreich.org 70 i (add-bookmark) Err bitreich.org 70 i (getpage "bitreich.org" 70 "/") Err bitreich.org 70 i+(display-buffer "1") Err bitreich.org 70 i (g 7) ;; going to radio Err bitreich.org 70 i (g 1) ;; going back Err bitreich.org 70 i (g 21) ;; banana ! Err bitreich.org 70 i@@ -25,6 +26,7 @@ Err bitreich.org 70 i (show-bookmarks) Err bitreich.org 70 i (g 1) Err bitreich.org 70 i Err bitreich.org 70 i+(print *links*) Err bitreich.org 70 i Err bitreich.org 70 i (print *history*) Err bitreich.org 70 i (format t "~%") Err bitreich.org 70 .