<> <> <> <<>> DIRECTORY BTree, Commander, DirectoryList, File, FS, FSBackdoor, FSDir, FSFileOps, FSName, IO, PrincOpsUtils, RefText, Rope; DirectoryListImpl: CEDAR PROGRAM IMPORTS BTree, Commander, File, FS, FSDir, FSName, IO, PrincOpsUtils, RefText, Rope EXPORTS DirectoryList ~ BEGIN ROPE: TYPE ~ Rope.ROPE; Count: PROC [volumeName: ROPE] RETURNS [n: INT _ 0] ~ TRUSTED { pp: FSName.ParsedFName ~ FSName.ParseCacheName[volumeName, "[]<>", FALSE].pn; volDesc: FSFileOps.VolumeDesc ~ pp.volDesc; Proc: UNSAFE PROC [entry: BTree.Entry] RETURNS [continue: BOOLEAN _ TRUE] ~ { n _ n + 1; }; [] _ BTree.EnumerateEntries[tree: volDesc.tree, key: NIL, relation: greaterEqual, Proc: Proc] }; GetText: UNSAFE PROC [textRep: LONG POINTER TO FSBackdoor.TextRep, text: REF TEXT] = UNCHECKED { text.length _ textRep.length; [] _ PrincOpsUtils.ByteBlt [ to: [ BASE[DESCRIPTOR[text]], 0, textRep.length ], from: [ BASE[DESCRIPTOR[textRep]], 0, textRep.length ] ]; }; zzz: ROPE ~ "\377"; aaa: ROPE ~ "\000"; CommonPrefixLength: PROC [path: ROPE, sub: BOOL] RETURNS [i: NAT] ~ { i _ Rope.Size[path]; WHILE i > 0 AND Rope.Fetch[path, i-1] # '> DO i _ i-1 ENDLOOP; IF NOT sub THEN { IF i>0 THEN i _ i-1; WHILE i > 0 AND Rope.Fetch[path, i-1] # '> DO i _ i-1 ENDLOOP; }; }; GetNextDirectory: PUBLIC PROC [path: ROPE, sub: BOOL _ FALSE] RETURNS [next: ROPE _ NIL] ~ TRUSTED { <> <> fullFName: ROPE; cp: FS.ComponentPositions; [fullFName, cp] _ FS.ExpandName[Rope.Concat[path, "*"]]; IF sub OR cp.subDirs.length # 0 THEN { text: REF TEXT _ RefText.ObtainScratch[512]; pp: FSName.ParsedFName ~ FSName.ParseCacheName[Rope.Substr[fullFName, cp.dir.start, cp.dir.length], path, FALSE].pn; volDesc: FSFileOps.VolumeDesc ~ pp.volDesc; key: ROPE _ Rope.Concat[Rope.Substr[fullFName, cp.subDirs.start, cp.subDirs.length+1], IF sub THEN aaa ELSE zzz]; commonPrefixLength: NAT _ CommonPrefixLength[key, sub]; matchProc: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept, stop: BOOLEAN _ FALSE] ~ { name: LONG POINTER TO FSBackdoor.TextRep = @entry[entry.nameBody]; IF name.length > text.length THEN text _ NEW[TEXT[name.length]]; GetText[name, text]; IF text.length > 0 AND text[0] = '[ THEN {text.length _ 0; stop _ TRUE; RETURN}; IF text.length <= commonPrefixLength OR Rope.Run[s1: LOOPHOLE[text], s2: key, case: FALSE] < commonPrefixLength THEN {text.length _ 0; stop _ TRUE; RETURN}; stop _ Rope.Find[s1: LOOPHOLE[text], s2: ">", pos1: commonPrefixLength] >= 0; }; newLength: NAT; FSDir.EnumerateEntries[vDesc: volDesc, start: Rope.Flatten[key], versions: bangLOnly, matchProc: matchProc, acceptProc: NIL]; newLength _ MIN[commonPrefixLength, text.length]; WHILE newLength < text.length AND text[newLength] # '> DO newLength _ newLength+1 ENDLOOP; IF newLength < text.length THEN newLength _ newLength+1; text.length _ newLength; WHILE text.length > 0 AND text[text.length-1] # '> DO text.length _ text.length - 1 ENDLOOP; IF text.length <= commonPrefixLength OR Rope.Run[s1: LOOPHOLE[text], pos1: 0, s2: fullFName, pos2: cp.subDirs.start, case: FALSE] < commonPrefixLength THEN text.length _ 0; IF text.length = Rope.Size[key]-1 AND Rope.Run[s1: LOOPHOLE[text], s2: key, case: FALSE] = text.length THEN text.length _ 0; IF text.length > 0 THEN { next _ Rope.Concat[Rope.Substr[fullFName, 0, cp.dir.start + cp.dir.length+1], Rope.FromRefText[text]]; }; RefText.ReleaseScratch[text]; }; }; RemoveSubDir: PUBLIC PROC [path: ROPE] RETURNS [ROPE] ~ { FOR i: INT DECREASING IN [0..Rope.Size[path]-1) DO IF Rope.Fetch[path, i] = '> THEN RETURN [Rope.Substr[path, 0, i+1]]; ENDLOOP; RETURN [NIL]; }; Levels: PROC [dir: ROPE] RETURNS [levels: NAT _ 0] ~ { FOR i: INT IN [0..Rope.Size[dir]) DO IF Rope.Fetch[dir, i] = '> THEN levels _ levels + 1; ENDLOOP; }; GetNext: PUBLIC PROC [path: ROPE, levelClip: NAT _ NAT.LAST] RETURNS [next: ROPE _ NIL] ~ { level: NAT _ Levels[path]; IF levelClip >= level THEN { next _ GetNextDirectory[path, TRUE]; } ELSE { WHILE levelClip+1 < level DO path _ RemoveSubDir[path]; level _ level - 1; ENDLOOP; }; WHILE Rope.Size[next] = 0 AND Rope.Size[path] # 0 DO next _ GetNextDirectory[path, FALSE]; IF next = NIL THEN path _ RemoveSubDir[path]; ENDLOOP; }; DirCommand: Commander.CommandProc ~ { <<[cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL]>> n: NAT _ LAST[NAT]; system: File.Volume ~ File.SystemVolume[]; n _ MIN[MAX[IO.GetInt[stream: IO.RIS[cmd.commandLine] ! IO.EndOfStream, IO.Error => CONTINUE], 0], n]; FOR volume: File.Volume _ File.NextVolume[NIL, FALSE], File.NextVolume[volume, FALSE] UNTIL volume = NIL DO dir: ROPE _ Rope.Cat["[]<", IF volume # system THEN File.LogicalInfo[volume].name ELSE NIL, ">"]; WHILE Rope.Size[dir] # 0 DO IO.PutRope[cmd.out, dir]; IO.PutChar[cmd.out, '\n]; dir _ GetNext[dir, n]; ENDLOOP; ENDLOOP; }; SubDirCommand: Commander.CommandProc ~ { <<[cmd: Commander.Handle] RETURNS [result: REF ANY _ NIL, msg: ROPE _ NIL]>> n: INTEGER _ 9999; dir, wd: ROPE _ NIL; dir _ FS.ExpandName["*"].fullFName; dir _ Rope.Substr[dir, 0, Rope.Size[dir]-1]; n _ MIN[MAX[IO.GetInt[stream: IO.RIS[cmd.commandLine] ! IO.EndOfStream, IO.Error => CONTINUE], -n], n]; IF n < 0 THEN { THROUGH [0..-n) WHILE Levels[dir] > 1 DO dir _ RemoveSubDir[dir] ENDLOOP; n _ 1; }; n _ MAX[n + Levels[dir] - 1, 0]; wd _ dir; WHILE Rope.Run[s1: dir, s2: wd, case: FALSE] = Rope.Size[wd] DO IO.PutRope[cmd.out, dir]; IO.PutChar[cmd.out, '\n]; dir _ GetNext[dir, n]; ENDLOOP; }; Commander.Register[key: "Dir", proc: DirCommand, doc: "List local directories and subdirectories, to the depth indicated"]; Commander.Register[key: "SubDir", proc: SubDirCommand, doc: "List local sub-directories of the current working directory, to the relative depth indicated"]; END.