DIRECTORY FileNames, FS USING [EnumerateForNames, Error, FileInfo, GetDefaultWDir, NameProc], List USING [AList, Assoc, PutAssoc], ProcessProps USING [GetPropList], Rope USING [Cat, Concat, Equal, Fetch, Find, Index, IsEmpty, Length, Replace, ROPE, SkipTo, Substr, Translate, TranslatorType], UserCredentials USING [Get]; FileNamesImpl: CEDAR PROGRAM IMPORTS FS, List, ProcessProps, Rope, UserCredentials EXPORTS FileNames = BEGIN FileWithSearchRules: PUBLIC PROC [root: Rope.ROPE, defaultExtension: Rope.ROPE, requireExtension: BOOL _ TRUE, requireExact: BOOL _ TRUE, searchRules: REF ANY] RETURNS [fullPath: Rope.ROPE _ NIL, ambiguous: BOOL _ FALSE] = { fullPathName: BOOL _ FALSE; withExt: Rope.ROPE _ NIL; withStarExt: Rope.ROPE _ NIL; rules: LIST OF REF ANY; ropeRules: LIST OF Rope.ROPE; list: LIST OF REF ANY; ropeList: LIST OF Rope.ROPE; hasExtension: BOOL; hasVersion: BOOL; doRoot, doWithExt, doWithStarExt: BOOL; Try: PROC [name: Rope.ROPE, wDir: Rope.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.ROPE, wDir: Rope.ROPE _ NIL] RETURNS [found: BOOL] = { lst: LIST OF Rope.ROPE _ 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: LIST OF REF ANY => rules _ lra; lr: LIST OF Rope.ROPE => ropeRules _ lr; ENDCASE; list _ rules; ropeList _ ropeRules; IF doRoot THEN { WHILE list # NIL DO IF Try[name: root, wDir: NARROW[list.first, Rope.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.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.ROPE]] THEN RETURN; list _ list.rest; ENDLOOP; WHILE ropeList # NIL DO IF UniqueMatch[name: withStarExt, wDir: ropeList.first] THEN RETURN; ropeList _ ropeList.rest; ENDLOOP; }; }; }; ResolveRelativePath: PUBLIC PROC [path: Rope.ROPE] RETURNS [Rope.ROPE] = { wDir: Rope.ROPE; pDir: Rope.ROPE; wDirLength: INT; current: BOOL _ path.Find["./"] = 0; parent: BOOL _ path.Find["../"] = 0; wDir _ CurrentWorkingDirectory[]; wDirLength _ wDir.Length[]; FOR i: INT DECREASING IN [0..wDirLength - 1) DO IF wDir.Fetch[i] = '/ THEN { pDir _ wDir.Substr[0, i + 1]; EXIT; }; REPEAT FINISHED => ERROR; ENDLOOP; IF path.Equal["."] THEN RETURN [wDir]; IF path.Equal[".."] THEN RETURN [pDir]; IF NOT current AND NOT parent THEN RETURN[path]; IF current THEN RETURN [Rope.Concat[wDir, path.Substr[2]]] ELSE RETURN [Rope.Concat[pDir, path.Substr[3]]]; }; ConvertToSlashFormat: PUBLIC PROC [path: Rope.ROPE] RETURNS [Rope.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.ROPE] = { ra: REF ANY _ List.Assoc[key: $WorkingDirectory, aList: ProcessProps.GetPropList[]]; wDir: Rope.ROPE; length: INT; slashWDir: Rope.ROPE; IF ISTYPE[ra, Rope.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.ROPE] = { RETURN[Rope.Cat["///Users/", UserCredentials.Get[].name, "/"]]; }; IsADirectory: PUBLIC PROC [path: Rope.ROPE] RETURNS [BOOL] = { length: INT _ path.Length[]; last: CHAR; IF length = 0 THEN RETURN[FALSE]; last _ path.Fetch[length-1]; RETURN[last = '/ OR last = '> OR last = ']]; }; IsAPattern: PUBLIC PROC [path: Rope.ROPE] RETURNS [BOOL] = { RETURN[path.Find["*"] # -1]; }; GetShortName: PUBLIC PROC [path: Rope.ROPE, stripOffVersionNumber: BOOL _ TRUE] RETURNS [Rope.ROPE] = { IF stripOffVersionNumber THEN { pos: INT _ path.Find["!"]; IF pos # -1 THEN path _ path.Substr[0, pos]; }; IF path.Find["/"] # -1 THEN RETURN[Tail[s: path, char: '/]]; IF path.Find[">"] # -1 THEN RETURN[Tail[s: path, char: '>]]; IF path.Find["]"] # -1 THEN RETURN[Tail[s: path, char: ']]]; RETURN[path]; }; Tail: PUBLIC PROC [s: Rope.ROPE, char: CHAR] RETURNS [Rope.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.ROPE] RETURNS [Rope.ROPE] = { pos: INT _ path.Find["!"]; IF pos = -1 THEN RETURN[path]; RETURN[path.Substr[0, pos]]; }; IsRemote: PUBLIC PROC [path: Rope.ROPE] RETURNS [BOOL] = { first, second: CHAR; 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.ROPE, path: Rope.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.ROPE, path: Rope.ROPE] RETURNS [Rope.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.ROPE, pos: INT] RETURNS [Rope.ROPE] = { length: INT _ 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.ROPE] RETURNS [Rope.ROPE] = { pos: INT _ path.Length[]; c: CHAR; 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; }; END. ’FileNamesImpl.mesa L. Stewart, December 13, 1983 6:43 pm FileWithSearchRules uses the working directory and the search rules to try to translate the short name of a file into a full path name. Calls ResolveRelativePath first. NIL is returned if the file cannot be found. searchRules should either be a LIST OF REF ANY or a LIST OF Rope.ROPE. If requireExtension, then if root has an extension, it must be the same as defaultExtension. FileWithSearchRules adds !H to all pattern matches IF requireExact = TRUE, then FileWithSearchRules will not try for a unique pattern match, but will only check exact names. FileWithSearchRules returns NIL and ambiguous = TRUE if it finds more than one matching file. FileNames returns not-NIL and ambiguous = TRUE if the result was found as a consequence of an exact pattern match. FileWithSearchRules uses the following rules: IF root is a full path name then { tries root tries Concat[root, defaultExtension] tries for a unique match for Cat[root, *, defaultExtension] } ELSE { tries root (automatically used $WorkingDirectory) FOR wdir _ each element of search rules { tries Concat[wdir, root] } tries Concat[root, defaultExtension] (automatically used $WorkingDirectory) FOR wdir _ each element of search rules { tries Cat[wdir, root, defaultExtension] } IF NOT requireExact THEN FOR wdir _ each element of search rules { tries for a unique match for Cat[wdir, root, *, defaultExtension] } } Returns NIL if can't find it. Exactly one match try the exact matches try for a unique match If path starts with ./ or ../, ResolveRelativePath converts it into the equivalent full path name using the $WorkingDirectory property on the process properties list. If path is exactly . or .., ResolveRelativePath converts it to the current or parent directory. Convert to slashes Store back the altered version. Strip off the version number If path is in slash format then get the part of path after the last '/ If path is in bracket format then get the part of path after the last '> Tail returns the part of a rope after the last instance of char. Return the prefix of the part of name before pos which is the name of a directory. Return the directory part of a name. Κ¬˜šœ™J™%—J™šΟk ˜ Jšœ ˜ Jšœœ@˜HJšœœ˜$Jšœ œ˜!JšœœDœ-˜Jšœœ˜—J˜šœœ˜Jšœœ,œ ˜GJšœ˜—J˜Icode2šœΧ™ΧKšœF™FKšœ\™\K™2Kšœz™zKšœ]™]Kšœr™ršœ-™-šœ"™"Jšœ ™ Jšœ$™$Jšœ;™;J™—šœ™Jšœ1™1šœ)™)Jšœ™J™—JšœK™Kšœ)™)Jšœ'™'J™—šœB™BJšœA™AJ™—J™—Jšœ™—š Οnœœœ œœœœœœœœœœœ œœ˜ΰJšœœœ˜Jšœœœ˜Jšœœœ˜Jš œœœœœ˜Jšœ œœœ˜Jš œœœœœ˜Jšœ œœœ˜Jšœœ˜Jšœ œ˜Jšœ"œ˜'šžœœ œ œœœ œ˜LJšœœœœ˜%Jš œ œ#œ œœœ ˜kšœœœ˜ Jšœ*˜*Jšœœ˜ J˜—Jšœœ˜J˜—šž œœ œ œœœ œ˜TJš œœœœœ˜šœœ ˜JšœœΟc2˜IJšœœœ œ˜#Jšœœ˜Jšœ˜—J˜šœœ˜Jšœ œ˜Jšœœ˜ J˜—Jš œ8œ œœœ˜lJš œœœœœ˜ šœ œ˜Jšœ œ˜Jšœœ˜ J˜—J™Jšœ œ˜Jšœ+˜+Jšœœ˜ J˜—J˜Jšœœœœ˜#Jšœ!˜!Jšœ"œ˜8Jšœ#˜#Jšœ!˜!Jšœœœœ œœœ˜Išœœœ˜+Jš œ:œ2œœœœ˜§J˜—šœœœ˜Jšœ.˜.Jšœ4˜4šœœ˜Jšœ%˜%Jšœ-˜-J˜—J˜—Jšœ œœ˜.Jšœ œ˜Jšœœœœ˜:šœœ˜J™Jšœœœœ˜*Jšœ œœœ˜0J™Jšœœ œœ˜@J˜—šœ˜Jšœœœœ˜*šœ œ˜Jš œœœœœ˜$Jšœœœœ˜(Jšœ˜—J˜ J˜šœœ˜šœœ˜Jš œœœœœ˜DJšœ˜Jšœ˜—šœ œ˜Jšœ'œœ˜5Jšœ˜Jšœ˜—J˜—Jšœ œœœ˜0šœ œ˜J˜ J˜šœœ˜Jš œœœœœ˜GJšœ˜Jšœ˜—šœ œ˜Jšœ*œœ˜8Jšœ˜Jšœ˜—J˜—šœœ˜Jšœ œœ˜.J˜ J˜šœœ˜Jš œ&œœœœ˜SJšœ˜Jšœ˜—šœ œ˜Jšœ6œœ˜DJšœ˜Jšœ˜—J˜—J˜—Jšœ˜—Icode˜Jšœ¦™¦Jšœ_™_š žœœœ œœœ˜JJšœ œ˜Jšœ œ˜Jšœ œ˜Jšœ œ˜$Jšœœ˜$Jšœ!˜!Jšœ˜š œœ œœ˜/šœœ˜Jšœ˜Jšœ˜J˜—Jš˜Jšœœ˜Jšœ˜—Jšœœœ˜&Jšœœœ˜'Jš œœ œœœœ˜0Jšœ œœ$˜:Jšœœ%˜0J˜—J˜š žœœœ œœœ˜KJšœœ˜ šœ ˜ šœœ˜J˜Jšœ˜—J˜—J™Jšœ˜Jšœ œ@˜PJšœ<œœ˜PJšœ7˜7Jšœ˜ J˜—L˜š žœœœœœ˜Lšœœ˜Lšœœ˜ Lšœ œœœ˜!Lšœ˜Lšœ œ œ ˜,L˜—L˜š ž œœœ œœœ˜