lab 49 - wrappers

NAME

lab 49 - wrappers

NOTES

Acme is the hub of my activity where all my tools are at hand. It is inconvenient to have to step outside it while working on a task. But it is impractical to port all the tools I need into Inferno. However, Inferno is an integrating environment. It is oftentimes easy to put a wrapper around an external tool so that it is usable within Acme.

In this lab I show a few examples of putting wrappers around external tools so that they blend in to the Inferno environment and work together with Inferno's tools.

The first, very simple example is awk.

fn awk {os awk $*}

With this definition it is only possible to use awk as a filter without naming files as command line arguments. But this is often good enough.

Very often I work on my laptop which has NT as the host os. But I need to use dict and spell all the time, and my laptop doesn't have these installed. I integrate these tools into my environment by running emu on a Plan9 or Linux machine and exporting a styx service that will include the #C device bound to /cmd. Then I just need to bind remote service before running the command.

fn spell {
 bind /n/linux/cmd /cmd
 cat $* |os spell
 unmount /n/linux/cmd /cmd> /dev/null>[2=1]
}

Now I can use >spell within acme and become oblivious to the fact that spell is running elsewhere on my network.

Let's say I want to run javac hello.java from within Acme, and also button-3-clicking on any error messages would jump to the relevant line within the file. The problem here is the Inferno namespace needs to be mapped to the host namespace. This can only be done if the file actually resides on the host. But given that assumption we can easily establish a convention whereby names within inferno map to hostnames.

fn file2host {
 for (i in $*) {
 f := `{cleanname -d `pwd $i}
 echo $f |sed 's@/n/d@D:@
 s@/n/c@C:@'
 }
}
fn host2file {
 sed 's@D:@/n/d@
 s@C:@/n/c@' |sed 's@'^`{pwd}^'/@@'
}
fn javac {
 classpath='your favourite jars'
 args=$*
 os javac -classpath $"classpath `{file2host $args} | host2file
}

This assumes we have bound, say the C: and D: drives to /n/c and /n/d. The mapping is simple. The benefit is that I can use mk to manage builds, and use plumber to plumb the compiler errors to Acme.

Here's a more complicated example, used for building the emu source code and libs. The assumption now is the source code is on the host and the mapping from inferno to host namespace is just prepending the $emuroot.

fn file2root {
 args = $*
 for (i in $args) {
 i = `{cleanname -d `pwd $i}
 echo $emuroot ^ $i |sed 's/\//\\/g'
 }
}
fn 9c {
 load arg
 args := $*
 flags=''
 (arg
 I+ {flags = $flags -I ^ `{file2root $arg}}
 D+ {flags = $flags -$opt ^ $arg}
 '*' {echo unknown option $opt}
 - $args
 )
 cc=cl
 cwd=`{echo $emuroot ^ `pwd | sed 's/\//\\/g'}
 cflags=(-c 
 -nologo
 -W3 
 -Zi
 -Yd
 -MT
 '-D_WIN32_WINNT=0x0400'
 -I$cwd
 -I$emuroot\Nt386円\include
 -I$emuroot\include
 -I$emuroot\libinterp
 -I$emuroot\emu\port
 -I'C:\Program Files\Microsoft Platform SDK\Include'
 -I'C:\Program Files\Microsoft Visual C++ Toolkit 2003\include'
 -Fo$cwd )
 os $cc $cflags $flags `{file2root $args}
}

This is interesting because emu could be built using mk from inside Inferno. Given a definition of 9c, 9l and 9ar for each platform the mkfiles could be simplified (however, shifting the complexity to the shell).

The final example is for XML tools. Often these tools take URLs instead of filenames. The trick is to serve the files I'm working on using httpd. Httpd is now serving as a general mapping from my local namespace to a universal namespace. I can bind the files in from anywhere and then work on them with XML tools, using tools that might also be bound in from anywhere. For example, I want to use xalan to transform XML using XSLT. I bind my working directory onto /services/httpd/root and run svc/httpd/httpd. Then files below the root get mapped to URLs.

domain:=.my.domain.name
fn file2url {
 sysname:=`{cat /dev/sysname}
 url:='http://'$sysname ^$domain
 for (i in $*) {
 f := `{cleanname -d `pwd $i}
 echo $f |sed 's@/services/httpd/root@'^$url^'@'
 }
}
fn xslt {
 classpath='/java/lib/xalan.jar'
 args := $*
 flags=''
 (arg
 t+ {flags =-XSL `{file2url $arg}}
 - $args
 )
 args=$*
 if {~ $#* 1} {
 args= -IN `{file2url $args}
 } {
 echo 'usage: xslt -t transform file '>/fd/2
 raise usage
 }
 bind /n/some_java_host/cmd /cmd
 os java -cp $classpath org.apache.xalan.xslt.Process $flags $args
 unmount /n/some_java_host/cmd /cmd>/dev/null>[2=1]
}

This last approach is possibly the most flexible. But all of them demonstrate that Inferno supports, in the classic UNIX way, using shell to modify a program's interface. In doing so the external tools and Inferno tools can be made to work together, creating a richer, more productive environment.

Comments

Anonymous said…
I use Inferno to remotely control itunes in OSX by doing
os -m /n/whitegene/cmd osascript
tell application "iTunes"
playpause
end tell
Anonymous said…
As you have said, Wrappers seem to be one of the most powerful tools that Inferno has, especially since Hosted Inferno can distribute the access to tools that run only on their native platforms to a single integrated environment. "iTunes" being an example here: It is also possible to route the audio output through the network back to the controlling Inferno terminal by exporting the audio device to the Macintosh Hosted Inferno.

Popular posts from this blog

lab 110 - inferno archive edition

I've been occupied recently with archiving my digital media. I've been copying home videos on DV tapes to hard-disk, ripping audio CD's to WAV files, gathering photo collections, and trying to copy documents from Iomega disks, floppies, and my dusty old Acorn RiscPC. The plan is to have a copy of this data to give to each of my children. My Dad recently scanned and sent me all his photographs of me and my siblings growing up; he also included pictures of himself and my Mother when they met in Africa. With technology today each generation can build a digital library of family history to hand on to the next generation. In the past a family album may have been passed on to only one person. The accumulation of digital data still presents problems. It requires discipline to store files that are open and not locked into devices or proprietary formats. With digital preservation in mind I've tried to use file formats recommended for long term archiving. WAV files for audio, D...

lab 109 - wm/view true color

There has been a three and a half year gap in my posts to this blog. In that time I hadn't done any Limbo programming. I've used Acme as my editor everyday, but I was drifting towards using Notepad++ more often. In the past couple of months I've had the time to contemplate doing some hacking projects. I wanted to explore what I could do with Inferno for multimedia file types. This lab was the first thing I tackled in using Inferno again. I had to open up the Limbo paper to remember even some basic syntax. It bothered me that wm/view only displayed images using the Inferno 256 color map. Charon didn't have this limitation and I thought it had something to do with their respective image libraries. They don't use the same code. I extracted Charon's img.b code out into another view tool only to realize once I'd finished that the difference was not in the handling of JPEGs or PNGs but in the remap of the raw image to an Inferno image after the image was load...

lab 52 - text files

NAME lab 52 - text files NOTES Some limbo programs can be replaced with shell one liners, and others can be replaced with more general programs that reduce the limbo line count but increase functionality. I'll look at the few I've discovered. The /prog filesystem exposes a textual interface that allows existing software tools to work with it. This implies I do not need a custom set of limbo tools to read and write to this filesystem. For example, ps is a 61 line limbo program, but I can do the same thing in one line of shell, fn ps {sed '' /prog/*/status |sort -n} This is the great power of Inferno. Simple textual interfaces exposed as files and a small set of software tools working together. Therefore, I can also try rewriting kill and broke . In this case I make the functionality more like Plan 9. Each command writes to output the commands that can be sent back to the shell to actually perform the kill action. fn kill { sed -n '/' ^ 1ドル ^ '/s/^ +...