TSyntax10.Scn.Fnt InfoElemsAllocXSyntax10.Scn.FntzStampElemsAlloc2002-Aug-09J"Title": Find "Author": HM (mainly), CS (added AllInfo) "Abstract": Provides you with the possibility to search strings in several files, to see which files are imported by several files, to see by which files several files are imported and to search several files after additional information (title, author, version, keywords etc.). "Keywords": search, import, client, additional info "Version": 1.0 "From": 28.10.94 16:40:44 "Until":  "Changes": "Hints": This text can again contain arbitrary text elements!  09 StampElemsAlloc2002-Aug-09.8FoldElemsNew#Syntax10.Scn.Fnt(*------------------------------------------------------------ Compare files. Search for a literal string in a sequence of files. Search for clients and imports of a specific module. Search for elements Find.Diff ^ Compares two texts starting from the two most recent selections. Sets new selections at the first position where the two texts differ. Find.Domain {filename} ~ Specify the files in which Find.All should search for a pattern. Find.All ^ Searches the selection in the files specified with Find.Domain. Lists all lines containing the pattern. Find.Elem (modname | ^) Find the next text element of kind k after the caret position. If a module name is specified, any element type declared in this module is searched for. If an element is specified as a selection, elements of this kind are searched for. Find.Clients (modname | ^) List all client modules which import module . Clients are searched in the same way as they would be searched for loading/opening. Find.Imports (modname | ^) List all modules imported by module . The modules are listed together with their keys. Find.AllInfo ^ The selection must be of the form item = pattern where item is one of the headings in an info element (Title, Author, ...) and pattern is any character sequence up to the end of the selection. Lists all modules specified with Find.Domain containing the pattern in their info elements. Find.DoubleFiles ("all" | filename) "all" : Search all paths beginning at Oberon-root for multiple files. filename: Search all paths beginning at Oberon-root for multiple files with name 'filename'. Patterns are allowed. ------------------------------------------------------------*)Syntax10i.Scn.Fnt 8MarkElemsAlloc=.8#Syntax10.Scn.Fnt.. BEGIN Files.ReadBytes(r, i, 2) END ReadInt; 8 =/8#Syntax10.Scn.Fnt// BEGIN Files.ReadBytes(r, i, 4) END ReadLInt; 8 X- 8#Syntax10.Scn.Fnt VAR t: Texts.Text; beg, end, time: LONGINT; r: Texts.Reader; BEGIN t := Oberon.Par.text; Texts.OpenScanner(s, t, Oberon.Par.pos); Texts.Scan(s); IF (s.class = Texts.Char) & (s.c = "^") THEN Oberon.GetSelection(t, beg, end, time); IF time >= 0 THEN Texts.OpenScanner(s, t, beg); Texts.Scan(s) END END; IF (s.class = Texts.Char) & (s.c = Texts.ElemChar) THEN Texts.OpenReader(r, t, beg); Texts.ReadElem(r); s.elem := r.elem END; END ScanPar; 8 X-08#Syntax10.Scn.Fnt VAR v: MenuViewers.Viewer; x, y: INTEGER; BEGIN Oberon.AllocateUserViewer(0, x, y); v := MenuViewers.New(TextFrames.NewMenu(name, "^Edit.Menu.Text"), TextFrames.NewText(t, 0), TextFrames.menuH, x, y) END OpenViewer; 8p_VersionElemsAllocBeg#Syntax10.Scn.FntPowerMac WindowsPowerMacPowerMac Windows=Syntax10.Scn.Fnt MarkElemsAllocX-Y@8FoldElemsNew#Syntax10.Scn.Fnt VAR ch: CHAR; i, dummy, entries, varentries,cmds, ptrs: INTEGER; ldummy: LONGINT; error: BOOLEAN; BEGIN Files.Set(r, f, 6); error := FALSE; ReadInt(r, varentries); ReadInt(r, entries); ReadInt(r, cmds); ReadInt(r, ptrs); ReadInt(r, dummy); ReadInt(r, imps); ReadInt(r, dummy); ReadInt(r, dummy); ReadLInt(r, ldummy); ReadInt(r, dummy); ReadInt(r, dummy); ReadLInt(r, key); REPEAT Files.Read(r, ch) UNTIL ch = 0X; (*skip name*) Files.Read(r, ch); IF ch = 08CX THEN Files.Set(r, f, Files.Pos(r) + 4*varentries ); (*skip varentries*) Files.Read(r, ch) ELSE error := TRUE END; IF ch = 082X THEN Files.Set(r, f, Files.Pos(r) + 2*entries ); (*skip entries*) Files.Read(r, ch) ELSE error := TRUE END; IF ch = 083X THEN FOR i := 1 TO cmds DO (*skip commands*) REPEAT Files.Read(r, ch) UNTIL ch = 0X; ReadInt(r, dummy) END ; Files.Read(r, ch) ELSE error := TRUE END; IF ch = 084X THEN Files.Set(r, f, Files.Pos(r) + 4*ptrs ); (*skip pointer offsets*) Files.Read(r, ch) ELSE error := TRUE END; IF error OR (ch # 085X) THEN Out.String("-- damaged object file$"); imps := 0 END END SkipToImports; 8fPROCEDURE SkipToImports(VAR r: Files.Rider; f : Files.File; VAR key: LONGINT; VAR imps : INTEGER);  X-Z828p_VersionElemsAllocEnd X-<8#Syntax10.Scn.Fnt CONST bufSize = 31744; (*2**15 - 1024*) VAR f: Files.File; r: Files.Rider; n, pos: LONGINT; i, j, i0: INTEGER; ch, patj: CHAR; found: BOOLEAN; tab: ARRAY 265 OF SHORTINT; text: ARRAY bufSize OF CHAR; BEGIN (*----- open file *) f := Files.Old(fn); IF f = NIL THEN RETURN END; pos := 0; (*----- initialize tab *) Texts.WriteString(w, "--- "); Texts.WriteString(w, fn); Texts.WriteLn(w); FOR i := 0 TO 255 DO tab[i] := SHORT(m) END; FOR i := 0 TO m-2 DO tab[ORD(pat[i])] := SHORT(m - i - 1) END; patj := pat[m-1]; found := FALSE; LOOP (*----- read text[0..n-1] *) n := Files.Length(f) - pos; IF n > bufSize THEN n := bufSize END; IF n < m THEN EXIT END; Files.Set(r, f, pos); Files.ReadBytes(r, text, n); (*----- search pat in text[0..n-1] *) i := m - 1; j := i; WHILE i < n DO IF text[i] = patj THEN i0 := i; REPEAT DEC(i); DEC(j) UNTIL (j < 0) OR (text[i] # pat[j]); IF j < 0 THEN (*------ found: print result *) found := TRUE; WHILE (i >= 0) & (text[i] # CR) & (text[i] >= TAB) DO DEC(i) END; INC(i); Files.Set(r, f, pos + i); REPEAT Files.Read(r, ch); Texts.Write(w, ch); INC(i) UNTIL (ch = CR) OR (ch < TAB) OR (r.eof) ELSE i := i + tab[ORD(text[i])] END; IF i <= i0 THEN i := i0 + 1 END; j := m - 1 ELSE i := i + tab[ORD(text[i])] END END; pos := pos + i - m + 1 END; IF found THEN Texts.WriteLn(w); Texts.Append(out, w.buf) ELSE Texts.OpenWriter(w) END END Search; 8 X-e8BSyntax10.Scn.FntSyntax10b.Scn.FntTp_VersionElemsAllocBeg#Syntax10.Scn.FntPowerMac WindowsPowerMacPowerMac Windows#Syntax10.Scn.Fnt44Out.String(d.path); Out.Char(Directories.delimiter);p_VersionElemsAllocEnd> VAR f: Files.File; r: Files.Rider; ch: CHAR; s: ARRAY 32 OF CHAR; i, j, imps: INTEGER; key: LONGINT; BEGIN IF isDir OR ~Strings.Match(name, "*.Obj") THEN RETURN END; f := Files.Old(name); IF f # NIL THEN SkipToImports(r, f, key, imps); FOR i := 1 TO imps DO ReadLInt(r, key); j := 0; REPEAT Files.Read(r, ch); s[j] := ch; INC(j) UNTIL ch = 0X; IF s = fileName THEN Out.String(" ");  Out.String(name); Out.Ln END END END END CheckObjFile; 8 ==8#Syntax10.Scn.Fnt66 VAR d, cur, startup: Directories.Directory; BEGIN d := Directories.This(path); cur := Directories.Current(); startup := Directories.Startup(); IF (d # NIL) & (d.path # cur.path) THEN Directories.Enumerate(d, CheckObjFile); IF d.path = startup.path THEN startupDone := TRUE END END END CheckObjFileCB; 8 =$ 8#Syntax10.Scn.Fnt VAR V: Viewers.Viewer; f: Display.Frame; BEGIN IF Oberon.Par.vwr.dsc = Oberon.Par.frame THEN f := Oberon.Par.frame.next; IF (f # NIL) & (f IS TextFrames.Frame) THEN RETURN f(TextFrames.Frame) ELSE RETURN NIL END ELSE V := Oberon.FocusViewer; IF (V # NIL) & (V IS MenuViewers.Viewer) & (V.dsc # NIL) THEN f := V.dsc.next; IF (f # NIL) & (f IS TextFrames.Frame) THEN RETURN f(TextFrames.Frame) ELSE RETURN NIL END ELSE RETURN NIL END END END TargetFrame; 8 =.y8CSyntax10.Scn.FntSyntax10b.Scn.FntE VAR beg, end, delta: LONGINT; BEGIN delta := 200; LOOP beg := F.org; end := TextFrames.Pos(F, F.X + F.W, F.Y); IF (beg <= pos) & (pos < end) OR (delta = 0) THEN EXIT END ; TextFrames.Show(F, pos - delta); delta := delta DIV 2 END; Oberon.PassFocus(Viewers.This(F.X, F.Y)); TextFrames.SetCaret(F, pos) END SetCaret; 8K8#Syntax10.Scn.Fnt** BEGIN IF (name[i] = 0X) & (pat[j] = 0X) THEN RETURN TRUE ELSIF pat[j] # "*" THEN RETURN (CAP(name[i]) = CAP(pat[j])) & matches(name, pat, i+1, j+1) ELSE (* pat[j] = "*", name[i] may be 0X *) RETURN matches(name, pat, i, j+1) OR ((name[i] # 0X) & matches(name, pat, i+1, j)) END END matches; 84[8#Syntax10.Scn.Fnt VAR p: fBlock; i, len: LONGINT; BEGIN len := Strings.Length(s); IF (blockIdx + len) >= blockSize THEN INC(blockSize, chunkSize); NEW(p, blockSize); FOR i := 0 TO blockSize - chunkSize - 1 DO p[i] := fileBlock[i] END; fileBlock := p END; FOR i := 0 TO len-1 DO fileBlock[blockIdx+i] := s[i] END; fileBlock[blockIdx+len] := 0X; idx := blockIdx; INC(blockIdx, len+1) END Add; 8< 8CSyntax10.Scn.FntSyntax10i.Scn.Fnt0m (* 0: equal, 1: s > blockStr, 2: s < blockStr *) VAR i: LONGINT; BEGIN i := 0; WHILE (s[i] # 0X) & (fileBlock[pos + i] # 0X) DO IF s[i] > fileBlock[pos + i] THEN RETURN 1 ELSIF s[i] < fileBlock[pos + i] THEN RETURN 2 END; INC(i) END; IF (s[i] = 0X) & (fileBlock[pos + i] = 0X) THEN RETURN 0 ELSIF s[i] > fileBlock[pos + i] THEN RETURN 1 ELSIF s[i] < fileBlock[pos + i] THEN RETURN 2 END END Compare; 8 i8#Syntax10.Scn.Fntuu VAR i : LONGINT; BEGIN i := 0; WHILE fileBlock[pos+i] # 0X DO Out.Char(fileBlock[pos+i]); INC(i) END END Output; 8r78_Syntax10.Scn.FntLSyntax10i.Scn.Fnt+JY+k VAR path: ARRAY 256 OF CHAR; cur, prev, p: DoubleFile; dd: Dir; BEGIN IF ~isDir THEN IF (fileName = "all") OR (matches(name, fileName, 0, 0)) THEN cur := double; prev := double; WHILE (cur # NIL) & (Compare(name, cur.pos) = 1) DO prev := cur; cur := cur.next END; IF (cur = NIL) OR (Compare(name, cur.pos) = 2) THEN (* not found, insert at current position *) NEW(p); Add(name, p.pos); NEW(p.d); p.d.d := d; p.d.next := NIL; (* save directory structure *) p.next := cur; IF prev # cur THEN prev.next := p ELSE double := p END ELSE (* name = cur.name, add a directoy entry *) NEW(dd); dd.d := d; dd.next := cur.d; cur.d := dd END END ELSE IF (name # ".") & (name # "..") THEN COPY(d.path, path); Strings.Append(Directories.delimiter, path); Strings.Append(name, path); ScanDirectory(path) END END; END FindDoubleFile; 8/E8#Syntax10.Scn.Fnt VAR d: Directories.Directory; BEGIN d := Directories.This(path); IF d # NIL THEN Directories.Enumerate(d, FindDoubleFile); END END ScanDirectory; 88#Syntax10.Scn.FntMM VAR cur: DoubleFile; curd: Dir; BEGIN Out.Open; cur := double; WHILE cur # NIL DO IF cur.d.next # NIL THEN curd := cur.d; WHILE curd # NIL DO Out.String(curd.d.path); Out.String(Directories.delimiter); Output(cur.pos); Out.Ln; curd := curd.next END; Out.Ln; END; cur := cur.next END END PrintDoubles; 8 X-Syntax10b.Scn.Fnt^8Syntax10.Scn.Fnt8FoldElemsNew#Syntax10.Scn.Fnt VAR time: LONGINT; v: Viewers.Viewer; x: INTEGER; f: Display.Frame; BEGIN time := -1; x := 0; F := NIL; WHILE x < Display.Width DO v := Viewers.This(x, 0); WHILE v.state > 1 DO f := v.dsc.next; WITH f: TextFrames.Frame DO IF f.hasSel & (f.time > time) THEN F := f; pos := f.selbeg.pos; time := f.time END ELSE END; v := Viewers.Next(v) END; x := x + v.W END; IF F # NIL THEN TextFrames.RemoveSelection(F); TextFrames.RemoveCaret(F) END END GetSelection; 8>8#Syntax10.Scn.Fnt VAR x: LONGINT; BEGIN IF pos > TextFrames.Pos(f, f.X + f.W - 1, f.Y) THEN x := pos - 150; IF x < 0 THEN x := 0 END; TextFrames.Show(f, x) END; TextFrames.SetSelection(f, pos, pos+1) END ShowSelection; 8e VAR f1, f2: TextFrames.Frame; p1, p2: LONGINT; r1, r2: Texts.Reader; ch1, ch2: CHAR; PROCEDURE GetSelection(VAR F: TextFrames.Frame; VAR pos: LONGINT);  PROCEDURE ShowSelection(f: TextFrames.Frame; pos: LONGINT);  BEGIN GetSelection(f1, p1); GetSelection(f2, p2); IF (f1 # NIL) & (f2 # NIL) THEN Texts.OpenReader(r1, f1.text, p1); Texts.OpenReader(r2, f2.text, p2); REPEAT Texts.Read(r1, ch1); INC(p1); Texts.Read(r2, ch2); INC(p2); UNTIL (ch1 # ch2) OR (ch1 = 0X); IF (ch1 = 0X) OR (ch2 = 0X) THEN DEC(p1); DEC(p2) END; ShowSelection(f1, p1-1); ShowSelection(f2, p2-1) END END Diff; 8 X-g8#Syntax10.Scn.Fntww VAR s: Texts.Scanner; f, last: File; BEGIN file := NIL; last := NIL; ScanPar(s); WHILE (s.class = Texts.Name) OR (s.class = Texts.String) DO NEW(f); f.next := NIL; IF last = NIL THEN file := f ELSE last.next := f END; last := f; COPY(s.s, f.name); Texts.Scan(s); WHILE (s.class = Texts.Char) & (s.c = "/") DO Texts.Scan(s); Texts.Scan(s) END END END Domain; 8 X- !8Syntax10.Scn.Fntu8FoldElemsNew#Syntax10.Scn.FntII VAR t: Texts.Text; r: Texts.Reader; beg, end, time: LONGINT; ch: CHAR; BEGIN Oberon.GetSelection(t, beg, end, time); IF time > 0 THEN Texts.OpenReader(r, t, beg); m := 0; WHILE beg < end DO Texts.Read(r, ch); IF m < 127 THEN pat[m] := ch END; INC(m); INC(beg) END; pat[m] := 0X END END ReadPattern;8 VAR pat: ARRAY 128 OF CHAR; m: INTEGER; f: File; PROCEDURE ReadPattern (VAR pat: ARRAY OF CHAR; VAR m: INTEGER);  BEGIN ReadPattern(pat, m); out := TextFrames.Text(""); OpenViewer(pat, out); f := file; WHILE f # NIL DO Search(f.name, pat, m); f := f.next END END All; 8 j~  8QSyntax10.Scn.FntxSyntax10b.Scn.Fntm;( VAR type: Types.Type; f: TextFrames.Frame; r: Texts.Reader; s: Texts.Scanner; i: INTEGER; beg: LONGINT; BEGIN ScanPar(s); IF (s.class = Texts.Name) & (s.line = 0) THEN i := 0; WHILE (s.s[i] # 0X) & (s.s[i] # ".") DO findinfo.mod[i] := s.s[i]; INC(i) END; findinfo.mod[i] := 0X ELSIF (s.class = Texts.Char) & (s.c = Texts.ElemChar) THEN type := Types.TypeOf(s.elem); COPY(type.module.name, findinfo.mod) END; f := TargetFrame(); IF f # NIL THEN IF f.hasCar THEN beg := f.carloc.pos ELSE beg := 0 END; Texts.OpenReader(r, f.text, beg); LOOP Texts.ReadElem(r); IF r.elem = NIL THEN TextFrames.RemoveCaret(f); EXIT END; type := Types.TypeOf(r.elem); IF type.module.name = findinfo.mod THEN SetCaret(f, Texts.Pos(r)); EXIT END END; TextFrames.RemoveSelection(f) END END Elem; 8 X-8#Syntax10.Scn.FntBB VAR i: INTEGER; cur, startup: Directories.Directory; BEGIN In.Open; In.Name(fileName); IF In.Done THEN i := Strings.Length(fileName); IF (i >= 4) & (fileName[i-4] = ".") THEN fileName[i-4] := 0X END; Out.String("modules importing "); Out.String(fileName); Out.Char(":"); Out.Ln; cur := Directories.Current(); startup := Directories.Startup(); Directories.Enumerate(cur, CheckObjFile); startupDone := cur.path = startup.path; Directories.EnumeratePaths(CheckObjFileCB); IF ~startupDone THEN Directories.Enumerate(startup, CheckObjFile) END END END Clients; 8 X-!8#Syntax10.Scn.Fnt CONST TAB = 9X; VAR f: Files.File; r: Files.Rider; ch: CHAR; a: ARRAY 256 OF CHAR; i, j, imps: INTEGER; key: LONGINT; BEGIN In.Open; In.Name(a); IF In.Done THEN i := Strings.Length(a); IF (i >= 4) & (a[i-4] = ".") THEN a[i-4] := 0X END; Strings.Append(".Obj", a); f := Files.Old(a); IF f # NIL THEN SkipToImports(r, f, key, imps); Out.String(a); Out.Char(TAB); Out.Char("["); Out.Int(key, 0); Out.String("] imports:"); Out.Ln; FOR i := 1 TO imps DO ReadLInt(r, key); j := 0; REPEAT Files.Read(r, ch); a[j] := ch; INC(j) UNTIL ch = 0X; Out.Char(TAB); Out.String(a); Out.Char(TAB); Out.Char("["); Out.Int(key, 0); Out.Char("]"); Out.Ln END END END END Imports; 8 X-I8#Syntax10.Scn.Fnt VAR t, infoT: Texts.Text; r, infoR: Texts.Reader; ch: CHAR; menuItem: ARRAY 32 OF CHAR; text: ARRAY 10000 OF CHAR; i: LONGINT; found: BOOLEAN; BEGIN Texts.WriteString(w, "--- "); Texts.WriteString(w, fn); Texts.WriteString(w, ", matches for "); Texts.WriteString(w, item); Texts.WriteString(w, " = "); Texts.WriteString(w, pat); Texts.WriteLn(w); found := FALSE; NEW(t); Texts.Open(t, fn); Texts.OpenReader(r, t, 0); Texts.ReadElem(r); WHILE ~r.eot DO IF r.elem IS InfoElems.Elem THEN (* InfoElem found *) infoT := r.elem(InfoElems.Elem).menu; (* get text of InfoElem *) Texts.OpenReader(infoR, infoT, 0); Texts.Read(infoR, ch); WHILE ~infoR.eot DO WHILE ~infoR.eot & (ch # '"') DO Texts.Read(infoR, ch) END; Texts.Read(infoR, ch); i := 0; WHILE ~infoR.eot & (ch # '"') DO menuItem[i] := ch; Texts.Read(infoR, ch); INC(i) END; menuItem[i] := 0X; WHILE ~infoR.eot & ((ch = '"') OR (ch = " ") OR (ch = TAB) OR (ch = ":")) DO Texts.Read(infoR, ch) END; i := 0; WHILE ~infoR.eot & (ch # '"') & (i < LEN(text) - 1) DO IF (ch # TAB) & (ch # CR) THEN text[i] := ch; INC(i) END; Texts.Read(infoR, ch) END; text[i] := 0X; IF menuItem = item THEN IF Strings.Match(text, pat) THEN i := 0; found := TRUE; WHILE text[i] # 0X DO Texts.Write(w, text[i]); INC(i) END END END END END; Texts.ReadElem(r) END; IF found THEN Texts.WriteLn(w); Texts.Append(out, w.buf) ELSE Texts.OpenWriter(w) END END InfoSearch; 8 X-8Syntax10.Scn.FntKSyntax10i.Scn.Fnta8FoldElemsNew#Syntax10.Scn.Fnt}} VAR t: Texts.Text; r: Texts.Reader; s: Texts.Scanner; beg, end, time: LONGINT; ch: CHAR; BEGIN Oberon.GetSelection(t, beg, end, time); IF time > 0 THEN Texts.OpenScanner(s, t, beg); Texts.Scan(s); IF s.class IN {Texts.Name, Texts.String} THEN COPY(s.s, item); Texts.Scan(s); IF (s.class = Texts.Char) & (s.c = "=") THEN Texts.Scan(s); Texts.OpenReader(r, t, Texts.Pos(s) - 2); m := 0; beg := Texts.Pos(s) - 2; WHILE beg < end DO Texts.Read(r, ch); IF m < (LEN(pat) - 1) THEN pat[m] := ch END; INC(m); INC(beg) END; pat[m] := 0X END; END; END END ReadPattern;8 VAR pat: ARRAY 128 OF CHAR; item: ARRAY 32 OF CHAR; m: INTEGER; f: File; PROCEDURE ReadPattern;  BEGIN ReadPattern; out := TextFrames.Text(""); OpenViewer(pat, out); f := file; WHILE f # NIL DO InfoSearch(f.name, pat, item); f := f.next END END AllInfo; 8  8#Syntax10.Scn.Fnt VAR r: Texts.Reader; i: INTEGER; ch: CHAR; t: Texts.Text; beg, end, time: LONGINT; BEGIN blockSize := 8192; blockIdx := 0; NEW(fileBlock, blockSize); double := NIL; i := 0; Texts.OpenReader(r, Oberon.Par.text, Oberon.Par.pos); Texts.Read(r, ch); WHILE ((ch = " ") OR (ch = 09X)) & ~r.eot DO Texts.Read(r, ch) END ; IF ch = "^" THEN Oberon.GetSelection(t, beg, end, time); IF time >= 0 THEN Texts.OpenReader(r, t, beg); Texts.Read(r, ch); WHILE ((ch = " ") OR (ch = 09X)) & ~r.eot DO Texts.Read(r, ch) END END END; WHILE ~r.eot & (ch >= " ") DO fileName[i] := ch; INC(i); Texts.Read(r, ch) END; fileName[i] := 0X; ScanDirectory("$"); PrintDoubles; double := NIL; fileBlock := NIL END DoubleFiles; 8I> MODULE Find;  (* HM/CS/SC  *) Documentation IMPORT Display, Files, Directories, Oberon, Viewers, MenuViewers, TextFrames, Texts, InfoElems, In, Out, Strings, Types; CONST CR = 0DX; TAB = 09X; chunkSize = 4096; TYPE File = POINTER TO FileDesc; FileDesc = RECORD name: ARRAY 256 OF CHAR; next: File END ; FindInfo = RECORD time: LONGINT; mod: ARRAY 32 OF CHAR END ; Dir = POINTER TO DirDesc; DirDesc = RECORD d: Directories.Directory; next: Dir; END; DoubleFile = POINTER TO DoubleFileDesc; DoubleFileDesc = RECORD pos: LONGINT; d: Dir; next: DoubleFile END; fBlock = POINTER TO ARRAY OF CHAR; VAR file: File; fileName: ARRAY 256 OF CHAR; w: Texts.Writer; out: Texts.Text; startupDone: BOOLEAN; (* state for CheckObjFileCB *) findinfo: FindInfo; double: DoubleFile; fileBlock: fBlock; blockIdx, blockSize: LONGINT; PROCEDURE ^ScanDirectory (path: ARRAY OF CHAR); PROCEDURE ReadInt (VAR r: Files.Rider; VAR i: INTEGER);  PROCEDURE ReadLInt (VAR r: Files.Rider; VAR i: LONGINT);  PROCEDURE ScanPar (VAR s: Texts.Scanner);  PROCEDURE OpenViewer(name: ARRAY OF CHAR; t: Texts.Text);  PROCEDURE SkipToImports (VAR r: Files.Rider; f : Files.File; VAR key: LONGINT; VAR imps : INTEGER);  VAR ch: CHAR; i, dummy, entries, cmds, ptrs: INTEGER; ldummy: LONGINT; BEGIN Files.Set(r, f, 6); ReadInt(r, entries); ReadInt(r, cmds); ReadInt(r, ptrs); ReadInt(r, dummy); ReadInt(r, imps); Files.Set(r, f, 30); ReadLInt(r, key); REPEAT Files.Read(r, ch) UNTIL ch = 0X; (*skip name*) Files.Set(r, f, Files.Pos(r) + 1 + 2*entries + 1); (*skip entries*) FOR i := 1 TO cmds DO (*skip commands*) REPEAT Files.Read(r, ch) UNTIL ch = 0X; ReadInt(r, dummy) END; Files.Set(r, f, Files.Pos(r) + 1 + 4*ptrs + 1) (*skip pointer offsets*) END SkipToImports;  PROCEDURE Search (fn: ARRAY OF CHAR; pat: ARRAY OF CHAR; m: INTEGER);  PROCEDURE CheckObjFile (d: Directories.Directory; name: ARRAY OF CHAR; isDir: BOOLEAN; VAR continue: BOOLEAN);  PROCEDURE CheckObjFileCB (path: ARRAY OF CHAR; VAR continue: BOOLEAN);  PROCEDURE TargetFrame (): TextFrames.Frame; (*body frame or focus frame*)  PROCEDURE SetCaret (F: TextFrames.Frame; pos: LONGINT);  PROCEDURE matches (VAR name, pat: ARRAY OF CHAR; i, j: INTEGER): BOOLEAN; PROCEDURE Add(s: ARRAY OF CHAR; VAR idx: LONGINT); PROCEDURE Compare(s: ARRAY OF CHAR; pos: LONGINT): INTEGER; PROCEDURE Output(pos: LONGINT); PROCEDURE FindDoubleFile (d: Directories.Directory; name: ARRAY OF CHAR; isDir: BOOLEAN; VAR continue: BOOLEAN); PROCEDURE ScanDirectory (path: ARRAY OF CHAR); PROCEDURE PrintDoubles; (* -- commands -- *) PROCEDURE Diff*; (** compares two texts from the last two selections; sets selection to first difference *)  PROCEDURE Domain*; (** {filename} ~ *)  PROCEDURE All*; (** ^ *)  PROCEDURE Elem*; (** (modname | ^) *)  PROCEDURE Clients*;  PROCEDURE Imports*;  PROCEDURE InfoSearch (fn: ARRAY OF CHAR; pat: ARRAY OF CHAR; item: ARRAY OF CHAR);  PROCEDURE AllInfo*; (** ^ format: item = searchPattern where item IN {"Title", "Author", "Abstract", "Keywords", "Version", "From", "Until", "Hints", "Changes"}, searchPattern must match exactly or contain asterisks *)  PROCEDURE DoubleFiles*; (** "all" | filename *) BEGIN file := NIL; findinfo.time := -1; Texts.OpenWriter(w) END Find.