allegro.lisp - clic - Clic is an command line interactive client for gopher written in Common LISP (HTM) git clone git://bitreich.org/clic/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/clic/ (DIR) Log (DIR) Files (DIR) Refs (DIR) Tags (DIR) README (DIR) LICENSE --- allegro.lisp (8591B) --- 1 ;;;; See LICENSE for licensing information. 2 3 (in-package :usocket) 4 5 #+cormanlisp 6 (eval-when (:compile-toplevel :load-toplevel :execute) 7 (require :acl-socket)) 8 9 #+allegro 10 (eval-when (:compile-toplevel :load-toplevel :execute) 11 (require :sock) 12 ;; for wait-for-input: 13 (require :process) 14 ;; note: the line below requires ACL 6.2+ 15 (require :osi)) 16 17 (defun get-host-name () 18 ;; note: the line below requires ACL 7.0+ to actually *work* on windows 19 #+allegro (excl.osi:gethostname) 20 #+cormanlisp "") 21 22 (defparameter +allegro-identifier-error-map+ 23 '((:address-in-use . address-in-use-error) 24 (:address-not-available . address-not-available-error) 25 (:network-down . network-down-error) 26 (:network-reset . network-reset-error) 27 (:network-unreachable . network-unreachable-error) 28 (:connection-aborted . connection-aborted-error) 29 (:connection-reset . connection-reset-error) 30 (:no-buffer-space . no-buffers-error) 31 (:shutdown . shutdown-error) 32 (:connection-timed-out . timeout-error) 33 (:connection-refused . connection-refused-error) 34 (:host-down . host-down-error) 35 (:host-unreachable . host-unreachable-error))) 36 37 ;; TODO: what's the error class of Corman Lisp? 38 (defun handle-condition (condition &optional (socket nil) (host-or-ip nil)) 39 "Dispatch correct usocket condition." 40 (typecase condition 41 #+allegro 42 (excl:socket-error 43 (let ((usock-error 44 (cdr (assoc (excl:stream-error-identifier condition) 45 +allegro-identifier-error-map+)))) 46 (declare (type symbol usock-error)) 47 (if usock-error 48 (cond ((subtypep usock-error 'ns-error) 49 (error usock-error :socket socket :host-or-ip host-or-ip)) 50 (t 51 (error usock-error :socket socket))) 52 (error 'unknown-error 53 :real-error condition 54 :socket socket)))))) 55 56 (defun to-format (element-type) 57 (if (subtypep element-type 'character) 58 :text 59 :binary)) 60 61 (defun socket-connect (host port &key (protocol :stream) (element-type 'character) 62 timeout deadline 63 (nodelay t) ;; nodelay == t is the ACL default 64 local-host local-port) 65 (when timeout (unsupported 'timeout 'socket-connect)) 66 (when deadline (unsupported 'deadline 'socket-connect)) 67 (when (eq nodelay :if-supported) 68 (setf nodelay t)) 69 70 (let ((socket)) 71 (setf socket 72 (with-mapped-conditions (socket (or host local-host)) 73 (ecase protocol 74 (:stream 75 (labels ((make-socket () 76 (socket:make-socket :remote-host (host-to-hostname host) 77 :remote-port port 78 :local-host (when local-host 79 (host-to-hostname local-host)) 80 :local-port local-port 81 :format (to-format element-type) 82 :nodelay nodelay))) 83 #+allegro 84 (if timeout 85 (mp:with-timeout (timeout nil) 86 (make-socket)) 87 (make-socket)) 88 #+cormanlisp (make-socket))) 89 (:datagram 90 (apply #'socket:make-socket 91 (nconc (list :type protocol 92 :address-family :internet 93 :local-host (when local-host 94 (host-to-hostname local-host)) 95 :local-port local-port 96 :format (to-format element-type)) 97 (if (and host port) 98 (list :connect :active 99 :remote-host (host-to-hostname host) 100 :remote-port port) 101 (list :connect :passive)))))))) 102 (ecase protocol 103 (:stream 104 (make-stream-socket :socket socket :stream socket)) 105 (:datagram 106 (make-datagram-socket socket :connected-p (and host port t)))))) 107 108 ;; One socket close method is sufficient, 109 ;; because socket-streams are also sockets. 110 (defmethod socket-close ((usocket usocket)) 111 "Close socket." 112 (with-mapped-conditions (usocket) 113 (close (socket usocket)))) 114 115 (defmethod socket-shutdown ((usocket stream-usocket) direction) 116 (with-mapped-conditions (usocket) 117 (socket:shutdown (socket usocket) :direction direction))) 118 119 (defun socket-listen (host port 120 &key reuseaddress 121 (reuse-address nil reuse-address-supplied-p) 122 (backlog 5) 123 (element-type 'character)) 124 ;; Allegro and OpenMCL socket interfaces bear very strong resemblence 125 ;; whatever you change here, change it also for OpenMCL 126 (let* ((reuseaddress (if reuse-address-supplied-p reuse-address reuseaddress)) 127 (sock (with-mapped-conditions (nil host) 128 (apply #'socket:make-socket 129 (append (list :connect :passive 130 :reuse-address reuseaddress 131 :local-port port 132 :backlog backlog 133 :format (to-format element-type) 134 ;; allegro now ignores :format 135 ) 136 (when (ip/= host *wildcard-host*) 137 (list :local-host host))))))) 138 (make-stream-server-socket sock :element-type element-type))) 139 140 (defmethod socket-accept ((socket stream-server-usocket) &key element-type) 141 (declare (ignore element-type)) ;; allegro streams are multivalent 142 (let ((stream-sock 143 (with-mapped-conditions (socket) 144 (socket:accept-connection (socket socket))))) 145 (make-stream-socket :socket stream-sock :stream stream-sock))) 146 147 (defmethod get-local-address ((usocket usocket)) 148 (hbo-to-vector-quad (socket:local-host (socket usocket)))) 149 150 (defmethod get-peer-address ((usocket stream-usocket)) 151 (hbo-to-vector-quad (socket:remote-host (socket usocket)))) 152 153 (defmethod get-local-port ((usocket usocket)) 154 (socket:local-port (socket usocket))) 155 156 (defmethod get-peer-port ((usocket stream-usocket)) 157 #+allegro 158 (socket:remote-port (socket usocket))) 159 160 (defmethod get-local-name ((usocket usocket)) 161 (values (get-local-address usocket) 162 (get-local-port usocket))) 163 164 (defmethod get-peer-name ((usocket stream-usocket)) 165 (values (get-peer-address usocket) 166 (get-peer-port usocket))) 167 168 #+allegro 169 (defmethod socket-send ((usocket datagram-usocket) buffer size &key host port (offset 0)) 170 (with-mapped-conditions (usocket host) 171 (let ((s (socket usocket))) 172 (socket:send-to s 173 (if (zerop offset) 174 buffer 175 (subseq buffer offset (+ offset size))) 176 size 177 :remote-host host 178 :remote-port port)))) 179 180 #+allegro 181 (defmethod socket-receive ((usocket datagram-usocket) buffer length &key) 182 (declare (values (simple-array (unsigned-byte 8) (*)) ; buffer 183 (integer 0) ; size 184 (unsigned-byte 32) ; host 185 (unsigned-byte 16))) ; port 186 (with-mapped-conditions (usocket) 187 (let ((s (socket usocket))) 188 (socket:receive-from s length :buffer buffer :extract t)))) 189 190 (defun get-host-by-address (address) 191 (with-mapped-conditions (nil address) 192 (socket:ipaddr-to-hostname (host-to-hbo address)))) 193 194 (defun get-hosts-by-name (name) 195 ;;###FIXME: ACL has the acldns module which returns all A records 196 ;; only problem: it doesn't fall back to tcp (from udp) if the returned 197 ;; structure is too long. 198 (with-mapped-conditions (nil name) 199 (list (hbo-to-vector-quad (socket:lookup-hostname 200 (host-to-hostname name)))))) 201 202 (defun %setup-wait-list (wait-list) 203 (declare (ignore wait-list))) 204 205 (defun %add-waiter (wait-list waiter) 206 (push (socket waiter) (wait-list-%wait wait-list))) 207 208 (defun %remove-waiter (wait-list waiter) 209 (setf (wait-list-%wait wait-list) 210 (remove (socket waiter) (wait-list-%wait wait-list)))) 211 212 #+allegro 213 (defun wait-for-input-internal (wait-list &key timeout) 214 (with-mapped-conditions () 215 (let ((active-internal-sockets 216 (if timeout 217 (mp:wait-for-input-available (wait-list-%wait wait-list) 218 :timeout timeout) 219 (mp:wait-for-input-available (wait-list-%wait wait-list))))) 220 ;; this is quadratic, but hey, the active-internal-sockets 221 ;; list is very short and it's only quadratic in the length of that one. 222 ;; When I have more time I could recode it to something of linear 223 ;; complexity. 224 ;; [Same code is also used in openmcl.lisp] 225 (dolist (x active-internal-sockets) 226 (setf (state (gethash x (wait-list-map wait-list))) 227 :read)) 228 wait-list)))