summaryrefslogtreecommitdiff
path: root/appl/cmd/disdep.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/disdep.b')
-rw-r--r--appl/cmd/disdep.b250
1 files changed, 250 insertions, 0 deletions
diff --git a/appl/cmd/disdep.b b/appl/cmd/disdep.b
new file mode 100644
index 00000000..0a12c617
--- /dev/null
+++ b/appl/cmd/disdep.b
@@ -0,0 +1,250 @@
+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;
+}
+