November 10, 2001

Portability 2: Sockets

The software I'm porting to CMUCL uses Franz' AllegroServe web server, which is not intended to be portable to other lisps. Luckily Jochen Schmidt has a Portable AllegroServe project that he maintains separately.

One very useful part of that package is an ACL-compatible socket interface for CMUCL. I like the ACL socket interface; It's not fancy or anything, it's just like a nice lisp version of BSD sockets. CMUCL's own socket interface is very low-level, supporting integer file descriptors instead of streams. (Back when I used MCL, I remember thinking that its networking interface had a few minor problems. Like having to call unexported functions to do essential things, not having the ability to ask the system to bind a socket to a local port that is known to be free, and not having an equivalent to the BSD socket concept of allowing queued connections.)

I am currently facing a situation where the socket compatibility code is not compatible enough, however. I have some code that looks like

(handler-case (read-and-handle-flap self :block-p T)
  (socket-error (e)
    (warn "AIM connection got the following error: ~A." e)
    (setf continue-p (handle-disconnection self))))

which wants to handle errors of type socket-error specially. Unfortunately CMUCL signals errors of type simple-error in situations in which ACL signals socket-error, for example when a hostname cannot be resolved to an IP address.

It probably wouldn't be too hard to create a unified socket function interface across common lisp implementations. What I bet would be harder is making sure that each one implements the exceptional situations that are so important when using sockets in the same way.

What to do.

Posted by jjwiseman at 02:13 PM | Comments (1)

Portability 1: Files and Iteration

I'm porting some lisp code from a lisp on windows (ACL) to a different lisp (CMUCL) on unix. Here are a few of the things I've run into while trying to get the code to work. (Most of these things are pretty obvious, some are just simple errors that ACL didn't catch.)

  • Assume filename case might be significant, but don't rely on it. "server/netmon-server.lisp" is not the same file as "Server/netmon-server.lisp" under unix, but it might be under Windows.

  • No hardcoded paths. "C:/projects/netmon/foo/file.txt" should be something like "../foo/file.txt" (but see the next item).

  • Don't use weird namestring tricks like "..". I lied above, it shouldn't be "../foo/file.txt". It should be something like

    (merge-pathnames
      (make-pathname :directory '(:relative :up "foo")
                     :name "file"
                     :type "txt")
      *load-pathname*)
    

    The problem is that "../foo/file.txt" has no standard meaning in lisp. It means something in unix and windows, but even there it makes assumptions about what the current directory must be. (And it's not like every operating system has a concept of "current directory". What would be the current directory on a Mac, huh? I bet you have lots of guesses, but odds are you don't know.)

    (Note that according to the Hyperspec an implementation is not required to accept :up in a directory list, and until recently (October 2001) CLISP did not--but they added support for it promptly after I pointed it out.

  • The termination test for do is not optional. So

    (do (...)
        ()
      ...)
    
    is not allowed. It has to be
    (do (...)
        (NIL)
      ...)
    

    (This one was just a dumb and weird mistake. What was I thinking?)

  • file-length is defined to take a stream argument, not a filename. So to determine the length of a file you have to open it first (which seems strange to me, but is not too much of a hassle).

Posted by jjwiseman at 02:17 AM | Comments (2)