<> <> <> <> <<>> DIRECTORY FileNames, FS USING [EnumerateForNames, Error, FileInfo, GetDefaultWDir, NameProc], List USING [AList, Assoc, PutAssoc], ProcessProps USING [GetPropList], RefText USING [AppendRope, New], Rope USING [Cat, Concat, Fetch, Find, FromRefText, Index, IsEmpty, Length, Replace, ROPE, SkipTo, Substr, Translate, TranslatorType], UserCredentials USING [Get]; FileNamesImpl: CEDAR PROGRAM IMPORTS FS, List, ProcessProps, RefText, Rope, UserCredentials EXPORTS FileNames = BEGIN LOR: TYPE = LIST OF ROPE; LORA: TYPE = LIST OF REF ANY; ROPE: TYPE = Rope.ROPE; 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; hasVersion: BOOL; doRoot, doWithExt, doWithStarExt: BOOL; Try: PROC [name: ROPE, wDir: ROPE _ NIL] RETURNS [found: BOOL] = { IF name.IsEmpty[] THEN RETURN[FALSE]; fullPath _ FS.FileInfo[name: name, wDir: wDir ! FS.Error => IF error.group = user THEN CONTINUE].fullFName; IF NOT fullPath.IsEmpty[] THEN { fullPath _ ConvertToSlashFormat[fullPath]; RETURN[TRUE]; }; RETURN[FALSE]; }; UniqueMatch: PROC [name: ROPE, wDir: ROPE _ NIL] RETURNS [found: BOOL] = { lst: LOR _ NIL; p: FS.NameProc = { continue _ lst = NIL; -- give up after this name if it is the second one IF lst # NIL THEN ambiguous _ TRUE; lst _ CONS[fullFName, lst]; }; IF name.IsEmpty[] THEN { fullPath _ NIL; RETURN[TRUE]; }; FS.EnumerateForNames[pattern: name, proc: p, wDir: wDir ! FS.Error => IF error.group = $user THEN CONTINUE]; IF lst = NIL THEN RETURN[FALSE]; IF ambiguous THEN { fullPath _ NIL; RETURN[TRUE]; }; <> ambiguous _ TRUE; fullPath _ ConvertToSlashFormat[lst.first]; RETURN[TRUE]; }; IF root.IsEmpty[] THEN RETURN[NIL]; root _ ResolveRelativePath[root]; fullPathName _ root.Fetch[0] = '/ OR root.Fetch[0] = '[; hasExtension _ root.Find["."] # -1; hasVersion _ root.Find["!"] # -1; IF requireExtension AND NOT hasExtension AND hasVersion THEN RETURN[NIL]; IF requireExtension AND hasExtension THEN { IF Rope.Find[s1: root, s2: defaultExtension, pos1: 0, case: FALSE] # (Rope.Index[s1: root, s2: "!", pos1: 0, case: TRUE] - defaultExtension.Length[]) THEN RETURN[NIL]; }; IF NOT hasExtension THEN { withExt _ Rope.Concat[root, defaultExtension]; withStarExt _ Rope.Cat[root, "*", defaultExtension]; IF root.Find["!"] = -1 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}; ResolveRelativePath: PUBLIC PROC [path: ROPE] RETURNS [ROPE] = { <> <> i: INT _ 0; state: ParseState _ slash; pathLength: INT; 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.Substr[0, 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.Substr[0, 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: CurrentWorkingDirectory[]]; 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; }; path _ ConvertToSlashFormat[path]; pathLength _ path.Length[]; IF pathLength = 0 THEN RETURN[path]; 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; '/ => { -- delete the "./" construct HandleDot[i]; i _ 0; -- continue scanning from beginning (since HandleDot has re-written path) state _ slash; LOOP; }; ENDCASE => state _ idle; }; twoDots => { IF char = '/ THEN { -- handle the "../" construct HandleDotDot[i]; i _ 0; -- continue scanning from beginning (since HandleDot has re-written path) 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 _ CurrentWorkingDirectory[]; RETURN[path]; }; ConvertToSlashFormat: PUBLIC PROC [path: ROPE] RETURNS [ROPE] = { pos: INT; ToSlash: Rope.TranslatorType = { new _ SELECT old FROM '[, '], '<, '> => '/ ENDCASE => old; }; <> pos _ path.Find["]<"]; 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 RETURN[path]; path _ Rope.Translate[base: path, translator: ToSlash]; RETURN[path]; }; CurrentWorkingDirectory: PUBLIC PROC RETURNS [ROPE] = { ra: REF ANY _ List.Assoc[key: $WorkingDirectory, aList: ProcessProps.GetPropList[]]; wDir: ROPE; length: INT; slashWDir: ROPE; IF ISTYPE[ra, ROPE] AND ra # NIL THEN { wDir _ NARROW[ra]; slashWDir _ ConvertToSlashFormat[wDir]; length _ slashWDir.Length[]; IF slashWDir = wDir AND length > 0 AND slashWDir.Fetch[length - 1] = '/ THEN RETURN[wDir]; -- EQ } ELSE slashWDir _ ConvertToSlashFormat[FS.GetDefaultWDir[]]; <> length _ slashWDir.Length[]; IF length > 0 AND slashWDir.Fetch[length - 1] # '/ THEN slashWDir _ Rope.Concat[slashWDir, "/"]; [] _ List.PutAssoc[key: $WorkingDirectory, val: slashWDir, aList: ProcessProps.GetPropList[]]; RETURN[slashWDir]; }; HomeDirectory: PUBLIC PROC RETURNS [ROPE] = { RETURN[Rope.Cat["///Users/", UserCredentials.Get[].name, "/"]]; }; IsADirectory: PUBLIC PROC [path: ROPE] RETURNS [BOOL] = { length: INT; path _ ConvertToSlashFormat[path]; length _ path.Length[]; IF length = 0 THEN RETURN[FALSE]; RETURN[path.Fetch[length-1] = '/]; }; IsAPattern: PUBLIC PROC [path: ROPE] RETURNS [BOOL] = { RETURN[path.Find["*"] # -1]; }; GetShortName: PUBLIC PROC [path: ROPE, stripOffVersionNumber: BOOL _ TRUE] RETURNS [ROPE] = { path _ ConvertToSlashFormat[path]; IF stripOffVersionNumber THEN { <> pos: INT _ path.Find["!"]; IF pos # -1 THEN path _ path.Substr[0, pos]; }; RETURN[Tail[s: path, char: '/]]; }; Tail: PUBLIC PROC [s: ROPE, char: CHAR] RETURNS [ROPE] = { <> pos: INT _ s.Length[] - 1; IF pos < 0 THEN RETURN[NIL]; DO IF s.Fetch[pos] = char THEN RETURN[s.Substr[pos + 1]]; IF pos = 0 THEN RETURN[s]; pos _ pos - 1; ENDLOOP; }; StripVersionNumber: PUBLIC PROC [path: ROPE] RETURNS [ROPE] = { pos: INT; path _ ConvertToSlashFormat[path]; pos _ path.Find["!"]; IF pos = -1 THEN RETURN[path]; RETURN[path.Substr[0, pos]]; }; <<>> IsRemote: PUBLIC PROC [path: ROPE] RETURNS [BOOL] = { first, second: CHAR; path _ ConvertToSlashFormat[path]; IF path.Length[] < 2 THEN ERROR; first _ path.Fetch[0]; second _ path.Fetch[1]; RETURN[NOT ((first = '/ AND second = '/) OR (first = '[ AND second = ']))]; }; <<>> InASubdirectory: PUBLIC PROC [parent: ROPE, path: ROPE] RETURNS [BOOL] = { pos: INT; path _ ConvertToSlashFormat[path]; 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]; 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] = { <> length: INT; path _ ConvertToSlashFormat[path]; length _ path.Length[]; IF length = 0 THEN RETURN[NIL]; pos _ MIN[pos, length-1]; DO IF path.Fetch[pos] = '/ THEN RETURN[Rope.Substr[base: path, start: 0, len: pos + 1]]; IF pos = 0 THEN RETURN[NIL]; pos _ pos - 1; ENDLOOP; }; <<>> Directory: PUBLIC PROC [path: ROPE] RETURNS [ROPE] = { <> c: CHAR; pos: INT; path _ ConvertToSlashFormat[path]; pos _ path.Length[]; DO pos _ pos - 1; IF pos < 0 THEN RETURN[NIL]; c _ path.Fetch[pos]; IF c = '/ OR c = '> OR c = '] THEN RETURN[Rope.Substr[base: path, start: 0, len: pos + 1]]; ENDLOOP; }; Parent: PUBLIC PROC [path: ROPE] RETURNS [ROPE] = { <> pathLength: INT; pos: INT; path _ ConvertToSlashFormat[path]; pathLength _ path.Length[]; IF pathLength <= 1 THEN RETURN[NIL]; pos _ IF path.Fetch[pathLength - 1] = '/ THEN pathLength - 2 ELSE pathLength - 1; RETURN [DirectoryContaining[path: path, pos: pos]]; }; END. January 15, 1984 6:01 pm, Stewart, bulletproofing, fix ResolveRelativePath