<> <> <> <> <> <<>> DIRECTORY PBasics USING [BoundsCheck], PFSNames, Rope USING [CompareSubstrs, ROPE]; PFSNamesImpl: CEDAR PROGRAM IMPORTS PBasics, PFSNames, Rope EXPORTS PFSNames ~ BEGIN OPEN PFSNames; <> PATH: TYPE = REF PathObject; PathObject: TYPE = PFSNames.PathObject; PrivatePathObject: PUBLIC TYPE = PACKED RECORD [ comps: PRIVATE Components _ NIL, <> <> <> lastComp: Component _ [], <> <> absolute: BOOL _ FALSE, < path is absolute, i.e. begins with protocol, server, etc.>> <<~absolute=> path is relative to some, as yet unspecified, working directory>> <> directory: BOOL _ FALSE, < name is syntactically that of a directory>> <<~directory=> name is syntactically that of a file>> unparsed: REF, <> parent: PATH _ NIL <> ]; ComponentSequence: TYPE = RECORD [SEQUENCE count: [0..NAT.LAST) OF Component]; Components: TYPE = REF ComponentSequence; <> NarrowPath: PUBLIC PROC [r: REF ANY] RETURNS [PATH] ~ { RETURN [NARROW[r, PATH]]; }; IsPath: PUBLIC PROC [r: REF ANY] RETURNS [BOOL] ~ { RETURN [ISTYPE[r, PATH]]; }; <> <<>> ConstructName: PUBLIC PROC [components: LIST OF Component, absolute, directory, reverse: BOOL _ FALSE, unparsed: REF _ NIL] RETURNS [name: PATH _ NIL] ~ { <> count: NAT _ 0; name _ NEW [PathObject _ [NEW[PrivatePathObject _ []]]]; name.absolute _ absolute; name.directory _ directory; name.unparsed _ unparsed; <> FOR l: LIST OF Component _ components, l.rest WHILE l#NIL DO count _ count+1; ENDLOOP; <> SELECT count FROM 0 => { name.absolute _ name.directory _ absolute OR directory; RETURN; }; 1 => NULL; ENDCASE => name.comps _ NEW[ComponentSequence[count-1]]; <> { l: LIST OF Component _ components; IF reverse THEN { name.lastComp _ l.first; l _ l.rest; FOR i: NAT DECREASING IN [0..count-1) DO name.comps[i] _ l.first; l _ l.rest ENDLOOP; } ELSE { FOR i: NAT IN [0..count-1) DO name.comps[i] _ l.first; l _ l.rest ENDLOOP; name.lastComp _ l.first; }; <> IF name.lastComp.name.base=NIL THEN name.lastComp.name _ ["", 0, 0]; }; }; <> ExpandName: PUBLIC PROC [name: PATH, wDir: PATH _ NIL] RETURNS [PATH] ~ { <> name _ LocalNonNIL[name]; wDir _ LocalNonNIL[wDir]; IF name.absolute OR NOT wDir.absolute THEN RETURN[name]; { privateName: REF PrivatePathObject _ name^; answer: PATH _ NEW[PathObject _ [NEW[PrivatePathObject _ privateName^]]]; answer.absolute _ TRUE; [answer.comps, answer.lastComp] _ AdjoinComps[wDir, 0, Count[wDir], name, 0, Count[name]]; RETURN[answer]; }; }; <<>> ComponentCount: PUBLIC PROC [name: PATH] RETURNS [count: NAT] ~ { IF name=NIL THEN RETURN[0]; count _ IF name.comps # NIL THEN name.comps.count+1 ELSE IF (name.lastComp.name.base#NIL) OR (name.lastComp.version.versionKind#none) -- OR -- name.absolute OR -- name.directory THEN 1 ELSE 0; }; emptyComps: Components ~ NEW[ComponentSequence[0]]; Compare: PUBLIC PROC [n1, n2: PATH, case: BOOL _ FALSE ] RETURNS [Comparison] ~ { c: Comparison; CompareBools: PROC [b1, b2: BOOL] RETURNS [Comparison] ~ { SELECT TRUE FROM ~b1 AND b2 => RETURN[less]; b1 AND ~b2 => RETURN[greater]; ENDCASE => NULL; RETURN[equal]; }; n1 _ LocalNonNIL[n1]; n2 _ LocalNonNIL[n2]; IF (c _ CompareBools[n1.absolute, n2.absolute]) # equal THEN RETURN[c]; { comps1: Components ~ IF n1.comps # NIL THEN n1.comps ELSE emptyComps; comps2: Components ~ IF n2.comps # NIL THEN n2.comps ELSE emptyComps; c1: NAT _ comps1.count; c2: NAT _ comps2.count; FOR p: NAT IN [0..MIN[c1, c2]) DO IF (c _ CompareComponents[comps1[p], comps2[p], case]) # equal THEN RETURN[c]; ENDLOOP; SELECT TRUE FROM c1 < c2 => { IF (c _ CompareComponents[n1.lastComp, comps2[c1], case]) # equal THEN RETURN[c] ELSE RETURN[less]; }; c1 > c2 => { IF (c _ CompareComponents[comps1[c2], n2.lastComp, case]) # equal THEN RETURN[c] ELSE RETURN[greater]; }; ENDCASE => NULL; }; <> IF (c _ CompareComponents[n1.lastComp, n2.lastComp, case]) # equal THEN RETURN[c]; RETURN[CompareBools[n1.directory, n2.directory]]; }; ShortName: PUBLIC PROC [name: PATH] RETURNS [shortName: Component] ~ { RETURN[LocalNonNIL[name].lastComp]; }; Cat: PUBLIC PROC [n1, n2: PATH] RETURNS [PATH] ~ { seq: Components; lastComp: Component; absolute, directory: BOOL; n1 _ LocalNonNIL[n1]; n2 _ LocalNonNIL[n2]; [seq, lastComp] _ AdjoinComps[p1~n1, p2~n2]; absolute _ n1.absolute OR (Count[n1]=0 AND n2.absolute); directory _ n2.directory OR (Count[n2]=0 AND n1.directory); RETURN[NEW[PathObject _ [NEW[PrivatePathObject _ [absolute~absolute, directory~directory, lastComp~lastComp, comps~seq, parent~IF Count[n2]=1 THEN EnsureDirectory[n1] ELSE NIL]]]]]; }; SubName: PUBLIC PROC [name: PATH, start: NAT _ 0, count: NAT _ NAT.LAST, absolute, directory: BOOL _ FALSE] RETURNS [PATH] ~ { result: PATH _ NEW[PathObject _ [NEW[PrivatePathObject]]]; name _ LocalNonNIL[name]; count _ MIN[count, Count[name]-start]; result.absolute _ absolute; result.directory _ directory; SELECT count FROM 0 => { result.directory _ result.absolute _ absolute OR directory; RETURN[result]; }; 1 => { result.lastComp _ Fetch[name, start]; <> IF result.lastComp.name.base=NIL THEN result.lastComp.name _ ["", 0, 0]; RETURN[result]; }; ENDCASE => result.comps _ NEW[ComponentSequence[count-1]]; FOR i: NAT IN [0..count-1) DO result.comps[i] _ Fetch[name, start+i]; ENDLOOP; result.lastComp _ Fetch[name, start+count-1]; RETURN[result]; }; Fetch: PUBLIC PROC [name: PATH, index: NAT] RETURNS [Component] ~ { c: NAT; name _ LocalNonNIL[name]; [] _ PBasics.BoundsCheck[index, c_Count[name]]; IF (c-1)=index THEN RETURN[name.lastComp] ELSE RETURN[name.comps[index]]; }; IsAPrefix: PUBLIC PROC [prefix, name: PATH] RETURNS [isa: BOOL, suffix: PATH] ~ { prefix _ LocalNonNIL[prefix]; name _ LocalNonNIL[name]; { pComps: NAT _ Count[prefix]; nComps: NAT _ Count[name]; IF pComps=0 AND ~prefix.absolute THEN { <> RETURN[TRUE, name]; }; IF (pComps >= nComps) OR (prefix.absolute#name.absolute) THEN { <> <> RETURN[FALSE, NIL]; }; FOR i: NAT IN [0.. pComps-1) DO IF NOT EqualComponents[name.comps[i], prefix.comps[i]] THEN RETURN[FALSE, NIL]; ENDLOOP; IF (pComps>0) AND NOT EqualComponents[prefix.lastComp, name.comps[pComps-1]] THEN RETURN[FALSE, NIL]; <> suffix _ NEW[PathObject _ [NEW[PrivatePathObject _ []]]]; [suffix.comps, suffix.lastComp] _ AdjoinComps[name, pComps, nComps-pComps, EmptyPath, 0,0]; suffix.absolute _ NOT prefix.directory; suffix.directory _ name.directory; RETURN[TRUE, suffix]; }; }; Count: PROC [name: PATH] RETURNS [count: NAT] ~ INLINE { count _ ComponentCount[name]; }; Error: ERROR ~ CODE; IsADirectory: PUBLIC PROC [name: PATH] RETURNS [BOOL] ~ { name _ LocalNonNIL[name]; RETURN[name.directory] }; EnsureDirectory: PUBLIC PROC [name: PATH] RETURNS [PATH] ~ { name _ LocalNonNIL[name]; IF name.directory THEN RETURN[name]; { answer: PATH _ CopyPath[name]; answer.directory _ TRUE; answer.unparsed _ NIL; RETURN[answer]; }; }; IsAbsolute: PUBLIC PROC [name: PATH] RETURNS [BOOL] ~ { name _ LocalNonNIL[name]; RETURN[name.absolute] }; EnsureAbsolute: PUBLIC PROC [name: PATH] RETURNS [PATH] ~ { name _ LocalNonNIL[name]; IF name.absolute THEN RETURN[name]; { answer: PATH _ CopyPath[name]; answer.absolute _ TRUE; answer.unparsed _ NIL; RETURN[answer]; }; }; StripVersionNumber: PUBLIC PROC [name: PATH] RETURNS [PATH] ~ { <> name _ LocalNonNIL[name]; RETURN[NEW[PathObject _ [NEW[PrivatePathObject _ [name.comps, [name.lastComp.name, [none]], name.absolute, name.directory, NIL, name.parent]]]]]; }; SetVersionNumber: PUBLIC PROC [name: PATH, version: Version] RETURNS [PATH] ~ { <> name _ LocalNonNIL[name]; RETURN[NEW[PathObject _ [NEW[PrivatePathObject _ [name.comps, [name.lastComp.name, version], name.absolute, name.directory, NIL, name.parent]]]]]; }; ReplaceShortName: PUBLIC PROC [name: PATH, newShortName: Component] RETURNS [PATH] ~ { name _ LocalNonNIL[name]; RETURN[NEW[PathObject _ [NEW[PrivatePathObject _ [name.comps, newShortName, name.absolute, name.directory, NIL, name.parent]]]]]; }; ReplaceComponent: PUBLIC PROC [name: PATH, index: NAT, new: Component] RETURNS [PATH] ~ { c: NAT; name _ LocalNonNIL[name]; [] _ PBasics.BoundsCheck[index, c_Count[name]]; IF new.name.base=NIL THEN new.name.base _ ""; -- must not convert 1 component name to 0 component name; IF (c-1)=index THEN RETURN[NEW[PathObject _ [NEW[PrivatePathObject _ [name.comps, new, name.absolute, name.directory, NIL, name.parent]]]]] ELSE { <> newPath: PATH _ NEW[PathObject _ [NEW[PrivatePathObject _ [NIL, name.lastComp, name.absolute, name.directory]]]]; newPath.comps _ NEW[ComponentSequence[name.comps.count]]; FOR i: NAT IN [0..name.comps.count) DO newPath.comps[i] _ name.comps[i]; ENDLOOP; newPath.comps[index] _ new; RETURN[ newPath ]; }; }; InASubdirectory: PUBLIC PROC [parent: PATH, name: PATH] RETURNS [BOOL] ~ { <> isa: BOOL; suffix: PATH; name _ LocalNonNIL[name]; parent _ LocalNonNIL[parent]; [isa, suffix] _ IsAPrefix[parent, name]; RETURN[isa AND Count[Directory[suffix]]#0]; }; <<>> FirstSubdirectory: PUBLIC PROC [parent: PATH, name: PATH] RETURNS [PATH] ~ { <> isa: BOOL; suffix: PATH; parent _ LocalNonNIL[parent]; name _ LocalNonNIL[name]; [isa, suffix] _ IsAPrefix[parent, name]; IF ~isa OR Count[Directory[suffix]]=0 THEN RETURN[NIL]; { answer: PATH _ CopyPath[parent]; answer.unparsed _ NIL; answer.parent _ NIL; [answer.comps, answer.lastComp] _ AdjoinComps[name, 0, Count[parent]+1, EmptyPath, 0, 0]; RETURN[answer]; }; }; <<>> Directory: PUBLIC PROC [name: PATH] RETURNS [PATH] ~ { <> IF (name _ LocalNonNIL[name]).directory THEN RETURN[name]; RETURN[Parent[name]]; }; <<>> parentCalls: CARD _ 0; parentsNotCached: CARD _ 0; ParentCalls: PROC RETURNS [CARD] ~ { RETURN[parentCalls] }; ParentsNotCached: PROC RETURNS [CARD] ~ { RETURN[parentsNotCached] }; Parent: PUBLIC PROC [name: PATH] RETURNS [PATH] ~ { <> nonNil: PATH ~ LocalNonNIL[name]; parentCalls _ parentCalls+1; IF nonNil.parent#NIL THEN RETURN[nonNil.parent] ELSE { answer: PATH _ CopyPath[nonNil]; answer.unparsed _ NIL; answer.parent _ NIL; parentsNotCached _ parentsNotCached+1; IF Count[answer]=0 AND ~answer.absolute THEN Error[]; [answer.comps, answer.lastComp] _ AdjoinComps[answer, 0, MAX[INTEGER[Count[answer]-1], 0], EmptyPath, 0, 0]; answer.directory _ TRUE; IF name#NIL THEN name.parent _ answer; RETURN[answer]; }; }; Map: PUBLIC PROC [name: PATH, componentProc: ComponentProc, separatorProc: SeparatorProc, ref: REF] ~ { name _ LocalNonNIL[name]; { c: NAT _ Count[name]; separatorProc[name.absolute, ref]; IF c=0 THEN RETURN; FOR i: NAT IN [0..c-1) DO componentProc[Fetch[name, i], ref]; separatorProc[TRUE, ref]; ENDLOOP; componentProc[Fetch[name, c-1], ref]; separatorProc[name.directory, ref]; }; }; <> CompareComponents: PUBLIC PROC [c1, c2: Component, case: BOOL] RETURNS [Comparison] ~ { SELECT Rope.CompareSubstrs[ c1.name.base, c1.name.start, c1.name.len, c2.name.base, c2.name.start, c2.name.len, case] FROM less => RETURN[less]; greater => RETURN[greater]; ENDCASE => NULL; RETURN[CompareVersions[c1.version, c2.version]]; }; <> CompareVersions: PUBLIC PROC [v1, v2: Version] RETURNS [Comparison] ~ { IF v1.versionKind#v2.versionKind THEN RETURN[incomparable]; IF v1.versionKind#numeric THEN RETURN[equal]; SELECT TRUE FROM v1.version RETURN[less]; v1.version>v2.version => RETURN[greater]; ENDCASE => RETURN[equal]; }; <> GetUnparsingHint: PUBLIC PROC [p: PATH] RETURNS [REF] ~ { RETURN [p.unparsed]; }; SetUnparsingHint: PUBLIC PROC [p: PATH, r: REF] RETURNS [prev: REF] ~ { prev _ p.unparsed; p.unparsed _ r; }; <> EmptyPath: PUBLIC PATH ~ NEW[PathObject _ [NEW[PrivatePathObject]]]; LocalNonNIL: PROC [p: PATH] RETURNS [PATH] ~ INLINE { RETURN[IF p=NIL THEN EmptyPath ELSE p]; }; <> CopyPath: PROC [path: PATH] RETURNS [PATH] ~ { <> privatePath: REF PrivatePathObject ~ path^; RETURN[ NEW[PathObject _ [NEW[PrivatePathObject _ privatePath^]]] ]; }; AdjoinComps: PROC [ p1: PATH, s1: NAT _ 0, c1: NAT _ LAST[NAT], p2: PATH _ NIL, s2: NAT _ 0, c2: NAT _ LAST[NAT] ] RETURNS [seq: Components _ NIL, lastComp: Component _ [] ] ~ { <> <> <<>> count1: NAT _ Count[p1]; count2: NAT _ Count[p2]; c1 _ MIN[c1, count1-s1]; c2 _ MIN[c2, count2-s2]; { CopyComps: PROC [from: PATH, fromStart, count: NAT, to: Components, toStart: NAT] ~ { j: NAT _ fromStart; FOR i: NAT IN [toStart..MIN[toStart+count, to.count]) DO to[i] _ Fetch[from, j]; j _ j+1; ENDLOOP; }; newCount: NAT _ c1+c2; SELECT newCount FROM 0 => { RETURN }; 1 => { NULL }; < { ERROR }; -- maybe?>> ENDCASE => seq _ NEW[ComponentSequence[newCount-1]]; < 0 OR c1 > 0>> lastComp _ IF c2 > 0 THEN Fetch[p2, s2+c2-1] ELSE Fetch[p1, s1+c1-1]; IF seq = NIL THEN RETURN; CopyComps[from: p1, fromStart: s1, count: c1, to: seq, toStart: 0]; CopyComps[from: p2, fromStart: s2, count: c2, to: seq, toStart: c1]; }; }; END.