Work Samples

Much of my recent work is available directly on the Net. Below I present several fragments of my work, in different styles.

Technical Communication

XEmacs maintainer

A good idea of the work of an XEmacs maintainer can be gleaned from http://www.us.xemacs.org/Releases/21.2.30.html

I did the release engineering to build and test this release (and many others) on multiple platforms and to create this "announcement" web page. An "executive summary" by me of the changes in the release are included. The announcement also includes several changes by me, including portability fixes, Lisp engine bug fixes, performance improvements, and added test cases. (see also xre below)

Autoconf — "config.cache considered harmful"

Perhaps my most useful non-coding contribution to the world was persuading the Autoconf maintainers to remove their configuration value cache.

Binary compatibility between object files produced by different compilers

I tried (and failed, I think) to persuade the GCC maintainers to improve the binary compatibility between object files created by gcc and other compilers like Sun cc.

Further examples

Further examples of my technical writing:

Source Code samples

The code samples below are extracts from the XEmacs source code authored by me. XEmacs source code can be obtained from an xemacs-21.4 source distribution, e.g. ftp://ftp.xemacs.org/xemacs-21.4/xemacs-21.4.4-src.tar.gz

xre

xre is my personal release engineering perl script for doing XEmacs releases such as the first URL above. This code has only ever been used my me, yet is designed for maintainability and reuse.

A notable robustness feature is never releasing a code base that is `corrupted' as a result of doing a CVS checkout in the middle of someone else's checkin, since CVS checkins are not atomic operations.

    # Checkout (or update) a CVS tree.
    # Because, being maintainers, we're in paranoid mode, we continue to
    # do CVS checkouts until the repository appears to be "quiescent".
    # This avoids the problem documented in the CVS Info node "Concurrency".
    sub Checkout_carefully {
      my @checkout_cmd = @_;
      while (1) {
	my $quiescent = 1;
	my $cvs_output = InputPipe (@checkout_cmd);
	print STDERR "==> @checkout_cmd\n";
	while (<$cvs_output>) {
	  print;
	  # See CVS Info node "update options" for description of checkout output.
	  # We ignore C, M and A because otherwise we'll infloop.
	  $quiescent = 0 if /^[UPR] /;
	}
	last if $quiescent;
      }
    }

Hashtable API

src/elhash.c is a fairly clean implementation of the Common Lisp hashtable API in C.

Test suite framework

tests/automated/lisp-tests.el (emacs lisp) is an example of a test suite framework designed to make life easy for the test case writer.

I created the first automated test suite for XEmacs. The design of the testing framework interface was particularly successful because of the simplicity of creating new tests, encouraging others to create tests for their own contributions.

Most tests can be written using only one simple interface:

  (Assert ...)
with obvious semantics.
  (Assert (eq (setq) nil))
Run-time exceptions can be tested for via these additional interfaces:
  (Check-Error wrong-number-of-arguments (setq setq-test-foo))

  (Check-Error-Message
    error "Object type has no properties"
    (get 2 'property))

Here is a more sophisticated example that tests a set of similar functions for proper error handling when presented with invalid arguments, including my favorite, circular lists of different lengths.

(One of my best changes to XEmacs was to change many Lisp functions to signal an exception instead of looping forever when given a circular list argument, without loss of efficiency)

    ;;-----------------------------------------------------
    ;; testing list-walker functions
    ;;-----------------------------------------------------
    (macrolet
	((test-fun
	  (fun)
	  `(progn
	     (Check-Error wrong-number-of-arguments (,fun))
	     (Check-Error wrong-number-of-arguments (,fun nil))
	     (Check-Error malformed-list (,fun nil 1))
	     ,@(loop for n in '(1 2 2000)
		 collect `(Check-Error circular-list (,fun 1 (make-circular-list ,n))))))
	 (test-funs (&rest funs) `(progn ,@(loop for fun in funs collect `(test-fun ,fun)))))

      (test-funs member old-member
		 memq   old-memq
		 assoc  old-assoc
		 rassoc old-rassoc
		 rassq  old-rassq
		 delete old-delete
		 delq   old-delq
		remassoc remassq remrassoc remrassq))

Emacs Lisp byte-compiler

I've maintained the Emacs Lisp byte-compiler. Here's an example:
http://list-archive.xemacs.org/xemacs-patches/200012/msg00086.html

(XEmacs has almost everything Java has: a byte compiler, VM, crash-proof language, portable GUI API, ...)

Inter-Unix portability code

I was the primary author for much of the inter-Unix portability code.

Here's an example of the portability nightmare of one aspect of the XEmacs source code — pty handling. Almost all Unix variants need to be handled differently.

I believe the pty portability layer in XEmacs is among the best available, even having some advantages over the obvious contender, "expect".

Here's a taste of the variation possible:


src/config.h.in:
    /* PTY support functions */
    #undef HAVE_GETPT     /* glibc's easy pty allocation function */
    #undef HAVE__GETPTY   /*   SGI's easy pty allocation function */
    #undef HAVE_OPENPTY   /*   BSD's easy pty allocation function */
    #undef HAVE_GRANTPT   /* Unix98 */
    #undef HAVE_UNLOCKPT  /* Unix98 */
    #undef HAVE_PTSNAME   /* Unix98 */
    #undef HAVE_KILLPG    /* BSD */
    #undef HAVE_TCGETPGRP /* Posix 1 */
    #undef HAVE_ISASTREAM /* SysV streams */
    #undef HAVE_PTY_H     /* Linux, Tru64 openpty */
    #undef HAVE_LIBUTIL_H /* BSD openpty */
    #undef HAVE_UTIL_H    /* NetBSD openpty */
    #undef HAVE_STROPTS_H /* SysV streams */
    #undef HAVE_STRTIO_H  /* SysV streams TIOCSIGNAL */

configure.in: (Autoconf/Bourne shell)
    dnl ----------------------------------------------------------------
    dnl Check for PTY support functions.
    dnl ----------------------------------------------------------------

    dnl There is no "standard" pty allocation method.  Every system is different.
    dnl  getpt()  is the preferred pty allocation method on glibc systems.
    dnl _getpty() is the preferred pty allocation method on SGI systems.
    dnl grantpt(), unlockpt(), ptsname() are defined by Unix98.
    AC_CHECK_FUNCS(getpt _getpty grantpt unlockpt ptsname killpg tcgetpgrp)

    dnl openpty() is the preferred pty allocation method on BSD and Tru64 systems.
    dnl openpty() might be declared in:
    dnl - pty.h (Tru64 or Linux)
    dnl - libutil.h (FreeBSD)
    dnl - util.h (NetBSD)
    AC_CHECK_FUNC(openpty, have_openpty=yes, [
      AC_CHECK_LIB(util, openpty, have_openpty=yes need_libutil=yes)])
    if test "$have_openpty" = "yes"; then
      AC_DEFINE(HAVE_OPENPTY)
      AC_CHECK_HEADERS(pty.h libutil.h util.h, break)
      test "$need_libutil" = "yes" && XE_APPEND(-lutil, libs_system)
    fi

One excellent feature of XEmacs' pty portability layer is deferring some of the handling to runtime. For example, the name of the "pty master clone device" can be different on different systems with the same ABI and can even be changed by system administrator action on some systems like Digital Unix while xemacs is running.

So instead of defining compile-time symbols like HAVE_DEV_PTMX, the right way to do it is like this:

    static const char * const clones[] = /* Different pty master clone devices */
      {
	"/dev/ptmx",      /* Various systems */
	"/dev/ptm/clone", /* HPUX */
	"/dev/ptc",       /* AIX */
	"/dev/ptmx_bsd"   /* Tru64 */
      };

      for (i = 0; i < countof (clones); i++)
	{
	  master_fd = open (clones[i], O_RDWR | O_NONBLOCK | OPEN_BINARY, 0);
	    if (master_fd != 0) ...
	}

For the details of the gory C code, look at: src/process-unix.c: allocate_pty(), unix_kill_child_process()

perl — C source code dependency generator

src/make-src-depend (perl — C source code dependency generator)

This small program generates reliable makefile dependencies for the XEmacs C code (naturally by looking for #include statements and forming the transitive closure of the `uses' relationship)

    # Make transitive closure of %uses
    while (1) {
      my $changedP = 0;
      for my $x (keys %uses) {
	for my $y (keys %{$uses{$x}}) {
	  for my $z (keys %{$uses{$y}}) {
	    if (! exists $uses{$x}{$z}) {
	      $uses{$x}{$z} = 1;
	      $changedP = 1;
	    }
	  }
	}
      }
      last if !$changedP;
    }

Two robustness features:

  1. Instead of the traditional Unix line 1:
        #!/usr/bin/perl
    

    our script can be independent of the actual location of the perl binary with this idiom, which seems to be portable to every Unix system:

        : #-*- Perl -*-
        eval 'exec perl -w -S $0 ${1+"$@"}'
          if 0;
    
  2. We ignore commented-out #include statements like:
        /* dead code
        #include "foo.h"
        */
    

    and even correctly handle the devious ...

        char * foo = "/*";
        ...
        #include "foo.h"
        ...
    

    using this typically perlish regexp

        # Surprisingly robust regexp to remove comments from arbitrary C code
        sub RemoveComments {
          $_[0] =~
    	s{ (
    	    [^\"\'/]+ |
    	    (?:\"[^\"\\]*(?:\\.[^\"\\]*)*\" [^\"\'/]*)+ |
    	    (?:\'[^\'\\]*(?:\\.[^\'\\]*)*\' [^\"\'/]*)+
    	   )
    	   | / (?:
    		\*[^*]*\*+(?:[^/*][^*]*\*+)*/
    		|
    		/[^\n]*
    	       )
    	 }{defined $1 ? $1 : ""}gsxeo;
        }
    

Implementing alignof

XEmacs' portability layer includes an implementation of alignof.

src/lisp.h:

    # if defined (__GNUC__) && (__GNUC__ >= 2)
    /* gcc has an extension that gives us exactly what we want. */
    #  define ALIGNOF(type) __alignof__ (type)
    # elif ! defined (__cplusplus)
    /* The following is mostly portable, except that:
       - it doesn't work for inside out declarations like void (*) (void).
	 (so just call ALIGNOF with a typedef'ed name)
       - it doesn't work with C++.  The C++ committee has decided,
	 in its infinite wisdom, that:
	 "Types must be declared in declarations, not in expressions." */
    #  define ALIGNOF(type) offsetof (struct { char c; type member; }, member)
    # else
    /* C++ is annoying, but it has a big bag of tricks.
       The following doesn't have the "inside out" declaration bug C does. */
    template<typename T> struct alignment_trick { char c; T member; };
    #  define ALIGNOF(type) offsetof (alignment_trick<type>, member)
    # endif
    #endif /* ALIGNOF */

Recently I've been doing more thinking about C++ …

If you are doing serious C++ work and you need to get the alignment of a non-POD type, the following typical C++ recursive template code might be fairly portable standard C++:

    template <class T> struct alignof;

    template <class T, int sizeof_quantum>
    struct alignof_helper
    {
      enum { RET = sizeof_quantum };
    };

    template <class T>
    struct alignof_helper<T,0>
    {
      enum { RET = alignof<T>::RET };
    };

    template <class T>
    struct alignof
    {
      struct bigger { T x; char c; };
      enum { PROVISIONAL =
	     alignof_helper<bigger, (sizeof (bigger) - sizeof (T))>::RET };
      enum { RET =
	     ((PROVISIONAL > sizeof (T)) ? sizeof (T) : PROVISIONAL) };
    };

    #define ALIGNOF(type) alignof<type>::RET

Back to Martin's home page
Last modified: Thu Jan 16 19:05:45 PST 2003
Copyright © 2003 Martin Buchholz