188 Programming 7. Why should the debugger do that? Why not write some “tool” that does it instead? 6. If the debugger dumps core, you should forget about debugging your application and debug the debugger. 5. It’s too hard to understand. 4. Where are the Twinkies? 3. Why fix things now? 2. Unix can’t do everything right. 1. What’s the problem? The statement “fixing bugs would break existing code” is a powerful excuse for Unix programmers who don’t want to fix bugs. But there might be a hidden agenda as well. More than breaking existing code, fixing bugs would require changing the Unix interface that zealots consider so simple and easy-to-understand. That this interface doesn’t work is irrelevant. But instead of buckling down and coming up with something better, or just fix- ing the existing bugs, Unix programmers chant the mantra that the Unix interface is Simple and Beautiful. Simple and Beautiful. Simple and Beau- tiful! (It’s got a nice ring to it, doesn’t it?) Unfortunately, programming around bugs is particularly heinous since it makes the buggy behavior part of the operating system specification. The longer you wait to fix a bug, the harder it becomes, because countless pro- grams that have the workaround now depend on the buggy behavior and will break if it is fixed. As a result, changing the operating system interface has an even higher cost since an unknown number of utility programs will need to be modified to handle the new, albeit correct, interface behavior. (This, in part, explains why programs like ls have so many different options to accomplish more-or-less the same thing, each with its own slight variation.) If you drop a frog into briskly boiling water it will immediately jump out. Boiling water is hot, you know. However, if you put a frog into cold water and slowly bring it to a boil, the frog won’t notice and will be boiled to death. The Unix interface is boiling over. The complete programming interface to input/output used to be open, close, read, and write. The addition of net- working was more fuel for the fire. Now there are at least five ways to send data on a file descriptor: write, writev, send, sendto, and sendmsg. Each involves a separate code path through the kernel, meaning there are five times as many opportunities for bugs and five different sets of performance characteristics to remember. The same holds true for reading data from a file descriptor (read, recv, recvfrom, and recvmsg). Dead frog.
“It Can’t Be a Bug, My Makefile Depends on It!” 189 Filename Expansion There is one exception to Unix’s each-program-is-self-contained rule: file- name expansion. Very often, one wants Unix utilities to operate on one or more files. The Unix shells provide a shorthand for naming groups of files that are expanded by the shell, producing a list of files that is passed to the utility. For example, say your directory contains the files A, B, and C. To remove all of these files, you might type rm *. The shell will expand “*” to “A B C” and pass these arguments to rm. There are many, many problems with this approach, which we discussed in the previous chapter. You should know, though, that using the shell to expand filenames is not an historical accident: it was a carefully reasoned design decision. In “The Unix Pro- gramming Environment” by Kernighan and Mashey (IEEE Computer, April 1981), the authors claim that, “Incorporating this mechanism into the shell is more efficient than duplicating it everywhere and ensures that it is available to programs in a uniform way.”3 Excuse me? The Standard I/O library (stdio in Unix-speak) is “available to programs in a uniform way.” What would have been wrong with having library functions to do filename expansion? Haven’t these guys heard of linkable code libraries? Furthermore, the efficiency claim is completely vacuous since they don't present any performance numbers to back it up. They don’t even explain what they mean by “efficient.” Does having file- name expansion in the shell produce the most efficient system for program- mers to write small programs, or does it simply produce the most efficient system imaginable for deleting the files of untutored novices? Most of the time, having the shell expand file names doesn’t matter since the outcome is the same as if the utility program did it. But like most things in Unix, it sometimes bites. Hard. Say you are a novice user with two files in a directory, A.m and B.m. You’re used to MS-DOS and you want to rename the files to A.c and B.c. Hmm. There’s no rename command, but there’s this mv command that looks like it does the same thing. So you type mv *.m *.c. The shell expands this to mv A.m B.m and mv overwrites B.m with A.m. This is a bit of a shame since you had been working on B.m for the last couple of hours and that was your only copy. 3Note that this decision flies in the face of the other lauded Unix decision to let any user run any shell. You can’t run any shell: you have to run a shell that performs star-name expansion.—Eds.