-- file: Compare.mesa -- Edited by: Loretta, October 20, 1980 3:29 PM -- Copyright Xerox Corporation 1979, 1980 DIRECTORY AltoDefs USING [BytesPerPage, BytesPerWord, PageSize], BcdDefs USING [Base, BCD, MTRecord, VersionStamp], BcdOps USING [MTHandle, SGHandle], ImageDefs USING [AbortMesa, MakeImage, StopMesa], IODefs USING [CR, ESC, GetInputStream, GetOutputStream, NumberFormat, ReadChar, ReadID, SP, WriteChar, WriteDecimal, WriteLine, WriteNumber, WriteOctal, WriteString], MiscDefs USING [CallDebugger], OsStaticDefs USING [OsStatics], SegmentDefs USING [Read], Stream USING [Block, Delete, EndOfStream, Handle, GetBlock], StreamDefs USING [DisplayHandle, NewByteStream, StreamError, StreamHandle], String USING [AppendChar, AppendString, StringToDecimal], StringDefs USING [BcplToMesaString, MesaToBcplString], Storage USING [Words, FreeString, FreeWords], STP USING [Close, Create, CreateFileStream, CreateRemoteStream, Destroy, Error, ErrorCode, Handle, Login, NextFileName, Open, SetDirectory], Time USING [Append, Unpack]; Compare: PROGRAM IMPORTS IODefs, MiscDefs, Storage, STP, Stream, StreamDefs, StringDefs, String, Time, ImageDefs SHARES String = BEGIN OPEN AltoDefs, IODefs, String; format: NumberFormat = [base:8,zerofill:FALSE,unsigned:TRUE,columns:8]; byteformat: NumberFormat = [base:8,zerofill:FALSE,unsigned:TRUE,columns:3]; pair: TYPE = RECORD[left,right: [0..377B]]; STPuser: STP.Handle _ NIL; name: STRING _ [40]; switches: STRING _ [5]; version: BcdDefs.VersionStamp; differences: BOOLEAN _ FALSE; MaxDiffs: CARDINAL _ 20; difcount: CARDINAL; BufSize: CARDINAL = 20*PageSize; buffer1: POINTER = Storage.Words[BufSize]; buffer2: POINTER = Storage.Words[BufSize]; header: POINTER TO BcdDefs.BCD = Storage.Words[SIZE[BcdDefs.BCD]]; -- Utilities CompareStreams: PROCEDURE [stream1,stream2: Stream.Handle, length1, length2: CARDINAL] = BEGIN bufcount1,bufcount2: CARDINAL _ 0; code1,code2: POINTER TO ARRAY[0..BufSize) OF WORD; w: pair; i: CARDINAL; BuffersFilled: PROCEDURE [f1,f2: Stream.Handle,l1,l2: CARDINAL] RETURNS [BOOLEAN] = BEGIN IF MIN[l1,l2] <= 0 THEN RETURN[FALSE]; SELECT bufcount1 FROM = bufcount2 => BEGIN code1 _ buffer1; code2 _ buffer2; [bufcount1,,] _ Stream.GetBlock[sH: f1, block: [blockPointer: buffer1, startIndex: 0, stopIndexPlusOne: l1*BytesPerWord]]; [bufcount2,,] _ Stream.GetBlock[sH: f2, block: [blockPointer: buffer2, startIndex: 0, stopIndexPlusOne: l2*BytesPerWord]]; bufcount1 _ bufcount1/BytesPerWord; bufcount2 _ bufcount2/BytesPerWord; IF MIN[bufcount1,bufcount2] <= 0 THEN RETURN [FALSE] ELSE RETURN[TRUE]; END; > bufcount2 => BEGIN code1 _ buffer1; code2 _ code2+bufcount1; --change array origin bufcount2 _ bufcount2 - bufcount1; [bufcount1,,] _ Stream.GetBlock[sH: f1, block: [blockPointer: buffer1, startIndex: 0, stopIndexPlusOne: l1*BytesPerWord]]; bufcount1 _ bufcount1/BytesPerWord; IF bufcount1 <= 0 THEN RETURN [FALSE] ELSE RETURN[TRUE]; END; < bufcount2 => BEGIN code1 _ code1+bufcount2; --change array origin code2 _ buffer2; bufcount1 _ bufcount1 - bufcount2; [bufcount2,,] _ Stream.GetBlock[sH: f2, block: [blockPointer: buffer2, startIndex: 0, stopIndexPlusOne: l2*BytesPerWord]]; bufcount2 _ bufcount2/BytesPerWord; IF bufcount2 <= 0 THEN RETURN[FALSE] ELSE RETURN[TRUE]; END; ENDCASE; RETURN[FALSE]; END; -- body of CompareStreams difcount _ 0; bufcount1_bufcount2_0; WHILE BuffersFilled[stream1,stream2,length1,length2] DO length1 _ length1 - bufcount1; length2 _ length2 - bufcount2; FOR i IN [0..MIN[bufcount1,bufcount2]) DO IF code1[i] # code2[i] THEN BEGIN WriteNumber[i,format]; WriteNumber[code1[i],format]; WriteChar['[]; w _ LOOPHOLE[code1[i], pair]; WriteNumber[w.left, byteformat]; WriteChar[',]; WriteNumber[w.right, byteformat]; WriteChar[']]; WriteNumber[code2[i],format]; WriteChar['[]; w _ LOOPHOLE[code2[i], pair]; WriteNumber[w.left, byteformat]; WriteChar[',]; WriteNumber[w.right, byteformat]; WriteChar[']]; WriteChar[CR]; IF difcount = MaxDiffs THEN BEGIN WriteLine["Too many differences encountered. Quitting."]; GOTO quit END; difcount _ difcount + 1; differences _ TRUE; END; ENDLOOP; REPEAT quit => NULL; ENDLOOP; WriteChar[CR]; IF difcount = MaxDiffs THEN WriteString["More than "]; WriteDecimal[difcount]; WriteLine[" differences"]; END; Config: SIGNAL = CODE; NoCode: SIGNAL = CODE; FindCode: PROCEDURE [stream:Stream.Handle, buf: POINTER] RETURNS [ codeCount: CARDINAL, dateStamp: BcdDefs.VersionStamp] = -- positions stream to start of code --FindCode returns size in words (codeCount) BEGIN OPEN BcdDefs; ENABLE UNWIND => NULL; streamPos: CARDINAL; sgPointer: BcdDefs.Base; mtPos, sgPos: CARDINAL; codeBase: CARDINAL; mOffset: CARDINAL; mth: BcdOps.MTHandle; sgh: BcdOps.SGHandle; AdvanceStream: PROCEDURE [newPos: CARDINAL] = -- positions stream to byte position newPos BEGIN incr: CARDINAL; WHILE newPos > streamPos DO [incr,,] _ Stream.GetBlock[stream, Stream.Block[buf, 0, MIN[BufSize, newPos - streamPos]]]; streamPos _ streamPos + incr; ENDLOOP; END; [streamPos,,] _ Stream.GetBlock[stream, Stream.Block[header,0,SIZE[BCD]*BytesPerWord]]; --read header -- get module table, which contains index of code segment, -- starting offset in segment (in words) -- and length of code (in bytes) IF header.nConfigs > 0 THEN SIGNAL Config; mtPos _ header.mtOffset*BytesPerWord; sgPos _ header.sgOffset*BytesPerWord; dateStamp _ header.version; IF mtPos < sgPos THEN -- read module table first, record fields, then overwrite with segment table BEGIN AdvanceStream[mtPos]; mth _ LOOPHOLE[buf, BcdOps.MTHandle]; AdvanceStream[mtPos + LOOPHOLE[header.mtLimit,CARDINAL]*BytesPerWord]; mOffset _ mth.code.offset; codeCount _ mth.code.length; sgPos _ sgPos + LOOPHOLE[mth.code.sgi,CARDINAL]*BytesPerWord; AdvanceStream[sgPos]; sgh _ LOOPHOLE[buf, BcdOps.SGHandle]; AdvanceStream[sgPos + LOOPHOLE[header.sgLimit,CARDINAL]*BytesPerWord]; END ELSE -- must read segment table before module table, but don't know which part of --segment table! store segment table in first part of buffer, advance buffer -- pointer to read module table behind it, access both at once BEGIN AdvanceStream[sgPos]; sgPointer _ LOOPHOLE[buf,BcdDefs.Base]; AdvanceStream[sgPos + LOOPHOLE[header.sgLimit,CARDINAL]*BytesPerWord]; buf _ LOOPHOLE[LOOPHOLE[buf,CARDINAL] + LOOPHOLE[header.sgLimit,CARDINAL],POINTER]; AdvanceStream[mtPos]; mth _ LOOPHOLE[buf, BcdOps.MTHandle]; AdvanceStream[mtPos + LOOPHOLE[header.mtLimit,CARDINAL]*BytesPerWord]; sgh _ @sgPointer[mth.code.sgi]; mOffset _ mth.code.offset; codeCount _ mth.code.length; END; IF codeCount = 0 THEN SIGNAL NoCode; -- compute starting address of code (StreamPosition) codeBase _ (sgh.base-1)*BytesPerPage + mOffset*BytesPerWord; AdvanceStream[codeBase]; -- round codeCount up to nearest work boundary and convert to word count IF codeCount MOD 2 # 0 THEN codeCount _ codeCount + 1; codeCount _ codeCount/BytesPerWord; RETURN END; ExhaustStream: PROCEDURE[stream: Stream.Handle, buf: POINTER] = BEGIN DO ENABLE Stream.EndOfStream => EXIT; [,,] _ Stream.GetBlock[stream, Stream.Block[buf, 0, BufSize*BytesPerWord]]; ENDLOOP; END; CheckStream: PROCEDURE[stream: Stream.Handle] = BEGIN ENABLE UNWIND => Stream.Delete[stream]; remoteFileName: STRING _ NIL; remoteFileName _ STP.NextFileName[stream]; IF remoteFileName # NIL THEN Storage.FreeString[remoteFileName] ELSE SIGNAL STP.Error[noSuchFile,"File not Found."]; END; ForceBCDExtension: PROCEDURE[string: STRING] = BEGIN i: CARDINAL _ 0; FOR i IN [0..name.length) DO IF name[i] = '. THEN BEGIN string.length _ i; EXIT END; ENDLOOP; String.AppendString[string, ".bcd"]; END; InitSTP: PROCEDURE [server: STRING] = BEGIN herald: STRING _ NIL; user: STRING _ [40]; password: STRING _ [40]; STPuser _ STP.Create[]; StringDefs.BcplToMesaString[OsStaticDefs.OsStatics.UserName, user]; StringDefs.BcplToMesaString[OsStaticDefs.OsStatics.UserPassword, password]; herald _ STP.Open[STPuser,server ! STP.Error => BEGIN SELECT code FROM noSuchHost => WriteLine["No such Host."L]; connectionTimedOut => WriteLine["Timeout."L]; connectionRejected => WriteLine["Rejected."L]; ENDCASE => WriteLine["Other (unspecified) error."L]; END]; WriteLine[herald]; Storage.FreeString[herald]; STP.Login[STPuser, user, password !STP.Error => CONTINUE]; END; PrintVersion: PROCEDURE [stamp: BcdDefs.VersionStamp ] = BEGIN OPEN Time; tmp: STRING _ [20]; WriteString[" compiled by "]; WriteOctal[stamp.net]; WriteChar['#]; WriteOctal[stamp.host]; WriteString["# on "]; Append[tmp,Unpack[stamp.time]]; WriteString[tmp]; END; ProcessSwitches: PROCEDURE = BEGIN i: CARDINAL; FOR i IN [0..switches.length) DO SELECT switches[i] FROM 'd,'D => IF name.length = 0 THEN MiscDefs.CallDebugger["Called from Code Compare"] ELSE BEGIN STP.SetDirectory[STPuser,name]; IODefs.WriteString[" Directory "]; IODefs.WriteLine[name]; END; 'm,'M => MaxDiffs _ StringToDecimal[name]; ENDCASE; ENDLOOP; END; TrimFileName: PROCEDURE[string: STRING] = BEGIN i,j: CARDINAL; IF string[0] = '< THEN BEGIN FOR i DECREASING IN [1..string.length) DO IF string[i] = '> THEN BEGIN i _ i+1; FOR j IN [i..string.length) DO string[j-i] _ string[j] ENDLOOP; string.length _ string.length-i; RETURN; END; ENDLOOP; string.length _ 0 END; END; Login: PROCEDURE = BEGIN CR: CHARACTER = IODefs.CR; ESC: CHARACTER = IODefs.ESC; SP: CHARACTER = IODefs.SP; user: STRING = [40]; password: STRING = [40]; c: CHARACTER; StringDefs.BcplToMesaString[OsStaticDefs.OsStatics.UserName,user]; WriteString["UserName: "L]; WriteString[user]; c _ IODefs.ReadChar[]; IF c#CR AND c#SP AND c#ESC THEN BEGIN CollectRestOfNewString[user,c]; END; WriteString[", Password: "L]; UNTIL (c_IODefs.ReadChar[])=CR OR c=SP OR c=ESC DO WriteChar['*]; String.AppendChar[password,c]; ENDLOOP; WriteChar[CR]; StringDefs.MesaToBcplString[user,OsStaticDefs.OsStatics.UserName]; StringDefs.MesaToBcplString[password,OsStaticDefs.OsStatics.UserPassword]; STP.Login[STPuser,user,password]; END; CollectRestOfNewString: PROCEDURE [s: STRING, c: CHARACTER] = BEGIN input: StreamDefs.StreamHandle = IODefs.GetInputStream[]; Backup[s]; input.putback[input, c]; s.length _ 0; ReadID[s]; END; Backup: PROCEDURE [s: STRING] = BEGIN outputStream: StreamDefs.StreamHandle = IODefs.GetOutputStream[]; ds: StreamDefs.DisplayHandle _ WITH o: outputStream SELECT FROM Display => @o, ENDCASE => NIL; i: CARDINAL; IF ds = NIL THEN RETURN; FOR i DECREASING IN [0..s.length) DO ds.clearChar[ds,s[i]]; ENDLOOP; END; ReadCmdStream: PROCEDURE [stream: StreamDefs.StreamHandle, name, switches: STRING] RETURNS [BOOLEAN] = BEGIN activestring: STRING _ name; c: CHARACTER; i: CARDINAL; name.length _ 0; switches.length _ 0; DO c _ stream.get[stream ! StreamDefs.StreamError => EXIT]; SELECT c FROM '/ => activestring _ switches; IODefs.SP => IF switches.length # 0 OR name.length # 0 THEN EXIT; IODefs.CR => EXIT; ENDCASE => String.AppendChar[activestring,c]; ENDLOOP; FOR i IN [0..switches.length) DO -- convert all to lower case IF (c_switches[i]) IN ['A..'Z] THEN switches[i] _ c + ('a-'A); ENDLOOP; RETURN[switches.length # 0 OR name.length # 0]; END; DoCompare: PROCEDURE = BEGIN oldStream,newStream: Stream.Handle _ NIL; oldSize, newSize: CARDINAL; cmdstream: StreamDefs.StreamHandle = StreamDefs.NewByteStream["Com.cm",SegmentDefs.Read]; [] _ ReadCmdStream[cmdstream,name,switches]; -- image [] _ ReadCmdStream[cmdstream,name,switches]; -- host InitSTP[name]; WHILE ReadCmdStream[cmdstream,name,switches] DO BEGIN ENABLE BEGIN STP.Error => {SELECT code FROM illegalUserName, illegalUserPassword => BEGIN WriteLine["Incorrect user/password."L]; Login[]; RETRY; END; ENDCASE =>{WriteString["Error: "L]; WriteError[error]}; LOOP}; Config => {WriteLine["Bound configuration; not a module."L]; ExhaustStream[newStream, buffer2]; Stream.Delete[newStream]; Stream.Delete[oldStream]; LOOP}; NoCode => {WriteLine["No code! Interface definition?"L]; ExhaustStream[newStream, buffer2]; Stream.Delete[newStream]; Stream.Delete[oldStream]; LOOP} END; IF switches.length > 0 THEN ProcessSwitches[] ELSE BEGIN WriteChar[CR]; WriteChar[CR]; ForceBCDExtension[name]; WriteString["Comparing file: "]; WriteString[name]; WriteString["..."]; newStream _ STP.CreateRemoteStream[STPuser,name, read]; WriteChar[CR]; CheckStream[newStream]; TrimFileName[name]; oldStream _ STP.CreateFileStream[name,read]; BEGIN ENABLE STP.Error => {WriteString["Error on remote file: "L]; WriteError[error]; ExhaustStream[newStream, buffer2]; Stream.Delete[newStream]; LOOP}; [newSize,version] _ FindCode[newStream, buffer2]; WriteString["Remote version"];PrintVersion[version]; WriteChar[CR]; END; BEGIN ENABLE STP.Error => {WriteString["Error on local file: "L]; WriteError[error]; ExhaustStream[newStream, buffer2]; Stream.Delete[newStream]; Stream.Delete[oldStream]; LOOP}; [oldSize,version] _ FindCode[oldStream, buffer1]; WriteString["Local version"]; PrintVersion[version]; WriteChar[CR]; END; CompareStreams[oldStream,newStream,oldSize,newSize !STP.Error => CONTINUE]; Stream.Delete[oldStream]; ExhaustStream[newStream, buffer2]; Stream.Delete[newStream]; END; END; ENDLOOP; Storage.FreeWords[buffer1]; --give buffers back Storage.FreeWords[buffer2]; Storage.FreeWords[header]; STP.Close[STPuser !STP.Error => CONTINUE]; STPuser _ STP.Destroy[STPuser]; END; WriteError: PROCEDURE [s: STRING] = BEGIN differences _ TRUE; WriteLine[s]; END; -- main program time:STRING _ [18]; Time.Append[time,Time.Unpack[0]]; ImageDefs.MakeImage["CodeCompare.image."]; WriteString["Alto/Mesa Code Compare 6.0 of "]; time.length _ time.length - 3; WriteLine[time]; WriteChar[CR]; time.length _ 0; Time.Append[time,Time.Unpack[0]]; WriteLine[time]; WriteChar[CR]; DoCompare[! STP.Error => BEGIN WriteString["Type any character to exit."]; [] _ ReadChar[]; ImageDefs.AbortMesa[]; END]; WriteChar[CR]; IF differences THEN BEGIN WriteString["Differences noted. Type any character to exit."]; [] _ ReadChar[]; END ELSE WriteString["No differences detected."]; ImageDefs.StopMesa[]; END.