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)))