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, directory: BOOL _ FALSE, 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 }; ENDCASE => seq _ NEW[ComponentSequence[newCount-1]]; 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. ¬PFSNamesImpl.mesa Copyright Σ 1988, 1989 by Xerox Corporation. All rights reserved. Carl Hauser, August 28, 1989 5:29:31 pm PDT Bill Jackson (bj) May 31, 1989 11:21:34 pm PDT Chauser, October 22, 1990 5:26 pm PDT Representation comps[0] is most significant IF pathKind = absolute, the first two components provide the protocol and server name. Clients (and the implementation) must treat the comps^ as immutable, since it may be shared. maybe we should provide extension parsing of this as well? lastComp is not included in comps: the idea is to let the implementation share comps structures between Paths differing only in the last component. absolute => path is absolute, i.e. begins with protocol, server, etc. ~absolute=> path is relative to some, as yet unspecified, working directory As a wDir argument, ~absolute implies no working directory, since working directories must be absolute. directory=> name is syntactically that of a directory ~directory=> name is syntactically that of a file an unparsing hint; the various parsers may store something here to help them unparse fast. another hint. If non-NIL, refers to a path object representing the parent directory for this object. Type Manipulation Parsing and Constructing Names A convenient way to construct names without parsing. Count the components Allocate room in the sequence for all but the last Copy the components to the PATH We don't want a 1-component name to inadvertantly look like a 0-component name. Operations Within the Algebra of Names If name is not absolute then prepends wDir. Assert (comps1.count=comps2.count) We don't want a 1-component name to inadvertantly look like a 0-component name. a relative, empty name is a prefix of anything. prefix too long, or unmatched absoluteness. some doubt about whether excluding equal lengths is useful. Now compute the suffix returns name with [none[]] substituted for name.version. returns name with version substituted for name.version. assert: name.comps#NIL This implementation could be more efficient; it's not necessary to construct the suffix and Directory[suffix] If NOT InASubdirectory[parent, name] then returns NIL. Otherwise, returns the part of name up to and including the first subdirectory after the end of parent. Return the directory part of the name. If name is a directory, then returns the parent directory. If name is a file, then returns the containing directory. In either case, this means that Parent returns the directory which contains the object denoted by name. Operations on Components Operations on Versions Unparsing Hint Helpers This is also in the interface, but this is better for use in the Impl because it doesn't indirect through the interface record to get to the EmptyPath. caller must ensure path is non-NIL; Combined Substring and Cat; avoids the allocations one would do to do them separately. caller must assure that p1 and p2 are non-NIL NAT.LAST => { ERROR }; -- maybe? assert: c2 > 0 OR c1 > 0 Κ­˜šœ™Icode™BKšœ+™+Kšœ.™.K™%K™—šΟk ˜ Kšœœ˜J˜ Jšœœœ˜"K˜—KšΠln œœ˜Kšœ˜Kšœ ˜Kšœœœ ˜head™Kšœœœ ˜Kšœ œ˜'š œœœœœ˜0šœœœ˜ Kšœ™KšœV™VK™^—šœ˜K™:KšΟeœŸœr™“—šœ œ˜KšœE™EKšœK™KKšœg™g—šœ œœ˜Kšœ5™5Kšœ1™1—šœ ˜K™Z—šœœœ˜K™e—Kšœ˜K˜—Kš œœœœ œœœ ˜NKšœ œœ˜)K˜—™š Οn œ œœœœœΟc˜7Kšœœœ˜K˜K˜—š  œ œœœœœ‘˜3Kšœœœ˜K˜——™K™š  œœœœœ*œœ œœœœœ˜šK™4Kšœœ˜Kšœœœ˜8Kšœ˜Kšœ˜K˜K™š œœœ œœ˜