MoberyImpl.Mesa
Copyright Ó 1986, 1991, 1992 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
Willie-s, May 13, 1992 3:06 pm PDT
DIRECTORY
BasicTime USING [GMT],
FS USING [ExpandName, FileInfo, GetInfo, OpenFileFromStream, StreamOpen],
IO,
MobDefs USING [NullVersion, VersionStamp],
Mobery USING [EnumerateStampProc, StampTable, StampTableRep, VersionStamp],
MoberyPrivate,
RefText USING [ObtainScratch, ReleaseScratch, TrustTextAsRope],
Rope,
SymTab;
a.out know-how
(thanks to Michael Plass)
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 ¬ 0;
end: INT ¬ sourceStampByteLimit;
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: INT ¬ IO.GetBlock[self: stream, block: buf];
i: NAT ¬ 0;
needSetIndex: BOOL ¬ FALSE;
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:
BOOL ¬
FALSE]
--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:
BOOL ¬
FALSE] ~ {
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:
ROPE ¬
NIL, stamp: VersionStamp ¬ MobDefs.NullVersion]
RETURNS [found:
BOOL ¬
FALSE] =
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: BOOL ¬ TRUE;
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:
ROPE ¬
NIL] ~ {
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];
};