VersionMapFromDFImpl.mesa
Copyright © 1984 by Xerox Corporation. All rights reserved.
Russ Atkinson, October 23, 1984 10:05:11 am PDT
DIRECTORY
Basics USING [bytesPerWord],
BasicTime USING [GMT, Now, nullGMT, ToPupTime],
BcdDefs USING [BCD, NullVersion, VersionStamp],
Commander USING [CommandProc, Register],
DefaultRemoteNames USING [Get],
DFUtilities USING [Date, RemoveVersionNumber],
FS USING
[Error, FileInfo, minimumStreamBufferParms, Open, OpenFile, StreamFromOpenFile, StreamOpen],
FSBackdoor USING [EnumerateCacheForInfo, InfoProc],
GenerateDFClosure USING [ActionProc, ClosureInfo, GenerateClosureToProc],
IFSFile USING
[CantOpen, Close, Completer, Error, FileHandle, Finalize, FSInstance, Initialize, Login, Logout, Open, Problem, StartRead, UnableToLogin],
IO USING
[Close, EndOfStream, GetIndex, GetTokenRope, IDProc, PutChar, PutF, PutFR, PutRope, RIS, SetIndex, SetLength, STREAM, UnsafeGetBlock],
MessagesOut USING [PutRopes],
PriorityQueue USING [Insert, Predict, Ref, Remove, Size, SortPred],
Process USING [Pause, SecondsToTicks],
ProcessExtras USING [CheckForAbort],
Rope USING
[Concat, Equal, Fetch, Find, Flatten, FromProc, Index, Length, Match, ROPE, Run, Size, SkipTo, Substr],
UserCredentials USING [Get],
VersionMap USING
[FillInShortNames, Map, MapEntry, MapList, MapRep, MyStamp, Range, RangeList, RangeToEntry, RestoreMapFromFile, SaveMapToFile, ShortNameToRanges, VersionToName],
ViewerIO USING [CreateViewerStreams];
VersionMapFromDFImpl: CEDAR MONITOR
LOCKS myData USING myData: MyData
IMPORTS
BasicTime, Commander, DefaultRemoteNames, DFUtilities, FS, FSBackdoor, GenerateDFClosure, IFSFile, IO, MessagesOut, PriorityQueue, Process, ProcessExtras, Rope, UserCredentials, VersionMap, ViewerIO
SHARES IO, VersionMap
= BEGIN
T Y P E S
BCD: TYPE = BcdDefs.BCD;
bytesPerWord: NAT = Basics.bytesPerWord;
Date: TYPE = DFUtilities.Date;
LORA: TYPE = LIST OF REF ANY;
PQ: TYPE = PriorityQueue.Ref;
GMT: TYPE = BasicTime.GMT;
Map: TYPE = VersionMap.Map;
MapList: TYPE = VersionMap.MapList;
MapEntry: TYPE = VersionMap.MapEntry;
MapRep: TYPE = VersionMap.MapRep;
MyStamp: TYPE = VersionMap.MyStamp;
NullStamp: MyStamp = LOOPHOLE[NullVersion];
MyStampAsHex: TYPE = PACKED ARRAY [0..12) OF [0..16);
ROPE: TYPE = Rope.ROPE;
RopeList: TYPE = LIST OF ROPE;
STREAM: TYPE = IO.STREAM;
VersionStamp: TYPE = BcdDefs.VersionStamp;
NullVersion: VersionStamp = BcdDefs.NullVersion;
FileEntry: TYPE = REF FileEntryRep;
FileEntryRep: TYPE = RECORD [
name: ROPE, date: Date, version: VersionStamp, from: ROPE];
NameSeq: TYPE = REF NameSeqRep;
NameSeqRep: TYPE = RECORD [
SEQUENCE len: NAT OF NameEntry];
NameEntry: TYPE = RECORD [hasPrefix: BOOL, name: ROPE];
MyData: TYPE = REF MyDataRep;
MyDataRep: TYPE = MONITORED RECORD [
sourceQueue: PQNIL,
objectQueue: PQNIL,
objectFiles,sourceFiles: INT ← 0,
oldObjectMap: MapList ← NIL,
oldSourceMap: MapList ← NIL,
prefixList: RopeList ← NIL,
warningMatch: ROPENIL,
errs: STREAMNIL,
remoteHandle: IFSFile.FSInstance ← NIL,
remoteHandleServer: ROPENIL,
remoteHandleReleased: CONDITION,
which: {source, object, both} ← both,
checkRemote: BOOLTRUE,
remoteHandleOwned: NAT ← 0,
abortRequested: BOOLFALSE
];
Global variables
pauseForServerGlitch: [0..3600] ← 20; -- seconds to pause if server busy
triesForServerGlitch: NAT ← 20; -- times to retry if server busy
Single file processing
EachFile: GenerateDFClosure.ActionProc = {
[data: REF, kind: {file, notFound, syntaxError}, name: ROPE, date: Date, from: ROPE]
myData: MyData ← NARROW[data];
CheckAbort[myData];
SELECT kind FROM
file => {
needsVersion: BOOLFALSE;
isBcd: BOOLFALSE;
entry: FileEntry ← NEW[FileEntryRep ← [
name: name, date: date, version: NullVersion, from: from]];
triesLeft: NAT ← triesForServerGlitch;
IF Rope.Match[myData.warningMatch, name, FALSE] THEN
MessagesOut.PutRopes[myData.errs,
"Warning match\n for ", name, "\n from ", from];
IF Rope.Match["*!*", name]
THEN
isBcd ← Rope.Match["*.symbols!*", name, FALSE]
OR Rope.Match["*.bcd!*", name, FALSE]
ELSE
isBcd ← Rope.Match["*.symbols", name, FALSE]
OR Rope.Match["*.bcd", name, FALSE];
IF NOT Rope.Match["[]*", name] THEN {
We need to merge this prefix in with the rest
pos: INT ← Rope.Find[name, ">"];
prefix: ROPE ← Rope.Flatten[name, 0, pos+1];
lag: RopeList ← NIL;
FOR each: RopeList ← myData.prefixList, each.rest WHILE each # NIL DO
IF Rope.Equal[prefix, each.first, FALSE] THEN GO TO found;
lag ← each;
ENDLOOP;
IF lag = NIL THEN myData.prefixList ← LIST[prefix] ELSE lag.rest ← LIST[prefix];
EXITS found => {};
};
DO
ENABLE
FS.Error => {
SELECT error.code FROM
$serverInaccessible => {
MessagesOut.PutRopes[myData.errs, "Server glitch: ", name];
Process.Pause[Process.SecondsToTicks[pauseForServerGlitch]];
IF (triesLeft ← triesLeft - 1) # 0 THEN LOOP;
};
ENDCASE;
MessagesOut.PutRopes[
myData.errs, "File not found: ", name,
IO.PutFR["\n from: %g\n reason: %g\n",
[rope[from]], [rope[error.explanation]]]];
IF error.group # bug THEN EXIT ELSE REJECT;
};
IF isBcd
THEN
SELECT myData.which FROM
object,both => FillInObjectVersion[myData, entry, myData.checkRemote];
ENDCASE
ELSE
SELECT myData.which FROM
source,both => FillInSourceVersion[myData, entry, myData.checkRemote];
ENDCASE;
EXIT;
ENDLOOP;
};
ENDCASE;
};
FillInSourceVersion: PROC
[myData: MyData, entry: FileEntry, checkRemote: BOOLTRUE] = {
created: BasicTime.GMT ← entry.date.gmt;
initName: ROPE ← entry.name;
full: ROPE ← initName;
attached: ROPENIL;
version: VersionStamp;
useFileSystem: BOOLFALSE;
IF entry.date.format = explicit AND created # BasicTime.nullGMT
THEN {
There is an explicit date in the DF file
version ← [0, 0, BasicTime.ToPupTime[created]];
IF checkRemote THEN {
We have been instructed to verify the DF file date, either through the remote server OR through the old source version map.
IF myData.oldSourceMap = NIL
THEN useFileSystem ← TRUE
ELSE {
We have been instructed to TRUST the old map (if the file is present). However, to guard against bogus matches for names that have the same versions, we also check the name for agreement up to the version.
fromMap: ROPE
VersionMap.VersionToName[myData.oldSourceMap, version].name;
len: INT ← fromMap.Length[];
bang: INT ← ScanName[fromMap].bang;
SELECT Rope.Run[fromMap, 0, full, 0, FALSE] FROM
0, < bang => useFileSystem ← TRUE;
ENDCASE => full ← fromMap;
};
};
}
ELSE {
For a non-explicit date, we can't trust anything.
useFileSystem ← TRUE;
created ← BasicTime.nullGMT;
checkRemote ← TRUE;
};
At this point, created = BasicTime.nullGMT if we are supposed to check the remote file server. If this check fails, it is the caller's responsibility to handle the error.
IF useFileSystem THEN {
IF created = BasicTime.nullGMT THEN full ← DFUtilities.RemoveVersionNumber[full];
[created: created, fullFName: full, attachedTo: attached] ← FS.FileInfo[
name: full, wantedCreatedTime: created, remoteCheck: checkRemote];
version ← [0, 0, BasicTime.ToPupTime[created]];
};
At this point, created # BasicTime.nullGMT, and the full name is presumed correct (although attached may take precedence as the 'real' name). So it is time to insert this file entry into the priority queue.
entry.date ← [format: explicit, gmt: created];
entry.version ← version;
IF attached # NIL THEN entry.name ← attached ELSE entry.name ← full;
IF Rope.Length[entry.name] = 0
THEN {
I wonder how this happened?
MessagesOut.PutRopes[
myData.errs, "Warning, failed to get source version for ", initName];
}
ELSE {
Put the entry into the priority queue for later handling
PriorityQueue.Insert[myData.sourceQueue, entry];
myData.sourceFiles ← myData.sourceFiles + 1;
};
};
FillInObjectVersion: PROC
[myData: MyData, entry: FileEntry, checkRemote: BOOLTRUE] = {
Read the version number from the BCD. Too bad we have to get the whole file!
outerCreated: BasicTime.GMT ← entry.date.gmt;
full: ROPE ← entry.name;
attached: ROPENIL;
valid: BOOLFALSE; -- indicates validity of version stamp
IF entry.date.format # explicit THEN {
outerCreated ← BasicTime.nullGMT;
full ← DFUtilities.RemoveVersionNumber[full];
};
For object files we are not sure whether we can trust the version number, so we MUST ask the file system. We use the resulting full file name as the truth when searching the old object map. As a side effect, the date in the entry is now explicit.
[created: outerCreated, fullFName: full, attachedTo: attached] ← FS.FileInfo[
name: full, wantedCreatedTime: outerCreated, remoteCheck: checkRemote];
entry.date ← [format: explicit, gmt: outerCreated];
IF attached # NIL THEN full ← attached;
entry.name ← full;
[valid, entry.version] ← FindShortObjectName[myData, full];
IF NOT valid THEN {
We have to check the file itself, rather than trust the old symbols map
cacheChecker: FSBackdoor.InfoProc = {
[fullGName: ROPE, created: BasicTime.GMT, bytes: INT, keep: CARDINAL]
RETURNS [continue: BOOLEAN]
IF outerCreated = created AND bytes > 0 THEN {
full ← fullGName;
inCache ← TRUE;
RETURN [FALSE];
};
RETURN [TRUE];
};
inCache: BOOLFALSE;
FSBackdoor.EnumerateCacheForInfo[cacheChecker, NIL, full];
IF inCache
THEN {
We can afford to open the file on the local disk, since this is better than trying to open the remote file.
file: FS.OpenFile ← FS.Open[
name: full, lock: read, wantedCreatedTime: outerCreated, remoteCheck: FALSE];
stream: STREAMFS.StreamFromOpenFile[
openFile: file, streamBufferParms: FS.minimumStreamBufferParms];
TRUSTED {
ENABLE UNWIND => IO.Close[stream];
versionOffset: NAT = bytesPerWord*SIZE[CARDINAL];
versionBytes: NAT = bytesPerWord*SIZE[VersionStamp];
IO.SetIndex[stream, versionOffset];
[] ← IO.UnsafeGetBlock[
stream, [LOOPHOLE[@entry.version], 0, versionBytes]];
};
IO.Close[stream];
}
ELSE {
We should try to use the remote server for this file.
serverPos: INT = Rope.SkipTo[full, 1, "]"];
server: ROPE = Rope.Flatten[full, 1, serverPos-1];
rest: ROPE = Rope.Flatten[full, serverPos+1];
IF AcquireRemoteHandle[myData, server]
THEN {
{ENABLE UNWIND => ReleaseRemoteHandle[myData];
[valid, entry.version] ← VersionStampFromRemoteHandle[
myData, server, rest, outerCreated];
};
ReleaseRemoteHandle[myData];
IF NOT valid THEN {
We could not find the file, so give a message and return
MessagesOut.PutRopes[myData.errs, "File not found: ", full];
RETURN;
};
}
ELSE {
We could NOT get to the server, a message has been produced by AcquireRemoteHandle, so we can just return?
RETURN;
};
};
};
PriorityQueue.Insert[myData.objectQueue, entry];
myData.objectFiles ← myData.objectFiles + 1;
};
EntrySortPred: PriorityQueue.SortPred = TRUSTED {
[x: Item, y: Item, data: REFNIL] RETURNS [BOOL]
xx: MyStamp = LOOPHOLE[LOOPHOLE[x, FileEntry].version];
yy: MyStamp = LOOPHOLE[LOOPHOLE[y, FileEntry].version];
IF xx.num # yy.num THEN RETURN [xx.num < yy.num];
IF xx.hi # yy.hi THEN RETURN [xx.hi < yy.hi];
RETURN [xx.lo < yy.lo];
};
VersionSortPred: PROC [xx,yy: MyStamp] RETURNS [BOOL] = TRUSTED {
IF xx.num # yy.num THEN RETURN [xx.num < yy.num];
IF xx.hi # yy.hi THEN RETURN [xx.hi < yy.hi];
RETURN [xx.lo < yy.lo];
};
VersionMapFromPQ: PROC
[pq: PQ, prefix: ROPE, myData: MyData] RETURNS [map: Map] = {
currentName: ROPE ← prefix ← Rope.Flatten[prefix];
prefixLen: NAT = Rope.Size[prefix];
pos: INT ← prefixLen+1;
entries: NAT = PriorityQueue.Size[pq];
seq: NameSeq ← NEW[NameSeqRep[entries]];
entryIndex: NAT ← 0;
subPos: NAT ← 0;
subLim: NAT ← prefixLen;
lastPos: INT ← 0;
lag: FileEntry ← NIL;
getChar: PROC [] RETURNS [c: CHAR] = {
IF subPos < subLim THEN {
c ← currentName.Fetch[subPos];
subPos ← subPos + 1;
RETURN;
};
c ← '\n;
IF entryIndex < entries THEN {
nameEntry: NameEntry ← seq[entryIndex];
currentName ← nameEntry.name;
subLim ← Rope.Length[currentName];
IF nameEntry.hasPrefix THEN subPos ← prefixLen ELSE subPos ← 0;
entryIndex ← entryIndex + 1;
};
};
ProcessExtras.CheckForAbort[];
map ← NEW[MapRep[entries]];
FOR i: NAT IN [0..entries) DO
entry: FileEntry = NARROW[PriorityQueue.Remove[pq]];
name: ROPE = entry.name;
hasPrefix: BOOL = Rope.Run[name, 0, prefix, 0, FALSE] = prefixLen;
seq[i] ← [hasPrefix, name];
map[i] ← [stamp: LOOPHOLE[entry.version], index: pos];
pos ← pos + Rope.Length[name] + 1;
IF hasPrefix THEN pos ← pos - prefixLen;
IF lag # NIL AND lag.version = entry.version THEN {
Warn the user that there are duplicate versions
MessagesOut.PutRopes[myData.errs,
"Warning, duplicate versions for",
IO.PutFR["\n %g\n from: %g", [rope[lag.name]], [rope[lag.from]]],
IO.PutFR["\n %g\n from: %g", [rope[entry.name]], [rope[entry.from]]]
];
};
lag ← entry;
ENDLOOP;
map.names ← Rope.FromProc[pos, getChar, 4040];
VersionMap.FillInShortNames[map];
};
WriteMapToFile: PROC [map: Map, name: ROPE] = {
prefix: ROPENIL;
st: IO.STREAMFS.StreamOpen[name, create];
count: NAT ← map.len;
names: ROPE ← map.names;
firstCR: INT ← names.Index[0, "\n"];
ProcessExtras.CheckForAbort[];
prefix ← names.Flatten[0, firstCR];
IO.PutRope[st, prefix];
IO.PutRope[st, "\n"];
FOR i: NAT IN [0..map.len) DO
Display the entry.
entry: MapEntry ← map[i];
index: INT ← entry.index;
stamp: MyStamp ← entry.stamp;
PutStampAsHex[st, stamp];
IO.PutRope[st, " "];
DO
c: CHAR ← names.Fetch[index];
index ← index + 1;
IO.PutChar[st, c];
IF c = '\n THEN EXIT;
ENDLOOP;
ENDLOOP;
IO.PutRope[st, "\n"];
IO.SetLength[st, IO.GetIndex[st]]; -- force goddamm truncation already!!!
IO.Close[st];
};
IndexToShortName: PROC [map: Map, index: INT] RETURNS [ROPE] = {
Return the name without the prefix or version
names: ROPE ← map.names;
end: INT ← names.SkipTo[index, "!\n"];
pos: INT ← end;
WHILE pos > index DO
SELECT names.Fetch[pos] FROM
'/, '], '> => EXIT;
ENDCASE;
pos ← pos - 1;
ENDLOOP;
RETURN [names.Substr[pos, end-pos]];
};
PutStampAsHex: PROC [st: IO.STREAM, stamp: MyStamp] = {
hex: MyStampAsHex ← LOOPHOLE[stamp];
FOR index: CARDINAL IN [0..12) DO
x: CARDINAL [0..15] ← hex[index];
c: CHARIF x IN [0..9] THEN '0 + x ELSE 'A + (x-10);
st.PutChar[c];
ENDLOOP;
};
ScanName: PROC [name: ROPE] RETURNS [pos,bang,dot: INT] = {
len: INT = Rope.Length[name];
pos ← bang ← dot ← len;
WHILE pos > 0 DO
posM: INT = pos-1;
SELECT Rope.Fetch[name, posM] FROM
'! => bang ← dot ← posM;
'. => dot ← posM;
'>, '/, '] => RETURN;
ENDCASE;
pos ← posM;
ENDLOOP;
};
CheckAbort: ENTRY PROC [myData: MyData] = {
... puts out the given ropes to the MessageWindow. This is an entry procedure to keep the message in the MessageWindow consistent.
ENABLE UNWIND => myData.abortRequested ← TRUE;
IF myData.abortRequested THEN RETURN WITH ERROR ABORTED;
ProcessExtras.CheckForAbort[];
};
CheckAbortInternal: INTERNAL PROC [myData: MyData] = {
... puts out the given ropes to the MessageWindow. This is an entry procedure to keep the message in the MessageWindow consistent.
ENABLE UNWIND => myData.abortRequested ← TRUE;
IF myData.abortRequested THEN RETURN WITH ERROR ABORTED;
ProcessExtras.CheckForAbort[];
};
RemoteHandle stuff (to avoid races in using it)
AcquireRemoteHandle: ENTRY PROC
[myData: MyData, server: ROPE] RETURNS [ok: BOOLTRUE] = {
ENABLE UNWIND => myData.abortRequested ← TRUE;
fs: IFSFile.FSInstance ← NIL;
problem: IFSFile.Problem ← ok;
CheckAbortInternal[myData];
IF myData.remoteHandle # NIL THEN
IF NOT Rope.Equal[myData.remoteHandleServer, server, FALSE] THEN TRUSTED {
We have a server open, but it is the wrong one, so close it. Notice that we have limited remote handle ownership to one per process by this approach, since if the current process already owns the wrong server, we will never exit this loop. This is OK, since each process handles only one file at a time when using IFSFile.
WHILE myData.remoteHandleOwned # 0 DO
CheckAbortInternal[myData];
WAIT myData.remoteHandleReleased;
ENDLOOP;
fs ← myData.remoteHandle;
myData.remoteHandle ← NIL;
IF fs # NIL THEN IFSFile.Logout[fs];
};
IF myData.remoteHandle = NIL THEN TRUSTED {
There is no remote handle, so open one
userName,password: ROPE;
[userName,password] ← UserCredentials.Get[];
fs ← IFSFile.Login[server, userName, password
! IFSFile.UnableToLogin => {
why: ROPE ← "Remote access failed for ";
SELECT problem FROM
credentials => why ← "Invalid credentials for ";
io => why ← "Server not found: ";
ENDCASE;
MessagesOut.PutRopes[myData.errs, why, server];
GO TO notGood;
}];
myData.remoteHandle ← fs;
myData.remoteHandleServer ← server;
};
myData.remoteHandleOwned ← myData.remoteHandleOwned + 1;
EXITS notGood => ok ← FALSE;
};
NotOwned: ERROR = CODE;
ReleaseRemoteHandle: ENTRY PROC [myData: MyData] = {
ENABLE UNWIND => myData.abortRequested ← TRUE;
IF myData.remoteHandleOwned = 0 THEN RETURN WITH ERROR NotOwned;
myData.remoteHandleOwned ← myData.remoteHandleOwned - 1;
IF myData.remoteHandleOwned = 0 THEN BROADCAST myData.remoteHandleReleased;
CheckAbortInternal[myData];
};
FinalizeRemoteHandle: ENTRY PROC [myData: MyData] = TRUSTED {
ENABLE UNWIND => myData.abortRequested ← TRUE;
fs: IFSFile.FSInstance ← NIL;
WHILE myData.remoteHandleOwned # 0 DO
WAIT myData.remoteHandleReleased;
ENDLOOP;
fs ← myData.remoteHandle;
myData.remoteHandle ← NIL;
IF fs # NIL THEN IFSFile.Logout[fs];
IFSFile.Finalize[];
CheckAbortInternal[myData];
};
VersionStampFromRemoteHandle: PROC
[myData: MyData, server,name: ROPE, created: GMT]
RETURNS [valid: BOOL ← FALSE, stamp: VersionStamp] = TRUSTED {
ENABLE UNWIND => myData.abortRequested ← TRUE;
completed: CONDITION;
problem: IFSFile.Problem ← ok;
OnCompletion: IFSFile.Completer = TRUSTED {
problem ← outcome;
NoteCompleted[myData];
};
NoteCompleted: ENTRY PROC [myData: MyData] = TRUSTED {
BROADCAST completed;
};
WaitForCompleted: ENTRY PROC [myData: MyData] = TRUSTED {
WAIT completed;
};
bcd: BCD;
ifsFile: IFSFile.FileHandle ← NIL;
bcd.version ← NullVersion;
ProcessExtras.CheckForAbort[];
FOR pause: NAT IN [2..5] DO
ifsFile ← IFSFile.Open[myData.remoteHandle, name
! IFSFile.Error => {problem ← reason; CONTINUE};
IFSFile.CantOpen =>
SELECT reason FROM
notFound => GO TO badNews;
ENDCASE => {problem ← other; CONTINUE}
];
IF problem = ok THEN EXIT;
MessagesOut.PutRopes[myData.errs, "File open glitch: [", server, "]", name];
Process.Pause[Process.SecondsToTicks[pause/2]];
ENDLOOP;
IF problem = ok THEN {
FOR pause: NAT IN [2..5] DO
IFSFile.StartRead[
ifsFile, 0, SIZE[BCD]*bytesPerWord, LOOPHOLE[LONG[@bcd]], OnCompletion, NIL];
WaitForCompleted[myData];
IF problem = ok THEN EXIT;
MessagesOut.PutRopes[myData.errs, "File access glitch: [", server, "]", name];
Process.Pause[Process.SecondsToTicks[pause/2]];
ENDLOOP;
IFSFile.Close[ifsFile];
valid ← problem = ok;
stamp ← bcd.version;
};
EXITS badNews => {valid ← FALSE};
};
Routines to use the old maps for verification of names
ShortName: PROC
[full: ROPE] RETURNS [ROPE] = {
pos: INT ← Rope.Length[full];
lim: INT ← pos;
WHILE pos > 0 DO
c: CHAR = Rope.Fetch[full, pos←pos-1];
SELECT c FROM
'! => lim ← pos;
'>, '] => {pos ← pos + 1; EXIT};
ENDCASE;
ENDLOOP;
RETURN [Rope.Flatten[full, pos, lim-pos]];
};
FindShortObjectName: PROC
[myData: MyData, full: ROPE] RETURNS [valid: BOOLFALSE, stamp: VersionStamp] = {
IF myData.oldObjectMap # NIL THEN {
short: ROPE = ShortName[full];
rangeList: VersionMap.RangeList ←
VersionMap.ShortNameToRanges[myData.oldObjectMap, short];
WHILE rangeList # NIL DO
range: VersionMap.Range ← rangeList.first;
rangeList ← rangeList.rest;
WHILE range.len # 0 DO
name: ROPE;
stamp: VersionStamp;
[name, stamp, range] ← VersionMap.RangeToEntry[range];
IF Rope.Equal[full, name, FALSE] THEN RETURN [TRUE, stamp];
ENDLOOP;
ENDLOOP;
At this point we did NOT get a match, so flake out!
valid ← FALSE;
};
};
Main command routine
warningList: RopeList ← LIST["[Indigo]<Cedar>*", "[Ivy]*"];
GenMap: Commander.CommandProc = {
[cmd: Handle] RETURNS [result: REFNIL, msg: ROPENIL]
ris: STREAM = IO.RIS[cmd.commandLine];
token: ROPE ← "CurrentCedar";
dump: BOOLTRUE;
dumpDebug: BOOLTRUE;
namesOnly: BOOLFALSE;
remoteOpened: BOOLFALSE;
pos,bang,dot: INT ← 0;
inName,sourceName,objectName,outPrefix: ROPENIL;
command: ROPE ← cmd.command;
forkers: NAT ← 0;
results: GenerateDFClosure.ClosureInfo;
myData: MyData;
sourceMap: Map ← NIL;
objectMap: Map ← NIL;
checkRemote: BOOLTRUE;
inLog,outLog: STREAMNIL;
genMaps: BOOLFALSE; -- gen regular version maps if TRUE
trustOld: BOOLFALSE; -- trust old version maps if TRUE
prefix: ROPENIL;  -- the first prefix seen
oldPath: ROPE ← DefaultRemoteNames.Get[].previous; -- the previous release
token ← IO.GetTokenRope[ris, IO.IDProc ! IO.EndOfStream => CONTINUE].token;
[pos,bang,dot] ← ScanName[inName ← token];
IF bang = Rope.Size[inName] -- no version stuff
AND (bang-dot # 3 OR Rope.Run[inName, dot, ".df", 0, FALSE] # 3) -- no DF extension
THEN inName ← Rope.Concat[token, ".df"];
inName ← FS.FileInfo[inName
! FS.Error =>
IF error.group # bug THEN {
IO.PutF[
cmd.out, "\nCan't open %g\n %g\n",
[rope[inName]], [rope[error.explanation]]];
GO TO quit;
};
].fullFName;
[inLog,outLog] ← ViewerIO.CreateViewerStreams[
"VersionMapBuilder.log", NIL, "VersionMapBuilder.log", FALSE];
[pos,bang,dot] ← ScanName[inName];
outPrefix ← Rope.Flatten[inName, pos, dot-pos];
IO.PutF[outLog, "\nVersion Map Builder started at %g", [time[BasicTime.Now[]]]];
IO.PutF[outLog, "\n\nInput from: %g\n", [rope[inName]]];
sourceName ← Rope.Concat[outPrefix, ".sourceMap"];
objectName ← Rope.Concat[outPrefix, ".symbolsMap"];
TRUSTED {IFSFile.Initialize[]};
{ENABLE
UNWIND => {
FinalizeRemoteHandle[myData];
};
SELECT cmd.procData.clientData FROM
$GenMap => {
forkers ← 2;
genMaps ← TRUE;
};
$MergeMap => {
forkers ← 2;
trustOld ← genMaps ← TRUE;
};
$GenCedarMap => {
forkers ← 2;
genMaps ← TRUE;
outPrefix ← "Cedar";
};
$MergeCedarMap => {
forkers ← 2;
trustOld ← genMaps ← TRUE;
outPrefix ← "Cedar";
};
ENDCASE;
myData ← NEW[MyDataRep ← [
sourceQueue: PriorityQueue.Predict[2000, EntrySortPred],
objectQueue: PriorityQueue.Predict[1500, EntrySortPred],
errs: outLog,
checkRemote: checkRemote
]];
SELECT cmd.procData.clientData FROM
$GenCedarMap, $MergeCedarMap =>
myData.warningMatch ← Rope.Concat[oldPath, "*.df*"];
ENDCASE;
IF genMaps THEN {
sourceName ← Rope.Concat[outPrefix, "Source.VersionMap"];
objectName ← Rope.Concat[outPrefix, "Symbols.VersionMap"];
};
IF trustOld THEN {
Setup the old maps (if any); also make an index for the object map.
IO.PutRope[outLog, "\nReading old maps."];
myData.oldSourceMap ← LIST[
VersionMap.RestoreMapFromFile[sourceName
! FS.Error => {
IO.PutRope[outLog, "\nWarning, can't read "];
IO.PutRope[outLog, sourceName];
CONTINUE}]
];
myData.oldObjectMap ← LIST[
VersionMap.RestoreMapFromFile[objectName
! FS.Error => {
IO.PutRope[outLog, "\nWarning, can't read "];
IO.PutRope[outLog, objectName];
GO TO exit}]
];
EXITS exit => {};
};
CheckAbort[myData];
myData.which ← both;
IO.PutRope[outLog, "\nGenerating DF closure."];
results ← GenerateDFClosure.GenerateClosureToProc[
inName, outLog, EachFile, myData, [toFork: forkers]];
};
FinalizeRemoteHandle[myData];
IO.PutF[outLog, "\n\n%g DF files, %g source files, %g object files.\n",
[integer[results.dfFiles]], [integer[myData.sourceFiles]], [integer[myData.objectFiles]]];
IO.PutRope[outLog, "\nPrefixes seen:"];
FOR each: RopeList ← myData.prefixList, each.rest WHILE each # NIL DO
IF prefix = NIL THEN prefix ← each.first;
IO.PutF[outLog, "\n %g", [rope[each.first]]];
ENDLOOP;
IO.PutRope[outLog, "\n\nMaking short name index for source map."];
sourceMap ← VersionMapFromPQ[myData.sourceQueue, prefix, myData];
CheckAbort[myData];
IO.PutRope[outLog, "\n\nWriting "];
IO.PutRope[outLog, sourceName];
IO.PutRope[outLog, "\n"];
IF genMaps
THEN VersionMap.SaveMapToFile[sourceMap, sourceName]
ELSE WriteMapToFile[sourceMap, sourceName];
IO.PutRope[outLog, "\nMaking short name index for object map."];
objectMap ← VersionMapFromPQ[myData.objectQueue, prefix, myData];
CheckAbort[myData];
IO.PutRope[outLog, "\n\nWriting "];
IO.PutRope[outLog, objectName];
IO.PutRope[outLog, "\n"];
IF genMaps
THEN VersionMap.SaveMapToFile[objectMap, objectName]
ELSE WriteMapToFile[objectMap, objectName];
IO.PutF[outLog, "\n\nVersion maps built at %g\n\n", [time[BasicTime.Now[]]]];
IO.Close[inLog];
IO.Close[outLog];
EXITS quit => {};
};
Commander.Register[
"GenMap", GenMap, "Generate version maps", $GenMap];
stores to *Source.VersionMap & *Symbols.VersionMap
Commander.Register[
"MergeMap", GenMap, "Merge version maps", $MergeMap];
stores to *Source.VersionMap & *Symbols.VersionMap
Commander.Register[
"GenCedarMap", GenMap, "Generate Cedar version maps", $GenCedarMap];
stores to CedarSource.VersionMap & CedarSymbols.VersionMap
Commander.Register[
"MergeCedarMap", GenMap, "Merge Cedar version maps", $MergeCedarMap];
stores to CedarSource.VersionMap & CedarSymbols.VersionMap
END.