-- file: CodeCompare.mesa -- Edited by: Loretta, 24-Feb-81 18:45:50 -- Edited by: Satterthwaite, March 21, 1983 1:00 pm DIRECTORY Ascii: TYPE USING [CR, ESC, NUL, SP], Environment: TYPE USING [bytesPerWord, wordsPerPage], Exec: TYPE USING [AddCommand, commandLine, w], BcdDefs: TYPE USING [Base, BCD, MTRecord, VersionStamp], BcdOps: TYPE USING [BcdBase, MTHandle, SGHandle], FileStream: TYPE USING [InvalidHandle], Format: TYPE USING [NumberFormat], Runtime: TYPE USING [CallDebugger, GetBcdTime], Stream: TYPE USING [Delete, EndOfStream, Handle, GetBlock], Streams: TYPE USING [Destroy], String: TYPE USING [AppendChar, AppendString, EquivalentString, StringToDecimal], Storage: TYPE USING [FreeString], LongStorage: TYPE USING [Words, FreeWords], STP: TYPE USING [ Close, Create, CreateFileStream, CreateRemoteStream, Destroy, Error, ErrorCode, FileErrors, Handle, Login, NextFileName, Open, SetDirectory], Time: TYPE USING [Append, Unpack], TTY: TYPE USING [ GetChar, GetID, PutChar, PutCR, PutDecimal, PutLine, PutNumber, PutString, ResetUserAbort, UserAbort], UserCredentialsUnsafe: TYPE USING [GetUserCredentials, SetUserCredentials]; CodeCompare: PROGRAM IMPORTS Exec, FileStream, LongStorage, Runtime, Storage, STP, Stream, Streams, String, Time, TTY, UserCredentialsUnsafe = { bytesPerWord: CARDINAL = Environment.bytesPerWord; wordsPerPage: CARDINAL = Environment.wordsPerPage; bytesPerPage: CARDINAL = bytesPerWord*wordsPerPage; format: Format.NumberFormat = [base~8,zerofill~FALSE,unsigned~TRUE,columns~8]; byteformat: Format.NumberFormat = [base~8,zerofill~FALSE,unsigned~TRUE,columns~3]; pair: TYPE = RECORD[left, right: [0..0FFh]]; STPuser: STP.Handle ← NIL; name: STRING ← [80]; localName: STRING ← [40]; switches: STRING ← [5]; version, sourceVersion: BcdDefs.VersionStamp; tmpString: STRING = [25]; differences: BOOL ← FALSE; MaxDiffs: CARDINAL ← 20; difcount: CARDINAL; BufSize: CARDINAL = 20*wordsPerPage; buffer1: LONG POINTER; buffer2: LONG POINTER; header: BcdOps.BcdBase; -- Utilities CompareStreams: PROC [stream1, stream2: Stream.Handle, length1, length2: CARDINAL] = { bufcount1, bufcount2: CARDINAL ← 0; code1, code2: LONG POINTER TO ARRAY [0..BufSize) OF WORD; BuffersFilled: PROC [f1, f2: Stream.Handle, l1, l2: CARDINAL] RETURNS [BOOL] = { IF MIN[l1,l2] <= 0 THEN RETURN [FALSE]; SELECT bufcount1 FROM = bufcount2 => { code1 ← buffer1; code2 ← buffer2; [bufcount1,,] ← f1.GetBlock[ block~[blockPointer~buffer1, startIndex~0, stopIndexPlusOne~l1*bytesPerWord]]; [bufcount2,,] ← f2.GetBlock[ block~[blockPointer~buffer2, startIndex~0, stopIndexPlusOne~l2*bytesPerWord]]; bufcount1 ← bufcount1/bytesPerWord; bufcount2 ← bufcount2/bytesPerWord; RETURN [MIN[bufcount1, bufcount2] > 0]}; > bufcount2 => { code1 ← buffer1; code2 ← code2+bufcount1; --change array origin bufcount2 ← bufcount2 - bufcount1; [bufcount1,,] ← f1.GetBlock[ block~[blockPointer~buffer1, startIndex~0, stopIndexPlusOne~l1*bytesPerWord]]; bufcount1 ← bufcount1/bytesPerWord; RETURN [bufcount1 > 0]}; < bufcount2 => { code1 ← code1+bufcount2; --change array origin code2 ← buffer2; bufcount1 ← bufcount1 - bufcount2; [bufcount2,,] ← f2.GetBlock[ block~[blockPointer~buffer2, startIndex~0, stopIndexPlusOne~l2*bytesPerWord]]; bufcount2 ← bufcount2/bytesPerWord; RETURN [bufcount2 > 0]}; ENDCASE; RETURN [FALSE]}; -- body of CompareStreams difcount ← 0; WHILE BuffersFilled[stream1,stream2,length1,length2] DO length1 ← length1 - bufcount1; length2 ← length2 - bufcount2; FOR i: CARDINAL IN [0..MIN[bufcount1, bufcount2]) DO IF code1[i] # code2[i] THEN { w: pair; (Exec.w).PutNumber[i, format]; (Exec.w).PutNumber[code1[i], format]; (Exec.w).PutChar['[]; w ← LOOPHOLE[code1[i], pair]; (Exec.w).PutNumber[w.left, byteformat]; (Exec.w).PutChar[',]; (Exec.w).PutNumber[w.right, byteformat]; (Exec.w).PutChar[']]; (Exec.w).PutNumber[code2[i],format]; (Exec.w).PutChar['[]; w ← LOOPHOLE[code2[i], pair]; (Exec.w).PutNumber[w.left, byteformat]; (Exec.w).PutChar[',]; (Exec.w).PutNumber[w.right, byteformat]; (Exec.w).PutChar[']]; (Exec.w).PutCR; IF difcount = MaxDiffs THEN { (Exec.w).PutLine["Too many differences encountered. Quitting."L]; GOTO quit}; difcount ← difcount + 1; differences ← TRUE}; ENDLOOP; REPEAT quit => NULL; ENDLOOP; IF difcount = MaxDiffs THEN (Exec.w).PutString["More than "L]; (Exec.w).PutDecimal[difcount]; (Exec.w).PutLine[" differences"L]}; Config: SIGNAL = CODE; NoCode: SIGNAL = CODE; FindCode: PROC [stream: Stream.Handle, buf: LONG POINTER] RETURNS [ codeCount: CARDINAL, dateStamp, sourceStamp: BcdDefs.VersionStamp] = { -- positions stream to start of code -- FindCode returns size in words (codeCount) OPEN BcdDefs; ENABLE UNWIND => {NULL}; streamPos: CARDINAL; sgPointer: BcdDefs.Base; mtPos, sgPos: CARDINAL; codeBase: CARDINAL; mOffset: CARDINAL; mth: BcdOps.MTHandle; sgh: BcdOps.SGHandle; AdvanceStream: PROC [newPos: CARDINAL] = { -- positions stream to byte position newPos incr: CARDINAL; WHILE newPos > streamPos DO [incr,,] ← stream.GetBlock[block~[buf, 0, MIN[BufSize, newPos - streamPos]]]; streamPos ← streamPos + incr; ENDLOOP}; [streamPos,,] ← stream.GetBlock[block~[header,0,BCD.SIZE*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; sourceStamp ← header.sourceVersion; IF mtPos < sgPos THEN { -- read module table first, record fields, then overwrite with segment table 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]} 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 AdvanceStream[sgPos]; sgPointer ← LOOPHOLE[buf,BcdDefs.Base]; AdvanceStream[sgPos + LOOPHOLE[header.sgLimit,CARDINAL]*bytesPerWord]; buf ← LOOPHOLE[LOOPHOLE[buf,LONG CARDINAL] + LOOPHOLE[header.sgLimit,CARDINAL],LONG 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}; 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}; ExhaustStream: PROC [stream: Stream.Handle, buf: LONG POINTER] = { DO ENABLE Stream.EndOfStream => {EXIT}; [,,] ← stream.GetBlock[block~[buf, 0, BufSize*bytesPerWord]]; ENDLOOP}; CheckStream: PROC [stream: Stream.Handle] = { ENABLE UNWIND => {Streams.Destroy[stream ! FileStream.InvalidHandle => {stream.Delete; CONTINUE}]}; remoteFileName: STRING ← NIL; remoteFileName ← STP.NextFileName[stream]; IF remoteFileName # NIL THEN Storage.FreeString[remoteFileName] ELSE SIGNAL STP.Error[noSuchFile,"File not Found."L]}; ForceBCDExtension: PROC [string: STRING] = { FOR i: CARDINAL IN [0..name.length) DO IF name[i] = '. THEN {string.length ← i; EXIT}; ENDLOOP; String.AppendString[string, ".bcd"L]}; InitSTP: PROC [server: STRING] = { herald: STRING ← NIL; userName: STRING = [40]; userPassword: STRING = [40]; STPuser ← STP.Create[]; herald ← STP.Open[STPuser,server ! STP.Error => { SELECT code FROM noSuchHost => (Exec.w).PutLine["No such Host."L]; connectionTimedOut => (Exec.w).PutLine["Timeout."L]; connectionRejected => (Exec.w).PutLine["Rejected."L]; ENDCASE => (Exec.w).PutLine["Other (unspecified) error."L]}]; (Exec.w).PutLine[herald]; Storage.FreeString[herald]; UserCredentialsUnsafe.GetUserCredentials[userName, userPassword]; STP.Login[STPuser, userName, userPassword ! STP.Error => {CONTINUE}]}; PrintVersion: PROC [stamp: BcdDefs.VersionStamp] = { stampWords: CARDINAL = BcdDefs.VersionStamp.SIZE; str: PACKED ARRAY [0..4*stampWords) OF [0..16) = LOOPHOLE[stamp]; digit: STRING = "0123456789abcdef"L; FOR i: CARDINAL IN [0..4*stampWords) DO (Exec.w).PutChar[digit[str[i]]] ENDLOOP}; ProcessSwitches: PROC = { s: STRING = [10]; FOR i: CARDINAL IN [0..switches.length) DO SELECT switches[i] FROM 'd,'D => IF name.length = 0 THEN Runtime.CallDebugger["Called from Code Compare"L] ELSE { (Exec.w).PutString["\nDirectory "L]; (Exec.w).PutLine[name]; STP.SetDirectory[STPuser,name]}; 'c,'C => IF String.EquivalentString[name,"debug"] THEN Runtime.CallDebugger["Called from Code Compare"L] ELSE IF String.EquivalentString[name,"dir"L] THEN IF GetToken[name,s] THEN { (Exec.w).PutString["\nDirectory "L]; (Exec.w).PutLine[name]; STP.SetDirectory[STPuser,name]}; 'm,'M => MaxDiffs ← String.StringToDecimal[name]; ENDCASE; ENDLOOP}; TrimFileName: PROC [string: STRING] = { IF string[0] = '< THEN { i: CARDINAL ← string.length - 1; WHILE i > 0 DO IF string[i] = '> THEN { i ← i+1; FOR j: CARDINAL IN [i..string.length) DO string[j-i] ← string[j] ENDLOOP; string.length ← string.length-i; RETURN}; i ← i - 1; ENDLOOP; string.length ← 0}}; Login: PROC = { OPEN Ascii; user: STRING = [40]; password: STRING = [40]; userName: STRING = [40]; c: CHAR; UserCredentialsUnsafe.GetUserCredentials[userName, NIL]; (Exec.w).PutString["UserName: "L]; (Exec.w).PutString[userName]; c ← (Exec.w).GetChar; IF c#CR AND c#SP AND c#ESC THEN CollectRestOfNewString[user, c] ELSE String.AppendString[user, userName]; (Exec.w).PutString[", Password: "L]; UNTIL (c←(Exec.w).GetChar)=CR OR c=SP OR c=ESC DO (Exec.w).PutChar['*]; String.AppendChar[password,c]; ENDLOOP; (Exec.w).PutCR; UserCredentialsUnsafe.SetUserCredentials[user,password]; STP.Login[STPuser,user,password]}; CollectRestOfNewString: PROC [s: STRING, c: CHAR] = { rest: STRING = [40]; (Exec.w).PutChar[c]; (Exec.w).GetID[rest]; s.length ← 0; String.AppendChar[s, c]; String.AppendString[s, rest]}; GetToken: PROC [token, switches: STRING] RETURNS [BOOL] = { s: STRING; c: CHAR; Get: PROC RETURNS [char: CHAR] = { OPEN line ~~ Exec.commandLine; IF line.i >= line.s.length THEN RETURN [Ascii.NUL]; char ← line.s.text[line.i]; line.i ← line.i.SUCC; RETURN}; token.length ← switches.length ← 0; s ← token; WHILE (c ← Get[]) # Ascii.NUL DO SELECT c FROM Ascii.SP, Ascii.CR => IF s.length # 0 THEN RETURN [TRUE]; '/ => s ← switches; ENDCASE => String.AppendChar[s, c]; ENDLOOP; RETURN [FALSE]}; DoCompare: PROC = { oldStream,newStream: Stream.Handle ← NIL; oldSize, newSize: CARDINAL; buffer1 ← LongStorage.Words[BufSize]; buffer2 ← LongStorage.Words[BufSize]; header ← LongStorage.Words[BcdDefs.BCD.SIZE]; [] ← GetToken[name,switches]; -- host InitSTP[name]; WHILE GetToken[name,switches] DO BEGIN ENABLE { UNWIND => { LongStorage.FreeWords[buffer1]; --give buffers back LongStorage.FreeWords[buffer2]; LongStorage.FreeWords[header]; STP.Close[STPuser ! STP.Error => {CONTINUE}]; STPuser ← STP.Destroy[STPuser]}; STP.Error => { SELECT code FROM illegalUserName, illegalUserPassword => { (Exec.w).PutLine["Incorrect user/password."L]; Login[]; RETRY}; ENDCASE => {(Exec.w).PutString["Error: "L]; WriteError[error]}; LOOP}; Config => { (Exec.w).PutLine["Bound configuration; not a module."L]; ExhaustStream[newStream, buffer2]; newStream.Delete; Streams.Destroy[oldStream]; LOOP}; NoCode => { (Exec.w).PutLine["No code!"L]; ExhaustStream[newStream, buffer2]; newStream.Delete; Streams.Destroy[oldStream]; LOOP}}; IF TTY.UserAbort[] THEN {(Exec.w).PutLine[" ...aborted."L]; TTY.ResetUserAbort[]; EXIT}; IF switches.length > 0 THEN ProcessSwitches[] ELSE { (Exec.w).PutCR; ForceBCDExtension[name]; (Exec.w).PutString["Comparing file: "L]; (Exec.w).PutString[name]; (Exec.w).PutString["..."L]; localName.length ← 0; String.AppendString[localName, name]; TrimFileName[localName]; oldStream ← STP.CreateFileStream[localName, read ! STP.Error => { IF code IN STP.FileErrors THEN { (Exec.w).PutLine["No local file."L]; LOOP}}]; newStream ← STP.CreateRemoteStream[STPuser, name, read ! STP.Error => { Streams.Destroy[oldStream]; IF code IN STP.FileErrors THEN { (Exec.w).PutLine["No remote file."L]; LOOP}}]; (Exec.w).PutCR; CheckStream[newStream ! STP.Error => {Streams.Destroy[oldStream]}]; BEGIN ENABLE STP.Error => { (Exec.w).PutString["Error on local file: "L]; WriteError[error]; ExhaustStream[newStream, buffer2]; newStream.Delete; Streams.Destroy[oldStream]; LOOP}; [oldSize,version, sourceVersion] ← FindCode[oldStream, buffer1]; (Exec.w).PutString["Local: "L]; PrintVersion[version]; (Exec.w).PutString[" (from "L]; tmpString.length ← 0; Time.Append[tmpString,Time.Unpack[[sourceVersion.time]]]; (Exec.w).PutString[tmpString]; (Exec.w).PutString[", "]; (Exec.w).PutDecimal[oldSize]; (Exec.w).PutString[" words)\n"]; END; BEGIN ENABLE STP.Error => { (Exec.w).PutString["Error on remote file: "L]; WriteError[error]; ExhaustStream[newStream, buffer2]; newStream.Delete; Streams.Destroy[oldStream]; LOOP}; [newSize,version, sourceVersion] ← FindCode[newStream, buffer2]; (Exec.w).PutString["Remote: "L]; PrintVersion[version]; (Exec.w).PutString[" (from "L]; tmpString.length ← 0; Time.Append[tmpString,Time.Unpack[[sourceVersion.time]]]; (Exec.w).PutString[tmpString]; (Exec.w).PutString[", "]; (Exec.w).PutDecimal[newSize]; (Exec.w).PutString[" words)\n"]; END; CompareStreams[oldStream,newStream,oldSize,newSize ! STP.Error => {CONTINUE}]; Streams.Destroy[oldStream]; ExhaustStream[newStream, buffer2]; newStream.Delete}; END; ENDLOOP; LongStorage.FreeWords[buffer1]; --give buffers back LongStorage.FreeWords[buffer2]; LongStorage.FreeWords[header]; STP.Close[STPuser ! STP.Error => {CONTINUE}]; STPuser ← STP.Destroy[STPuser]}; WriteError: PROC [s: STRING] = { differences ← TRUE; (Exec.w).PutLine[s]}; CompareIt: PROC = { herald: STRING ← [60]; String.AppendString[herald, "Cedar 4.0 Code Compare of "L]; Time.Append[herald, Time.Unpack[Runtime.GetBcdTime[]]]; herald.length ← herald.length - 3; (Exec.w).PutLine[herald]; (Exec.w).PutCR; differences ← FALSE; DoCompare[ ! STP.Error => {differences ← TRUE; CONTINUE}]; (Exec.w).PutCR; IF differences THEN { (Exec.w).PutString["Differences or problems noted. Type any character to exit."L]; [] ← (Exec.w).GetChar} ELSE (Exec.w).PutString["No differences detected."L]}; Init: PROC = {Exec.AddCommand["CodeCompare.~"L, CompareIt]}; Init[]; }.