Mon Jun 6 10:42:44 BST 2005
Live 8

You can now txt C to the Live 8 number (84599) to enter into the draw. Let's say, for example, that 10 million people enter. There are 72,500 winners (each winner gets a pair of tickets, but I'll not count the lucky tag-alongs as winners for now). So there's a 72500/10000000 = 1/133 chance of winning a ticket. Since each entry in the draw costs £1.50-£1.60 the effective price of a ticket-pair is £199-£213. That's one hell of an expensive ticket!

Wed Jun 1 19:45:04 BST 2005
Asynchronous DNS lookups with glibc

This is very poorly documented, but glibc can do DNS lookups asynchronously. You can get the original design document here, but it's a bit verbose.

Firstly, this is glibc specific and you need to link against libanl. The headers you'll need are netdb.h and signal.h

The core function is getaddrinfo_a which takes four arguments:

int getaddrinfo_a(int mode, struct gaicb *list[], int ent, struct sigevent *);

The mode is either GAI_NOWAIT or GAI_WAIT. Since you're trying to do asynchronous lookups you'll want GAI_NOWAIT. A gaicb looks like:

struct gaicb {
	const char *ar_name;
	const char *ar_service;
	const struct addrinfo *ar_request;
	struct addrinfo *ar_result;
};

You should see the manpage for getaddrinfo for details of those fields. In short, set ar_name to the hostname, ar_service, ar_request and ar_result to NULL.

So, getaddrinfo_a takes a pointer to a list of those structures and ent is the number of entries in that list. The final argument tells glibc how you want to be informed about the result. A sigevent structure looks like:

strict sigevent {
	sigval_t sigev_value;
	int sigev_signo;
	int sigev_notify;
	void (*sigev_notify_function) (sigval_t);
	pthread_addr_t *sigev_notify_attributes;
};

So you can either ignore the notification (set sigev_notify to SIGEV_NONE), get a signal (set sigev_notify to SIGEV_SIGNAL) or request a callback in a new thread (set SIGEV_THREAD).

Hopefully the rest of the values are fairly obvious in light of that. If you want the sigev_value to be passed to a signal handler you'll need to register the handler with the SA_SIGINFO flag to sigaction. Also remember that the realtime signals (SIGRTMIN+0 to SIGRTMIN+31) are free for user-defined uses.

When you get notified (or, indeed, at any time) you can call int gai_error(struct gaicb *) which will return 0 if the request is ready. A return value other than EAI_INPROGRESS is an error code which you can find as EAI_* in netdb.h. Once you know that a request has completed you can get the result from the ar_result member. And you will remember to call freeaddrinfo won't you?

Wed Jun 1 14:59:42 BST 2005

New page up about using Tor with Firefox 1.1

Sat May 28 23:41:38 BST 2005

First release of new project: pyGnuTLS

Tue May 24 11:08:46 BST 2005
Hmm, I wonder if it's getting too complex...
Thu May 12 17:36:19 BST 2005
Apparently I'm all wet!

Jeff Darcy replies to my last post:

What Adam seems to be considering is only a pure party-list system, in which there is no geographic representation at all, but thats not the only kind. In fact, under either an Additional Member System or Mixed Member System (from the copy of the Voting Systems FAQ that I've been hosting for two years), the exact balance between geographically-elected and “at” large candidates can be set anywhere from one extreme to the other just by adjusting the number of representatives selected each way. If the "my local representative works for me" dynamic is weaker under such a system, its by design.

When people shout “proportional representation”, that's what they mean around here. I didn't mean to suggest that other systems with proportional elements don't exist, but even in those systems my concerns still stand (to a greater or lesser extent, depending on the degree of proportionality).

Proportional representation is not a way to select MPs, it's a way to select parties. In a proportional vote you really might as well give the parties block votes and save the effort. Debates may be held, but a party has made its mind up by the time the bill reaches the house. (The quality of Commons debates is usually pretty bad as well.)

That brings me to a more general kind of question about arguments like Adams. Why is geographic representation considered so important anyway?

Geographic selection is much less useful and less needed now than ever before. But it still gets us a specific representative for each person in the country. (As you can guess, I quite like that.) I think there should be more feedback for a legislature than a single vote once every five years. I just don't see that a letter to "party headquarters" is the same. (Maybe I'm fooling myself in thinking that writing to an MP makes any more difference at the moment.)

So maybe we would be better off without a geographical basis. Let people vote for a single party and give that party voting power equal to number of votes/total number of voters. The party can then use their fraction to vote in a representative manner (possibly with internal voting procedures). We could all vote for the "Freedom loving geek party" and be happily represented.

(In fact, if a party were allowed to split their fraction into "yes" and "no" parts we could vote for a direct democracy party which would let its members vote on each and every decision and split the party vote accordingly. Direct democracy worries me because a great many of my fellow countrymen are really stupid. Several years ago I'm sure that a popular vote would have introduced the death penality for pediatricians, such was the public concern about pedophiles.)

This also leaves open the question of how the executive is selected. At the moment it's the leader of the biggest party (well, actually, it's up to the Queen, but she's pretty predictable). With a proportional system may well need to directly elect the executive too. Condorcet anyone?

But I'm unsure about proscribing such a change because there are likely to be lots of emergent effects. Thus my support for a fairly modest change (to approval voting) at first.

Wed May 11 16:48:05 BST 2005
Male Brains

A little while back the president of Harvard upset a lot of people by suggesting that men and women aren't identical. Steven Pinker and Elizabeth Spelke recently had a fantastic debate on this subject (though they are careful to call it a conversation for some reason). You should absolutely take the time to watch it.

As you can probably guess I started watching it with the opinion that there are important differences between males and females which go some way to explaining the ratio in top-tier academic positions. After watching it I still have that view, but I enjoyed Spelke's presentation as a mental exercise in picking apart arguments if nothing else.

I think that discrimination is stupid and wrong and I certainly don't support “affirmative action”. Discrimination is still wrong even when someone says “It's fine in this case because it's discriminating in favor of what I want”. It's amazing how quickly some groups turn about when the discrimination is in their favor.

Electoral Reform

Once again, many people are looking at the numbers of MPs vs. the percentage of votes cast and noting the sad difference that first-past-the-post brings. Proportional representation and STV are being shouted again.

Firstly, STV sucks[1][2]. It should never be used for anything.

Secondly, proportional representation means that no one is responsible for you. At the moment, you can type your postcode into TheyWorkForYou and find out your your MP is. Your very own MP and there's no discussion about who is responsible for listening to your concerns.

Party lists mean that many people are responsible for you, and that means that no one is. And they have to vote with the government because their job depends on it. MPs in this situation become so useless they could just as well give the parties a block vote and be done with it.

And, of course, there are lots of parties and lots of backroom dealings to form coalitions. Ick.

As a first step I think we should switch to constituency based approval voting to eliminate tactical voting and redistrict to make things a little more fair. It's a good first step and we can reassess things after a couple of elections under that system.

Thu May 5 16:25:14 BST 2005

(Lack of posting due to exams - which still aren't over)

Yep, I've voted. Actually I did it sometime ago since it was a postal vote. Not because I'm lazy, but because I live on the other side of the country. I actually quite like going to the polls and wouldn't postal vote given the chance.

Truly, this time, it was a question of the least bad option. There are no center-right options in this country. Because of that we'll wake up tomorrow with another Labour government, but with a reduced majority.

The country feels like it's standing in line at the supermarket. The queue is dreadful but, looking to the left and right - none of the others look like they're moving any faster. So you just stay where you are because you really don't want to jump queues and find out that the original one was a better option.

Never mind, because much more important to the future is the French vote on the EU Constitution later this month - and I don't even get to vote in that.

Wed Apr 27 15:57:02 BST 2005
Better typing through key maps

Everything I program these days is either C++ or Python and I'm sure that if a keyboard was designed by Python programmers it wouldn't be Qwerty. "Dvorak" they shout and I know that it works for some - but not for me. It screws me up too much whenever I use another computer.

However a few small tweaks have improved the comfort to Qwerty a lot for me. I have these mappings at Vim insert level (imap) so they only happen in one mode of one application, which works ok for me (though it is a pain when using the Python interactive console). They all consist of switching an unshifted keypress with a shifted one. (please note that I use a UK keyboard.)

Python

C/C++

For C++ I'm also looking at the '\'' and '#' keys and wondering if they could be put to better use.

Sun Apr 24 10:53:57 BST 2005
Parsers for network protocols

(If you wish, you can see this post as being related to the previous two. This is all about automatically building state machines, which happens to be very useful in Actor systems. If you can make all your actors stack-free then you can save memory, schedule them across worker threads etc. But, it also stands alone.)

Parser theory is well established. People usually reach for the parser generator when it comes to handling complex files. (Possibly not quite as often as they should if the state of some config parsers is anything to go by.) Parsers are understood and work.

Why then does no one use them for parsing network protocols? It's not like network protocols are too simple. Take section nine of the IMAP RFC. That's complex. Why does anyone want to write a parser for that when the EBNF is provided?

Did you know that the following is valid HTTP?:

GET / HTTP/1.1
Host:
  www.webserver.com

If you read the HTTP RFC and the EBNF definitions of LWS etc you can check it. It's the sort of thing that hand built parsers often miss. It doesn't work with whatever webserver /. is using for one.

These aren't simple protocols and, if you're coding in C/C++, chances are you'll screw it up in a buffer-overflowable or a seg-fault-killing-the-whole-process way. Parsers can do it better.

So why don't people use generated parsers? Probably because they've been designed for parsing files and all the toolkits are built around that idea:

Less importantly...

I've addressed the first four problems something I'm hacking up at the moment. Firstly, you can tell when any reduction happens as soon as you feed the data into the parser. So you could ask for the HTTP headers as they happen.

To explain the second problem, consider SLR type rules:

Token := TokenChar
Token := TokenChar Token

That parses one-or-more TokenChars into a single Token. But that leaves you with a parse tree like this for the input POST: ['P', ['O', ['S', ['T']]]]. The key to this are reduction functions which do something sensible with the parse tree as they're being generated. The above would be:

Token = OneOrMore(TokenChar, string_)

Where string_ is a special marker which says "It's a string, so give me something sensible in the parse tree" If nothing does what you need, you can define your own:

Token = OneOrMore(TokenChar)
def Token_reduce(values):
	return ''.join(values)

(and that does exactly the same thing.) Also, special forms like OneOrMore save you from thinking (too much) about how to write the grammar. OneOrMore is a simple case but something like Alternation(a, b) (a sequence of a, separated by b with optional bs at the beginning and end with reduction functions which give you a flat list of them) isn't.

So that's the evangelising for now. People should use parser generators for network protocols because who the hell wants to write another damm HTTP parser by hand (which you'll probably screw up)?

More tricks

Parsing is good. But there are more state machines in network protocols than just parsing. Take SMTP:

220 imperialviolet.org ESMTP
MAIL FROM: foo@foo.com
250 ok
RCPT TO: bar@bar.net
250 ok
DATA
354 go ahead
Subject: testing

Hello there!
.
250 ok 1114338782 qp 5232
QUIT
221 imperialviolet.org

Here a valid conversation can't have a DATA after the RCPT TO if the RCPT TO failed. So you could have the parser working line-by-line and a higher level state machine tracking the valid commands at this point etc. (I would admit that a per-line generated parser for SMTP would be overkill.)

So let us introduce two new terminals: T and ⊥ which are «command successful» and «command failed». We can inject these into the parser when we're finished processing a command and then define a conversation something like this:

RCPTTO = RCPTTOCommand
RCPTTO = RCPTTOCommand, ⊥ RCPTTO
SMTPConversation := HELOCommand, T, MAILFROM, T, RCPTTO, T, DATA, T

(That's not RFC at all, but humor me.) So a client can have as many failed RCPT TO commands as they like, but can't send a DATA command until one has completed. Thus, if the parser parsed it, it's a valid command and you don't need to keep track of the state yourself.

Thu Apr 21 23:14:51 BST 2005

Review: On Intelligence, by Jeff Hawkins

I finished this book with a sense of dissatisfaction. The author makes some fairly grandiose claims about advancing the state of AI and normally that would be the sign of a moonbat. However, I was impressed with what little I saw of his talk at Google last year.

Sadly this book is very dilute. There is some good stuff in there but I think that his co-author (almost certainly forced on him by the publisher) had been told to water it down for a more lay audience. About half the paragraphs in the book need removing.

There's something worthwhile in there. What I take away from this book is a resuscitated hope that there is a general algorithm for the brain. So many AI papers have claimed that their algorithm was the magic fairy dust that myself, and others, had mostly given up on the whole venture - conceding that the brain was inelegant.

But given the scant neurological evidence presented in the book what would have really sold me is a good computer implementation. He makes grand claims about the possibility of one without ever trying it out. There is something along those lines here, but nothing seems to be moving very fast.

Maybe the ideas here will shape a future AI revolution, but the author isn't fanning the flames with this book.

Wed Apr 20 20:32:51 BST 2005
Directions in future languages - edge triggered IO

This is a follow up to the actors model post, below. That was a fairly generic advocation and this is a specific demonstration of how to build a small part of such a system.

In an actors model (in mine at least) actors only ever block on message receive. Blocking on I/O is not an option and so one uses all the usual techniques of setting O_NONBLOCK. One actor in the system is special, however, and blocks on an I/O multiplexing call (select, poll etc). This actor sends signals to other actors when interesting I/O is ready.

So assume that the I/O actor is using select/poll to wait for events. Data arrives from the network, sending the descriptor high, and the poll call returns. The I/O actor fires a message off to the correct actor and carries on.

However, the next poll call will return immediately because the other actor probably hasn't had a chance to perform a read and empty the kernel buffers yet. So the only option is to remove the descriptor from the set of `interesting' ones and force any actor which wants to do I/O to reenable it with a message as soon as they have finished reading.

This is a mess as it involves lots of messages going back for forth. There is a better way.

Recent multiplexing calls (epoll under Linux 2.6, kqueue under FreeBSD and realtime signals under Linux 2.4) have an edge triggered mode. In this mode the call only delivers events on a rising edge. So if a socket becomes readable it will tell you once. select and poll will tell you whenever the socket is readable.

This is clearly ideal for an actors model. The I/O actor waits for edge notifications and sends messages. The descriptor doesn't need to be removed from the interesting set and another actor can read the data at its leasure.

In the case of flow control even the descriptor need not be removed. An actor can just ignore the edge message if it doesn't currently want more data. In the future it can read until EAGAIN and then start waiting for edge messages again.

Thus my actors which talk to a socket end up looking like this:

def run(self):
	send-message-to-io-actor-registering-my-interest-in-a-certain-fd()

	while True:
		if interested-in-reading-data:
			read-data-until-EAGAIN()

		message = receive-message()
		if message == edge-notification-message:
			continue
		...
Fri Apr 15 21:00:31 BST 2005
Directions in future languages - actor based concurrency

What I'm looking for is a quote from Zooko about why any kind of preemptive or co-operative threading model is unsettling. I can't find one so I'm going to make it up:

I don't like it because I can never be sure, from line to line, that the world hasn't just changed behind my back. -- not zooko

Which is true; tragically so in preemptive systems and enough to keep you on your feet in syncthreaded (co-operative) ones. I've long said that it's far too easy to screwup preemptive multithreading with its locks, deadlocks, livelocks and super-intelligent-shade-of-the-colour-blue locks. Syncthreading quietens down things enough for me and yet lets you keep the same flow-of-control.

(I like the flow-of-control. Fully asynchronous code with huge state-machines is a mess.)

But I've just dumped syncthreading for an actors model in my current project - mostly for non-concurrency related reasons. (And certainly not because I have a compulsive disorder which causes me to dump and rewrite any code which is starting to work .)

An actors model involves many threads co-operating via copy-everything message passing. It's an asynchronous pi-calculus if you like that sort of thing (which I don't). Copy-everything means no shared data and no locks. At all.

The majority of threads are short classes (a couple of pages of Python) with a simple specification. They are nearly all small state-machines which return to the same point after each message. There's a single blocking function - recv which gets the next message.

You break up threads in much the same way you break up functions. You get a feel for how much complexity should be contained in each one. The diagram on the right is taken from my project report at the moment - each block is an actor (and a single class) and they exchange messages with the other blocks that they're connected to. It may look a little complex, but that's smaller than the class inheritance diagrams in many systems.

Any the reasons for choosing it over syncthreading are a little odd for a concurrency model: modularity and unit testing.

I like unit tests. I don't think there's a whole lot of disagreement about their utility so I'm going to leave it at this: unit tests good.

It's always good and easy to write unit tests for some things - data structures are prime choice. Lots of scope for silly errors and no side effects. You can write a good data-structure unit test and feel good about yourself.

It's side effects which make unit tests difficult to write. If your code interacts with the outside world, bets are your unit tests are limited.

And that's the wonderful thing about actors - almost nothing interacts with anything else directly. It's all via message passing and that's eminently controllable by unit tests. If you want to test your timeout logic in a unit test it's not a problem. Timeouts are just messages like anything else and you can sent them in any order you choose.

Likewise I'm fairly sure that the code ends up being naturally more reusable. For all the same reasons; a lack of direct external dependencies.

My design is mostly taken from Erlang. There are a lot of links in the C2 wiki page but there was a very good talk about Erlang at the LL2 conference and they recorded it. It's real-media but mplayer copes with it for me.

Mon Apr 11 19:30:00 BST 2005
Market Forces

Market Forces is from the same author as Altered Carbon. I read the latter some time ago and, although I quite enjoyed it, I didn't feel that I'd actually gained anything by the end of it. I'm quite happy just reading for pleasure but, with every book there's a scale of effect, from the profound to these two. These are deeply unimportant books.

Market Forces is billed as some great anti-globalisation work and is meant to be about large multi-nationals who take sides in wars for a piece of the post-war pie. Frankly, our governments do this already and so the idea isn't very shocking.

But the focus is on the lead character, Chris Faulkner, who is a rising star in this corporate world. The book also tries to be a character development story, charting how power corrupts - or something. I didn't ever feel that I understood or empathised with this character. His fall from grace seemed more a series of random acts than a warning shot that we can all be driven to immoral acts in a corrupting environment.

And much of the action involves legal kill-or-be-killed car battles. These companies compete for deals by having the best drivers in a ritual combat. Why? I assume there's an answer in the author's head, but he certainly didn't write it down for the rest of us.

DVD: Battlestar Galactica

Battlestar Galactica [introduction mini-series][series 1] - yes it was that film that they show on TV at Christmas with the robot dog and the baddies who look like tin cans with the front of the Knight Rider car glued on. Forget it. This isn't a remake, it's a thousand times better.

Although has some of the same story elements as the original this show has great writers, great cast and looks beautiful. Watch it.

Thu Apr 7 17:57:58 BST 2005

Welcome to Britain. As soon as it gets Royal Assent:

I suppose we should be thankful that the ID cards bill got dropped:

"Labour's manifesto will confirm that the reintroduction of identity cards legislation will be an early priority after the election."

In other news

Sony patent takes first step towards real-life Matrix. The described device seems very interesting and probably worthy of the patent ... if it worked. Quoting from that NS text:

There were not any experiments done ... This particular patent was a prophetic invention. It was based on an inspiration that this may someday be the direction that technology will take us.

And reminding ourselves about the requirements for a patent:

An invention is the creation of a new technical concept and the physical means to implement or embody the idea. That physical means, prototype, or tangible form referred to as reduction to practice is what separates a discovery from an invention.

So; yet another result of the super-secret US patent office source code: while 1: print 'granted'

Sun Apr 3 12:20:06 BST 2005
Fixing LCD subpixel hinting

Subpixel hinting uses that fact that LCD displays have separate red-green-blue cells in order to triple the x resolution of the display resulting in nicer fonts etc.

However, this assumes that the system knows the ordering of the cells in the LCD, and this differs from system to system. If, when you look at your fonts, you see a red and blue tinge at the edges of vertical strokes, your font render has guessed wrong.

In that case, drop this little file into your homedir as ~/.fonts.conf, restart programs and everything will be fixed. You're welcome.

<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
       <match target="font">
               <edit name="rgba" mode="assign"><const>gbr</const></edit>
       </match>
</fontconfig>
Sat Apr 2 12:40:12 BST 2005

Google doubles GMail storage to 2GB - wrong. Should read "Google increases GMail storage to 2050MB and still counting" .

(Though looking at the page source suggests that it will stop at 2075MB)

Tue Mar 29 13:17:24 BST 2005
Why POSIX AIO has such a schizophrenic time

The Linux AIO effort is doing pretty well. It now has kernel interfaces to handle the AIO calls and the performance is looking pretty good.

Talking about AIO is often an odd experience because people don't realise that it does a completely different job to asynchronous network IO. There are two utterly separate problems that AIO can solve:

Networking calls have always had a non-blocking option since Berkeley sockets. Filesystem IO has never had one for some reason. This leads to programs which either handle filesystem congestion very badly or by using IO worker threads to try and cope in an otherwise single-threaded program.

For an example of the first, try having an NFS mounted home directory when the network fails. Everything drops into disk-wait because all the filesystem calls block for many minutes.

Networking calls have always been non-blocking because everyone knows that network calls can take ages to complete. But with the relative speed of modern CPUs/Memory against disks (and certainly network based filesystems) we really need non-blocking file IO.

Sadly the POSIX AIO API only deals with the first problem. open, getdents etc calls still block.

So, kernel developers, quit tweaking little things. We want some real progress! Give us non-blocking file IO which works with epoll.

Update: Something I didn't make clear. Non-blocking support is a stronger feature than high-performance support. If a kernel has non-blocking filesystem support it implies that it has high-performance support too.

Mon Mar 28 11:16:52 BST 2005
Dealing with too many config files

I've finally got round to doing something about keeping all my config files in sync across the five different hosts that I ssh to regularly.

I've created a SVN repository with my config files in and symlinked the dot files to the checked out version.

.zprompt -> .aglconfig/agldotfiles/zprompt-green
.zshrc -> .aglconfig/agldotfiles/zshrc
.zlogin -> .aglconfig/agldotfiles/zlogin
.zshenv -> .aglconfig/agldotfiles/zshenv
.vimrc -> .aglconfig/agldotfiles/vimrc
.gvimrc -> .aglconfig/agldotfiles/gvimrc
...

I can now keep them synced across all the boxes and I've a little tar ball in the repository as well which creates all the symlinks for me. I can borgify a new install with two commands now

I should have done this years ago.

Mon Mar 28 10:46:55 BST 2005
Future Battles

I'm sure that everyone who uses Firefox (1.0.2, keeping up with the security releases, right?) has discovered AdBlock. A more useful plugin doesn't exist (well, maybe greasemonkey with some good scripts) but I think we can expect that AdBlock is going to work a whole lot worse quite soon.

We've seen the efforts that some sites put into getting pop{up|under}s passed blockers. They don't seem to be doing too well from my point of view, but may be I just don't go to the right sites. None the less, they are fighting a loosing battle. Fundamentally the browser can stop Javascript on random sites from opening new windows - it's not rocket science.

The battle AdBlock is fighting is the other way round. For the moment, many sites are neatly organised with all their adverts in a directory called /ads/, or from a host called advertising.com. This makes AdBlock work very well with simple patten matching. But I soon expect that we'll see sites where every image is a random filename.

What do we do then? We could use greasemonkey scripts to rewrite the webpage as we like, right? We could remove the adverts and we can get rid of non-image adverts too (which AdBlock currently doesn't).

That's going to work for a while; probably a long time after AdBlock stops working due to the amount of effort required to create each script. Someone only needs to create it once, but there are a lot of websites and people will have to download and install these things etc.

I don't expect it will work forever. There's a strange idea amongst people who call themselves "content producers" that it's wrong for you to view their content in any way other than as they intended it. (For examples see Odeon reacting to an excellent scrape and most of the anti Google Autolink stuff recently).

It's more difficult to imagine how they're going to stop it but, as tools like greasemonkey become better, expect to see DOM obfuscators running in webservers. These will mess up the HTML differently for every GET request. The pages will look the same in a browser, but you won't be able to use nice class names and such to extract the bits you need.

(The greasemonkey script below would be far more complex if Google didn't neatly put search results into their own class.)

Within a few years I expect that we'll have AI-like filters to remove adverts and obfuscators+human workers doing their best to defeat them. Much as spam filters work today.

But that's a ray of hope because the efforts put into spam filters have paid off. I get 30+ spam messages a day (after simple blacklist filtering which removes a lot) and Gmail's filters have a 0% percent false-positive rate and maybe 1-2% false-negative. That's very good.

Site Map
/Root
     AlternateThe Weird and Wonderful
          BacklinksWhat are backlinks
          John GilmoreWhat's Wrong with Copy Protection
     ArchivesBlog Archives
          OneArchive 1
          TwoArchive 2
          ThreeArchive 3
          FourArchive 4
          FiveArchive 5
          SixArchive 6
          SevenArchive 7
          EightArchive 8
          NineArchive 9
          TenArchive 10
          ElevenArchive 11
          TwelveArchive 12
          ThirteenArchive 13
          FourteenArchive 14
          FifteenArchive 15
          SixteenArchive 16
          SeventeenArchive 17
          EighteenArchive 18
          NineteenArchive 19
          Twenty Archive 20
          Twenty OneArchive 21
          Twenty TwoArchive 22
          Twenty ThreeArchive 23
          Twenty FourArchive 24
          Twenty FiveArchive 25
          Twenty SixArchive 26
          Twenty SevenArchive 27
          Twenty EightArchive 28
          Twenty NineArchive 29
     PhotosPoor People Caught on Film
          Jack and the Beanstalk Jack and the Beanstalk
          RIP ScanResults of a Stage Scan Fire
          YosemiteYosemite National Park
     ProjectsIncomplete things from the lab
          Seagull's BaneLinux Automounter
          bttrackdBitTorrent Tracker
          CAPTCHACAPTCHA CGI script
          ConservConsole Serving
          DeerparkUsing Tor with Firefox/1.1 (Deerpark)
          DNSFixFixing DNS
          XoversXTA Crossover Control
          IAFSArchive Org Storage
          JBIG2JBIG2 Encoder
          VerifyPGP Key Verifier
          MaxFlowMaximal Flow in Python
          PyBloomBloom Filters in Python
          pyGnuTLSPython wrapping of GnuTLS
          SxmapApache SuEXEC Map
          HellardUnion Server Notes
     RecordingsFree recordings
          ICSM ChoirSt Paul's Church
     SchoolAncient School Stuff
     WritingsWho knows
          Cap SystemsCapability Systems
          IntroIntroduction to me
          SupremaJMC2 Group Project
          MP LettersLetters I've written to my MP
          SoundSound With Dramsoc
          SyncThreadingThe wonders of user-land threads