DIRECTORY Atom USING [GetPName], Basics USING [bytesPerWord], BasicTime USING [GMT, MonthOfYear, Now, Unpack, Unpacked], Buttons USING [Button, ButtonProc, Create, SetDisplayStyle], Checksum USING [ComputeChecksum], Commander USING [CommandProc, Register], Containers USING [ChildXBound, ChildYBound, Create], Convert USING [Error, IntFromRope, RopeFromInt], FS USING [EnumerateForInfo, Error, InfoProc, StreamOpen], IO USING [Close, CreateStream, CreateStreamProcs, GetBlock, Flush, PutChar, PutF, RopeFromROS, ROS, STREAM, StreamProcs], Labels USING [Create], Loader USING [BCDBuildTime], Process USING [Detach], RefText USING [ObtainScratch, ReleaseScratch], Rope USING [Length, ROPE], Rules USING [Create], STP USING [CompletionProcType, ConfirmProcType, Close, Create, DesiredProperties, Error, GetProperty, Handle, Login, Open, Retrieve, SetDesiredProperties], TypeScript USING [ChangeLooks, Create], UserCredentials USING [Get], VFonts USING [FontHeight, StringWidth], ViewerClasses USING [Viewer], ViewerEvents USING [EventProc, RegisterEventProc], ViewerIO USING [CreateViewerStreams], ViewerOps USING [AddProp, ComputeColumn, CreateViewer, FetchProp, MoveViewer, OpenIcon, SetOpenHeight], ViewerTools USING [GetContents, MakeNewTextViewer, SetContents, SetSelection]; Compare: CEDAR MONITOR IMPORTS Atom, BasicTime, Buttons, Checksum, Commander, Containers, Convert, FS, IO, Labels, Loader, Process, RefText, Rope, Rules, STP, TypeScript, UserCredentials, VFonts, ViewerEvents, ViewerIO, ViewerOps, ViewerTools = BEGIN BYTE: TYPE = [0..100H); ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; Viewer: TYPE = ViewerClasses.Viewer; buttonHeight: INT _ VFonts.FontHeight[] + 3; buttonWidth: INT _ VFonts.StringWidth["Checksum"] + 2*3; ClientData: TYPE = REF ClientDataRep; ClientDataRep: TYPE = RECORD [ log: STREAM _ NIL, in: STREAM _ NIL, pleaseStop: BOOLEAN _ FALSE, user: PROCESS _ NIL, host1, file1: Viewer _ NIL, host2, file2: Viewer _ NIL ]; global: ClientData _ NIL; -- debugging Create: Commander.CommandProc = { viewer, buttons, log: Viewer _ NIL; data: ClientData _ NEW[ClientDataRep _ []]; global _ data; viewer _ ViewerOps.CreateViewer [ flavor: $Container, info: [name: "Compare", column: left, iconic: TRUE, scrollable: FALSE]]; [] _ ViewerEvents.RegisterEventProc[Poof, destroy, viewer, TRUE]; ViewerOps.AddProp[viewer, $Compare, data]; log _ TypeScript.Create[ [name: "Compare.log", wy: 27+4, parent: viewer, border: FALSE], FALSE]; [data.in, data.log] _ ViewerIO.CreateViewerStreams [ name: "Compare.log", backingFile: "Compare.log", viewer: log, editedStream: FALSE]; Containers.ChildXBound[viewer, log]; Containers.ChildYBound[viewer, log]; CreateButtons[data, viewer, log]; TypeScript.ChangeLooks[log, 'f]; IO.PutF[data.log, "Compare of %G.\n\n", [time[Loader.BCDBuildTime[Create]]]]; ViewerOps.OpenIcon[viewer]; }; CreateButtons: ENTRY PROC [data: ClientData, parent, log: Viewer] = { child: Viewer _ NIL; kids: Viewer = Containers.Create[ info: [parent: parent, border: FALSE, scrollable: FALSE, wx: 0, wy: -9999, ww: 9999, wh: 0] ]; Containers.ChildXBound[parent, kids]; child _ MakeRule[kids, child]; child _ MakeLabel[kids, child, "File 1: "]; child _ data.host1 _ MakeLabeledText[ parent: kids, sibling: child, name: "Host:", data: "Host (empty for local)", width: VFonts.StringWidth["BunkerHill-xxx"], prev: data.host1, newline: FALSE ]; child _ data.file1 _ MakeLabeledText[ parent: kids, sibling: child, name: "Name:", data: "File", width: VFonts.StringWidth["Big long file name ..........................."], prev: data.file1, newline: FALSE ]; Containers.ChildXBound[parent, child]; child _ MakeLabel[kids, child, "File 2: "]; child _ data.host2 _ MakeLabeledText[ parent: kids, sibling: child, name: "Host:", data: "Host (empty for local)", width: VFonts.StringWidth["BunkerHill-xxx"], prev: data.host2, newline: FALSE ]; child _ data.file2 _ MakeLabeledText[ parent: kids, sibling: child, name: "Name:", data: "File", width: VFonts.StringWidth["Big long file name ..........................."], prev: data.file2, newline: FALSE ]; Containers.ChildXBound[parent, child]; child _ MakeRule[kids, child]; child _ MakeLabel[kids, child, "What: "]; child _ MakeButton[kids, child, data, "Stop", StopProc]; child _ MakeButton[kids, child, data, "Checksum", ChecksumProc]; child _ MakeButton[kids, child, data, "Compare", CompareProc]; child _ MakeRule[kids, child]; { kidsY: INTEGER = 2; kidsH: INTEGER = child.wy + child.wh + 2; ViewerOps.MoveViewer[viewer: log, x: 0, y: kidsY + kidsH, w: log.ww, h: parent.ch - (kids.wy + kidsH), paint: FALSE]; ViewerOps.SetOpenHeight[parent, kidsY + kidsH + 12 * buttonHeight]; IF ~parent.iconic THEN ViewerOps.ComputeColumn[parent.column]; ViewerOps.MoveViewer[viewer: kids, x: kids.wx, y: kidsY, w: kids.ww, h: kidsH]; }; }; Poof: ViewerEvents.EventProc = { data: ClientData _ NARROW[ViewerOps.FetchProp[viewer, $Compare]]; IF event # destroy OR before # TRUE THEN ERROR; Stop[data]; IO.Close[data.log]; IO.Close[data.in]; }; StopProc: Buttons.ButtonProc = { data: ClientData _ NARROW[clientData]; Stop[data]; }; Stop: PROC [data: ClientData] = TRUSTED { data.pleaseStop _ TRUE; IF data.user # NIL THEN JOIN data.user; data.user _ NIL; }; ChecksumProc: Buttons.ButtonProc = { data: ClientData _ NARROW[clientData]; data.pleaseStop _ FALSE; ChecksumIt[data]; }; CompareProc: Buttons.ButtonProc = { data: ClientData _ NARROW[clientData]; data.pleaseStop _ FALSE; CompareIt[data]; }; streamProcs: REF IO.StreamProcs = IO.CreateStreamProcs[ variety: $output, class: $Compare, putBlock: PutBlock, close: Close ]; StreamData: TYPE = REF StreamDataRep; StreamDataRep: TYPE = RECORD [ data: ClientData, host: ROPE _ NIL, fileName: ROPE _ NIL, sizeRope: ROPE _ NIL, createRope: ROPE _ NIL, size: INT _ 0, bytes: INT _ 0, checksum: WORD _ 0, stream: STREAM _ NIL, index: INT _ 0, block: REF READONLY TEXT _ NIL, length: INT _ 0, done: BOOL _ FALSE ]; waiting: CONDITION; PushLocalBits: PROC [data: ClientData, me: StreamData, files: ROPE] = { buffer: REF TEXT = RefText.ObtainScratch[512]; -- Hack, must be same size as STP uses PutBlock: ENTRY PROC = { IF me.block # NIL THEN ERROR; me.block _ buffer; me.length _ buffer.length; BROADCAST waiting; UNTIL me.block = NIL DO WAIT waiting; ENDLOOP; }; EachFile: FS.InfoProc = { checksumRope: ROPE; stream: STREAM _ FS.StreamOpen[fileName: fullFName, streamBufferParms: [128, 4]]; me.size _ bytes; me.bytes _ 0; me.checksum _ 0; me.index _ 0; DO nBytes: NAT = IO.GetBlock[stream, buffer, 0]; IF nBytes = 0 THEN EXIT; PutBlock[]; ENDLOOP; IO.Close[stream]; checksumRope _ Convert.RopeFromInt[me.checksum, 16, FALSE]; IO.PutF[ data.log, "%8G %4G %G %G.\n", [integer[me.bytes]], [rope[checksumRope]], [rope[FTPStyleDate[created]]], [rope[fullFName]] ]; IF me.size # me.bytes THEN IO.PutF[ data.log, "***** Length mixup. Expected %g bytes, got %G.\n", [integer[me.size]], [integer[me.bytes]] ]; RETURN[~data.pleaseStop]; }; FS.EnumerateForInfo[files, EachFile ! FS.Error => { IO.PutF[data.log, " FS troubles: %G.\n", [rope[error.explanation]] ]; CONTINUE; } ]; RefText.ReleaseScratch[buffer]; me.done _ TRUE; PokeWaiting[]; }; PushTheBits: PROC [data: ClientData, me: StreamData, host, files: ROPE] = { Open: STP.ConfirmProcType = { me.fileName _ file; me.sizeRope _ STP.GetProperty[stp, size]; me.createRope _ STP.GetProperty[stp, createDate]; me.size _ 0; me.bytes _ 0; me.checksum _ 0; me.index _ 0; IF data.pleaseStop THEN RETURN[abort, NIL]; me.size _ Convert.IntFromRope[me.sizeRope ! Convert.Error => CONTINUE]; RETURN[do, me.stream]; }; Close: ENTRY STP.CompletionProcType = { SELECT what FROM ok => NULL; error => IO.PutF[data.log, "***** STP Says error: %G while processing %G.\n", [rope[fileOrError]], [rope[me.fileName]] ]; ENDCASE => ERROR; }; herald, name, password: ROPE; failed: BOOL _ FALSE; stp: STP.Handle _ STP.Create[]; desiredProperties: STP.DesiredProperties _ ALL[FALSE]; desiredProperties[size] _ TRUE; desiredProperties[createDate] _ TRUE; desiredProperties[directory] _ TRUE; desiredProperties[nameBody] _ TRUE; desiredProperties[version] _ TRUE; herald _ STP.Open[stp, host ! STP.Error => { IO.PutF[data.log, " STP Open failed: %G.\n", [rope[error]] ]; herald _ NIL; failed _ TRUE; CONTINUE }]; IF herald # NIL THEN IO.PutF[data.log, "%G\n", [rope[herald]] ]; IF failed THEN { me.done _ TRUE; PokeWaiting[]; RETURN; }; me.stream _ IO.CreateStream[streamProcs: streamProcs, streamData: me]; me.host _ host; [name, password] _ UserCredentials.Get[]; STP.Login[stp, name, password]; STP.SetDesiredProperties[stp, desiredProperties]; STP.Retrieve[stp, files, Open, Close ! STP.Error => { IO.PutF[data.log, " STP Retrieve failed: %G.\n", [rope[error]] ]; CONTINUE }]; STP.Close[stp ! STP.Error => CONTINUE ]; me.done _ TRUE; PokeWaiting[]; }; PokeWaiting: ENTRY PROC = { BROADCAST waiting; }; Words: PROC [bytes: NAT] RETURNS [words: NAT] = { RETURN[(bytes+Basics.bytesPerWord-1)/Basics.bytesPerWord]; }; PutBlock: ENTRY PROC [self: STREAM, block: REF READONLY TEXT, startIndex: NAT, count: NAT] = { me: StreamData = NARROW[self.streamData]; IF startIndex # 0 THEN ERROR; IF me.block # NIL THEN ERROR; me.block _ block; me.length _ MIN[count, block.length]; BROADCAST waiting; UNTIL me.block = NIL DO WAIT waiting; ENDLOOP; }; Close: ENTRY PROC [self: STREAM, abort: BOOL] = { me: StreamData = NARROW[self.streamData]; data: ClientData _ me.data; checksumRope: ROPE _ Convert.RopeFromInt[me.checksum, 16, FALSE]; IO.PutF[ data.log, "%8G %4G %G [%G]%G.\n", [integer[me.size]], [rope[checksumRope]], [rope[me.createRope]], [rope[me.host]], [rope[me.fileName]] ]; IF me.size # me.bytes THEN IO.PutF[ data.log, "***** Length mixup. Expected %g bytes, got %G.\n", [integer[me.size]], [integer[me.bytes]] ]; }; ChecksumIt: ENTRY PROC [data: ClientData] = { me: StreamData _ NEW[StreamDataRep _ [data: data]]; host: ROPE = ViewerTools.GetContents[data.host1]; files: ROPE = ViewerTools.GetContents[data.file1]; IO.PutF[ data.log, "\n%G Scanning [%G]%G.\n\n", [time[BasicTime.Now[]]], [rope[host]], [rope[files]] ]; TRUSTED { IF Rope.Length[host] = 0 THEN Process.Detach[FORK PushLocalBits[data, me, files]] ELSE Process.Detach[FORK PushTheBits[data, me, host, files]]; }; UNTIL me.done DO base: LONG POINTER; bytes: LONG POINTER TO PACKED ARRAY [0..NAT.LAST] OF CHAR; UNTIL me.block # NIL OR me.done DO WAIT waiting; ENDLOOP; IF me.done THEN EXIT; base _ LOOPHOLE[me.block, LONG POINTER]+TEXT[0].SIZE; bytes _ LOOPHOLE[base]; me.bytes _ me.bytes + me.length; IF (me.length MOD 2) # 0 THEN TRUSTED { bytes[me.length] _ 0C; }; TRUSTED { me.checksum _ Checksum.ComputeChecksum[me.checksum, Words[me.length], base]; }; me.index _ me.index + me.length; me.block _ NIL; BROADCAST waiting; ENDLOOP; IO.Flush[data.log]; }; CompareIt: ENTRY PROC [data: ClientData] = { me1: StreamData _ NEW[StreamDataRep _ [data: data]]; me2: StreamData _ NEW[StreamDataRep _ [data: data]]; host1: ROPE = ViewerTools.GetContents[data.host1]; file1: ROPE = ViewerTools.GetContents[data.file1]; host2: ROPE = ViewerTools.GetContents[data.host2]; file2: ROPE = ViewerTools.GetContents[data.file2]; IO.PutF[ data.log, "\n%G\nComparing [%G]%G with [%G]%G.\n\n", [time[BasicTime.Now[]]], [rope[host1]], [rope[file1]], [rope[host2]], [rope[file2]] ]; TRUSTED { IF Rope.Length[host1] = 0 THEN Process.Detach[FORK PushLocalBits[data, me1, file1]] ELSE Process.Detach[FORK PushTheBits[data, me1, host1, file1]]; IF Rope.Length[host2] = 0 THEN Process.Detach[FORK PushLocalBits[data, me2, file2]] ELSE Process.Detach[FORK PushTheBits[data, me2, host2, file2]]; }; DO UNTIL me1.block # NIL OR me1.done DO WAIT waiting; ENDLOOP; IF me1.done THEN EXIT; UNTIL me2.block # NIL OR me2.done DO WAIT waiting; ENDLOOP; IF me2.done THEN EXIT; me1.bytes _ me1.bytes + me1.length; me2.bytes _ me2.bytes + me2.length; IF me1.length # me2.length THEN { IO.PutF[ data.log, "Ugh: Can't yet process different size blocks: %G vs %G.\n", [integer[me1.length]], [integer[me2.length]] ]; EXIT; } ELSE { words: NAT _ Words[me1.length]; base1: LONG POINTER _ LOOPHOLE[me1.block, LONG POINTER]+TEXT[0].SIZE; bytes1: LONG POINTER TO PACKED ARRAY [0..NAT.LAST] OF CHAR _ LOOPHOLE[base1]; base2: LONG POINTER _ LOOPHOLE[me2.block, LONG POINTER]+TEXT[0].SIZE; bytes2: LONG POINTER TO PACKED ARRAY [0..NAT.LAST] OF CHAR _ LOOPHOLE[base2]; IF (me1.length MOD 2) # 0 THEN TRUSTED { bytes1[me1.length] _ 0C; }; IF (me2.length MOD 2) # 0 THEN TRUSTED { bytes2[me2.length] _ 0C; }; TRUSTED { me1.checksum _ Checksum.ComputeChecksum[me1.checksum, words, base1]; me2.checksum _ Checksum.ComputeChecksum[me2.checksum, words, base2]; }; FOR i: INT IN [0..words) UNTIL data.pleaseStop DO word1, word2: WORD; TRUSTED { word1 _ base1^; word2 _ base2^; }; IF word1 # word2 THEN IO.PutF[data.log, "%8B/ %4G %4G\n", [integer[me1.index+i*Basics.bytesPerWord]], [rope[Convert.RopeFromInt[word1, 16, FALSE]]], [rope[Convert.RopeFromInt[word2, 16, FALSE]]] ]; base1 _ base1 + 1; base2 _ base2 + 1; ENDLOOP; }; me1.index _ me1.index + me1.length; me2.index _ me2.index + me2.length; me1.block _ NIL; me2.block _ NIL; BROADCAST waiting; ENDLOOP; data.pleaseStop _ TRUE; UNTIL me1.done DO -- Skip rest of me1 UNTIL me1.block # NIL OR me1.done DO WAIT waiting; ENDLOOP; IF me1.done THEN EXIT; me1.bytes _ me1.bytes + me1.length; me1.index _ me1.index + me1.length; me1.block _ NIL; BROADCAST waiting; ENDLOOP; UNTIL me2.done DO -- Skip rest of me2 UNTIL me2.block # NIL OR me2.done DO WAIT waiting; ENDLOOP; IF me2.done THEN EXIT; me2.bytes _ me2.bytes + me2.length; me2.index _ me1.index + me2.length; me2.block _ NIL; BROADCAST waiting; ENDLOOP; IO.Flush[data.log]; }; MakeRule: PROC [parent, sibling: Viewer] RETURNS [child: Viewer] = { child _ Rules.Create[ info: [parent: parent, border: FALSE, wy: IF sibling = NIL THEN 0 ELSE sibling.wy + sibling.wh + 2, wx: 0, ww: parent.ww, wh: 1], paint: FALSE ]; Containers.ChildXBound[parent, child]; }; MakeButton: PROC [parent, sibling: Viewer, data: REF ANY, name: ROPE, proc: Buttons.ButtonProc] RETURNS[child: Viewer] = { child _ Buttons.Create[ info: [name: name, parent: parent, border: TRUE, wy: sibling.wy, wx: sibling.wx + sibling.ww - 1, ww: buttonWidth], proc: proc, clientData: data, fork: TRUE, paint: FALSE]; }; SelectorProc: TYPE = PROC [parent: Viewer, clientData: REF, value: ATOM]; Selector: TYPE = REF SelectorRec; SelectorRec: TYPE = RECORD [ value: REF ATOM, change: PROC [parent: Viewer, clientData: REF, value: ATOM], clientData: REF, buttons: LIST OF Buttons.Button, values: LIST OF ATOM ]; MakeSelector: PROC [name: ROPE, values: LIST OF ATOM, init: REF ATOM _ NIL, change: SelectorProc _ NIL, clientData: REF _ NIL, parent: Viewer, x, y: INTEGER] RETURNS [child: Viewer] = { selector: Selector _ NEW [SelectorRec _ [ value: IF init # NIL THEN init ELSE NEW [ATOM _ values.first], change: change, clientData: clientData, buttons: NIL, values: values ] ]; last: LIST OF Buttons.Button _ NIL; child _ Labels.Create[info: [name: name, parent: parent, border: FALSE, wx: x, wy: y] ]; FOR a: LIST OF ATOM _ values, a.rest UNTIL a = NIL DO child _ Buttons.Create[ info: [name: Atom.GetPName[a.first], parent: parent, border: TRUE, wx: child.wx + child.ww + 2, wy: child.wy], proc: SelectorHelper, clientData: selector, fork: TRUE, paint: TRUE]; IF last = NIL THEN last _ selector.buttons _ CONS[first: child, rest: NIL] ELSE { last.rest _ CONS[first: child, rest: NIL]; last _ last.rest }; IF a.first = selector.value^ THEN Buttons.SetDisplayStyle[child, $WhiteOnBlack]; ENDLOOP; }; SelectorHelper: Buttons.ButtonProc = { self: Buttons.Button = NARROW[parent]; selector: Selector = NARROW[clientData]; buttons: LIST OF Buttons.Button _ selector.buttons; FOR a: LIST OF ATOM _ selector.values, a.rest UNTIL a = NIL DO IF self = buttons.first THEN { selector.value^ _ a.first; IF selector.change # NIL THEN selector.change[self.parent, selector.clientData, a.first]; Buttons.SetDisplayStyle[buttons.first, $WhiteOnBlack]; } ELSE Buttons.SetDisplayStyle[buttons.first, $BlackOnWhite]; buttons _ buttons.rest; ENDLOOP; }; BoolProc: TYPE = PROC [parent: Viewer, clientData: REF, value: BOOL]; Bool: TYPE = REF BoolRec; BoolRec: TYPE = RECORD [ value: REF BOOL, change: BoolProc, clientData: REF, button: Viewer ]; MakeBool: PROC [name: ROPE, init: REF BOOL, change: BoolProc _ NIL, clientData: REF _ NIL, parent: Viewer, x, y: INTEGER] RETURNS [child: Viewer] = { bool: Bool _ NEW [BoolRec _ [ value: IF init # NIL THEN init ELSE NEW [BOOL _ TRUE], change: change, clientData: clientData, button: NIL ] ]; child _ Buttons.Create[ info: [name: name, parent: parent, border: TRUE, wx: x, wy: y], proc: BoolHelper, clientData: bool, fork: TRUE, paint: TRUE]; bool.button _ child; IF bool.value^ THEN Buttons.SetDisplayStyle[child, $WhiteOnBlack]; }; BoolHelper: Buttons.ButtonProc = { self: Buttons.Button = NARROW[parent]; bool: Bool = NARROW[clientData]; bool.value^ _ ~bool.value^; IF bool.value^ THEN Buttons.SetDisplayStyle[bool.button, $WhiteOnBlack] ELSE Buttons.SetDisplayStyle[bool.button, $BlackOnWhite]; IF bool.change # NIL THEN bool.change[self.parent, bool.clientData, bool.value^]; }; MakeLabel: PROC [parent, sibling: Viewer, name: ROPE] RETURNS [child: Viewer] = { child _ Labels.Create[ info: [name: name, parent: parent, border: FALSE, wy: sibling.wy + sibling.wh + (IF sibling.class.flavor = $Button THEN -1 ELSE 2), wx: 2, ww: VFonts.StringWidth[name] + 2*3 + 2], paint: FALSE ]; }; MakeLabeledText: PROC [ parent, sibling: Viewer, name, data: ROPE, prev: Viewer, width: INT, newline: BOOL _ TRUE] RETURNS [child: Viewer] = { buttonWidth: INT _ VFonts.StringWidth[name] + 2*3; x: INTEGER = IF newline THEN 2 ELSE sibling.wx + sibling.ww + 10; y: INTEGER = IF newline THEN sibling.wy + sibling.wh + 1 ELSE sibling.wy; child _ ViewerTools.MakeNewTextViewer[ info: [ parent: parent, wh: buttonHeight, ww: width+10, data: IF prev = NIL THEN data ELSE ViewerTools.GetContents[prev], border: FALSE, wx: x + buttonWidth + 2, wy: y, scrollable: FALSE ], paint: FALSE ]; [] _ Buttons.Create[ info: [name: name, parent: parent, wh: buttonHeight, border: FALSE, wx: x, wy: y], proc: LabeledTextProc, clientData: child, fork: FALSE, paint: FALSE]; RETURN[child]; }; LabeledTextProc: Buttons.ButtonProc = { text: Viewer = NARROW[clientData]; SELECT mouseButton FROM red => ViewerTools.SetSelection[text, NIL]; yellow => NULL; blue => { ViewerTools.SetContents[text, NIL]; ViewerTools.SetSelection[text, NIL] }; ENDCASE => ERROR; }; FTPStyleDate: PROC [date: BasicTime.GMT] RETURNS [rope: ROPE] = { month: ARRAY BasicTime.MonthOfYear OF Rope.ROPE = [ January: "Jan", February: "Feb", March: "Mar", April: "Apr", May: "May", June: "Jun", July: "Jul", August: "Aug", September: "Sep", October: "Oct", November: "Nov", December: "Dec" ]; zoneIndex: TYPE = [4 .. 10]; zoneChars: ARRAY zoneIndex OF CHAR = ['A, 'E, 'C, 'M, 'P, 'Y, 'H]; unpack: BasicTime.Unpacked = BasicTime.Unpack[date]; absZone: INT _ ABS[IF unpack.dst = yes THEN unpack.zone - 60 ELSE unpack.zone]; str: IO.STREAM = IO.ROS[]; IO.PutF[str, "%02d-%g-%02d", [integer[unpack.day]], [rope[month[unpack.month]]], [integer[unpack.year MOD 100]] ]; IO.PutF[str, " %02d:%02d:%02d ", [integer[unpack.hour]], [integer[unpack.minute]], [integer[unpack.second]] ]; IF (unpack.zone / 60 IN zoneIndex) AND absZone MOD 60 = 0 THEN { IO.PutChar[str, zoneChars[unpack.zone / 60]]; IO.PutChar[str, IF unpack.dst = yes THEN 'D ELSE 'S]; IO.PutChar[str, 'T]; } ELSE IO.PutF[str, "%g%g:%02d", [character[IF unpack.zone < 0 THEN '- ELSE '+]], [integer[absZone/60]], [integer[absZone MOD 60]] ]; RETURN[IO.RopeFromROS[str]]; }; Commander.Register["Compare", Create, "Compare a pair of remote files."]; END. φCompare.mesa Copyright c 1985, 1986 by Xerox Corporation. All rights reserved. Hal Murray, September 26, 1986 2:18:16 am PDT Viewer layout parameters [viewer: ViewerClasses.Viewer, event: ViewerEvent, before: BOOL] RETURNS[abort: BOOL _ FALSE] parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL Desired syntax is: "dd-mmm-yy hh:mm:ss zzz" ΚB˜šœ ™ Icodešœ Οmœ7™BK™-—J˜šΟk ˜ Jšœžœ ˜Jšœžœ˜Jšœ žœžœ&˜:Jšœžœ/˜˜>J˜šœ˜Jšœžœ˜Jšœžœ˜)Kšœnžœ˜uJ˜CJšžœžœ(˜>JšœR˜R—J˜—š œ˜ Jšœ;žœ™@Jšžœžœžœ™Jšœžœ(˜AJš žœžœ žœžœžœ˜/Jšœ ˜ Jšžœ˜Jšžœ˜J˜J˜—š œ˜ JšœL™LJšœžœ ˜&Jšœ˜J˜—š‘œžœžœ˜)Jšœžœ˜Jšžœ žœžœžœ ˜'Jšœ žœ˜J˜—š  œ˜$JšœL™LJšœžœ ˜&Jšœžœ˜Jšœ˜J˜—š  œ˜#JšœL™LJšœžœ ˜&Jšœžœ˜Jšœ˜J˜—šœ žœžœžœ˜7Kšœ˜Kšœ˜Kšœ˜K˜K˜—Jšœ žœžœ˜%šœžœžœ˜Jšœ˜Jšœžœžœ˜Jšœ žœžœ˜Jšœ žœžœ˜Jšœ žœžœ˜Jšœžœ˜Jšœžœ˜Jšœ žœ˜Jšœžœžœ˜Jšœžœ˜Jš œžœžœžœžœ˜Jšœžœ˜Jšœžœžœ˜J˜—Jšœ ž œ˜J˜šΠbn œžœ+žœ˜GJšœžœžœŸ&˜Uš’œžœžœ˜Kšžœ žœžœžœ˜Kšœ˜KšœΟrœ £œ˜Kšž œ ˜Kš žœ žœžœžœ žœ˜.Jšœ˜—š‘œžœ ˜Jšœžœ˜Jšœžœžœ>˜QJšœ˜Jšœ ˜ Jšœ˜Jšœ ˜ šž˜Kšœžœžœ˜-Kšžœ žœžœ˜Kšœ ˜ Kšžœ˜—Kšžœ˜Jšœ4žœ˜;šžœ˜Jšœ ˜ Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜—šžœž˜šžœ˜Jšœ ˜ Jšœ3˜3Jšœ˜Jšœ˜——Jšžœ˜Jšœ˜—šžœ$žœ ˜3JšžœEžœ˜U—Kšœ˜Jšœ žœ˜Jšœ˜Jšœ˜—š’ œžœ1žœ˜Kš’œžœ˜Jšœ˜Jšœžœ˜)Jšœžœ˜1Jšœ ˜ Jšœ ˜ Jšœ˜Jšœ ˜ Jšžœžœžœžœ˜+Jšœ=žœ˜GJšžœ˜Jšœ˜—š’œžœžœ˜'šžœž˜Jšœžœ˜ Jšœ žœn˜yJšžœžœ˜—Jšœ˜—Jšœžœ˜Jšœžœžœ˜Jšœžœ žœ ˜Jšœžœžœžœ˜6Jšœžœ˜Jšœ žœ˜%Jšœžœ˜$Jšœžœ˜#Jšœžœ˜"šœ žœžœ ˜,Jšžœ<˜>Jšœ žœ˜ Jšœ žœ˜Jšžœ˜ —Jšžœ žœžœžœ)˜@Jšžœžœ žœžœ˜:Jšœ žœ8˜FJšœ˜Jšœ)˜)Jšžœ˜Jšžœ.˜1šžœ$žœ ˜5Jšžœ@˜BJšžœ˜ —Jšžœ žœ žœ˜(Jšœ žœ˜Jšœ˜Jšœ˜—š‘ œž œ˜Jšž œ ˜Jšœ˜—š ‘œžœ žœžœ žœ˜1Jšžœ4˜:Jšœ˜—š’œžœžœžœ žœžœžœžœ žœ˜^Jšœžœ˜)Kšžœžœžœ˜Kšžœ žœžœžœ˜K˜Kšœ£œžœ£ œ˜%Kšž œ ˜Kš žœ žœžœžœ žœ˜.Jšœ˜—š ’œžœžœžœ žœ˜1Jšœžœ˜)J˜Jšœžœ(žœ˜Ašžœ˜Jšœ ˜ Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜—šžœž˜šžœ˜Jšœ ˜ Jšœ3˜3Jšœ˜Jšœ˜——Jšœ˜—š‘ œžœžœ˜-Jšœžœ˜3Jšœžœ'˜1Jšœžœ'˜2šžœ˜Jšœ ˜ Jšœ˜Jšœ˜Jšœ ˜ Jšœ˜—šžœ˜ Jšžœžœžœ ˜QJšžœžœ(˜@—šžœ ž˜Kšœžœžœ˜Kšœžœžœžœžœžœžœžœžœžœ˜:Jš žœ žœžœ žœžœ žœ˜9Jšžœ žœžœ˜Kš œžœ žœžœžœžœ˜5Kšœžœ˜Kšœ£œ˜ Kš žœ£œžœžœžœ £œ ˜Ašžœ˜ Kšœ<£œ ˜O—Kšœ£œ˜ Jšœ žœ˜Jšž œ ˜Jšžœ˜—Jšžœ˜Jšœ˜J˜—š‘ œžœžœ˜,Jšœžœ˜4Jšœžœ˜4Jšœžœ'˜2Jšœžœ'˜2Jšœžœ'˜2Jšœžœ'˜2šžœ˜Jšœ ˜ Jšœ*˜*Jšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ˜—šžœ˜ Jšžœžœžœ!˜SJšžœžœ'˜?Jšžœžœžœ!˜SJšžœžœ*˜B—šž˜Jš žœ žœžœ žœžœ žœ˜;Jšžœ žœžœ˜Jš žœ žœžœ žœžœ žœ˜;Jšžœ žœžœ˜Kšœ£œ˜#Kšœ£œ˜#šžœžœ˜!šžœ˜Jšœ ˜ Jšœ<˜J˜J˜Jšœ žœ˜ J˜—Jšœžœžœžœ˜#JšœAžœ˜Xš žœžœžœžœžœžœž˜5šœ˜Kšœ=žœ-˜nJšœ2žœ žœ˜E—Jš žœžœžœžœžœ˜JJšžœžœžœ˜EJšžœžœ/˜PJšžœ˜ ——J˜š œ˜&JšœL™LJšœžœ ˜&Jšœžœ ˜(Jšœ žœžœ#˜3š žœžœžœžœžœžœž˜>šžœžœ˜J˜Jšžœžœžœ<˜YJšœ8˜8—Jšžœ7˜;J˜Jšžœ˜ ——J˜Jš ‘œžœžœžœ žœ˜EJšœžœžœ ˜šœ žœžœ˜Jšœžœžœ˜Jšœ˜Jšœ žœ˜Jšœ˜—J˜š‘œž˜Kšœžœžœžœžœžœžœžœ˜jKšžœ˜šœ žœ ˜Jšœžœžœžœžœžœžœžœ˜6J˜Jšœ˜Jšœžœ˜—šœ˜Kšœ+žœ˜?Jšœ*žœ žœ˜=—Jšœ˜Jšžœ žœ2˜E—J˜š  œ˜"JšœL™LJšœžœ ˜&Jšœ žœ ˜ Jšœ˜Jšžœ žœ4˜GJšžœ5˜9Jšžœžœžœ<˜U—š‘ œžœ!žœžœ˜Q˜šœ+žœ˜1Jšœžœ žœžœ˜QJ˜Jšœ(˜(—Jšœžœ˜——J˜š‘œžœ˜Jš œ%žœžœ žœžœžœ˜vJšœ žœ"˜2Jš œžœžœ žœžœ˜AJš œžœžœ žœžœ ˜I˜&šœ˜Jšœ/˜/Jš œžœžœžœžœ˜AJšœžœ˜J˜Jšœ žœ˜—Jšœžœ˜—˜Jšœ=žœ˜RJšœ0žœ žœ˜E—Jšžœ ˜J˜—š œ˜'JšœL™LJšœžœ ˜"šžœ ž˜Jšœ&žœ˜+Jšœ žœ˜Jšœ(žœ"žœ˜TJšžœžœ˜——J˜š ‘ œžœžœžœžœ˜Ašœžœžœžœ˜3K˜·—Kšœ žœ ˜Kšœ žœ žœžœ!˜CKšœ4˜4Kš œ žœžœžœžœžœ˜OKšœ+™+Kš œžœžœžœžœ˜Kšžœdžœ ˜rKšžœl˜nš žœžœ žœ žœžœ˜@Kšžœ+˜-Kšžœžœžœžœ˜5Kšžœ˜—š žœžœ#žœžœžœ˜OKšœ(žœ˜3—Kšžœžœ˜Kšœ˜K˜—šœI˜IJ˜—Jšžœ˜J˜J˜J˜——…—Lςi*