Jun 08

Problem:

You are trying to run Ruby on Rails on OS X, and all you ever get is

Rails requires RubyGems >= 0.9.4. Please install RubyGems and try again: http://rubygems.rubyforge.org

When you check gem --version you find that you already have RubyGems 1.1.1, or some other version higher than 0.9.4 that Rails ought to be happy with.

Possible explanation:

At some point you installed MacPorts. MacPorts built and/or installed a redundant version of ruby, even though you didn’t ask it to.

To confirm that this is the problem, type which ruby and see if the answer has /opt in it. If so, yes, MacPorts hosed your Rails/RubyGems. Solution: sudo port uninstall ruby

(I’m pretty confident that MacPorts is at fault here because I installed it for the first time this evening to build bzr, which involves no Ruby, and indeed is from a bunch of Python programmers who would probably sooner drink raw sewage than require Ruby for anything. So thanks, MacPorts.)

Mar 07

iPhone SDK: no wireless network access (WiFi only), and no multi-tasking.

Feb 07

It’s possible to write some C code to work out whether a machine’s architecture is little-endian or big-endian with respect to bytes.

Is it possible, using only ANSI C, to work out whether the machine’s architecture is big-endian or little-endian with respect to bits?

I don’t know the ANSI spec in nearly as much pedantic detail as would be necessary to answer the question, but I bet someone does.

Dec 19

Nice to see that the developers of Perl are still solving the important problems:

say() is a new built-in, only available when use feature 'say' is in effect, that is similar to print(), but that implicitly appends a newline to the printed string.

A new prototype character has been added. _ is equivalent to $ but defaults to $_ if the corresponding argument isn’t supplied.

perldelta for 5.10

This is exactly the kind of stuff that made me give up on Perl. And mro is a pretty horrible new feature too.

Oct 23

One of the things I found confusing about bash was its startup scripts: there were so many of them. Eventually I snapped and sat down with a terminal and the man pages, and worked out how it actually behaves. Here’s a summary.

Interactive
login
Interactive
non-login
Non-interactive Remote shell
/etc/profile A      
/etc/bash.bashrc   A†    
~/.bashrc   B   A
~/.bash_profile B2      
~/.bash_login B3      
~/.profile B4      
~/.bash_logout C      
BASH_ENV     A  

On startup, bash executes any script labeled A in the table above, followed by the first script B it finds. On exit, it executes any script labeled C above.

Let’s look at the column headings in a little more detail.

  • An interactive login shell is a shell that you are typing into, that is the first such shell you execute on the machine. Typically you will have had to log in immediately before the shell starts. For example, when you SSH to a remote system and type commands to that system, you are typing into an interactive login shell.
  • An interactive non-login shell is a new shell started once you have already logged in; one which doesn’t require that you log in again.For example, if you open a new terminal window in your graphical user interface and get a shell prompt, that’s an interactive non-login shell. Another example of an interactive non-login shell would be a sub-shell started from inside a text editor; for example, typing :sh in vi.
  • A non-interactive shell is a shell which doesn’t prompt you; it just runs a program and then exits. The most common example of this is any program written in shell script, such as a configure script, a startup script in /etc/init.d, or any other file marked as executable that has #!/bin/bash on the first line.
  • A remote shell is a shell started by a program such as SSH or rsh in order to run a command on a remote machine.For example, the rsync and scp commands use SSH remote shells in order to copy files between machines.

So looking at the second column, an interactive login shell will execute /etc/profile always. It then looks for ~/.bash_profile, ~/.bash_login, and ~/.profile in turn, and executes the first of those it finds. On logout, it executes ~/.bash_logout.

The /etc/bash.bashrc A† item is special; whether bash searches for it is dependent on a compile-time option.

BASH_ENV is an environment variable which allows you to make non-interactive non-login shells (such as shell scripts) execute a startup script. Set BASH_ENV to the filename of a script and then invoke a sub-shell, and the script will be executed when the sub-shell starts up.

Problems with bash’s behavior

Given the above table, the short summary is:

  • If you want something executed only when you first log in, put it in ~/.bash_profile
  • If you want something executed only for additional shells (such as OS X terminal windows and xterms), put it in ~/.bashrc

But there are a couple of problems with this arrangement, problems which suggest that bash’s startup behavior wasn’t really thought out with users in mind.

Firstly, if you are anything like me, most of the things you want to put in shell startup scripts are things you always want executed. Command aliases, for example; or environment variables that tell pieces of software where to find their bits (JAVA_HOME, ECLIPSE_HOME).

You could put those in both .bashrc and .bash_login, but that represents a maintenance problem: if you change something, you have to remember to change it in both places. So, you might set up a third file for global stuff, and use the shell command source to read it in from both .bashrc and .bash_login. I’ve seen some Linux distributions set this up as the default. I don’t like it, however, because it means you now have 3 different startup files floating around, and when you want to change something you have to remember which file it’s in (or sit and work it out).

The second issue with bash startup scripts is that the distinction between login shell and non-login shell isn’t a very useful one these days. Most of us use graphical user interfaces, so we never see a login shell on the machine we’re using. (For example, any terminal window you open on OS X is a non-login one.) Even when I use SSH to shell into a remote system, I don’t generally want that first login to behave differently to any other shell I start (such as shells inside screen).

What I do care about, on the other hand, is whether the shell is interactive. I don’t want my reminder program printing stuff when rsync is trying to connect and transfer files. I don’t want all my custom commands and aliases getting in the way when running scripts to configure or build software. And I don’t want to slow things down loading cdargs unless I’m actually going to be maneuvering around the directory structure by typing.

So what I want is to have a single customization script, and be able to split it into stuff that is always run, and stuff that is only run when I’m using the shell session interactively. Here’s how to do that.

Simple all-purpose bash initialization script

Start off by moving all your current bash startup scripts into a temporary directory, so you have a clean slate. Then, create a skeleton ~/.bashrc that looks like this:

### Start of universal section ###
# Commands in this section will be executed by both interactive and
# non-interactive shells.
# Commands here must produce no output, or they will break commands
# like scp and rsync.

### End of universal section ###
[ -z "$PS1" ] && return
### Start of interactive section ###
# Commands in this section will be executed only by interactive shells.

### End of interactive section ###

Next, cd ~ if you’re not already in your home directory, then ln -s .bashrc .bash_login

Now you have a single customization file for all your shell sessions, called ~/.bashrc. You can copy in each command from your old customization files, placing them in the appropriate section according to whether you need them all the time, or just in shells that you’re typing in to.

If you really care about login shells

If for some reason you do want to have login shells behave differently from non-login, that’s pretty simple too. Instead of the ln -s command above, create the following ~/.bash_login file:

if [ -f ~/.bashrc ]; then
  source ~/.bashrc
fi
# Commands for login shells only go under here

Now you have two places customization commands may be placed, but you get the option of having login-specific stuff.

Dealing with multiple systems

Another trick I use is to examine the host name of the machine. This lets me use the same .bashrc everywhere; my Mac’s .bashrc is the same as the one I use on my Linux box and the System z mainframe at work. Here’s the code:

if [ "$HOSTNAME" = "T41p" ]; then
  # Customizations specific to the ThinkPad laptop go in here
fi

You can use code like this in either the interactive or non-interactive section of the .bashrc above.

Apr 18

I got tags working via a plugin.

Since I was messing with the site anyway, I hacked together some Ruby code to pull all the content out of the database and perform automatic keyword extraction via naïve bayesian analysis.

It spat out a file of SQL commands, consisting of the subject of each posting and the first line of text (in comments), followed by the commands to add the tags. I ran through the file in vim deleting here and adding there, then executed the result. So now pretty much everything should be tagged, right back to the start. How cool is that?

Mar 23

I wonder if it’s possible to change your name to O’;DROP DATABASE; legally?

Mar 09

You know how DST rules for the USA have been changed this year, and every OS needs patches?

You know how Java doesn’t use the OS’s info, so you have to patch all your Java VMs separately?

Well, it turns out that Sun’s “fix” was broken, in that it changed the behavior of the (deprecated) 3-letter time zones EST, MST and HST so that they now no longer reflect daylight saving time at all.

So, chances are you need to check every Java runtime again, and maybe delete 3 files, or run the fix again with a -bc flag.

But hey, you’ve got until 1am on Sunday to fix all your production systems. No big deal, right?

While I’m on the subject of Java’s date/time handling being a confusing mess, let’s talk about a few of the other things wrong with it…

Java.util.Date represents a date/time, i.e. an instant in time.

Java.sql.Time represents a date/time as well. It is a subclass of Java.util.Date, yet you can’t cast a Date to a Time. Instead, you have to get the epoch time in milliseconds from your Date, and use that long value to construct a Time.

To get the epoch time/date from your Date, you use the getTime() method, which gives you a long rather than a Time. Java.util.Calendar also represents a date/time. It also has a getTime() method. However, that getTime() method returns a Date object, not a Time object or a long epoch time.

Next: Calendar numbers months from zero, so January is month 0, February is month 1. This makes it unlike any calendar in human history.

Next: If you read a Time value from a database, you can’t examine it (e.g. check the hour is AM or PM) until you convert it into a Calendar. You can’t do that by simply constructing a Calendar with the Time value. The only way to create a Calendar with a particular time value is to construct an empty Calendar, then call setTime(). And of course, setTime takes a Date, not a Time. So you have to convert your Time to a long, convert the long to a Date, create a Calendar, then call setTime() on the Calendar with the Date as argument.

In fact Date objects are pretty much a useless relic, almost all date and time handling is done with Calendar objects. For example, SimpleDateFormat allows you to convert String objects to and from date/time values. However, it only returns Date objects. So your String gets parsed into fields, which are then converted by Java to a Calendar-like representation, which is then converted to a Date object passed back to you; you then convert the Date object back into fields in a Calendar so you can work with it.

Still, it’s a much more enterprisey way to do things.

Jan 09

I was kinda enthusiastic about the iPhone…then I found out from Macintouch that it’s a closed, locked down unit.

Forget about installing software to use it as an e-book reader, or reading Word documents or PDFs. You’re not going to be using it to give business presentations. Forget about downloading music via the WiFi connection. Forget about writing your own neat applications and running them. There’s no Xcode iPhone developer kit, and Apple apparently has no plans to produce one for public use.

So basically, it’s a phone that does exactly the same stuff my current phone does, but with a much prettier interface. I’m sure Apple will sell a boatload of them to the same people who bought the Motorola RAZR because it looked cool. But to me, it’s not that interesting unless it’s open.

The fact that you need to sign a 2 year contract with Cingular makes it even less attractive. Cingular’s SMS is flaky to the point of near uselessness, and their Internet connectivity is expensive compared to any other carrier. I’m sure their iPhone contract will require a monthly reaming that will make my current unpleasant cellphone bill look like a bargain.

If you like the idea of what the iPhone could have been, though, there are a couple of upcoming alternatives worth considering.

OpenMoko is a Linux-based phone with an iPhone-like touch interface. It’ll be about half the price of the iPhone, and not locked to Cingular. It’s also going to be open to developers.

The Greenphone is a more traditional phone design (i.e. one with buttons). Again, it runs Linux and is open to third party developers.

Also, since it seems it isn’t common knowledge: Apple didn’t invent the multitouch technology as Steve Jobs claimed. It was actually developed by a company called Fingerworks. Said company mysteriously shut down, and the owners refused to say who had purchased their operation, citing confidentiality agreements. However, one of the founders of the company was subsequently confirmed to be working for Apple.

Apr 17

We don’t need a Google Summer of Code; we need a Google Summer of Documentation.