2005-01-27

Copying HTML to the clipboard from Java

It's rare that I find myself faced with something that works perfectly with Apple's Java implementation and is broken on Linux and Windows. It's not often true the other way round, but Apple has been playing catch-up, and we still don't have 1.5.0 on Mac OS.

But I'm pleased to report that you can copy/paste HTML to/from the clipboard on Mac OS, even though it doesn't seem to work on Linux, and someone else says it doesn't work on Windows (Java bug #4765240).

Note that what I'm talking about is being able to paste styled text into a native application. So rather than "hello <b>world!</b>", you get "hello world!".

If you run the oddly-named "PasteHorkTest" from bug #4765240 on Linux and try to paste from Mozilla Firefox, you'll find that there seems to be a problem with the character encoding. It looks like the clipboard has 2-byte Unicode data (I can't remember which endian), because each recognizable character is separated from the next by the square that Sun's implementations use to render characters they have no glyph for. It may be that the HTML tags would be correctly interpreted if it weren't for this encoding problem, but whatever, it's broken.

If you run the same .class file on Mac OS, you can paste from Safari or Mail just fine.

I couldn't find anything useful on the web about copying HTML to the clipboard from Java, perhaps because the most common case – copying from a JEditorPane – is already dealt with by Swing. I knocked up some code based on javax.swing.plaf.basic.BasicTransferable but using the collections classes rather than primitive arrays, for clarity:

import java.awt.*;
import java.awt.event.*;
import java.awt.datatransfer.*;
import java.io.*;
import java.util.*;
import javax.swing.*;

public class html {
public html() {
JButton b = new JButton("copy");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
copy();
}
});

JFrame f = new JFrame();
f.getContentPane().add(b);
f.pack();
f.setVisible(true);
}

public static void main(String[] args) {
new html();
}

private void copy() {
Transferable t = new HtmlSelection("<html><p>Hello <b>world!</b>");
Toolkit.getDefaultToolkit().getSystemClipboard().setContents(t, null);
}

private static class HtmlSelection implements Transferable {
private static ArrayList htmlFlavors = new ArrayList();

static {
try {
htmlFlavors.add(new DataFlavor("text/html;class=java.lang.String"));
htmlFlavors.add(new DataFlavor("text/html;class=java.io.Reader"));
htmlFlavors.add(new DataFlavor("text/html;charset=unicode;class=java.io.InputStream"));
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
}
}

private String html;

public HtmlSelection(String html) {
this.html = html;
}

public DataFlavor[] getTransferDataFlavors() {
return (DataFlavor[]) htmlFlavors.toArray(new DataFlavor[htmlFlavors.size()]);
}

public boolean isDataFlavorSupported(DataFlavor flavor) {
return htmlFlavors.contains(flavor);
}

public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException {
if (String.class.equals(flavor.getRepresentationClass())) {
return html;
} else if (Reader.class.equals(flavor.getRepresentationClass())) {
return new StringReader(html);
} else if (InputStream.class.equals(flavor.getRepresentationClass())) {
return new StringBufferInputStream(html);
}
throw new UnsupportedFlavorException(flavor);
}
}
}

This doesn't work on Linux, if you want to paste into a native application. I assume that it doesn't work on Windows either (but I don't use Windows, so I don't know).

On Mac OS, it works like a charm. Click the button, and you can paste styled text into Mail. Which is exactly what I wanted to be able to do. Annoyingly, inline CSS (or any other kind of CSS, as far as I can tell) doesn't work. And I really need CSS for what I want to do. It's strange, because I can copy this from Safari to Mail just fine:

 White Text on a Blue Background 

Anyway, feel free to copy & paste the HtmlSelection class into your own software!

[If you don't want deprecated code, you can remove the support for InputStream and the copying still works fine on Mac OS, which as far as I know is the only platform where this works at all. Note also that I'm not really gloating here; I'd much rather this worked on the other platforms.]

2005-01-23

A Java equivalent of heap(1)

I read the claim recently that a lot of heap is wasted on zero-length arrays of primitive types. I was interested to know whether this was true, so I modified a program I've been meaning to mention for a while so that it could check whether this sweeping claim was true. (Update 2007-03-17: If you just want to use something like this, upgrade yourself to Java 6 or later and use "jmap -histo ". The private API below has changed a bit by Java 7, too.)

Some interesting stuff appeared in Java 1.5's tools.jar and sa-idl.jar (the latter may be sa-jdi.jar or simply part of tools.jar depending on your particular JVM/platform). I haven't seen it documented anywhere; unsurprisingly, since it's undocumented API. What I mean is, I haven't even seen it mentioned anywhere. And it's too cool not to mention. In case you didn't know, jps(1) can be written in a few lines of Java. Here's roughly what it might look like:

MonitoredHost localhost = MonitoredHost.getMonitoredHost("localhost");
Set ids = new TreeSet(localhost.activeVms());
for (Object id : ids) {
MonitoredVm vm = localhost.getMonitoredVm(new VmIdentifier("//" + id));
String name = MonitoredVmUtil.mainClass(vm, false);
System.err.println(id + "\t" + name);
}


More interesting than that, though, is that you can iterate over all the oops [Ordinary Object Pointers] on the Java heap. Your visitor's prologue method is invoked once at the beginning, then doObj is invoked once for every oop, and finally the epilogue method is invoked once when it's all over.

Here's a short program to do a job similar to Apple's heap(1) (though the output is slightly simpler, for the benefit of other programs):

import java.util.*;

// Found in "sa-jdi.jar"...
import sun.jvm.hotspot.bugspot.*;
import sun.jvm.hotspot.oops.*;
import sun.jvm.hotspot.runtime.*;

// Found in "tools.jar"...
import sun.jvmstat.monitor.*;

public class HeapDump implements HeapVisitor {
private BugSpotAgent agent;
private HashMap<Klass, KlassInfo> map;
private int objectCount;

private int zeroLengthPrimitiveArrayCount;

private long startTime;
private long endTime;

private HeapDump(int pid) {
agent = new BugSpotAgent();
agent.attach(pid);

VM vm = VM.getVM();
System.err.println("Running on: " + vm.getOS() + "/" + vm.getCPU());
System.err.println("VM release: " + vm.getVMRelease());
System.err.println("VM internal info: " + vm.getVMInternalInfo());

ObjectHeap heap = vm.getObjectHeap();
System.err.println("ObjectHeap = " + heap);

heap.iterate(this);

long duration = endTime - startTime;
System.err.println("Time taken: " + duration + "ms");
System.err.println("Speed: " + objectCount/duration + " objects/ms");
System.err.println("Objects on heap: " + objectCount);
System.err.println("Unique classes: " + map.size());

agent.detach();
}

public void prologue(long x) {
map = new HashMap<Klass, KlassInfo>();
objectCount = 0;
startTime = System.currentTimeMillis();
}

public void doObj(Oop oop) {
++objectCount;
Klass klass = oop.getKlass();
KlassInfo info = map.get(klass);
if (info == null) {
info = new KlassInfo(klass);
map.put(klass, info);
}
++info.instanceCount;
info.totalByteCount += oop.getObjectSize();
if (oop instanceof TypeArray) {
long arrayLength = ((TypeArray) oop).getLength();
if (arrayLength == 0) {
System.out.println(klass.signature() +
" array of length " + arrayLength +
" (" + oop.getObjectSize() + " bytes)");
++zeroLengthPrimitiveArrayCount;
}
}
}

public void epilogue() {
endTime = System.currentTimeMillis();
dumpPerKlassInfo();
}

private void dumpPerKlassInfo() {
long overallByteCount = 0;
for (KlassInfo info : map.values()) {
overallByteCount += info.totalByteCount;
System.out.println(info);
}
System.out.println("Overall bytes on heap: " + overallByteCount);
System.out.println("Zero-length primitive arrays: " +
zeroLengthPrimitiveArrayCount);
}

public static void main(String[] args) throws Throwable {
if (args.length == 0) {
System.err.println("usage: HeapDump <pid>");
MonitoredHost localhost = MonitoredHost.getMonitoredHost("localhost");
Set ids = new TreeSet(localhost.activeVms());
for (Object id : ids) {
MonitoredVm vm = localhost.getMonitoredVm(new VmIdentifier("//" + id));
String name = MonitoredVmUtil.mainClass(vm, false);
System.err.println(id + "\t" + name);
}
System.exit(0);
}
for (String pid : args) {
new HeapDump(Integer.parseInt(pid));
}
}

private static class KlassInfo {
public Klass klass;

public int instanceCount = 0;
public int totalByteCount = 0;

public KlassInfo(Klass klass) {
this.klass = klass;
}

public String toString() {
return instanceCount + "\t" + totalByteCount + "B\t" + name();
}

private String name() {
Symbol symbol = klass.getName();
return (symbol != null) ? symbol.asString() : "?";
}
}
}

The bad news is that it's slow. Whereas Apple's heap(1) returns almost instantaneously, when pointed at my editor (which had been running for a week at the time), HeapDump took about 40s on a fast Linux/x86 box. It manages about 30 objects/ms.

Here's the tail of the output when pointed to a smaller program, a simple dual time zone clock:

807 19368B java/awt/Rectangle
1 16B sun/font/CMap$NullCMapClass
27 1512B sun/nio/cs/StreamEncoder$CharsetSE
17 544B sun/font/FontStrikeDesc
1 24B java/lang/NullPointerException
31 744B java/awt/datatransfer/DataFlavor
9 288B sun/reflect/UnsafeQualifiedStaticIntegerFieldAccessorImpl
2 48B java/util/regex/Pattern$TreeInfo
1 8B sun/net/DefaultProgressMeteringPolicy
2 32B java/awt/ImageCapabilities
1 32B [Ljava/util/Map;
1 32B sun/awt/image/OffScreenImageSource
Overall bytes on heap: 18265616
Zero-length primitive arrays: 534
Time taken: 10468ms
Speed: 26 objects/ms
Objects on heap: 275170
Unique classes: 776

For that Java VM, a zero-length array of any primitive type takes 16 bytes. So that's just over 8KiB out of 18MiB. Even for my editor, only a few KiB were used by zero-length arrays of primitive types.

By the way, pointed at Keynote, Apple's heap(1) produces output like this (excerpted):

hydrogen:~$ time heap 2273
Process 2273: 1 zones
Zone DefaultMallocZone_0x400000: Overall size: 31340KB; 176007 nodes malloced for 21929KB (69% of capacity); largest unused: [0x01b0d400-5099KB]
All zones: 176007 nodes malloced - 21929KB

. . .

Found 3067 ObjC classes in process 2273

. . .

ADVersionHistory = 1 (16 bytes)
PBExQuickTimeMovieData = 1 (16 bytes)
_NSDrawingThreadData = 1 (16 bytes)


real 0m2.760s
user 0m1.720s
sys 0m0.380s

So heap(1) is ten times faster for a not incomparable process. I don't know if this is because heap(1) itself does something particularly clever, or maybe the Objective-C runtime does some book-keeping to make this easy, or maybe sun.jvm.hotspot.oops just isn't ready for the prime time yet. Which would explain why no-one seems to have mentioned it.

Bummer though; I was hoping to write something like Apple's ObjectAlloc, but this is way too slow for that.

2005-01-22

GTA Shaftesbury Avenue

There have been ads on TV for Getaway: Black Monday for weeks now. Every time I see one, I wonder if the silicon valley audience has any trouble with the language. Or the accent, which is somewhere between "The Bill" and "EastEnders". One ad goes something like "shooters ... motors ... blaggers ... coppers ... and a little bit of how's your father".

The Wired review confirms my suspicions: "the dialog, though well-acted, is sometimes utterly incomprehensible due to its heavy use of thick London slang".

I also wonder what the image of someone shooting up a tube station will do for tourism, but Wired's review doesn't mention whether it makes London seem more or less attractive. (To me, 28 Days Later was as attractive as London ever looked. London would be great if it weren't for all the crowds.)

I couldn't help but laugh at "London's signature brightly colored police wagons". Police wagons? Sounds like something from Judge Dredd! And from my position as an Englishman in Santa Clara, it's odd to imagine that our police cars could ever stand out the same way as the CHiPs motorbikes and big, bouncy, screechy-tired black and whites they have round here.

I'd love to play Getaway, though. At least in free-roaming mode. Wired's screenshots look convincingly London-esque, but I want to know whether I can drive past my sister's house or the new Apple store, or ride down the steps from the Albert Hall and visit that restaurant next to the Goethe Institut.

They should put it on the web. It would be way cooler than multimap to see a drive-through of your journey. Or a neat screensaver, with a car driving much too fast through night-time traffic.

Where's the Mac version?

iWork '05

iWork '05 arrived yesterday. I bought it because it was cheap, and I was curious. I rarely have any need for a word processor, and I even more rarely have any need for presentation software. I think it's been one year and three years, respectively.

It comes on a single DVD, in a box made surprisingly heavy by the inclusion of a couple of stocky little manuals and other bits of dead tree. The same manuals are available in PDF form from the applications' Help menus. I haven't read the manuals, because much of my interest in these programs is in the walk-up experience.

Keynote
Keynote is impressive. I'm not sure I've ever used PowerPoint except as a reader; I seem to remember the couple of times I tried, I ended up going back to LaTeX. Keynote's "White" theme is very similar to what I used to produce with LaTeX. The other themes, though very attractive, seem more like demos to me. I can't imagine using them without being very self-conscious at having used a ready-made template; of having gone to the effort of choosing a template, but not having gone to the effort of creating one.

Keynote fails my two geeks tests by default; it doesn't have a master slide for "Title & Code", and it's not obvious to me how you'd enter little bits of mathematical expressions.

I still don't know what you're supposed to do about the latter, but the former problem is easily solved. The first non-obvious bit is that there's no menu item for "New Master Slide". You have to choose View > Show Master Slides, select one to copy, and then the Slide > New Slide menu option magically changes to New Master Slide. Apple do that quite a bit, and I don't like it when I'm just starting out with a new application, because it makes it hard to discover all the functionality. The second non-obvious bit is that you can't just use the Text toolbar icon (it seems a stretch to call them buttons, since they look nothing like buttons) to add the code area. You have to choose the Slide Inspector, choose its Appearance tab, and enable Body, which you then edit.

I guess slide masters are supposed to be an advanced feature, and not the first thing you use. But they should really have a LaTeX-like theme if they want that to be true for geeks. I'm always surprised that geeks seem to support themselves quite badly. Geeks make heavy use of mail, but MS Outlook, Evolution, and Thunderbird are all abysmal. Spelling checkers don't cope with CamelCase, even though it's easy ("Checking Spelling in Source Code", PDF). Presentation programs make it unnecessarily awkward to include code snippets. And document preparation programs (with the notable exception of Mathematica) make it difficult to link to code so you can conveniently show code snippets and the result of running the code without having to have an out-of-date duplicate in the document.

Maybe mathematicians, scientists, and computer programmers are all too tight to pay for this stuff, and too lazy to write it for themselves? Maybe it's only Men In Suits who buy this stuff, and all they need past bullet points is Standard Marketing Graph #1, the one that goes up exponentially and has success at now + 3 years.

(I laugh, but I don't want to imply that some of them aren't very good at what they do. Just the other day, one watched a group of engineers give a presentation, and blurted out "don't you guys rehearse?" not realizing that engineers have about as much clue when it comes to presentations as presenters have when it comes to engineering. Everyone underestimates the difficulty of the stuff they're good at, never mind the value of experience.)

Pages
Pages seems a bit awkward to me at the moment. It doesn't help that there isn't an obvious template to start from for someone who was basically happy with LaTeX. The HTML export produces output that Safari can't render correctly, judging by a few of the default documents I exported.

As with Keynote, it seems to me a mistake to concentrate on having very fancy templates to select from rather than concentrate on making it really easy to make your own template. I've long been intrigued by the idea of meta-layout rules; having the computer help you design a good layout. There are books on this stuff, and it doesn't seem like it would be too hard to codify.

For now, I'm hoping that someone (if not Apple) will come up with a LaTeX-like template for Pages before I next have to write something.

2005-01-17

Exposé

At MWSF2005, Steve Jobs said that 'most everyone uses Exposé. Drunken Batman begs to differ.

Me? I agree with the guy wearing the utility belt.

Even when I know Exposé is a better solution to my problem, such as when I want to switch Safari window, I'll either cycle through the windows with C-` and C-~ or, if there are too many for that to be practical – and that's exactly when Exposé would be most appropriate – I'll use the "Window" menu.

Sometimes I spend long enough reading through the different window titles that I'll actually think "hang on; if I hit F10, I could just click on the right picture". But most of the time, it's only as I'm going back to what I was doing that I think "that would have been a job for Exposé".

I don't know how you get to the point where you reach for Exposé first, but I wish I did. I could try removing the "Window" menu from Safari's MainMenu.nib, but I don't like the way Interface Builder says "8 inconsistencies were found" when I open the nib. It seems to work okay on Calculator (where Interface Builder finds 3 inconsistencies). I find it kind of odd, personally, that you can do this. I don't understand why Cocoa doesn't add stuff like the "Window" menu for you, as it does the "Special Characters..." option on the "Edit" menu.

Anyway, as it is, the only part of Exposé's functionality that I automatically reach for is hitting F11 to slide all windows out of the way so I can see the desktop.

Maybe that's all Steve meant.

[Update: Ed Porter and Morgan Schweers both suggest using Active Screen Corners as a more convenient alternative to the function keys, especially on iBook/PowerBook keyboards. Ed claims not to use C-tab, which suggests he tends to have fewer applications open than I do. Anyway, time to set up some Active Screen Corners and see how that goes...]

2005-01-15

MS' Mac Business Unit continues to not get it

For all the noise they make about being Mac people, Microsoft's MBU don't seem able to turn out anything decent. The latest version of MSN Messenger, shown in this MacWorld news item continues to appall rather than appeal.

Part of it's Apple's fault. There's no set of standard icons that developers are encouraged to use, so although it would make sense to use either the Mail.app icon or Mail's "Compose" icon for Messenger's "Mail" button, they probably didn't feel in a position to do so. Which means they got Microsoft artists to draw the icons and, unsurprisingly, they end up with a bunch of icons that somehow manage to look out of place with their Microsoft house style. (I'm not an artist, so I can't really explain what I mean, but there's a very unsubtle use of color in Microsoft icons. They're less uniform in shape than Apple's ones, which tend to leave the impression of a full square. And Microsoft icons have a habit of using a different set of analogies to the familiar Apple ones.)

My favorite, though, is the bulbous baboon's backside of a "Send" button. The oversize "Send" button looks odd enough in the original MS Windows version, but at least there the button's flat and drawn in a washed-out color, and so manages to remain relatively inoffensive. (What's the send button for, anyway? Who broke their return key?)

Interesting that they use a drawer, just as Apple have removed the one from Mail (judging by MWSF2005). Drawers sounded like an interesting idea at first, but I can't think of an application where they've really worked out. Everything I use falls into one of two classes: the always-open class, like Mail and Preview, where you need the drawer's contents to use the program, and all the rest, where you never open the drawer and don't even necessarily remember that there is one.

I'll stick with AdiumX. It manages to look perfectly at home.

Seeing the speed of sound

Everyone knows that light travels quickly and sound travels slowly. Those people who know that light and sound aren't instantaneous, anyway. It's odd, that: I don't really believe in action at a distance because I can't see how it would work, and yet talking to people with no computational/mathematical/scientific bent, action at a distance seems to be the natural assumption.

I guess it leaves more room for their gods.

And it's not that unreasonable an assumption, most of the time. I had proof of its reasonableness this week, when – for the first time I can remember – I was able to experience the relative sluggishness of sound, after almost thirty years on Earth.

I was sitting about 300m, I guessed, from a digging machine. This digging machine was using its scoop to bang a girder into the ground. I'd see the contact, wait almost a second, and only then would I hear the sound.

Weird! I must never have sat an appropriate distance from repetitive construction work before.

Google confirmed my memory from school of the speed of sound at sea level. The US may be notorious for its use of archaic units (and how embarrassing that they should refer to them as "English"), but at least the geeks at Google have the sense to respond to a search for "speed of sound" with "speed of sound at sea level = 340.29 m / s".

When I mentioned my excitement to someone, they said "what about thunder and lightning?" but that seems different because I for one have no idea where the thunder or the lightning are. They're in visible/audible range, but where exactly? And it's not quite as convincing that they're the same event; I couldn't swear to knowing anything about thunder or lightning. Whereas I think I understand the basics of banging one piece of metal against another.

2005-01-08

Monaco FontMetrics

There's been a problem in Terminator when using the Monaco font on Mac OS. Unfortunately, Mac OS doesn't have Lucida Sans Typewriter, the only other monospaced font worth using, so this needed to be fixed.

(Until today, I was unaware that Kris Holmes was involved with both. Hats off to the Hermann Zapf of monospaced fonts!)

I had assumed, based on previous experience with incorrect widths for anti-aliased text in Apple's Java implementation, that the problem was Apple's, not ours. Today I sat down to write a convincing demonstration of the differences between Monaco and Courier's FontMetrics suitable for submission as a bug. What I found was that the reason we were having trouble with Monaco is that it has non-zero leading.

One part of our code, when trying to work out where to put the baseline, assumes that FontMetrics.getHeight is equivalent to getAscent + getDescent, but it's actually equivalent to getAscent + getDescent + getLeading. It's just that, for most fonts, the leading is zero.

If I'd found a page suggesting this the first time I asked Google for "Monaco FontMetrics", I could have had this fixed a long time ago. The fix itself took just seconds, including the time to find the erroneous code!

Insolent bc(1)

I read Jonathan 'Wolf' Rentzsch's recommendation of Longhand with interest because I'm in a similar situation. I've used many calculators (both physical and virtual) in my time, but I still haven't found the one I'm completely happy with.

I have a Calculator window open right now; a testament to the fact that if your job is sufficiently simple, Calculator doesn't totally suck. Anyone who thinks that an on-screen imitation of a physical device is a good idea just isn't thinking very hard. The problems with Calculator and DVD Player, two of Apple's worst interfaces, boil down to this mistake. (Actually, the unnecessary power given to DVD creators handicaps anyone wanting to make a really good DVD interface, because there's a lot of room for bad interface on the DVD itself. The truly insolent "NOT PERMITTED", and the messing about on the "loading screen" before you can get the damn thing to play. There's a level of hell reserved for those idiots.)

But back to calculating...

Under no circumstances would I even consider using AppleScript. It still strikes me as a theoretical exercise in constructing Perl's dual. If you've ever wondered what a read-only language would look like, AppleScript is it. The sooner that revenant from the ancien regime goes to its grave, the better.

I do use bc(1), though, especially for base conversion. I use it less now that Terminator lets me simply select any number (or copy it to the clipboard if it's in a different application) and see it in whichever of decimal, hex, octal, and binary it wasn't already in. That's a really cool feature, and it's given me a new pet peeve of waiting while someone converts bases in their head or with some other tool, rather than just double-clicking. It's almost as annoying as those people who scroll up and down looking for something rather than use find.

I hate bc(1) too, but mainly because scale has a stupid default (so 22/7 gives 3). There's also the fact that it's Yet Another Language I Don't Use Often Enough To Remember. The command-line tool underlying Longhand seems (based on my superficial examination) to be pretty similar, but I'm hopeful that the GUI (which I haven't seen yet) might try to make the functionality less invisible.

Jon has two problems with bc(1). He says:

(Why do I hate bc? Two reasons: the dumb ABSOLUTELY NO WARRANTY banner it prints upon launch and the fact it catches control-c and tells you to type quit to exit. Either exit should exit, or it should honor my control-c (or both). Usually I simply close the entire Terminal window. I actually derive pleasure from force-killing bc that way. Die insolent software!)

I'm ambivalent about the banner. I can see both sides' point of view. Yes, it's ugly; yes, it might save the authors some grief they don't want. Using the -q argument, or setting BC_ENV_ARGS to -q suppresses this anyway.

I find his control-C complaint odd, though, as someone from a Unix background. I'd use control-C if I wanted to interrupt bc(1), but not if I wanted to quit. It's an interactive program, so I'd use control-D to signal the end of input, and it would go quietly into the night. bc(1) might be "insolent" in refusing to die when interrupted, but it's rude to interrupt in the first place!

What about "quit" versus "exit"? True, some programs understand both. ftp(1) is an example. But a lot only understand "quit". gdb(1) is probably the one I use most often:

hydrogen:~$ gdb
GNU gdb 5.3-20030128 (Apple version gdb-330.1) (Fri Jul 16 21:42:28 GMT 2004)
Copyright 2003 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB. Type "show warranty" for details.
This GDB was configured as "powerpc-apple-darwin".
(gdb) exit
Undefined command: "exit". Try "help".
(gdb) quit
hydrogen:~$

(Hey, look: it has an ugly banner too!)

I can't think where the argument comes from, but the argument I'm familiar with for why Unix programs should only understand "quit" is that "exit" is for the shell. The shell understands "logout", too, but "exit" is more common, perhaps because of its use in shell scripts.

More common still is the use of control-D. Which works for all interactive programs not just the shell. So is it worth trying to reserve "exit" for the shell? Probably not. But who'd type any of these when they can just hit control-D anyway?

Actually, my other personality – the one who uses gdb(1) – reminds me that we always quit gdb(1) with "quit". Neither of us knows why. Perhaps it's something to do with how heavyweight the program feels? bc(1) does its best to come across as a filter (albeit one with an ugly banner) waiting for the end of input, whereas gdb(1) really does come across as an interactive program. One worthy of a "quit".

2005-01-04

"House of Flying Daggers"

I don't get Chinese cinema. Even when I like something, I don't necessarily understand it. The Missing Gun, for example. Anyway, House of Flying Daggers is a love story, not a war story, and there's next to no unarmed combat, and even less respect for the laws of physics. The girl's hot, but the guy looks way too Japanese to be working for the Chinese government in China in 829 without any kind of explanation.

Ah well, maybe Elektra will have some decent chick-fu. (An underdeveloped genre, just begging to be exploited.)

For a computer programmer, I have an strong dislike of wires and computer graphics.

In fight scenes, anyway.

2005-01-01

Biting the Terminator bullet on Mac OS

I've long used Terminator as my only terminal emulator on Linux. On Mac OS, I've kept on using Terminal. I sometimes find myself thinking "damn, I wish I was running Terminator", usually when I need to search for something, or I'm staring at something like SourceFile.java:123 and wondering why I can't just click on it. But somehow I still keep using Terminal.

Anyway, someone recently asked what the best way to run Terminator is on Mac OS, and I didn't understand what all the fuss was about. I'd been happily running ./bin/terminator from the bash(1) prompt. I assumed that, when I was ready to switch, I'd just go into System Preferences and add the shell script as a new Startup Item.

It turns out that you System Preferences will let you choose a shell script as a Startup Item, but when you log in, nothing will seem to happen, and the console (the one in /Library/Logs/Console/elliotth/console, obviously) will say:

2004-12-31 22:31:31.361 loginwindow[1426] LSOpenFromURLSpec return err = -10814

Which here means "try again with an app bundle instead". You can find this error in the LSInfo.h header file, which suggests that an application might need to claim shell scripts before you can open them like this; experimentation with open(1) concurs.

Seems stupid to me, but I don't make the rules.

To fix the problem, I told make(1) to copy all the necessary files into terminator.app/Contents/Resources, and to write a little shell script called terminator.app/Contents/MacOS/terminator that just execs the real shell script in ../Resources. If I choose that as a Startup Item, Terminator now starts when I log in.

No more Terminal for me.

Yum! Dog food. My favorite!

And already I'm annoyed because Mac OS is still using Java 1.4, where Sun deliberately broke System.getenv, and with it my ability to click on links in Terminator. I know I'm no worse off than I was two minutes ago, when I was using Terminal, but it's a whole lot more annoying. Tantalus had no idea.

Anyway, if you want Terminator.app you'll have to type make app in the terminator directory. It'll create an app bundle in the parent directory. This doesn't sound like a permanent solution, and all the copying is especially unsuited to my situation as developer: it takes as long to make the app bundle as it does to build all the classes. Maybe I should make a hollow Terminator.app that just has symbolic links to the real project directories?