<<>> <> <> <> <> <> <> <> <> <> <> <<>> <> <> <<>> DIRECTORY PFS, FileNames, RefText, Rope, SystemNames; FileNamesOnPFSImpl: CEDAR MONITOR IMPORTS PFS, RefText, Rope, SystemNames EXPORTS FileNames = BEGIN LOR: TYPE = LIST OF ROPE; LORA: TYPE = LIST OF REF ANY; ROPE: TYPE = Rope.ROPE; <> lastPreSlash1: ROPE ¬ NIL; lastPostSlash1: ROPE ¬ NIL; lastPreSlash2: ROPE ¬ NIL; lastPostSlash2: ROPE ¬ NIL; lastUserName: ROPE ¬ NIL; lastHomeDirectory: ROPE ¬ NIL; FileWithSearchRules: PUBLIC PROC [root: ROPE, defaultExtension: ROPE, requireExtension: BOOL ¬ TRUE, requireExact: BOOL ¬ TRUE, searchRules: REF ANY] RETURNS [fullPath: ROPE ¬ NIL, ambiguous: BOOL ¬ FALSE] = { <> <> <> <> <> <> <> <> <> <> <> <> <> <<}>> <> <> <> <> <<}>> <> <> <> <<}>> <> <> <<}>> <<}>> <> fullPathName: BOOL ¬ FALSE; withExt: ROPE ¬ NIL; withStarExt: ROPE ¬ NIL; rules: LORA; ropeRules: LOR; list: LORA; ropeList: LOR; hasExtension: BOOL ¬ FALSE; hasVersion: BOOL ¬ FALSE; doRoot, doWithExt, doWithStarExt: BOOL ¬ FALSE; defaultExtLen: INT = Rope.Length[defaultExtension]; rootLen: INT ¬ Rope.Length[root ¬ ResolveRelativePath[root]]; Try: PROC [name: ROPE, wDir: ROPE ¬ NIL] RETURNS [found: BOOL] = { IF NOT Rope.IsEmpty[name] THEN { fullPathName: PFS.PATH ¬ NIL; Inner: PROC ~ { fullPathName ¬ PFS.FileInfo[name: PFS.PathFromRope[name] ! PFS.Error => IF error.group = user THEN CONTINUE].fullFName; }; IF wDir = NIL THEN Inner[] ELSE PFS.DoInWDir[PFS.PathFromRope[wDir], Inner]; fullPath ¬ PFS.RopeFromPath[fullPathName]; RETURN [fullPathName # NIL]; }; RETURN [FALSE]; }; UniqueMatch: PROC [name: ROPE, wDir: ROPE ¬ NIL] RETURNS [found: BOOL] = { lst: LOR ¬ NIL; p: PFS.NameProc = { continue ¬ lst = NIL; -- give up after this name if it is the second one IF lst # NIL THEN ambiguous ¬ TRUE; lst ¬ CONS[PFS.RopeFromPath[name], lst]; }; IF name.IsEmpty[] THEN { fullPath ¬ NIL; RETURN [TRUE]; }; BEGIN Inner: PROC ~ { PFS.EnumerateForNames[pattern: PFS.PathFromRope[name], proc: p ! PFS.Error => IF error.group = user THEN CONTINUE]; }; IF wDir = NIL THEN Inner[] ELSE PFS.DoInWDir[PFS.PathFromRope[wDir], Inner]; END; IF lst = NIL THEN RETURN [FALSE]; IF ambiguous THEN { fullPath ¬ NIL; RETURN [TRUE]; }; <> ambiguous ¬ TRUE; fullPath ¬ ConvertToSlashFormat[lst.first]; RETURN [TRUE]; }; IF rootLen = 0 THEN RETURN [NIL]; SELECT Rope.Fetch[root, 0] FROM '/, '[ => fullPathName ¬ TRUE; ENDCASE; FOR i: INT DECREASING IN [0..rootLen) DO SELECT Rope.Fetch[root, i] FROM '>, '], '/ => EXIT; '. => {hasExtension ¬ TRUE; EXIT}; '! => hasVersion ¬ TRUE; ENDCASE; ENDLOOP; IF requireExtension AND NOT hasExtension AND hasVersion THEN RETURN [NIL]; IF requireExtension AND hasExtension AND defaultExtLen # 0 THEN IF Rope.Find[s1: root, s2: defaultExtension, pos1: 0, case: FALSE] # (Rope.Index[s1: root, s2: "!", pos1: 0, case: TRUE] - defaultExtLen) THEN RETURN [NIL]; IF NOT hasExtension THEN { withExt ¬ Rope.Concat[root, defaultExtension]; withStarExt ¬ Rope.Cat[root, "*", defaultExtension]; IF NOT hasVersion THEN { withExt ¬ Rope.Concat[withExt, "!H"]; withStarExt ¬ Rope.Concat[withStarExt, "!H"]; }; }; doRoot ¬ NOT requireExtension OR hasExtension; doWithExt ¬ NOT hasExtension; doWithStarExt ¬ (NOT requireExact) AND (NOT hasExtension); IF fullPathName THEN { <> IF doRoot AND Try[name: root] THEN RETURN; IF doWithExt AND Try[name: withExt] THEN RETURN; <> IF doWithStarExt AND UniqueMatch[name: withStarExt] THEN RETURN; } ELSE { IF doRoot AND Try[name: root] THEN RETURN; WITH searchRules SELECT FROM lra: LORA => rules ¬ lra; lr: LOR => ropeRules ¬ lr; ENDCASE; list ¬ rules; ropeList ¬ ropeRules; IF doRoot THEN { WHILE list # NIL DO IF Try[name: root, wDir: NARROW[list.first, ROPE]] THEN RETURN; list ¬ list.rest; ENDLOOP; WHILE ropeList # NIL DO IF Try[name: root, wDir: ropeList.first] THEN RETURN; ropeList ¬ ropeList.rest; ENDLOOP; }; IF doWithExt AND Try[name: withExt] THEN RETURN; IF doWithExt THEN { list ¬ rules; ropeList ¬ ropeRules; WHILE list # NIL DO IF Try[name: withExt, wDir: NARROW[list.first, ROPE]] THEN RETURN; list ¬ list.rest; ENDLOOP; WHILE ropeList # NIL DO IF Try[name: withExt, wDir: ropeList.first] THEN RETURN; ropeList ¬ ropeList.rest; ENDLOOP; }; IF doWithStarExt THEN { IF UniqueMatch[name: withStarExt] THEN RETURN; list ¬ rules; ropeList ¬ ropeRules; WHILE list # NIL DO IF UniqueMatch[name: withStarExt, wDir: NARROW[list.first, ROPE]] THEN RETURN; list ¬ list.rest; ENDLOOP; WHILE ropeList # NIL DO IF UniqueMatch[name: withStarExt, wDir: ropeList.first] THEN RETURN; ropeList ¬ ropeList.rest; ENDLOOP; }; }; }; ParseState: TYPE = {idle, slash, oneDot, twoDots}; CurrentWorkingDirectory: PUBLIC PROC RETURNS [ROPE] = { RETURN [PFS.RopeFromPath[PFS.GetWDir[]]] }; ResolveRelativePath: PUBLIC PROC [path: ROPE] RETURNS [ROPE] = { <> <> i: INT ¬ 0; state: ParseState ¬ slash; pathLength: INT ¬ Rope.Length[path ¬ ConvertToSlashFormat[path]]; out: REF TEXT ¬ NIL; char: CHAR; usedCWD: BOOL ¬ FALSE; <> <> <> <> HandleDot: PROC [pos: INT] = { IF out = NIL THEN out ¬ RefText.New[pathLength]; IF pos - 1 > 0 THEN out ¬ RefText.AppendRope[to: out, from: path, start: 0, len: pos - 1]; <> IF pos >= pathLength THEN path ¬ NIL ELSE path ¬ path.Substr[pos + 1]; <> pathLength ¬ path.Length[]; -- recompute pathLength }; HandleDotDot: PROC [pos: INT] = { <> <> <> <> first: BOOL ¬ TRUE; <> IF out = NIL THEN out ¬ RefText.New[pathLength]; IF pos - 2 > 0 THEN out ¬ RefText.AppendRope[to: out, from: path, start: 0, len: pos - 2]; <> IF pos >= pathLength THEN path ¬ NIL ELSE path ¬ path.Substr[pos + 1]; <> pathLength ¬ path.Length[]; -- recompute pathLength <> pos ¬ out.length - 1; -- index of last character in out DO IF pos < 0 THEN { out.length ¬ 0; IF usedCWD THEN EXIT; usedCWD ¬ TRUE; out ¬ RefText.AppendRope[to: out, from: PFS.RopeFromPath[PFS.GetWDir[]]]; pos ¬ out.length - 1; -- continue scanning at end of CWD LOOP; }; IF out[pos] = '/ AND NOT first THEN { out.length ¬ pos + 1; EXIT; }; pos ¬ pos - 1; first ¬ FALSE; ENDLOOP; }; IF pathLength = 0 THEN RETURN [path]; IF path.Fetch[0] = '~ THEN { <> usedCWD ¬ TRUE; out ¬ RefText.AppendRope[to: RefText.New[pathLength], from: HomeDirectory[]]; out.length ¬ out.length - 1; state ¬ idle; pathLength ¬ Rope.Length[path ¬ Rope.Substr[path, 1]]; }; DO IF i >= pathLength THEN EXIT; char ¬ path.Fetch[i]; SELECT state FROM idle => IF char = '/ THEN state ¬ slash; slash => { SELECT char FROM '. => state ¬ oneDot; '/ => state ¬ slash; ENDCASE => state ¬ idle; }; oneDot => { SELECT char FROM '. => state ¬ twoDots; '/ => { <> HandleDot[i]; i ¬ 0; <> state ¬ slash; LOOP; }; ENDCASE => state ¬ idle; }; twoDots => { IF char = '/ THEN { <> HandleDotDot[i]; i ¬ 0; <> state ¬ slash; <> LOOP; } ELSE state ¬ idle; }; ENDCASE => ERROR; i ¬ i + 1; ENDLOOP; <> IF state = oneDot OR state = twoDots THEN { <> IF state = oneDot THEN HandleDot[pathLength] ELSE HandleDotDot[pathLength]; path ¬ Rope.FromRefText[out]; } ELSE IF out # NIL THEN path ¬ Rope.Concat[Rope.FromRefText[out], path]; IF path.IsEmpty[] AND NOT usedCWD THEN path ¬ PFS.RopeFromPath[PFS.GetWDir[]]; RETURN [path]; }; ConvertToSlashFormat: PUBLIC ENTRY PROC [path: ROPE] RETURNS [ROPE] = { ENABLE UNWIND => NULL; SELECT TRUE FROM Rope.Equal[path, lastPreSlash1] => RETURN [lastPostSlash1]; Rope.Equal[path, lastPreSlash2] => RETURN [lastPostSlash2]; ENDCASE => { ToSlash: Rope.TranslatorType = { <> gnu: CHAR ¬ SELECT old FROM '[, '], '<, '> => '/ ENDCASE => old; RETURN[gnu]; }; <> pos: INT ¬ path.Find["]<"]; lastPreSlash1 ¬ lastPreSlash2; lastPostSlash1 ¬ lastPostSlash2; lastPreSlash2 ¬ path; IF pos # -1 THEN path ¬ Rope.Replace[base: path, start: pos, len: 2, with: "/"]; IF Rope.SkipTo[s: path, pos: 0, skip: "[]<>"] # path.Length[] THEN path ¬ Rope.Translate[base: path, translator: ToSlash]; lastPostSlash2 ¬ path; RETURN [path]; }; }; HomeDirectory: PUBLIC ENTRY PROC RETURNS [ROPE] = { ENABLE UNWIND => NULL; user: ROPE ¬ SystemNames.UserName[]; --UserCredentials.Get[].name; IF ( NOT Rope.Equal[lastUserName, user] ) THEN { lastHomeDirectory ¬ SystemNames.SimpleHomeDirectory[]; lastUserName ¬ user; }; RETURN [lastHomeDirectory]; }; IsADirectory: PUBLIC PROC [path: ROPE] RETURNS [BOOL] = { length: INT ¬ Rope.Length[path]; IF length # 0 THEN SELECT Rope.Fetch[path, length-1] FROM '/, '], '> => RETURN [TRUE]; ENDCASE; RETURN [FALSE]; }; IsAPattern: PUBLIC PROC [path: ROPE] RETURNS [BOOL] = { RETURN [Rope.SkipTo[path, 0, "*"] # Rope.Length[path]]; }; GetShortName: PUBLIC PROC [path: ROPE, stripOffVersionNumber: BOOL ¬ TRUE] RETURNS [ROPE] = { len: INT ¬ Rope.Length[path]; bang: INT ¬ len; pos: INT ¬ len; WHILE pos # 0 DO np: INT ¬ pos - 1; c: CHAR ¬ Rope.Fetch[path, np]; SELECT c FROM '! => IF stripOffVersionNumber THEN bang ¬ np; '>, '], '/ => RETURN [Rope.Substr[path, pos, bang-pos]]; ENDCASE; pos ¬ np; ENDLOOP; RETURN [Rope.Substr[path, 0, bang]]; }; Tail: PUBLIC PROC [s: ROPE, char: CHAR] RETURNS [ROPE] = { <> pos: INT ¬ s.Length[]; WHILE pos # 0 DO IF Rope.Fetch[s, pos-1] = char THEN RETURN [Rope.Substr[s, pos]]; pos ¬ pos - 1; ENDLOOP; RETURN [s]; }; StripVersionNumber: PUBLIC PROC [path: ROPE] RETURNS [ROPE] = { pos: INT ¬ path.Find["!"]; IF pos = -1 THEN RETURN [path]; RETURN [path.Substr[0, pos]]; }; <<>> IsRemote: PUBLIC PROC [path: ROPE] RETURNS [BOOL] = { first, second: CHAR; IF path.Length[] < 2 THEN RETURN [FALSE]; first ¬ path.Fetch[0]; second ¬ path.Fetch[1]; SELECT first FROM '/ => RETURN [second # '/]; '[ => RETURN [second # ']]; ENDCASE; RETURN [FALSE]; }; <<>> InASubdirectory: PUBLIC PROC [parent: ROPE, path: ROPE] RETURNS [BOOL] = { pos: INT; path ¬ ConvertToSlashFormat[path]; parent ¬ ConvertToSlashFormat[parent]; pos ¬ Rope.Find[s1: path, s2: parent, case: FALSE]; IF pos = -1 THEN RETURN [FALSE]; RETURN [Rope.Find[s1: path, s2: "/", pos1: parent.Length[]] # -1]; }; FirstSubdirectory: PUBLIC PROC [parent: ROPE, path: ROPE] RETURNS [ROPE] = { pos: INT; end: INT; path ¬ ConvertToSlashFormat[path]; parent ¬ ConvertToSlashFormat[parent]; pos ¬ Rope.Find[s1: path, s2: parent, case: FALSE]; IF pos # 0 THEN RETURN [NIL]; pos ¬ parent.Length[]; IF pos = 0 THEN RETURN [NIL]; end ¬ Rope.Find[s1: path, s2: "/", pos1: pos]; IF end = -1 THEN RETURN [NIL]; RETURN [Rope.Substr[base: path, start: 0, len: end + 1]]; }; DirectoryContaining: PUBLIC PROC [path: ROPE, pos: INT] RETURNS [ROPE] = { <> WHILE pos > 0 DO c: CHAR ¬ Rope.Fetch[path, pos ¬ pos - 1]; SELECT c FROM '>, '], '/ => RETURN [Rope.Substr[path, 0, pos+1]]; ENDCASE; ENDLOOP; RETURN [NIL]; }; <<>> Directory: PUBLIC PROC [path: ROPE] RETURNS [ROPE] = { <> RETURN [DirectoryContaining[path, Rope.Length[path]]]; }; Parent: PUBLIC PROC [path: ROPE] RETURNS [ROPE] = { <> len: INT ¬ Rope.Length[path]; pos: INT ¬ len; first: BOOL ¬ TRUE; WHILE pos # 0 DO c: CHAR ¬ Rope.Fetch[path, pos - 1]; SELECT c FROM '>, '], '/ => IF pos # len THEN RETURN [Rope.Substr[path, 0, pos]]; ENDCASE; pos ¬ pos - 1; ENDLOOP; RETURN [NIL]; }; <> RelativizePath: PUBLIC PROC [path: ROPE] RETURNS [ROPE] = { homeDir: ROPE ¬ HomeDirectory[]; homeDirLen: INT ¬ Rope.Length[homeDir]; slashName: ROPE ¬ ConvertToSlashFormat[path]; IF Rope.Run[homeDir, 0, slashName, 0, FALSE] = homeDirLen THEN path ¬ Rope.Replace[path, 0, homeDirLen, "~/"]; RETURN [path]; }; END.