ref: babf901b4a508c3ec5d1f89655f10377bbdf9637
dir: /appl/cmd/disdep.b/
implement Disdep;
#
# Copyright © 2000 Vita Nuova Limited. All rights reserved.
#
include "sys.m";
sys: Sys;
print, sprint: import sys;
include "bufio.m";
bufio: Bufio;
Iobuf: import bufio;
include "draw.m";
include "string.m";
str: String;
include "arg.m";
arg: Arg;
include "dis.m";
dis: Dis;
Mod: import dis;
include "hash.m";
hash: Hash;
HashTable, HashVal: import hash;
Disdep: module
{
init: fn(ctxt: ref Draw->Context, argv: list of string);
};
Item: adt {
name: string;
needs: cyclic list of ref Item;
visited: int;
find: fn(s: string): ref Item;
};
bout: ref Iobuf;
pending: list of ref Item;
roots: list of ref Item;
tab: ref HashTable;
aflag := 0; # display all non-recursive dependencies
oflag := 0; # only list the immediate (outer) dependencies
sflag := 0; # include $system modules
pflag := 0; # show dependency sets as pairs, one per line
showdepth := 0; # indent to show the dependency structure
noload(mod: string)
{
sys->fprint(sys->fildes(2), "disdep: can't load %s: %r\n", mod);
raise "fail:load";
}
usage()
{
sys->fprint(sys->fildes(2), "Usage: disdep [-a] [-d] [-o] [-p] [-s] file.dis ...\n");
raise "fail:usage";
}
init(nil: ref Draw->Context, argv: list of string)
{
sys = load Sys Sys->PATH;
bufio = load Bufio Bufio->PATH;
if(bufio == nil)
noload(Bufio->PATH);
str = load String String->PATH;
if(str == nil)
noload(String->PATH);
hash = load Hash Hash->PATH;
if(hash == nil)
noload(Hash->PATH);
arg = load Arg Arg->PATH;
if(arg == nil)
noload(Arg->PATH);
dis = load Dis Dis->PATH;
if(dis == nil)
noload(Dis->PATH);
dis->init();
arg->init(argv);
while((opt := arg->opt()) != 0)
case opt {
'a' => aflag = 1; showdepth = 1;
'o' => oflag = 1;
's' => sflag = 1;
'd' => showdepth = 1;
'p' => pflag = 1;
* => usage();
}
argv = arg->argv();
if(argv == nil)
usage();
tab = hash->new(521);
bout = bufio->fopen(sys->fildes(1), Sys->OWRITE);
for(l := rev(argv); l != nil; l = tl l)
roots = Item.find(hd l) :: roots;
pending = roots;
while(pending != nil){
f := hd pending;
pending = tl pending;
(m, s) := dis->loadobj(f.name);
if(s != nil){
sys->fprint(sys->fildes(2), "disdep: can't open %s: %s\n", f.name, s);
continue;
}
f.needs = disfind(m);
for(nl := f.needs; nl != nil; nl = tl nl){
n := hd nl;
if(!n.visited){
n.visited = 1;
if(!oflag && !isdol(n.name))
pending = n :: pending;
}
}
}
if(pflag){
for(i := 0; i < nextitem; i++){
f := items[i];
if(f.needs != nil){
for(nl := f.needs; nl != nil; nl = tl nl){
bout.puts(f.name);
bout.putc(' ');
bout.puts((hd nl).name);
bout.putc('\n');
}
}else{
bout.puts(f.name);
bout.putc('\n');
}
}
}else{
unvisited();
for(; roots != nil; roots = tl roots){
if(aflag)
unvisited();
f := hd roots;
depth := 0;
if(showdepth){
bout.puts(f.name);
bout.putc('\n');
depth = 1;
}
prdep(hd roots, depth);
}
}
bout.flush();
}
disfind(m: ref Mod): list of ref Item
{
needs: list of ref Item;
for(d := m.data; d != nil; d = tl d) {
pick dat := hd d {
String =>
if(isdisfile(dat.str) || sflag && isdol(dat.str))
needs = Item.find(dat.str) :: needs;
}
}
return rev(needs);
}
prdep(f: ref Item, depth: int)
{
f.visited = 1; # short-circuit self-reference
for(nl := f.needs; nl != nil; nl = tl nl){
n := hd nl;
if(!n.visited){
n.visited = 1;
name(n.name, depth);
prdep(n, depth+1);
}else if(aflag)
name(n.name, depth);
}
}
items := array[100] of ref Item;
nextitem := 0;
Item.find(name: string): ref Item
{
k := tab.find(name);
if(k != nil)
return items[k.i];
if(nextitem >= len items){
a := array[len items + 100] of ref Item;
a[0:] = items;
items = a;
}
f := ref Item;
f.name = name;
f.visited = 0;
items[nextitem] = f;
tab.insert(name, HashVal(nextitem, 0.0, nil));
nextitem++;
return f;
}
unvisited()
{
for(i := 0; i < nextitem; i++)
items[i].visited = 0;
}
name(s: string, depth: int)
{
if(showdepth)
for(i:=0; i<depth; i++)
bout.putc('\t');
bout.puts(s);
bout.putc('\n');
}
isdisfile(s: string): int
{
if(len s > 4 && s[len s-4:]==".dis"){ # worth a look
for(i := 0; i < len s; i++)
if(s[i] <= ' ' || s[i] == '%')
return 0;
return 1;
}
return 0;
}
isdol(s: string): int
{
return len s > 1 && s[0] == '$' && s[1]>='A' && s[1]<='Z'; # reasonable guess
}
rev[T](l: list of T): list of T
{
t: list of T;
for(; l != nil; l = tl l)
t = hd l :: t;
return t;
}