<> <> <> <> DIRECTORY Basics, BasicTime USING [GMT, ToNSTime], FileNames, FS, IO, MobDefs, RefText, Rope, VersionMap, VersionMapClassify; VersionMapClassifyImpl: CEDAR PROGRAM IMPORTS BasicTime, FileNames, FS, IO, RefText, Rope, VersionMap EXPORTS VersionMapClassify = BEGIN <> GMT: TYPE = BasicTime.GMT; Map: TYPE = VersionMap.Map; MapList: TYPE = VersionMap.MapList; ROPE: TYPE = Rope.ROPE; RopeList: TYPE = LIST OF ROPE; STREAM: TYPE = IO.STREAM; VersionStamp: TYPE = VersionMap.VersionStamp; NullVersion: VersionStamp = VersionMapClassify.NullVersion; BadVersion: VersionStamp = VersionMapClassify.BadVersion; <> KindName: PUBLIC ARRAY VersionMapClassify.KindSet OF ROPE ¬ [ "unknown", "source", "intermediate", "sparc", "sparcOpt", "all" ]; <> <<>> Classify: PUBLIC PROC [name: ROPE] RETURNS [kind: VersionMapClassify.Kind] ~ { nameWithoutVersion: ROPE ~ FileNames.StripVersionNumber[name]; whichMap, other: ATOM; [whichMap, other] ¬ VersionMap.MapAtomForName[nameWithoutVersion]; SELECT TRUE FROM whichMap = $Intermediate => kind ¬ intermediate; whichMap = $Source => kind ¬ source; whichMap = $Executable => IF other = $opt THEN kind ¬ sparcOpt ELSE kind ¬ sparc; ENDCASE => kind ¬ source}; CreatedToMimosaVersionStamp: PUBLIC PROC [created: BasicTime.GMT] RETURNS [VersionStamp] = { RETURN [[BasicTime.ToNSTime[created], 0]]}; ReadVersionStamp: PUBLIC PROC [kind: VersionMapClassify.Kind, fullName: ROPE, created: BasicTime.GMT, rejectServerInaccessible: BOOL] RETURNS [stamp: VersionStamp, whyNot, warning: ROPE] ~ { bangPos: INT ~ fullName.Index[s2: "!"]; verless: ROPE ~ fullName.Substr[len: bangPos]; SELECT kind FROM unknown, source => RETURN [CreatedToMimosaVersionStamp[created], NIL, NIL]; intermediate => SELECT TRUE FROM Rope.Match["*.mob", verless] => RETURN ReadMobStamp[fullName, created, rejectServerInaccessible]; Rope.Match["*.c2c.c", verless] => { [stamp, whyNot] ¬ ReadCOStamp[fullName, created, rejectServerInaccessible]; warning ¬ NIL; RETURN}; ENDCASE => RETURN [CreatedToMimosaVersionStamp[created], NIL, NIL]; sparc, sparcOpt => { [stamp, whyNot] ¬ ReadCOStamp[fullName, created, rejectServerInaccessible]; IF whyNot#NIL AND NOT Rope.Match["*.c2c.o", verless] THEN { stamp ¬ CreatedToMimosaVersionStamp[created]; warning ¬ whyNot.Concat["; create date used for stamp"]; whyNot ¬ NIL} ELSE warning ¬ NIL; RETURN}; ENDCASE => ERROR; }; ReadMobStamp: PROC [fullName: ROPE, created: BasicTime.GMT, rejectServerInaccessible: BOOL] RETURNS [stamp: VersionStamp, whyNot, warning: ROPE ¬ NIL] ~ { from: IO.STREAM; mob: MobDefs.Mob; nRead: INT; from ¬ FS.StreamOpen[fileName: fullName, wantedCreatedTime: created !FS.Error => IF error.code#$serverInaccessible OR NOT rejectServerInaccessible THEN {whyNot ¬ IO.PutFLR["FS.Open[%g of %g] => Error[%g, %g]", LIST[ [rope[fullName]], [time[created]], [atom[error.code]], [rope[error.explanation]]] ]; GOTO Abort} ]; TRUSTED {nRead ¬ from.UnsafeGetBlock[[base: LOOPHOLE[@mob], count: BYTES[MobDefs.Mob]]]}; from.Close[]; IF nRead # BYTES[MobDefs.Mob] THEN RETURN [BadVersion, IO.PutFLR["%g of %g yielded only %g bytes, instead of %g", LIST[ [rope[fullName]], [time[created]], [integer[nRead]], [integer[BYTES[MobDefs.Mob]]]] ]]; {cvi: CARD ~ LOOPHOLE[mob.versionIdent]; SELECT cvi FROM CARD[MobDefs.VersionID] => RETURN [mob.version, NIL]; SwapHWords[MobDefs.VersionID] => RETURN [[SwapHWords[mob.version[0]], SwapHWords[mob.version[1]] ], NIL]; ENDCASE => RETURN[BadVersion, IO.PutFLR["%g of %g has unrecognized versionIdent (%08xH) [expected %08xH]", LIST[ [rope[fullName]], [time[created]], [cardinal[cvi]], [cardinal[MobDefs.VersionID]]] ]]; }; EXITS Abort => stamp ¬ BadVersion}; SwapHWords: PROC [c: CARD] RETURNS [CARD] ~ { n: Basics.LongNumber ~ LOOPHOLE[c]; sn: Basics.LongNumber ~ [pair[hi: n.lo, lo: n.hi]]; RETURN [sn.card]; }; hashMark: ROPE = Rope.Cat["@(", "#)mob", "_version ["]; --broken up to avoid spurrious one sourceStampByteLimit: INT ¬ 8000; headerBytes: INT = 4+4*7; -- size of header for .o files textSizeOffset: INT = 4; -- byte offset of text size field in .o files ReadCOStamp: PROC [fullName: ROPE, created: BasicTime.GMT, rejectServerInaccessible: BOOL] RETURNS [stamp: VersionStamp, whyNot: ROPE ¬ NIL] ~ { hashMarkSize: INT = Rope.Length[hashMark]; hashStartChar: CHAR = Rope.Fetch[hashMark, 0]; buf: REF TEXT = RefText.ObtainScratch[nChars: 512]; smallBuf: REF TEXT = NEW[TEXT[hashMarkSize]]; start: INT ¬ 0; end: INT ¬ sourceStampByteLimit; index: INT ¬ 0; baselen: INT; stream: IO.STREAM; cp: FS.ComponentPositions; base: ROPE; [fullName, cp, ] ¬ FS.ExpandName[fullName]; base ¬ fullName.Substr[start: cp.base.start, len: cp.base.length]; base ¬ base.Substr[len: baselen ¬ base.Index[s2: "."]]; {stream ¬ FS.StreamOpen[fileName: fullName, wantedCreatedTime: created !FS.Error => IF error.code#$serverInaccessible OR NOT rejectServerInaccessible THEN {whyNot ¬ IO.PutFLR["FS.Open[%g of %g] => Error[%g, %g]", LIST[ [rope[fullName]], [time[created]], [atom[error.code]], [rope[error.explanation]]] ]; GOTO NotOpen} ]; {ENABLE { IO.Error => {whyNot ¬ IO.PutFR["IO.Error while trying to find version stamp in %g of %g", [rope[fullName]], [time[created]] ]; GOTO Abort}; IO.EndOfStream => GOTO Alldun}; IF (NOT stream.EndOf[]) AND IO.PeekChar[self: stream].ORD < 2 THEN { <> ReadBinaryINT: PROC [stream: IO.STREAM] RETURNS [INT] ~ { ln: Basics.LongNumber ¬ [card[0]]; ln.hh ¬ IO.GetByte[stream]; ln.hl ¬ IO.GetByte[stream]; ln.lh ¬ IO.GetByte[stream]; ln.ll ¬ IO.GetByte[stream]; RETURN [ln.int] }; IO.SetIndex[self: stream, index: textSizeOffset]; start ¬ headerBytes + ReadBinaryINT[stream]; end ¬ start + ReadBinaryINT[stream]; IF NOT start IN [0..end) AND end <= IO.GetLength[stream] THEN { <> start ¬ 0; end ¬ IO.GetLength[stream]; }; }; IO.SetIndex[stream, 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 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 buf[i]=hashStartChar AND IsHashMark[pos: i] THEN { moduleName: ROPE; newIndex: INT; vs: VersionStamp ¬ ALL[0]; vs[0] ¬ stream.GetCard[ !IO.Error => IF ec=SyntaxError OR ec=Overflow THEN GOTO NotReally]; IF stream.GetChar[] # ', THEN GOTO NotReally; vs[1] ¬ stream.GetCard[ !IO.Error => IF ec=SyntaxError OR ec=Overflow THEN GOTO NotReally]; IF stream.GetChar[] # '] THEN GOTO NotReally; IF stream.GetChar[] # ' THEN GOTO NotReally; moduleName ¬ IO.GetTokenRope[stream: stream, breakProc: StopOnQuoteOrNull].token; IF base.IsPrefix[moduleName, FALSE] AND (baselen = moduleName.Length[] OR moduleName.EqualSubstrs[start1: baselen, s2: ".config", case: FALSE]) THEN { RefText.ReleaseScratch[buf]; IO.Close[stream]; RETURN [vs]}; newIndex ¬ stream.GetIndex[]; IF newIndex > index + bytes THEN{ index ¬ newIndex; IO.SetIndex[stream, index]; GOTO GetNextBlock; }; i ¬ newIndex - index; EXITS NotReally => i ¬ i}; i ¬ i + 1; REPEAT GetNextBlock => LOOP; -- the enclosing WHILE -- ENDLOOP; index ¬ index + bytes; IF needSetIndex THEN IO.SetIndex[stream, index]; ENDLOOP; GOTO Alldun; EXITS Alldun => whyNot ¬ IO.PutFR["%g of %g has no version stamp", [rope[fullName]], [time[created]] ]; Abort => stamp ¬ BadVersion}; IO.Close[stream]; EXITS NotOpen => stamp ¬ BadVersion}; RefText.ReleaseScratch[buf]; RETURN [BadVersion, whyNot]}; StopOnQuoteOrNull: IO.BreakProc = { <<[char: CHAR] RETURNS [IO.CharClass]>> RETURN [IF char = '\000 OR char = '" THEN break ELSE other] }; 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; }; <> MapListLookup: PUBLIC PROC [mapList: MapList, fullName: ROPE, created: BasicTime.GMT, onlyOne, testCreated, testName: BOOL] RETURNS [found: BOOL ¬ FALSE, fromMap: ROPE ¬ NIL, version: VersionStamp ¬ NullVersion] = { IF onlyOne THEN { version ¬ CreatedToMimosaVersionStamp[created]; fromMap ¬ VersionMap.VersionToName[mapList, version].name; {len: INT ¬ fromMap.Length[]; bang: INT ¬ ScanName[fromMap].bang; SELECT Rope.Run[fromMap, 0, fullName, 0, FALSE] FROM =0, RETURN [FALSE]; ENDCASE => RETURN [TRUE, fromMap, version]; }} ELSE { short: ROPE = VersionMap.ShortName[fullName]; rangeList: VersionMap.RangeList ¬ VersionMap.ShortNameToRanges[mapList, short]; WHILE rangeList # NIL DO range: VersionMap.Range ¬ rangeList.first; rangeList ¬ rangeList.rest; WHILE range.len # 0 DO name: ROPE; stamp: VersionStamp; eCreated: BasicTime.GMT; [name, stamp, eCreated, range] ¬ VersionMap.RangeToEntry[range]; IF testCreated AND created#eCreated THEN LOOP; IF testName AND NOT Rope.Equal[fullName, name, FALSE] THEN LOOP; RETURN [TRUE, name, stamp]; ENDLOOP; ENDLOOP; <> found ¬ FALSE; }; RETURN}; END.