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