MorePfsEnumerationImpl.mesa
Copyright Ó 1990 by Xerox Corporation.  All rights reserved.
Last tweaked by Mike Spreitzer on April 10, 1992 9:52 am PDT
 
DIRECTORY IO, MorePfsEnumeration, MorePfsNames, PFS, PFSNames, Process, Rope, RopeParts, SimpleFeedback;
MorePfsEnumerationImpl: 
CEDAR 
PROGRAM
IMPORTS IO, MorePfsNames, PFS, PFSNames, Process, RopeParts, SimpleFeedback
EXPORTS MorePfsEnumeration
=
 
BEGIN OPEN PFS, MorePfsEnumeration, RP:RopeParts, MPfsN:MorePfsNames, SF:SimpleFeedback;
RopePart: TYPE ~ RP.RopePart;
Component: TYPE ~ PFSNames.Component;
Version: TYPE ~ PFSNames.Version;
PatternSet: TYPE ~ LIST OF PATH;
debug: BOOL ← TRUE;
EnumerateForNames: 
PUBLIC Enumerator ~ {
TransformEnumerator[PFS.EnumerateForNames, PfsCaser, PfsETester, pattern, proc, lbound, hbound];
RETURN};
 
EnumerateForInfo: 
PUBLIC 
PROC [pattern: 
PATH, proc: InfoProc, lbound: 
PATH ← 
NIL, hbound: 
PATH ← 
NIL ] ~ {
PerName: 
PROC [name: 
PATH] 
RETURNS [continue: 
BOOL ← 
TRUE] ~ {
RETURN APPLY[proc, PFS.FileInfo[name]]};
 
EnumerateForNames[pattern, PerName, lbound, hbound];
RETURN};
 
PfsCaser: 
PUBLIC 
PROC [name: 
PATH] 
RETURNS [sensitive: 
BOOL] ~ {
sensitive ← FALSE;
sensitive ← PFS.CaseSensitive[name !PFS.Error => CONTINUE];
RETURN};
 
PfsETester: 
PUBLIC 
PROC [name: 
PATH] 
RETURNS [exists: 
BOOL ← 
FALSE] ~ {
ENABLE PFS.Error => CONTINUE;
exists ← PFS.FileInfo[name].bytes >= 0;
RETURN};
 
TransformEnumerator: 
PUBLIC 
PROC [base: Enumerator, caser: Caser, exists: ETester, pattern: 
PATH, proc: NameProc, lbound: 
PATH ← 
NIL, hbound: 
PATH ← 
NIL ] ~ {
stopped: BOOL ← FALSE;
WorkFrom: 
PROC [i: 
INT, ps: PatternSet] ~ {
Enumerate each name that matches at least one pattern in ps.
The patterns each have at least i+1 components, and are identical up to (but not including) component i.
r: RopePart;
v: Version;
dvg, hastar: BOOL;
stem, lag: PATH ← NIL;
laghps, lagnhps: PatternSet ← NIL;
dhlag, dnhlag: BOOL ← FALSE;
Continue: NameProc ~ {
case, deliver, dlow, dhigh, newSeries: BOOL ← FALSE;
nps, elowps, ehighps: PatternSet ← NIL;
IF stopped THEN RETURN [FALSE];
Process.CheckForAbort[];
[case, deliver, nps, dlow, dhigh, elowps, ehighps] ← RefinePatterns[i, name, ps, caser];
IF dlow 
OR dhlag 
OR elowps#
NIL 
OR laghps#
NIL 
THEN {
newSeries ← lag=NIL OR NOT EqualModVersion[lag, name, case];
};
 
IF dnhlag 
OR (newSeries 
AND dhlag) 
THEN {
IF (stopped ← NOT proc[lag]) THEN RETURN [FALSE];
};
 
IF lagnhps#
NIL 
OR (newSeries 
AND laghps#
NIL) 
THEN {
WorkFrom[i+1, PSUnion[lagnhps, IF newSeries THEN laghps ELSE NIL]];
};
 
IF stopped THEN RETURN [FALSE];
IF dhigh 
THEN {
dnhlag ← deliver OR (dlow AND newSeries);
deliver ← dlow ← FALSE}
 
ELSE dnhlag ← FALSE;
dhlag ← dhigh;
IF ehighps#
NIL 
THEN {
lagnhps ← IF newSeries THEN PSUnion[nps, elowps] ELSE nps;
nps ← elowps ← NIL}
 
ELSE lagnhps ← NIL;
laghps ← ehighps;
IF deliver 
OR (dlow 
AND newSeries) 
THEN {
IF (stopped ← NOT proc[name]) THEN RETURN [FALSE];
};
 
IF nps#
NIL 
OR (newSeries 
AND elowps#
NIL)
THEN WorkFrom[i+1, PSUnion[nps, IF newSeries THEN elowps ELSE NIL]];
 
lag ← name;
RETURN [NOT stopped]};
 
IF stopped THEN RETURN;
Process.CheckForAbort[];
[r, v, dvg, hastar] ← FindDivergence[caser, ps, i];
IF dvg 
OR hastar 
THEN {
stem ← ps.first.SubName[count: i+1, absolute: TRUE];
stem ← stem.ReplaceShortName[MPfsN.ConsComponent[r, v]];
IF debug THEN SF.PutFL[$MorePfs, oneLiner, $Debug, "Base enumerating %g at level %g", LIST[ [rope[PFS.RopeFromPath[stem]]], [integer[i]] ]];
base[stem, Continue, lbound, hbound !PFS.Error => CONTINUE];
IF (NOT stopped) AND (dnhlag OR dhlag) THEN stopped ← NOT proc[lag];
}
 
ELSE {
here, dlow, dhigh: BOOL;
nps, elowps, ehighps: PatternSet;
[,here, nps, dlow, dhigh, elowps, ehighps] ← RefinePatterns[i, ps.first.SubName[count: i+1, directory: FALSE], ps, caser];
IF here 
OR dlow 
OR dhigh 
THEN {
ex: BOOL ← FALSE;
ex ← exists[ps.first !PFS.Error => CONTINUE];
IF ex THEN {IF NOT proc[ps.first] THEN RETURN};
};
 
IF nps#NIL OR elowps#NIL OR ehighps#NIL THEN WorkFrom[i+1, PSUnion[elowps, PSUnion[ehighps, nps]]];
};
 
RETURN};
 
IF debug THEN SF.PutF[$MorePfs, oneLiner, $Debug, "Transforming enumeration of %g", [rope[PFS.RopeFromPath[pattern]]] ];
IF NOT pattern.IsAbsolute[] THEN PFS.Error[[user, $PatternNotAbsolute, IO.PutFR1["pattern %g not absolute", [rope[IF pattern=NIL THEN "<NIL>" ELSE PFS.RopeFromPath[pattern] ]] ]]];
WorkFrom[0, LIST[pattern]];
RETURN};
 
star: RopePart ← RP.FromChar['*];
dstar: RopePart ← RP.Make["**"];
FindDivergence: 
PROC [caser: Caser, ps: PatternSet, i: 
INT] 
RETURNS [r: RopePart, v: Version, dvg, hastar: 
BOOL ← 
FALSE] ~ {
Return the most specific name and version patterns that cover the i'th component of every member of ps.
Doit: 
PROC [case: 
BOOL] 
RETURNS [r: RopePart, v: Version, dvg, hastar: 
BOOL ← 
FALSE] ~ {
c: Component ← ps.first.Fetch[i];
l: INT;
clip: BOOL ← FALSE;
r ← MPfsN.ComponentName[c];
v ← c.version;
hastar ← r.Find[star]>=0;
l ← r.Length[];
FOR rps: PatternSet ← ps.rest, rps.rest 
WHILE rps#
NIL 
DO
thisCase: BOOL ← caser[rps.first];
thisL: INT;
thisR: RopePart;
IF thisCase AND NOT case THEN RETURN Doit[TRUE];
Process.CheckForAbort[];
c ← rps.first.Fetch[i];
IF v#c.version THEN v ← [all];
thisR ← MPfsN.ComponentName[c];
hastar ← hastar OR thisR.Find[star]>=0;
thisL ← r.Run[s2: thisR, case: case];
IF thisL<l THEN {r ← r.Substr[len: l ← thisL]; dvg ← TRUE};
IF thisL=0 AND v=[all] THEN EXIT;
ENDLOOP;
 
IF dvg THEN r ← r.Concat[star];
RETURN};
 
RETURN Doit[caser[ps.first]]};
 
RefinePatterns: 
PROC [i: 
INT, prefix: 
PATH, ps: PatternSet, caser: Caser] 
RETURNS [case, here: 
BOOL ← 
FALSE, nps: PatternSet ← 
NIL, dlow, dhigh: 
BOOL ← 
FALSE, elowps, ehighps: PatternSet ← 
NIL] ~ {
prefix is of length i+1.  Return a pattern set that will match the same names that start with prefix as ps, and that includes only patterns that start with prefix.  nps is the patterns that certainly match the prefix; elowps is the patterns that can only be enumerated if prefix is the lowest version; ehighps is the patterns that can only be enumerated if prefix is the highest version.
nc: Component ~ prefix.Fetch[i];
nr: RopePart ← MPfsN.ComponentName[nc];
rps: PatternSet ← ps;
dall, eall: BOOL ← FALSE;
case ← caser[prefix];
IF debug THEN SF.PutF[$MorePfs, oneLiner, $Debug, "Refining patterns for prefix %g", [rope[PFS.RopeFromPath[prefix]]] ];
WHILE rps#
NIL 
DO
pat: PATH ~ rps.first;
n: INT ~ pat.ComponentCount[];
c: Component ~ pat.Fetch[i];
r: RopePart ← MPfsN.ComponentName[c];
d: INT ← r.Find[dstar];
asRope: ROPE ← PFS.RopeFromPath[pat];
Process.CheckForAbort[];
rps ← rps.rest;
IF debug THEN SF.PutF[$MorePfs, oneLiner, $Debug, "consuming %g", [rope[PFS.RopeFromPath[pat]]] ];
IF d>=0 
THEN {
r1: RopePart ← r.Replace[d, 1];
p1: PATH ← pat.ReplaceComponent[i, MPfsN.ConsComponent[r1, c.version]];
c2a: Component ← MPfsN.ConsComponent[r.Substr[len: d+1], [all] ];
c2b: Component ← MPfsN.ConsComponent[r.Substr[start: d], c.version ];
p2a: PATH ← pat.SubName[count: i+1].ReplaceShortName[c2a];
p2b: PATH ← pat.SubName[start: i, directory: pat.IsADirectory] .ReplaceComponent[0, c2b];
p2: PATH ← p2a.Cat[p2b];
rps ← CONS[p1, CONS[p2, rps]];
IF debug THEN SF.PutFL[$MorePfs, oneLiner, $Debug, "split into %g and %g", LIST[ [rope[PFS.RopeFromPath[p1]]], [rope[PFS.RopeFromPath[p2]]] ]];
}
 
ELSE 
IF i+1=n 
THEN {
IF r.Match[nr, case] 
THEN 
SELECT c.version.versionKind 
FROM
numeric, none => here ← nc.version = c.version;
lowest => dlow ← TRUE;
highest => dhigh ← TRUE;
all => here ← dall ← TRUE;
ENDCASE => ERROR;
 
}
 
ELSE 
IF r.Match[nr, case] 
THEN {
newp: PATH ~ pat.ReplaceComponent[i, nc];
SELECT c.version.versionKind 
FROM
numeric, none => 
IF nc.version = c.version 
THEN {
IF debug THEN SF.PutF[$MorePfs, oneLiner, $Debug, "continuing %g", [rope[PFS.RopeFromPath[newp]]] ];
nps ← CONS[newp, nps]};
all => {
IF debug THEN SF.PutF[$MorePfs, oneLiner, $Debug, "continuing %g", [rope[PFS.RopeFromPath[newp]]] ];
eall ← TRUE;
nps ← CONS[newp, nps]};
lowest => {
IF debug THEN SF.PutF[$MorePfs, oneLiner, $Debug, "elow %g", [rope[PFS.RopeFromPath[newp]]] ];
elowps ← CONS[newp, elowps]};
highest => {
IF debug THEN SF.PutF[$MorePfs, oneLiner, $Debug, "ehigh %g", [rope[PFS.RopeFromPath[newp]]] ];
ehighps ← CONS[newp, ehighps]};
ENDCASE => ERROR};
 
 
ENDLOOP;
 
IF dall THEN dlow ← dhigh ← FALSE;
IF eall THEN elowps ← ehighps ← NIL;
RETURN};
 
VersionMatch: 
PROC [p, o: Version, pat: 
PATH] 
RETURNS [
BOOL] ~ {
IF p=o THEN RETURN [TRUE];
SELECT p.versionKind 
FROM
numeric, none => RETURN [FALSE];
lowest, highest, all => RETURN [TRUE];
next => ERROR PFS.Error[[user, $VersionNextInPattern, IO.PutFR1["the pattern %g has a `next' version in it", [rope[PFS.RopeFromPath[pat]]] ], pat]];
ENDCASE => ERROR};
 
 
EqualModVersion: 
PROC [p1, p2: 
PATH, case: 
BOOL] 
RETURNS [
BOOL] ~ {
n: INT ~ p1.ComponentCount[];
n2: INT ~ p2.ComponentCount[];
c1, c2: Component;
IF n#n2 THEN RETURN [FALSE];
FOR i: 
INT 
IN [0..n-1) 
DO
c1 ← p1.Fetch[i];
c2 ← p2.Fetch[i];
IF NOT c1.EqualComponents[c2, case] THEN RETURN [FALSE];
ENDLOOP;
 
c1 ← p1.ShortName[];
c2 ← p2.ShortName[];
RETURN MPfsN.ComponentName[c1].Equal[MPfsN.ComponentName[c2], case]};
 
PSUnion: 
PROC [a, b: PatternSet] 
RETURNS [PatternSet] ~ {
IF b=NIL THEN RETURN [a];
FOR a ← a, a.rest WHILE a#NIL DO b ← CONS[a.first, b] ENDLOOP;
RETURN [b]};
 
END.