tree
Friday, November 22, 2013
I’ve used Factor to build several common unix programs including copy, cat, fortune, wc, move, uniq, and others.
Today, I wanted to show how to build the tree
program. If we look at
the man page, we can see that it is
used to:
Tree
is a recursive directory listing program that produces a depth indented listing of files. With no arguments,tree
lists the files in the current directory. When directory arguments are given,tree
lists all the files and/or directories found in the given directories each in turn. Upon completion of listing all files/directories found,tree
returns the total number of files and/or directories listed.
We need to keep track of files and directories that are traversed:
SYMBOL: #files
SYMBOL: #directories
Each file name is indented according to a specified depth:
: indent ( n -- )
[ [ "| " write ] times ] unless-zero "+-- " write ;
: write-name ( indent entry -- )
[ indent ] [ name>> write ] bi* ;
File names are written and increment the #files
counter:
: write-file ( indent entry -- )
write-name #files [ 1 + ] change-global ;
Directory names are written, increase the indent, recurse writing their
directory tree, and increment the #directories
counter:
DEFER: write-tree
: write-dir ( indent entry -- )
[ write-name ] [
[ [ 1 + ] [ name>> ] bi* write-tree ]
[ 3drop " [error opening dir]" write ] recover
] 2bi #directories [ 1 + ] change-global ;
Using write-file
and write-dir
, we can implement write-tree
to
sort the entries and then write each according to their type (e.g., file
or directory):
: write-tree ( indent path -- )
[
[ name>> ] sort-with [
nl [ dup ] bi@ type>> +directory+ =
[ write-dir ] [ write-file ] if
] each drop
] with-directory-entries ;
Finally, we can implement the tree
command, initializing the file and
directory count, recursing on the path specified, and then printing out
the number of files and directories observed:
: tree ( path -- )
0 #directories set-global 0 #files set-global
[ write ] [ 0 swap write-tree ] bi nl
#directories get-global #files get-global
"\n%d directories, %d files\n" printf ;
Our command-line word will either operate on the arguments specifying a list of directories to process, or the current directory if none are provided:
: run-tree ( -- )
command-line get [
current-directory get tree
] [
[ tree ] each
] if-empty ;
MAIN: run-tree
This is available on my GitHub.