1.0.23.69: Add docstrings to SB-BSD-SOCKETS:SOCKET-MAKE-STREAM.
[sbcl.git] / contrib / sb-bsd-sockets / sockets.lisp
index c010811..e21228f 100644 (file)
@@ -4,6 +4,7 @@
 ;;;; is deferred to inet.lisp, unix.lisp, etc
 
 (eval-when (:load-toplevel :compile-toplevel :execute)
+
 (defclass socket ()
   ((file-descriptor :initarg :descriptor
                     :reader socket-file-descriptor)
@@ -19,7 +20,7 @@ protocol. Other values are used as-is.")
           :reader socket-type
           :documentation "Type of the socket: :STREAM or :DATAGRAM.")
    (stream))
-  (:documentation "Common base class of all sockets, not ment to be
+  (:documentation "Common base class of all sockets, not meant to be
 directly instantiated.")))
 
 (defmethod print-object ((object socket) stream)
@@ -95,7 +96,9 @@ values"))
                                sockaddr
                                (size-of-sockaddr socket))))
       (cond
-        ((and (= fd -1) (= sockint::EAGAIN (sb-unix::get-errno)))
+        ((and (= fd -1)
+              (member (sb-unix::get-errno)
+                      (list sockint::EAGAIN sockint::EINTR)))
          nil)
         ((= fd -1) (socket-error "accept"))
         (t (apply #'values
@@ -153,25 +156,27 @@ values"))
 
 (defgeneric socket-receive (socket buffer length
                             &key
-                            oob peek waitall element-type)
-  (:documentation "Read LENGTH octets from SOCKET into BUFFER (or a freshly-consed buffer if
-NIL), using recvfrom(2).  If LENGTH is NIL, the length of BUFFER is
-used, so at least one of these two arguments must be non-NIL.  If
-BUFFER is supplied, it had better be of an element type one octet wide.
-Returns the buffer, its length, and the address of the peer
-that sent it, as multiple values.  On datagram sockets, sets MSG_TRUNC
-so that the actual packet length is returned even if the buffer was too
-small"))
+                            oob peek waitall dontwait element-type)
+  (:documentation
+   "Read LENGTH octets from SOCKET into BUFFER (or a freshly-consed
+buffer if NIL), using recvfrom(2). If LENGTH is NIL, the length of
+BUFFER is used, so at least one of these two arguments must be
+non-NIL. If BUFFER is supplied, it had better be of an element type
+one octet wide. Returns the buffer, its length, and the address of the
+peer that sent it, as multiple values. On datagram sockets, sets
+MSG_TRUNC so that the actual packet length is returned even if the
+buffer was too small."))
 
 (defmethod socket-receive ((socket socket) buffer length
                            &key
-                           oob peek waitall
+                           oob peek waitall dontwait
                            (element-type 'character))
   (with-sockaddr-for (socket sockaddr)
     (let ((flags
            (logior (if oob sockint::MSG-OOB 0)
                    (if peek sockint::MSG-PEEK 0)
                    (if waitall sockint::MSG-WAITALL 0)
+                   (if dontwait sockint::MSG-DONTWAIT 0)
                    #+linux sockint::MSG-NOSIGNAL ;don't send us SIGPIPE
                    (if (eql (socket-type socket) :datagram)
                        sockint::msg-TRUNC 0))))
@@ -199,7 +204,10 @@ small"))
                                         sockaddr
                                         (sb-alien:addr sa-len))))
                 (cond
-                  ((and (= len -1) (= sockint::EAGAIN (sb-unix::get-errno))) nil)
+                  ((and (= len -1)
+                        (member (sb-unix::get-errno)
+                                (list sockint::EAGAIN sockint::EINTR)))
+                   nil)
                   ((= len -1) (socket-error "recvfrom"))
                   (t (loop for i from 0 below len
                            do (setf (elt buffer i)
@@ -211,6 +219,76 @@ small"))
                                                  (bits-of-sockaddr socket sockaddr)))))))
           (sb-alien:free-alien copy-buffer))))))
 
+(defmacro with-vector-sap ((name vector) &body body)
+  `(sb-sys:with-pinned-objects (,vector)
+     (let ((,name (sb-sys:vector-sap ,vector)))
+       ,@body)))
+
+(defgeneric socket-send (socket buffer length
+                                &key
+                                address
+                                external-format
+                                oob eor dontroute dontwait nosignal
+                                #+linux confirm #+linux more)
+  (:documentation
+   "Send LENGTH octets from BUFFER into SOCKET, using sendto(2). If BUFFER
+is a string, it will converted to octets according to EXTERNAL-FORMAT. If
+LENGTH is NIL, the length of the octet buffer is used. The format of ADDRESS
+depends on the socket type (for example for INET domain sockets it would
+be a list of an IP address and a port). If no socket address is provided,
+send(2) will be called instead. Returns the number of octets written."))
+
+(defmethod socket-send ((socket socket) buffer length
+                        &key
+                        address
+                        (external-format :default)
+                        oob eor dontroute dontwait nosignal
+                        #+linux confirm #+linux more)
+  (let* ((flags
+          (logior (if oob sockint::MSG-OOB 0)
+                  (if eor sockint::MSG-EOR 0)
+                  (if dontroute sockint::MSG-DONTROUTE 0)
+                  (if dontwait sockint::MSG-DONTWAIT 0)
+                  (if nosignal sockint::MSG-NOSIGNAL 0)
+                  #+linux (if confirm sockint::MSG-CONFIRM 0)
+                  #+linux (if more sockint::MSG-MORE 0)))
+         (buffer (etypecase buffer
+                   (string
+                    (sb-ext:string-to-octets buffer
+                                             :external-format external-format
+                                             :null-terminate nil))
+                   ((simple-array (unsigned-byte 8))
+                    buffer)
+                   ((array (unsigned-byte 8))
+                    (make-array (length buffer)
+                                :element-type '(unsigned-byte 8)
+                                :initial-contents buffer))))
+         (len (with-vector-sap (buffer-sap buffer)
+                (unless length
+                  (setf length (length buffer)))
+                (if address
+                    (with-sockaddr-for (socket sockaddr address)
+                      (sb-alien:with-alien ((sa-len sockint::socklen-t
+                                                    (size-of-sockaddr socket)))
+                        (sockint::sendto (socket-file-descriptor socket)
+                                         buffer-sap
+                                         length
+                                         flags
+                                         sockaddr
+                                         sa-len)))
+                    (sockint::send (socket-file-descriptor socket)
+                                   buffer-sap
+                                   length
+                                   flags)))))
+    (cond
+      ((and (= len -1)
+            (member (sb-unix::get-errno)
+                    (list sockint::EAGAIN sockint::EINTR)))
+       nil)
+      ((= len -1)
+       (socket-error "sendto"))
+      (t len))))
+
 (defgeneric socket-listen (socket backlog)
   (:documentation "Mark SOCKET as willing to accept incoming connections.  BACKLOG
 defines the maximum length that the queue of pending connections may
@@ -269,22 +347,45 @@ calls CLOSE on that stream instead"))
                nil))))))
 
 
-(defgeneric socket-make-stream (socket &rest args)
+(defgeneric socket-make-stream (socket &key input output
+                                       element-type external-format
+                                       buffering
+                                       timeout)
   (:documentation "Find or create a STREAM that can be used for IO on
-SOCKET (which must be connected).  ARGS are passed onto
-SB-SYS:MAKE-FD-STREAM."))
-
-(defmethod socket-make-stream ((socket socket) &rest args)
+SOCKET \(which must be connected\).  Specify whether the stream is for
+INPUT, OUTPUT, or both \(it is an error to specify neither\).  ELEMENT-TYPE
+and EXTERNAL-FORMAT are as per OPEN.  TIMEOUT specifies a read timeout
+for the stream."))
+
+(defmethod socket-make-stream ((socket socket)
+                               &key input output
+                               (element-type 'character)
+                               (buffering :full)
+                               (external-format :default)
+                               timeout)
+  "Default method for SOCKET objects.  An ELEMENT-TYPE of :DEFAULT
+will construct a bivalent stream.  Acceptable values for BUFFERING
+are :FULL, :LINE and :NONE.  Streams will have no TIMEOUT
+by default.
+  The stream for SOCKET will be cached, and a second invocation of this
+method will return the same stream.  This may lead to oddities if this
+function is invoked with inconsistent arguments \(e.g., one might request
+an input stream and get an output stream in response\)."
   (let ((stream
          (and (slot-boundp socket 'stream) (slot-value socket 'stream))))
     (unless stream
-      (setf stream (apply #'sb-sys:make-fd-stream
-                          (socket-file-descriptor socket)
-                          :name "a constant string"
-                          :dual-channel-p t
-                          args))
+      (setf stream (sb-sys:make-fd-stream
+                    (socket-file-descriptor socket)
+                    :name "a socket"
+                    :dual-channel-p t
+                    :input input
+                    :output output
+                    :element-type element-type
+                    :buffering buffering
+                    :external-format external-format
+                    :timeout timeout)))
       (setf (slot-value socket 'stream) stream)
-      (sb-ext:cancel-finalization socket))
+    (sb-ext:cancel-finalization socket)
     stream))
 
 \f