DIRECTORY Basics USING [bytesPerWord], BasicTime USING [GMT, Now, nullGMT, ToPupTime], BcdDefs USING [BCD, NullVersion, VersionStamp], CedarProcess, Commander USING [CommandProc, Register], CommandTool, DefaultRemoteNames USING [Get], DFUtilities USING [Date, RemoveVersionNumber], FS USING [Error, FileInfo, minimumStreamBufferParms, Open, OpenFile, StreamFromOpenFile, StreamOpen], GenerateDFClosure USING [ActionProc, ClosureInfo, GenerateClosureToProc], IO USING [Close, GetIndex, PutChar, PutF, PutFR, PutRope, SetIndex, SetLength, STREAM, UnsafeGetBlock], MessagesOut USING [PutRopes], PriorityQueue USING [Insert, Predict, Ref, Remove, Size, SortPred], Process USING [CheckForAbort, Pause, SecondsToTicks], Rope USING [Concat, Equal, Fetch, Find, Flatten, FromProc, Index, Length, Match, ROPE, Run, Size, SkipTo, Substr], VersionMap, ViewerIO USING [CreateViewerStreams]; VersionMapFromDFImpl: CEDAR MONITOR LOCKS myData USING myData: MyData IMPORTS BasicTime, CedarProcess, Commander, CommandTool, DefaultRemoteNames, DFUtilities, FS, GenerateDFClosure, IO, MessagesOut, PriorityQueue, Process, Rope, VersionMap, ViewerIO SHARES IO, VersionMap = BEGIN 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: PQ _ NIL, objectQueue: PQ _ NIL, objectFiles,sourceFiles: INT _ 0, oldObjectMap: MapList _ NIL, oldSourceMap: MapList _ NIL, prefixList: RopeList _ NIL, warningMatch: ROPE _ NIL, errs: STREAM _ NIL, noIFSFile: BOOL _ FALSE, which: {source, object, both} _ both, checkRemote: BOOL _ TRUE, abortRequested: BOOL _ FALSE ]; pauseForServerGlitch: [0..3600] _ 90; -- seconds to pause if server busy triesForServerGlitch: NAT _ 12; -- times to retry if server busy forkers: NAT _ 2; -- # of processes to fork EachFile: GenerateDFClosure.ActionProc = { myData: MyData _ NARROW[data]; CheckAbort[myData]; SELECT kind FROM file => { needsVersion: BOOL _ FALSE; isBcd: BOOL _ FALSE; 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 { 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: BOOL _ TRUE] = { created: BasicTime.GMT _ entry.date.gmt; initName: ROPE _ entry.name; full: ROPE _ initName; attached: ROPE _ NIL; version: VersionStamp; useFileSystem: BOOL _ FALSE; IF entry.date.format = explicit AND created # BasicTime.nullGMT THEN { version _ [0, 0, BasicTime.ToPupTime[created]]; IF checkRemote THEN { IF myData.oldSourceMap = NIL THEN useFileSystem _ TRUE ELSE { 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 { useFileSystem _ TRUE; created _ BasicTime.nullGMT; checkRemote _ TRUE; }; 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]]; }; 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 { MessagesOut.PutRopes[ myData.errs, "Warning, failed to get source version for ", initName]; } ELSE { PriorityQueue.Insert[myData.sourceQueue, entry]; myData.sourceFiles _ myData.sourceFiles + 1; }; }; FillInObjectVersion: PROC [myData: MyData, entry: FileEntry, checkRemote: BOOL _ TRUE] = { outerCreated: BasicTime.GMT _ entry.date.gmt; full: ROPE _ entry.name; attached: ROPE _ NIL; valid: BOOL _ FALSE; -- indicates validity of version stamp SELECT TRUE FROM Rope.SkipTo[full, 0, "!"] = Rope.Length[full] => { valid _ FALSE; }; entry.date.format # explicit OR outerCreated = BasicTime.nullGMT => { valid _ FALSE; }; myData.oldObjectMap # NIL => { shortName: ROPE _ VersionMap.ShortName[full]; rangeList: VersionMap.RangeList _ VersionMap.ShortNameToRanges[myData.oldObjectMap, shortName]; IF rangeList # NIL THEN { FOR each: VersionMap.RangeList _ rangeList, each.rest WHILE each # NIL DO short: VersionMap.ShortNameSeq _ each.first.map.shortNameSeq; FOR i: CARDINAL IN [each.first.first..each.first.first+each.first.len) DO IF VersionMap.FetchCreated[each.first.map, short[i]] = outerCreated THEN { entry.version _ VersionMap.FetchStamp[each.first.map, short[i]]; GO TO enter; }; ENDLOOP; ENDLOOP; }; valid _ FALSE; }; ENDCASE => { valid _ FALSE; }; outerCreated _ BasicTime.nullGMT; full _ DFUtilities.RemoveVersionNumber[full]; [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 { UseFS: PROC [name: ROPE] = { file: FS.OpenFile _ FS.Open[ name: full, lock: read, wantedCreatedTime: outerCreated, remoteCheck: FALSE]; stream: STREAM _ FS.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]; }; UseFS[full]; }; GO TO enter; EXITS enter => { PriorityQueue.Insert[myData.objectQueue, entry]; myData.objectFiles _ myData.objectFiles + 1; }; }; EntrySortPred: PriorityQueue.SortPred = TRUSTED { 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; }; }; Process.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], created: entry.date.gmt, index: pos]; pos _ pos + Rope.Length[name] + 1; IF hasPrefix THEN pos _ pos - prefixLen; IF lag # NIL AND lag.version = entry.version THEN { 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: ROPE _ NIL; st: IO.STREAM _ FS.StreamOpen[name, create]; count: NAT _ map.len; names: ROPE _ map.names; firstCR: INT _ names.Index[0, "\n"]; Process.CheckForAbort[]; prefix _ names.Flatten[0, firstCR]; IO.PutRope[st, prefix]; IO.PutRope[st, "\n"]; FOR i: NAT IN [0..map.len) DO 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] = { 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: CHAR _ IF 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] = { ENABLE UNWIND => myData.abortRequested _ TRUE; IF myData.abortRequested THEN RETURN WITH ERROR ABORTED; Process.CheckForAbort[]; }; CheckAbortInternal: INTERNAL PROC [myData: MyData] = { ENABLE UNWIND => myData.abortRequested _ TRUE; IF myData.abortRequested THEN RETURN WITH ERROR ABORTED; Process.CheckForAbort[]; }; 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: BOOL _ FALSE, 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; created: BasicTime.GMT; [name, stamp, created, range] _ VersionMap.RangeToEntry[range]; IF Rope.Equal[full, name, FALSE] THEN RETURN [TRUE, stamp]; ENDLOOP; ENDLOOP; valid _ FALSE; }; }; Quit: ERROR = CODE; GenMap: Commander.CommandProc = { dump: BOOL _ TRUE; dumpDebug: BOOL _ TRUE; namesOnly: BOOL _ FALSE; remoteOpened: BOOL _ FALSE; pos,bang,dot: INT _ 0; inName,sourceName,objectName,outPrefix: ROPE _ NIL; command: ROPE _ cmd.command; results: GenerateDFClosure.ClosureInfo; myData: MyData; sourceMap: Map _ NIL; objectMap: Map _ NIL; checkRemote: BOOL _ TRUE; inLog,outLog: STREAM _ NIL; genMaps: BOOL _ FALSE; -- gen regular version maps if TRUE trustOld: BOOL _ FALSE; -- trust old version maps if TRUE prefix: ROPE _ NIL; -- the first prefix seen oldPath: ROPE _ DefaultRemoteNames.Get[].previous; -- the previous release switches: PACKED ARRAY CHAR['a..'z] OF BOOL _ ALL[FALSE]; ProcessArgument: PROC = { [pos,bang,dot] _ ScanName[inName]; 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[inName, ".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]]]; ERROR 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"]; { SELECT cmd.procData.clientData FROM $GenMap => { genMaps _ TRUE; }; $MergeMap => { trustOld _ genMaps _ TRUE; }; $GenCedarMap => { genMaps _ TRUE; outPrefix _ "Cedar"; }; $MergeCedarMap => { 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 { 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, followImports: NOT switches['s]]]; }; { 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]; }; }; ProcessSwitches: PROC [arg: ROPE] = { sense: BOOL _ TRUE; FOR index: INT IN [0..Rope.Length[arg]) DO char: CHAR _ Rope.Fetch[arg, index]; SELECT char FROM '- => LOOP; '~ => {sense _ NOT sense; LOOP}; IN ['a..'z] => switches[char] _ sense; IN ['A..'Z] => switches[char + ('a-'A)] _ sense; ENDCASE; sense _ TRUE; ENDLOOP; }; argv: CommandTool.ArgumentVector _ CommandTool.Parse[cmd: cmd, starExpand: FALSE ! CommandTool.Failed => {msg _ errorMsg; GO TO failed}]; FOR i: NAT IN [1..argv.argc) DO arg: ROPE = argv[i]; IF Rope.Length[arg] = 0 THEN LOOP; IF Rope.Fetch[arg, 0] = '- THEN { ProcessSwitches[arg]; LOOP; }; inName _ arg; CedarProcess.DoWithPriority[background, ProcessArgument ! Quit, ABORTED => GO TO quit]; ENDLOOP; IF inName = NIL THEN { inName _ "CurrentCedar.df"; CedarProcess.DoWithPriority[background, ProcessArgument ! Quit, ABORTED => GO TO quit]; }; EXITS quit => {}; failed => result _ $Failed; }; Commander.Register[ "GenMap", GenMap, "Generate version maps", $GenMap]; Commander.Register[ "MergeMap", GenMap, "Merge version maps", $MergeMap]; Commander.Register[ "GenCedarMap", GenMap, "Generate Cedar version maps", $GenCedarMap]; Commander.Register[ "MergeCedarMap", GenMap, "Merge Cedar version maps", $MergeCedarMap]; END. "VersionMapFromDFImpl.mesa Copyright c 1984, 1985, 1986 by Xerox Corporation. All rights reserved. Russ Atkinson (RRA) June 25, 1985 7:23:40 pm PDT Doug Wyatt, April 23, 1986 4:49:12 pm PST T Y P E S remoteHandle: IFSFile.FSInstance _ NIL, remoteHandleServer: ROPE _ NIL, remoteHandleReleased: CONDITION, remoteHandleOwned: NAT _ 0, Global variables Single file processing [data: REF, kind: {file, notFound, syntaxError}, name: ROPE, date: Date, from: ROPE] We need to merge this prefix in with the rest There is an explicit date in the DF file We have been instructed to verify the DF file date, either through the remote server OR through the old source version map. 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. For a non-explicit date, we can't trust anything. 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. 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. I wonder how this happened? Put the entry into the priority queue for later handling Read the version number from the BCD. Too bad we have to get the whole file! How did this happen? Don't trust the version map, we have to look at the server. We have an explicit date with which we can check the existing version map Scan for an acceptable date in the current files. We have found a version for the date! 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. 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: BOOL _ FALSE; FSBackdoor.EnumerateCacheForInfo[cacheChecker, NIL, full]; IF inCache THEN UseFS[full] We can afford to open the file on the local disk, since this is better than trying to open the remote file. 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 try to use FS directly (there may not be a Leaf server, for example). tempName: ROPE _ FS.Copy[from: full, to: "///Temp/VersionMapBuilder.temp$", setKeep: TRUE, keep: 8, wantedCreatedTime: outerCreated, remoteCheck: FALSE]; UseFS[tempName]; }; }; [x: Item, y: Item, data: REF _ NIL] RETURNS [BOOL] Warn the user that there are duplicate versions Display the entry. Return the name without the prefix or version ... puts out the given ropes to the MessageWindow. This is an entry procedure to keep the message in the MessageWindow consistent. ... puts out the given ropes to the MessageWindow. This is an entry procedure to keep the message in the MessageWindow consistent. RemoteHandle stuff (to avoid races in using it) AcquireRemoteHandle: ENTRY PROC [myData: MyData, server: ROPE] RETURNS [ok: BOOL _ TRUE] = { ENABLE UNWIND => myData.abortRequested _ TRUE; fs: IFSFile.FSInstance _ NIL; problem: IFSFile.Problem _ ok; CheckAbortInternal[myData]; IF myData.noIFSFile THEN GO TO notGood; 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 reason FROM credentials => why _ "Invalid credentials for "; io => { Quite probably the server does not support Leaf access myData.noIFSFile _ TRUE; GO TO notGood}; 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; Process.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]]; 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]]; ENDLOOP; IFSFile.Close[ifsFile]; valid _ problem = ok; stamp _ bcd.version; }; EXITS badNews => {valid _ FALSE}; }; Routines to use the old maps for verification of names At this point we did NOT get a match, so flake out! Main command routine [cmd: Handle] RETURNS [result: REF _ NIL, msg: ROPE _ NIL] TRUSTED {IFSFile.Initialize[]}; ENABLE UNWIND => { FinalizeRemoteHandle[myData] }; Setup the old maps (if any); also make an index for the object map. FinalizeRemoteHandle[myData]; When parsing the command line, be prepared for failure. The error is reported to the user Each argument can either be a switch specification or a genuine argument to be processed. The first argument (argv[0]) is not examined, because by convention it is the name of the command as given by the user. Ignore null arguments (it is not easy to generate them, even). This argument sets switches for the remaining patterns. By convention, switches are normally "sticky", in that they stay set until explicitly changed. stores to *Source.VersionMap & *Symbols.VersionMap stores to *Source.VersionMap & *Symbols.VersionMap stores to CedarSource.VersionMap & CedarSymbols.VersionMap stores to CedarSource.VersionMap & CedarSymbols.VersionMap ส!˜codešœ™Kšœ ฯmœ=™HK™0K™)—K˜šฯk ˜ Kšœžœ˜Kšœ žœžœ˜/Kšœžœžœ˜/Kšœ ˜ Kšœ žœ˜(Kšœ ˜ Kšœžœ˜Kšœ žœ˜.Kšžœžœ]˜eKšœžœ2˜IKšžœžœGžœ˜gKšœ žœ ˜Kšœžœ0˜CKšœžœ(˜5KšœžœGžœ˜rKšœ ˜ Kšœ žœ˜%K˜—šœžœž˜#Kšžœžœ˜!KšžœSžœžœA˜ดKšžœžœ ˜Kšœž˜K˜—šœ ™ K˜Kšžœžœ žœ˜Kšœžœ˜(Kšœžœ˜Kš žœžœžœžœžœžœ˜Kšžœžœ˜Kšžœžœ žœ˜Kšœžœ˜Kšœ žœ˜#Kšœ žœ˜%Kšœžœ˜!šœ žœ˜#Kšœžœ˜+—Kš œžœžœžœ žœ ˜5Kšžœžœžœ˜Kš œ žœžœžœžœ˜Kšžœžœžœžœ˜šœžœ˜*Kšœ0˜0—K˜Kšœ žœžœ˜#šœžœžœ˜Kšœžœ+žœ˜;K˜—Kšœ žœžœ ˜šœ žœžœ˜Kšžœžœžœ ˜ —Kš œ žœžœ žœžœ˜7K˜Kšœžœžœ ˜šœ žœž œžœ˜$Kšœ žœžœ˜Kšœ žœžœ˜Kšœžœ˜!Kšœžœ˜Kšœžœ˜Kšœžœ˜Kšœžœžœ˜Kšœžœžœ˜Kšœ žœžœ˜Kšœ#žœ™'Kšœžœžœ™Kšœž œ™ K˜%Kšœ žœžœ˜Kšœžœ™Kšœžœž˜Kšœ˜K˜——šœ™Kšœ&ฯc"˜HKšœžœŸ ˜@Kšœ žœŸ˜+K˜—™K™šœ*˜*Kšœžœ-žœžœ™TKšœžœ˜Kšœ˜šžœž˜šœ ˜ Kšœžœžœ˜Kšœžœžœ˜šœžœ˜'Kšœ;˜;—Kšœ žœ˜&šžœ'žœž˜4šœ!˜!Kšœ2˜2——šžœ˜šž˜šœ(žœ˜.Kšžœžœ˜%——šž˜šœ&žœ˜,Kšžœžœ˜$———šžœžœžœ˜%Kšœ-™-Kšœžœ˜ Kšœžœ ˜,Kšœžœ˜šžœ/žœžœž˜EKš žœ žœžœžœžœ˜:Kšœ ˜ Kšžœ˜—Kš žœžœžœžœ žœ žœ ˜PKšžœ ˜K˜—šž˜šž˜šžœ ˜ šžœ ž˜šœ˜Kšœ;˜;Kšœ<˜—Kšœ"˜"Kšœ/˜/KšžœN˜PKšžœ6˜8Kšœ2˜2Kšœ3˜3Kšžœ™šœ˜šž™Kšžœ%™+—šžœž˜#šœ ˜ Kšœ žœ˜K˜—šœ˜Kšœžœ˜K˜—šœ˜Kšœ žœ˜Kšœ˜K˜—šœ˜Kšœžœ˜Kšœ˜K˜—Kšžœ˜—šœ žœ˜Kšœ8˜8Kšœ8˜8Kšœ ˜ K˜Kšœ˜—šžœž˜#šœ˜Kšœ4˜4—Kšžœ˜—šžœ žœ˜Kšœ9˜9Kšœ:˜:K˜—šžœ žœ˜KšœC™CKšžœ(˜*šœžœ˜šœ(˜(šœžœ ˜Kšžœ+˜-Kšžœ˜Kšžœ˜ ——Kšœ˜—šœžœ˜šœ(˜(šœžœ ˜Kšžœ+˜-Kšžœ˜Kšžœžœ˜ ——K˜—Kšžœ ˜K˜—K˜Kšœ˜Kšœ˜Kšžœ-˜/šœ2˜2KšœCžœ˜V—K˜—˜Kšœ™šžœE˜GKšœZ˜Z—Kšžœ%˜'šžœ/žœžœž˜EKšžœ žœžœ˜)Kšžœ,˜.Kšžœ˜—Kšžœ@˜BKšœA˜AKšœ˜Kšžœ!˜#Kšžœ˜Kšžœ˜šžœ˜ Kšžœ0˜4Kšžœ'˜+—Kšžœ>˜@KšœA˜AKšœ˜Kšžœ!˜#Kšžœ˜Kšžœ˜šžœ˜ Kšžœ0˜4Kšžœ'˜+—KšžœK˜MKšžœ˜Kšžœ˜K˜—K˜—š œžœžœ˜%Kšœžœžœ˜šžœžœžœž˜*Kšœžœ˜$šžœž˜Kšœžœ˜ Kšœžœžœ˜ Kšžœ$˜&Kšžœ.˜0Kšžœ˜—Kšœžœ˜ Kšžœ˜—K˜—–I[cmd: Commander.Handle, starExpand: BOOL _ FALSE, switchChar: CHAR]šœKž˜PKšœ)žœžœ ˜8KšœZ™Z—K˜šžœžœžœž˜Kšœา™าKšœžœ ˜šžœžœžœ˜"Kšœ>™>—šžœžœ˜!Kšœ—™—Kšœ˜Kšžœ˜Kšœ˜—K˜ ˜7Kšœžœžœžœ˜—Kšžœ˜K˜—šžœ žœžœ˜K˜˜7Kšœžœžœžœ˜—K˜—šž˜Kšœ ˜ K˜—Kšœ˜K˜—šœ˜Kšœ4˜4Kšœ2™2—šœ˜Kšœ5˜5Kšœ2™2—šœ˜KšœD˜DKšœ:™:—šœ˜KšœE˜EKšœ:™:——K˜Kšžœ˜K˜—…—GŠW