0.7.12.28
authorDaniel Barlow <dan@telent.net>
Fri, 7 Feb 2003 17:11:38 +0000 (17:11 +0000)
committerDaniel Barlow <dan@telent.net>
Fri, 7 Feb 2003 17:11:38 +0000 (17:11 +0000)
Added WHN's port of Kevin Rosenberg's ACL-like toplevel as
the contrib package SB-ACLREPL

Tidied up some of the contrib infrastructure in pursuance of OAOO

Renamefest: adopt standard SB- package prefixes for all contribs

Contrib packages are now built by make-target-contrib.sh
(called from make.sh) instead of being left to install.sh time

32 files changed:
NEWS
contrib/STANDARDS
contrib/asdf/Makefile
contrib/sb-bsd-sockets/FAQ [new file with mode: 0644]
contrib/sb-bsd-sockets/Makefile [new file with mode: 0644]
contrib/sb-bsd-sockets/NEWS [new file with mode: 0644]
contrib/sb-bsd-sockets/README [new file with mode: 0644]
contrib/sb-bsd-sockets/TODO [new file with mode: 0644]
contrib/sb-bsd-sockets/alien/get-h-errno.c [new file with mode: 0755]
contrib/sb-bsd-sockets/alien/undefs.c [new file with mode: 0644]
contrib/sb-bsd-sockets/api-reference.html [new file with mode: 0644]
contrib/sb-bsd-sockets/array-data.lisp [new file with mode: 0644]
contrib/sb-bsd-sockets/constants.lisp [new file with mode: 0644]
contrib/sb-bsd-sockets/def-to-lisp.lisp [new file with mode: 0644]
contrib/sb-bsd-sockets/defpackage.lisp [new file with mode: 0644]
contrib/sb-bsd-sockets/doc.lisp [new file with mode: 0644]
contrib/sb-bsd-sockets/foreign-glue.lisp [new file with mode: 0644]
contrib/sb-bsd-sockets/inet.lisp [new file with mode: 0644]
contrib/sb-bsd-sockets/malloc.lisp [new file with mode: 0644]
contrib/sb-bsd-sockets/misc.lisp [new file with mode: 0644]
contrib/sb-bsd-sockets/name-service.lisp [new file with mode: 0644]
contrib/sb-bsd-sockets/rt.lisp [new file with mode: 0644]
contrib/sb-bsd-sockets/sb-bsd-sockets.asd [new file with mode: 0644]
contrib/sb-bsd-sockets/sockets.lisp [new file with mode: 0644]
contrib/sb-bsd-sockets/sockopt.lisp [new file with mode: 0644]
contrib/sb-bsd-sockets/split.lisp [new file with mode: 0644]
contrib/sb-bsd-sockets/tests.lisp [new file with mode: 0644]
contrib/sb-bsd-sockets/unix.lisp [new file with mode: 0644]
contrib/vanilla-module.mk [new file with mode: 0644]
make-target-contrib.sh [new file with mode: 0644]
make.sh
version.lisp-expr

diff --git a/NEWS b/NEWS
index 37303aa..66ca016 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1549,8 +1549,11 @@ changes in sbcl-0.7.13 relative to sbcl-0.7.12:
   * REQUIRE and PROVIDE are now optionally capable of doing something
     useful: see the documentation string for REQUIRE
   * infrastructure for a managed SBCL contrib system: contributed 
-    modules in this release include a copy of the ASDF system definition
-    facility, and an interface to the BSD Sockets API
+    modules in this release include 
+       - the ASDF system definition facility
+       - an interface to the BSD Sockets API
+       - an ACL-like convenience interface to the repl 
+               (thanks to Kevin Rosenberg)
 
 planned incompatible changes in 0.7.x:
   * (not done yet, but planned:) When the profiling interface settles
index f5b9598..f344dc5 100644 (file)
@@ -56,7 +56,7 @@ If the contrib package involves more than one file, you are encouraged
 to use ASDF to build it and load it.  A version of asdf is bundled as
 an SBCL contrib, which knows to look in $SBCL_HOME/systems/ for asd
 files - your install target should create an appropriate symlink there
-to the installed location of the system file.  Look in bsd-sockets/Makefile
+to the installed location of the system file.  Look in sb-bsd-sockets/Makefile
 for an example of an asdf-using contrib
 
 $(INSTALL_DIR) will have been created by the system before your
@@ -77,25 +77,21 @@ of the preceding.  Document formats not available on typical
 well-endowed-with-free-stuff Unix systems are discouraged.  DocBook
 is fine, as the SBCL manual is DocBook anyway ]
 
-[ install.sh should copy the documentation somewhere that the user can
-find it ]
+[ make install should copy the documentation somewhere that the user
+can find it ]
 
 * Lisp-level requirements
 
-An sbcl contrib should not stamp on sbcl internals or redefine symbols
-in CL, CL-USER.  Sometimes this is the only way to do something,
-though: individual cases will be considered on their merits.  A
-package that hacks undocumented(sic) interfaces may be accepted for
-contrib, but it does not follow from that that the interface is now
-published or will be preserved in future SBCL versions - contrib
-authors are encouraged instead to submit patches to SBCL that provide
-clean documented APIs which reasonably can be preserved.  If in doubt,
-seek consensus on the sbcl-devel list
+An sbcl contrib should attempt to avoid stamping on sbcl internals or
+redefining symbols in CL, CL-USER.  Sometimes this is the only way to do
+something, though: individual cases will be considered on their
+merits.  A package that hacks undocumented(sic) interfaces may be
+accepted for contrib, but it does not follow from that that the
+interface is now published or will be preserved in future SBCL
+versions - contrib authors are encouraged instead to submit patches to
+SBCL that provide clean documented APIs which reasonably can be
+preserved.  If in doubt, seek consensus on the sbcl-devel list
 
 A contrib must load into its own Lisp package(s) instead of polluting
 CL-USER or one of the system packages.  The Lisp package name should
-be chosen in some way that has reasonable expectation of being unique.
-[We could potentially keep a registry of contrib archive name =>
-package name(s)]
-
-
+begin with "SB-".  Ask the sbcl-devel list for a suitable name.
index 3067de6..caa7300 100644 (file)
@@ -1,8 +1,5 @@
-asdf.fasl: asdf.lisp
-       $(SBCL) --eval '(compile-file "asdf")' </dev/null
+MODULE=asdf
+include ../vanilla-module.mk
 
-test:
+test::
        true
-
-install: asdf.fasl
-       cp $< $(INSTALL_DIR)
diff --git a/contrib/sb-bsd-sockets/FAQ b/contrib/sb-bsd-sockets/FAQ
new file mode 100644 (file)
index 0000000..d788eb2
--- /dev/null
@@ -0,0 +1,47 @@
+Frequently Asked Questions
+
+Q1) Is this the same thing as db-sockets
+
+A1) Basically, yes.  It's hoped that bundling it as a contrib may make
+it easier for people to install.
+
+Q2) What are these test things?  How do I run the tests?
+
+A2) Some of the tests get run automatically when the package is built
+- if the tests fail, the package is not installed.  The rest of the
+tests depend on having Internet access which may not always be the
+case on a build machine, but you can run them by hand from the Lisp
+listener, if you want to:
+
+* (rt:do-tests)
+
+This uses the regression tester from the CMU AI repository to run the
+tests defined in tests.lisp.  You should not get any test failures,
+unless -
+
+a) your "echo" services are disabled in inetd.conf -
+SIMPLE-TCP-CLIENT and SIMPLE-UDP-CLIENT both attempt to connect to the
+echo port.
+
+b) you're not on the internet - SIMPLE-HTTP-CLIENT attempts to connect to
+ww.telent.net, and other tests do DNS lookups for well-known hosts
+
+c) a.root-servers.net has moved IP address
+
+Q3) What's constants.lisp-temp?
+
+A3) Many of the structure offsets and symbolic constants vary between
+architectures and operating systems.  To avoid a maintenance
+nightmare, we derive them automatically by creating and running a
+small C program.  The C program is created by def-to-lisp.lisp
+with input from constants.lisp
+
+Some of the exciting stuff in bsd-sockets.asd writes a C program in
+/tmp, compiles it, and runs it.  The output from this program becomes
+constants.lisp-temp
+
+Q4) Is this compatible with ACL?  With CMUCL's internet.lisp?
+
+A4) No.  This is a sufficiently low-level interface that either could
+be built on top of it, though.  Actually, theq ACL-COMPAT library that
+comes with Portable Allegroserve may already have this.
diff --git a/contrib/sb-bsd-sockets/Makefile b/contrib/sb-bsd-sockets/Makefile
new file mode 100644 (file)
index 0000000..42a6e8e
--- /dev/null
@@ -0,0 +1,13 @@
+SYSTEM=bsd-sockets
+
+all: 
+       $(MAKE) -C ../asdf
+       echo "(asdf:operate 'asdf:load-op :$(SYSTEM))" | \
+         $(SBCL) --eval '(load "../asdf/asdf")'
+
+test:
+       true
+
+install: all
+       tar cf - . | ( cd $(INSTALL_DIR) && tar xpvf - )
+       ( cd  $(SBCL_HOME)/systems && ln -fs ../$(SYSTEM)/$(SYSTEM).asd . )
diff --git a/contrib/sb-bsd-sockets/NEWS b/contrib/sb-bsd-sockets/NEWS
new file mode 100644 (file)
index 0000000..c12398d
--- /dev/null
@@ -0,0 +1,135 @@
+Changes in 0.58 - Sun Jan 12 00:53:53 GMT 2003
+
+Fix db-sockets.asd so that it doesn't recompile alien.so every single
+time.
+
+Announce anon-cvs repo for people to get in-between versions
+
+MSG_NOSIGNAL is a linuxism, I'm told.
+
+Changes in 0.57 - Wed Sep 11 12:27:32 2002
+
+Fix for compilation bug reported by Andreas Fuchs.  Don't use 0.56, it
+was a mistakenly uploaded file
+
+Changes in 0.55 - Tue Sep 10 23:42:27 2002
+
+Fix for a unix-domain sockets problem, courtesy of David Lichteblau
+
+Changes in 0.54 - Wed Mar 6 2002
+
+New version mostly due to new packaging format: this is now a
+vendor-neutral cclan (vn-cclan) package.  See INSTALL file
+
+Fixed bug in af_file support.
+
+Changes in 0.53 - Thu Jan 31 2002
+
+By popular request (two people, at last count) this works in CMUCL again.
+Also, some documentation updates, a really silly bug in make-instance 
+fixed, and support for the TCP_NODELAY socket option
+
+Changes in 0.52 - Tue Jan 8 2002
+
+Very few.  This release was put out a few days after 0.5.1 because
+0.5.1 is less than 0.42, and various packaging tools tend to get
+confused to see version numbers go backwards.
+
+Changes in 0.5.1 - Mon Jan 7 2002
+
+Support for AF_FILE (formerly known as Unix-domain) sockets; both
+stream and datagram.  
+
+MAKE-INET-SOCKET has been deprecated (but is still there).  New code is
+encouraged to write (make-instance 'inet-socket ...) instead
+
+Fairly pervasive low-level changes to avoid leaking quite as much
+memory.  May also have fixed a file descriptor leak in the process.
+
+Changes in 0.42
+
+Repackaged to be a debian-like package, and use
+common-lisp-controller, which required a reasonably large amount of
+thrashing around renaming files and so on.
+
+New function GET-HOST-BY-ADDRESS returns a HOST-ENT just like
+GET-HOST-BY-NAME does.
+
+Tested on SBCL 0.6.12.7.flaky1.1 (x86),  SBCL 0.6.12.7 (Alpha),
+CMUCL 18c+ 2.5.2 (x86)
+
+Changes in 0.41 - Sun Jan 7 2001
+
+Cleanups in the tests for more intelligible failure messages
+
+SOCKET-ERROR conditions now inherit from ERROR not CONDITION - as
+otherwise IGNORE-ERROR doesn't ignore them, which is unexpected
+
+Tested on debian cmucl 2.4.19 , sbcl pre-0.6.9 snapshot of Nov 30 2000.
+
+The latter doesn't build without manual intervention:
+
+ error in function SB-C::%DEFCONSTANT:
+    The constant INET-ADDRESS-ANY is being redefined.
+
+(just continue)
+
+Changes in 0.4 - Mon Jul 3 2000
+
+Now works (passes tests) in
+
+- Solaris 2.6 SPARC (CMUCL 18b)
+- Debian x86 GNU/Linux (Debian CMUCL 2.4.19)
+- Debian x86 GNU/Linux (SBCL 0.6.5)
+
+Some CMUCL-on-FreeBSD changes (mostly involve commenting stuff out).  Doesn't 
+work, though (but might in SBCL/FreeBSD)
+
+The Solaris changes comprised disabling bits and fixing an 
+endianness problem. 
+
+
+Changes in 0.37 -  Sat May 20 2000
+
+
+Changes from Martin Atzmueller to make it compile more cleanly in SBCL
+
+Changes in 0.36 -  Thu May 11 2000
+
+Some documentation cleanups
+
+New functions NON-BLOCKING-MODE and (SETF NON-BLOCKING-MODE) 
+
+EINTR now generates a INTERRUPTED-ERROR condition
+
+
+Changes in 0.35  - Mon May 1 2000
+
+
+MAKE-INET-SOCKET now can take a keyword for PROTOCOL: it lowercases
+the symbol's name, then looks it up using GET-PROTOCOL-BY-NAME
+
+A bad bug in the CMUCL code (which caused the EXTENSIONS package to
+disappear - oops...) was found and fixed
+
+
+Changes in 0.3  -  Apr 17 2000 
+
+Now works with SBCL (0.6.1, 0.6.2) in addition to CMUCL.
+
+Fixed to actually work with a READ-SEQUENCE implementation that does
+the right thing instead of the (suspected buggy) implementation in
+CMUCL. At least, the Hyperspec doesn't give me any particular cause
+for belief that READ-SEQUENCE can return before reading as much as the
+user asks it to, which is what we were using it for hitherto.
+
+The Makefile got a lot bigger. defs-to-lisp.lisp got a lot smaller.
+
+Standard make target creates "sockets-system.x86f" which contains all
+the code in a single file
+
+If you want to build it on SBCL you'll need a working defsystem for
+said platform first. This involves some fiddling around: first you
+need to get it from CLOCC on Sourceforge then you need to patch it
+with this diff. Unless you're looking at a version newer than 1.12, in
+which case they might have patched it already before you
diff --git a/contrib/sb-bsd-sockets/README b/contrib/sb-bsd-sockets/README
new file mode 100644 (file)
index 0000000..91e4df8
--- /dev/null
@@ -0,0 +1,29 @@
+o/~  Hey Mr Tambourine Man, play some -*- Text -*- for me   o/~
+
+A semi-sane sockets interface for SBCL.  Usually also works in CMUCL, 
+but is rarely actually tested there so may require some massaging
+
+See INSTALL for prerequisites and build details
+
+It uses the regression tester from the CMU AI repository.  This is
+bundled in the file rt.lisp which is unchanged except where I added a
+DEFPACKAGE form.  The tests themselves are in tests.lisp, and can be
+run using the Makefile target intended for the purpose, or by
+evaluating (rt:do-tests).  Note that one of the tests is an HTTP
+client that connects back to ww.telent.net; if this bothers your
+expectations of privacy, don't run it.
+
+There is an automatically generated API reference in
+api-reference.html which you can regenerate if you can figure out how
+doc.lisp works.  You might find the examples in tests.lisp useful,
+too.
+
+Feedback, patches, development versions
+
+Instructions on how to access the CVS repository for db-sockets are
+at http://cvs.telent.net/
+
+If you find bugs or want to send patches for enhancements, by email to
+Daniel Barlow <dan@telent.net>, but please check the CVS version first.
+
+$Id$ 
diff --git a/contrib/sb-bsd-sockets/TODO b/contrib/sb-bsd-sockets/TODO
new file mode 100644 (file)
index 0000000..90c82a3
--- /dev/null
@@ -0,0 +1,20 @@
+
+Things To Do - Urgent!    (with apologies to Douglas Adams)
+
+I probably have opinions about how to do most of these.  Even if not,
+I almost certainly have opinions on how not to.  Send me a proposal
+before spending serious amounts of time on it.
+
+- the rest of the functions.  A socket-send that doesn't use streams 
+would be a good one
+
+- the rest of the errors
+
+- the rest of the socket options: integer and boolean socket-level
+options are in but need odd ones, plus tcp, udp, ip
+
+- async name service lookups.
+
+- write tests for socket-name and socket-peername
+
+- documentation: see doc.lisp, but beware: it's grotty.
diff --git a/contrib/sb-bsd-sockets/alien/get-h-errno.c b/contrib/sb-bsd-sockets/alien/get-h-errno.c
new file mode 100755 (executable)
index 0000000..a1d22a6
--- /dev/null
@@ -0,0 +1,6 @@
+#include <netdb.h>
+
+int get_h_errno()
+{
+    return h_errno;
+}
diff --git a/contrib/sb-bsd-sockets/alien/undefs.c b/contrib/sb-bsd-sockets/alien/undefs.c
new file mode 100644 (file)
index 0000000..fca6cde
--- /dev/null
@@ -0,0 +1,9 @@
+/* create a .o file with undefined references to all the C stuff we need
+ * that cmucl hasn't already fouind for us.  Not needed on Linux/i386
+ * because it has dynamic loading anyway
+ */
+
+void likewecare() {
+    getprotobyname();
+}
+
diff --git a/contrib/sb-bsd-sockets/api-reference.html b/contrib/sb-bsd-sockets/api-reference.html
new file mode 100644 (file)
index 0000000..09e3f04
--- /dev/null
@@ -0,0 +1,188 @@
+<html><head><title>db-sockets API Reference</title></head><body>
+<h1>Package SOCKETS</h1>
+
+<P>
+A thinly-disguised BSD socket API for SBCL.  Ideas stolen from the BSD
+socket API for C and Graham Barr's IO::Socket classes for Perl.
+<P>
+We represent sockets as CLOS objects, and rename a lot of methods and
+arguments to fit Lisp style more closely.
+<P>
+
+<P>
+<h2>Contents</h2>
+<P>
+<ol>
+<li> General concepts
+<li> Methods applicable to all <a href="#socket">sockets</a>
+<li> <a href="#sockopt">Socket Options</a>
+<li> Methods applicable to a particular subclass
+<ol>
+<li> <a href="#internet">INET-SOCKET</a> - Internet Protocol (TCP, UDP, raw) sockets
+<li> Methods on <a href="#UNIX-SOCKET">UNIX-SOCKET</a> - Unix-domain sockets 
+</ol>
+<li> <a href="#name-service">Name resolution</a> (DNS, /etc/hosts, &amp;c)
+</ol>
+<P>
+<h2>General concepts</h2>
+<P>
+<p>Most of the functions are modelled on the BSD socket API.  BSD sockets
+are widely supported, portably <i>(well, fairly portably)</i>
+available on a variety of systems, and documented.  There are some
+differences in approach where we have taken advantage of some of the more useful features of Common Lisp - briefly
+<P>
+<ul>
+<li> Where the C API would typically return -1 and set errno, db-sockets
+signals an error.  All the errors are subclasses of SOCKET-CONDITION
+and generally correspond one for one with possible <tt>errno</tt> values
+<P>
+<li> We use multiple return values in many places where the C API would use p[ass-by-reference values
+<P>
+<li> We can often avoid supplying an explicit <i>length</i> argument to
+functions because we already know how long the argument is.
+<P>
+<li> IP addresses and ports are represented in slightly friendlier fashion
+than "network-endian integers".  See the section on <a href="#internet"
+>Internet domain</a> sockets for details.
+</ul>
+<P>
+<P>
+<hr> <h2>SOCKETs</h2>
+<P>
+<p><a name="SOCKET"><i>Class: </i><b>SOCKET</b></a>
+<p><b>Slots:</b><ul><li>FILE-DESCRIPTOR : </li>
+<li>FAMILY : </li>
+<li>PROTOCOL : </li>
+<li>TYPE : </li>
+<li>STREAM : </li>
+</ul><p><a name="SOCKET-BIND"><table width="100%"><tr><td width="80%">(socket-bind <i> (s <a href="#socket">socket</a>) &rest address</i>)</td><td align=right>Generic Function</td></tr></table>
+<p><a name="SOCKET-ACCEPT"><table width="100%"><tr><td width="80%">(socket-accept <i> (socket <a href="#socket">socket</a>)</i>)</td><td align=right>Method</td></tr></table>
+<blockquote>Perform the accept(2) call, returning a newly-created connected socket
+and the peer address as multiple values</blockquote>
+<p><a name="SOCKET-CONNECT"><table width="100%"><tr><td width="80%">(socket-connect <i> (s <a href="#socket">socket</a>) &rest address</i>)</td><td align=right>Generic Function</td></tr></table>
+<p><a name="SOCKET-PEERNAME"><table width="100%"><tr><td width="80%">(socket-peername <i> (socket <a href="#socket">socket</a>)</i>)</td><td align=right>Method</td></tr></table>
+<blockquote>Return the socket's peer; depending on the address family this may return multiple values</blockquote>
+<p><a name="SOCKET-NAME"><table width="100%"><tr><td width="80%">(socket-name <i> (socket <a href="#socket">socket</a>)</i>)</td><td align=right>Method</td></tr></table>
+<blockquote>Return the address (as vector of bytes) and port that the socket is bound to, as multiple values</blockquote>
+<p><a name="SOCKET-RECEIVE"><table width="100%"><tr><td width="80%">(socket-receive <i> (socket <a href="#socket">socket</a>) buffer length &key oob peek waitall (element-type
+                                                                            'character)</i>)</td><td align=right>Method</td></tr></table>
+<blockquote>Read LENGTH octets from <a href="#SOCKET">SOCKET</a> 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</blockquote>
+<p><a name="SOCKET-LISTEN"><table width="100%"><tr><td width="80%">(socket-listen <i> (socket <a href="#socket">socket</a>) backlog</i>)</td><td align=right>Method</td></tr></table>
+<blockquote>Mark <a href="#SOCKET">SOCKET</a> as willing to accept incoming connections.  BACKLOG
+defines the maximum length that the queue of pending connections may
+grow to before new connection attempts are refused.  See also listen(2)</blockquote>
+<p><a name="SOCKET-CLOSE"><table width="100%"><tr><td width="80%">(socket-close <i> (socket <a href="#socket">socket</a>)</i>)</td><td align=right>Method</td></tr></table>
+<blockquote>Close <a href="#SOCKET">SOCKET</a>.  May throw any kind of error that write(2) would have
+thrown.  If <a href="#SOCKET-MAKE-STREAM">SOCKET-MAKE-STREAM</a> has been called, calls CLOSE on that
+stream instead</blockquote>
+<p><a name="SOCKET-MAKE-STREAM"><table width="100%"><tr><td width="80%">(socket-make-stream <i> (socket <a href="#socket">socket</a>) &rest args</i>)</td><td align=right>Method</td></tr></table>
+<blockquote>Find or create a STREAM that can be used for IO on <a href="#SOCKET">SOCKET</a> (which
+must be connected).  ARGS are passed onto SB-SYS:MAKE-FD-STREAM.</blockquote>
+<hr>
+<H2> Socket Options </h2>
+<a name="sockopt"> </a>
+<p> A subset of socket options are supported, using a fairly
+general framework which should make it simple to add more as required 
+- see sockopt.lisp for details.  The name mapping from C is fairly
+straightforward: <tt>SO_RCVLOWAT</tt> becomes
+<tt>sockopt-receive-low-water</tt> and <tt>(setf
+sockopt-receive-low-water)</tt>.
+|<p><a name="SOCKOPT-REUSE-ADDRESS"><table width="100%"><tr><td width="80%">(sockopt-reuse-address <i> (socket <a href="#socket">socket</a>) argument</i>)</td><td align=right>Accessor</td></tr></table>
+<blockquote>Return the value of the SO-REUSEADDR socket option for <a href="#SOCKET">SOCKET</a>.  This can also be updated with SETF.</blockquote>
+<p><a name="SOCKOPT-KEEP-ALIVE"><table width="100%"><tr><td width="80%">(sockopt-keep-alive <i> (socket <a href="#socket">socket</a>) argument</i>)</td><td align=right>Accessor</td></tr></table>
+<blockquote>Return the value of the SO-KEEPALIVE socket option for <a href="#SOCKET">SOCKET</a>.  This can also be updated with SETF.</blockquote>
+<p><a name="SOCKOPT-OOB-INLINE"><table width="100%"><tr><td width="80%">(sockopt-oob-inline <i> (socket <a href="#socket">socket</a>) argument</i>)</td><td align=right>Accessor</td></tr></table>
+<blockquote>Return the value of the SO-OOBINLINE socket option for <a href="#SOCKET">SOCKET</a>.  This can also be updated with SETF.</blockquote>
+<p><a name="SOCKOPT-BSD-COMPATIBLE"><table width="100%"><tr><td width="80%">(sockopt-bsd-compatible <i> (socket <a href="#socket">socket</a>) argument</i>)</td><td align=right>Accessor</td></tr></table>
+<blockquote>Return the value of the SO-BSDCOMPAT socket option for <a href="#SOCKET">SOCKET</a>.  This can also be updated with SETF.</blockquote>
+<p><a name="SOCKOPT-PASS-CREDENTIALS"><table width="100%"><tr><td width="80%">(sockopt-pass-credentials <i> (socket <a href="#socket">socket</a>) argument</i>)</td><td align=right>Accessor</td></tr></table>
+<blockquote>Return the value of the SO-PASSCRED socket option for <a href="#SOCKET">SOCKET</a>.  This can also be updated with SETF.</blockquote>
+<p><a name="SOCKOPT-DEBUG"><table width="100%"><tr><td width="80%">(sockopt-debug <i> (socket <a href="#socket">socket</a>) argument</i>)</td><td align=right>Accessor</td></tr></table>
+<blockquote>Return the value of the SO-DEBUG socket option for <a href="#SOCKET">SOCKET</a>.  This can also be updated with SETF.</blockquote>
+<p><a name="SOCKOPT-DONT-ROUTE"><table width="100%"><tr><td width="80%">(sockopt-dont-route <i> (socket <a href="#socket">socket</a>) argument</i>)</td><td align=right>Accessor</td></tr></table>
+<blockquote>Return the value of the SO-DONTROUTE socket option for <a href="#SOCKET">SOCKET</a>.  This can also be updated with SETF.</blockquote>
+<p><a name="SOCKOPT-BROADCAST"><table width="100%"><tr><td width="80%">(sockopt-broadcast <i> (socket <a href="#socket">socket</a>) argument</i>)</td><td align=right>Accessor</td></tr></table>
+<blockquote>Return the value of the SO-BROADCAST socket option for <a href="#SOCKET">SOCKET</a>.  This can also be updated with SETF.</blockquote>
+<p><a name="SOCKOPT-TCP-NODELAY"><table width="100%"><tr><td width="80%">(sockopt-tcp-nodelay <i> (socket <a href="#socket">socket</a>) argument</i>)</td><td align=right>Accessor</td></tr></table>
+<blockquote>Return the value of the TCP-NODELAY socket option for <a href="#SOCKET">SOCKET</a>.  This can also be updated with SETF.</blockquote>
+<hr> <h2>INET-domain sockets</h2>
+<P>
+<p>The TCP and UDP sockets that you know and love.  Some representation issues:
+<ul>
+<li>These functions do not accept hostnames directly: see <a href="#name-service">name resolution</a>
+<li>Internet <b>addresses</b> are represented by vectors of <tt>(unsigned-byte 8)</tt> - viz. <tt>#(127 0 0 1)</tt>.  <b>Ports</b> are just integers: <tt>6010</tt>.  No conversion between network- and host-order data is needed from the user of this package.
+<li><b><i>socket addresses</i></b> are represented by the two values for <b>address</b> and <b>port</b>, so for example, <tt>(<a href="#SOCKET-CONNECT">socket-connect</a> s #(192.168.1.1) 80)</tt>
+</ul>
+<P>
+<p><a name="INET-SOCKET"><i>Class: </i><b>INET-SOCKET</b></a>
+<p><b>Slots:</b><ul><li>FAMILY : </li>
+</ul><p><a name="MAKE-INET-ADDRESS"><table width="100%"><tr><td width="80%">(make-inet-address <i> dotted-quads</i>)</td><td align=right>Function</td></tr></table>
+<blockquote>Return a vector of octets given a string DOTTED-QUADS in the format
+"127.0.0.1"</blockquote>
+<p><a name="GET-PROTOCOL-BY-NAME"><table width="100%"><tr><td width="80%">(get-protocol-by-name <i> name</i>)</td><td align=right>Function</td></tr></table>
+<blockquote>Returns the network protocol number associated with the string NAME,
+using getprotobyname(2) which typically looks in NIS or /etc/protocols</blockquote>
+<p><a name="MAKE-INET-SOCKET"><table width="100%"><tr><td width="80%">(make-inet-socket <i> type protocol</i>)</td><td align=right>Function</td></tr></table>
+<blockquote>Make an INET socket.  Deprecated in favour of make-instance</blockquote>
+<hr> <h2>File-domain sockets</h2>
+<P>
+File-domain (AF_FILE) sockets are also known as Unix-domain sockets, but were
+renamed by POSIX presumably on the basis that they may be
+available on other systems too.  
+<P>
+A file-domain socket address is a string, which is used to create a node
+in the local filesystem.  This means of course that they cannot be used across
+a network.
+<P>
+|<p><a name="UNIX-SOCKET"><i>Class: </i><b>UNIX-SOCKET</b></a>
+<p><b>Slots:</b><ul><li>FAMILY : </li>
+</ul><hr> <a name="name-service"><h2>Name Service</h2></a>
+<P>
+<p>Presently name service is implemented by calling whatever
+gethostbyname(2) uses.  This may be any or all of /etc/hosts, NIS, DNS,
+or something completely different.  Typically it's controlled by
+/etc/nsswitch.conf
+<P>
+<p> Direct links to the asynchronous resolver(3) routines would be nice to have
+eventually, so that we can do DNS lookups in parallel with other things
+<p><a name="HOST-ENT"><i>Class: </i><b>HOST-ENT</b></a>
+<p><b>Slots:</b><ul><li>NAME : </li>
+<li>ALIASES : </li>
+<li>ADDRESS-TYPE : </li>
+<li>ADDRESSES : </li>
+</ul><p><a name="HOST-ENT-ADDRESS"><table width="100%"><tr><td width="80%">(host-ent-address <i> (host-ent <a href="#host-ent">host-ent</a>)</i>)</td><td align=right>Method</td></tr></table>
+<p><a name="GET-HOST-BY-NAME"><table width="100%"><tr><td width="80%">(get-host-by-name <i> host-name</i>)</td><td align=right>Function</td></tr></table>
+<blockquote>Returns a <a href="#HOST-ENT">HOST-ENT</a> instance for HOST-NAME or throws some kind of condition.
+HOST-NAME may also be an IP address in dotted quad notation or some other
+weird stuff - see gethostbyname(3) for grisly details.</blockquote>
+<p><a name="GET-HOST-BY-ADDRESS"><table width="100%"><tr><td width="80%">(get-host-by-address <i> address</i>)</td><td align=right>Function</td></tr></table>
+<blockquote>Returns a <a href="#HOST-ENT">HOST-ENT</a> instance for ADDRESS, which should be a vector of
+(integer 0 255), or throws some kind of error.  See gethostbyaddr(3) for
+grisly details.</blockquote>
+<p><a name="NAME-SERVICE-ERROR"><table width="100%"><tr><td width="80%">(name-service-error <i> where</i>)</td><td align=right>Function</td></tr></table>
+<hr><p><a name="NON-BLOCKING-MODE"><table width="100%"><tr><td width="80%">(non-blocking-mode <i> (socket <a href="#socket">socket</a>)</i>)</td><td align=right>Method</td></tr></table>
+<blockquote>Is <a href="#SOCKET">SOCKET</a> in non-blocking mode?</blockquote>
+<hr>
+<P>
+<H1>Tests</h1>
+<P>
+There should be at least one test for pretty much everything you can do
+with the package.  In some places I've been more diligent than others; more
+tests gratefully accepted.
+<P>
+Tests are in the file <tt>tests.lisp</tt> and also make good examples.
+<P>
+|
+<h2>Unix-domain sockets</h2>
+<P>
+A fairly rudimentary test that connects to the syslog socket and sends a 
+message.  Priority 7 is kern.debug; you'll probably want to look at
+/etc/syslog.conf or local equivalent to find out where the message ended up
+|
\ No newline at end of file
diff --git a/contrib/sb-bsd-sockets/array-data.lisp b/contrib/sb-bsd-sockets/array-data.lisp
new file mode 100644 (file)
index 0000000..8a53daa
--- /dev/null
@@ -0,0 +1,72 @@
+(in-package :sockint)
+
+;;; borrowed from CMUCL manual, lightly ported
+
+(defun array-data-address (array)
+  "Return the physical address of where the actual data of an array is
+stored.
+
+ARRAY must be a specialized array type - an array of one of these types:
+
+                  double-float
+                  single-float
+                  (unsigned-byte 32)
+                  (unsigned-byte 16)
+                  (unsigned-byte  8)
+                  (signed-byte 32)
+                  (signed-byte 16)
+                  (signed-byte  8)
+"
+  (declare (type (or (array (signed-byte 8))
+                    (array base-char)
+                    simple-base-string
+                     (array (signed-byte 16))
+                     (array (signed-byte 32))
+                     (array (unsigned-byte 8))
+                     (array (unsigned-byte 16))
+                     (array (unsigned-byte 32))
+                     (array single-float)
+                     (array double-float))
+                 array)
+           (optimize (speed 0) (debug 3) (safety 3)))
+  ;; with-array-data will get us to the actual data.  However, because
+  ;; the array could have been displaced, we need to know where the
+  ;; data starts.
+
+  (let* ((type (car (multiple-value-list (array-element-type array))))
+        (type-size
+         (cond ((or (equal type '(signed-byte 8))
+                    (equal type 'cl::base-char)
+                    (equal type '(unsigned-byte 8)))
+                1)
+               ((or (equal type '(signed-byte 16))
+                    (equal type '(unsigned-byte 16)))
+                2)
+               ((or (equal type '(signed-byte 32))
+                    (equal type '(unsigned-byte 32)))
+                4)
+               ((equal type 'single-float)
+                4)
+               ((equal type 'double-float)
+                8)
+               (t (error "Unknown specialized array element type")))))
+    (with-array-data ((data array)
+                     (start)
+                     (end))
+      (declare (ignore end))
+      ;; DATA is a specialized simple-array.  Memory is laid out like this:
+      ;;
+      ;;   byte offset    Value
+      ;;        0         type code (e.g. 70 for double-float vector)
+      ;;        4         FIXNUMIZE(number of elements in vector)
+      ;;        8         1st element of vector
+      ;;      ...         ...
+      ;;
+      (let* ((addr (+ 8 (logandc1 7 (sb-kernel:get-lisp-obj-address data)))))
+       (declare (type (unsigned-byte 32) addr)
+                (optimize (speed 3) (safety 0)))
+       (sb-sys:int-sap (the (unsigned-byte 32)
+                         (+ addr (* type-size start))))))))
+
+
+
diff --git a/contrib/sb-bsd-sockets/constants.lisp b/contrib/sb-bsd-sockets/constants.lisp
new file mode 100644 (file)
index 0000000..e792888
--- /dev/null
@@ -0,0 +1,189 @@
+;;; -*- Lisp -*-
+
+;;; This isn't really lisp, but it's definitely a source file.  we
+;;; name it thus to avoid having to mess with the clc lpn translations
+
+;;; first, the headers necessary to find definitions of everything
+("sys/types.h" "sys/socket.h" "sys/stat.h" "unistd.h" "sys/un.h"
+ "netinet/in.h" "netinet/in_systm.h" "netinet/ip.h" "net/if.h"
+ "netdb.h" "errno.h" "netinet/tcp.h" "fcntl.h" )
+
+;;; then the stuff we're looking for
+((:integer af-inet "AF_INET" "IP Protocol family")
+ (:integer af-unspec "AF_UNSPEC" "Unspecified.")
+#-solaris (:integer af-local "AF_LOCAL" "Local to host (pipes and file-domain).")
+ (:integer af-unix "AF_UNIX" "Old BSD name for af-local. ")
+#-(or solaris freebsd) (:integer af-file "AF_FILE" "POSIX name for af-local. ")
+#+linux (:integer af-inet6 "AF_INET6"   "IP version 6. ")
+#+linux (:integer af-route "AF_NETLINK" "Alias to emulate 4.4BSD ")
+
+ (:integer sock-stream "SOCK_STREAM"
+           "Sequenced, reliable, connection-based byte streams.")
+ (:integer sock-dgram "SOCK_DGRAM"
+           "Connectionless, unreliable datagrams of fixed maximum length.")
+ (:integer sock-raw "SOCK_RAW"
+           "Raw protocol interface.")
+ (:integer sock-rdm "SOCK_RDM"
+           "Reliably-delivered messages.")
+ (:integer sock-seqpacket "SOCK_SEQPACKET"
+           "Sequenced, reliable, connection-based, datagrams of fixed maximum length.")
+
+ (:integer sol-socket "SOL_SOCKET")
+
+ ;; some of these may be linux-specific
+ (:integer so-debug "SO_DEBUG"
+   "Enable debugging in underlying protocol modules")
+ (:integer so-reuseaddr "SO_REUSEADDR" "Enable local address reuse")
+ (:integer so-type "SO_TYPE")                  ;get only
+ (:integer so-error "SO_ERROR")                 ;get only (also clears)
+ (:integer so-dontroute "SO_DONTROUTE"
+           "Bypass routing facilities: instead send direct to appropriate network interface for the network portion of the destination address")
+ (:integer so-broadcast "SO_BROADCAST" "Request permission to send broadcast datagrams")
+ (:integer so-sndbuf "SO_SNDBUF")
+#+linux (:integer so-passcred "SO_PASSCRED")
+ (:integer so-rcvbuf "SO_RCVBUF")
+ (:integer so-keepalive "SO_KEEPALIVE"
+           "Send periodic keepalives: if peer does not respond, we get SIGPIPE")
+ (:integer so-oobinline "SO_OOBINLINE"
+           "Put out-of-band data into the normal input queue when received")
+ (:integer so-no-check 11)            
+#+linux (:integer so-priority "SO_PRIORITY")            
+ (:integer so-linger "SO_LINGER"
+           "For reliable streams, pause a while on closing when unsent messages are queued")
+#+linux (:integer so-bsdcompat "SO_BSDCOMPAT")
+ (:integer so-sndlowat "SO_SNDLOWAT")
+ (:integer so-rcvlowat "SO_RCVLOWAT")
+ (:integer so-sndtimeo "SO_SNDTIMEO")
+ (:integer so-rcvtimeo "SO_RCVTIMEO")
+
+ (:integer tcp-nodelay "TCP_NODELAY")
+ #+linux (:integer so-bindtodevice "SO_BINDTODEVICE")
+ (:integer ifnamsiz "IFNAMSIZ")
+ (:integer EADDRINUSE "EADDRINUSE")
+ (:integer EAGAIN "EAGAIN")
+ (:integer EBADF "EBADF")
+ (:integer ECONNREFUSED "ECONNREFUSED")
+ (:integer EINTR "EINTR")
+ (:integer EINVAL "EINVAL")
+ (:integer ENOBUFS "ENOBUFS")
+ (:integer ENOMEM "ENOMEM")
+ (:integer EOPNOTSUPP "EOPNOTSUPP")
+ (:integer EPERM "EPERM")
+ (:integer EPROTONOSUPPORT "EPROTONOSUPPORT")
+ (:integer ESOCKTNOSUPPORT "ESOCKTNOSUPPORT")
+ (:integer ENETUNREACH "ENETUNREACH")
+
+ (:integer NETDB-INTERNAL "NETDB_INTERNAL" "See errno.")
+ (:integer NETDB-SUCCESS "NETDB_SUCCESS" "No problem.")
+ (:integer HOST-NOT-FOUND "HOST_NOT_FOUND" "Authoritative Answer Host not found.")
+ (:integer TRY-AGAIN "TRY_AGAIN" "Non-Authoritative Host not found, or SERVERFAIL.")
+ (:integer NO-RECOVERY "NO_RECOVERY" "Non recoverable errors, FORMERR, REFUSED, NOTIMP.")
+ (:integer NO-DATA "NO_DATA" "Valid name, no data record of requested type.")
+ (:integer NO-ADDRESS "NO_ADDRESS" "No address, look for MX record.")
+
+ (:integer O-NONBLOCK "O_NONBLOCK")
+ (:integer f-getfl "F_GETFL")
+ (:integer f-setfl "F_SETFL")
+
+ #+linux (:integer msg-nosignal "MSG_NOSIGNAL")
+ (:integer msg-oob "MSG_OOB")
+ (:integer msg-peek "MSG_PEEK")
+ (:integer msg-trunc "MSG_TRUNC")
+ (:integer msg-waitall "MSG_WAITALL")
+
+ #|
+ ;;; stat is nothing to do with sockets, but I keep it around for testing
+ ;;; the ffi glue
+ (:structure stat ("struct stat"
+                   (t dev "dev_t" "st_dev")
+                   ((alien:integer 32) atime "time_t" "st_atime")))
+ (:function stat ("stat" (integer 32)
+                  (file-name (* t))
+ (buf (* t))))
+ |#
+ (:structure protoent ("struct protoent"
+                       ((* t) name "char *" "p_name")
+                       ((* (* t)) aliases "char **" "p_aliases")
+                      (integer proto "int" "p_proto")))
+ (:function getprotobyname ("getprotobyname" (* t)
+                                            (name c-string)))
+ (:integer inaddr-any "INADDR_ANY")
+ (:structure in-addr ("struct in_addr"
+                     ((array (unsigned 8) 4) addr "u_int32_t" "s_addr")))
+ (:structure sockaddr-in ("struct sockaddr_in"
+                          (integer family "sa_family_t" "sin_family")
+                          ((array (unsigned 8) 2) port "u_int16_t" "sin_port")
+                          ((array (unsigned 8) 4) addr "struct in_addr" "sin_addr")))
+ (:structure sockaddr-un ("struct sockaddr_un"
+                          (integer family "sa_family_t" "sun_family")
+                          ((array (unsigned 8) 108) path "char" "sun_path")))
+ (:structure hostent ("struct hostent"
+                      ((* t) name "char *" "h_name")
+                      ((* c-string) aliases "char **" "h_aliases")
+                      (integer type "int" "h_addrtype")
+                      (integer length "int" "h_length")
+                      ((* (* (unsigned 8))) addresses "char **" "h_addr_list")))
+ (:function socket ("socket" integer
+                    (domain integer)
+                    (type integer)
+                    (protocol integer)))
+ (:function bind ("bind" integer
+                  (sockfd integer)
+                  (my-addr (* t))
+                  (addrlen integer)))
+ (:function listen ("listen" integer
+                    (socket integer)
+                    (backlog integer)))
+ (:function accept ("accept" integer
+                    (socket integer)
+                    (my-addr (* t))
+                    (addrlen integer :in-out)))
+ (:function getpeername ("getpeername" integer
+                         (socket integer)
+                         (her-addr (* t))
+                         (addrlen integer :in-out)))
+ (:function getsockname ("getsockname" integer
+                         (socket integer)
+                         (my-addr (* t))
+                         (addrlen integer :in-out)))
+ (:function connect ("connect" integer
+                    (socket integer)
+                    (his-addr (* t))
+                    (addrlen integer )))
+ (:function close ("close" integer
+                   (fd integer)))
+ (:function recvfrom ("recvfrom" integer
+                                (socket integer)
+                                (buf (* t))
+                                (len integer)
+                                (flags integer)
+                                (sockaddr (* t))
+                                (socklen (* integer))))
+ (:function gethostbyname ("gethostbyname" (* t ) (name c-string)))
+ (:function gethostbyaddr ("gethostbyaddr" (* t )
+                                          (addr (* t))
+                                          (len integer)
+                                          (af integer)))
+ (:structure hostent ("struct hostent"
+                      ((* t) name "char *" "h_name")
+                      (integer length "int" "h_length")))
+
+ (:function setsockopt ("setsockopt" integer
+                        (socket integer)
+                        (level integer)
+                        (optname integer)
+                        (optval (* t))
+                        (optlen integer)))
+ (:function fcntl ("fcntl" integer
+                   (fd integer)
+                   (cmd integer)
+                   (arg integer)))
+ (:function getsockopt ("getsockopt" integer
+                        (socket integer)
+                        (level integer)
+                        (optname integer)
+                        (optval (* t))
+                        (optlen integer :in-out))))
+)
diff --git a/contrib/sb-bsd-sockets/def-to-lisp.lisp b/contrib/sb-bsd-sockets/def-to-lisp.lisp
new file mode 100644 (file)
index 0000000..7940126
--- /dev/null
@@ -0,0 +1,70 @@
+(in-package :SB-BSD-SOCKETS-SYSTEM)
+(defvar *export-symbols* nil)
+
+(defun c-for-structure (stream lisp-name c-struct)
+  (destructuring-bind (c-name &rest elements) c-struct
+    (format stream "printf(\"(define-c-struct ~A %d)\\n\",sizeof (~A));~%" lisp-name c-name)
+    (dolist (e elements)
+      (destructuring-bind (lisp-type lisp-el-name c-type c-el-name) e
+        (format stream "printf(\"(define-c-accessor ~A-~A ~A ~A \");~%"
+                lisp-name lisp-el-name lisp-name lisp-type)
+        ;; offset
+        (format stream "{ ~A t;printf(\"%d \",((unsigned long)&(t.~A)) - ((unsigned long)&(t)) ); }~%"
+                c-name c-el-name)
+        ;; length
+        (format stream "{ ~A t;printf(\"%d\",(sizeof t.~A));}~%"
+                c-name c-el-name)
+        (format stream "printf(\")\\n\");~%")))))
+
+(defun c-for-function (stream lisp-name alien-defn)
+  (destructuring-bind (c-name &rest definition) alien-defn
+    (let ((*print-right-margin* nil))
+      (format stream "printf(\"(declaim (inline ~A))\\n\");~%"
+              lisp-name)
+      (princ "printf(\"(def-foreign-routine (" stream)
+      (princ "\\\"" stream) (princ c-name stream) (princ "\\\" " stream)
+      (princ lisp-name stream)
+      (princ " ) " stream)
+      (dolist (d definition)
+        (write d :length nil
+               :right-margin nil :stream stream)
+        (princ " " stream))
+      (format stream ")\\n\");")
+      (terpri stream))))
+
+
+(defun print-c-source (stream headers definitions package-name)
+  ;(format stream "#include \"struct.h\"~%")
+  (let ((*print-right-margin* nil))
+    (loop for i in headers
+          do (format stream "#include <~A>~%" i))
+    (format stream "main() { ~%
+printf(\"(in-package ~S)\\\n\");~%" package-name)  
+    (format stream "printf(\"(defconstant size-of-int %d)\\\n\",sizeof (int));~%")
+    (format stream "printf(\"(defconstant size-of-char %d)\\\n\",sizeof (char));~%")
+    (format stream "printf(\"(defconstant size-of-long %d)\\\n\",sizeof (long));~%")
+    (dolist (def definitions)
+      (destructuring-bind (type lispname cname &optional doc) def
+        (cond ((eq type :integer)
+               (format stream
+                       "printf(\"(defconstant ~A %d \\\"~A\\\")\\\n\",~A);~%"
+                       lispname doc cname))
+              ((eq type :string)
+               (format stream
+                       "printf(\"(defvar ~A %S \\\"~A\\\")\\\n\",~A);~%"
+                     lispname doc cname))
+              ((eq type :function)
+               (c-for-function stream lispname cname))
+              ((eq type :structure)
+               (c-for-structure stream lispname cname))
+              (t
+               (format stream
+                       "printf(\";; Non hablo Espagnol, Monsieur~%")))))
+    (format stream "exit(0);~%}")))
+
+(defun c-constants-extract  (filename output-file package)
+  (with-open-file (f output-file :direction :output)
+    (with-open-file (i filename :direction :input)
+      (let* ((headers (read i))
+             (definitions (read i)))
+        (print-c-source  f headers definitions package)))))
diff --git a/contrib/sb-bsd-sockets/defpackage.lisp b/contrib/sb-bsd-sockets/defpackage.lisp
new file mode 100644 (file)
index 0000000..58e5270
--- /dev/null
@@ -0,0 +1,123 @@
+(defpackage "SB-BSD-SOCKETS-INTERNAL"
+  (:nicknames "SOCKINT")
+  (:shadow close listen)
+  #+cmu (:shadowing-import-from "CL" with-array-data)
+  #+sbcl (:shadowing-import-from "SB-KERNEL" with-array-data)
+
+  #+cmu (:use "COMMON-LISP" "ALIEN" "SYSTEM" "EXT" "C-CALL")
+  #+sbcl (:use "COMMON-LISP" "SB-ALIEN" #+nil "SB-SYSTEM" "SB-EXT" "SB-C-CALL"))
+
+;;; SBCL changes a lot of package prefixes.  To avoid littering the
+;;; code with conditionals, we use the SBCL package prefixes
+;;; throughout.  This means that we need to create said packages
+;;; first, if we're using CMUCL
+
+;;; One thing that this exercise really has made clear is just how much
+;;; of the alien stuff is scattered around the cmucl package space
+;;; seemingly at random.  Hmm.
+
+#+cmu
+(eval-when (:compile-toplevel :load-toplevel)
+  (defun add-package-nickname (name nickname)
+    (let ((p (find-package name)))
+      (rename-package p (package-name p)
+                      (cons nickname (package-nicknames name)))))
+  (add-package-nickname "EXT" "SB-EXT")
+  (add-package-nickname "ALIEN" "SB-ALIEN")
+  (add-package-nickname "UNIX" "SB-UNIX")
+  (add-package-nickname "C-CALL" "SB-C-CALL")
+  (add-package-nickname "KERNEL" "SB-KERNEL")
+  (add-package-nickname "SYSTEM" "SB-SYS"))
+
+(defpackage "SB-BSD-SOCKETS"
+  (:export socket unix-socket inet-socket
+           make-unix-socket make-inet-socket
+           socket-bind socket-accept socket-connect
+           socket-send socket-receive socket-recv
+           socket-name socket-peername socket-listen
+           socket-close socket-file-descriptor socket-make-stream
+           get-protocol-by-name
+
+           get-host-by-name get-host-by-address
+           host-ent
+           host-ent-addresses host-ent-address
+           host-ent aliases host-ent-name
+           name-service-error
+           ;; not sure if these are really good names or not
+           netdb-internal-error
+           netdb-success-error
+           host-not-found-error
+           try-again-error
+           no-recovery-error
+           
+          ;; all socket options are also exported, by code in
+          ;; sockopt.lisp
+
+           bad-file-descriptor-error
+           address-in-use-error
+           interrupted-error
+           invalid-argument-error
+           out-of-memory-error
+           operation-not-supported-error
+           operation-not-permitted-error
+           protocol-not-supported-error
+          socket-type-not-supported-error
+           network-unreachable-error
+           
+           make-inet-address
+
+           non-blocking-mode
+           )
+  (:use "COMMON-LISP" "SB-BSD-SOCKETS-INTERNAL")
+  (:documentation
+   "
+
+A thinly-disguised BSD socket API for SBCL.  Ideas stolen from the BSD
+socket API for C and Graham Barr's IO::Socket classes for Perl.
+
+We represent sockets as CLOS objects, and rename a lot of methods and
+arguments to fit Lisp style more closely.
+
+"
+   ))
+
+#||
+
+<h2>Contents</h2>
+
+<ol>
+<li> General concepts
+<li> Methods applicable to all <a href="#socket">sockets</a>
+<li> <a href="#sockopt">Socket Options</a>
+<li> Methods applicable to a particular subclass
+<ol>
+<li> <a href="#internet">INET-SOCKET</a> - Internet Protocol (TCP, UDP, raw) sockets
+<li> Methods on <a href="#UNIX-SOCKET">UNIX-SOCKET</a> - Unix-domain sockets 
+</ol>
+<li> <a href="#name-service">Name resolution</a> (DNS, /etc/hosts, &amp;c)
+</ol>
+
+<h2>General concepts</h2>
+
+<p>Most of the functions are modelled on the BSD socket API.  BSD sockets
+are widely supported, portably <i>("portable" by Unix standards, at least)</i>
+available on a variety of systems, and documented.  There are some
+differences in approach where we have taken advantage of some of the more useful features of Common Lisp - briefly
+
+<ul>
+<li> Where the C API would typically return -1 and set errno, bsd-sockets
+signals an error.  All the errors are subclasses of SOCKET-CONDITION
+and generally correspond one for one with possible <tt>errno</tt> values
+
+<li> We use multiple return values in many places where the C API would use p[ass-by-reference values
+
+<li> We can often avoid supplying an explicit <i>length</i> argument to
+functions because we already know how long the argument is.
+
+<li> IP addresses and ports are represented in slightly friendlier fashion
+than "network-endian integers".  See the section on <a href="#internet"
+>Internet domain</a> sockets for details.
+</ul>
+
+
+|#
diff --git a/contrib/sb-bsd-sockets/doc.lisp b/contrib/sb-bsd-sockets/doc.lisp
new file mode 100644 (file)
index 0000000..3c85f3e
--- /dev/null
@@ -0,0 +1,225 @@
+(eval-when (:load-toplevel :compile-toplevel :execute)
+  (defpackage :db-doc (:use  :cl :asdf #+sbcl :sb-ext #+cmu :ext )))
+(in-package :db-doc)
+;;; turn water into wine ^W^W^W lisp into HTML
+
+#|
+OK.  We need a design
+
+1) The aim is to document the current package, given a system.
+2) The assumption is that the system is loaded; this makes it easier to
+do cross-references and stuff
+3) We output HTML on *standard-output*
+4) Hyperlink wherever useful
+5) We're allowed to intern symbols all over the place if we like
+
+|#
+
+;;; note: break badly on multiple packages
+
+
+(defvar *symbols* nil
+  "List of external symbols to print; derived from parsing DEFPACKAGE form")
+
+
+(defun worth-documenting-p (symbol)
+  (and symbol
+       (eql (symbol-package symbol) *package*)
+       (or (ignore-errors (find-class symbol))
+          (boundp symbol) (fboundp symbol))))
+
+(defun linkable-symbol-p (word)
+  (labels ((symbol-char (c) (or (upper-case-p c) (digit-char-p c)
+                               (eql c #\-))))
+    (and (every  #'symbol-char word)
+        (some #'upper-case-p word)
+        (worth-documenting-p (find-symbol word)))))
+
+(defun markup-word (w)
+  (if (symbolp w) (setf w (princ-to-string w)))
+  (cond ((linkable-symbol-p w) 
+        (format nil "<a href=\"#~A\">~A</a>"
+                w  w))
+       ((and (> (length w) 0)
+             (eql (elt w 0) #\_)
+             (eql (elt w (1- (length w))) #\_))
+        (format nil "<b>~A</b>" (subseq w 1 (1- (length w)))))
+       (t w)))
+(defun markup-space (w)
+  (let ((para (search (coerce '(#\Newline #\Newline) 'string) w)))
+    (if para
+       (format nil "~A<P>~A"
+               (subseq w 0 (1+ para))
+               (markup-space (subseq w (1+ para) nil)))
+       w)))
+
+(defun text-markup (text)
+  (let ((start-word 0) (end-word 0))
+    (labels ((read-word ()
+              (setf end-word
+                    (position-if
+                     (lambda (x) (member x '(#\Space #\, #\.  #\Newline)))
+                     text :start start-word))
+              (subseq text start-word end-word))
+            (read-space ()
+              (setf start-word
+                    (position-if-not
+                     (lambda (x) (member x '(#\Space #\, #\.  #\Newline)))
+                     text :start end-word ))
+              (subseq text end-word start-word)))
+      (with-output-to-string (o)
+       (loop for inword = (read-word)
+             do (princ (markup-word inword) o)
+             while (and start-word end-word)
+             do (princ (markup-space (read-space)) o)
+             while (and start-word end-word))))))
+
+
+(defun do-defpackage (form stream)
+  (setf *symbols* nil)
+  (destructuring-bind (defn name &rest options) form
+    (when (string-equal name (package-name *package*))
+      (format stream "<h1>Package ~A</h1>~%" name)
+      (when (documentation *package* t)
+       (princ (text-markup (documentation *package* t))))
+      (let ((exports (assoc :export options)))
+        (when exports
+          (setf *symbols* (mapcar #'symbol-name (cdr exports)))))
+      1)))
+
+(defun do-defclass (form stream)
+  (destructuring-bind (defn name super slots &rest options) form
+    (when (interesting-name-p name)
+      (let ((class  (find-class name)))
+       (format stream "<p><a name=\"~A\"><i>Class: </i><b>~A</b></a>~%"
+               name  name)
+       #+nil (format stream "<p><b>Superclasses: </b> ~{~A ~}~%"
+               (mapcar (lambda (x) (text-markup (class-name x)))
+                       (mop:class-direct-superclasses class)))
+       (if (documentation class 'type)
+           (format stream "<blockquote>~A</blockquote>~%"
+                   (text-markup (documentation class  'type))))
+       (when slots
+         (princ "<p><b>Slots:</b><ul>" stream)
+         (dolist (slot slots)
+           (destructuring-bind
+                 (name &key reader writer accessor initarg initform type
+                       documentation)
+               (if (consp slot) slot (list slot))
+             (format stream "<li>~A : ~A</li>~%" name
+                     (if documentation (text-markup documentation) "")))) 
+         (princ "</ul>" stream))
+       t))))
+       
+
+(defun interesting-name-p (name)
+  (cond ((consp name)
+        (and (eql (car name) 'setf)
+             (interesting-name-p (cadr name))))
+       (t (member (symbol-name name) *symbols* :test #'string=))))
+
+(defun markup-lambdalist (l)
+  (let (key-p)
+    (loop for i in l
+         if (eq '&key i) do (setf key-p t)
+         end
+         if (and (not key-p) (consp i))
+         collect (list (car i) (markup-word (cadr i)))
+         else collect i)))
+
+(defun do-defunlike (form label stream)
+  (destructuring-bind (defn name lambdalist &optional doc &rest code) form
+    (when (interesting-name-p name)
+      (when (symbolp name)
+       (setf *symbols* (remove (symbol-name name) *symbols* :test #'string=)))
+      (format stream "<p><a name=\"~A\"><table width=\"100%\"><tr><td width=\"80%\">(~A <i>~A</i>)</td><td align=right>~A</td></tr></table>~%"
+              name  (string-downcase (princ-to-string name))
+             (string-downcase
+              (format nil "~{ ~A~}" (markup-lambdalist lambdalist)))
+             label)
+      (if (stringp doc)
+          (format stream "<blockquote>~A</blockquote>~%"
+                 (text-markup doc)))
+      t)))
+
+(defun do-defun (form stream) (do-defunlike form "Function" stream))
+(defun do-defmethod (form stream) (do-defunlike form "Method" stream))
+(defun do-defgeneric (form stream) (do-defunlike form "Generic Function" stream))
+(defun do-boolean-sockopt (form stream)
+  (destructuring-bind (type lisp-name level c-name) form
+    (pushnew (symbol-name lisp-name) *symbols*)
+
+    (do-defunlike `(defun  ,lisp-name ((socket socket) argument)
+                   ,(format nil "Return the value of the ~A socket option for SOCKET.  This can also be updated with SETF." (symbol-name c-name) ) 'empty)
+      "Accessor" stream)))
+    
+(defun do-form (form output-stream)
+  (cond ((not (listp form)) nil)
+       ((string= (symbol-name (car form)) "DEFINE-SOCKET-OPTION-BOOL")
+        (do-boolean-sockopt form output-stream))
+       ((eq (car form) 'defclass)
+        (do-defclass form output-stream))
+       ((eq (car form) 'eval-when)
+        (do-form (third form) output-stream))
+       ((eq (car form) 'defpackage)
+        (do-defpackage form output-stream))
+       ((eq (car form) 'defun)
+        (do-defun form output-stream))
+       ((eq (car form) 'defmethod)
+        (do-defmethod form output-stream))
+       ((eq (car form) 'defgeneric)
+        (do-defgeneric form output-stream))
+       (t nil)))
+
+(defun do-file (input-stream output-stream)
+  "Read in a Lisp program on INPUT-STREAM and make semi-pretty HTML on OUTPUT-STREAM"
+  (let ((eof-marker (gensym)))
+    (if (< 0 
+        (loop for form =  (read input-stream nil eof-marker)
+              until (eq form eof-marker)
+              if (do-form form output-stream)
+              count 1 #| and
+              do (princ "<hr width=\"20%\">" output-stream) |# ))
+       (format output-stream "<hr>"
+               ))))
+
+(defvar *standard-sharpsign-reader*
+  (get-dispatch-macro-character #\# #\|))
+
+(defun document-system (system &key
+                               (output-stream *standard-output*)
+                               (package *package*))
+  "Produce HTML documentation for all files defined in SYSTEM, covering
+symbols exported from PACKAGE"
+  (let ((*package* (find-package package))
+       (*readtable* (copy-readtable)) 
+       (*standard-output* output-stream))
+    (set-dispatch-macro-character
+     #\# #\|
+     (lambda (s c n)
+       (if (eql (peek-char nil s t nil t) #\|)
+          (princ
+           (text-markup
+            (coerce 
+             (loop with discard = (read-char s t nil t)
+                   ;initially (princ "<P>")
+                   for c = (read-char s t nil t)
+                   until (and (eql c #\|)
+                              (eql (peek-char nil s t nil t) #\#))
+                   collect c
+                   finally (read-char s t nil t))
+             'string)))
+          (funcall *standard-sharpsign-reader* s c n))))
+    (dolist (c (cclan:all-components 'db-sockets))
+      (when (and (typep c 'cl-source-file)
+                (not (typep c 'db-sockets-system::constants-file)))
+       (with-open-file (in (component-pathname c) :direction :input)
+           (do-file in *standard-output*))))))
+
+(defun start ()
+  (with-open-file (*standard-output* "index.html" :direction :output)
+      (format t "<html><head><title>SBCL BSD-Sockets API Reference</title></head><body>~%")
+    (asdf:operate 'asdf:load-op 'sb-bsd-sockets)
+    (document-system 'sb-bsd-sockets :package :sb-bsd-sockets)))
+
+(start)
diff --git a/contrib/sb-bsd-sockets/foreign-glue.lisp b/contrib/sb-bsd-sockets/foreign-glue.lisp
new file mode 100644 (file)
index 0000000..446b5d2
--- /dev/null
@@ -0,0 +1,88 @@
+(in-package :sb-bsd-sockets-internal)
+
+;;;; Foreign function glue.  This is the only file in the distribution
+;;;; that's _intended_ to be vendor-specific.  The macros defined here
+;;;; are called from constants.lisp, which was generated from constants.ccon
+;;;; by the C compiler as driven by that wacky def-to-lisp thing.
+
+;;;; of course, the whole thing is vendor-specific actually, due to
+;;;; the way we use cmucl alien types in constants.ccon as a cheap way
+;;;; of transforming C-world alues into Lisp-world values.  But if
+;;;; anyone were to port that bit to their preferred implementation, they
+;;;; wouldn't need to port all the rest of the cmucl alien interface at
+;;;; the same time
+
+;;; (def-foreign-routine ("stat" STAT ) (INTEGER 32) (FILE-NAME
+;;; C-CALL:C-STRING) (BUF (* T)) )
+
+;;; I can't help thinking this was originally going to do something a
+;;; lot more complex
+(defmacro def-foreign-routine
+  (&whole it (c-name lisp-name) return-type &rest args)
+  (declare (ignorable c-name lisp-name return-type args))
+  `(def-alien-routine ,@(cdr it)))
+#|
+(define-c-accessor FOO-PORT SOCKADDR-IN (ARRAY (UNSIGNED 8) 2) 2 2)
+(define-c-accessor SOCKADDR-IN-FAMILY SOCKADDR-IN INTEGER 6 2)
+|#
+;;; define-c-accessor makes us a setter and a getter for changing
+;;; memory at the appropriate offset
+
+;;;    (define-c-accessor STAT-ATIME STAT (INTEGER 32) 56 4)
+
+(defmacro define-c-accessor (el structure type offset length)
+  (declare (ignore structure))
+  (let* ((ty (cond
+              ((eql type 'integer) `(,type ,(* 8 length)))
+              ((eql (car type) '*) `(unsigned ,(* 8 length)))
+              ((eql type 'c-string) `(unsigned ,(* 8 length)))
+              ((eql (car type) 'array) (cadr type))))
+        (sap-ref-? (intern (format nil "~ASAP-REF-~A"
+                                   (if (member (car ty) '(INTEGER SIGNED))
+                                       "SIGNED-" "")
+                                   (cadr ty))
+                           (find-package "SB-SYS"))))
+    (labels ((template (before after)
+              `(let* ((addr (+ 8 (logandc1 7 (sb-kernel:get-lisp-obj-address ptr))))
+                      (sap (sb-sys:int-sap (the (unsigned-byte 32) (+ addr ,offset)))))
+                (,before (,sap-ref-? sap index) ,after))))
+      `(progn
+       ;;(declaim (inline ,el (setf ,el)))
+       (defun ,el (ptr &optional (index 0))
+         ,(template 'prog1 nil))
+       (defconstant ,(intern (format nil "OFFSET-OF-~A" el)) ,offset)
+       (defun (setf ,el) (newval ptr &optional (index 0))
+         ,(template 'setf 'newval))))))
+
+
+;;; make memory allocator for appropriately-sized block of memory, and
+;;; a constant to tell us how big it was anyway
+(defmacro define-c-struct (name size)
+  (labels ((p (x) (intern (concatenate 'string x (symbol-name name)))))
+    `(progn
+      (defun ,(p "ALLOCATE-") () (make-array ,size :initial-element 0
+                                            :element-type '(unsigned-byte 8)))
+      (defconstant ,(p "SIZE-OF-") ,size)
+      (defun ,(p "FREE-" ) (p) (declare (ignore p))))))
+
+(defun foreign-nullp (c)
+  "C is a pointer to 0?"
+  (= 0 (sb-sys:sap-int (sb-alien:alien-sap  c))))
+
+;;; this could be a lot faster if I cared enough to think about it
+(defun foreign-vector (pointer size length)
+  "Compose a vector of the words found in foreign memory starting at
+POINTER.  Each word is SIZE bytes long; LENGTH gives the number of
+elements of the returned vector.  See also FOREIGN-VECTOR-UNTIL-ZERO"
+  (assert (= size 1))
+  (let ((ptr
+        (typecase pointer
+          (sb-sys:system-area-pointer
+           (sap-alien pointer (* (sb-alien:unsigned 8))))
+          (t
+           (sb-alien:cast pointer (* (sb-alien:unsigned 8))))))
+       (result (make-array length :element-type '(unsigned-byte 8))))
+    (loop for i from 0 to (1- length) by size
+         do (setf (aref result i) (sb-alien:deref ptr i)))
+     ;;(format t "~S~%" result)
+    result))
diff --git a/contrib/sb-bsd-sockets/inet.lisp b/contrib/sb-bsd-sockets/inet.lisp
new file mode 100644 (file)
index 0000000..eeb2b25
--- /dev/null
@@ -0,0 +1,94 @@
+(in-package :sb-bsd-sockets)
+
+#|| <h2>INET-domain sockets</h2>
+
+<p>The TCP and UDP sockets that you know and love.  Some representation issues:
+<ul>
+<li>These functions do not accept hostnames directly: see <a href="#name-service">name resolution</a>
+<li>Internet <b>addresses</b> are represented by vectors of <tt>(unsigned-byte 8)</tt> - viz. <tt>#(127 0 0 1)</tt>.  <b>Ports</b> are just integers: <tt>6010</tt>.  No conversion between network- and host-order data is needed from the user of this package.
+<li><b><i>socket addresses</i></b> are represented by the two values for <b>address</b> and <b>port</b>, so for example, <tt>(<a href="#SOCKET-CONNECT">socket-connect</a> s #(192.168.1.1) 80)</tt>
+</ul>
+
+|#
+
+;;; Our class and constructor
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (defclass inet-socket (socket)
+    ((family :initform sockint::AF-INET))))
+
+;;; XXX should we *...* this?
+(defparameter inet-address-any (vector 0 0 0 0))
+
+;;; binding a socket to an address and port.  Doubt that anyone's
+;;; actually using this much, to be honest.
+
+(defun make-inet-address (dotted-quads)
+  "Return a vector of octets given a string DOTTED-QUADS in the format
+\"127.0.0.1\""
+  (coerce
+   (mapcar #'parse-integer
+           (split dotted-quads nil '(#\.)))
+   'vector))
+
+;;; getprotobyname only works in the internet domain, which is why this
+;;; is here
+(defun get-protocol-by-name (name)      ;exported
+  "Returns the network protocol number associated with the string NAME,
+using getprotobyname(2) which typically looks in NIS or /etc/protocols"
+  ;; for extra brownie points, could return canonical protocol name
+  ;; and aliases as extra values
+  (let ((ent (sockint::foreign-vector (sockint::getprotobyname name) 1
+                                     sockint::size-of-protoent)))
+    (sockint::protoent-proto ent)))
+
+
+;;; sockaddr protocol
+;;; (1) sockaddrs are represented as the semi-foreign array-of-octets
+;;; thing
+;;; (2) a protocol provides make-sockaddr-for, size-of-sockaddr,
+;;; bits-of-sockaddr
+
+(defmethod make-sockaddr-for ((socket inet-socket) &optional sockaddr &rest address &aux (host (first address)) (port (second address)))
+  (let ((sockaddr (or sockaddr (sockint::allocate-sockaddr-in))))
+    (when (and host port)
+      ;; port and host are represented in C as "network-endian" unsigned
+      ;; integers of various lengths.  This is stupid.  The value of the
+      ;; integer doesn't matter (and will change depending on your
+      ;; machine's endianness); what the bind(2) call is interested in
+      ;; is the pattern of bytes within that integer.
+      
+      ;; We have no truck with such dreadful type punning.  Octets to
+      ;; octets, dust to dust.
+      
+      (setf (sockint::sockaddr-in-family sockaddr) sockint::af-inet)
+      (setf (sockint::sockaddr-in-port sockaddr 0) (ldb (byte 8 8) port))
+      (setf (sockint::sockaddr-in-port sockaddr 1) (ldb (byte 8 0) port))
+      
+      (setf (sockint::sockaddr-in-addr sockaddr 0) (elt host 0))
+      (setf (sockint::sockaddr-in-addr sockaddr 1) (elt host 1))
+      (setf (sockint::sockaddr-in-addr sockaddr 2) (elt host 2))
+      (setf (sockint::sockaddr-in-addr sockaddr 3) (elt host 3)))
+    sockaddr))
+
+(defmethod size-of-sockaddr ((socket inet-socket))
+  sockint::size-of-sockaddr-in)
+
+(defmethod bits-of-sockaddr ((socket inet-socket) sockaddr)
+  "Returns address and port of SOCKADDR as multiple values"
+  (values
+   (vector
+    (sockint::sockaddr-in-addr sockaddr 0) 
+    (sockint::sockaddr-in-addr sockaddr 1) 
+    (sockint::sockaddr-in-addr sockaddr 2) 
+    (sockint::sockaddr-in-addr sockaddr 3))
+   (+ (* 256 (sockint::sockaddr-in-port sockaddr 0))
+      (sockint::sockaddr-in-port sockaddr 1))))  
+   
+
+(defun make-inet-socket (type protocol)
+  "Make an INET socket.  Deprecated in favour of make-instance"
+  (make-instance 'inet-socket :type type :protocol protocol))
+
+
+
diff --git a/contrib/sb-bsd-sockets/malloc.lisp b/contrib/sb-bsd-sockets/malloc.lisp
new file mode 100644 (file)
index 0000000..0b6ca39
--- /dev/null
@@ -0,0 +1,16 @@
+(in-package :sb-bsd-sockets-internal)
+
+(defun malloc (size)
+  "Allocate foreign memory in some way that allows the garbage collector to free it later.  Note that memory allocated this way does not count as `consed' for the purposes of deciding when to gc, so explicitly calling EXT:GC occasionally would be a good idea if you use it a lot"
+  ;; we can attach finalizers to any object, and they'll be called on
+  ;; the next gc after the object no longer has references.  We can't
+  ;; however make the finalizer close over the object, or it'll never
+  ;; have no references.  I experimentally determined that (sap-alien
+  ;; (alien-sap f)) is not EQ to f, so we can do it that way
+  (let* ((memory (make-alien (unsigned 8) size))
+         (alias (sap-alien (alien-sap memory)
+                                 (* (unsigned 8)))))
+    (sb-ext:finalize memory
+                     (lambda ()
+                       (free-alien alias)))))
+
diff --git a/contrib/sb-bsd-sockets/misc.lisp b/contrib/sb-bsd-sockets/misc.lisp
new file mode 100644 (file)
index 0000000..6dd2bfb
--- /dev/null
@@ -0,0 +1,36 @@
+(in-package :sb-bsd-sockets)
+
+;;; Miscellaneous things, placed here until I can find a logically more
+;;; coherent place to put them
+
+;;; I don't want to provide a complete interface to unix file
+;;; operations, for example, but being about to set O_NONBLOCK on a
+;;; socket is a necessary operation.
+
+;;; XXX bad (sizeof (int) ==4 ) assumptions
+
+(defmethod non-blocking-mode ((socket socket))
+  "Is SOCKET in non-blocking mode?"
+  (let ((fd (socket-file-descriptor socket)))
+    (sb-alien:with-alien ((arg integer))
+                         (> (logand
+                             (sockint::fcntl fd sockint::f-getfl arg)
+                             sockint::o-nonblock)
+                            0))))
+
+(defmethod (setf non-blocking-mode) (non-blocking-p (socket socket))
+  "Put SOCKET in non-blocking mode - or not, according to NON-BLOCKING-P"
+  (declare (optimize (speed 3)))
+  (let* ((fd (socket-file-descriptor socket))
+         (arg1 (the (signed-byte 32) (sockint::fcntl fd sockint::f-getfl 0)))
+         (arg2
+          (if non-blocking-p
+              (logior arg1 sockint::o-nonblock)
+            (logand (lognot sockint::o-nonblock) arg1))))
+    (when (= (the (signed-byte 32) -1)
+             (the (signed-byte 32) 
+               (sockint::fcntl fd sockint::f-setfl arg2)))
+      (socket-error "fcntl"))
+    non-blocking-p))
+
+
diff --git a/contrib/sb-bsd-sockets/name-service.lisp b/contrib/sb-bsd-sockets/name-service.lisp
new file mode 100644 (file)
index 0000000..5f03859
--- /dev/null
@@ -0,0 +1,144 @@
+(in-package :sb-bsd-sockets)
+#|| <a name="name-service"><h2>Name Service</h2></a>
+
+<p>Presently name service is implemented by calling whatever
+gethostbyname(2) uses.  This may be any or all of /etc/hosts, NIS, DNS,
+or something completely different.  Typically it's controlled by
+/etc/nsswitch.conf
+
+<p> Direct links to the asynchronous resolver(3) routines would be nice to have
+eventually, so that we can do DNS lookups in parallel with other things
+|#
+
+(defclass host-ent ()
+  ((name :initarg :name :accessor host-ent-name)
+   (aliases :initarg :aliases :accessor host-ent-aliases)
+   (address-type :initarg :type :accessor host-ent-address-type)
+                                       ; presently always AF_INET
+   (addresses :initarg :addresses :accessor host-ent-addresses)))
+
+(defmethod host-ent-address ((host-ent host-ent))
+  (car (host-ent-addresses host-ent)))
+
+;(define-condition host-not-found-error (socket-error)) ; host unknown
+;(define-condition no-address-error (socket-error)) ; valid name but no IP address
+;(define-condition no-recovery-error (socket-error)) ; name server error
+;(define-condition try-again-error (socket-error)) ; temporary
+
+(defun get-host-by-name (host-name)
+  "Returns a HOST-ENT instance for HOST-NAME or throws some kind of condition.
+HOST-NAME may also be an IP address in dotted quad notation or some other
+weird stuff - see gethostbyname(3) for grisly details."
+  (let ((h (sockint::gethostbyname host-name)))
+    (make-host-ent h)))
+
+(defun get-host-by-address (address)
+  "Returns a HOST-ENT instance for ADDRESS, which should be a vector of
+(integer 0 255), or throws some kind of error.  See gethostbyaddr(3) for
+grisly details."
+  (let ((packed-addr (sockint::allocate-in-addr)))
+    (loop for i from 0 to 3 
+         do (setf (sockint::in-addr-addr packed-addr i) (elt address i)))
+    (make-host-ent
+     (sb-sys:without-gcing
+      (sockint::gethostbyaddr (sockint::array-data-address packed-addr)
+                             4
+                             sockint::af-inet)))))
+
+(defun make-host-ent (h)
+  (if (sockint::foreign-nullp h) (name-service-error "gethostbyname"))
+  (let* ((local-h (sockint::foreign-vector h 1 sockint::size-of-hostent))
+        (length (sockint::hostent-length local-h))
+        (aliases 
+         (loop for i = 0 then (1+ i)
+               for al = (sb-sys:sap-ref-sap
+                         (sb-sys:int-sap (sockint::hostent-aliases local-h))
+                         (* i 4))
+               until (= (sb-sys:sap-int al) 0) 
+               collect (sb-c-call::%naturalize-c-string al)))
+        (address0 (sb-sys:sap-ref-sap (sb-sys:int-sap (sockint::hostent-addresses local-h)) 0))
+        (addresses 
+         (loop for i = 0 then (+ length i)
+               for ad = (sb-sys:sap-ref-32 address0 i)
+               while (> ad 0)
+               collect
+               (sockint::foreign-vector (sb-sys:sap+ address0 i) 1 length))))
+    (make-instance 'host-ent
+                   :name (sb-c-call::%naturalize-c-string
+                         (sb-sys:int-sap (sockint::hostent-name local-h)))
+                  :type (sockint::hostent-type local-h)
+                   :aliases aliases
+                   :addresses addresses)))
+
+;;; The remainder is my fault - gw
+
+(defvar *name-service-errno* 0
+  "The value of h_errno, after it's been fetched from Unix-land by calling
+GET-NAME-SERVICE-ERRNO")
+
+(defun name-service-error (where)
+  (get-name-service-errno)
+  ;; Comment next to NETDB_INTERNAL in netdb.h says "See errno.".
+  ;; This special case treatment hasn't actually been tested yet.
+  (if (= *name-service-errno* sockint::NETDB-INTERNAL)
+      (socket-error where)
+    (let ((condition
+          (condition-for-name-service-errno *name-service-errno*)))
+      (error condition :errno *name-service-errno* :syscall where))))
+
+(define-condition name-service-error (condition)
+  ((errno :initform nil
+         :initarg :errno
+         :reader name-service-error-errno)
+   (symbol :initform nil :initarg :symbol :reader name-service-error-symbol)
+   (syscall :initform "an unknown location" :initarg :syscall :reader name-service-error-syscall))
+  (:report (lambda (c s)
+            (let ((num (name-service-error-errno c)))
+              (format s "Name service error in \"~A\": ~A (~A)"
+                      (name-service-error-syscall c)
+                      (or (name-service-error-symbol c)
+                          (name-service-error-errno c))
+                      (get-name-service-error-message num))))))
+
+(defmacro define-name-service-condition (symbol name)
+  `(progn
+     (define-condition ,name (name-service-error)
+       ((symbol :reader name-service-error-symbol :initform (quote ,symbol))))
+     (push (cons ,symbol (quote ,name)) *conditions-for-name-service-errno*)))
+
+(defparameter *conditions-for-name-service-errno* nil)
+
+(define-name-service-condition sockint::NETDB-INTERNAL netdb-internal-error)
+(define-name-service-condition sockint::NETDB-SUCCESS netdb-success-error)
+(define-name-service-condition sockint::HOST-NOT-FOUND host-not-found-error)
+(define-name-service-condition sockint::TRY-AGAIN try-again-error)
+(define-name-service-condition sockint::NO-RECOVERY no-recovery-error)
+;; this is the same as the next one
+;;(define-name-service-condition sockint::NO-DATA no-data-error)
+(define-name-service-condition sockint::NO-ADDRESS no-address-error)
+
+(defun condition-for-name-service-errno (err)
+  (or (cdr (assoc err *conditions-for-name-service-errno* :test #'eql))
+      'name-service))
+
+
+
+(defun get-name-service-errno ()
+  (setf *name-service-errno*
+       (sb-alien:alien-funcall
+        (sb-alien:extern-alien "get_h_errno" (function integer)))))
+
+#-solaris
+(progn
+  #+sbcl
+  (sb-alien:define-alien-routine "hstrerror"
+      sb-c-call:c-string
+    (errno integer))
+  #+cmu
+  (alien:def-alien-routine "hstrerror"
+      sb-c-call:c-string
+    (errno integer))
+  (defun get-name-service-error-message (num)
+  (hstrerror num))
+)
+
diff --git a/contrib/sb-bsd-sockets/rt.lisp b/contrib/sb-bsd-sockets/rt.lisp
new file mode 100644 (file)
index 0000000..ab7a79c
--- /dev/null
@@ -0,0 +1,167 @@
+;-*-syntax:COMMON-LISP;Package:(RT :use "COMMON-LISP" :colon-mode :external)-*-
+
+#|----------------------------------------------------------------------------|
+ | Copyright 1990 by the Massachusetts Institute of Technology, Cambridge MA. |
+ |                                                                            |
+ | Permission  to  use,  copy, modify, and distribute this software  and  its |
+ | documentation for any purpose  and without fee is hereby granted, provided |
+ | that this copyright  and  permission  notice  appear  in  all  copies  and |
+ | supporting  documentation,  and  that  the  name  of M.I.T. not be used in |
+ | advertising or  publicity  pertaining  to  distribution  of  the  software |
+ | without   specific,   written   prior   permission.      M.I.T.  makes  no |
+ | representations  about  the  suitability of this software for any purpose. |
+ | It is provided "as is" without express or implied warranty.                |
+ |                                                                            |
+ |  M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,  INCLUDING  |
+ |  ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL  |
+ |  M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL  DAMAGES  OR  |
+ |  ANY  DAMAGES  WHATSOEVER  RESULTING  FROM  LOSS OF USE, DATA OR PROFITS,  |
+ |  WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER  TORTIOUS  ACTION,  |
+ |  ARISING  OUT  OF  OR  IN  CONNECTION WITH THE USE OR PERFORMANCE OF THIS  |
+ |  SOFTWARE.                                                                 |
+ |----------------------------------------------------------------------------|#
+
+;This is the December 19, 1990 version of the regression tester.
+\f
+(defpackage "RT"
+  (:use "COMMON-LISP")
+  (:export deftest get-test do-test rem-test
+           rem-all-tests do-tests pending-tests
+           continue-testing *test*
+           *do-tests-when-defined*))
+(in-package :rt)
+(defvar *test* nil "Current test name")
+(defvar *do-tests-when-defined* nil)
+(defvar *entries* '(nil) "Test database")
+(defvar *in-test* nil "Used by TEST")
+(defvar *debug* nil "For debugging")
+
+(defstruct (entry (:conc-name nil)
+                 (:type list))
+  pend name form)
+
+(defmacro vals (entry) `(cdddr ,entry))
+
+(defmacro defn (entry) `(cdr ,entry))
+
+(defun pending-tests ()
+  (do ((l (cdr *entries*) (cdr l))
+       (r nil))
+      ((null l) (nreverse r))
+    (when (pend (car l))
+      (push (name (car l)) r))))
+
+(defun rem-all-tests ()
+  (setq *entries* (list nil))
+  nil)
+
+(defun rem-test (&optional (name *test*))
+  (do ((l *entries* (cdr l)))
+      ((null (cdr l)) nil)
+    (when (equal (name (cadr l)) name)
+      (setf (cdr l) (cddr l))
+      (return name))))
+\f
+(defun get-test (&optional (name *test*))
+  (defn (get-entry name)))
+
+(defun get-entry (name)
+  (let ((entry (find name (cdr *entries*)
+                    :key #'name
+                    :test #'equal)))
+    (when (null entry)
+      (report-error t
+        "~%No test with name ~:@(~S~)."
+       name))
+    entry))
+
+(defmacro deftest (name form &rest values)
+  `(add-entry '(t ,name ,form .,values)))
+
+(defun add-entry (entry)
+  (setq entry (copy-list entry))
+  (do ((l *entries* (cdr l))) (nil)
+    (when (null (cdr l))
+      (setf (cdr l) (list entry))
+      (return nil))
+    (when (equal (name (cadr l)) 
+                (name entry))
+      (setf (cadr l) entry)
+      (report-error nil
+        "Redefining test ~@:(~S~)"
+        (name entry))
+      (return nil)))
+  (when *do-tests-when-defined*
+    (do-entry entry))
+  (setq *test* (name entry)))
+
+(defun report-error (error? &rest args)
+  (cond (*debug* 
+        (apply #'format t args)
+        (if error? (throw '*debug* nil)))
+       (error? (apply #'error args))
+       (t (apply #'warn args))))
+\f
+(defun do-test (&optional (name *test*))
+  (do-entry (get-entry name)))
+
+(defun do-entry (entry &optional
+                (s *standard-output*))
+  (catch '*in-test*
+    (setq *test* (name entry))
+    (setf (pend entry) t)
+    (let* ((*in-test* t)
+           (*break-on-warnings* t)
+          (r (multiple-value-list
+               (eval (form entry)))))
+      (setf (pend entry)
+           (not (equal r (vals entry))))
+      (when (pend entry)
+       (format s "~&Test ~:@(~S~) failed~
+                   ~%Form: ~S~
+                   ~%Expected value~P: ~
+                      ~{~S~^~%~17t~}~
+                   ~%Actual value~P: ~
+                      ~{~S~^~%~15t~}.~%"
+               *test* (form entry)
+               (length (vals entry))
+               (vals entry)
+               (length r) r))))
+      (when (not (pend entry)) *test*))
+
+(defun continue-testing ()
+  (if *in-test*
+      (throw '*in-test* nil)
+      (do-entries *standard-output*)))
+\f
+(defun do-tests (&optional
+                (out *standard-output*))
+  (dolist (entry (cdr *entries*))
+    (setf (pend entry) t))
+  (if (streamp out)
+      (do-entries out)
+      (with-open-file 
+         (stream out :direction :output)
+       (do-entries stream))))
+
+(defun do-entries (s)
+  (format s "~&Doing ~A pending test~:P ~
+             of ~A tests total.~%"
+          (count t (cdr *entries*)
+                :key #'pend)
+         (length (cdr *entries*)))
+  (dolist (entry (cdr *entries*))
+    (when (pend entry)
+      (format s "~@[~<~%~:; ~:@(~S~)~>~]"
+             (do-entry entry s))))
+  (let ((pending (pending-tests)))
+    (if (null pending)
+       (format s "~&No tests failed.")
+       (format s "~&~A out of ~A ~
+                   total tests failed: ~
+                   ~:@(~{~<~%   ~1:;~S~>~
+                         ~^, ~}~)."
+               (length pending)
+               (length (cdr *entries*))
+               pending))
+    (null pending)))
diff --git a/contrib/sb-bsd-sockets/sb-bsd-sockets.asd b/contrib/sb-bsd-sockets/sb-bsd-sockets.asd
new file mode 100644 (file)
index 0000000..e259756
--- /dev/null
@@ -0,0 +1,127 @@
+;;; -*-  Lisp -*-
+
+(defpackage #:sb-bsd-sockets-system (:use #:asdf #:cl))
+(in-package #:sb-bsd-sockets-system)
+
+;;; constants.lisp requires special treatment
+
+(defclass constants-file (cl-source-file) ())
+
+(defmethod perform ((op compile-op) (component constants-file))
+  ;; we want to generate all our temporary files in the fasl directory
+  ;; because that's where we have write permission.  Can't use /tmp;
+  ;; it's insecure (these files will later be owned by root)
+  (let* ((output-file (car (output-files op component)))
+        (filename (component-pathname component))
+        (real-output-file
+         (if (typep output-file 'logical-pathname)
+             (translate-logical-pathname output-file)
+             (pathname output-file)))
+        (tmp-c-source (merge-pathnames #p"foo.c" real-output-file))
+        (tmp-a-dot-out (merge-pathnames #p"a.out" real-output-file))
+        (tmp-constants (merge-pathnames #p"constants.lisp-temp"
+                                        real-output-file)))
+    (princ (list filename output-file real-output-file
+                tmp-c-source tmp-a-dot-out tmp-constants))
+    (terpri)
+    (funcall (intern "C-CONSTANTS-EXTRACT" (find-package "BSD-SOCKETS-SYSTEM"))
+            filename tmp-c-source :bsd-sockets-internal)
+    (and
+     (= (run-shell-command
+        "/usr/bin/gcc -o ~S ~S" (namestring tmp-a-dot-out)
+        (namestring tmp-c-source)) 0)
+     (= (run-shell-command "~A >~A"
+                          (namestring tmp-a-dot-out)
+                          (namestring tmp-constants)) 0)
+     (compile-file tmp-constants :output-file output-file))))
+
+
+;;; we also have a shared library with some .o files in it
+
+(defclass unix-dso (module) ())
+(defun unix-name (pathname)
+  (namestring 
+   (typecase pathname
+     (logical-pathname (translate-logical-pathname pathname))
+     (t pathname))))
+
+(defmethod asdf::input-files ((operation compile-op) (dso unix-dso))
+  (mapcar #'component-pathname (module-components dso)))
+
+(defmethod output-files ((operation compile-op) (dso unix-dso))
+  (let ((dir (component-pathname dso)))
+    (list
+     (make-pathname :type "so"
+                   :name (car (last (pathname-directory dir)))
+                   :directory (butlast (pathname-directory dir))
+                   :defaults dir))))
+
+
+(defmethod perform :after ((operation compile-op) (dso unix-dso))
+  (let ((dso-name (unix-name (car (output-files operation dso)))))
+    (unless (zerop
+            (run-shell-command
+             "gcc -shared -o ~S ~{~S ~}"
+             dso-name
+             (mapcar #'unix-name
+                     (mapcan (lambda (c)
+                               (output-files operation c))
+                             (module-components dso)))))
+      (error 'operation-error :operation operation :component dso))))
+
+;;; if this goes into the standard asdf, it could reasonably be extended
+;;; to allow cflags to be set somehow
+(defmethod output-files ((op compile-op) (c c-source-file))
+  (list 
+   (make-pathname :type "o" :defaults
+                 (component-pathname c))))
+(defmethod perform ((op compile-op) (c c-source-file))
+  (unless
+      (= 0 (run-shell-command "/usr/bin/gcc -fPIC -o ~S -c ~S"
+                             (unix-name (car (output-files op c)))
+                             (unix-name (component-pathname c))))
+    (error 'operation-error :operation op :component c)))
+
+(defmethod perform ((operation load-op) (c c-source-file))
+  t)
+  
+(defmethod perform ((o load-op) (c unix-dso))
+  (let ((co (make-instance 'compile-op)))
+    (let ((filename (car (output-files co c))))
+      #+cmu (ext:load-foreign filename)
+      #+sbcl (sb-alien:load-1-foreign filename))))
+
+(defsystem bsd-sockets
+    :version "0.58"
+    :components ((:file "defpackage" :depends-on ("rt"))
+                (:file "split" :depends-on ("defpackage"))
+                 (:file "array-data" :depends-on ("defpackage"))
+                (:unix-dso "alien"
+                           :components ((:c-source-file "undefs")
+                                        (:c-source-file "get-h-errno")))
+                (:file "malloc" :depends-on ("defpackage"))
+                (:file "foreign-glue" :depends-on ("defpackage" "malloc"))
+                (:constants-file "constants"
+                                 :pathname "constants.lisp"
+                                 :depends-on
+                                 ("def-to-lisp" "defpackage" "foreign-glue"))
+                (:file "sockets"
+                       :depends-on ("constants" "array-data"))
+                
+                (:file "sockopt" :depends-on ("sockets"))
+                (:file "inet" :depends-on ("sockets" "split"  "constants" ))
+                (:file "unix" :depends-on ("sockets" "split" "constants" ))
+                (:file "name-service" :depends-on ("sockets" "constants" "alien"))
+                (:file "misc" :depends-on ("sockets" "constants"))
+
+                (:file "rt")
+                (:file "def-to-lisp")
+                (:file "tests" :depends-on ("inet" "sockopt" "rt"))
+
+                (:static-file "NEWS")
+                (:static-file "INSTALL")
+                (:static-file "README")
+                (:static-file "index" :pathname "index.html")
+                (:static-file "doc" :pathname "doc.lisp")
+                (:static-file "TODO")))
+
diff --git a/contrib/sb-bsd-sockets/sockets.lisp b/contrib/sb-bsd-sockets/sockets.lisp
new file mode 100644 (file)
index 0000000..69834dc
--- /dev/null
@@ -0,0 +1,279 @@
+(in-package "SB-BSD-SOCKETS")
+
+;;;; Methods, classes, functions for sockets.  Protocol-specific stuff
+;;;; is deferred to inet.lisp, unix.lisp, etc
+
+#|| <h2>SOCKETs</h2>
+
+|#
+
+(eval-when (:load-toplevel :compile-toplevel :execute)
+(defclass socket ()
+  ((file-descriptor :initarg :descriptor
+                   :reader socket-file-descriptor)
+   (family :initform (error "No socket family") :reader socket-family)
+   (protocol :initarg :protocol :reader socket-protocol)
+   (type  :initarg :type :reader socket-type)
+   (stream))))
+  
+(defmethod print-object ((object socket) stream)
+  (print-unreadable-object (object stream :type t :identity t)
+                           (princ "descriptor " stream)
+                           (princ (slot-value object 'file-descriptor) stream)))
+
+
+(defmethod shared-initialize :after ((socket socket) slot-names
+                                    &key protocol type
+                                    &allow-other-keys)
+  (let* ((proto-num
+         (cond ((and protocol (keywordp protocol))
+                (get-protocol-by-name (string-downcase (symbol-name protocol))))
+               (protocol protocol)
+               (t 0)))
+        (fd (or (and (slot-boundp socket 'file-descriptor)
+                     (socket-file-descriptor socket))
+                (sockint::socket (socket-family socket)
+                                 (ecase type
+                                   ((:datagram) sockint::sock-dgram)
+                                   ((:stream) sockint::sock-stream))
+                                 proto-num))))
+      (if (= fd -1) (socket-error "socket"))
+      (setf (slot-value socket 'file-descriptor) fd
+           (slot-value socket 'protocol) proto-num
+           (slot-value socket 'type) type)
+      (sb-ext:finalize socket (lambda () (sockint::close fd)))))
+
+\f
+
+;; we deliberately redesign the "bind" interface: instead of passing a
+;; sockaddr_something as second arg, we pass the elements of one as
+;; multiple arguments.
+
+(defgeneric socket-bind (socket &rest address))
+(defmethod socket-bind ((socket socket)
+                        &rest address)
+  "Bind SOCKET to ADDRESS, which may vary according to socket family.  For
+the INET family, pass ADDRESS and PORT as two arguments; for FILE address
+family sockets, pass the filename string.  See also bind(2)"
+  (let ((sockaddr (apply #'make-sockaddr-for socket nil address)))
+    (if (= (sb-sys:without-gcing
+           (sockint::bind (socket-file-descriptor socket)
+                          (sockint::array-data-address sockaddr)
+                          (size-of-sockaddr socket)))
+           -1)
+        (socket-error "bind"))))
+
+\f
+(defmethod socket-accept ((socket socket))
+  "Perform the accept(2) call, returning a newly-created connected socket
+and the peer address as multiple values"
+  (let* ((sockaddr (make-sockaddr-for socket))
+         (fd (sb-sys:without-gcing
+             (sockint::accept (socket-file-descriptor socket)
+                              (sockint::array-data-address sockaddr)
+                              (size-of-sockaddr socket)))))
+    (apply #'values
+          (if (= fd -1)
+              (socket-error "accept") 
+              (let ((s (make-instance (class-of socket)
+                                      :type (socket-type socket)
+                                      :protocol (socket-protocol socket)
+                                      :descriptor fd)))
+                (sb-ext:finalize s (lambda () (sockint::close fd)))))
+          (multiple-value-list (bits-of-sockaddr socket sockaddr)))))
+
+(defgeneric socket-connect (socket &rest address))
+(defmethod socket-connect ((socket socket) &rest peer)
+  "Perform the connect(2) call to connect SOCKET to a remote PEER.  No useful return value"
+  (let* ((sockaddr (apply #'make-sockaddr-for socket nil peer)))
+    (if (= (sb-sys:without-gcing
+           (sockint::connect (socket-file-descriptor socket)
+                             (sockint::array-data-address sockaddr)
+                             (size-of-sockaddr socket)))
+          -1)
+       (socket-error "connect") )))
+
+(defmethod socket-peername ((socket socket))
+  "Return the socket's peer; depending on the address family this may return multiple values"  
+  (let* ((sockaddr (make-sockaddr-for socket)))
+    (when (= (sb-sys:without-gcing
+             (sockint::getpeername (socket-file-descriptor socket)
+                                   (sockint::array-data-address sockaddr)
+                                   (size-of-sockaddr socket)))
+            -1)
+      (socket-error "getpeername"))
+    (bits-of-sockaddr socket sockaddr)))
+
+(defmethod socket-name ((socket socket))
+  "Return the address (as vector of bytes) and port that the socket is bound to, as multiple values"
+  (let* ((sockaddr (make-sockaddr-for socket)))
+    (when (= (sb-sys:without-gcing
+             (sockint::getsockname (socket-file-descriptor socket)
+                                   (sockint::array-data-address sockaddr)
+                                   (size-of-sockaddr socket)))
+            -1)
+      (socket-error "getsockname"))
+    (bits-of-sockaddr socket sockaddr)))
+
+
+;;; There are a whole bunch of interesting things you can do with a
+;;; socket that don't really map onto "do stream io", especially in
+;;; CL which has no portable concept of a "short read".  socket-receive
+;;; allows us to read from an unconnected socket into a buffer, and
+;;; to learn who the sender of the packet was
+
+(defmethod socket-receive ((socket socket) buffer length
+                        &key
+                        oob peek waitall
+                        (element-type 'character))
+  "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"
+  (let ((flags
+        (logior (if oob sockint::MSG-OOB 0)
+                (if peek sockint::MSG-PEEK 0)
+                (if waitall sockint::MSG-WAITALL 0)
+                sockint::MSG-NOSIGNAL  ;don't send us SIGPIPE
+                (if (eql (socket-type socket) :datagram)
+                    sockint::msg-TRUNC 0)))
+       (sockaddr (make-sockaddr-for socket)))
+    (unless (or buffer length)
+      (error "Must supply at least one of BUFFER or LENGTH"))
+    (unless buffer
+      (setf buffer (make-array length :element-type element-type)))
+    (sb-alien:with-alien ((sa-len (array (sb-alien:unsigned 32) 2)))
+      (setf (sb-alien:deref sa-len 0) (size-of-sockaddr socket))
+      (sb-sys:without-gcing 
+       (let ((len
+             (sockint::recvfrom (socket-file-descriptor socket)
+                                (sockint::array-data-address buffer)
+                                (or length (length buffer))
+                                flags
+                                (sockint::array-data-address sockaddr)
+                                (sb-alien:cast sa-len (* integer)))))
+        (when (= len -1) (socket-error "recvfrom"))
+        (apply #'values buffer len (multiple-value-list
+                                    (bits-of-sockaddr socket sockaddr))))))))
+
+
+
+(defmethod socket-listen ((socket socket) backlog)
+  "Mark SOCKET as willing to accept incoming connections.  BACKLOG
+defines the maximum length that the queue of pending connections may
+grow to before new connection attempts are refused.  See also listen(2)"
+  (let ((r (sockint::listen (socket-file-descriptor socket) backlog)))
+    (if (= r -1)
+        (socket-error "listen"))))
+
+(defmethod socket-close ((socket socket))
+  "Close SOCKET.  May throw any kind of error that write(2) would have
+thrown.  If SOCKET-MAKE-STREAM has been called, calls CLOSE on that
+stream instead"
+  ;; the close(2) manual page has all kinds of warning about not
+  ;; checking the return value of close, on the grounds that an
+  ;; earlier write(2) might have returned successfully w/o actually
+  ;; writing the stuff to disk.  It then goes on to define the only
+  ;; possible error return as EBADF (fd isn't a valid open file
+  ;; descriptor).  Presumably this is an oversight and we could also
+  ;; get anything that write(2) would have given us.
+
+  ;; What we do: we catch EBADF.  It should only ever happen if
+  ;; (a) someone's closed the socket already (stream closing seems
+  ;; to have this effect) or (b) the caller is messing around with
+  ;; socket internals.  That's not supported, dude
+  
+  (if (slot-boundp socket 'stream)
+      (close (slot-value socket 'stream))  ;; closes socket as well
+    (handler-case
+     (if (= (sockint::close (socket-file-descriptor socket)) -1)
+         (socket-error "close"))
+     (bad-file-descriptor-error (c) (declare (ignore c)) nil)
+     (:no-error (c)  (declare (ignore c)) nil))))
+
+(defmethod socket-make-stream ((socket socket)  &rest args)
+  "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."
+  (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) args))
+      (setf (slot-value socket 'stream) stream)
+      (sb-ext:cancel-finalization socket))
+    stream))
+
+\f
+
+;;; Error handling
+
+(define-condition socket-error (error)
+  ((errno :initform nil
+          :initarg :errno  
+          :reader socket-error-errno) 
+   (symbol :initform nil :initarg :symbol :reader socket-error-symbol)
+   (syscall  :initform "outer space" :initarg :syscall :reader socket-error-syscall))
+  (:report (lambda (c s)
+             (let ((num (socket-error-errno c)))
+               (format s "Socket error in \"~A\": ~A (~A)"
+                       (socket-error-syscall c)
+                       (or (socket-error-symbol c) (socket-error-errno c))
+                       #+cmu (sb-unix:get-unix-error-msg num)
+                       #+sbcl (sb-int:strerror num))))))
+
+;;; watch out for slightly hacky symbol punning: we use both the value
+;;; and the symbol-name of sockint::efoo
+
+(defmacro define-socket-condition (symbol name)
+  `(progn
+     (define-condition ,name (socket-error)
+       ((symbol :reader socket-error-symbol :initform (quote ,symbol))))
+     (push (cons ,symbol (quote ,name)) *conditions-for-errno*)))
+
+(defparameter *conditions-for-errno* nil)
+;;; this needs the rest of the list adding to it, really.  They also
+;;; need
+;;; - conditions to be exported in the DEFPACKAGE form
+;;; - symbols to be added to constants.ccon
+;;; I haven't yet thought of a non-kludgey way of keeping all this in
+;;; the same place
+(define-socket-condition sockint::EADDRINUSE address-in-use-error)
+(define-socket-condition sockint::EAGAIN interrupted-error)
+(define-socket-condition sockint::EBADF bad-file-descriptor-error)
+(define-socket-condition sockint::ECONNREFUSED connection-refused-error)
+(define-socket-condition sockint::EINTR interrupted-error)
+(define-socket-condition sockint::EINVAL invalid-argument-error)
+(define-socket-condition sockint::ENOBUFS no-buffers-error)
+(define-socket-condition sockint::ENOMEM out-of-memory-error)
+(define-socket-condition sockint::EOPNOTSUPP operation-not-supported-error)
+(define-socket-condition sockint::EPERM operation-not-permitted-error)
+(define-socket-condition sockint::EPROTONOSUPPORT protocol-not-supported-error)
+(define-socket-condition sockint::ESOCKTNOSUPPORT socket-type-not-supported-error)
+(define-socket-condition sockint::ENETUNREACH network-unreachable-error)
+
+
+(defun condition-for-errno (err)
+  (or (cdr (assoc err *conditions-for-errno* :test #'eql)) 'socket-error))
+      
+#+cmu
+(defun socket-error (where)
+  ;; Peter's debian/x86 cmucl packages (and sbcl, derived from them)
+  ;; use a direct syscall interface, and have to call UNIX-GET-ERRNO
+  ;; to update the value that unix-errno looks at.  On other CMUCL
+  ;; ports, (UNIX-GET-ERRNO) is not needed and doesn't exist
+  (when (fboundp 'unix::unix-get-errno) (unix::unix-get-errno))
+  (let ((condition (condition-for-errno sb-unix:unix-errno)))
+    (error condition :errno sb-unix:unix-errno  :syscall where)))
+
+#+sbcl
+(defun socket-error (where)
+  (let* ((errno  (sb-unix::get-errno))
+         (condition (condition-for-errno errno)))
+    (error condition :errno errno  :syscall where)))
+
+
+
diff --git a/contrib/sb-bsd-sockets/sockopt.lisp b/contrib/sb-bsd-sockets/sockopt.lisp
new file mode 100644 (file)
index 0000000..2b89066
--- /dev/null
@@ -0,0 +1,189 @@
+(in-package :sb-bsd-sockets)
+
+#||
+<H2> Socket Options </h2>
+<a name="sockopt"> </a>
+<p> A subset of socket options are supported, using a fairly
+general framework which should make it simple to add more as required 
+- see sockopt.lisp for details.  The name mapping from C is fairly
+straightforward: <tt>SO_RCVLOWAT</tt> becomes
+<tt>sockopt-receive-low-water</tt> and <tt>(setf
+sockopt-receive-low-water)</tt>.
+||#
+
+#|
+getsockopt(socket, level, int optname, void *optval, socklen_t *optlen)
+setsockopt(socket, level, int optname, void *optval, socklen_t optlen)
+             ^ SOL_SOCKET or a protocol number
+
+In terms of providing a useful interface, we have to face up to the
+fact that most of these take different data types - some are integers,
+some are booleans, some are foreign struct instances, etc etc
+
+(define-socket-option lisp-name level number mangle-arg size mangle-return)
+
+macro-expands to two functions that define lisp-name and (setf ,lisp-name)
+and calls the functions mangle-arg and mangle-return on outgoing and incoming
+data resp.
+
+Parameters passed to the function thus defined (lisp-name)
+are all passed directly into mangle-arg.  mangle-arg should return an
+alien pointer  - this is passed unscathed to the foreign routine, so
+wants to have type (* t).  Note that even for options that have
+integer arguments, this is still a pointer to said integer.
+
+size is the size of the buffer that the return of mangle-arg points
+to, and also of the buffer that we should allocate for getsockopt 
+to write into.
+
+mangle-return is called with an alien buffer and should turn it into
+something that the caller will want.
+
+Code for options that not every system has should be conditionalised:
+
+(if (boundp 'sockint::IP_RECVIF)
+    (define-socket-option so-receive-interface (getprotobyname "ip")
+      sockint::IP_RECVIF  ...  ))
+
+
+|#
+
+(defmacro define-socket-option
+  (lisp-name level number mangle-arg size mangle-return)
+  (let ((find-level
+        (if (numberp (eval level))
+            level
+            `(get-protocol-by-name ,(string-downcase (symbol-name level))))))
+    `(progn
+      (export ',lisp-name)
+      (defun ,lisp-name (socket &aux (fd (socket-file-descriptor socket)))
+       (sb-sys:without-gcing
+        (let ((buf (make-array sockint::size-of-int
+                               :element-type '(unsigned-byte 8)
+                               :initial-element 0)))
+          (if (= -1 (sockint::getsockopt
+                     fd ,find-level ,number (sockint::array-data-address buf) ,size))
+              (socket-error "getsockopt")
+              (,mangle-return buf ,size)))))
+      (defun (setf ,lisp-name) (new-val socket
+                               &aux (fd (socket-file-descriptor socket)))
+       (if (= -1
+              (sb-sys:without-gcing
+               (sockint::setsockopt
+                fd ,find-level ,number (funcall (function ,mangle-arg) new-val ,size)
+                ,size)))
+           (socket-error "setsockopt"))))))
+
+;;; sockopts that have integer arguments
+
+(defun int-to-foreign (x size)
+  ;; can't use with-alien, as the variables it creates only have
+  ;; dynamic scope.  can't use the passed-in size because sap-alien
+  ;; is a macro and evaluates its second arg at read time
+  (let* ((v (make-array size :element-type '(unsigned-byte 8)
+                       :initial-element 0))
+        (d (sockint::array-data-address v))
+        (alien (sb-alien:sap-alien
+                d; (sb-sys:int-sap d)
+                (* (sb-alien:signed #.(* 8 sockint::size-of-int))))))
+    (setf (sb-alien:deref alien 0) x)
+    alien))
+
+(defun buffer-to-int (x size)
+  (declare (ignore size))
+  (let ((alien (sb-alien:sap-alien
+               (sockint::array-data-address x)
+               (* (sb-alien:signed #.(* 8 sockint::size-of-int))))))
+    (sb-alien:deref alien)))
+
+(defmacro define-socket-option-int (name level number)
+  `(define-socket-option ,name ,level ,number
+     int-to-foreign sockint::size-of-int buffer-to-int))
+
+(define-socket-option-int
+  sockopt-receive-low-water sockint::sol-socket sockint::so-rcvlowat)
+(define-socket-option-int
+  sockopt-send-low-water sockint::sol-socket sockint::so-sndlowat)
+(define-socket-option-int
+  sockopt-type sockint::sol-socket sockint::so-type)
+(define-socket-option-int
+  sockopt-send-buffer sockint::sol-socket sockint::so-sndbuf)
+(define-socket-option-int
+  sockopt-receive-buffer sockint::sol-socket sockint::so-rcvbuf)
+(define-socket-option-int
+  sockopt-priority sockint::sol-socket sockint::so-priority)
+
+;;; boolean options are integers really
+
+(defun bool-to-foreign (x size)
+  (int-to-foreign (if x 1 0) size))
+
+(defun buffer-to-bool (x size)
+  (not (= (buffer-to-int x size) 0)))
+
+(defmacro define-socket-option-bool (name level number)
+  `(define-socket-option ,name ,level ,number
+     bool-to-foreign sockint::size-of-int buffer-to-bool))
+
+(define-socket-option-bool
+  sockopt-reuse-address sockint::sol-socket sockint::so-reuseaddr)
+(define-socket-option-bool
+  sockopt-keep-alive sockint::sol-socket sockint::so-keepalive)
+(define-socket-option-bool
+  sockopt-oob-inline sockint::sol-socket sockint::so-oobinline)
+(define-socket-option-bool
+  sockopt-bsd-compatible sockint::sol-socket sockint::so-bsdcompat)
+(define-socket-option-bool
+  sockopt-pass-credentials sockint::sol-socket sockint::so-passcred)
+(define-socket-option-bool
+  sockopt-debug sockint::sol-socket sockint::so-debug)
+(define-socket-option-bool
+  sockopt-dont-route sockint::sol-socket sockint::so-dontroute)
+(define-socket-option-bool
+  sockopt-broadcast sockint::sol-socket sockint::so-broadcast)
+
+(define-socket-option-bool sockopt-tcp-nodelay :tcp sockint::tcp-nodelay)
+
+(defun string-to-foreign (string size)
+  (declare (ignore size))
+  (let ((data (sockint::array-data-address string)))
+    (sb-alien:sap-alien data (* t))))
+                                                         
+(defun buffer-to-string (x size)
+  (declare (ignore size))
+  (sb-c-call::%naturalize-c-string
+   (sockint::array-data-address x)))
+
+(define-socket-option sockopt-bind-to-device sockint::sol-socket
+  sockint::so-bindtodevice string-to-foreign sockint::ifnamsiz
+  buffer-to-string)
+
+;;; other kinds of socket option
+
+;;; so_peercred takes a ucre structure
+;;; so_linger struct linger {
+;                  int   l_onoff;    /* linger active */
+;                  int   l_linger;   /* how many seconds to linger for */
+;              };
+
+#|
+
+(sockopt-reuse-address 2)
+
+(defun echo-server ()
+  (let ((s (make-inet-socket :stream (get-protocol-by-name "tcp"))))
+    (setf (sockopt-reuse-address s) t)
+    (setf (sockopt-bind-to-device s) "lo")
+    (socket-bind s (make-inet-address "127.0.0.1") 3459)
+    (socket-listen s 5)
+    (dotimes (i 10)
+      (let* ((s1 (socket-accept s))
+             (stream (socket-make-stream s1 :input t :output t :buffering :none)))
+        (let ((line (read-line stream)))
+          (format t "got one ~A ~%" line)
+          (format stream "~A~%" line))
+        (close stream)))))
+
+NIL
+|#
+
diff --git a/contrib/sb-bsd-sockets/split.lisp b/contrib/sb-bsd-sockets/split.lisp
new file mode 100644 (file)
index 0000000..fec708c
--- /dev/null
@@ -0,0 +1,23 @@
+(in-package :sb-bsd-sockets)
+
+;;; This courtesy of Pierre Mai in comp.lang.lisp 08 Jan 1999 00:51:44 +0100
+;;; Message-ID: <87lnjebq0f.fsf@orion.dent.isdn.cs.tu-berlin.de>
+
+(defun split (string &optional max (ws '(#\Space #\Tab)))
+  "Split `string' along whitespace as defined by the sequence `ws'.
+The whitespace is elided from the result.  The whole string will be
+split, unless `max' is a non-negative integer, in which case the
+string will be split into `max' tokens at most, the last one
+containing the whole rest of the given `string', if any."
+  (flet ((is-ws (char) (find char ws)))
+    (loop for start = (position-if-not #'is-ws string)
+          then (position-if-not #'is-ws string :start index)
+          for index = (and start
+                           (if (and max (= (1+ word-count) max))
+                               nil
+                             (position-if #'is-ws string :start start)))
+          while start
+          collect (subseq string start index)
+          count 1 into word-count
+          while index)))
+
diff --git a/contrib/sb-bsd-sockets/tests.lisp b/contrib/sb-bsd-sockets/tests.lisp
new file mode 100644 (file)
index 0000000..22512f5
--- /dev/null
@@ -0,0 +1,225 @@
+(defpackage "SB-BSD-SOCKETS-TEST"
+  (:use "CL" "SB-BSD-SOCKETS" "RT"))
+
+#||
+
+<H1>Tests</h1>
+
+There should be at least one test for pretty much everything you can do
+with the package.  In some places I've been more diligent than others; more
+tests gratefully accepted.
+
+Tests are in the file <tt>tests.lisp</tt> and also make good examples.
+
+||#
+
+(in-package :sb-bsd-sockets-test)
+
+;;; a real address
+(deftest make-inet-address
+  (equalp (make-inet-address "127.0.0.1")  #(127 0 0 1))
+  t)
+;;; and an address with bit 8 set on some octets
+(deftest make-inet-address2
+  (equalp (make-inet-address "242.1.211.3")  #(242 1 211 3))
+  t)
+
+(deftest make-inet-socket
+  ;; make a socket
+  (let ((s (make-instance 'inet-socket :type :stream :protocol (get-protocol-by-name "tcp"))))
+    (and (> (socket-file-descriptor s) 1) t))
+  t)
+
+(deftest make-inet-socket-keyword
+    ;; make a socket
+    (let ((s (make-instance 'inet-socket :type :stream :protocol :tcp)))
+      (and (> (socket-file-descriptor s) 1) t))
+  t)
+
+(deftest make-inet-socket-wrong
+    ;; fail to make a socket: check correct error return.  There's no nice
+    ;; way to check the condition stuff on its own, which is a shame
+    (handler-case
+       (make-instance 'inet-socket :type :stream :protocol (get-protocol-by-name "udp"))
+      ((or socket-type-not-supported-error protocol-not-supported-error) (c)
+       (declare (ignorable c)) t)
+      (:no-error nil))
+  t)
+
+(deftest make-inet-socket-keyword-wrong
+    ;; same again with keywords
+    (handler-case
+       (make-instance 'inet-socket :type :stream :protocol :udp)
+      ((or protocol-not-supported-error socket-type-not-supported-error) (c)
+       (declare (ignorable c)) t)
+      (:no-error nil))
+  t)
+
+
+(deftest non-block-socket
+  (let ((s (make-instance 'inet-socket :type :stream :protocol :tcp)))
+    (setf (non-blocking-mode s) t)
+    (non-blocking-mode s))
+  t)
+
+(defun do-gc-portably ()
+  ;; cmucl on linux has generational gc with a keyword argument,
+  ;; sbcl GC function takes same arguments no matter what collector is in
+  ;; use
+  #+(or sbcl gencgc) (SB-EXT:gc :full t)
+  ;; other platforms have full gc or nothing
+  #-(or sbcl gencgc) (sb-ext:gc))
+
+(deftest inet-socket-bind
+  (let ((s (make-instance 'inet-socket :type :stream :protocol (get-protocol-by-name "tcp"))))
+    ;; Given the functions we've got so far, if you can think of a
+    ;; better way to make sure the bind succeeded than trying it
+    ;; twice, let me know
+    ;; 1974 has no special significance, unless you're the same age as me
+    (do-gc-portably) ;gc should clear out any old sockets bound to this port
+    (socket-bind s (make-inet-address "127.0.0.1") 1974)
+    (handler-case
+       (let ((s2 (make-instance 'inet-socket :type :stream :protocol (get-protocol-by-name "tcp"))))
+         (socket-bind s2 (make-inet-address "127.0.0.1") 1974)
+         nil)
+      (address-in-use-error () t)))
+  t)
+
+(deftest simple-sockopt-test
+  ;; test we can set SO_REUSEADDR on a socket and retrieve it, and in
+  ;; the process that all the weird macros in sockopt happened right.
+  (let ((s (make-instance 'inet-socket :type :stream :protocol (get-protocol-by-name "tcp"))))
+    (setf (sockopt-reuse-address s) t)
+    (sockopt-reuse-address s))
+  t)
+
+(defun read-buf-nonblock (buffer stream)
+  "Like READ-SEQUENCE, but returns early if the full quantity of data isn't there to be read.  Blocks if no input at all"
+  (let ((eof (gensym)))
+    (do ((i 0 (1+ i))
+         (c (read-char stream nil eof)
+            (read-char-no-hang stream nil eof)))
+        ((or (>= i (length buffer)) (not c) (eq c eof)) i)
+      (setf (elt buffer i) c))))
+
+;;; these require that the echo services are turned on in inetd
+
+(deftest simple-tcp-client
+    (let ((s (make-instance 'inet-socket :type :stream :protocol :tcp))
+         (data (make-string 200)))
+      (socket-connect s #(127 0 0 1) 7)
+      (let ((stream (socket-make-stream s :input t :output t :buffering :none)))
+       (format stream "here is some text")
+       (let ((data (subseq data 0 (read-buf-nonblock data stream))))
+         (format t "~&Got ~S back from TCP echo server~%" data)
+         (> (length data) 0))))
+  t)
+
+(deftest simple-udp-client
+  (let ((s (make-instance 'inet-socket :type :datagram :protocol (get-protocol-by-name "udp")))
+        (data (make-string 200)))
+    (format t "Socket type is ~A~%" (sockopt-type s))
+    (socket-connect s #(127 0 0 1) 7)
+    (let ((stream (socket-make-stream s :input t :output t :buffering :none)))
+      (format stream "here is some text")
+      (let ((data (subseq data 0 (read-buf-nonblock data stream))))
+       (format t "~&Got ~S back from UDP echo server~%" data)
+       (> (length data) 0))))
+  t)
+
+#||
+<h2>Unix-domain sockets</h2>
+
+A fairly rudimentary test that connects to the syslog socket and sends a 
+message.  Priority 7 is kern.debug; you'll probably want to look at
+/etc/syslog.conf or local equivalent to find out where the message ended up
+||#
+
+(deftest simple-unix-client
+    (let ((s (make-instance 'unix-socket :type :datagram)))
+      (format t "~A~%" s)
+      (socket-connect s "/dev/log")
+      (let ((stream (socket-make-stream s :input t :output t :buffering :none)))
+       (format stream
+               "<7>bsd-sockets: Don't panic.  We're testing unix-domain client code; this message can safely be ignored")
+       t))
+  t)
+
+
+;;; these require that the internet (or bits of it, atleast) is available
+
+(deftest get-host-by-name
+  (equalp (car (host-ent-addresses (get-host-by-name "a.root-servers.net")))
+          #(198 41 0 4))
+  t)
+
+(deftest get-host-by-address
+    (host-ent-name (get-host-by-address #(198 41 0 4)))
+  "a.root-servers.net")
+
+(deftest get-host-by-name-wrong
+  (handler-case
+   (get-host-by-name "foo.tninkpad.telent.net")
+   (NAME-SERVICE-ERROR () t)
+   (:no-error nil))
+  t)
+
+(defun http-stream (host port request)
+  (let ((s (make-instance 'inet-socket :type :stream :protocol :tcp)))
+    (socket-connect
+     s (car (host-ent-addresses (get-host-by-name host))) port)
+    (let ((stream (socket-make-stream s :input t :output t :buffering :none)))
+      (format stream "~A HTTP/1.0~%~%" request))
+    s))
+
+(deftest simple-http-client-1
+    (handler-case
+       (let ((s (http-stream "ww.telent.net" 80 "HEAD /")))
+         (let ((data (make-string 200)))
+           (setf data (subseq data 0
+                              (read-buf-nonblock data
+                                                 (socket-make-stream s))))
+           (princ data)
+           (> (length data) 0)))
+      (network-unreachable-error () 'network-unreachable))
+  t)
+
+
+(deftest sockopt-receive-buffer
+    ;; on Linux x86, the receive buffer size appears to be doubled in the
+    ;; kernel: we set a size of x and then getsockopt() returns 2x.
+    ;; This is why we compare with >= instead of =
+    (handler-case
+       (let ((s (http-stream "ww.telent.net" 80 "HEAD /")))
+         (setf (sockopt-receive-buffer s) 1975)
+         (let ((data (make-string 200)))
+           (setf data (subseq data 0
+                              (read-buf-nonblock data
+                                                 (socket-make-stream s))))
+           (and (> (length data) 0)
+                (>= (sockopt-receive-buffer s) 1975))))
+      (network-unreachable-error () 'network-unreachable))
+  t)
+
+
+;;; we don't have an automatic test for some of this yet.  There's no
+;;; simple way to run servers and have something automatically connect
+;;; to them as client, unless we spawn external programs.  Then we
+;;; have to start telling people what external programs they should
+;;; have installed.  Which, eventually, we will, but not just yet
+
+
+;;; to check with this: can display packets from multiple peers
+;;; peer address is shown correctly for each packet
+;;; packet length is correct
+;;; long (>500 byte) packets have the full length shown (doesn't work)
+
+(defun udp-server (port)
+  (let ((s (make-instance 'inet-socket :type :datagram :protocol :udp)))
+    (socket-bind s #(0 0 0 0) port)
+    (loop
+     (multiple-value-bind (buf len address port) (socket-receive s nil 500)
+       (format t "Received ~A bytes from ~A:~A - ~A ~%"
+              len address port (subseq buf 0 (min 10 len)))))))
+  
+  
diff --git a/contrib/sb-bsd-sockets/unix.lisp b/contrib/sb-bsd-sockets/unix.lisp
new file mode 100644 (file)
index 0000000..bd9835d
--- /dev/null
@@ -0,0 +1,40 @@
+(in-package :sb-bsd-sockets)
+
+#|| <h2>File-domain sockets</h2>
+
+File-domain (AF_FILE) sockets are also known as Unix-domain sockets, but were
+renamed by POSIX presumably on the basis that they may be
+available on other systems too.  
+
+A file-domain socket address is a string, which is used to create a node
+in the local filesystem.  This means of course that they cannot be used across
+a network.
+
+||#
+
+(defclass unix-socket (socket)
+  ((family :initform sockint::af-unix)))
+
+(defmethod make-sockaddr-for ((socket unix-socket) &optional sockaddr &rest address &aux (filename (first address)))
+  (let ((sockaddr (or sockaddr (sockint::allocate-sockaddr-un))))
+    (setf (sockint::sockaddr-un-family sockaddr) sockint::af-unix)
+    (when filename
+      (loop for c across filename
+           ;; XXX magic constant ew ew ew.  should grovel this from
+           ;; system headers
+           for i from 0 to (min 107 (1- (length filename)))
+           do (setf (sockint::sockaddr-un-path sockaddr i) (char-code c))
+           finally
+           (setf (sockint::sockaddr-un-path sockaddr (1+ i)) 0)))
+    sockaddr))
+
+(defmethod size-of-sockaddr ((socket unix-socket))
+  sockint::size-of-sockaddr-un)
+
+(defmethod bits-of-sockaddr ((socket unix-socket) sockaddr)
+  "Returns filename of SOCKADDR"
+  (let ((name (sb-c-call::%naturalize-c-string
+              (sb-sys:sap+ (sockint::array-data-address sockaddr)
+                           sockint::offset-of-sockaddr-un-path))))
+    (if (zerop (length name)) nil name)))
+
diff --git a/contrib/vanilla-module.mk b/contrib/vanilla-module.mk
new file mode 100644 (file)
index 0000000..25efdd1
--- /dev/null
@@ -0,0 +1,8 @@
+
+$(MODULE).fasl: $(MODULE).lisp
+       $(SBCL) --eval '(compile-file "$(MODULE)")' </dev/null
+
+test:: $(MODULE).fasl
+
+install: test
+       cp $(MODULE).fasl $(INSTALL_DIR)
diff --git a/make-target-contrib.sh b/make-target-contrib.sh
new file mode 100644 (file)
index 0000000..8e818b5
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+# This is a script to be run as part of make.sh. The only time you'd
+# probably want to run it by itself is if you're trying to
+# cross-compile the system or if you're doing some kind of
+# troubleshooting.
+
+# This software is part of the SBCL system. See the README file for
+# more information.
+#
+# This software is derived from the CMU CL system, which was
+# written at Carnegie Mellon University and released into the
+# public domain. The software is in the public domain and is
+# provided with absolutely no warranty. See the COPYING and CREDITS
+# files for more information.
+
+SBCL="`pwd`/src/runtime/sbcl --noinform --core `pwd`/output/sbcl.core --userinit /dev/null --sysinit /dev/null --disable-debugger"
+SBCL_BUILDING_CONTRIB=1
+export SBCL SBCL_BUILDING_CONTRIB
+for i in contrib/*; do
+    test -d $i || continue;
+    # export INSTALL_DIR=$SBCL_HOME/`basename $i `
+    make -C $i test 
+done
diff --git a/make.sh b/make.sh
index 6aa8f99..8ba89c0 100755 (executable)
--- a/make.sh
+++ b/make.sh
@@ -86,7 +86,8 @@ sh make-config.sh || exit 1
 #     SBCL_XC_HOST=<whatever> sh make-host-2.sh
 #   Copy output/cold-sbcl.core from the host system to the target system.
 #   On the target system:
-#     sh make-host-2.sh
+#     sh make-target-2.sh
+#     sh make-target-contrib.sh
 # Or, if you can set up the files somewhere shared (with NFS, AFS, or
 # whatever) between the host machine and the target machine, the basic
 # procedure above should still work, but you can skip the "copy" steps.
@@ -94,4 +95,5 @@ sh make-host-1.sh   || exit 1
 sh make-target-1.sh || exit 1
 sh make-host-2.sh   || exit 1
 sh make-target-2.sh || exit 1
+sh make-target-contrib.sh || exit 1
 date
index 4525530..98410f1 100644 (file)
@@ -18,4 +18,4 @@
 ;;; versions, especially for internal versions off the main CVS
 ;;; branch, it gets hairier, e.g. "0.pre7.14.flaky4.13".)
 
-"0.7.12.27"
+"0.7.12.28"