MoberyImpl.Mesa
Copyright © 1986 by Xerox Corporation. All rights reserved.
Mike Spreitzer September 3, 1986 10:32:43 am PDT
Andy Litman March 3, 1988 6:08:18 pm PST
Eduardo Pelegri-Llopart, December 21, 1989 8:27:57 pm PST
Michael Plass, June 29, 1989
JKF January 11, 1989 10:23:16 am PST
Last tweaked by Mike Spreitzer on August 14, 1990 8:26 am PDT
Mna, September 16, 1991 3:08 pm PDT
DIRECTORY
BasicTime USING [GMT],
FS USING [ExpandName, FileInfo, GetInfo, OpenFileFromStream, StreamOpen],
IO USING [BreakProc, Close, EndOfStream, Error, GetBlock, GetCard, GetID, GetLength, GetTokenRope, NUL, RIS, SetIndex, SkipWhitespace, SP, STREAM],
IO, UXStrings,
MobDefs USING [NullVersion, VersionStamp],
Mobery USING [EnumerateStampProc, StampTable, StampTableRep, VersionStamp],
MoberyPrivate,
RefText USING [ObtainScratch, ReleaseScratch, TrustTextAsRope],
Rope,
SymTab;
MoberyImpl: CEDAR MONITOR
IMPORTS FS, IO, RefText, Rope, SymTab, UXStrings
EXPORTS Mobery, MoberyPrivate =
BEGIN
Types
ROPE: TYPE = Rope.ROPE;
VersionStamp: TYPE = Mobery.VersionStamp;
NoStamp: PUBLIC ERROR = CODE;
StampPatterns: LIST OF ROPE = LIST["mob←version", "c2c←time", "c←stamp"];
hashMark: ROPE = "@(#)";
CacheEntry: TYPE ~ REF CacheEntryRep; CacheEntryRep: TYPE ~ RECORD [
list: LIST OF ROPE,
created: BasicTime.GMT
];
sourceStampByteLimit: INT ← 8000;
StopOnQuoteOrNull: IO.BreakProc = {
[char: CHAR] RETURNS [IO.CharClass]
RETURN [IF char = '\000 OR char = '" THEN break ELSE other]
};
cache: SymTab.Ref ~ SymTab.Create[];
FlushCache: PUBLIC PROC ~ {
cache.Erase[];
};
objRegs: LIST OF MoberyPrivate.ObjectFileTypeMatcherProc ← NIL;
RegisterObjectFileType: PUBLIC PROC[p: MoberyPrivate.ObjectFileTypeMatcherProc] ~ {
objRegs ← CONS[p, objRegs];
};
GetStamps: ENTRY PROC [fileName: ROPE] RETURNS [LIST OF ROPE] ~ {
ENABLE UNWIND => {};
fullName: ROPE ~ FS.ExpandName[fileName].fullFName;
list: LIST OF ROPE;
created: BasicTime.GMT;
WITH cache.Fetch[fullName].val SELECT FROM
x: CacheEntry => {
created ← FS.FileInfo[fileName].created;
IF created = x.created THEN RETURN [x.list];
};
ENDCASE => NULL;
[list: list, created: created] ← GetCOStamps[fullName];
{
ce: CacheEntry ~ NEW [CacheEntryRep ← [list: list, created: created]];
[] ← cache.Insert[fullName, ce];
RETURN [ce.list];
};
};
GetCOStamps: PUBLIC PROC [fileName: ROPE] RETURNS [list: LIST OF ROPE, created: BasicTime.GMT] = {
fileName refers to either a .c file or a .o file. Action is called with those strings delimited by hashMark at the front and either a double quote or a null at the end. For source files, only the first sourceStampByteLimit bytes are examined; for object files, the search is confined to the initialized data region.
head: LIST OF ROPE = LIST[NIL];
last: LIST OF ROPE ← head;
stream: IO.STREAM = FS.StreamOpen[fileName: fileName, accessOptions: $read];
buf: REF TEXT = RefText.ObtainScratch[nChars: 512];
hashMarkSize: INT = Rope.Length[hashMark];
hashStartChar: CHAR = Rope.Fetch[hashMark, 0];
smallBuf: REF TEXT = NEW[TEXT[hashMarkSize]];
start: INT;
end: INT;
match: BOOL;
index: INT ← 0;
created ← FS.GetInfo[FS.OpenFileFromStream[stream]].created;
FOR r: LIST OF MoberyPrivate.ObjectFileTypeMatcherProc ← objRegs, r.rest WHILE r # NIL DO
[match, start, end] ← r.first[stream];
IF match THEN EXIT;
IO.SetIndex[self: stream, index: 0];
ENDLOOP;
IF NOT match THEN {
if not found then search in a "reasonable" area
start ← 0;
end ← sourceStampByteLimit;
};
IO.SetIndex[self: stream, index: start];
index ← start;
WHILE index < end DO
bytes: INTIO.GetBlock[self: stream, block: buf];
i: NAT ← 0;
needSetIndex: BOOLFALSE;
IsHashMark: PROC [pos: INT] RETURNS [BOOL] ~ {
IF buf[pos] # hashStartChar THEN RETURN [FALSE];
IF pos+hashMarkSize <= bytes
THEN {
FOR j: INT ← 1, j+1 WHILE j < hashMarkSize DO
IF hashMark.Fetch[j] # buf[pos+j] THEN RETURN [FALSE];
ENDLOOP;
IO.SetIndex[stream, index+pos+hashMarkSize];
needSetIndex ← TRUE;
RETURN [TRUE];
}
ELSE {
IO.SetIndex[stream, index+pos];
needSetIndex ← TRUE;
[] ← IO.GetBlock[self: stream, block: smallBuf];
RETURN [Rope.Equal[hashMark, RefText.TrustTextAsRope[smallBuf]]];
}
};
IF bytes = 0 THEN EXIT;
WHILE i < bytes DO
IF IsHashMark[pos: i] THEN {
{
token: ROPE = IO.GetTokenRope[stream: stream, breakProc: StopOnQuoteOrNull ! IO.EndOfStream => EXIT].token;
newIndex: INT = index + i + hashMarkSize + Rope.Length[token];
last ← last.rest ← LIST[token];
IF newIndex > index + bytes THEN{
index ← newIndex;
IO.SetIndex[stream, index];
GOTO GetNextBlock;
};
i ← newIndex - index;
};
};
i ← i + 1;
REPEAT
GetNextBlock => LOOP; -- the enclosing WHILE --
ENDLOOP;
index ← index + bytes;
IF needSetIndex THEN IO.SetIndex[stream, index];
ENDLOOP;
RefText.ReleaseScratch[buf];
IO.Close[stream];
RETURN [head.rest, created]
};
Stamp Tables
StampTable: TYPE = Mobery.StampTable;
StampTableRep: PUBLIC TYPE = RECORD [
name: ROPE, -- name of the current file --
stamps: SymTab.Ref
];
StampPair: TYPE = RECORD [
stampClass: ROPE, -- the stamp class --
stamp: VersionStamp -- its version stamp --
];
UserData: TYPE = REF UserDataRep;
UserDataRep: TYPE = RECORD [
name: ROPE, -- name of the file --
stampList: LIST OF StampPair -- all the stamps --
];
EnumerateStampTable: PUBLIC PROC [stampTable: StampTable, to: Mobery.EnumerateStampProc] =
BEGIN
Enumerate all the files in stampTable
DoInner: PROC [key: ROPE, val: REF ANY] RETURNS [stop: BOOLFALSE] --SymTab.EachPairAction-- ~ {
myData: UserData = NARROW[val];
FOR stampList: LIST OF StampPair ← myData.stampList, stampList.rest WHILE stampList # NIL AND NOT stop DO
stamp: StampPair ~ stampList.first;
stop ← to[myData.name, stamp.stampClass, stamp.stamp];
ENDLOOP;
RETURN [stop: stop];
};
[] ← stampTable.stamps.Pairs[DoInner];
END;
InsertInStampTable: PROC [table: StampTable, name, class: ROPE, stamp: VersionStamp] RETURNS [previously: BOOLFALSE] ~ {
userData: UserData ← NARROW[table.stamps.Fetch[name].val];
IF userData = NIL THEN {
userData ← NEW [UserDataRep];
userData.name ← name;
userData.stampList ← LIST[[stampClass: class, stamp: stamp]];
IF NOT table.stamps.Insert[name, userData] THEN ERROR;
RETURN [previously: FALSE]
};
{
FOR stampList: LIST OF StampPair ← userData.stampList, stampList.rest WHILE stampList # NIL AND NOT previously DO
stampPair: StampPair ~ stampList.first;
IF stamp = stampPair.stamp AND Rope.Equal[class, stampPair.stampClass]
THEN RETURN [previously: TRUE];
ENDLOOP;
userData.stampList ← CONS[StampPair[stampClass: class, stamp: stamp], userData.stampList];
};
RETURN [previously: FALSE];
};
StampFromStampTable: PUBLIC PROC [stampTable: StampTable, fileName: ROPE] RETURNS [stampClass: ROPE, stamp: VersionStamp] =
BEGIN
Get the stamp of a given name; gives an error if none is present.
data: UserData = NARROW [stampTable.stamps.Fetch[fileName].val];
IF data = NIL THEN ERROR NoStamp;
FOR stampList: LIST OF StampPair ← data.stampList, stampList.rest WHILE stampList # NIL DO
stamp: StampPair ~ stampList.first;
RETURN [stamp: stamp.stamp, stampClass: stamp.stampClass];
ENDLOOP;
RETURN [stampClass: NIL, stamp: MobDefs.NullVersion]
END;
Has: PUBLIC PROC [stampTable: StampTable, name: ROPE, class: ROPENIL, stamp: VersionStamp ← MobDefs.NullVersion] RETURNS [found: BOOLFALSE] =
BEGIN
Test if a particular stamp is in the table
data: UserData = NARROW [stampTable.stamps.Fetch[name].val];
IF data = NIL THEN RETURN [found: FALSE];
FOR stampList: LIST OF StampPair ← data.stampList, stampList.rest WHILE stampList # NIL DO
stampPair: StampPair ~ stampList.first;
IF (class = NIL OR Rope.Equal[class, stampPair.stampClass]) AND
(stamp = MobDefs.NullVersion OR stamp = stampPair.stamp) THEN RETURN [found: TRUE];
ENDLOOP;
RETURN [found: FALSE]
END;
ReadStampTable: PUBLIC PROC [fileName: ROPE] RETURNS [stampTable: StampTable] ~ {
Get all the version stamps from an C or O File. Save them so they can be enumerated afterwards.
listOfStamps: LIST OF ROPE ~ GetStamps[fileName: fileName];
stampTable ← NEW [StampTableRep ← [
name: fileName,
stamps: SymTab.Create[case: TRUE]
]];
FOR stampList: LIST OF ROPE ← listOfStamps, stampList.rest WHILE stampList # NIL DO
aStamp: ROPE ~ stampList.first;
FOR patternList: LIST OF ROPE ← StampPatterns, patternList.rest WHILE patternList # NIL DO
pattern: ROPE ~ patternList.first;
patternLen: INT ~ pattern.Length[];
IF pattern.Equal[aStamp.Substr[len: patternLen]] THEN {
restOfString: ROPE ~ aStamp.Substr[start: patternLen+1];
name: ROPE;
stamp: MobDefs.VersionStamp;
[stamp: stamp, name: name] ← VersionAndNameFromString[restOfString];
IF name = NIL THEN RETURN [stampTable: NIL];
[] ← InsertInStampTable[table: stampTable, name: name, class: pattern, stamp: stamp];
};
ENDLOOP;
ENDLOOP;
RETURN [stampTable: stampTable];
};
StampTableSubset: PUBLIC PROC [tab1, tab2: StampTable] RETURNS [BOOL] = {
subset: BOOLTRUE;
SeeDiff: PROC [name, stampClass: ROPE, stamp: Mobery.VersionStamp] RETURNS [BOOL]
~ {subset ← FALSE; RETURN [TRUE]};
EnumerateStampTableDifference[tab1, tab2, SeeDiff];
RETURN [subset]};
EnumerateStampTableDifference: PUBLIC PROC [tab1, tab2: StampTable, to: Mobery.EnumerateStampProc] = {
CheckOne: PROC [name, stampClass: ROPE, stamp: Mobery.VersionStamp] RETURNS [BOOL] --Mobery.EnumerateStampProc-- ~ {
IF NOT Has[tab2, name, stampClass, stamp] THEN RETURN to[name, stampClass, stamp];
RETURN [FALSE];
};
EnumerateStampTable[tab1, CheckOne];
RETURN};
StampAndNameFromFile: PUBLIC PROC [fileName: ROPE] RETURNS [stamp: MobDefs.VersionStamp ← MobDefs.NullVersion, name: ROPENIL] ~ {
Extract the version stamp from fileName. If there is none, return MobDefs.NullVersion.
It uses the prefixes "@(#)pattern", where pattern is in StampPatterns.
listOfStamps: LIST OF ROPE ~ GetStamps[fileName: fileName];
FOR stampList: LIST OF ROPE ← listOfStamps, stampList.rest WHILE stampList # NIL DO
aStamp: ROPE ~ stampList.first;
FOR patternList: LIST OF ROPE ← StampPatterns, patternList.rest WHILE patternList # NIL DO
pattern: ROPE ~ patternList.first;
patternLen: INT ~ pattern.Length[];
IF pattern.Equal[aStamp.Substr[len: patternLen]] THEN {
restOfString: ROPE ~ aStamp.Substr[start: patternLen+1];
[stamp: stamp, name: name] ← VersionAndNameFromString[restOfString !IO.EndOfStream, IO.Error => GOTO NotThisOne];
RETURN;
EXITS NotThisOne => name ← name};
ENDLOOP;
ENDLOOP;
};
VersionAndNameFromString: PUBLIC PROC [aRope: ROPE] RETURNS [stamp: MobDefs.VersionStamp, name: ROPE] ~ {
Skip over blanks, then construct a version stamp;
MyBreakProc: IO.BreakProc ~ {
RETURN[SELECT char FROM
IN [IO.NUL .. IO.SP] => sepr,
'[, '], '(, '), '{, '}, '", '+, '-, '*, '/, '@, '←, ',, ':, '; => break,
ENDCASE => other]
};
aStream: IO.STREAM ~ IO.RIS[aRope];
token: ROPE;
[] ← aStream.SkipWhitespace[];
[token: token] ← aStream.GetTokenRope[MyBreakProc];
IF NOT token.Equal["["] THEN ERROR; -- this should not happen --
stamp[0] ← aStream.GetCard[];
[token: token] ← aStream.GetTokenRope[MyBreakProc];
IF NOT token.Equal[","] THEN ERROR; -- this should not happen --
stamp[1] ← aStream.GetCard[];
[token: token] ← aStream.GetTokenRope[MyBreakProc];
IF NOT token.Equal["]"] THEN ERROR; -- this should not happen --
name ← aStream.GetID[!
IO.EndOfStream, IO.Error => { name ← NIL; CONTINUE };
];
RETURN [stamp: stamp, name: name];
};
END.