FileNamesImpl.mesa
L. Stewart, December 13, 1983 6:43 pm
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 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.
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];
};
Exactly one match
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 {
try the exact matches
IF doRoot AND Try[name: root] THEN RETURN;
IF doWithExt AND Try[name: withExt] THEN RETURN;
try for a unique match
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;
};
};
};
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.
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;
};
Convert to slashes
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[]];
Store back the altered version.
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 {
Strip off the version number
pos: INT ← path.Find["!"];
IF pos # -1 THEN path ← path.Substr[0, pos];
};
If path is in slash format then get the part of path after the last '/
IF path.Find["/"] # -1 THEN RETURN[Tail[s: path, char: '/]];
If path is in bracket format then get the part of path after the last '>
IF path.Find[">"] # -1 THEN RETURN[Tail[s: path, char: '>]];
IF path.Find["]"] # -1 THEN RETURN[Tail[s: path, char: ']]];
RETURN[path];
};
Tail returns the part of a rope after the last instance of char.
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]];
};
Return the prefix of the part of name before pos which is the name of a directory.
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;
};
Return the directory part of a name.
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.