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 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; }