Tips and tricks

Troff in Technicolor (2011/04/12)

Someone asked on 9fans today how one goes about getting coloured troff graph/charts/&c. Sape Mullender graciously provided how he does this.

From: Sape Mullender sape@pla... Subject: where to get color troff? Date: Tue, 12 Apr 2011 09:33:47 +0200

I use these macros for generating color:

.de CL
\\X'PS \\$1 \\$2 \\$3 setrgbcolor'\\c
.de BK
\\X'PS 0 0 0 setrgbcolor'\\c
.de EM
\\$3\\X'PS 1 0 0 setrgbcolor'\\$1\\X'PS 0 0 0 setrgbcolor'\\$2

They only work if you generate postscript (you can, of course, turn that into pdf as well).

.CL r g b

sets the color to the rgb values (0 0 0 being black, 1 1 1 being white,

0.5 0.5 0.5 being middle grey)

sets the color to black.

.EM "string1" "string2" "string3"

(emphasis), where strings 2 and 3 are optional prints string3 in the current color, followed by string1 in red and string2 in black (with no space between strings 3, 1 and 2 — they work as .B or .I in the .ms macro package.


9fans post

Using Inferno to serve files to Plan 9 in Qemu (2010/08/17)

Venkatesh Srinivas has posted a very brief guide that lets you serve files from Inferno to Plan 9 in Qemu.

I run Plan 9 in qemu, but I run neither fossil nor any other (major) disk file server in qemu. Instead, I have Inferno on my host serve files to Plan 9.

To accomplish this:

  1. I installed Plan 9, as normal, into a qemu disk image, with fossil as my fileserver
  2. I tarred up my root and copied it to my host
  3. I patched the 9 boot program to accept a file server on port 6666 rather than 564 (styx instead of 9fs)
  4. I untarred the root fs and had Inferno: ‘styxlisten -A tcp!*!6666 export /plan9’
  5. Start the both (qemu and inferno) together.
  6. Profit!

Original 9fans post.

avoiding auth ndb entry for your plan9 site (2010/08/02)

here is a trick that lets one mount a plan9 filesystem on another site without having to create a ndb entry to specify the domain name of the auth server.

when your auth domain is also your dns domain name. one can register p9auth subdomain and point it to your auth server:

lets say your dom is, then your auth server becomes

this works as authdial() in libauthsrv uses that default subdomain if here are no keys in your ndb for that particular auth domain.

Putting the 9 in S9FES (2009/07/19)

Scheme 9 from Empty Space (s9fes) is a work-in-progress implementation of Scheme, only experimentally ported to Plan 9. The compilation is simple enough, issuing ‘mk’ in the root source directory. However, properly making use of all of the libraries it comes with requires five minutes of configuration.

For the sake of organisation,

    term% mkdir $home/path/to/s9fes/sys # a rose by any other name...
    term% cp $home/path/to/s9fes/s9.^(scm image) $home/path/to/s9fes/sys
    term% mkdir $home/lib/s9fes

then making use of such, we do the following because s9fes uses environment variables, in the spirit of modern UNIX, to find its essential files (i.e., libraries and images):

    term% echo '#!/bin/rc


            for(i in lib contrib sys)
                bind -a $s9inst/$i $home/lib/s9fes


            $s9inst/s9' > $home/bin/rc/s9
    term% chmod +x $home/bin/rc/s9

and invoke the script s9 for actual use. Aside from the creation of lib/s9fes, which is a trade-off for organisation, this configuration is noninvasive and upgrade withstanding, for use until the Plan 9 port is finalised.

N.B.: In the above script, it might be necessary to define LIBDIR=$S9FES_LIBRARY_PATH – on modern UNIX systems, $S9FES_LIBRARY_PATH is a colon-separated list of paths containing scm and image files for s9fes, while $LIBDIR is the specific system-wide copy of s9fes/lib. On Plan 9, however, we don’t require the distinction due to the use of binds (and all library file names have to be distinct anyways, due to the way they are loaded from within s9fes).

Troff realtime preview for acme. (2009/07/17)

A while ago Russ Cox posted this script to keep a realtime preview of a troff document being edited with Acme:

One really nice thing about troff is that it is very fast. It hasn’t changed appreciably in decades, so it is riding out the hardware performance improvements quite nicely.

I was marvelling about how when I middle-click a troff | page command line in acme it just pops up instantly, and I decided to write a little shell script to do that for me.

This script watches the acme window it is executed in, and every time you type a newline or cut/paste with the mouse, it re-runs troff with the window contents, updating a postscript file being watched by gv –watch.

There is a slight lag before gv notices the file has changed, but it’s certainly good enough, and even better than repeatedly clicking Put and then executing the troff command.

If one were going to write a real command, you’d want to reroff in the background, waiting for pauses in the editing. But for a shell script, this isn’t bad.

One unfortunate effect is that because the window is attached to a program, Undo, Redo, and Put go away and have to be added on the other side of the tag | .

Enjoy. Russ


   . $PLAN9/lib/acme.rc


   fn reroff {
       9p read acme/$winid/body >$
       { 9 tbl $ | 9 troff -ms | tr2post >$tmp.ps_
           && mv $tmp.ps_ $ } >[2]/dev/null

   fn event {
       case KI
           if(~ $9 $nl)
       case KD MD MI
       case Mx MX Ml ML
           winwriteevent $*

   if(! winread tag | 9 grep -s Put)
       echo -n ' Put' | winwrite tag
   wineventloop &
   psv --watch $
   kill $apid
   killall 9p  # BUG
   rm $tmp.*

Update: Stuart M (aka stu9) notes in #plan9 that Presotto had posted back in 2001 to 9fans about how he usually kept a window running the following loop:

    troff ... |lp -dstdout >

Installing ‘Standard’ Inferno Fonts (2009/02/13)

Update: sqweek has been kind enough to ignore some dumb laws and conveniently host a copy of the fonts for easy download at Thanks!

The original Inferno fonts are not free (as in freedom) and can only be distributed together with the ‘official’ Inferno distribution.

Because of this restriction the source distribution and other derivative Inferno packages can’t include fonts, and if you have built Inferno from source many applications wont work.

Here is a script by caerwyn that used to be included with acme-sac which will download the fonts from VitaNuova’s site and install them:

    load std

    if {ftest -d /fonts/lucidasans}{
            echo 'error: /fonts directory already exists'

    mkdir /fonts
    mkdir /tmp/inferno
    cd /tmp/inferno

    webgrab -v -r -o - > inferno.tgz
    gunzip <inferno.tgz > inferno.tar
    gettar < inferno.tar
    gunzip <install/inferno/1178807193.gz >install/inferno/1178807193
    archfs -m mnt/wrap install/inferno/1178807193 /fonts
    cp -r mnt/wrap/fonts/* /fonts

    unmount mnt/wrap
    rm -rf /tmp/inferno

It is strongly recommended that anyone working on Inferno-based projects replaces the ‘standard’ fonts with a free set of fonts like acme-sac has eventually done by using the Vera font set.

Audio support in VmWare (2008/08/06)

Continuing with the audio theme, this week I will explain the arcane black magic rituals (ie., undocumented or at least badly documented) required to get audio working on Plan 9 when running inside a VmWare instance.

Maybe I will write instructions on how to install Plan 9 from scratch under VmWare, but the process should not be too painful now that a patch to disable vga acceleration by default when under VmWare has been accepted in the standard distribution.

After you have Plan 9 up and running one needs to enable SB16 emulation in VmWare, this is not possible with the GUI configuration tools, so after sacrificing a goat to the God of Mysterious Ways and adding the default audio device using the GUI interface you should find the .vmx configuration file for your Plan 9 VmWare image, open it in a text editor and make sure it contains the following lines:

sound.virtualDev = "sb16"
sound.baseAddr = "0x220"
sound.dma16 = "5"
sound.irq = "7" 
sound.autodetect = "FALSE"

This should work most remotely recent VmWare version. Now all we need to do is configure Plan 9 to use the virtual SB16, and this part is quite trivial despite the IMHO rather obtuse device configuration line because Plan 9 actually has proper documentation you can read and find with lookman(1) ;)

term% 9fat:
term% grep audio0 /n/9fat/plan9.ini
audio0=type=sb16 port=0x220 irq=7 dma=5

Make sure that the audio0 line is part of the configuration for whatever kernel you are going to use and reboot (if you are only booting one kernel just make sure that line is somewhere in your plan9.ini).

Now reboot and all that is left is to mount your new audio device with:

term% bind -a '#A' /dev/

Of course you will want to put that in your $home/lib/profile or riostart script so it is up and working in all your namespaces.

Thanks to Federico G. Benavento (aka fgb) for initially pointing out to me that this was possible and thanks to that after some archaeological research on unofficial VmWare docs I got it to work.

Redirecting audio output over a network with Inferno (2008/07/11)

We are inaugurating a new NineTimes section for small tips and tricks.

The first one explains how to export all audio output from a Linux box over the network using Inferno to any other system that can run Inferno (or to native Plan 9) without having to mess around with pulseaudio or any other such beast.

The difficult bit is figuring out how to get ALSA to output all audio to a fifo, you will need to have this in your /etc/asound.conf (note that many lunix systems don’t include that file by default so you might need to create it):

pcm.!default {
    type file               # File PCM
    slave.pcm "hw:0,0"      # This should match the playback device at /proc/asound/devices
    file /tmp/audio         # Output filename
    format raw              # File format ("raw" or "wav")
    perm 0666               # Output file permission (octal, def. 0600)

The slave.pcm is the most mysterious and magic part, hw:0,0 should work in most cases, but if for example you have an entry like: 48: [ 1- 0]: digital audio playback you should use “hw:1,0”.

Once you have alsa setup properly do: mkfifo /tmp/audio; chmod 666 /tmp/audio

And you are done messing with Linux and can move on to the real easy part, start Inferno’s emu: emu ; bind ‘#U*/’ /n/local/ ; listen -A tcp!YOUR.LINUX_BOX.IP.ADDRESS!9999 {export /n/local/tmp/}

Now go to your windows or OS X box start Inferno there and run (tried this in acme-sac, which has a reasonable default setup, if you are using inferno-os you might need to mount your ‘#A’ device to /dev): ; echo rate 44100 > /dev/audioctl ; mount -A tcp!YOUR.LINUX_BOX.IP.ADDRESS!9999 /n/lunix ; while {sleep 1} { cat /n/lunix/audio > /dev/audio }

And you are done, after this all audio you play on your linux box should be heard on your other box. How to listen from Plan 9, do proper auth, and how to reverse the setup so you export the audio device rather than the fifo are left as exercises for the reader.

Thanks to hiro for that inspired this trick (he was doing the same but with only the output of mpd), and thanks to the folks in the #alsa irc channel that helped me decipher the incomprehensible ALSA docs.