2004-11-25

CSS

Every time I play with CSS, I'm impressed. It was pretty easy to make my blog look more like something I would have come up with (though without Douglas Bowman's "Rounders 3" to start from, it would have looked much rougher).

I wonder what people who do CSS for a living use. Surely they don't use vi(1) as an editor and Google as a reference manual, as I just did?

2004-11-20

Blogger backup

So you're vain enough to have a blog. You're probably vain enough to want a backup of your words of wisdom. Here you go:

#!/usr/bin/ruby -w

if ARGV.length() != 1
print("usage: blogger-backup.rb <blog-name>\n")
exit(1)
end

blog_name=ARGV[0]
blog_uri="http://#{blog_name}.blogspot.com/"

print("Downloading #{blog_uri}...\n")
blog_content=`curl #{blog_uri}`

blog_content.split("\n").each() {
|line|
if line =~ /"(#{blog_uri}(\d+_\d+_\d+_\S+_archive\.html))"/
next_archive_uri=$1
next_filename=$2
system("curl -o #{next_filename} #{next_archive_uri}")
end
}

Note that each time this is run, it downloads each month of the blog from scratch. A kinder version might go back in time and stop when it finds one it's seen before, though that assumes you never update really old posts.

Update 2007-03-14: the version shown here is for "old" blogger; for a version that works with "new" blogger, and which also copes with images, download the salma-hayek library and check out bin/blogger-backup.rb, which will always be the latest version of this script.

2004-11-18

Cube Farm

There's a great word in German, Verriss. Ask Google, and you get this review of a Renée Fleming concert in Dortmund. Only review isn't the right word. Verriss is.

It's like "review", but with overtones of "scathing" and "contemptuous".

Imagine a Mac user forced to review Linux.

In fact, a good computer-related example is Jamie Zawinski's linux usability ...or, why do I bother, the article that taught me the word "fucktard". Search for "computer's speakers" for another laugh.

Anyway, now there's another computer-related example. Bill Blunden's "Cube Farm", an entertaining Verriss of a software company called Lawson.

I'd heard the name "Lawson" before, because they have an office next to Sun in Bracknell, GB. I hadn't a clue what they do, and if this book's to be believed, neither have they.

I knew I was going to enjoy this book when I got as far as the part where he's talking about the difficulties of getting a job as a physicist, and the paragraph "Adam Smith's invisible hand was giving me the middle finger" made me laugh out loud.

If you think reading about all the worst bits of all the jobs you've had amplified and rolled into one would be depressing, you're wrong. It's like background reading for "Fight Club". Instead of starting with the "Fight Club" premise, this book is a collection of evidence. Reasons why we might want to stand up and be counted with Tyler.

But forget society. What I want to know is how Lawson is still standing after the publication of this book. Either no-one believes it, no-one of importance has read it, or the acceptance of failure that Blunden talks about reaches far beyond Lawson.

And thinking about it, it wouldn't be so familiar and funny if we didn't recognize milder forms of the same diseases.

The scariest part is that even now, the author doesn't seem to know what a repository is. Google's automatic dictionary link for repository isn't very enlightening (and would be positively misleading to someone in a business like Lawson's). Maybe it wasn't just the guy who did the NT port who had to download the nightly source drops from an FTP server?

I did wonder if that, and other things such as the use of "dummy terminal" for "dumb terminal" wasn't just an attempt to be more authentic. "See: look how little I learned". Like misspellings on an Italian restaurant's menu, or waiters who put on an accent.

Maybe it's time I donned my tin-foil hat...

2004-11-17

NSSpellChecker, heap(1), and faking ispell(1) in 89 lines on Mac OS

There's an interesting dilemma when you use Java to write cross-platform software, because there are two conflicting notions of what "cross-platform" means.

It could mean that your program looks and works the same on each system. This might be good for someone who has to use the same program on a variety of systems, but only really has to use that one program.

It could also mean that your program looks and works as much like a native program on each platform as possible. This is probably better for people who also use other software on the various systems.

My editor uses ispell(1) to mark misspelled words, as a word processor might. On Linux, I can use apt-get(1) to install it. On Mac OS, I can't. I did try building from source, but it doesn't build out of the box on Mac OS 10.3, and as I was wondering what to do I realized that I don't really want ispell(1) anyway. I want my editor to use the same spelling checker that all the other programs on the system use.

So I knocked up a little program to imitate ispell(1) using use NSSpellChecker. It's 32 lines of C++ and 57 lines of Objective C. I couldn't use Objective C++ because my attempts to bootstrap GCC 4 killed my Objective C++ compiler. The C++ is the main loop, pretending to be ispell -a, and the Objective C takes care of talking to NSSpellChecker.

I haven't written much Objective C, and I still don't find its approach to memory management particularly natural, so I made two mistakes in this program, and that's what this post is about.

The Leak
The first mistake was one I assumed I'd made. I assumed there would be a leak, because there always is. I knew that objects returned by class methods are autoreleased, and I hadn't used anything else, so I couldn't see where the leak would be, but I was sure there would be one. To find out, I used heap(1). It's the command-line cousin of ObjectAlloc.app, whose praises I've sung in the past.

Given the process id of NSSpell, I could pipe the output of heap(1) to diff(1) or grep(1) and see what was going on.

hydrogen:~$ heap 16951 | grep String
NSCFString = 951 (27584 bytes)
NSAttributedString = 1 (16 bytes)
NSConcreteMutableAttributedString = 1 (16 bytes)
NSConstantString = 1 (16 bytes)
NSMutableString = 1 (16 bytes)
NSDebugString = 1 (16 bytes)
%NSCFString = 1 (16 bytes)
NSPlaceholderString = 1 (16 bytes)
NSPlaceholderMutableString = 1 (16 bytes)
hydrogen:~$

Watching the count of NSCFString instances, I could see I was leaking them. (As an aside, I've no idea what the "%NSCFString" instance is, and Google doesn't seem to know either.)

Thinking about it, I didn't know where or how the NSAutoreleasePool gets cleaned up. Turns out that if you don't use the usual event loop, you're supposed to release NSAutoreleasePools yourself (there doesn't seem to be a public way to reuse them).

I wanted to use boost::scoped_ptr<NSAutoreleasePool>, but even with Objective C++ that isn't an option. Damn you, Apple! Anyway, with a suitable hack in place, my leak was plugged.

The Double Free
Everything seemed fine until I was actually using it, when all of a sudden my editor hung. I killed the NSSpell process so I could finish what I was doing, and didn't have chance to come back to the problem until just now.

I just typed random junk into my editor until it hung, and then I attached gdb(1) to see where we were:

(gdb) bt
#0 0x90010664 in write ()
#1 0x9009e2c0 in malloc_printf ()
#2 0x90001aac in szone_free ()
#3 0x90190d30 in CFRelease ()
#4 0x909f15d4 in NSPopAutoreleasePool ()
#5 0x00003710 in NSSpellChecker_showSuggestions (word=0x130afc "slkjdfhskjfskdjf") at NSSpellChecker.m:29
#6 0x00003300 in main ()
#7 0x00002de0 in _start (argc=1, argv=0xbffffc14, envp=0xbffffc1c) at /SourceCache/Csu/Csu-47/crt.c:267
#8 0x8fe1a558 in __dyld__dyld_start ()
(gdb)

This suggested that the malloc library was detecting a problem during the destruction of the NSAutoreleasePool. It was hanging trying to write its diagnostic. Why? Because it was connected to the editor's pipe, and the editor wasn't seeing what it expected. Killing NSSpell causes an IOException, and the editor gives up talking to the spelling checker and carries on as normal. Minus spelling checking.

Running NSSpell from the terminal, we get to see the complaint:

*** malloc[22264]: error for object 0x130a40: Double free

And, sure enough, I was calling autorelease on an object returned from a class method. I know consciously not to do that, but I don't know it intuitively. I don't understand why a seemingly simple rule doesn't sink in. Perhaps because I do very little Objective C, with such long intervals in between, and during those intervals I'm writing Java and C++, with two different, simpler solutions of their own.

The syntax may be cool, but Objective C's memory management sucks compared to C++ (with boost, for when you have to use pointers) or Java.

Something I found particularly interesting here is that it's actually harder to debug a problem with programs communicating via a pipe than when they're talking through a socket. In the latter case, you have Ethereal, but there's nothing as convenient for pipes.

I guess this is the future.

Apart from the manual memory management crap.

(Calling Objective C's solution semi-automatic is misleading. Java is semi-automatic, if you consider the thought that goes in to avoiding excessive allocation and unwanted heap retention. C++ is part-automatic, if you consider the strong need for discipline, and the extra care needed if you're using pointers. Objective C is only automatic if your objects have no fields. On the bright side, the tools are pretty good.)

Anyway, the working version of the fake ispell(1) is checked in to salma-hayek, available off the Edit page.

2004-11-14

Steaming Hardcore '92

Not only does 1992's "steaming hardcore" sound about as hard as Celine Dion, stuff like Saint's "Show me Heaven" (http://www.discogs.com/release/147663) is what I think I was listening to in the early 1990s, and yet it's all of a year old.

Mmm... Google suggests that maybe I was listening to it then, because that's when the original was released.

But no, it can't have been that version, judging by Google's claim that it was in Days of Thunder.

How confusing.

Why do i remember everything as having been faster and more exciting than it really was? Like the speeder chase in "Star Wars: Return of the Jedi", which I remember as being awesome, but which totally sucks if you watch it today. I was struck by the similar scene in The Incredibles, which manages to be as fast and exciting as I remember the "original".

I remember back when I was young enough for Star Wars to appeal to me thinking how one day we'll have computers good enough that I'll be able to play a speeder chase game at home that's as good as what I'd seen in the cinema. And now Pixar come along (with an insane render farm) and show me that, no, I can have something far better. Who'd aim lower?

If you stick around to watch the closing the credits, you can see part of the chase in -g instead of -O2 form. Wireframe 1980s goodness. But it still looks pretty cool, and it's nice as a geek to get a glimpse of what it's like to work on that stuff.

I think "The Incredibles" may be what computer animation was born to do. I've never seen a more convincing superhero movie. Unconstrained by physical reality, these superheroes really were super. Yet never incredible.

At the same time, there was some impressive acting. I thought the scene with Elastigirl and her kids on the plane was amazing. Her fear and concern was more affecting than most live action. Nothing's had me so eager to get in and help since the scene in Lilja 4-ever where the little boy Volodya decides to kill himself.

I realize now, thanks to imdb, why Irena Neski in The Bourne Supremacy looked so familiar. That was another movie I was reminded of, because it contains the car chase to beat all car chases. Which was a set-piece "The Incredibles" did surprisingly badly.

But there's no shame in losing to "The Bourne Supremacy". It's the best movie of its genre I've ever seen. And the car chase in particular is something that's stuck with me. The way you see the other guy's car coming from the side at the same time Jason does? Brilliant. The fight with the rolled-up magazine? The way he hurts his leg and limps for the rest of the movie? The tower block where Lilja/Oksana/Irena lives; the way it's bleak and desperate, and beautiful at the same time?

I should make an effort to see more of Paul Greengrass' work.

Making hidden applications look hidden in the Dock

When you hide an application with C-H, the Dock can show that the application's hidden by decreasing the alpha value of the application's dock icon. I'd forgotten this isn't how Mac OS ships, but it isn't, and not having it has been annoying me for the last couple of weeks. I've finally got round to digging out what you have to do. Simply run:

defaults write com.apple.Dock showhidden -bool true

You need to kill Dock to see any change.

Interestingly, any Java application that was using -Xdock:icon to get a dock icon will come back with the wrong icon; some kind of variant of the Terminal icon. Turning magnification on shows that the word in tiny green text reads "exec".

How very l33t.

2004-11-07

Why I can't get excited about JDIC

I was wondering this afternoon why it is I find JDIC, the Java Desktop Integration Components, the least interesting of the things I have on my to-do list. And I realized it's because they're really the Java MS Windows Integration Components.

Suppose you're a Mac user, like I am. I also use Linux a lot, but it's such a mess that "desktop" seems stretching the point. "Display", perhaps. If there was a single KDE program worth running, I could quite easily have an Xt application, a Java application, a GTK+ application, and a KDE application sharing the same display. There's probably someone out there still using Motif, also. That kind of hodge-podge does not a desktop make.

So, like I said, suppose you're a Mac user. Suppose also that you're either a Java developer, or the user of a Java application. The question is: what doesn't Java let you do that hampers your Java application's interface?

First some things you can do:

1. You can open a URI in the user's default browser, or open any file in the user's preferred application, courtesy of java.lang.Process and open(1). You can send mail this way too, using a mailto: URI. This is what org.jdesktop.jdic.desktop would give you.

2. You can write an Info.plist for your application that tells Mac OS what file types to associate with you. This is what org.jdesktop.jdic.filetypes would give you.

And some things you can't:

1. Badge your Dock icon to show unread counts/activity/state.

2. Have a dynamic Dock menu, with items specific to your application.

3. Have a standard response to the "About..." menu item. [Apple provides API for this.]

4. Bring up a preferences dialog in response to the "Preferences..." menu item. [Apple provides API for this.]

5. Add application-specific items to your application menu (like Safari's "Report Bugs to Apple...").

6. "Show In Finder". [You can use open(1) to show the directory, but you can't select the relevant file within the directory.]

7. Use the system's spelling checking (and the user's dictionaries). [I've written something to use ispell or NSSpellChecker but that's not the same, and doesn't work on Mac OS as-shipped, or on MS Windows.]

8. Make a JDialog a sheet of its owner.

9. Implement the "Special Characters..." item on every other application's "Edit" menu.

10. Open a file chooser that looks like the native ones and lets you specify file formats. You have to choose between one (AWT) and the other (Swing) because of a dubious argument made long ago, and presumably some corresponding argument for "Save As".

11. Maybe I dozed through it, but where's java.awt.FontDialog? Even an unconvincing javax.swing.JFontChooser would be a start.

12. Automatic support for the "Window" menu. And the "Open Recent" menu item on the "File" menu.

13. Automatic support for an application having a life beyond its open windows/better understanding of a world where there's only one menu bar.

14. Access to the system's address book. [Unlike iCal, Address Book doesn't store everything as text. And as I found out, iTunes only pretends to.]

15. Easy Rendezvous/ZeroConf. This may sound Mac-specific, but iTunes uses it whatever platform it's running on, and that only makes it cooler.

Those are things that would make a difference. Those are all things I've actually wanted. Until I could write TextEdit or Sketch in pure Java, I won't feel integrated. I'm sure MS Windows users and developers have their own lists. I assume that the "system tray", browser embedding, and screen-saver support in JDIC represents what they care about (but although the last two apply to Mac OS, I don't really see that they're particularly useful; especially not the screen-savers).

2004-11-03

Java already has too much operator overloading for me

This caught me out this evening:

public class Quote {
public static void main(String[] args) {
int i = 10;
String s = "";
s += i + '"';
System.err.println(s);
}
}

It was less obvious what the mistake was in my real program, though, because i wasn't a convenient constant, so (i + 34) wasn't so easily recognizable.

Given that it's rare that you want to do this, and no effort to cast the char to an int, I'd really rather int + char was disallowed.

Using + for string concatenation was never the brightest of ideas. It's tricks like that, and using + to mean add on a GUI container or push_back on a sequence, or union on a set, that give operator overloading its bad name.

So it's odd to see it in a language with no user operator overloading, and it's not obvious why we should believe that it's okay for append on a string.

Interesting to see that my brain was in C++ mode here. I spelled boolean as just bool several times, and the particular mistake above looks as if I was really trying to write something more like:

std::ostringstream result;
result << i << '"';
std::cerr << result.str() << std::endl;

2004-11-01

TexturePaint does not play well with large images

The JavaDoc for TexturePaint says "The size of the BufferedImage object should be small because the BufferedImage data is copied by the TexturePaint object."

I took that to be a warning about memory use, shrugged, and went ahead and used it anyway.

What it actually means is that for a 2048x1024 pixel image, TexturePaint is unusably slow. The cost seems to be per-operation, so if all you want is a single fillRect, it's acceptable. If you want to draw lots of lines, as I did today: forget it.

My home-grown code to draw textured horizontal lines using BufferedImage.getRGB and BufferedImage.setRGB can draw 2,000 lines in 0.7s, which isn't the kind of performance you show off about, but it's good enough for my application. Using Graphics.drawLine and a TexturePaint took minutes.

If I halve the size (and quarter the area) of the images I'm using to a size that actually fits on my 1920-pixel wide display, the 912 lines draw in 0.1s, which is as good as I could want.

Anyway, that's sunclock/xsunclock/kworldwatch ported to Java. I can now tell you that everyone I'm in regular contact with is in the dark. If only literally.