Building "cat"
Saturday, August 21, 2010
One neat feature of Factor is the ability to create and deploy programs as compiled binaries – both CLI (command-line) or UI (graphical) applications.
I thought it might be fun to build the cat command-line program in Factor, and show how it can be deployed as a binary. From the man pages:
The cat utility reads files sequentially, writing them to the standard output. The file operands are processed in command-line order. If file is a single dash (’-’) or absent, cat reads from the standard input.
We’ll start by creating the cat
vocabulary. You can either create the
cat.factor
file yourself, or use
tools.scaffold
to do it for you:
IN: scratchpad USE: tools.scaffold
IN: scratchpad "cat" scaffold-work
Creating scaffolding for P" resource:work/cat/cat.factor"
IN: scratchpad "cat" vocab edit
Begin the implementation by listing some imports and a namespace:
USING: command-line kernel io io.encodings.binary io.files
namespaces sequences strings ;
IN: cat
Printing each line from a stream is easy using the
each-line
word (flushing after each write to match the behavior of cat
):
: cat-lines ( -- )
[ write nl flush ] each-line ;
I chose to treat files (which might be text or binary) as binary, reading and writing 1024 bytes at a time. We check that the file exists, printing an error if not found:
: cat-stream ( -- )
[ 1024 read dup ] [ >string write flush ] while drop ;
: cat-file ( path -- )
dup exists?
[ binary [ cat-stream ] with-file-reader ]
[ write ": not found" write nl flush ] if ;
Given a list of files, with a special case for “-” (to read from
standard input), we can cat
each one:
: cat-files ( paths -- )
[ dup "-" = [ drop cat-lines ] [ cat-file ] if ] each ;
Finally, we need an entry point that checks if command-line arguments have been provided:
: run-cat ( -- )
command-line get [ cat-lines ] [ cat-files ] if-empty ;
MAIN: run-cat
Using the deploy-tool:
IN: scratchpad "cat" deploy-tool
Click “Save” to persist the deploy settings into a deploy.factor
file,
and “Deploy” to create a binary. You should see output like the
following:
Deploying cat...
Writing vocabulary manifest
Preparing deployed libraries
Stripping manual memory management debug code
Stripping destructor debug code
Stripping stack effect checking from call( and execute(
Stripping specialized arrays
Stripping startup hooks
Stripping default methods
Stripping compiler classes
Finding megamorphic caches
Stripping globals
Compressing objects
Compressing quotations
Stripping word properties
Stripping symbolic word definitions
Stripping word names
Clearing megamorphic caches
Saving final image
And your binary should be in the same directory as your Factor
installation (in a cat.app
sub-directory on the Mac).
$ ls -hl cat.app/Contents/MacOS/cat
-rwxr-xr-x 1 user staff 421k Aug 21 11:11 cat.app/Contents/MacOS/cat*
$ cat.app/Contents/MacOS/cat
hello, world
hello, world
^D
The code for this is on my GitHub.