You make one little Durex pun and all of a sudden-like, somebody's making a PNG file for you.
Maintenant j'ouvre la fenêtre et une mouche la chambre entre
Ni la moche ni l'italienne ne peut être trouvée plus à son centre
This place with its wood and its shingles, this place
with birdshit splattered down the bricks
This place with drunken girls screaming Meat Loaf lyrics
and behaving like abject pricks
Maintenant je suis bien loin
But yet oh so close
Cerca de la criatura del lago
Not Iago as has been previously claimed
Humming, screeching, gurgling, squeaking
Not unlike Barbie's mom on a Thursday night
It goes through the motions of accomplishing something
When really it is advancing its own sinister agenda
An agenda so sinister that you'd name it Dexter if you could.
You can't.
Then, as the disruptive forces destabilize reality, like
they destabilize the rhyme and scansion and hyperlinklessness
of
This Poem
Then, like
Mike Stone
expressing
gratitude,
like something demanding its $3.50 (but oh, oh, it does not want its $3.50; it wants
something far more visceral and ephemeral), it makes a simple statement.
F01.
Natuurlijk.
Dear Sirs and/or/but Madam:
Kindly write a free clone of Syndicate Wars.
I can never remember this BTS URL:
This one is long and boring. You'll want to skip it entirely.
A while ago, Ulrich Drepper made getaddrinfo() in GNU libc behave according to an IETF RFC. Sounds sane, right? Well, not entirely. The way RFC 3484 rules work for IPv4 is less than ideal. On the other hand, returning network address query results unsorted is also less than ideal. Imagine if getent passwd returned lines in a randomized order. It would be perfectly functional, but piss me off to no end until I conditioned myself to always sort them the way I wanted them. I digress.
So, we shipped a stable release that behaved this “new” way. Nobody objected. I don't know if Ubuntu did or not. It would amuse me if they did, but I don't much care.
Then all of a sudden, along comes bug #438179. The gist is that Kurt thinks that trying to make intelligent decisions based on network topology is a bad idea because “it defeats the point of having multiple A-records in the first place.” We'll come back to that in two minutes. James filed the analogous bug in Ubuntu. I keep mentioning Ubuntu for a reason. I'm not going to tell you what that reason is.
The GNU libc maintainers were reluctant to change the default (which is how most other modern operating systems behave; MacOS X being a notable exception, though I heard that they were working on it), so Kurt escalated to the Technical Committee. Now nothing in our lovely and flawed Constitution says that the tech-ctte should be a technically-capable bastion of wisdom and patience; I just think it does because I don't waste my time reading Foundation Documents that don't accurately describe our governance structure.
Now for a brief interlude while I vamp incoherently for some of my less technical readers who are not implementors of a DNS resolver libraries, past IETF participants, DNS administrators, someones who've followed some of the IPv6 transition work, or have invented the Internet lately: Round-robin DNS is a giant hack, a poor man's loadbalancer, as it were, something that only works due to a de facto standard that no standards bodies seem to have thought worth preserving. gethostbyname() has been marked obsolescent in POSIX and deprecate in GNU libc for what seems like forever, and the GNU libc implementation of it suffers from bugs that probably no one should ever fix, and as such, no one should ever use it. getaddrinfo() is not a drop-in replacement for gethostbyname(), and expecting it to behave the same is daft. If it were meant to behave the same, I would expect that it using it would be simply a matter of changing calls to gethostbyname() to getaddrinfo(), rather than the amount of code rewriting you need to do now to change the same effect. Furthermore, nothing guarantees the behavior of gethostbyname() except for years of observed consistent behavior and the expectation that should anyone implement a C or resolver library that sorted gethostbyname() results, there would be much loud screaming that it was the library that's broken, and not the applications or programmers for being naïve.
I hope that was less than helpful. Let's move on with the plot.
While the Technical Committee “deliberated”, and I use the quotation marks because several -ctte members don't seem to have participated at all, Ian Jackson jumped up and down like some kind of Rumpelstiltskin, screaming that he is Der Komissar and that voting is in order, and a few other things I won't mention. On the 20th of September, Ian Jackson called for a vote. On the 20th of September, Ian Jackson uploaded an Ubuntu version of glibc with the default changed. On the 20th of September, my mother had trouble sleeping, though I suspect she has no idea it was because of this debacle.
This story has no ending. Let the rain come down.
Once upon a time I proposed a DebConf talk about how to write zsh completion functions, but it was rejected. Accordingly, I didn't waste any time preparing materials for it, so I never have anything to throw at people when they ask for some kind of introduction.
Here we have a fictitious program called arismom. Usage information from the fictitious manpage and the fictitious --help output is as follows:
Usage: arismom [OPTION]... [FILE]...
Do it Jersey style.
-a, --all do all those things
-b bubble
--bounce bonuce
--CoC=STYLE adhere to STYLE code of conduct
-d, --debian=PACKAGE dedicate actions to Debian PACKAGE
-e, --ensqualm=USER ensqualm USER first
-t, --tempdir=DIRECTORY spew temporary files into DIRECTORY
So let's cut to the quick. Create a file called _arismom somewhere in your function search path. This is described by the array $fpath. You can view its contents by typing print -l $fpath, and you can add a a directory to the beginning with fpath=(\~/.zsh/scratch $fpath) or to the end with fpath+=(\~/.zsh/scritch). For the purposes of this blog entry, we'll pretend you have a \~/.michaelbolton/squatch directory in your $fpath and that you are now editing \~/.michaelbolton/squatch/_arismom. The first line of the file, at the very tippy top, should read
#compdef arismom
This ensures that when the completion system boots up and finds your file, it will associate your function with the command arismom and complete options and arguments for it accordingly. Speaking of arguments, skip a line for aesthetic equilibrium, and invoke the _arguments utility function.
_arguments \
_arguments is sort of the cdbs of the zsh completion fleet. By the end of this blog entry, you'll have no idea how it works, and if you want to do anything particularly complex with it, you might encounter some resistance. For those of you unfamiliar with Z-Shell syntax or shell syntax in general, the trailing backslash means “I'm a-gonna feed you a ton of information about the command-line interface to arismom.” So tell it about the first option already.
'(-a --all)'{-a,--all}'[do all those things]' \
To oversimplify, this declares that -a and --all are options which produce the identical behavior of “do all those things”. Specifically, the part in parentheses says to not complete either -a or --all when either -a or --all is already on the command-line. The part in braces is merely brace expansion; for that reason it is outside of the single quotes. If you're unfamiliar with brace expansion, try print '(alice)'{bob,carnie}'[wilson]' to see how it expands. Finally, the phrase in brackets is an explanation of the option, which may or may not be displayed depending on your configuration. Next, do a short option that has no long option equivalent.
'-b[bubble]' \
A long option with no short option equivalent looks similar. Don't feel limited by upstream's inadequate descriptions, misspellings, or poor grammar.
'--bounce[amplify bounce level according to X-la algorithm]' \
Some options take arguments. Now we use colons.
'--CoC=[adhere to CoC]:CoC style:(mjg59 buxy ubuntu)' \
The first “column” is the same optspec by which you've been so excited thus far, and the part between the colons is the message or description of that which will be matched. The part after the last colon is the action; in this case you are specifying a list of possibilities within single parentheses. In most cases, you'll want to be more dynamic than a pre-defined list, and there are many helper functions all ready to serve you.
'(-d --debian)'{-d,--debian=}'[dedicate actions to Debian package]:package:_deb_packages avail' \
_deb_packages is a function that completes Debian packages; it can take avail, installed, or uninstalled to restrict which set of packages it offers. In this case we want it to complete any package available from your sources.
'(-e --ensqualm)'{-e,--ensqualm=}'[ensqualm user first]:user to ensqualm:_users' \
Here the _users function will complete usernames.
'(-t --tempdir)'{-t,--tempdir=}'[spew]:temp dir:_files -/' \
Normally the _files function will complete files, but you can tell it that you only want directories with the -/ option. Finally, we want to cover all the remaining arguments (which according to the fictitious usage information is a list of files). In this case, you happen to believe that files with the .nj extension are to be completed.
'*:NJ files:_files -g "*.nj"'
The -g option specifies a glob pattern to match files. Now the entire file should look like this.
#compdef arismom
_arguments \
'(-a --all)'{-a,--all}'[do all those things]' \
'-b[bubble]' \
'--bounce[amplify bounce level according to X-la algorithm]' \
'--CoC=[adhere to CoC]:CoC style:(mjg59 buxy ubuntu)' \
'(-d --debian)'{-d,--debian=}'[dedicate actions to Debian package]:package:_deb_packages avail' \
'(-e --ensqualm)'{-e,--ensqualm=}'[ensqualm user first]:user to ensqualm:_users' \
'(-t --tempdir)'{-t,--tempdir=}'[spew]:temp dir:_files -/' \
'*:NJ files:_files -g "*.nj"'
There you have it. Restart zsh and try tab-completing various things after arismom.
P.S. I expect bug reports containing functions for dpatch-edit-patch, pkill, and pgrep by tomorrow morning.
P.P.S. Why is Scott James Remnant still on Planet?
Joey, I find it the creepiest when I see my words scroll across my boss's monitor.
My next installment was going to cover a different topic, but Marty made a wish and unless Rajaton intercedes before this blog entry is over, the curriculum is going to careen off the track.
The relevant question is something like “Why do I have to type all that junk in when there's perfectly good --help output to parse?” and the relevant answer is something like “Yes. No. ROFLcows. Lamar.” This is somewhat easy when a program has well-behaved, sane-ish, GNU-style --help output.
Let's say you want completion for GNU ptx. Now you could go through and write a function using _arguments like you learned all about here, or you could just type
compdef _gnu_generic ptx
Suddenly you'll be able to complete options after ptx. You'll notice a few things as you perform your QA due diligence. It won't understand the subtleties of -G. It won't know what STRING and REGEXP and NUMBER mean, but then, neither do I.
It's possible to use _arguments to explain what WORDS IN ALL CAPITALS mean, but let's find a less BORING example.
Usage: nurmepuu [OPTION]... [FILE]
Drive Miss Nurmepuu
-c, --compress=TYPE apply this compression TYPE to the aircon
-f, --force use force
-s, --side=SIDE drive on this SIDE of the road
-u, --unbearability=LEVEL tolerate this LEVEL of unbearability
So start out the way you have now learned instinctually.
#compdef nurmepuu
_arguments -- \
WAIT. WHAT'S THAT‽‽ The double hyphen-minus, contrary to the expectations of GNU and git users alike, means (are you ready for this?) parse the --help output and try to sanely complete long options. Now lets give it some hints so it isn't too braindead.
'*=TYPE*:compression type:(gzip bzip2 lossy gainy pancakebatfish)' \
'*=SIDE*:side of the road:(left right east west up)' \
'*=LEVEL*:tolerance level:((japan\:haha radiohead\:hoho))'
There you go. The patterns on the left will be matched against the help output, and the completion actions on the right. The list in double parens are matches paired with descriptions (note the backslashed colons). Once more, the whole thing:
#compdef nurmepuu
_arguments -- \
'*=TYPE*:compression type:(gzip bzip2 lossy gainy pancakebatfish)' \
'*=SIDE*:side of the road:(left right east west up)' \
'*=LEVEL*:tolerance level:((japan\:haha radiohead\:hoho))'